IJPL-149476 convert InspectopediaExtractor to language with modern API

GitOrigin-RevId: 9721f95f85456710dd49deb96d54f65e1da40e0e
This commit is contained in:
Vladimir Krivosheev
2024-05-12 20:46:24 +02:00
committed by intellij-monorepo-bot
parent 7c5bb9bd3c
commit 3eaa41d59a

View File

@@ -1,244 +1,247 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.inspectopedia.extractor;
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@file:Suppress("ReplacePutWithAssignment")
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.intellij.codeInspection.InspectionEP;
import com.intellij.codeInspection.InspectionProfileEntry;
import com.intellij.codeInspection.ex.*;
import com.intellij.codeInspection.options.*;
import com.intellij.ide.plugins.PluginManager;
import com.intellij.inspectopedia.extractor.data.Inspection;
import com.intellij.inspectopedia.extractor.data.OptionsPanelInfo;
import com.intellij.inspectopedia.extractor.data.Plugin;
import com.intellij.inspectopedia.extractor.data.Plugins;
import com.intellij.inspectopedia.extractor.utils.HtmlUtils;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ApplicationStarter;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.util.text.HtmlChunk;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
package com.intellij.inspectopedia.extractor
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.MapperFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper
import com.intellij.codeInspection.ex.InspectionMetaInformationService
import com.intellij.codeInspection.options.*
import com.intellij.ide.plugins.IdeaPluginDescriptor
import com.intellij.ide.plugins.PluginManager
import com.intellij.inspectopedia.extractor.InspectopediaExtractor.Companion.getMyText
import com.intellij.inspectopedia.extractor.data.Inspection
import com.intellij.inspectopedia.extractor.data.OptionsPanelInfo
import com.intellij.inspectopedia.extractor.data.Plugin
import com.intellij.inspectopedia.extractor.data.Plugins
import com.intellij.inspectopedia.extractor.utils.HtmlUtils
import com.intellij.openapi.application.ApplicationInfo
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ApplicationStarter
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.ProjectManager
import com.intellij.profile.codeInspection.InspectionProjectProfileManager
import com.intellij.util.containers.ContainerUtil
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Path
import java.util.*
import java.util.function.Function
import java.util.stream.Collectors
final class InspectopediaExtractor implements ApplicationStarter {
private static final Logger LOG = Logger.getInstance(InspectopediaExtractor.class);
private static final Map<String, ObjectMapper> ASSETS = new HashMap<>();
private class InspectopediaExtractor : ApplicationStarter {
private val assets: MutableMap<String, ObjectMapper> = HashMap()
static {
JsonMapper jsonMapper = JsonMapper.builder()
init {
val jsonMapper = JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
.build();
ASSETS.put("json", jsonMapper);
.build()
assets.put("json", jsonMapper)
}
@Override
public void main(@NotNull List<String> args) {
final int size = args.size();
override fun main(args: List<String>) {
val size = args.size
if (size != 2) {
LOG.error("Usage: inspectopedia-generator <output directory>");
System.exit(-1);
LOG.error("Usage: inspectopedia-generator <output directory>")
System.exit(-1)
}
ApplicationInfo appInfo = ApplicationInfo.getInstance();
String IDE_CODE = appInfo.getBuild().getProductCode().toLowerCase(Locale.getDefault());
String IDE_NAME = appInfo.getVersionName();
String IDE_VERSION = appInfo.getShortVersion();
String ASSET_FILENAME = IDE_CODE + "-inspections.";
val appInfo = ApplicationInfo.getInstance()
val IDE_CODE = appInfo.build.productCode.lowercase(Locale.getDefault())
val IDE_NAME = appInfo.versionName
val IDE_VERSION = appInfo.shortVersion
val ASSET_FILENAME = "$IDE_CODE-inspections."
final String outputDirectory = args.get(1);
final Path rootOutputPath = Path.of(outputDirectory).toAbsolutePath();
final Path outputPath = rootOutputPath.resolve(IDE_CODE);
val outputDirectory = args[1]
val rootOutputPath = Path.of(outputDirectory).toAbsolutePath()
val outputPath = rootOutputPath.resolve(IDE_CODE)
try {
Files.createDirectories(outputPath);
Files.createDirectories(outputPath)
}
catch (IOException e) {
LOG.error("Output directory does not exist and could not be created");
System.exit(-1);
catch (e: IOException) {
LOG.error("Output directory does not exist and could not be created")
System.exit(-1)
}
if (!Files.exists(outputPath) || !Files.isDirectory(outputPath) || !Files.isWritable(outputPath)) {
LOG.error("Output path is invalid");
System.exit(-1);
LOG.error("Output path is invalid")
System.exit(-1)
}
try {
final Project project = ProjectManager.getInstance().getDefaultProject();
val project = ProjectManager.getInstance().defaultProject
LOG.info("Using project " + project.getName() + ", default: " + project.isDefault());
final InspectionProjectProfileManager inspectionManager = InspectionProjectProfileManager.getInstance(project);
final List<ScopeToolState> scopeToolStates = inspectionManager.getCurrentProfile().getAllTools();
LOG.info("Using project " + project.name + ", default: " + project.isDefault)
val inspectionManager = InspectionProjectProfileManager.getInstance(project)
val scopeToolStates = inspectionManager.currentProfile.allTools
final Map<String, Plugin> availablePlugins = Arrays.stream(PluginManager.getPlugins()).map(
pluginDescriptor -> new Plugin(pluginDescriptor.getPluginId().getIdString(), pluginDescriptor.getName(),
pluginDescriptor.getVersion())).distinct()
.collect(Collectors.toMap(Plugin::getId, plugin -> plugin));
val availablePlugins = Arrays.stream(
PluginManager.getPlugins()).map { pluginDescriptor: IdeaPluginDescriptor ->
Plugin(pluginDescriptor.pluginId.idString, pluginDescriptor.name,
pluginDescriptor.version)
}.distinct()
.collect(Collectors.toMap(
Function { obj: Plugin -> obj.getId() }, Function { plugin: Plugin -> plugin }))
availablePlugins.put(IDE_NAME, new Plugin(IDE_NAME, IDE_NAME, IDE_VERSION));
availablePlugins[IDE_NAME] = Plugin(IDE_NAME, IDE_NAME, IDE_VERSION)
final InspectionMetaInformationService
service = ApplicationManager.getApplication().getService(InspectionMetaInformationService.class);
val service = ApplicationManager.getApplication().getService(
InspectionMetaInformationService::class.java)
final MetaInformationState inspectionsExtraState = service == null ? null : (MetaInformationState)service.getState(null);
val inspectionsExtraState = if (service == null) null else service.getState(null)
for (final ScopeToolState scopeToolState : scopeToolStates) {
for (scopeToolState in scopeToolStates) {
val wrapper = scopeToolState.tool
val extension = wrapper.extension
val pluginId = extension?.pluginDescriptor?.pluginId?.idString ?: IDE_NAME
val originalDescription = wrapper.loadDescription()
val description = originalDescription?.split("<!-- tooltip end -->".toRegex())?.dropLastWhile { it.isEmpty() }?.toTypedArray()
?: arrayOf("")
final InspectionToolWrapper<?, ?> wrapper = scopeToolState.getTool();
final InspectionEP extension = wrapper.getExtension();
final String pluginId = extension == null ? IDE_NAME : extension.getPluginDescriptor().getPluginId().getIdString();
final String originalDescription = wrapper.loadDescription();
final String[] description = originalDescription == null ? new String[]{""} : originalDescription.split("<!-- tooltip end -->");
List<OptionsPanelInfo> panelInfo = null;
var panelInfo: List<OptionsPanelInfo?>? = null
try {
InspectionProfileEntry tool = wrapper.getTool();
final OptPane panel = tool.getOptionsPane();
val tool = wrapper.tool
val panel = tool.optionsPane
if (!panel.equals(OptPane.EMPTY)) {
LOG.info("Saving options panel for " + wrapper.getShortName());
panelInfo = retrievePanelStructure(panel, tool.getOptionController());
if (panel != OptPane.EMPTY) {
LOG.info("Saving options panel for " + wrapper.shortName)
panelInfo = retrievePanelStructure(panel, tool.optionController)
}
}
catch (Throwable t) {
LOG.info("Cannot create options panel " + wrapper.getShortName(), t);
catch (t: Throwable) {
LOG.info("Cannot create options panel " + wrapper.shortName, t)
}
final MetaInformation metaInformation = inspectionsExtraState == null ? null : inspectionsExtraState.getInspections().get(wrapper.getID());
final List<Integer> cweIds = metaInformation == null ? null : metaInformation.getCweIds();
val metaInformation = inspectionsExtraState?.inspections?.get(wrapper.id)
val cweIds = metaInformation?.cweIds
final String language = wrapper.getLanguage();
final String briefDescription = HtmlUtils.cleanupHtml(description[0], language);
final String extendedDescription = description.length > 1 ? HtmlUtils.cleanupHtml(description[1], language) : null;
final Inspection inspection = new Inspection(wrapper.getShortName(), wrapper.getDisplayName(), wrapper.getDefaultLevel().getName(),
language, briefDescription,
extendedDescription, Arrays.asList(wrapper.getGroupPath()), wrapper.applyToDialects(),
wrapper.isCleanupTool(), wrapper.isEnabledByDefault(), panelInfo, cweIds);
val language = wrapper.language
val briefDescription = HtmlUtils.cleanupHtml(description[0], language)
val extendedDescription = if (description.size > 1) HtmlUtils.cleanupHtml(
description[1], language)
else null
val inspection = Inspection(wrapper.shortName, wrapper.displayName, wrapper.defaultLevel.name,
language, briefDescription,
extendedDescription, Arrays.asList(*wrapper.groupPath), wrapper.applyToDialects(),
wrapper.isCleanupTool, wrapper.isEnabledByDefault, panelInfo, cweIds)
availablePlugins.get(pluginId).addInspection(inspection);
availablePlugins[pluginId]!!.addInspection(inspection)
}
var sortedPlugins = availablePlugins.values().stream()
.sorted(Comparator.comparing(Plugin::getId))
.peek(plugin -> {
plugin.inspections.sort(null);
}).toList();
final Plugins pluginsData = new Plugins(sortedPlugins, IDE_CODE, IDE_NAME, IDE_VERSION);
val sortedPlugins = availablePlugins.values.stream()
.sorted(Comparator.comparing { obj: Plugin -> obj.getId() })
.peek { plugin: Plugin ->
plugin.inspections.sort(null)
}.toList()
val pluginsData = Plugins(sortedPlugins, IDE_CODE, IDE_NAME, IDE_VERSION)
for (final String ext : ASSETS.keySet()) {
String data = "";
for (ext in assets.keys) {
var data: String? = ""
try {
data = ASSETS.get(ext).writeValueAsString(pluginsData);
data = assets[ext]!!.writeValueAsString(pluginsData)
}
catch (JsonProcessingException e) {
LOG.error("Cannot serialize " + ext.toUpperCase(Locale.getDefault()), e);
System.exit(-1);
catch (e: JsonProcessingException) {
LOG.error("Cannot serialize " + ext.uppercase(Locale.getDefault()), e)
System.exit(-1)
}
final Path outPath = outputPath.resolve(ASSET_FILENAME + ext);
val outPath = outputPath.resolve(ASSET_FILENAME + ext)
try {
Files.writeString(outPath, data);
Files.writeString(outPath, data)
}
catch (IOException e) {
LOG.error("Cannot write " + outPath, e);
System.exit(-1);
catch (e: IOException) {
LOG.error("Cannot write $outPath", e)
System.exit(-1)
}
LOG.info("Inspections info saved in " + outPath);
LOG.info("Inspections info saved in $outPath")
}
}
catch (Exception e) {
LOG.error(e.getMessage(), e);
System.exit(-1);
catch (e: Exception) {
LOG.error(e.message, e)
System.exit(-1)
}
System.exit(0);
System.exit(0)
}
}
private static @Nullable LocMessage getMyText(final @NotNull OptComponent cmp) {
if (cmp instanceof OptCheckbox checkbox) {
return checkbox.label();
}
else if (cmp instanceof OptString string) {
return string.splitLabel();
}
else if (cmp instanceof OptNumber number) {
return number.splitLabel();
}
else if (cmp instanceof OptExpandableString expandableString) {
return expandableString.label();
}
else if (cmp instanceof OptStringList list) {
return list.label();
}
else if (cmp instanceof OptTable table) {
return table.label();
}
else if (cmp instanceof OptTableColumn column) {
return column.name();
}
else if (cmp instanceof OptTab tab) {
return tab.label();
}
else if (cmp instanceof OptDropdown dropdown) {
return dropdown.splitLabel();
}
else if (cmp instanceof OptGroup group) {
return group.label();
}
else if (cmp instanceof OptSettingLink link) {
return link.displayName();
}
else {
return null;
}
}
private val LOG = logger<InspectopediaExtractor>()
private static @NotNull List<@NotNull OptionsPanelInfo> retrievePanelStructure(@NotNull OptPane pane,
@NotNull OptionController controller) {
List<OptionsPanelInfo> children = new ArrayList<>();
for (OptRegularComponent component : pane.components()) {
children.add(retrievePanelStructure(component, controller));
}
return children;
private fun getMyText(cmp: OptComponent): LocMessage? {
return if (cmp is OptCheckbox) {
cmp.label
}
else if (cmp is OptString) {
cmp.splitLabel
}
else if (cmp is OptNumber) {
cmp.splitLabel
}
else if (cmp is OptExpandableString) {
cmp.label
}
else if (cmp is OptStringList) {
cmp.label
}
else if (cmp is OptTable) {
cmp.label
}
else if (cmp is OptTableColumn) {
cmp.name
}
else if (cmp is OptTab) {
cmp.label
}
else if (cmp is OptDropdown) {
cmp.splitLabel
}
else if (cmp is OptGroup) {
cmp.label
}
else if (cmp is OptSettingLink) {
cmp.displayName
}
else {
null
}
}
private static @NotNull OptionsPanelInfo retrievePanelStructure(@NotNull OptComponent component, @NotNull OptionController controller) {
final OptionsPanelInfo result = new OptionsPanelInfo();
result.type = component.getClass().getSimpleName();
result.value = component instanceof OptControl control ? controller.getOption(control.bindId()) : null;
if (component instanceof OptDropdown dropdown) {
if (result.value != null) {
OptDropdown.Option option = dropdown.findOption(result.value);
result.value = option == null ? null : option.label().label();
}
result.content = ContainerUtil.map(dropdown.options(), opt -> opt.label().label());
}
LocMessage text = getMyText(component);
result.text = text == null ? null : text.label();
if (component instanceof OptDescribedComponent describedComponent) {
HtmlChunk description = describedComponent.description();
result.description = description == null ? null : description.toString();
}
List<OptionsPanelInfo> children = new ArrayList<>();
for (OptComponent child : component.children()) {
children.add(retrievePanelStructure(child, controller));
}
if (!children.isEmpty()) {
result.children = children;
}
return result;
private fun retrievePanelStructure(pane: OptPane,
controller: OptionController): List<OptionsPanelInfo?> {
val children: MutableList<OptionsPanelInfo?> = ArrayList()
for (component in pane.components) {
children.add(retrievePanelStructure(component, controller))
}
return children
}
private fun retrievePanelStructure(component: OptComponent, controller: OptionController): OptionsPanelInfo {
val result = OptionsPanelInfo()
result.type = component.javaClass.simpleName
result.value = if (component is OptControl) controller.getOption(component.bindId()) else null
if (component is OptDropdown) {
if (result.value != null) {
val option = component.findOption(result.value)
result.value = option?.label?.label()
}
result.content = ContainerUtil.map(component.options) { opt: OptDropdown.Option -> opt.label.label() }
}
val text = getMyText(component)
result.text = text?.label()
if (component is OptDescribedComponent) {
val description = component.description()
result.description = description?.toString()
}
val children: MutableList<OptionsPanelInfo> = ArrayList()
for (child in component.children()) {
children.add(retrievePanelStructure(child, controller))
}
if (!children.isEmpty()) {
result.children = children
}
return result
}