Snapshot of commit d5ec1d5018ed24f1b4f32b1d09df6dbd7e2fc425
from branch master of git://git.jetbrains.org/idea/community.git
diff --git a/plugins/devkit/devkit.iml b/plugins/devkit/devkit.iml
new file mode 100644
index 0000000..77509ce
--- /dev/null
+++ b/plugins/devkit/devkit.iml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/resources" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="org.jetbrains.idea.devkit" />
+ <sourceFolder url="file://$MODULE_DIR$/testSources" isTestSource="true" packagePrefix="org.jetbrains.idea.devkit" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module" module-name="openapi" />
+ <orderEntry type="module" module-name="util" />
+ <orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
+ <orderEntry type="library" name="JDOM" level="project" />
+ <orderEntry type="library" name="Log4J" level="project" />
+ <orderEntry type="module" module-name="compiler-openapi" />
+ <orderEntry type="module" module-name="execution-openapi" />
+ <orderEntry type="module" module-name="dom-openapi" />
+ <orderEntry type="module" module-name="execution-impl" />
+ <orderEntry type="module" module-name="testFramework-java" scope="TEST" />
+ <orderEntry type="module" module-name="java-impl" />
+ <orderEntry type="library" name="TestNG" level="project" />
+ <orderEntry type="module" module-name="compiler-impl" />
+ <orderEntry type="module" module-name="properties" />
+ <orderEntry type="module" module-name="idea-ui" scope="TEST" />
+ <orderEntry type="module" module-name="xml" />
+ <orderEntry type="library" scope="TEST" name="Groovy" level="project" />
+ <orderEntry type="module" module-name="jps-builders" />
+ </component>
+</module>
+
diff --git a/plugins/devkit/jps-plugin/devkit-jps-plugin.iml b/plugins/devkit/jps-plugin/devkit-jps-plugin.iml
new file mode 100644
index 0000000..f88b075
--- /dev/null
+++ b/plugins/devkit/jps-plugin/devkit-jps-plugin.iml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/testSrc" isTestSource="true" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module" module-name="jps-model-api" />
+ <orderEntry type="module" module-name="jps-model-serialization" />
+ <orderEntry type="module" module-name="jps-builders" />
+ <orderEntry type="module" module-name="jps-model-impl" scope="TEST" />
+ </component>
+</module>
+
diff --git a/plugins/devkit/jps-plugin/src/META-INF/services/org.jetbrains.jps.incremental.artifacts.JpsSyntheticArtifactProvider b/plugins/devkit/jps-plugin/src/META-INF/services/org.jetbrains.jps.incremental.artifacts.JpsSyntheticArtifactProvider
new file mode 100644
index 0000000..2f95418
--- /dev/null
+++ b/plugins/devkit/jps-plugin/src/META-INF/services/org.jetbrains.jps.incremental.artifacts.JpsSyntheticArtifactProvider
@@ -0,0 +1 @@
+org.jetbrains.jps.devkit.builder.JpsPluginSyntheticArtifactProvider
\ No newline at end of file
diff --git a/plugins/devkit/jps-plugin/src/META-INF/services/org.jetbrains.jps.model.serialization.JpsModelSerializerExtension b/plugins/devkit/jps-plugin/src/META-INF/services/org.jetbrains.jps.model.serialization.JpsModelSerializerExtension
new file mode 100644
index 0000000..34f5c15
--- /dev/null
+++ b/plugins/devkit/jps-plugin/src/META-INF/services/org.jetbrains.jps.model.serialization.JpsModelSerializerExtension
@@ -0,0 +1 @@
+org.jetbrains.jps.devkit.model.impl.JpsDevKitModelSerializerExtension
\ No newline at end of file
diff --git a/plugins/devkit/jps-plugin/src/org/jetbrains/jps/devkit/builder/JpsPluginSyntheticArtifactProvider.java b/plugins/devkit/jps-plugin/src/org/jetbrains/jps/devkit/builder/JpsPluginSyntheticArtifactProvider.java
new file mode 100644
index 0000000..b432f86
--- /dev/null
+++ b/plugins/devkit/jps-plugin/src/org/jetbrains/jps/devkit/builder/JpsPluginSyntheticArtifactProvider.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.devkit.builder;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.JDOMUtil;
+import com.intellij.openapi.util.io.FileUtil;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.jps.util.JpsPathUtil;
+import org.jetbrains.jps.devkit.model.JpsIdeaSdkProperties;
+import org.jetbrains.jps.devkit.model.JpsIdeaSdkType;
+import org.jetbrains.jps.devkit.model.JpsPluginModuleProperties;
+import org.jetbrains.jps.devkit.model.JpsPluginModuleType;
+import org.jetbrains.jps.incremental.artifacts.JpsSyntheticArtifactProvider;
+import org.jetbrains.jps.model.JpsElementFactory;
+import org.jetbrains.jps.model.JpsModel;
+import org.jetbrains.jps.model.JpsSimpleElement;
+import org.jetbrains.jps.model.artifact.DirectoryArtifactType;
+import org.jetbrains.jps.model.artifact.JpsArtifact;
+import org.jetbrains.jps.model.artifact.JpsArtifactService;
+import org.jetbrains.jps.model.artifact.elements.JpsCompositePackagingElement;
+import org.jetbrains.jps.model.artifact.elements.JpsPackagingElementFactory;
+import org.jetbrains.jps.model.java.JpsJavaClasspathKind;
+import org.jetbrains.jps.model.java.JpsJavaDependenciesEnumerator;
+import org.jetbrains.jps.model.java.JpsJavaExtensionService;
+import org.jetbrains.jps.model.java.JpsJavaModuleType;
+import org.jetbrains.jps.model.library.JpsLibrary;
+import org.jetbrains.jps.model.library.JpsOrderRootType;
+import org.jetbrains.jps.model.library.sdk.JpsSdk;
+import org.jetbrains.jps.model.module.JpsModule;
+import org.jetbrains.jps.model.module.JpsTypedModule;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class JpsPluginSyntheticArtifactProvider extends JpsSyntheticArtifactProvider {
+ private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.devkit.builder.JpsPluginSyntheticArtifactProvider");
+
+ @NotNull
+ @Override
+ public List<JpsArtifact> createArtifacts(@NotNull JpsModel model) {
+ List<JpsArtifact> artifacts = new ArrayList<JpsArtifact>();
+ for (JpsTypedModule<JpsSimpleElement<JpsPluginModuleProperties>> module : model.getProject().getModules(JpsPluginModuleType.INSTANCE)) {
+ artifacts.add(createArtifact(module, module.getProperties().getData()));
+ }
+ return artifacts;
+ }
+
+ private static JpsArtifact createArtifact(JpsModule module, JpsPluginModuleProperties properties) {
+ JpsPackagingElementFactory factory = JpsPackagingElementFactory.getInstance();
+ JpsCompositePackagingElement root = factory.createArtifactRoot();
+ String pluginXmlUrl = properties.getPluginXmlUrl();
+ if (pluginXmlUrl != null) {
+ String pluginXmlPath = JpsPathUtil.urlToPath(pluginXmlUrl);
+ JpsCompositePackagingElement metaInfDir = factory.getOrCreateDirectory(root, "META-INF");
+ metaInfDir.addChild(factory.createFileCopy(pluginXmlPath, null));
+ File pluginXmlFile = JpsPathUtil.urlToFile(pluginXmlUrl);
+ if (pluginXmlFile.exists()) {
+ try {
+ Element rootElement = JDOMUtil.loadDocument(pluginXmlFile).getRootElement();
+ for (Element dependsElement : JDOMUtil.getChildren(rootElement, "depends")) {
+ String relativePath = dependsElement.getAttributeValue("config-file");
+ if (relativePath != null) {
+ File dependencyFile = new File(pluginXmlFile.getParent(), FileUtil.toSystemDependentName(relativePath));
+ String dependencyPath = FileUtil.toSystemIndependentName(dependencyFile.getAbsolutePath());
+ metaInfDir.addChild(factory.createFileCopy(dependencyPath, null));
+ }
+ }
+ }
+ catch (JDOMException e) {
+ LOG.info(e);
+ }
+ catch (IOException e) {
+ LOG.info(e);
+ }
+ }
+ }
+
+ JpsJavaDependenciesEnumerator enumerator = JpsJavaExtensionService.dependencies(module).recursively().includedIn(
+ JpsJavaClasspathKind.PRODUCTION_RUNTIME);
+ JpsCompositePackagingElement classesDir = factory.getOrCreateDirectory(root, "classes");
+ for (JpsModule depModule : enumerator.getModules()) {
+ if (depModule.getModuleType().equals(JpsJavaModuleType.INSTANCE)) {
+ classesDir.addChild(JpsJavaExtensionService.getInstance().createProductionModuleOutput(depModule.createReference()));
+ }
+ }
+ classesDir.addChild(JpsJavaExtensionService.getInstance().createProductionModuleOutput(module.createReference()));
+
+ for (JpsLibrary library : enumerator.getLibraries()) {
+ JpsCompositePackagingElement parent;
+ if (hasDirsOnly(library)) {
+ parent = classesDir;
+ }
+ else {
+ parent = factory.getOrCreateDirectory(root, "lib");
+ }
+ parent.addChild(factory.createLibraryElement(library.createReference()));
+ }
+
+ String name = module.getName() + ":plugin";
+ JpsArtifact artifact = JpsArtifactService.getInstance().createArtifact(name, root, DirectoryArtifactType.INSTANCE, JpsElementFactory.getInstance().createDummyElement());
+
+ JpsSdk<JpsSimpleElement<JpsIdeaSdkProperties>> sdk = module.getSdk(JpsIdeaSdkType.INSTANCE);
+ if (sdk != null) {
+ String sandboxHome = sdk.getSdkProperties().getData().getSandboxHome();
+ if (sandboxHome != null) {
+ artifact.setOutputPath(sandboxHome + "/plugins/" + module.getName());
+ }
+ }
+ return artifact;
+ }
+
+ private static boolean hasDirsOnly(JpsLibrary library) {
+ List<File> files = library.getFiles(JpsOrderRootType.COMPILED);
+ for (File file : files) {
+ if (!file.isDirectory()) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/plugins/devkit/jps-plugin/src/org/jetbrains/jps/devkit/model/JpsIdeaSdkProperties.java b/plugins/devkit/jps-plugin/src/org/jetbrains/jps/devkit/model/JpsIdeaSdkProperties.java
new file mode 100644
index 0000000..2f6aca6
--- /dev/null
+++ b/plugins/devkit/jps-plugin/src/org/jetbrains/jps/devkit/model/JpsIdeaSdkProperties.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.devkit.model;
+
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public class JpsIdeaSdkProperties {
+ private final String mySandboxHome;
+ private final String myJdkName;
+
+ public JpsIdeaSdkProperties(String sandboxHome, String jdkName) {
+ mySandboxHome = sandboxHome;
+ myJdkName = jdkName;
+ }
+
+ @Nullable
+ public String getSandboxHome() {
+ return mySandboxHome;
+ }
+
+ @Nullable
+ public String getJdkName() {
+ return myJdkName;
+ }
+}
diff --git a/plugins/devkit/jps-plugin/src/org/jetbrains/jps/devkit/model/JpsIdeaSdkType.java b/plugins/devkit/jps-plugin/src/org/jetbrains/jps/devkit/model/JpsIdeaSdkType.java
new file mode 100644
index 0000000..6f5d951
--- /dev/null
+++ b/plugins/devkit/jps-plugin/src/org/jetbrains/jps/devkit/model/JpsIdeaSdkType.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.devkit.model;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.jps.model.JpsElement;
+import org.jetbrains.jps.model.JpsSimpleElement;
+import org.jetbrains.jps.model.java.JpsJavaSdkTypeWrapper;
+import org.jetbrains.jps.model.library.sdk.JpsSdkType;
+
+/**
+ * @author nik
+ */
+public class JpsIdeaSdkType extends JpsSdkType<JpsSimpleElement<JpsIdeaSdkProperties>> implements JpsJavaSdkTypeWrapper {
+ public static final JpsIdeaSdkType INSTANCE = new JpsIdeaSdkType();
+
+ @Override
+ public String getJavaSdkName(@NotNull JpsElement properties) {
+ return ((JpsIdeaSdkProperties)((JpsSimpleElement<?>)properties).getData()).getJdkName();
+ }
+}
diff --git a/plugins/devkit/jps-plugin/src/org/jetbrains/jps/devkit/model/JpsPluginModuleProperties.java b/plugins/devkit/jps-plugin/src/org/jetbrains/jps/devkit/model/JpsPluginModuleProperties.java
new file mode 100644
index 0000000..b70cdf1
--- /dev/null
+++ b/plugins/devkit/jps-plugin/src/org/jetbrains/jps/devkit/model/JpsPluginModuleProperties.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.devkit.model;
+
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public class JpsPluginModuleProperties {
+ private final String myPluginXmlUrl;
+ private final String myManifestFileUrl;
+
+ public JpsPluginModuleProperties(@Nullable String pluginXmlUrl, @Nullable String manifestFileUrl) {
+ myPluginXmlUrl = pluginXmlUrl;
+ myManifestFileUrl = manifestFileUrl;
+ }
+
+ @Nullable
+ public String getPluginXmlUrl() {
+ return myPluginXmlUrl;
+ }
+
+ @Nullable
+ public String getManifestFileUrl() {
+ return myManifestFileUrl;
+ }
+}
diff --git a/plugins/devkit/jps-plugin/src/org/jetbrains/jps/devkit/model/JpsPluginModuleType.java b/plugins/devkit/jps-plugin/src/org/jetbrains/jps/devkit/model/JpsPluginModuleType.java
new file mode 100644
index 0000000..2cc4166
--- /dev/null
+++ b/plugins/devkit/jps-plugin/src/org/jetbrains/jps/devkit/model/JpsPluginModuleType.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.devkit.model;
+
+import org.jetbrains.jps.model.JpsSimpleElement;
+import org.jetbrains.jps.model.module.JpsModuleType;
+
+/**
+ * @author nik
+ */
+public class JpsPluginModuleType extends JpsModuleType<JpsSimpleElement<JpsPluginModuleProperties>> {
+ public static final JpsPluginModuleType INSTANCE = new JpsPluginModuleType();
+
+}
diff --git a/plugins/devkit/jps-plugin/src/org/jetbrains/jps/devkit/model/impl/JpsDevKitModelSerializerExtension.java b/plugins/devkit/jps-plugin/src/org/jetbrains/jps/devkit/model/impl/JpsDevKitModelSerializerExtension.java
new file mode 100644
index 0000000..0dd76f7
--- /dev/null
+++ b/plugins/devkit/jps-plugin/src/org/jetbrains/jps/devkit/model/impl/JpsDevKitModelSerializerExtension.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.devkit.model.impl;
+
+import com.intellij.openapi.util.JDOMExternalizerUtil;
+import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.devkit.model.JpsIdeaSdkProperties;
+import org.jetbrains.jps.devkit.model.JpsIdeaSdkType;
+import org.jetbrains.jps.devkit.model.JpsPluginModuleProperties;
+import org.jetbrains.jps.devkit.model.JpsPluginModuleType;
+import org.jetbrains.jps.model.JpsElementFactory;
+import org.jetbrains.jps.model.JpsSimpleElement;
+import org.jetbrains.jps.model.serialization.*;
+import org.jetbrains.jps.model.serialization.module.JpsModulePropertiesSerializer;
+import org.jetbrains.jps.model.serialization.library.JpsSdkPropertiesSerializer;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class JpsDevKitModelSerializerExtension extends JpsModelSerializerExtension {
+ @NotNull
+ @Override
+ public List<? extends JpsModulePropertiesSerializer<?>> getModulePropertiesSerializers() {
+ return Arrays.asList(new JpsPluginModulePropertiesSerializer());
+ }
+
+ @NotNull
+ @Override
+ public List<? extends JpsSdkPropertiesSerializer<?>> getSdkPropertiesSerializers() {
+ return Arrays.asList(new JpsIdeaSdkPropertiesSerializer());
+ }
+
+ private static class JpsIdeaSdkPropertiesSerializer extends JpsSdkPropertiesSerializer<JpsSimpleElement<JpsIdeaSdkProperties>> {
+ private static final String SANDBOX_HOME_FIELD = "mySandboxHome";
+ private static final String JDK_NAME_ATTRIBUTE = "sdk";
+
+ public JpsIdeaSdkPropertiesSerializer() {
+ super("IDEA JDK", JpsIdeaSdkType.INSTANCE);
+ }
+
+ @NotNull
+ @Override
+ public JpsSimpleElement<JpsIdeaSdkProperties> loadProperties(@Nullable Element propertiesElement) {
+ String sandboxHome = null;
+ String jdkName = null;
+ if (propertiesElement != null) {
+ sandboxHome = JDOMExternalizerUtil.readField(propertiesElement, SANDBOX_HOME_FIELD);
+ jdkName = propertiesElement.getAttributeValue(JDK_NAME_ATTRIBUTE);
+ }
+ return JpsElementFactory.getInstance().createSimpleElement(new JpsIdeaSdkProperties(sandboxHome, jdkName));
+ }
+
+ @Override
+ public void saveProperties(@NotNull JpsSimpleElement<JpsIdeaSdkProperties> properties, @NotNull Element element) {
+ JDOMExternalizerUtil.writeField(element, SANDBOX_HOME_FIELD, properties.getData().getSandboxHome());
+ element.setAttribute(JDK_NAME_ATTRIBUTE, properties.getData().getJdkName());
+ }
+ }
+
+ private static class JpsPluginModulePropertiesSerializer extends JpsModulePropertiesSerializer<JpsSimpleElement<JpsPluginModuleProperties>> {
+ private static final String URL_ATTRIBUTE = "url";
+ private static final String MANIFEST_ATTRIBUTE = "manifest";
+
+ private JpsPluginModulePropertiesSerializer() {
+ super(JpsPluginModuleType.INSTANCE, "PLUGIN_MODULE", "DevKit.ModuleBuildProperties");
+ }
+
+ @Override
+ public JpsSimpleElement<JpsPluginModuleProperties> loadProperties(@Nullable Element componentElement) {
+ String pluginXmlUrl = componentElement != null ? componentElement.getAttributeValue(URL_ATTRIBUTE) : null;
+ String manifestFileUrl = componentElement != null ? componentElement.getAttributeValue(MANIFEST_ATTRIBUTE) : null;
+ return JpsElementFactory.getInstance().createSimpleElement(new JpsPluginModuleProperties(pluginXmlUrl, manifestFileUrl));
+ }
+
+ @Override
+ public void saveProperties(@NotNull JpsSimpleElement<JpsPluginModuleProperties> element, @NotNull Element componentElement) {
+ String pluginXmlUrl = element.getData().getPluginXmlUrl();
+ if (pluginXmlUrl != null) {
+ componentElement.setAttribute(URL_ATTRIBUTE, pluginXmlUrl);
+ }
+ String manifestFileUrl = element.getData().getManifestFileUrl();
+ if (manifestFileUrl != null) {
+ componentElement.setAttribute(MANIFEST_ATTRIBUTE, manifestFileUrl);
+ }
+ }
+ }
+}
+
diff --git a/plugins/devkit/jps-plugin/testData/pluginProject/pluginProject.iml b/plugins/devkit/jps-plugin/testData/pluginProject/pluginProject.iml
new file mode 100644
index 0000000..953b0b8
--- /dev/null
+++ b/plugins/devkit/jps-plugin/testData/pluginProject/pluginProject.iml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="PLUGIN_MODULE" version="4">
+ <component name="DevKit.ModuleBuildProperties" url="file://$MODULE_DIR$/META-INF/plugin.xml" />
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ </content>
+ <orderEntry type="jdk" jdkName="IDEA plugin SDK" jdkType="IDEA JDK" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
+
diff --git a/plugins/devkit/jps-plugin/testData/pluginProject/pluginProject.ipr b/plugins/devkit/jps-plugin/testData/pluginProject/pluginProject.ipr
new file mode 100644
index 0000000..a2be79a
--- /dev/null
+++ b/plugins/devkit/jps-plugin/testData/pluginProject/pluginProject.ipr
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="CompilerConfiguration">
+ <option name="DEFAULT_COMPILER" value="Javac" />
+ <resourceExtensions />
+ <wildcardResourcePatterns>
+ <entry name="?*.properties" />
+ <entry name="?*.xml" />
+ <entry name="?*.gif" />
+ <entry name="?*.png" />
+ <entry name="?*.jpeg" />
+ <entry name="?*.jpg" />
+ <entry name="?*.html" />
+ <entry name="?*.dtd" />
+ <entry name="?*.tld" />
+ <entry name="?*.ftl" />
+ </wildcardResourcePatterns>
+ <annotationProcessing>
+ <profile default="true" name="Default" enabled="false">
+ <processorPath useClasspath="true" />
+ </profile>
+ </annotationProcessing>
+ </component>
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/pluginProject.iml" filepath="$PROJECT_DIR$/pluginProject.iml" />
+ </modules>
+ </component>
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="IDEA IU-111.277" project-jdk-type="IDEA JDK">
+ <output url="file://$PROJECT_DIR$/out" />
+ </component>
+</project>
+
diff --git a/plugins/devkit/jps-plugin/testSrc/org/jetbrains/jps/devkit/model/JpsPluginProjectSerializationTest.java b/plugins/devkit/jps-plugin/testSrc/org/jetbrains/jps/devkit/model/JpsPluginProjectSerializationTest.java
new file mode 100644
index 0000000..39de480
--- /dev/null
+++ b/plugins/devkit/jps-plugin/testSrc/org/jetbrains/jps/devkit/model/JpsPluginProjectSerializationTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.jps.devkit.model;
+
+import org.jetbrains.jps.model.JpsDummyElement;
+import org.jetbrains.jps.model.JpsElementFactory;
+import org.jetbrains.jps.model.JpsSimpleElement;
+import org.jetbrains.jps.model.java.JpsJavaSdkType;
+import org.jetbrains.jps.model.library.JpsTypedLibrary;
+import org.jetbrains.jps.model.library.sdk.JpsSdk;
+import org.jetbrains.jps.model.module.JpsModule;
+import org.jetbrains.jps.model.module.JpsTypedModule;
+import org.jetbrains.jps.model.serialization.JpsSerializationTestCase;
+
+/**
+ * @author nik
+ */
+public class JpsPluginProjectSerializationTest extends JpsSerializationTestCase {
+ public void testLoadProject() {
+ loadProject("plugins/devkit/jps-plugin/testData/pluginProject/pluginProject.ipr");
+ JpsModule module = assertOneElement(myProject.getModules());
+ assertEquals(JpsPluginModuleType.INSTANCE, module.getModuleType());
+ JpsTypedModule<JpsSimpleElement<JpsPluginModuleProperties>> pluginModule = module.asTyped(JpsPluginModuleType.INSTANCE);
+ assertNotNull(pluginModule);
+ String url = pluginModule.getProperties().getData().getPluginXmlUrl();
+ assertEquals(getUrl("META-INF/plugin.xml"), url);
+
+ JpsTypedLibrary<JpsSdk<JpsDummyElement>> javaSdk = myModel.getGlobal().addSdk("1.6", null, null, JpsJavaSdkType.INSTANCE);
+ JpsSimpleElement<JpsIdeaSdkProperties> properties =
+ JpsElementFactory.getInstance().createSimpleElement(new JpsIdeaSdkProperties(null, "1.6"));
+ JpsTypedLibrary<JpsSdk<JpsSimpleElement<JpsIdeaSdkProperties>>> pluginSdk = myModel.getGlobal()
+ .addSdk("IDEA plugin SDK", null, null, JpsIdeaSdkType.INSTANCE, properties);
+ assertSame(pluginSdk.getProperties(), module.getSdk(JpsIdeaSdkType.INSTANCE));
+ assertSame(javaSdk.getProperties(), module.getSdk(JpsJavaSdkType.INSTANCE));
+ }
+}
diff --git a/plugins/devkit/resources/META-INF/plugin.xml b/plugins/devkit/resources/META-INF/plugin.xml
new file mode 100644
index 0000000..3ef7a65
--- /dev/null
+++ b/plugins/devkit/resources/META-INF/plugin.xml
@@ -0,0 +1,141 @@
+<idea-plugin version="2">
+ <name>Plugin DevKit</name>
+ <id>DevKit</id>
+ <version>1.0</version>
+
+ <category>inspection</category>
+ <vendor>JetBrains</vendor>
+
+ <depends>com.intellij.properties</depends>
+
+ <resource-bundle>org.jetbrains.idea.devkit.DevKitBundle</resource-bundle>
+
+ <extensions defaultExtensionNs="com.intellij">
+ <errorHandler implementation="com.intellij.diagnostic.ITNReporter"/>
+
+ <junitPatcher implementation="org.jetbrains.idea.devkit.run.JUnitDevKitPatcher"/>
+ <antBuildGen implementation="org.jetbrains.idea.devkit.build.ant.ChunkBuildPluginExtension"/>
+ <compiler.buildParticipantProvider implementation="org.jetbrains.idea.devkit.build.PluginBuildParticipantProvider"/>
+ <compiler.buildTargetScopeProvider implementation="org.jetbrains.idea.devkit.build.PluginModuleBuildScopeProvider"/>
+
+ <dom.fileDescription implementation="org.jetbrains.idea.devkit.dom.impl.PluginXmlDomFileDescription"/>
+ <dom.extender domClass="org.jetbrains.idea.devkit.dom.Extensions"
+ extenderClass="org.jetbrains.idea.devkit.dom.impl.ExtensionDomExtender"/>
+ <dom.implementation interfaceClass="org.jetbrains.idea.devkit.dom.IdeaPlugin"
+ implementationClass="org.jetbrains.idea.devkit.dom.impl.IdeaPluginImpl"/>
+ <dom.implementation interfaceClass="org.jetbrains.idea.devkit.dom.Extension"
+ implementationClass="org.jetbrains.idea.devkit.dom.impl.ExtensionImpl"/>
+ <dom.implementation interfaceClass="org.jetbrains.idea.devkit.dom.Extensions"
+ implementationClass="org.jetbrains.idea.devkit.dom.impl.ExtensionsImpl"/>
+ <useScopeEnlarger implementation="org.jetbrains.idea.devkit.DevKitUseScopeEnlarger"/>
+
+ <configurationType implementation="org.jetbrains.idea.devkit.run.PluginConfigurationType"/>
+ <moduleType id="PLUGIN_MODULE" implementationClass="org.jetbrains.idea.devkit.module.PluginModuleType" classpathProvider="true"/>
+ <sdkType implementation="org.jetbrains.idea.devkit.projectRoots.IdeaJdk"/>
+ <compileServer.plugin classpath="jps/devkit-jps-plugin.jar"/>
+ <fileTemplateGroup implementation="org.jetbrains.idea.devkit.DevKitFileTemplatesFactory"/>
+ <documentationProvider implementation="org.jetbrains.idea.devkit.references.extensions.ExtensionPointQuickDocProvider"/>
+
+ <localInspection language="XML" shortName="PluginXmlValidity" displayName="Plugin.xml Validity"
+ bundle="org.jetbrains.idea.devkit.DevKitBundle"
+ groupKey="inspections.group.name" enabledByDefault="true" level="ERROR"
+ implementationClass="org.jetbrains.idea.devkit.inspections.PluginXmlDomInspection"/>
+ <localInspection language="JAVA" shortName="ComponentNotRegistered" bundle="org.jetbrains.idea.devkit.DevKitBundle"
+ key="inspections.component.not.registered.name" groupKey="inspections.group.name" enabledByDefault="true"
+ level="WARNING" implementationClass="org.jetbrains.idea.devkit.inspections.ComponentNotRegisteredInspection"/>
+ <localInspection language="JAVA" shortName="InspectionDescriptionNotFoundInspection" displayName="Inspection Description Checker"
+ bundle="org.jetbrains.idea.devkit.DevKitBundle"
+ groupKey="inspections.group.name" enabledByDefault="true" level="WARNING"
+ implementationClass="org.jetbrains.idea.devkit.inspections.InspectionDescriptionNotFoundInspection"/>
+ <localInspection language="JAVA" shortName="InspectionUsingGrayColors" displayName="Using new Color(a,a,a)"
+ bundle="org.jetbrains.idea.devkit.DevKitBundle"
+ groupKey="inspections.group.name" enabledByDefault="true" level="WARNING"
+ implementationClass="org.jetbrains.idea.devkit.inspections.UseGrayInspection"/>
+ <localInspection language="JAVA" shortName="UseJBColor" displayName="Use Darcula aware JBColor"
+ bundle="org.jetbrains.idea.devkit.DevKitBundle"
+ groupKey="inspections.group.name" enabledByDefault="true" level="WARNING"
+ implementationClass="org.jetbrains.idea.devkit.inspections.UseJBColorInspection"/>
+ <localInspection language="JAVA" shortName="IntentionDescriptionNotFoundInspection" displayName="Intention Description Checker"
+ bundle="org.jetbrains.idea.devkit.DevKitBundle"
+ groupKey="inspections.group.name" enabledByDefault="true" level="WARNING"
+ implementationClass="org.jetbrains.idea.devkit.inspections.IntentionDescriptionNotFoundInspection"/>
+ <localInspection shortName="ComponentRegistrationProblems"
+ bundle="org.jetbrains.idea.devkit.DevKitBundle" key="inspections.registration.problems.name"
+ groupKey="inspections.group.name"
+ enabledByDefault="true" level="ERROR"
+ implementationClass="org.jetbrains.idea.devkit.inspections.RegistrationProblemsInspection"/>
+ <localInspection language="JAVA" shortName="DialogTitleCapitalization"
+ bundle="org.jetbrains.idea.devkit.DevKitBundle"
+ groupKey="inspections.group.name"
+ displayName="Incorrect dialog title capitalization"
+ enabledByDefault="true"
+ level="WARNING"
+ implementationClass="org.jetbrains.idea.devkit.inspections.TitleCapitalizationInspection"/>
+ <localInspection language="XML" shortName="InspectionMappingConsistency"
+ bundle="org.jetbrains.idea.devkit.DevKitBundle"
+ groupKey="inspections.group.name"
+ displayName="<inspection> tag consistency"
+ enabledByDefault="true"
+ level="WARNING"
+ implementationClass="org.jetbrains.idea.devkit.inspections.InspectionMappingConsistencyInspection"/>
+
+ <moduleConfigurationEditorProvider implementation="org.jetbrains.idea.devkit.module.PluginModuleEditorsProvider"/>
+ <implicitUsageProvider implementation="org.jetbrains.idea.devkit.inspections.DevKitEntryPoints"/>
+ <psi.referenceContributor implementation="org.jetbrains.idea.devkit.dom.impl.InspectionsPropertiesReferenceProviderContributor"/>
+ <psi.referenceContributor implementation="org.jetbrains.idea.devkit.references.IconsReferencesContributor"/>
+ <referencesSearch implementation="org.jetbrains.idea.devkit.references.IconsReferencesContributor"/>
+ <unusedDeclarationFixProvider implementation="org.jetbrains.idea.devkit.inspections.quickfix.RegisterInspectionFixProvider"/>
+ </extensions>
+
+ <module-components>
+ <component>
+ <implementation-class>org.jetbrains.idea.devkit.build.PluginBuildConfiguration</implementation-class>
+ <option name="type" value="PLUGIN_MODULE"/>
+ </component>
+ </module-components>
+
+ <actions>
+ <group id="PluginDeployActions">
+ <action class="org.jetbrains.idea.devkit.build.PrepareToDeployAction" id="MakeJarAction"/>
+ <action class="org.jetbrains.idea.devkit.build.PrepareAllToDeployAction" id="MakeAllJarsAction"/>
+ <add-to-group anchor="after" group-id="ProjectViewPopupMenu" relative-to-action="Compile"/>
+ <add-to-group anchor="after" group-id="BuildMenu" relative-to-action="BuildJar"/>
+ </group>
+
+ <action class="org.jetbrains.idea.devkit.actions.NewApplicationComponentAction" text="Application Component"
+ id="NewApplicationComponent">
+ <add-to-group anchor="last" group-id="NewGroup"/>
+ </action>
+
+ <action class="org.jetbrains.idea.devkit.actions.NewProjectComponentAction" text="Project Component" id="NewProjectComponent">
+ <add-to-group anchor="last" group-id="NewGroup"/>
+ </action>
+
+ <action class="org.jetbrains.idea.devkit.actions.NewModuleComponentAction" text="Module Component" id="NewModuleComponent">
+ <add-to-group anchor="last" group-id="NewGroup"/>
+ </action>
+
+ <action class="org.jetbrains.idea.devkit.actions.NewActionAction" text="Action" id="NewAction">
+ <add-to-group anchor="last" group-id="NewGroup"/>
+ </action>
+
+ <action class="org.jetbrains.idea.devkit.actions.GenerateComponentExternalizationAction" text="Generate Externalization"
+ id="GenerateExternalization">
+ <add-to-group anchor="last" group-id="GenerateGroup"/>
+ </action>
+
+ <group id="Internal.DevKit" internal="true" text="DevKit" popup="true">
+ <action internal="true" class="org.jetbrains.idea.devkit.actions.ToggleHighlightingMarkupAction" text="Toggle Expected Highlighting Markup"
+ id="ToggleHighlightingMarkup"/>
+
+ <action internal="true" class="org.jetbrains.idea.devkit.actions.ShuffleNamesAction" text="Shuffle Names"
+ id="ShuffleNamesAction"/>
+
+ <action internal="true" class="org.jetbrains.idea.devkit.actions.ShowSerializedXmlAction" text="Show Serialized XML for Class"
+ id="ShowSerializedXml"/>
+ <add-to-group group-id="Internal" anchor="last"/>
+ </group>
+
+ </actions>
+
+</idea-plugin>
diff --git a/plugins/devkit/resources/add_sdk.png b/plugins/devkit/resources/add_sdk.png
new file mode 100644
index 0000000..749cf92
--- /dev/null
+++ b/plugins/devkit/resources/add_sdk.png
Binary files differ
diff --git a/plugins/devkit/resources/fileTemplates/j2ee/Action.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/Action.java.ft
new file mode 100644
index 0000000..13c7f48
--- /dev/null
+++ b/plugins/devkit/resources/fileTemplates/j2ee/Action.java.ft
@@ -0,0 +1,11 @@
+package ${PACKAGE_NAME};
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+
+#parse("File Header.java")
+public class ${NAME} extends AnAction {
+ public void actionPerformed(AnActionEvent e) {
+ // TODO: insert action logic here
+ }
+}
diff --git a/plugins/devkit/resources/fileTemplates/j2ee/Action.java.html b/plugins/devkit/resources/fileTemplates/j2ee/Action.java.html
new file mode 100644
index 0000000..77a42a6
--- /dev/null
+++ b/plugins/devkit/resources/fileTemplates/j2ee/Action.java.html
@@ -0,0 +1,11 @@
+<html>
+<body>
+<table border="0" cellpadding="2" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111">
+ <tr>
+ <td colspan="3"><font face="verdana" size="-1">This is a built-in template used each time you create
+ a new <b>IntelliJ IDEA</b> action.</font>
+ </td>
+ </tr>
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ApplicationComponent.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationComponent.java.ft
new file mode 100644
index 0000000..1302bba
--- /dev/null
+++ b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationComponent.java.ft
@@ -0,0 +1,23 @@
+package ${PACKAGE_NAME};
+
+import com.intellij.openapi.components.ApplicationComponent;
+import org.jetbrains.annotations.NotNull;
+
+#parse("File Header.java")
+public class ${NAME} implements ApplicationComponent {
+ public ${NAME}() {
+ }
+
+ public void initComponent() {
+ // TODO: insert component initialization logic here
+ }
+
+ public void disposeComponent() {
+ // TODO: insert component disposal logic here
+ }
+
+ @NotNull
+ public String getComponentName() {
+ return "${NAME}";
+ }
+}
diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ApplicationComponent.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationComponent.java.html
new file mode 100644
index 0000000..70dcf44
--- /dev/null
+++ b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationComponent.java.html
@@ -0,0 +1,11 @@
+<html>
+<body>
+<table border="0" cellpadding="2" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111">
+ <tr>
+ <td colspan="3"><font face="verdana" size="-1">This is a built-in template used each time you create
+ a new <b>IntelliJ IDEA</b> application component.</font>
+ </td>
+ </tr>
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/devkit/resources/fileTemplates/j2ee/InspectionDescription.html.ft b/plugins/devkit/resources/fileTemplates/j2ee/InspectionDescription.html.ft
new file mode 100644
index 0000000..942f318
--- /dev/null
+++ b/plugins/devkit/resources/fileTemplates/j2ee/InspectionDescription.html.ft
@@ -0,0 +1,7 @@
+<html>
+<body>
+Write your description here.
+<!-- tooltip end -->
+Text after this comment will not be shown in tooltips.
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/devkit/resources/fileTemplates/j2ee/InspectionDescription.html.html b/plugins/devkit/resources/fileTemplates/j2ee/InspectionDescription.html.html
new file mode 100644
index 0000000..b3588c7
--- /dev/null
+++ b/plugins/devkit/resources/fileTemplates/j2ee/InspectionDescription.html.html
@@ -0,0 +1,11 @@
+<html>
+<body>
+<table border="0" cellpadding="2" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111">
+ <tr>
+ <td colspan="3"><font face="verdana" size="-1">This is a built-in template used each time you create
+ a new description for a code inspection.</font>
+ </td>
+ </tr>
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ModuleComponent.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/ModuleComponent.java.ft
new file mode 100644
index 0000000..25c4120
--- /dev/null
+++ b/plugins/devkit/resources/fileTemplates/j2ee/ModuleComponent.java.ft
@@ -0,0 +1,37 @@
+package ${PACKAGE_NAME};
+
+import com.intellij.openapi.module.ModuleComponent;
+import com.intellij.openapi.module.Module;
+import org.jetbrains.annotations.NotNull;
+
+#parse("File Header.java")
+public class ${NAME} implements ModuleComponent {
+ public ${NAME}(Module module) {
+ }
+
+ public void initComponent() {
+ // TODO: insert component initialization logic here
+ }
+
+ public void disposeComponent() {
+ // TODO: insert component disposal logic here
+ }
+
+ @NotNull
+ public String getComponentName() {
+ return "${NAME}";
+ }
+
+ public void projectOpened() {
+ // called when project is opened
+ }
+
+ public void projectClosed() {
+ // called when project is being closed
+ }
+
+ public void moduleAdded() {
+ // Invoked when the module corresponding to this component instance has been completely
+ // loaded and added to the project.
+ }
+}
diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ModuleComponent.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ModuleComponent.java.html
new file mode 100644
index 0000000..1929e40
--- /dev/null
+++ b/plugins/devkit/resources/fileTemplates/j2ee/ModuleComponent.java.html
@@ -0,0 +1,11 @@
+<html>
+<body>
+<table border="0" cellpadding="2" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111">
+ <tr>
+ <td colspan="3"><font face="verdana" size="-1">This is a built-in template used each time you create
+ a new <b>IntelliJ IDEA</b> module component.</font>
+ </td>
+ </tr>
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ProjectComponent.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/ProjectComponent.java.ft
new file mode 100644
index 0000000..155dac7
--- /dev/null
+++ b/plugins/devkit/resources/fileTemplates/j2ee/ProjectComponent.java.ft
@@ -0,0 +1,32 @@
+package ${PACKAGE_NAME};
+
+import com.intellij.openapi.components.ProjectComponent;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+#parse("File Header.java")
+public class ${NAME} implements ProjectComponent {
+ public ${NAME}(Project project) {
+ }
+
+ public void initComponent() {
+ // TODO: insert component initialization logic here
+ }
+
+ public void disposeComponent() {
+ // TODO: insert component disposal logic here
+ }
+
+ @NotNull
+ public String getComponentName() {
+ return "${NAME}";
+ }
+
+ public void projectOpened() {
+ // called when project is opened
+ }
+
+ public void projectClosed() {
+ // called when project is being closed
+ }
+}
diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ProjectComponent.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ProjectComponent.java.html
new file mode 100644
index 0000000..b42f0ac
--- /dev/null
+++ b/plugins/devkit/resources/fileTemplates/j2ee/ProjectComponent.java.html
@@ -0,0 +1,11 @@
+<html>
+<body>
+<table border="0" cellpadding="2" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111">
+ <tr>
+ <td colspan="3"><font face="verdana" size="-1">This is a built-in template used each time you create
+ a new <b>IntelliJ IDEA</b> project component.</font>
+ </td>
+ </tr>
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/devkit/resources/fileTemplates/j2ee/plugin.xml.ft b/plugins/devkit/resources/fileTemplates/j2ee/plugin.xml.ft
new file mode 100644
index 0000000..71f115b
--- /dev/null
+++ b/plugins/devkit/resources/fileTemplates/j2ee/plugin.xml.ft
@@ -0,0 +1,42 @@
+<idea-plugin version="2">
+ <id>com.yourcompany.unique.plugin.id</id>
+ <name>Plugin display name here</name>
+ <version>1.0</version>
+ <vendor email="[email protected]" url="http://www.yourcompany.com">YourCompany</vendor>
+
+ <description><![CDATA[
+ Enter short description for your plugin here.<br>
+ <small>most HTML tags may be used</small>
+ ]]></description>
+
+ <change-notes><![CDATA[
+ Add change notes here.<br>
+ <small>most HTML tags may be used</small>
+ ]]>
+ </change-notes>
+
+ <!-- please see http://confluence.jetbrains.net/display/IDEADEV/Build+Number+Ranges for description -->
+ <idea-version since-build="107.105"/>
+
+ <!-- please see http://confluence.jetbrains.net/display/IDEADEV/Plugin+Compatibility+with+IntelliJ+Platform+Products
+ on how to target different products -->
+ <!-- uncomment to enable plugin in all products
+ <depends>com.intellij.modules.lang</depends>
+ -->
+
+ <application-components>
+ <!-- Add your application components here -->
+ </application-components>
+
+ <project-components>
+ <!-- Add your project components here -->
+ </project-components>
+
+ <actions>
+ <!-- Add your actions here -->
+ </actions>
+
+ <extensions defaultExtensionNs="com.intellij">
+ <!-- Add your extensions here -->
+ </extensions>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/resources/fileTemplates/j2ee/plugin.xml.html b/plugins/devkit/resources/fileTemplates/j2ee/plugin.xml.html
new file mode 100644
index 0000000..e1aea3b
--- /dev/null
+++ b/plugins/devkit/resources/fileTemplates/j2ee/plugin.xml.html
@@ -0,0 +1,11 @@
+<html>
+<body>
+<table border="0" cellpadding="2" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111">
+ <tr>
+ <td colspan="3"><font face="verdana" size="-1">This is a built-in template used each time you create
+ a new <b>IntelliJ IDEA</b> plugin module.</font>
+ </td>
+ </tr>
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/devkit/resources/icons/DevkitIcons.java b/plugins/devkit/resources/icons/DevkitIcons.java
new file mode 100644
index 0000000..1d5a572
--- /dev/null
+++ b/plugins/devkit/resources/icons/DevkitIcons.java
@@ -0,0 +1,20 @@
+package icons;
+
+import com.intellij.openapi.util.IconLoader;
+
+import javax.swing.*;
+
+/**
+ * NOTE THIS FILE IS AUTO-GENERATED by the build/scripts/icons.gant
+ * Don't repeat mistakes of others ;-)
+ */
+public class DevkitIcons {
+ private static Icon load(String path) {
+ return IconLoader.getIcon(path, DevkitIcons.class);
+ }
+
+ public static final Icon Add_sdk = load("/add_sdk.png"); // 16x16
+ public static final Icon New_html = load("/new_html.png"); // 16x16
+ public static final Icon Sdk_closed = load("/sdk_closed.png"); // 16x16
+ public static final Icon Sdk_open = load("/sdk_open.png"); // 16x16
+}
diff --git a/plugins/devkit/resources/inspectionDescriptions/ComponentNotRegistered.html b/plugins/devkit/resources/inspectionDescriptions/ComponentNotRegistered.html
new file mode 100644
index 0000000..71dbedd
--- /dev/null
+++ b/plugins/devkit/resources/inspectionDescriptions/ComponentNotRegistered.html
@@ -0,0 +1,14 @@
+<html>
+<body>
+This inspection detects plugin components and actions that are not (yet) registered in a plugin.xml
+descriptor and offers a QuickFix to register the component. This eases developing new components when
+making use of the "Create Class" intention and helps to keep track of potentially obsolete
+components.
+<p>
+ There's an option to turn off the check for Actions as they may be intentionally created and
+ registered dynamically. Note that the inspection can also be suppressed on a per-class level,
+ so it normally shouldn't be necessary to switch off the inspection completely. Especially
+ because the inspection ignores abstract classes and by default non-public classes as well.
+</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/devkit/resources/inspectionDescriptions/ComponentRegistrationProblems.html b/plugins/devkit/resources/inspectionDescriptions/ComponentRegistrationProblems.html
new file mode 100644
index 0000000..eeddcef
--- /dev/null
+++ b/plugins/devkit/resources/inspectionDescriptions/ComponentRegistrationProblems.html
@@ -0,0 +1,28 @@
+<html>
+<body>
+This inspection finds various problems about incorrect registration of plugin components
+(Components and Actions) both in Java code as well as the plugin.xml descriptor.
+
+<p>
+ Those problems include
+ <ul>
+ <li>Unresolved component/action implementation-classes (plugin.xml)</li>
+ <li>Unresolved component interface-classes (plugin.xml)</li>
+ <li>Component implementation-class is not assignable to its interface-class (plugin.xml)</li>
+ <li>Incorrect component registration, e.g. ProjectComponent is registered as Application Component
+ or the class doesn't implement any component interface at all (plugin.xml / Java Code)
+ </li>
+ <li>Incorrect class modifiers: Action classes must be public and must have a public
+ no-argument constructor. Additionally, Action and Component implementation classes
+ must not be abstract (plugin.xml / Java Code)
+ </li>
+ <li>Duplicate interface-class: A class may only be used once as interface-class (plugin.xml)</li>
+ </ul>
+</p>
+
+<p>
+ The inspection provides options to switch off the checks in plugin.xml and Java code. Checking
+ Action classes in Java can also be toggled separately.
+</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/devkit/resources/inspectionDescriptions/DialogTitleCapitalization.html b/plugins/devkit/resources/inspectionDescriptions/DialogTitleCapitalization.html
new file mode 100644
index 0000000..5aad538
--- /dev/null
+++ b/plugins/devkit/resources/inspectionDescriptions/DialogTitleCapitalization.html
@@ -0,0 +1,4 @@
+<html><body>
+This inspections checks that titles of dialogs implemented using the DialogWrapper class, as well as messages displayed using the Messages
+class, use title capitalization, which is required by most platform UI guidelines.
+</body></html>
\ No newline at end of file
diff --git a/plugins/devkit/resources/inspectionDescriptions/InspectionDescriptionNotFoundInspection.html b/plugins/devkit/resources/inspectionDescriptions/InspectionDescriptionNotFoundInspection.html
new file mode 100644
index 0000000..ccf1446
--- /dev/null
+++ b/plugins/devkit/resources/inspectionDescriptions/InspectionDescriptionNotFoundInspection.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+This inspection detects missing HTML-description for an inspection.
+</body>
+</html>
diff --git a/plugins/devkit/resources/inspectionDescriptions/InspectionMappingConsistency.html b/plugins/devkit/resources/inspectionDescriptions/InspectionMappingConsistency.html
new file mode 100644
index 0000000..2e2ee5d
--- /dev/null
+++ b/plugins/devkit/resources/inspectionDescriptions/InspectionMappingConsistency.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+Checks inspection mappings consistency.
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/devkit/resources/inspectionDescriptions/InspectionUsingGrayColors.html b/plugins/devkit/resources/inspectionDescriptions/InspectionUsingGrayColors.html
new file mode 100644
index 0000000..7e596819
--- /dev/null
+++ b/plugins/devkit/resources/inspectionDescriptions/InspectionUsingGrayColors.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+Use cache (com.intellij.ui.Gray) for gray colors.
+</body>
+</html>
diff --git a/plugins/devkit/resources/inspectionDescriptions/IntentionDescriptionNotFoundInspection.html b/plugins/devkit/resources/inspectionDescriptions/IntentionDescriptionNotFoundInspection.html
new file mode 100644
index 0000000..2252353
--- /dev/null
+++ b/plugins/devkit/resources/inspectionDescriptions/IntentionDescriptionNotFoundInspection.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+Checks if intention has a description.
+</body>
+</html>
diff --git a/plugins/devkit/resources/inspectionDescriptions/PluginXmlValidity.html b/plugins/devkit/resources/inspectionDescriptions/PluginXmlValidity.html
new file mode 100644
index 0000000..f1fd248
--- /dev/null
+++ b/plugins/devkit/resources/inspectionDescriptions/PluginXmlValidity.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+This inspection finds various problems in plugin.xml.
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/devkit/resources/inspectionDescriptions/UseJBColor.html b/plugins/devkit/resources/inspectionDescriptions/UseJBColor.html
new file mode 100644
index 0000000..3a1ccf9
--- /dev/null
+++ b/plugins/devkit/resources/inspectionDescriptions/UseJBColor.html
@@ -0,0 +1,7 @@
+<html>
+<body>
+Use dark theme aware JBColor instead of java.awt.Color
+<!-- tooltip end -->
+Specify regular and dark theme (Darcula) colors in JBColor constructor
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/devkit/resources/new_html.png b/plugins/devkit/resources/new_html.png
new file mode 100644
index 0000000..12e6376
--- /dev/null
+++ b/plugins/devkit/resources/new_html.png
Binary files differ
diff --git a/plugins/devkit/resources/sdk_closed.png b/plugins/devkit/resources/sdk_closed.png
new file mode 100644
index 0000000..56dd0bd
--- /dev/null
+++ b/plugins/devkit/resources/sdk_closed.png
Binary files differ
diff --git a/plugins/devkit/resources/sdk_open.png b/plugins/devkit/resources/sdk_open.png
new file mode 100644
index 0000000..0e72923
--- /dev/null
+++ b/plugins/devkit/resources/sdk_open.png
Binary files differ
diff --git a/plugins/devkit/src/DevKitBundle.java b/plugins/devkit/src/DevKitBundle.java
new file mode 100644
index 0000000..d507273
--- /dev/null
+++ b/plugins/devkit/src/DevKitBundle.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit;
+
+import com.intellij.CommonBundle;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.PropertyKey;
+
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.util.ResourceBundle;
+
+/**
+ * User: anna
+ * Date: Aug 11, 2005
+ */
+public class DevKitBundle {
+ private static Reference<ResourceBundle> ourBundle;
+
+ @NonNls private static final String BUNDLE = "org.jetbrains.idea.devkit.DevKitBundle";
+
+ private DevKitBundle() {
+ }
+
+ public static String message(@PropertyKey(resourceBundle = BUNDLE)String key, Object... params) {
+ return CommonBundle.message(getBundle(), key, params);
+ }
+
+ private static ResourceBundle getBundle() {
+ ResourceBundle bundle = null;
+ if (ourBundle != null) bundle = ourBundle.get();
+ if (bundle == null) {
+ bundle = ResourceBundle.getBundle(BUNDLE);
+ ourBundle = new SoftReference<ResourceBundle>(bundle);
+ }
+ return bundle;
+ }
+}
diff --git a/plugins/devkit/src/DevKitBundle.properties b/plugins/devkit/src/DevKitBundle.properties
new file mode 100644
index 0000000..c17b1f5
--- /dev/null
+++ b/plugins/devkit/src/DevKitBundle.properties
@@ -0,0 +1,162 @@
+#module type
+module.title=IntelliJ IDEA Plugin
+module.description=Plugin modules are used for development of <b>IntelliJ IDEA Plugins</b>. They support <b>IntelliJ IDEA Plugin SDK</b> configuration and \
+ <b>Run Configuration</b> for running a dedicated <b>IntelliJ IDEA</b> instance for plugin debugging.
+plugin.descriptor=IntelliJ IDEA Plugin Descriptor
+
+#Module Editor - Deployment
+deployment.title=Plugin Deployment
+deployment.cleanup=Clean up {0} directory
+deployment.directory.location=Select {0} Directory Location
+deployment.view.select=Select {0}
+manifest.settings=Manifest Settings
+manifest.selection=Selected {0} will be included in resulting distribution
+manifest.use.user.defined=Use user manifest:
+deployment.view.delete=Delete {0}?
+deployment.view.meta-inf.label=Path to {0}
+suggest.to.delete=Do you want to delete ''{0}''?
+vm.parameters=&VM Options
+program.parameters=&Program Arguments
+
+#Prepare for deployment action
+select.plugin.modules.title=Select modules
+select.plugin.modules.description=Select modules to be prepared for deployment
+prepare.for.deployment.common=Preparing For Deployment
+prepare.for.deployment=Prepare Plugin Module ''{0}'' For Deployment
+prepare.for.deployment.all=Prepare All Plugin Modules For Deployment
+saved.message.common={0} will be saved in selected directory
+saved.message= {0, choice, 1#Zip|2#Jar} for module ''{1}'' was saved to {2}
+success.deployment.message=Plugin Module ''{0}'' Successfully Prepared For Deployment
+success.deployment.message.all=All Plugins Successfully Prepared For Deployment
+
+##Idea Sdk
+sdk.title=IntelliJ IDEA Plugin SDK
+sandbox.specification=Please configure the sandbox
+sandbox.home=Sandbox Home
+sandbox.home.label=Sandbox Home:
+sandbox.no.configured=No sandbox specified for idea jdk
+sandbox.purpose=Browse folder to put config, system and plugins for target IDEA
+
+#errors
+error.occurred=Error Occurred
+jdk.no.specified=No jdk specified for plugin module ''{0}''
+jdk.type.incorrect=Wrong jdk type for plugin module ''{0}''.
+jdk.type.incorrect.common=Wrong jdk type for plugin module
+incorrect.dependency.non-plugin-module=The non-plugin module ''{0}'' cannot depend on plugin module ''{1}''.
+incorrect.dependency.not-declared=The plugin module ''{0}'' doesn''t declare the dependency on ''{1}'' in its plugin.xml.
+error.file.not.found=File not found
+error.file.not.found.message=File ''{0}'' not found
+error.no.plugin.xml=No plugin.xml file found
+error.plugin.xml.readonly=The plugin.xml file is read-only
+
+#run configurations
+run.configuration.classpath.from.module.choose=Use Classpath and JDK from Module:
+run.configuration.no.module.specified=No plugin module specified for configuration
+run.configuration.title=Plugin
+run.configuration.type.description=Plugin Sandbox Environment
+idea.log.tab.title=IDEA LOG
+
+#Misc
+info.message=Info
+create.smth=Create {0}
+show.smth=&Show {0}
+presentable.plugin.module.name=Plugin Module ''{0}''
+
+action.MakeJarAction.text=Prepare To Deploy
+action.MakeAllJarsAction.text=Prepare All Plugins To Deploy
+plugin.DevKit.description=IntelliJ IDEA plugins development kit. Adds dedicated Plugin module type to IntelliJ IDEA project.\
+ Allows running/debugging IDEA plugins through Plugin-oriented Run/Debug Configuration
+
+dont.add.idea.libs.to.classpath=IDEA-related libraries ({0}) must not be added to the module classpath. Please add them to the IDEA-JDK instead.
+new.action.id=&Action ID:
+new.action.description=&Description:
+new.action.class.name=&Class Name:
+new.action.text=&Name:
+new.action.add.to.group=Add to Group
+new.action.group.actions=Act&ions:
+new.action.group.anchor=Anchor:
+new.action.group.anchor.first=&First
+new.action.group.anchor.=&Last
+new.action.group.anchor.before=&Before
+new.action.group.anchor.after=Af&ter
+new.action.group.groups=&Groups:
+new.action.keyboard.shortcuts=Keyboard Shortcuts
+new.action.keyboard.first=First:
+new.action.keyboard.second=Second:
+new.action.keyboard.clear=X
+new.action.keyboard.clear.tooltip=Clear shortcut
+command.implement.externalizable=Implement Externalizable
+new.menu.action.text=Action
+new.menu.action.description=Create New Action
+new.action.error=Cannot create action
+new.action.command=Create Action
+new.action.action.name=Creating new action: {0}.{1}
+new.action.dialog.title=New Action
+new.menu.application.component.text=Application Component
+new.menu.application.component.description=Create New Application Component
+new.application.component.error=Cannot create application component
+new.application.component.command=Create Application Component
+new.application.component.prompt.title=New Application Component
+new.application.component.prompt=Enter new application component name:
+new.application.component.action.name=Creating new application component: {0}.{1}
+new.menu.module.component.text=Module Component
+new.menu.module.component.description=Create New Module Component
+new.module.component.error=Cannot create module component
+new.module.component.command=Create Module Component
+new.module.component.prompt.title=New Module Component
+new.module.component.prompt=Enter new module component name:
+new.module.component.action.name=Creating new module component: {0}.{1}
+new.menu.project.component.text=Project Component
+new.menu.project.component.description=Create New Project Component
+new.project.component.error=Cannot create project component
+new.project.component.command=Create Project Component
+new.project.component.prompt.title=New Project Component
+new.project.component.prompt=Enter new project component name:
+new.project.component.action.name=Creating new project component: {0}.{1}
+
+select.plugin.modules.to.patch=Select Plugin Modules to Patch
+
+keyword.extend=extend
+keyword.implement=implement
+class.action=action
+class.interface=interface
+class.implementation=implementation
+
+inspections.group.name=Plugin DevKit
+inspections.registration.problems.name=Component type mismatch
+inspections.registration.problems.option.check.plugin.xml=Check Plugin Descriptor (plugin.xml)
+inspections.registration.problems.option.check.java.actions=Check Java Actions
+inspections.registration.problems.option.check.java.code=Check Java Code
+inspections.registration.problems.quickfix.read-only=Class ''{0}'' is read-only
+inspections.registration.problems.quickfix.make.public=Make {0} public
+inspections.registration.problems.quickfix.create.constructor=Create no-argument constructor
+
+inspections.registration.problems.incompatible.message=According to its registration in plugin.xml, the class should {0} ''{1}''
+inspections.registration.problems.abstract=Plugin component class must not be abstract
+inspections.registration.problems.missing.noarg.ctor=Action class must have a no-argument constructor
+
+inspections.registration.problems.missing.implementation.class=Missing implementation-class
+inspections.registration.problems.cannot.resolve.class=Cannot resolve {0} class
+inspections.registration.problems.component.should.implement=Component class must implement ''{0}''
+inspections.registration.problems.component.incompatible.interface=Component class is not assignable to its interface-class ''{0}''
+inspections.registration.problems.component.duplicate.interface=Multiple components with the same interface-class are not allowed
+inspections.registration.problems.action.incompatible.class=Action class must extend ''{0}''
+
+inspections.component.not.registered.name=Component/Action not registered
+inspections.component.not.registered.message={0} is not registered in plugin.xml
+inspections.component.not.registered.option.check.actions=Check Actions
+inspections.component.not.registered.option.ignore.non.public=Ignore non-public classes
+inspections.component.not.registered.quickfix.family=Register Component
+inspections.component.not.registered.quickfix.name=Register {0}
+inspections.component.not.registered.quickfix.error=Cannot Register {0}
+InspectionUseGrayColor=Use Gray
+ant.build.jar.comment=Build archive for plugin ''{0}''
+ant.build.jar.description=Build plugin archive for module ''{0}''
+project.title=Plugin Project
+no.java.sdk.for.idea.sdk.found=No Java SDK of appropriate version found. In addition to the IDEA Plugin SDK, you need to define a JDK with the same Java version ({0}).
+no.idea.sdk.version.found=Failed to detect JDK version required for IDEA Plugin SDK.
+group.PluginDeployActions.text=Plugin Deployment Actions
+
+error.cannot.resolve.plugin=Cannot resolve plugin {0}
+create.description.file=Create Description File
+select.target.location.of.description=Select target location of {0}
diff --git a/plugins/devkit/src/DevKitFileTemplatesFactory.java b/plugins/devkit/src/DevKitFileTemplatesFactory.java
new file mode 100644
index 0000000..b31e543
--- /dev/null
+++ b/plugins/devkit/src/DevKitFileTemplatesFactory.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.fileTemplates.FileTemplateDescriptor;
+import com.intellij.ide.fileTemplates.FileTemplateGroupDescriptor;
+import com.intellij.ide.fileTemplates.FileTemplateGroupDescriptorFactory;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+
+public class DevKitFileTemplatesFactory implements FileTemplateGroupDescriptorFactory {
+
+ public FileTemplateGroupDescriptor getFileTemplatesDescriptor() {
+ FileTemplateGroupDescriptor descriptor = new FileTemplateGroupDescriptor(DevKitBundle.message("plugin.descriptor"),
+ AllIcons.Nodes.Plugin);
+ descriptor.addTemplate(new FileTemplateDescriptor("plugin.xml", StdFileTypes.XML.getIcon()));
+ descriptor.addTemplate(new FileTemplateDescriptor("ProjectComponent.java", StdFileTypes.JAVA.getIcon()));
+ descriptor.addTemplate(new FileTemplateDescriptor("ApplicationComponent.java", StdFileTypes.JAVA.getIcon()));
+ descriptor.addTemplate(new FileTemplateDescriptor("ModuleComponent.java", StdFileTypes.JAVA.getIcon()));
+ descriptor.addTemplate(new FileTemplateDescriptor("Action.java", StdFileTypes.JAVA.getIcon()));
+ descriptor.addTemplate(new FileTemplateDescriptor("InspectionDescription.html", StdFileTypes.HTML.getIcon()));
+ return descriptor;
+ }
+
+}
diff --git a/plugins/devkit/src/DevKitUseScopeEnlarger.java b/plugins/devkit/src/DevKitUseScopeEnlarger.java
new file mode 100644
index 0000000..f34c40e
--- /dev/null
+++ b/plugins/devkit/src/DevKitUseScopeEnlarger.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2000-2011 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit;
+
+import com.intellij.pom.PomTarget;
+import com.intellij.pom.PomTargetPsiElement;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.search.SearchScope;
+import com.intellij.psi.search.UseScopeEnlarger;
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.DomTarget;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.dom.ExtensionPoint;
+
+/**
+ * @author peter
+ */
+public class DevKitUseScopeEnlarger extends UseScopeEnlarger {
+ @Override
+ public SearchScope getAdditionalUseScope(@NotNull PsiElement element) {
+ if (element instanceof PomTargetPsiElement) {
+ PomTarget target = ((PomTargetPsiElement)element).getTarget();
+ if (target instanceof DomTarget) {
+ DomElement domElement = ((DomTarget)target).getDomElement();
+ if (domElement instanceof ExtensionPoint) {
+ return GlobalSearchScope.allScope(element.getProject());
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/plugins/devkit/src/actions/GenerateClassAndPatchPluginXmlActionBase.java b/plugins/devkit/src/actions/GenerateClassAndPatchPluginXmlActionBase.java
new file mode 100644
index 0000000..322316a
--- /dev/null
+++ b/plugins/devkit/src/actions/GenerateClassAndPatchPluginXmlActionBase.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.actions;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.util.ComponentType;
+
+import javax.swing.*;
+
+/**
+ * @author max
+ */
+public abstract class GenerateClassAndPatchPluginXmlActionBase extends GeneratePluginClassAction {
+ public GenerateClassAndPatchPluginXmlActionBase(String text, String description, @Nullable Icon icon) {
+ super(text, description, icon);
+ }
+
+ protected abstract String getClassNamePrompt();
+ protected abstract String getClassNamePromptTitle();
+
+ protected PsiElement[] invokeDialogImpl(Project project, PsiDirectory directory) {
+ MyInputValidator validator = new MyInputValidator(project, directory);
+ Messages.showInputDialog(project, getClassNamePrompt(), getClassNamePromptTitle(), Messages.getQuestionIcon(), "", validator);
+ return validator.getCreatedElements();
+ }
+
+ protected abstract ComponentType getComponentType();
+
+ public void patchPluginXml(XmlFile pluginXml, PsiClass klass) throws IncorrectOperationException {
+ getComponentType().patchPluginXml(pluginXml, klass);
+ }
+}
diff --git a/plugins/devkit/src/actions/GenerateComponentExternalizationAction.java b/plugins/devkit/src/actions/GenerateComponentExternalizationAction.java
new file mode 100644
index 0000000..9b5bee4
--- /dev/null
+++ b/plugins/devkit/src/actions/GenerateComponentExternalizationAction.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.actions;
+
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.components.StoragePathMacros;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.*;
+import com.intellij.psi.codeStyle.CodeStyleManager;
+import com.intellij.psi.codeStyle.JavaCodeStyleManager;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.DevKitBundle;
+
+/**
+ * @author max
+ */
+public class GenerateComponentExternalizationAction extends AnAction {
+ private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.devkit.actions.GenerateComponentExternalizationAction");
+
+ @NonNls private final static String BASE_COMPONENT = "com.intellij.openapi.components.BaseComponent";
+ @NonNls private final static String PERSISTENCE_STATE_COMPONENT = "com.intellij.openapi.components.PersistentStateComponent";
+ @NonNls private final static String STATE = "com.intellij.openapi.components.State";
+ @NonNls private final static String STORAGE = "com.intellij.openapi.components.Storage";
+
+ public void actionPerformed(AnActionEvent e) {
+ final PsiClass target = getComponentInContext(e.getDataContext());
+ assert target != null;
+
+ final PsiElementFactory factory = JavaPsiFacade.getInstance(target.getProject()).getElementFactory();
+ final CodeStyleManager formatter = CodeStyleManager.getInstance(target.getManager().getProject());
+ final JavaCodeStyleManager styler = JavaCodeStyleManager.getInstance(target.getProject());
+ final String qualifiedName = target.getQualifiedName();
+ Runnable runnable = new Runnable() {
+ public void run() {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ public void run() {
+ try {
+ final PsiReferenceList implList = target.getImplementsList();
+ assert implList != null;
+ final PsiJavaCodeReferenceElement referenceElement =
+ factory.createReferenceFromText(PERSISTENCE_STATE_COMPONENT + "<" + qualifiedName + ">", target);
+ implList.add(styler.shortenClassReferences(referenceElement.copy()));
+ PsiMethod read = factory.createMethodFromText(
+ "public void loadState(" + qualifiedName + " state) {\n" +
+ " com.intellij.util.xmlb.XmlSerializerUtil.copyBean(state, this);\n" +
+ "}",
+ target
+ );
+
+ read = (PsiMethod)formatter.reformat(target.add(read));
+ styler.shortenClassReferences(read);
+
+ PsiMethod write = factory.createMethodFromText(
+ "public " + qualifiedName + " getState() {\n" +
+ " return this;\n" +
+ "}\n",
+ target
+ );
+ write = (PsiMethod)formatter.reformat(target.add(write));
+ styler.shortenClassReferences(write);
+
+ PsiAnnotation annotation = target.getModifierList().addAnnotation(STATE);
+
+ annotation = (PsiAnnotation)formatter.reformat(annotation.replace(
+ factory.createAnnotationFromText("@" + STATE +
+ "(name = \"" + qualifiedName + "\", " +
+ "storages = {@" + STORAGE + "(file = \"" + StoragePathMacros.WORKSPACE_FILE + "\"\n )})",
+ target)));
+ styler.shortenClassReferences(annotation);
+ }
+ catch (IncorrectOperationException e1) {
+ LOG.error(e1);
+ }
+ }
+ });
+ }
+ };
+
+ CommandProcessor.getInstance().executeCommand(target.getProject(), runnable,
+ DevKitBundle.message("command.implement.externalizable"), null);
+ }
+
+ @Nullable
+ private PsiClass getComponentInContext(DataContext context) {
+ Editor editor = PlatformDataKeys.EDITOR.getData(context);
+ Project project = PlatformDataKeys.PROJECT.getData(context);
+ if (editor == null || project == null) return null;
+
+ PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
+
+ PsiFile file = LangDataKeys.PSI_FILE.getData(context);
+ if (file == null) return null;
+
+ PsiClass contextClass = PsiTreeUtil.findElementOfClassAtOffset(file, editor.getCaretModel().getOffset(), PsiClass.class, false);
+ if (contextClass == null || contextClass.isEnum() || contextClass.isInterface() || contextClass instanceof PsiAnonymousClass) {
+ return null;
+ }
+
+ PsiClass componentClass = JavaPsiFacade.getInstance(file.getProject()).findClass(BASE_COMPONENT, file.getResolveScope());
+ if (componentClass == null || !contextClass.isInheritor(componentClass, true)) return null;
+
+ PsiClass externClass = JavaPsiFacade.getInstance(file.getProject()).findClass(PERSISTENCE_STATE_COMPONENT, file.getResolveScope());
+ if (externClass == null || contextClass.isInheritor(externClass, true)) return null;
+
+
+ return contextClass;
+ }
+
+ public void update(AnActionEvent e) {
+ super.update(e);
+ final PsiClass target = getComponentInContext(e.getDataContext());
+
+ final Presentation presentation = e.getPresentation();
+ presentation.setEnabled(target != null);
+ presentation.setVisible(target != null);
+ }
+}
+
diff --git a/plugins/devkit/src/actions/GeneratePluginClassAction.java b/plugins/devkit/src/actions/GeneratePluginClassAction.java
new file mode 100644
index 0000000..6ee75b2
--- /dev/null
+++ b/plugins/devkit/src/actions/GeneratePluginClassAction.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.actions;
+
+import com.intellij.ide.IdeView;
+import com.intellij.ide.actions.CreateElementActionBase;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.JavaDirectoryService;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.module.PluginModuleType;
+import org.jetbrains.idea.devkit.util.ChooseModulesDialog;
+import org.jetbrains.idea.devkit.util.DescriptorUtil;
+
+import javax.swing.*;
+import java.util.*;
+
+/**
+ * @author yole
+ */
+public abstract class GeneratePluginClassAction extends CreateElementActionBase implements DescriptorUtil.Patcher {
+ protected final Set<XmlFile> myFilesToPatch = new HashSet<XmlFile>();
+
+ // length == 1 is important to make MyInputValidator close the dialog when
+ // module selection is canceled. That's some weird interface actually...
+ private static final PsiElement[] CANCELED = new PsiElement[1];
+
+ public GeneratePluginClassAction(String text, String description, @Nullable Icon icon) {
+ super(text, description, icon);
+ }
+
+ @NotNull protected final PsiElement[] invokeDialog(Project project, PsiDirectory directory) {
+ try {
+ final PsiElement[] psiElements = invokeDialogImpl(project, directory);
+ return psiElements == CANCELED ? PsiElement.EMPTY_ARRAY : psiElements;
+ } finally {
+ myFilesToPatch.clear();
+ }
+ }
+
+ protected abstract PsiElement[] invokeDialogImpl(Project project, PsiDirectory directory);
+
+ private void addPluginModule(Module module) {
+ final XmlFile pluginXml = PluginModuleType.getPluginXml(module);
+ if (pluginXml != null) myFilesToPatch.add(pluginXml);
+ }
+
+ public void update(final AnActionEvent e) {
+ super.update(e);
+ final Presentation presentation = e.getPresentation();
+ if (presentation.isEnabled()) {
+ final DataContext context = e.getDataContext();
+ Module module = LangDataKeys.MODULE.getData(context);
+ if (module == null || !PluginModuleType.isPluginModuleOrDependency(module)) {
+ presentation.setEnabled(false);
+ presentation.setVisible(false);
+ }
+ final IdeView view = LangDataKeys.IDE_VIEW.getData(e.getDataContext());
+ final Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
+ if (view != null && project != null) {
+ // from com.intellij.ide.actions.CreateClassAction.update()
+ ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(project).getFileIndex();
+ PsiDirectory[] dirs = view.getDirectories();
+ for (PsiDirectory dir : dirs) {
+ if (projectFileIndex.isInSourceContent(dir.getVirtualFile()) && JavaDirectoryService.getInstance().getPackage(dir) != null) {
+ return;
+ }
+ }
+
+ presentation.setEnabled(false);
+ presentation.setVisible(false);
+ }
+ }
+ }
+
+ @Nullable
+ protected static Module getModule(PsiDirectory dir) {
+ Project project = dir.getProject();
+ final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
+
+ final VirtualFile vFile = dir.getVirtualFile();
+ if (fileIndex.isInLibrarySource(vFile) || fileIndex.isInLibraryClasses(vFile)) {
+ final List<OrderEntry> orderEntries = fileIndex.getOrderEntriesForFile(vFile);
+ if (orderEntries.isEmpty()) {
+ return null;
+ }
+ Set<Module> modules = new HashSet<Module>();
+ for (OrderEntry orderEntry : orderEntries) {
+ modules.add(orderEntry.getOwnerModule());
+ }
+ final Module[] candidates = modules.toArray(new Module[modules.size()]);
+ Arrays.sort(candidates, ModuleManager.getInstance(project).moduleDependencyComparator());
+ return candidates[0];
+ }
+ return fileIndex.getModuleForFile(vFile);
+ }
+
+ @NotNull
+ protected PsiElement[] create(String newName, PsiDirectory directory) throws Exception {
+ final Project project = directory.getProject();
+ final Module module = getModule(directory);
+
+ if (module != null) {
+ if (ModuleType.get(module) == PluginModuleType.getInstance()) {
+ addPluginModule(module);
+ }
+ else {
+ final List<Module> candidateModules = PluginModuleType.getCandidateModules(module);
+ final Iterator<Module> it = candidateModules.iterator();
+ while (it.hasNext()) {
+ Module m = it.next();
+ if (PluginModuleType.getPluginXml(m) == null) it.remove();
+ }
+
+ if (candidateModules.size() == 1) {
+ addPluginModule(candidateModules.get(0));
+ }
+ else {
+ final ChooseModulesDialog dialog = new ChooseModulesDialog(project, candidateModules, getTemplatePresentation().getDescription());
+ dialog.show();
+ if (!dialog.isOK()) {
+ // create() should return CANCELED now
+ return CANCELED;
+ }
+ else {
+ final List<Module> modules = dialog.getSelectedModules();
+ for (Module m : modules) {
+ addPluginModule(m);
+ }
+ }
+ }
+ }
+ }
+
+ if (myFilesToPatch.size() == 0) {
+ throw new IncorrectOperationException(DevKitBundle.message("error.no.plugin.xml"));
+ }
+ if (myFilesToPatch.size() == 0) {
+ // user canceled module selection
+ return CANCELED;
+ }
+
+ final PsiClass klass = JavaDirectoryService.getInstance().createClass(directory, newName, getClassTemplateName());
+
+ DescriptorUtil.patchPluginXml(this, klass, myFilesToPatch.toArray(new XmlFile[myFilesToPatch.size()]));
+
+ return new PsiElement[] {klass};
+ }
+
+ @NonNls protected abstract String getClassTemplateName();
+}
diff --git a/plugins/devkit/src/actions/NewActionAction.java b/plugins/devkit/src/actions/NewActionAction.java
new file mode 100644
index 0000000..3beb149
--- /dev/null
+++ b/plugins/devkit/src/actions/NewActionAction.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.actions;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.util.ActionType;
+
+/**
+ * @author yole
+ */
+public class NewActionAction extends GeneratePluginClassAction {
+ private NewActionDialog myDialog;
+
+ public NewActionAction() {
+ super(DevKitBundle.message("new.menu.action.text"), DevKitBundle.message("new.menu.action.description"), null);
+ }
+
+ protected PsiElement[] invokeDialogImpl(Project project, PsiDirectory directory) {
+ myDialog = new NewActionDialog(project);
+ myDialog.show();
+ if (myDialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) {
+ final MyInputValidator validator = new MyInputValidator(project, directory);
+ // this actually runs the action to create the class from template
+ validator.canClose(myDialog.getActionName());
+ myDialog = null;
+ return validator.getCreatedElements();
+ }
+ myDialog = null;
+ return PsiElement.EMPTY_ARRAY;
+ }
+
+ protected String getClassTemplateName() {
+ return "Action.java";
+ }
+
+ public void patchPluginXml(final XmlFile pluginXml, final PsiClass klass) throws IncorrectOperationException {
+ ActionType.ACTION.patchPluginXml(pluginXml, klass, myDialog);
+ }
+
+ protected String getErrorTitle() {
+ return DevKitBundle.message("new.action.error");
+ }
+
+ protected String getCommandName() {
+ return DevKitBundle.message("new.action.command");
+ }
+
+ protected String getActionName(PsiDirectory directory, String newName) {
+ return DevKitBundle.message("new.action.action.name", directory, newName);
+ }
+}
diff --git a/plugins/devkit/src/actions/NewActionDialog.java b/plugins/devkit/src/actions/NewActionDialog.java
new file mode 100644
index 0000000..faa1db5
--- /dev/null
+++ b/plugins/devkit/src/actions/NewActionDialog.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.actions;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.actionSystem.ActionGroup;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.keymap.KeymapUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiClass;
+import com.intellij.ui.ColoredListCellRenderer;
+import com.intellij.ui.ListSpeedSearch;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.Function;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.util.ActionData;
+import org.jetbrains.idea.devkit.util.ActionType;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author yole
+ */
+public class NewActionDialog extends DialogWrapper implements ActionData {
+ private JPanel myRootPanel;
+ private JList myGroupList;
+ private JList myActionList;
+ private JTextField myActionNameEdit;
+ private JTextField myActionIdEdit;
+ private JTextField myActionTextEdit;
+ private JTextField myActionDescriptionEdit;
+ private JRadioButton myAnchorFirstRadio;
+ private JRadioButton myAnchorLastRadio;
+ private JRadioButton myAnchorBeforeRadio;
+ private JRadioButton myAnchorAfterRadio;
+ private JPanel myShortcutPanel;
+ private JPanel myFirstKeystrokeEditPlaceholder;
+ private JPanel mySecondKeystrokeEditPlaceholder;
+ private JButton myClearFirstKeystroke;
+ private JButton myClearSecondKeystroke;
+ private ShortcutTextField myFirstKeystrokeEdit;
+ private ShortcutTextField mySecondKeystrokeEdit;
+ private TextFieldWithBrowseButton myIconEdit;
+ private Project myProject;
+ private ButtonGroup myAnchorButtonGroup;
+
+ public NewActionDialog(PsiClass actionClass) {
+ this(actionClass.getProject());
+
+ myActionNameEdit.setText(actionClass.getQualifiedName());
+ myActionNameEdit.setEditable(false);
+ myActionIdEdit.setText(actionClass.getQualifiedName());
+ if (ActionType.GROUP.isOfType(actionClass)) {
+ myShortcutPanel.setVisible(false);
+ }
+ }
+
+ protected NewActionDialog(final Project project) {
+ super(project, false);
+ myProject = project;
+ init();
+ setTitle(DevKitBundle.message("new.action.dialog.title"));
+ final ActionManager actionManager = ActionManager.getInstance();
+ final String[] actionIds = actionManager.getActionIds("");
+ Arrays.sort(actionIds);
+ final List<ActionGroup> actionGroups = new ArrayList<ActionGroup>();
+ for(String actionId: actionIds) {
+ if (actionManager.isGroup(actionId)) {
+ final AnAction anAction = actionManager.getAction(actionId);
+ if (anAction instanceof DefaultActionGroup) {
+ actionGroups.add((ActionGroup) anAction);
+ }
+ }
+ }
+ myGroupList.setListData(actionGroups.toArray());
+ myGroupList.setCellRenderer(new MyActionRenderer());
+ myGroupList.addListSelectionListener(new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ ActionGroup group = (ActionGroup) myGroupList.getSelectedValue();
+ if (group == null) {
+ myActionList.setListData(ArrayUtil.EMPTY_OBJECT_ARRAY);
+ }
+ else {
+ final AnAction[] actions = group.getChildren(null);
+ // filter out actions that don't have IDs - they can't be used for anchoring in plugin.xml
+ List<AnAction> realActions = new ArrayList<AnAction>();
+ for(AnAction action: actions) {
+ if (actionManager.getId(action) != null) {
+ realActions.add(action);
+ }
+ }
+ myActionList.setListData(realActions.toArray());
+ }
+ }
+ });
+ new ListSpeedSearch(myGroupList, new Function<Object, String>() {
+ public String fun(final Object o) {
+ return ActionManager.getInstance().getId((AnAction) o);
+ }
+ });
+
+ myActionList.setCellRenderer(new MyActionRenderer());
+ myActionList.addListSelectionListener(new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ updateControls();
+ }
+ });
+
+ final MyDocumentListener listener = new MyDocumentListener();
+ myActionIdEdit.getDocument().addDocumentListener(listener);
+ myActionNameEdit.getDocument().addDocumentListener(listener);
+ myActionTextEdit.getDocument().addDocumentListener(listener);
+
+ myAnchorButtonGroup.setSelected(myAnchorFirstRadio.getModel(), true);
+
+ myFirstKeystrokeEdit = new ShortcutTextField();
+ myFirstKeystrokeEditPlaceholder.setLayout(new BorderLayout());
+ myFirstKeystrokeEditPlaceholder.add(myFirstKeystrokeEdit, BorderLayout.CENTER);
+ myClearFirstKeystroke.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ myFirstKeystrokeEdit.setKeyStroke(null);
+ }
+ });
+ myFirstKeystrokeEdit.getDocument().addDocumentListener(listener);
+ myClearFirstKeystroke.setText(null);
+
+ final Icon icon = AllIcons.Actions.Cancel;
+ final Dimension size = new Dimension(icon.getIconWidth(), icon.getIconHeight());
+ myClearFirstKeystroke.setIcon(icon);
+ myClearFirstKeystroke.setPreferredSize(size);
+ myClearFirstKeystroke.setMaximumSize(size);
+
+ mySecondKeystrokeEdit = new ShortcutTextField();
+ mySecondKeystrokeEditPlaceholder.setLayout(new BorderLayout());
+ mySecondKeystrokeEditPlaceholder.add(mySecondKeystrokeEdit, BorderLayout.CENTER);
+ myClearSecondKeystroke.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ mySecondKeystrokeEdit.setKeyStroke(null);
+ }
+ });
+ mySecondKeystrokeEdit.getDocument().addDocumentListener(listener);
+ myClearSecondKeystroke.setText(null);
+ myClearSecondKeystroke.setIcon(icon);
+ myClearSecondKeystroke.setPreferredSize(size);
+ myClearSecondKeystroke.setMaximumSize(size);
+
+ updateControls();
+ }
+
+ protected JComponent createCenterPanel() {
+ return myRootPanel;
+ }
+
+ @Override public JComponent getPreferredFocusedComponent() {
+ return myActionIdEdit;
+ }
+
+ @NotNull
+ public String getActionId() {
+ return myActionIdEdit.getText();
+ }
+
+ public String getActionName() {
+ return myActionNameEdit.getText();
+ }
+
+ @NotNull
+ public String getActionText() {
+ return myActionTextEdit.getText();
+ }
+
+ public String getActionDescription() {
+ return myActionDescriptionEdit.getText();
+ }
+
+ @Nullable
+ public String getSelectedGroupId() {
+ ActionGroup group = (ActionGroup) myGroupList.getSelectedValue();
+ return group == null ? null : ActionManager.getInstance().getId(group);
+ }
+
+ @Nullable
+ public String getSelectedActionId() {
+ AnAction action = (AnAction) myActionList.getSelectedValue();
+ return action == null ? null : ActionManager.getInstance().getId(action);
+ }
+
+ @NonNls
+ public String getSelectedAnchor() {
+ ButtonModel selection = myAnchorButtonGroup.getSelection();
+ if (selection == myAnchorFirstRadio.getModel()) return "first";
+ if (selection == myAnchorLastRadio.getModel()) return "last";
+ if (selection == myAnchorBeforeRadio.getModel()) return "before";
+ if (selection == myAnchorAfterRadio.getModel()) return "after";
+ return null;
+ }
+
+ public String getFirstKeyStroke() {
+ return getKeystrokeText(myFirstKeystrokeEdit.getKeyStroke());
+ }
+
+ public String getSecondKeyStroke() {
+ return getKeystrokeText(mySecondKeystrokeEdit.getKeyStroke());
+ }
+
+ private static String getKeystrokeText(KeyStroke keyStroke) {
+ //noinspection HardCodedStringLiteral
+ return keyStroke != null ?
+ keyStroke.toString().replaceAll("pressed ", "").replaceAll("released ", "") :
+ null;
+ }
+
+ private void updateControls() {
+ setOKActionEnabled(myActionIdEdit.getText().length() > 0 &&
+ myActionNameEdit.getText().length() > 0 &&
+ myActionTextEdit.getText().length() > 0 &&
+ (!myActionNameEdit.isEditable() || JavaPsiFacade.getInstance(myProject).getNameHelper().isIdentifier(myActionNameEdit.getText())));
+
+ myAnchorBeforeRadio.setEnabled(myActionList.getSelectedValue() != null);
+ myAnchorAfterRadio.setEnabled(myActionList.getSelectedValue() != null);
+
+ boolean enabled = myFirstKeystrokeEdit.getDocument().getLength() > 0;
+ myClearFirstKeystroke.setEnabled(enabled);
+ mySecondKeystrokeEdit.setEnabled(enabled);
+ myClearSecondKeystroke.setEnabled(enabled);
+
+ enabled = enabled && mySecondKeystrokeEdit.getDocument().getLength() > 0;
+ myClearSecondKeystroke.setEnabled(enabled);
+ }
+
+ private static class MyActionRenderer extends ColoredListCellRenderer {
+ protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
+ AnAction group = (AnAction) value;
+ append(ActionManager.getInstance().getId(group), SimpleTextAttributes.REGULAR_ATTRIBUTES);
+ final String text = group.getTemplatePresentation().getText();
+ if (text != null) {
+ append(" (" + text + ")", SimpleTextAttributes.REGULAR_ATTRIBUTES);
+ }
+ }
+ }
+
+ private class MyDocumentListener implements DocumentListener {
+ public void insertUpdate(DocumentEvent e) {
+ updateControls();
+ }
+
+ public void removeUpdate(DocumentEvent e) {
+ updateControls();
+ }
+
+ public void changedUpdate(DocumentEvent e) {
+ updateControls();
+ }
+ }
+
+ private static class ShortcutTextField extends JTextField {
+ private KeyStroke myKeyStroke;
+
+ public ShortcutTextField() {
+ enableEvents(AWTEvent.KEY_EVENT_MASK);
+ setFocusTraversalKeysEnabled(false);
+ }
+
+ protected void processKeyEvent(KeyEvent e) {
+ if (e.getID() == KeyEvent.KEY_PRESSED) {
+ int keyCode = e.getKeyCode();
+ if (
+ keyCode == KeyEvent.VK_SHIFT ||
+ keyCode == KeyEvent.VK_ALT ||
+ keyCode == KeyEvent.VK_CONTROL ||
+ keyCode == KeyEvent.VK_ALT_GRAPH ||
+ keyCode == KeyEvent.VK_META
+ ){
+ return;
+ }
+
+ setKeyStroke(KeyStroke.getKeyStroke(keyCode, e.getModifiers()));
+ }
+ }
+
+ public void setKeyStroke(KeyStroke keyStroke) {
+ myKeyStroke = keyStroke;
+ if (keyStroke == null) {
+ setText("");
+ }
+ else {
+ setText(KeymapUtil.getKeystrokeText(keyStroke));
+ }
+ }
+
+ public KeyStroke getKeyStroke() {
+ return myKeyStroke;
+ }
+ }
+
+ @Override
+ protected String getHelpId() {
+ return "reference.new.action.dialog";
+ }
+}
+
+
diff --git a/plugins/devkit/src/actions/NewActionForm.form b/plugins/devkit/src/actions/NewActionForm.form
new file mode 100644
index 0000000..ed1e3bc
--- /dev/null
+++ b/plugins/devkit/src/actions/NewActionForm.form
@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.idea.devkit.actions.NewActionDialog">
+ <grid id="a7288" binding="myRootPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="59" y="24" width="536" height="421"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <grid id="450dd" layout-manager="GridLayoutManager" row-count="2" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <clientProperties>
+ <BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithoutIndent"/>
+ </clientProperties>
+ <border type="none" title-resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" title-key="new.action.add.to.group"/>
+ <children>
+ <scrollpane id="fe8a" class="com.intellij.ui.components.JBScrollPane">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="6075d" class="com.intellij.ui.components.JBList" binding="myGroupList">
+ <constraints/>
+ <properties>
+ <selectionMode value="0"/>
+ </properties>
+ </component>
+ </children>
+ </scrollpane>
+ <component id="d15d0" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <labelFor value="c0ce9"/>
+ <text resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" key="new.action.group.actions"/>
+ </properties>
+ </component>
+ <scrollpane id="c0ce9" class="com.intellij.ui.components.JBScrollPane">
+ <constraints>
+ <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="c1416" class="com.intellij.ui.components.JBList" binding="myActionList">
+ <constraints/>
+ <properties>
+ <selectionMode value="0"/>
+ </properties>
+ </component>
+ </children>
+ </scrollpane>
+ <grid id="d4854" layout-manager="GridLayoutManager" row-count="5" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="2" row-span="2" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <clientProperties>
+ <BorderFactoryClass class="java.lang.String" value=""/>
+ </clientProperties>
+ <border type="empty" title-resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" title-key="new.action.group.anchor"/>
+ <children>
+ <component id="f4be7" class="javax.swing.JRadioButton" binding="myAnchorFirstRadio">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" key="new.action.group.anchor.first"/>
+ </properties>
+ </component>
+ <component id="74ccc" class="javax.swing.JRadioButton" binding="myAnchorLastRadio">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" key="new.action.group.anchor."/>
+ </properties>
+ </component>
+ <component id="8b7" class="javax.swing.JRadioButton" binding="myAnchorBeforeRadio">
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" key="new.action.group.anchor.before"/>
+ </properties>
+ </component>
+ <component id="a46a8" class="javax.swing.JRadioButton" binding="myAnchorAfterRadio">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" key="new.action.group.anchor.after"/>
+ </properties>
+ </component>
+ <vspacer id="73244">
+ <constraints>
+ <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
+ </children>
+ </grid>
+ <component id="de6c0" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <labelFor value="fe8a"/>
+ <text resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" key="new.action.group.groups"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ <grid id="69b17" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="5645" class="javax.swing.JTextField" binding="myActionNameEdit">
+ <constraints>
+ <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="47ab9" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <labelFor value="ce44f"/>
+ <text resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" key="new.action.id"/>
+ </properties>
+ </component>
+ <component id="26274" class="javax.swing.JTextField" binding="myActionTextEdit">
+ <constraints>
+ <grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="27b6b" class="javax.swing.JTextField" binding="myActionDescriptionEdit">
+ <constraints>
+ <grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="3c736" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <labelFor value="27b6b"/>
+ <text resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" key="new.action.description"/>
+ </properties>
+ </component>
+ <component id="2f01e" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <labelFor value="5645"/>
+ <text resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" key="new.action.class.name"/>
+ </properties>
+ </component>
+ <component id="7cf7b" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <labelFor value="26274"/>
+ <text resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" key="new.action.text"/>
+ </properties>
+ </component>
+ <component id="ce44f" class="javax.swing.JTextField" binding="myActionIdEdit">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ </children>
+ </grid>
+ <grid id="3f8c6" binding="myShortcutPanel" layout-manager="GridLayoutManager" row-count="2" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <clientProperties>
+ <BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithIndent"/>
+ </clientProperties>
+ <border type="none" title-resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" title-key="new.action.keyboard.shortcuts"/>
+ <children>
+ <component id="d5e7e" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" key="new.action.keyboard.first"/>
+ </properties>
+ </component>
+ <component id="5ce08" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" key="new.action.keyboard.second"/>
+ </properties>
+ </component>
+ <grid id="24949" binding="myFirstKeystrokeEditPlaceholder" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ <grid id="3ba64" binding="mySecondKeystrokeEditPlaceholder" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children/>
+ </grid>
+ <component id="bcafe" class="javax.swing.JButton" binding="myClearFirstKeystroke">
+ <constraints>
+ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false">
+ <minimum-size width="20" height="20"/>
+ <preferred-size width="20" height="20"/>
+ <maximum-size width="20" height="20"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" key="new.action.keyboard.clear"/>
+ <toolTipText resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" key="new.action.keyboard.clear.tooltip"/>
+ </properties>
+ </component>
+ <component id="986ce" class="javax.swing.JButton" binding="myClearSecondKeystroke">
+ <constraints>
+ <grid row="1" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false">
+ <minimum-size width="20" height="20"/>
+ <preferred-size width="20" height="20"/>
+ <maximum-size width="20" height="20"/>
+ </grid>
+ </constraints>
+ <properties>
+ <text resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" key="new.action.keyboard.clear"/>
+ <toolTipText resource-bundle="org/jetbrains/idea/devkit/DevKitBundle" key="new.action.keyboard.clear.tooltip"/>
+ </properties>
+ </component>
+ </children>
+ </grid>
+ </children>
+ </grid>
+ <buttonGroups>
+ <group name="myAnchorButtonGroup" bound="true">
+ <member id="f4be7"/>
+ <member id="74ccc"/>
+ <member id="8b7"/>
+ <member id="a46a8"/>
+ </group>
+ </buttonGroups>
+</form>
diff --git a/plugins/devkit/src/actions/NewApplicationComponentAction.java b/plugins/devkit/src/actions/NewApplicationComponentAction.java
new file mode 100644
index 0000000..8cc6988
--- /dev/null
+++ b/plugins/devkit/src/actions/NewApplicationComponentAction.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.actions;
+
+import com.intellij.psi.PsiDirectory;
+import org.jetbrains.idea.devkit.util.ComponentType;
+import org.jetbrains.idea.devkit.DevKitBundle;
+
+/**
+ * @author max
+ */
+public class NewApplicationComponentAction extends GenerateClassAndPatchPluginXmlActionBase {
+ /**
+ *.
+ */
+ public NewApplicationComponentAction() {
+ super(DevKitBundle.message("new.menu.application.component.text"),
+ DevKitBundle.message("new.menu.application.component.description"), null);
+ }
+
+ protected ComponentType getComponentType() {
+ return ComponentType.APPLICATION;
+ }
+
+ protected String getErrorTitle() {
+ return DevKitBundle.message("new.application.component.error");
+ }
+
+ protected String getCommandName() {
+ return DevKitBundle.message("new.application.component.command");
+ }
+
+ protected String getClassNamePromptTitle() {
+ return DevKitBundle.message("new.application.component.prompt.title");
+ }
+
+ protected String getClassTemplateName() {
+ return "ApplicationComponent.java";
+ }
+
+ protected String getClassNamePrompt() {
+ return DevKitBundle.message("new.application.component.prompt");
+ }
+
+ protected String getActionName(PsiDirectory directory, String newName) {
+ return DevKitBundle.message("new.application.component.action.name", directory, newName);
+ }
+}
diff --git a/plugins/devkit/src/actions/NewModuleComponentAction.java b/plugins/devkit/src/actions/NewModuleComponentAction.java
new file mode 100644
index 0000000..7a999c3
--- /dev/null
+++ b/plugins/devkit/src/actions/NewModuleComponentAction.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.actions;
+
+import com.intellij.psi.PsiDirectory;
+import org.jetbrains.idea.devkit.util.ComponentType;
+import org.jetbrains.idea.devkit.DevKitBundle;
+
+/**
+ * @author max
+ */
+public class NewModuleComponentAction extends GenerateClassAndPatchPluginXmlActionBase {
+ /**
+ *.
+ */
+ public NewModuleComponentAction() {
+ super(DevKitBundle.message("new.menu.module.component.text"),
+ DevKitBundle.message("new.menu.module.component.description"), null);
+ }
+
+ protected ComponentType getComponentType() {
+ return ComponentType.MODULE;
+ }
+
+ protected String getErrorTitle() {
+ return DevKitBundle.message("new.module.component.error");
+ }
+
+ protected String getCommandName() {
+ return DevKitBundle.message("new.module.component.command");
+ }
+
+ protected String getClassNamePromptTitle() {
+ return DevKitBundle.message("new.module.component.prompt.title");
+ }
+
+ protected String getClassTemplateName() {
+ return "ModuleComponent.java";
+ }
+
+ protected String getClassNamePrompt() {
+ return DevKitBundle.message("new.module.component.prompt");
+ }
+
+ protected String getActionName(PsiDirectory directory, String newName) {
+ return DevKitBundle.message("new.module.component.action.name", directory, newName);
+ }
+}
diff --git a/plugins/devkit/src/actions/NewProjectComponentAction.java b/plugins/devkit/src/actions/NewProjectComponentAction.java
new file mode 100644
index 0000000..d97b4fc
--- /dev/null
+++ b/plugins/devkit/src/actions/NewProjectComponentAction.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.actions;
+
+import com.intellij.psi.PsiDirectory;
+import org.jetbrains.idea.devkit.util.ComponentType;
+import org.jetbrains.idea.devkit.DevKitBundle;
+
+/**
+ * @author max
+ */
+public class NewProjectComponentAction extends GenerateClassAndPatchPluginXmlActionBase {
+ /**
+ *.
+ */
+ public NewProjectComponentAction() {
+ super(DevKitBundle.message("new.menu.project.component.text"),
+ DevKitBundle.message("new.menu.project.component.description"), null);
+ }
+
+ protected ComponentType getComponentType() {
+ return ComponentType.PROJECT;
+ }
+
+ protected String getErrorTitle() {
+ return DevKitBundle.message("new.project.component.error");
+ }
+
+ protected String getCommandName() {
+ return DevKitBundle.message("new.project.component.command");
+ }
+
+ protected String getClassNamePromptTitle() {
+ return DevKitBundle.message("new.project.component.prompt.title");
+ }
+
+ protected String getClassTemplateName() {
+ return "ProjectComponent.java";
+ }
+
+ protected String getClassNamePrompt() {
+ return DevKitBundle.message("new.project.component.prompt");
+ }
+
+ protected String getActionName(PsiDirectory directory, String newName) {
+ return DevKitBundle.message("new.project.component.action.name", directory, newName);
+ }
+}
diff --git a/plugins/devkit/src/actions/ShowSerializedXmlAction.java b/plugins/devkit/src/actions/ShowSerializedXmlAction.java
new file mode 100644
index 0000000..9ef05e3
--- /dev/null
+++ b/plugins/devkit/src/actions/ShowSerializedXmlAction.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.actions;
+
+import com.intellij.CommonBundle;
+import com.intellij.compiler.impl.FileSetCompileScope;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompileStatusNotification;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtilCore;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.OrderEnumerator;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.JDOMUtil;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.util.ClassUtil;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.util.containers.FList;
+import com.intellij.util.lang.UrlClassLoader;
+import com.intellij.util.xmlb.Accessor;
+import com.intellij.util.xmlb.XmlSerializer;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+import org.jdom.Element;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.lang.reflect.Array;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class ShowSerializedXmlAction extends DumbAwareAction {
+ private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.devkit.actions.ShowSerializedXmlAction");
+
+ @Override
+ public void update(AnActionEvent e) {
+ e.getPresentation().setEnabled(getEventProject(e) != null && e.getData(LangDataKeys.PSI_FILE) != null
+ && e.getData(PlatformDataKeys.EDITOR) != null);
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final PsiClass psiClass = getPsiClass(e);
+ if (psiClass == null) return;
+
+ final VirtualFile virtualFile = psiClass.getContainingFile().getVirtualFile();
+ final Module module = ModuleUtilCore.findModuleForPsiElement(psiClass);
+ if (module == null || virtualFile == null) return;
+
+ final String className = ClassUtil.getJVMClassName(psiClass);
+
+ final Project project = getEventProject(e);
+ CompilerManager.getInstance(project).make(new FileSetCompileScope(Arrays.asList(virtualFile), new Module[]{module}), new CompileStatusNotification() {
+ @Override
+ public void finished(boolean aborted, int errors, int warnings, CompileContext compileContext) {
+ if (aborted || errors > 0) return;
+ generateAndShowXml(module, className);
+ }
+ });
+ }
+
+ private static void generateAndShowXml(final Module module, final String className) {
+ final List<URL> urls = new ArrayList<URL>();
+ final List<String> list = OrderEnumerator.orderEntries(module).recursively().runtimeOnly().getPathsList().getPathList();
+ for (String path : list) {
+ try {
+ urls.add(new File(FileUtil.toSystemIndependentName(path)).toURI().toURL());
+ }
+ catch (MalformedURLException e1) {
+ LOG.info(e1);
+ }
+ }
+
+ final Project project = module.getProject();
+ UrlClassLoader loader = new UrlClassLoader(urls, XmlSerializer.class.getClassLoader());
+ final Class<?> aClass;
+ try {
+ aClass = Class.forName(className, true, loader);
+ }
+ catch (ClassNotFoundException e) {
+ Messages.showErrorDialog(project, "Cannot find class '" + className + "'", CommonBundle.getErrorTitle());
+ LOG.info(e);
+ return;
+ }
+
+ final Object o;
+ try {
+ o = new SampleObjectGenerator().createValue(aClass, FList.<Type>emptyList());
+ }
+ catch (Exception e) {
+ Messages.showErrorDialog(project, "Cannot generate class '" + className + "': " + e.getMessage(), CommonBundle.getErrorTitle());
+ LOG.info(e);
+ return;
+ }
+
+ final Element element = XmlSerializer.serialize(o);
+ final String text = JDOMUtil.writeElement(element, "\n");
+ Messages.showInfoMessage(project, text, "Serialized XML for '" + className + "'");
+ }
+
+ @Nullable
+ private static PsiClass getPsiClass(AnActionEvent e) {
+ final PsiFile psiFile = e.getData(LangDataKeys.PSI_FILE);
+ final Editor editor = e.getData(PlatformDataKeys.EDITOR);
+ if (editor == null || psiFile == null) return null;
+ final PsiElement element = psiFile.findElementAt(editor.getCaretModel().getOffset());
+ return PsiTreeUtil.getParentOfType(element, PsiClass.class);
+ }
+
+ private static class SampleObjectGenerator {
+ private int myNum;
+
+ @Nullable
+ public Object createValue(Type type, final FList<Type> types) throws Exception {
+ if (types.contains(type)) return null;
+ FList<Type> processedTypes = types.prepend(type);
+ final Class<?> valueClass = type instanceof Class ? (Class<Object>)type : (Class<Object>)((ParameterizedType)type).getRawType();
+ if (String.class.isAssignableFrom(valueClass)) {
+ return "value" + (myNum++);
+ }
+ else if (byte.class.isAssignableFrom(valueClass) || Byte.class.isAssignableFrom(valueClass)
+ || short.class.isAssignableFrom(valueClass) || Short.class.isAssignableFrom(valueClass)
+ || int.class.isAssignableFrom(valueClass) || Integer.class.isAssignableFrom(valueClass)
+ || long.class.isAssignableFrom(valueClass) || Long.class.isAssignableFrom(valueClass)) {
+ return myNum++ % 127;
+ }
+ else if (double.class.isAssignableFrom(valueClass) || Double.class.isAssignableFrom(valueClass)
+ || float.class.isAssignableFrom(valueClass) || Float.class.isAssignableFrom(valueClass)) {
+ return 0.5 + myNum++;
+ }
+ else if (boolean.class.isAssignableFrom(valueClass) || Boolean.class.isAssignableFrom(valueClass)) {
+ return (myNum++ % 2) == 0;
+ }
+ else if (valueClass.isEnum()) {
+ final Object[] constants = valueClass.getEnumConstants();
+ return constants[(myNum++) % constants.length];
+ }
+ else if (Collection.class.isAssignableFrom(valueClass) && type instanceof ParameterizedType) {
+ return createCollection(valueClass, (ParameterizedType)type, processedTypes);
+ }
+ else if (Map.class.isAssignableFrom(valueClass) && type instanceof ParameterizedType) {
+ return createMap((ParameterizedType)type, processedTypes);
+ }
+ else if (valueClass.isArray()) {
+ return createArray(valueClass, processedTypes);
+ }
+ else if (Element.class.isAssignableFrom(valueClass)) {
+ return new Element("customElement" + (myNum++)).setAttribute("attribute", "value" + (myNum++)).addContent(new Element("child" + (myNum++)));
+ }
+ else {
+ return createObject(valueClass, processedTypes);
+ }
+ }
+
+ public Object createObject(Class<?> aClass, FList<Type> processedTypes) throws Exception {
+ final Object o = aClass.newInstance();
+ for (Accessor accessor : XmlSerializerUtil.getAccessors(aClass)) {
+ final Type type = accessor.getGenericType();
+ Object value = createValue(type, processedTypes);
+ if (value != null) {
+ accessor.write(o, value);
+ }
+ }
+ return o;
+ }
+
+ @Nullable
+ private Object createArray(Class<?> valueClass, FList<Type> processedTypes) throws Exception {
+ final Object[] array = (Object[])Array.newInstance(valueClass.getComponentType(), 2);
+ for (int i = 0; i < array.length; i++) {
+ array[i] = createValue(valueClass.getComponentType(), processedTypes);
+ }
+ return array;
+ }
+
+ private Object createMap(ParameterizedType type, FList<Type> processedTypes) throws Exception {
+ Type keyType = type.getActualTypeArguments()[0];
+ Type valueType = type.getActualTypeArguments()[1];
+ final HashMap<Object, Object> map = new HashMap<Object, Object>();
+ for (int i = 0; i < 2; i++) {
+ Object key = createValue(keyType, processedTypes);
+ Object value = createValue(valueType, processedTypes);
+ if (key != null && value != null) {
+ map.put(key, value);
+ }
+ }
+ return map;
+ }
+
+ @Nullable
+ private Object createCollection(Class<?> aClass, ParameterizedType genericType, FList<Type> processedTypes) throws Exception {
+ final Type elementClass = genericType.getActualTypeArguments()[0];
+ Collection<Object> o;
+ if (List.class.isAssignableFrom(aClass)) {
+ o = new ArrayList<Object>();
+ }
+ else if (Set.class.isAssignableFrom(aClass)) {
+ o = new HashSet<Object>();
+ }
+ else {
+ return null;
+ }
+ for (int i = 0; i < 2; i++) {
+ final Object item = createValue(elementClass, processedTypes);
+ if (item != null) {
+ o.add(item);
+ }
+ }
+ return o;
+ }
+ }
+}
diff --git a/plugins/devkit/src/actions/ShuffleNamesAction.java b/plugins/devkit/src/actions/ShuffleNamesAction.java
new file mode 100644
index 0000000..5cf7dde
--- /dev/null
+++ b/plugins/devkit/src/actions/ShuffleNamesAction.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.actions;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.application.AccessToken;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.CommandProcessorEx;
+import com.intellij.openapi.command.UndoConfirmationPolicy;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
+import com.intellij.psi.impl.source.tree.LeafPsiElement;
+import gnu.trove.THashMap;
+
+import java.util.*;
+
+/**
+ * @author gregsh
+ */
+public class ShuffleNamesAction extends AnAction {
+ @Override
+ public void update(AnActionEvent e) {
+ Editor editor = PlatformDataKeys.EDITOR.getData(e.getDataContext());
+ PsiFile file = LangDataKeys.PSI_FILE.getData(e.getDataContext());
+ e.getPresentation().setEnabled(editor != null && file != null);
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final Editor editor = PlatformDataKeys.EDITOR.getData(e.getDataContext());
+ PsiFile file = LangDataKeys.PSI_FILE.getData(e.getDataContext());
+ if (editor == null || file == null) return;
+ final Project project = file.getProject();
+ CommandProcessorEx commandProcessor = (CommandProcessorEx)CommandProcessorEx.getInstance();
+ Object commandToken = commandProcessor.startCommand(project, e.getPresentation().getText(), e.getPresentation().getText(), UndoConfirmationPolicy.DEFAULT);
+ AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(getClass());
+ try {
+ shuffleIds(file, editor);
+ }
+ finally {
+ token.finish();
+ commandProcessor.finishCommand(project, commandToken, null);
+ }
+ }
+
+ private static boolean shuffleIds(PsiFile file, Editor editor) {
+ final Map<String, String> map = new THashMap<String, String>();
+ final StringBuilder sb = new StringBuilder();
+ final StringBuilder quote = new StringBuilder();
+ final ArrayList<String> split = new ArrayList<String>(100);
+ file.acceptChildren(new PsiRecursiveElementWalkingVisitor() {
+ @Override
+ public void visitElement(PsiElement element) {
+ if (element instanceof LeafPsiElement) {
+ String type = ((LeafPsiElement)element).getElementType().toString();
+ String text = element.getText();
+ if (text.isEmpty()) return;
+
+ for (int i=0, len=text.length(); i<len/2; i++) {
+ char c = text.charAt(i);
+ if (c == text.charAt(len-i-1) && !Character.isLetter(c)) {
+ quote.append(c);
+ }
+ else break;
+ }
+
+ boolean isQuoted = quote.length() > 0;
+ boolean isNumber = false;
+ if (isQuoted || type.equals("ID") || type.contains("IDENT") && !"ts".equals(text) ||
+ (isNumber = text.matches("[0-9]+"))) {
+ String replacement = map.get(text);
+ if (replacement == null) {
+ split.addAll(Arrays.asList((isQuoted? text.substring(quote.length(), text.length()-quote.length()).replace("''", "") : text).split("")));
+ if (!isNumber) {
+ for (ListIterator<String> it = split.listIterator(); it.hasNext(); ) {
+ String s = it.next();
+ if (s.isEmpty()) {
+ it.remove();
+ continue;
+ }
+ int c = s.charAt(0);
+ int cap = c & 32;
+ c &= ~cap;
+ c = (char) ((c >= 'A') && (c <= 'Z') ? ((c - 'A' + 7) % 26 + 'A') : c) | cap;
+ it.set(String.valueOf((char)c));
+ }
+ }
+ Collections.shuffle(split);
+ if (isNumber && "0".equals(split.get(0))) {
+ split.set(0, "1");
+ }
+ replacement = StringUtil.join(split, "");
+ if (isQuoted) {
+ replacement = quote + replacement + quote.reverse();
+ }
+ map.put(text, replacement);
+ }
+ text = replacement;
+ }
+ sb.append(text);
+ quote.setLength(0);
+ split.clear();
+ }
+ super.visitElement(element);
+ }
+ });
+ editor.getDocument().setText(sb.toString());
+ return true;
+ }
+}
diff --git a/plugins/devkit/src/actions/ToggleHighlightingMarkupAction.java b/plugins/devkit/src/actions/ToggleHighlightingMarkupAction.java
new file mode 100644
index 0000000..bd56787
--- /dev/null
+++ b/plugins/devkit/src/actions/ToggleHighlightingMarkupAction.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.actions;
+
+import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl;
+import com.intellij.codeInsight.daemon.impl.HighlightInfo;
+import com.intellij.codeInsight.daemon.impl.IndentsPass;
+import com.intellij.lang.annotation.HighlightSeverity;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.application.AccessToken;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.CommandProcessorEx;
+import com.intellij.openapi.command.UndoConfirmationPolicy;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.SelectionModel;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiFile;
+import com.intellij.util.Processor;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author gregsh
+ */
+public class ToggleHighlightingMarkupAction extends AnAction {
+ @Override
+ public void update(AnActionEvent e) {
+ Editor editor = PlatformDataKeys.EDITOR.getData(e.getDataContext());
+ PsiFile file = LangDataKeys.PSI_FILE.getData(e.getDataContext());
+ e.getPresentation().setEnabled(editor != null && file != null);
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ final Editor editor = PlatformDataKeys.EDITOR.getData(e.getDataContext());
+ PsiFile file = LangDataKeys.PSI_FILE.getData(e.getDataContext());
+ if (editor == null || file == null) return;
+ final Project project = file.getProject();
+ CommandProcessorEx commandProcessor = (CommandProcessorEx)CommandProcessorEx.getInstance();
+ Object commandToken = commandProcessor.startCommand(project, e.getPresentation().getText(), e.getPresentation().getText(), UndoConfirmationPolicy.DEFAULT);
+ AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(getClass());
+ try {
+ final SelectionModel selectionModel = editor.getSelectionModel();
+ int[] starts = selectionModel.getBlockSelectionStarts();
+ int[] ends = selectionModel.getBlockSelectionEnds();
+
+ int startOffset = starts.length == 0? 0 : starts[0];
+ int endOffset = ends.length == 0? editor.getDocument().getTextLength() : ends[ends.length - 1];
+
+ perform(project, editor.getDocument(), startOffset, endOffset);
+ }
+ finally {
+ token.finish();
+ commandProcessor.finishCommand(project, commandToken, null);
+ }
+ }
+
+ private static void perform(Project project, final Document document, final int startOffset, final int endOffset) {
+ final CharSequence sequence = document.getCharsSequence();
+ final StringBuilder sb = new StringBuilder();
+ Pattern pattern = Pattern.compile("<(error|warning|EOLError|EOLWarning|info|weak_warning)((?:\\s|=|\\w+|\\\"(?:[^\"]|\\\\\\\")*?\\\")*?)>(.*?)</\\1>");
+ Matcher matcher = pattern.matcher(sequence);
+ List<TextRange> ranges = new ArrayList<TextRange>();
+ if (matcher.find(startOffset)) {
+ boolean compactMode = false;
+ int pos;
+ do {
+ if (matcher.start(0) >= endOffset) break;
+ if (matcher.start(2) < matcher.end(2)) {
+ if (!compactMode) {
+ ranges.clear();
+ compactMode = true;
+ }
+ ranges.add(new TextRange(matcher.start(2), matcher.end(2)));
+ }
+ else if (!compactMode) {
+ ranges.add(new TextRange(matcher.start(0), matcher.start(3)));
+ ranges.add(new TextRange(matcher.end(3), matcher.end(0)));
+ }
+ pos = Math.max(matcher.end(1), matcher.end(2));
+ }
+ while (matcher.find(pos));
+ Collections.sort(ranges, IndentsPass.RANGE_COMPARATOR);
+ }
+ if (!ranges.isEmpty()) {
+ int pos = 0;
+ for (TextRange range : ranges) {
+ sb.append(sequence, pos, range.getStartOffset());
+ pos = range.getEndOffset();
+ }
+ sb.append(sequence, pos, sequence.length());
+ }
+ else {
+ final int[] offset = new int[] {0};
+ final ArrayList<HighlightInfo> infos = new ArrayList<HighlightInfo>();
+ DaemonCodeAnalyzerImpl.processHighlights(
+ document, project, HighlightSeverity.WARNING, 0, sequence.length(),
+ new Processor<HighlightInfo>() {
+ @Override
+ public boolean process(HighlightInfo info) {
+ if (info.severity != HighlightSeverity.WARNING && info.severity != HighlightSeverity.ERROR) return true;
+ if (info.getStartOffset() >= endOffset) return false;
+ if (info.getEndOffset() > startOffset) {
+ offset[0] = appendInfo(info, sb, sequence, offset[0], infos, false);
+ }
+ return true;
+ }
+ });
+ offset[0] = appendInfo(null, sb, sequence, offset[0], infos, false);
+ sb.append(sequence.subSequence(offset[0], sequence.length()));
+ }
+ document.setText(sb);
+ }
+
+ private static int appendInfo(@Nullable HighlightInfo info,
+ StringBuilder sb,
+ CharSequence sequence,
+ int offset,
+ ArrayList<HighlightInfo> infos, final boolean compact) {
+ if (info == null || !infos.isEmpty() && getMaxEnd(infos) < info.getStartOffset()) {
+ if (infos.size() == 1) {
+ HighlightInfo cur = infos.remove(0);
+ sb.append(sequence.subSequence(offset, cur.getStartOffset()));
+ appendTag(sb, cur, true, compact);
+ sb.append(sequence.subSequence(cur.getStartOffset(), cur.getEndOffset()));
+ appendTag(sb, cur, false, compact);
+ offset = cur.getEndOffset();
+ }
+ else {
+ // process overlapped
+ LinkedList<HighlightInfo> stack = new LinkedList<HighlightInfo>();
+ for (HighlightInfo cur : infos) {
+ offset = processStack(stack, sb, sequence, offset, cur.getStartOffset(), compact);
+ sb.append(sequence.subSequence(offset, cur.getStartOffset()));
+ offset = cur.getStartOffset();
+ appendTag(sb, cur, true, compact);
+ stack.addLast(cur);
+ }
+ offset = processStack(stack, sb, sequence, offset, sequence.length(), compact);
+ infos.clear();
+ }
+ }
+ if (info != null) {
+ boolean found = false;
+ for (HighlightInfo cur : infos) {
+ if (cur.getStartOffset() == info.getStartOffset() && cur.getEndOffset() == info.getEndOffset() && cur.severity == info.severity) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) infos.add(info);
+ }
+ return offset;
+ }
+
+ private static int getMaxEnd(ArrayList<HighlightInfo> infos) {
+ int max = -1;
+ for (HighlightInfo info : infos) {
+ int endOffset = info.getEndOffset();
+ if (max < endOffset) max = endOffset;
+ }
+ return max;
+ }
+
+ private static int processStack(LinkedList<HighlightInfo> stack,
+ StringBuilder sb,
+ CharSequence sequence,
+ int offset,
+ final int endOffset,
+ final boolean compact) {
+ if (stack.isEmpty()) return offset;
+ for (HighlightInfo cur = stack.peekLast(); cur != null && cur.getEndOffset() <= endOffset; cur = stack.peekLast()) {
+ stack.removeLast();
+ if (offset <= cur.getEndOffset()) {
+ sb.append(sequence.subSequence(offset, cur.getEndOffset()));
+ }
+ else {
+ //System.out.println("Incorrect overlapping infos: " + offset + " > " + cur.getEndOffset());
+ }
+ offset = cur.getEndOffset();
+ appendTag(sb, cur, false, compact);
+ }
+ return offset;
+ }
+
+ private static void appendTag(StringBuilder sb, HighlightInfo cur, boolean opening, final boolean compact) {
+ sb.append("<");
+ if (!opening) sb.append("/");
+ if (cur.isAfterEndOfLine) {
+ sb.append(cur.severity == HighlightSeverity.WARNING ? "EOLWarning" : "EOLError");
+ }
+ else {
+ sb.append(cur.severity == HighlightSeverity.WARNING ? "warning" : "error");
+ }
+ if (opening && !compact) {
+ sb.append(" descr=\"").append(cur.description).append("\"");
+
+ }
+ sb.append(">");
+ }
+}
diff --git a/plugins/devkit/src/build/PluginBuildConfiguration.java b/plugins/devkit/src/build/PluginBuildConfiguration.java
new file mode 100644
index 0000000..5a34c45
--- /dev/null
+++ b/plugins/devkit/src/build/PluginBuildConfiguration.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.build;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleComponent;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.JDOMExternalizable;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
+import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
+import com.intellij.util.descriptors.ConfigFile;
+import com.intellij.util.descriptors.ConfigFileContainer;
+import com.intellij.util.descriptors.ConfigFileFactory;
+import com.intellij.util.descriptors.ConfigFileInfo;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.module.PluginDescriptorConstants;
+
+import java.io.File;
+
+public class PluginBuildConfiguration implements ModuleComponent, JDOMExternalizable {
+ private final Module myModule;
+ private final ConfigFileContainer myPluginXmlContainer;
+ private VirtualFilePointer myManifestFilePointer;
+ private boolean myUseUserManifest = false;
+ @NonNls private static final String URL_ATTR = "url";
+ @NonNls private static final String MANIFEST_ATTR = "manifest";
+ @NonNls private static final String META_INF = "META-INF";
+ @NonNls private static final String PLUGIN_XML = "plugin.xml";
+ private final PluginBuildParticipant myBuildParticipant;
+
+ public PluginBuildConfiguration(Module module) {
+ myModule = module;
+ myPluginXmlContainer = ConfigFileFactory.getInstance().createSingleFileContainer(myModule.getProject(), PluginDescriptorConstants.META_DATA);
+ Disposer.register(module, myPluginXmlContainer);
+ myBuildParticipant = new PluginBuildParticipant(module, this);
+ }
+
+ @Nullable
+ public static PluginBuildConfiguration getInstance(Module module) {
+ return module.getComponent(PluginBuildConfiguration.class);
+ }
+
+ public void projectOpened() {}
+
+ public void projectClosed() {}
+
+ public void moduleAdded() {}
+
+ @NotNull
+ public String getComponentName() {
+ return "DevKit.ModuleBuildProperties";
+ }
+
+ public void initComponent() {
+ }
+
+ public void disposeComponent() {
+ }
+
+ public void readExternal(Element element) throws InvalidDataException {
+ String url = element.getAttributeValue(URL_ATTR);
+ if (url != null) {
+ myPluginXmlContainer.getConfiguration().replaceConfigFile(PluginDescriptorConstants.META_DATA, url);
+ }
+ url = element.getAttributeValue(MANIFEST_ATTR);
+ if (url != null) {
+ setManifestPath(VfsUtil.urlToPath(url));
+ }
+ }
+
+ public void writeExternal(Element element) throws WriteExternalException {
+ element.setAttribute(URL_ATTR, getPluginXmlUrl());
+ if (myManifestFilePointer != null){
+ element.setAttribute(MANIFEST_ATTR, myManifestFilePointer.getUrl());
+ }
+ }
+
+ @Nullable
+ public ConfigFile getPluginXML() {
+ return myPluginXmlContainer.getConfigFile(PluginDescriptorConstants.META_DATA);
+ }
+
+ private void createDescriptor(final String url) {
+ final ConfigFileInfo descriptor = new ConfigFileInfo(PluginDescriptorConstants.META_DATA, url);
+ myPluginXmlContainer.getConfiguration().addConfigFile(descriptor);
+ ConfigFileFactory.getInstance().createFile(myModule.getProject(), descriptor.getUrl(), PluginDescriptorConstants.META_DATA.getDefaultVersion(),
+ false);
+ }
+
+ @Nullable
+ public ConfigFile getPluginXmlConfigFile() {
+ return myPluginXmlContainer.getConfigFile(PluginDescriptorConstants.META_DATA);
+ }
+
+ @Nullable
+ private String getPluginXmlUrl() {
+ ConfigFile configFile = getPluginXmlConfigFile();
+ return configFile != null ? configFile.getUrl() : null;
+ }
+
+ private String getDefaultLocation() {
+ return new File(myModule.getModuleFilePath()).getParent() + File.separator + META_INF + File.separator + PLUGIN_XML;
+ }
+
+ @NotNull
+ public String getPluginXmlPath() {
+ String url = getPluginXmlUrl();
+ if (url == null) {
+ return getDefaultLocation();
+ }
+ return FileUtil.toSystemDependentName(VfsUtil.urlToPath(url));
+ }
+
+ public void setPluginXmlPathAndCreateDescriptorIfDoesntExist(final String pluginXmlPath) {
+ myPluginXmlContainer.getConfiguration().removeConfigFiles(PluginDescriptorConstants.META_DATA);
+ new WriteAction() {
+ protected void run(final Result result) throws Throwable {
+ createDescriptor(VfsUtil.pathToUrl(FileUtil.toSystemIndependentName(pluginXmlPath)));
+ }
+ }.execute();
+ }
+
+ public void setManifestPath(final String manifestPath) {
+ if (manifestPath == null || manifestPath.length() == 0){
+ myManifestFilePointer = null;
+ } else {
+
+ final VirtualFile manifest = LocalFileSystem.getInstance().findFileByPath(manifestPath);
+ if (manifest == null){
+ Messages.showErrorDialog(myModule.getProject(), DevKitBundle.message("error.file.not.found.message", manifestPath), DevKitBundle.message("error.file.not.found"));
+ ApplicationManager.getApplication().runReadAction(new Runnable() {
+ public void run() {
+ myManifestFilePointer = VirtualFilePointerManager.getInstance().create(VfsUtil.pathToUrl(FileUtil.toSystemIndependentName(manifestPath)), myModule, null);
+ }
+ });
+ } else {
+ ApplicationManager.getApplication().runReadAction(new Runnable() {
+ public void run() {
+ myManifestFilePointer = VirtualFilePointerManager.getInstance().create(manifest, myModule, null);
+ }
+ });
+ }
+ }
+ }
+
+ @Nullable
+ public String getManifestPath() {
+ if (myManifestFilePointer != null){
+ return FileUtil.toSystemDependentName(myManifestFilePointer.getPresentableUrl());
+ }
+ return null;
+ }
+
+ @Nullable
+ public VirtualFile getManifest(){
+ if (myManifestFilePointer != null){
+ return myManifestFilePointer.getFile();
+ }
+ return null;
+ }
+
+ public boolean isUseUserManifest() {
+ return myUseUserManifest;
+ }
+
+ public void setUseUserManifest(final boolean useUserManifest) {
+ myUseUserManifest = useUserManifest;
+ }
+
+ public PluginBuildParticipant getBuildParticipant() {
+ return myBuildParticipant;
+ }
+}
diff --git a/plugins/devkit/src/build/PluginBuildParticipant.java b/plugins/devkit/src/build/PluginBuildParticipant.java
new file mode 100644
index 0000000..174dd17
--- /dev/null
+++ b/plugins/devkit/src/build/PluginBuildParticipant.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.build;
+
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.compiler.make.BuildParticipant;
+import com.intellij.openapi.deployment.DeploymentUtil;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.ArtifactRootElement;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementFactory;
+import com.intellij.packaging.impl.artifacts.ArtifactImpl;
+import com.intellij.packaging.impl.artifacts.PlainArtifactType;
+import com.intellij.psi.xml.XmlDocument;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.util.descriptors.ConfigFile;
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.DomManager;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.dom.Dependency;
+import org.jetbrains.idea.devkit.dom.IdeaPlugin;
+import org.jetbrains.idea.devkit.module.PluginModuleType;
+import org.jetbrains.idea.devkit.projectRoots.IdeaJdk;
+import org.jetbrains.idea.devkit.util.DescriptorUtil;
+
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * @author peter
+*/
+public class PluginBuildParticipant extends BuildParticipant {
+ @NonNls private static final String CLASSES = "/classes";
+ @NonNls private static final String LIB = "/lib/";
+ @NonNls private static final String LIB_DIRECTORY = "lib";
+ private final Module myModule;
+ private final PluginBuildConfiguration myPluginBuildConfiguration;
+
+ public PluginBuildParticipant(final Module module, final PluginBuildConfiguration pluginBuildConfiguration) {
+ super();
+ myModule = module;
+ myPluginBuildConfiguration = pluginBuildConfiguration;
+ }
+
+ @Override
+ public Artifact createArtifact(CompileContext context) {
+ Sdk jdk = IdeaJdk.findIdeaJdk(ModuleRootManager.getInstance(myModule).getSdk());
+ if (jdk != null && IdeaJdk.isFromIDEAProject(jdk.getHomePath())) {
+ return null;
+ }
+
+ if (jdk == null) {
+ context.addMessage(CompilerMessageCategory.ERROR, DevKitBundle.message("jdk.type.incorrect", myModule.getName()), null, -1, -1);
+ return null;
+ }
+
+ final String outputPath = PluginBuildUtil.getPluginExPath(myModule);
+ if (outputPath == null) {
+ return null;
+ }
+
+ if (!checkDependencies(context)) {
+ return null;
+ }
+
+
+ final PackagingElementFactory factory = PackagingElementFactory.getInstance();
+ final ArtifactRootElement<?> root = factory.createArtifactRootElement();
+
+ ConfigFile configFile = myPluginBuildConfiguration.getPluginXML();
+ if (configFile != null) {
+ DeploymentUtil.getInstance().checkConfigFile(configFile, context, myModule);
+ factory.addFileCopy(root, "META-INF/", VfsUtil.urlToPath(configFile.getUrl()));
+
+ final XmlFile xmlFile = configFile.getXmlFile();
+ if (xmlFile != null) {
+ final XmlDocument document = xmlFile.getDocument();
+ if (document != null) {
+ final DomElement domElement = DomManager.getDomManager(xmlFile.getProject()).getDomElement(document.getRootTag());
+ if (domElement instanceof IdeaPlugin) {
+ for(Dependency dependency: ((IdeaPlugin)domElement).getDependencies()) {
+ final String file = dependency.getConfigFile().getValue();
+ if (file != null) {
+ final VirtualFile virtualFile = configFile.getVirtualFile();
+ assert virtualFile != null;
+ final VirtualFile parent = virtualFile.getParent();
+ assert parent != null;
+ final String url = parent.getUrl();
+ factory.addFileCopy(root, "META-INF/", VfsUtil.urlToPath(url) + "/" + file);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ HashSet<Module> modules = new HashSet<Module>();
+ PluginBuildUtil.getDependencies(myModule, modules);
+
+ final CompositePackagingElement<?> classesDir = factory.getOrCreateDirectory(root, CLASSES);
+ for (Module dep : modules) {
+ classesDir.addOrFindChild(factory.createModuleOutput(dep));
+ }
+ classesDir.addOrFindChild(factory.createModuleOutput(myModule));
+
+ HashSet<Library> libs = new HashSet<Library>();
+ PluginBuildUtil.getLibraries(myModule, libs);
+ for (Module dependentModule : modules) {
+ PluginBuildUtil.getLibraries(dependentModule, libs);
+ }
+
+
+ // libraries
+ final VirtualFile libDir = jdk.getHomeDirectory().findFileByRelativePath(LIB_DIRECTORY);
+ for (Library library : libs) {
+ boolean hasDirsOnly = true;
+ VirtualFile[] files = library.getFiles(OrderRootType.CLASSES);
+ for (VirtualFile file : files) {
+ if (file.getFileSystem() instanceof JarFileSystem) {
+ hasDirsOnly = false;
+ file = ((JarFileSystem)file.getFileSystem()).getVirtualFileForJar(file);
+ }
+ if (libDir != null && file != null && VfsUtilCore.isAncestor(libDir, file, false)) {
+ context.addMessage(CompilerMessageCategory.ERROR, DevKitBundle.message("dont.add.idea.libs.to.classpath", file.getName()), null,
+ -1, -1);
+ }
+ }
+
+ final List<? extends PackagingElement<?>> elements = factory.createLibraryElements(library);
+ if (hasDirsOnly) {
+ //todo split one lib into 2 separate libs if there are jars and dirs
+ classesDir.addOrFindChildren(elements);
+ }
+ else {
+ factory.getOrCreateDirectory(root, LIB).addOrFindChildren(elements);
+ }
+ }
+
+ return new ArtifactImpl(getArtifactName(), PlainArtifactType.getInstance(), false, root, FileUtil.toSystemIndependentName(outputPath));
+ }
+
+ private String getArtifactName() {
+ return myModule.getName() + ":plugin";
+ }
+
+ private boolean checkDependencies(CompileContext context) {
+ final Module[] wrongSetDependencies = PluginBuildUtil.getWrongSetDependencies(myModule);
+ if (wrongSetDependencies.length != 0) {
+ boolean realProblems = false;
+ final String pluginId = DescriptorUtil.getPluginId(myModule);
+
+ for (Module dependency : wrongSetDependencies) {
+ if (!PluginModuleType.isOfType(dependency)) {
+ realProblems = true;
+ context.addMessage(CompilerMessageCategory.ERROR,
+ DevKitBundle.message("incorrect.dependency.non-plugin-module", dependency.getName(), myModule.getName()), null,
+ -1, -1);
+ }
+ else {
+ final XmlFile pluginXml = PluginModuleType.getPluginXml(dependency);
+ boolean isDeclared = false;
+ if (pluginXml != null) {
+ final XmlTag rootTag = pluginXml.getDocument().getRootTag();
+ final XmlTag[] dependencies = rootTag != null ? rootTag.findSubTags("depends") : XmlTag.EMPTY;
+ for (XmlTag dep : dependencies) {
+ if (dep.getValue().getTrimmedText().equals(pluginId)) {
+ isDeclared = true;
+ break;
+ }
+ }
+ }
+ if (!isDeclared) {
+ // make this a warning instead?
+ realProblems = true;
+ context.addMessage(CompilerMessageCategory.ERROR,
+ DevKitBundle.message("incorrect.dependency.not-declared", dependency.getName(), myModule.getName()), null, -1,
+ -1);
+ }
+ }
+ }
+ if (realProblems) return false;
+ }
+ return true;
+ }
+
+}
diff --git a/plugins/devkit/src/build/PluginBuildParticipantProvider.java b/plugins/devkit/src/build/PluginBuildParticipantProvider.java
new file mode 100644
index 0000000..b8a35d3
--- /dev/null
+++ b/plugins/devkit/src/build/PluginBuildParticipantProvider.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.idea.devkit.build;
+
+import com.intellij.openapi.compiler.make.BuildParticipantProvider;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import org.jetbrains.idea.devkit.module.PluginModuleType;
+
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * @author nik
+ */
+public class PluginBuildParticipantProvider extends BuildParticipantProvider {
+ public Collection<PluginBuildParticipant> getParticipants(final Module module) {
+ if (ModuleType.get(module) != PluginModuleType.getInstance()) {
+ return Collections.emptyList();
+ }
+
+ final PluginBuildConfiguration configuration = PluginBuildConfiguration.getInstance(module);
+ return configuration != null ? Collections.singletonList(configuration.getBuildParticipant())
+ : Collections.<PluginBuildParticipant>emptyList();
+ }
+}
diff --git a/plugins/devkit/src/build/PluginBuildUtil.java b/plugins/devkit/src/build/PluginBuildUtil.java
new file mode 100644
index 0000000..157d31c
--- /dev/null
+++ b/plugins/devkit/src/build/PluginBuildUtil.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.build;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.module.StdModuleTypes;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.util.Computable;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.Processor;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.projectRoots.IdeaJdk;
+import org.jetbrains.idea.devkit.projectRoots.Sandbox;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * User: anna
+ * Date: Nov 24, 2004
+ */
+public class PluginBuildUtil {
+ private PluginBuildUtil() {
+ }
+
+ @NonNls @Nullable public static String getPluginExPath(Module module) {
+ final Sdk jdk = IdeaJdk.findIdeaJdk(ModuleRootManager.getInstance(module).getSdk());
+ if (jdk == null) {
+ return null;
+ }
+ String sandboxHome = ((Sandbox)jdk.getSdkAdditionalData()).getSandboxHome();
+ if (sandboxHome == null) return null;
+ try {
+ sandboxHome = new File(sandboxHome).getCanonicalPath();
+ }
+ catch (IOException e) {
+ return null;
+ }
+ return sandboxHome + File.separator + "plugins" + File.separator + module.getName();
+ }
+
+ public static void getDependencies(Module module, final Set<Module> modules) {
+ productionRuntimeDependencies(module).forEachModule(new Processor<Module>() {
+ @Override
+ public boolean process(Module dep) {
+ if (ModuleType.get(dep) == StdModuleTypes.JAVA && !modules.contains(dep)) {
+ modules.add(dep);
+ getDependencies(dep, modules);
+ }
+ return true;
+ }
+ });
+ }
+
+ public static Module[] getWrongSetDependencies(final Module module) {
+ return ApplicationManager.getApplication().runReadAction(new Computable<Module[]>() {
+ public Module[] compute() {
+ ArrayList<Module> result = new ArrayList<Module>();
+ final Module[] projectModules = ModuleManager.getInstance(module.getProject()).getModules();
+ for (Module projectModule : projectModules) {
+ if (ArrayUtil.find(ModuleRootManager.getInstance(projectModule).getDependencies(), module) > -1) {
+ result.add(projectModule);
+ }
+ }
+ return result.toArray(new Module[result.size()]);
+ }
+ });
+ }
+
+ public static void getLibraries(Module module, final Set<Library> libs) {
+ productionRuntimeDependencies(module).forEachLibrary(new Processor<Library>() {
+ @Override
+ public boolean process(Library library) {
+ libs.add(library);
+ return true;
+ }
+ });
+ }
+
+ private static OrderEnumerator productionRuntimeDependencies(Module module) {
+ return OrderEnumerator.orderEntries(module).productionOnly().runtimeOnly();
+ }
+}
diff --git a/plugins/devkit/src/build/PluginModuleBuildConfEditor.java b/plugins/devkit/src/build/PluginModuleBuildConfEditor.java
new file mode 100644
index 0000000..64e63d5
--- /dev/null
+++ b/plugins/devkit/src/build/PluginModuleBuildConfEditor.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.build;
+
+import com.intellij.ide.util.BrowseFilesListener;
+import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleConfigurationEditor;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.roots.ui.configuration.ModuleConfigurationState;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.ui.IdeBorderFactory;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.idea.devkit.DevKitBundle;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+
+/**
+ * User: anna
+ * Date: Nov 24, 2004
+ */
+public class PluginModuleBuildConfEditor implements ModuleConfigurationEditor {
+ private final JPanel myWholePanel = new JPanel(new GridBagLayout());
+ @NonNls private final JLabel myPluginXMLLabel = new JLabel(DevKitBundle.message("deployment.view.meta-inf.label", " META-INF" + File.separator + "plugin.xml:"));
+ private final TextFieldWithBrowseButton myPluginXML = new TextFieldWithBrowseButton();
+
+ private final TextFieldWithBrowseButton myManifest = new TextFieldWithBrowseButton();
+ private final JCheckBox myUseUserManifest = new JCheckBox(DevKitBundle.message("manifest.use.user.defined"));
+
+ private final PluginBuildConfiguration myBuildProperties;
+
+ private final Module myModule;
+ @NonNls private static final String META_INF = "META-INF";
+ @NonNls private static final String PLUGIN_XML = "plugin.xml";
+ @NonNls private static final String MANIFEST_MF = "manifest.mf";
+
+ public PluginModuleBuildConfEditor(ModuleConfigurationState state) {
+ myModule = state.getRootModel().getModule();
+ myBuildProperties = PluginBuildConfiguration.getInstance(myModule);
+ }
+
+ public JComponent createComponent() {
+ myPluginXML.addActionListener(new BrowseFilesListener(myPluginXML.getTextField(), DevKitBundle.message("deployment.directory.location", META_INF), DevKitBundle.message("saved.message.common", META_INF + File.separator + PLUGIN_XML), BrowseFilesListener.SINGLE_DIRECTORY_DESCRIPTOR));
+ myManifest.addActionListener(new BrowseFilesListener(myManifest.getTextField(), DevKitBundle.message("deployment.view.select", MANIFEST_MF), DevKitBundle.message("manifest.selection", MANIFEST_MF), BrowseFilesListener.SINGLE_FILE_DESCRIPTOR));
+ myManifest.setEnabled(myBuildProperties.isUseUserManifest());
+ myUseUserManifest.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ final boolean selected = myUseUserManifest.isSelected();
+
+ myManifest.setEnabled(selected);
+ }
+ });
+ final GridBagConstraints gc = new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST,
+ GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0);
+ myWholePanel.add(myPluginXMLLabel, gc);
+ myWholePanel.add(myPluginXML, gc);
+ JPanel manifestPanel = new JPanel(new GridBagLayout());
+ manifestPanel.setBorder(IdeBorderFactory.createTitledBorder(DevKitBundle.message("manifest.settings"), true));
+ gc.insets.left = 0;
+ manifestPanel.add(myUseUserManifest, gc);
+ gc.insets.left = 2;
+ gc.weighty = 1.0;
+ manifestPanel.add(myManifest, gc);
+ myWholePanel.add(manifestPanel, gc);
+ myWholePanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+ return myWholePanel;
+ }
+
+ public boolean isModified() {
+ final String pluginXmlPath = new File(myBuildProperties.getPluginXmlPath()).getParentFile().getParent(); //parent for meta-inf
+ boolean modified = !Comparing.strEqual(myPluginXML.getText(), pluginXmlPath);
+ final boolean selected = myUseUserManifest.isSelected();
+ modified |= myBuildProperties.isUseUserManifest() != selected;
+ if (selected) {
+ modified |= !Comparing.strEqual(myManifest.getText(), myBuildProperties.getManifestPath());
+ }
+ return modified;
+ }
+
+ public void apply() throws ConfigurationException {
+ if (myUseUserManifest.isSelected() && myManifest.getText() != null && !new File(myManifest.getText()).exists()){
+ throw new ConfigurationException(DevKitBundle.message("error.file.not.found.message", myManifest.getText()));
+ }
+ final File plugin = myBuildProperties.getPluginXmlPath() != null ? new File(myBuildProperties.getPluginXmlPath()) : null;
+ final String newPluginPath = myPluginXML.getText() + File.separator + META_INF + File.separator + PLUGIN_XML;
+ if (plugin != null &&
+ plugin.exists() &&
+ !plugin.getPath().equals(newPluginPath) &&
+ Messages.showYesNoDialog(myModule.getProject(),
+ DevKitBundle.message("deployment.view.delete", plugin.getPath()),
+ DevKitBundle.message("deployment.cleanup", META_INF), null) == DialogWrapper.OK_EXIT_CODE) {
+
+ CommandProcessor.getInstance().executeCommand(myModule.getProject(),
+ new Runnable() {
+ public void run() {
+ FileUtil.delete(plugin.getParentFile());
+ }
+ },
+ DevKitBundle.message("deployment.cleanup", META_INF),
+ null);
+ }
+ myBuildProperties.setPluginXmlPathAndCreateDescriptorIfDoesntExist(newPluginPath);
+ myBuildProperties.setManifestPath(myManifest.getText());
+ myBuildProperties.setUseUserManifest(myUseUserManifest.isSelected());
+ }
+
+ public void reset() {
+ myPluginXML.setText(myBuildProperties.getPluginXmlPath().substring(0, myBuildProperties.getPluginXmlPath().length() - META_INF.length() - PLUGIN_XML.length() - 2));
+ myManifest.setText(myBuildProperties.getManifestPath());
+ myUseUserManifest.setSelected(myBuildProperties.isUseUserManifest());
+ }
+
+ public void disposeUIResources() {}
+
+ public void saveData() {}
+
+ public String getDisplayName() {
+ return DevKitBundle.message("deployment.title");
+ }
+
+ public String getHelpTopic() {
+ return "plugin.configuring";
+ }
+
+ public void moduleStateChanged() {
+ }
+
+}
diff --git a/plugins/devkit/src/build/PluginModuleBuildScopeProvider.java b/plugins/devkit/src/build/PluginModuleBuildScopeProvider.java
new file mode 100644
index 0000000..d489978
--- /dev/null
+++ b/plugins/devkit/src/build/PluginModuleBuildScopeProvider.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.build;
+
+import com.intellij.compiler.impl.BuildTargetScopeProvider;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.CompilerFilter;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.module.PluginModuleType;
+import org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage.TargetTypeBuildScope;
+import org.jetbrains.jps.incremental.artifacts.ArtifactBuildTargetType;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class PluginModuleBuildScopeProvider extends BuildTargetScopeProvider {
+ @NotNull
+ @Override
+ public List<TargetTypeBuildScope> getBuildTargetScopes(@NotNull CompileScope baseScope, @NotNull CompilerFilter filter, @NotNull Project project) {
+ List<String> pluginArtifactTargetIds = new ArrayList<String>();
+ for (Module module : baseScope.getAffectedModules()) {
+ if (PluginModuleType.isOfType(module)) {
+ pluginArtifactTargetIds.add(module.getName()+":plugin");
+ }
+ }
+
+ if (pluginArtifactTargetIds.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return Collections.singletonList(TargetTypeBuildScope.newBuilder().setTypeId(ArtifactBuildTargetType.INSTANCE.getTypeId()).addAllTargetId(pluginArtifactTargetIds).build());
+ }
+}
diff --git a/plugins/devkit/src/build/PrepareAllToDeployAction.java b/plugins/devkit/src/build/PrepareAllToDeployAction.java
new file mode 100644
index 0000000..da57a7d
--- /dev/null
+++ b/plugins/devkit/src/build/PrepareAllToDeployAction.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.build;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.module.PluginModuleType;
+import org.jetbrains.idea.devkit.util.ChooseModulesDialog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PrepareAllToDeployAction extends PrepareToDeployAction {
+
+ public void actionPerformed(final AnActionEvent e) {
+ final Project project = e.getData(PlatformDataKeys.PROJECT);
+ if ( project == null ) return;
+
+ List<Module> pluginModules = new ArrayList<Module>();
+ for (Module aModule : ModuleManager.getInstance(project).getModules()) {
+ if (ModuleType.get(aModule) instanceof PluginModuleType) {
+ pluginModules.add(aModule);
+ }
+ }
+
+ //TODO replace with com.intellij.openapi.roots.ui.configuration.libraryEditor.ChooseModulesDialog
+ ChooseModulesDialog dialog = new ChooseModulesDialog(project, pluginModules, DevKitBundle.message("select.plugin.modules.title"),
+ DevKitBundle.message("select.plugin.modules.description"));
+ dialog.show();
+ if (dialog.isOK()) {
+ doPrepare(dialog.getSelectedModules(), project);
+ }
+ }
+
+ public void update(AnActionEvent e) {
+ int moduleCount = 0;
+ final Project project = e.getData(PlatformDataKeys.PROJECT);
+ if (project != null) {
+ for (Module aModule : (ModuleManager.getInstance(project).getModules())) {
+ if (ModuleType.get(aModule) instanceof PluginModuleType) {
+ moduleCount++;
+ }
+ }
+ }
+ boolean enabled = false;
+ if (moduleCount > 1) {
+ enabled = true;
+ }
+ else if (moduleCount > 0) {
+ final Module module = e.getData(LangDataKeys.MODULE);
+ if (module == null || !(ModuleType.get(module) instanceof PluginModuleType)) {
+ enabled = true;
+ }
+ }
+ e.getPresentation().setVisible(enabled);
+ e.getPresentation().setEnabled(enabled);
+ if (enabled) {
+ e.getPresentation().setText(DevKitBundle.message("prepare.for.deployment.all"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/devkit/src/build/PrepareToDeployAction.java b/plugins/devkit/src/build/PrepareToDeployAction.java
new file mode 100644
index 0000000..4a450c0
--- /dev/null
+++ b/plugins/devkit/src/build/PrepareToDeployAction.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.build;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompileStatusNotification;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.compiler.make.ManifestBuilder;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.CompilerModuleExtension;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.ReadonlyStatusHandler;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.io.ZipUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.module.PluginModuleType;
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+import java.util.jar.Attributes;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * User: anna
+ * Date: May 5, 2005
+ */
+public class PrepareToDeployAction extends AnAction {
+ @NonNls private static final String ZIP_EXTENSION = ".zip";
+ @NonNls private static final String JAR_EXTENSION = ".jar";
+ @NonNls private static final String TEMP_PREFIX = "temp";
+ @NonNls private static final String MIDDLE_LIB_DIR = "lib";
+
+ private final FileTypeManager myFileTypeManager = FileTypeManager.getInstance();
+
+ public void actionPerformed(final AnActionEvent e) {
+ final Module module = LangDataKeys.MODULE.getData(e.getDataContext());
+ if (module != null && ModuleType.get(module) instanceof PluginModuleType) {
+ doPrepare(Arrays.asList(module), PlatformDataKeys.PROJECT.getData(e.getDataContext()));
+ }
+ }
+
+ public void doPrepare(final List<Module> pluginModules, final Project project) {
+ final List<String> errorMessages = new ArrayList<String>();
+ final List<String> successMessages = new ArrayList<String>();
+
+ final CompilerManager compilerManager = CompilerManager.getInstance(project);
+ compilerManager.make(compilerManager.createModulesCompileScope(pluginModules.toArray(new Module[pluginModules.size()]), true),
+ new CompileStatusNotification() {
+ public void finished(final boolean aborted,
+ final int errors,
+ final int warnings,
+ final CompileContext compileContext) {
+ if (aborted || errors != 0) return;
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ public void run() {
+ for (Module aModule : pluginModules) {
+ if (!doPrepare(aModule, errorMessages, successMessages)) {
+ return;
+ }
+ }
+
+ if (!errorMessages.isEmpty()) {
+ Messages.showErrorDialog(errorMessages.iterator().next(), DevKitBundle.message("error.occurred"));
+ }
+ else if (!successMessages.isEmpty()) {
+ StringBuilder messageBuf = new StringBuilder();
+ for (String message : successMessages) {
+ if (messageBuf.length() != 0) {
+ messageBuf.append('\n');
+ }
+ messageBuf.append(message);
+ }
+ Messages.showInfoMessage(messageBuf.toString(),
+ pluginModules.size() == 1
+ ? DevKitBundle.message("success.deployment.message", pluginModules.get(0).getName())
+ : DevKitBundle.message("success.deployment.message.all"));
+ }
+ }
+ });
+ }
+ });
+ }
+
+ private boolean doPrepare(final Module module, final List<String> errorMessages, final List<String> successMessages) {
+ final String pluginName = module.getName();
+ final String defaultPath = new File(module.getModuleFilePath()).getParent() + File.separator + pluginName;
+ final HashSet<Module> modules = new HashSet<Module>();
+ PluginBuildUtil.getDependencies(module, modules);
+ modules.add(module);
+ final Set<Library> libs = new HashSet<Library>();
+ for (Module module1 : modules) {
+ PluginBuildUtil.getLibraries(module1, libs);
+ }
+ final boolean isZip = libs.size() != 0;
+ final String oldPath = defaultPath + (isZip ? JAR_EXTENSION : ZIP_EXTENSION);
+ final File oldFile = new File(oldPath);
+ if (oldFile.exists()) {
+ if (Messages
+ .showYesNoDialog(module.getProject(), DevKitBundle.message("suggest.to.delete", oldPath), DevKitBundle.message("info.message"),
+ Messages.getInformationIcon()) == DialogWrapper.OK_EXIT_CODE) {
+ FileUtil.delete(oldFile);
+ }
+ }
+
+ final String dstPath = defaultPath + (isZip ? ZIP_EXTENSION : JAR_EXTENSION);
+ final File dstFile = new File(dstPath);
+ return clearReadOnly(module.getProject(), dstFile) && ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
+ public void run() {
+
+ final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
+ if (progressIndicator != null) {
+ progressIndicator.setText(DevKitBundle.message("prepare.for.deployment.common"));
+ progressIndicator.setIndeterminate(true);
+ }
+ try {
+ File jarFile = preparePluginsJar(module, modules);
+ if (isZip) {
+ processLibraries(jarFile, dstFile, pluginName, libs, progressIndicator);
+ }
+ else {
+ FileUtil.copy(jarFile, dstFile);
+ }
+ successMessages.add(DevKitBundle.message("saved.message", isZip ? 1 : 2, pluginName, dstPath));
+ }
+ catch (final IOException e) {
+ errorMessages.add(e.getMessage() + "\n(" + dstPath + ")");
+ }
+ }
+ }, DevKitBundle.message("prepare.for.deployment", pluginName), true, module.getProject());
+
+ }
+
+ private static boolean clearReadOnly(final Project project, final File dstFile) {
+ //noinspection EmptyCatchBlock
+ final URL url;
+ try {
+ url = dstFile.toURL();
+ }
+ catch (MalformedURLException e) {
+ return true;
+ }
+ final VirtualFile vfile = VfsUtil.findFileByURL(url);
+ return vfile == null || !ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(vfile).hasReadonlyFiles();
+ }
+
+ private static FileFilter createFilter(final ProgressIndicator progressIndicator, @Nullable final FileTypeManager fileTypeManager) {
+ return new FileFilter() {
+ public boolean accept(File pathName) {
+ if (progressIndicator != null) {
+ progressIndicator.setText2("");
+ }
+ return fileTypeManager == null || !fileTypeManager.isFileIgnored(FileUtil.toSystemIndependentName(pathName.getName()));
+ }
+ };
+ }
+
+ private void processLibraries(final File jarFile,
+ final File zipFile,
+ final String pluginName,
+ final Set<Library> libs,
+ final ProgressIndicator progressIndicator) throws IOException {
+ if (FileUtil.ensureCanCreateFile(zipFile)) {
+ ZipOutputStream zos = null;
+ try {
+ zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
+ addStructure(pluginName, zos);
+ addStructure(pluginName + "/" + MIDDLE_LIB_DIR, zos);
+ final String entryName = pluginName + JAR_EXTENSION;
+ ZipUtil.addFileToZip(zos, jarFile, getZipPath(pluginName, entryName), new HashSet<String>(),
+ createFilter(progressIndicator, myFileTypeManager));
+ Set<String> usedJarNames = new HashSet<String>();
+ usedJarNames.add(entryName);
+ Set<VirtualFile> jarredVirtualFiles = new HashSet<VirtualFile>();
+ for (Library library : libs) {
+ final VirtualFile[] files = library.getFiles(OrderRootType.CLASSES);
+ for (VirtualFile virtualFile : files) {
+ if (jarredVirtualFiles.add(virtualFile)) {
+ if (virtualFile.getFileSystem() instanceof JarFileSystem) {
+ addLibraryJar(virtualFile, zipFile, pluginName, zos, usedJarNames, progressIndicator);
+ }
+ else {
+ makeAndAddLibraryJar(virtualFile, zipFile, pluginName, zos, usedJarNames, progressIndicator, library.getName());
+ }
+ }
+ }
+ }
+ }
+ finally {
+ if (zos != null) zos.close();
+ }
+ }
+ }
+
+ private static String getZipPath(final String pluginName, final String entryName) {
+ return "/" + pluginName + "/" + MIDDLE_LIB_DIR + "/" + entryName;
+ }
+
+ private void makeAndAddLibraryJar(final VirtualFile virtualFile,
+ final File zipFile,
+ final String pluginName,
+ final ZipOutputStream zos,
+ final Set<String> usedJarNames,
+ final ProgressIndicator progressIndicator,
+ final String preferredName) throws IOException {
+ File libraryJar = FileUtil.createTempFile(TEMP_PREFIX, JAR_EXTENSION);
+ libraryJar.deleteOnExit();
+ ZipOutputStream jar = null;
+ try {
+ jar = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(libraryJar)));
+ ZipUtil.addFileOrDirRecursively(jar, libraryJar, VfsUtil.virtualToIoFile(virtualFile), "",
+ createFilter(progressIndicator, myFileTypeManager), null);
+ }
+ finally {
+ if (jar != null) jar.close();
+ }
+ final String jarName =
+ getLibraryJarName(virtualFile.getName() + JAR_EXTENSION, usedJarNames, preferredName == null ? null : preferredName + JAR_EXTENSION);
+ ZipUtil.addFileOrDirRecursively(zos, zipFile, libraryJar, getZipPath(pluginName, jarName), createFilter(progressIndicator, null), null);
+ }
+
+ private static String getLibraryJarName(final String fileName, Set<String> usedJarNames, @Nullable final String preferredName) {
+ String uniqueName;
+ if (preferredName != null && !usedJarNames.contains(preferredName)) {
+ uniqueName = preferredName;
+ }
+ else {
+ uniqueName = fileName;
+ if (usedJarNames.contains(uniqueName)) {
+ final int dotPos = uniqueName.lastIndexOf(".");
+ final String name = dotPos < 0 ? uniqueName : uniqueName.substring(0, dotPos);
+ final String ext = dotPos < 0 ? "" : uniqueName.substring(dotPos);
+
+ int i = 0;
+ do {
+ i++;
+ uniqueName = name + i + ext;
+ }
+ while (usedJarNames.contains(uniqueName));
+ }
+ }
+ usedJarNames.add(uniqueName);
+ return uniqueName;
+ }
+
+ private static void addLibraryJar(final VirtualFile virtualFile,
+ final File zipFile,
+ final String pluginName,
+ final ZipOutputStream zos,
+ final Set<String> usedJarNames,
+ final ProgressIndicator progressIndicator) throws IOException {
+ File ioFile = VfsUtil.virtualToIoFile(virtualFile);
+ final String jarName = getLibraryJarName(ioFile.getName(), usedJarNames, null);
+ ZipUtil.addFileOrDirRecursively(zos, zipFile, ioFile, getZipPath(pluginName, jarName), createFilter(progressIndicator, null), null);
+ }
+
+ private static void addStructure(@NonNls final String relativePath, final ZipOutputStream zos) throws IOException {
+ ZipEntry e = new ZipEntry(relativePath + "/");
+ e.setMethod(ZipEntry.STORED);
+ e.setSize(0);
+ e.setCrc(0);
+ zos.putNextEntry(e);
+ zos.closeEntry();
+ }
+
+ private File preparePluginsJar(Module module, final HashSet<Module> modules) throws IOException {
+ final PluginBuildConfiguration pluginModuleBuildProperties = PluginBuildConfiguration.getInstance(module);
+ File jarFile = FileUtil.createTempFile(TEMP_PREFIX, JAR_EXTENSION);
+ jarFile.deleteOnExit();
+ final Manifest manifest = createOrFindManifest(pluginModuleBuildProperties);
+ ZipOutputStream jarPlugin = null;
+ try {
+ jarPlugin = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(jarFile)), manifest);
+ final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
+ final HashSet<String> writtenItemRelativePaths = new HashSet<String>();
+ for (Module module1 : modules) {
+ final VirtualFile compilerOutputPath = CompilerModuleExtension.getInstance(module1).getCompilerOutputPath();
+ if (compilerOutputPath == null) continue; //pre-condition: output dirs for all modules are up-to-date
+ ZipUtil.addDirToZipRecursively(jarPlugin, jarFile, new File(compilerOutputPath.getPath()), "",
+ createFilter(progressIndicator, myFileTypeManager), writtenItemRelativePaths);
+ }
+ final String pluginXmlPath = pluginModuleBuildProperties.getPluginXmlPath();
+ @NonNls final String metaInf = "/META-INF/plugin.xml";
+ ZipUtil.addFileToZip(jarPlugin, new File(pluginXmlPath), metaInf, writtenItemRelativePaths, createFilter(progressIndicator, null));
+ }
+ finally {
+ if (jarPlugin != null) jarPlugin.close();
+ }
+ return jarFile;
+ }
+
+ public static Manifest createOrFindManifest(final PluginBuildConfiguration pluginModuleBuildProperties) throws IOException {
+ final Manifest manifest = new Manifest();
+ final VirtualFile vManifest = pluginModuleBuildProperties.getManifest();
+ if (pluginModuleBuildProperties.isUseUserManifest() && vManifest != null) {
+ InputStream in = null;
+ try {
+ in = new BufferedInputStream(vManifest.getInputStream());
+ manifest.read(in);
+ }
+ finally {
+ if (in != null) in.close();
+ }
+ }
+ else {
+ Attributes mainAttributes = manifest.getMainAttributes();
+ ManifestBuilder.setGlobalAttributes(mainAttributes);
+ }
+ return manifest;
+ }
+
+ public void update(AnActionEvent e) {
+ final Module module = LangDataKeys.MODULE.getData(e.getDataContext());
+ boolean enabled = module != null && ModuleType.get(module) instanceof PluginModuleType;
+ e.getPresentation().setVisible(enabled);
+ e.getPresentation().setEnabled(enabled);
+ if (enabled) {
+ e.getPresentation().setText(DevKitBundle.message("prepare.for.deployment", module.getName()));
+ }
+ }
+}
diff --git a/plugins/devkit/src/build/ant/BuildJarTarget.java b/plugins/devkit/src/build/ant/BuildJarTarget.java
new file mode 100644
index 0000000..2a8fd29
--- /dev/null
+++ b/plugins/devkit/src/build/ant/BuildJarTarget.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * User: anna
+ * Date: 19-Dec-2006
+ */
+package org.jetbrains.idea.devkit.build.ant;
+
+import com.intellij.compiler.ant.*;
+import com.intellij.compiler.ant.taskdefs.*;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.CompilerModuleExtension;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.build.PluginBuildConfiguration;
+import org.jetbrains.idea.devkit.build.PluginBuildUtil;
+import org.jetbrains.idea.devkit.build.PrepareToDeployAction;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+
+public class BuildJarTarget extends Target {
+ public BuildJarTarget(final ModuleChunk chunk,
+ final GenerationOptions genOptions,
+ final PluginBuildConfiguration moduleBuildProperties) {
+ super(PluginBuildProperties.getBuildJarTargetName(chunk.getName()), BuildProperties.getCompileTargetName(chunk.getName()),
+ DevKitBundle.message("ant.build.jar.description", chunk.getName()), null);
+
+ final File moduleBaseDir = chunk.getBaseDir();
+
+ final Module[] modules = chunk.getModules();
+
+ final Module module = modules[0];
+
+ final String moduleName = module.getName();
+
+
+ final HashSet<Library> libs = new HashSet<Library>();
+ for (Module module1 : modules) {
+ PluginBuildUtil.getLibraries(module1, libs);
+ }
+
+ final String jarPathPropertyRef = BuildProperties.propertyRef(PluginBuildProperties.getJarPathProperty(moduleName));
+
+ if (libs.isEmpty()) {
+ add(createPluginsJar(jarPathPropertyRef, modules, moduleBaseDir, genOptions, moduleBuildProperties));
+ } else {
+ @NonNls final String tempSuffix = "temp";
+ final File jarDir = new File(moduleBaseDir.getParentFile(), tempSuffix);
+ String tempDir = GenerationUtils.toRelativePath(jarDir.getPath(), chunk, genOptions);
+ final String tempDirProperty = BuildProperties.getTempDirForModuleProperty(moduleName);
+
+ add(new Property(tempDirProperty, tempDir));
+ add(new Mkdir(BuildProperties.propertyRef(tempDirProperty)));
+
+ add(new Mkdir(BuildProperties.propertyRef(tempDirProperty) + "/lib"));
+
+ final @NonNls String libRelativePath = BuildProperties.propertyRef(tempDirProperty) + "/lib/";
+
+ add(createPluginsJar(libRelativePath + chunk.getName() + ".jar", modules, moduleBaseDir, genOptions, moduleBuildProperties));
+
+ for (Library lib : libs) {
+ final VirtualFile[] files = lib.getFiles(OrderRootType.CLASSES);
+ for (VirtualFile file : files) {
+ final String relativePath = GenerationUtils.toRelativePath(file, chunk, genOptions);
+ if (file.getFileSystem() instanceof JarFileSystem) {
+ add(new Copy(relativePath, libRelativePath + file.getName()));
+ } else {
+ final Jar jar = new Jar(libRelativePath + file.getNameWithoutExtension() + ".jar", "preserve");
+ jar.add(new ZipFileSet(relativePath, "", true));
+ add(jar);
+ }
+ }
+ }
+
+ final Tag zipTag = new Zip(jarPathPropertyRef);
+ zipTag.add(new FileSet(tempDir));
+ add(zipTag);
+
+ add(new Delete(BuildProperties.propertyRef(tempDirProperty)));
+ }
+ }
+
+ private static Tag createPluginsJar(@NonNls final String jarPathProperty,
+ final Module[] modules, final File moduleBaseDir,
+ final GenerationOptions genOptions,
+ final PluginBuildConfiguration moduleBuildProperties) {
+ final Tag jarTag = new Jar(jarPathProperty, "preserve");
+ for (Module m : modules) {
+ final String path = VfsUtil.urlToPath(CompilerModuleExtension.getInstance(m).getCompilerOutputUrl());
+ final String relativePath = GenerationUtils.toRelativePath(path, moduleBaseDir, m, genOptions);
+ jarTag.add(new ZipFileSet(relativePath, "", true));
+ }
+ final Module module = modules[0];
+
+ //plugin.xml
+
+ final String pluginXmlPath = moduleBuildProperties.getPluginXmlPath();
+ final String relativePluginXMLPath = GenerationUtils.toRelativePath(pluginXmlPath, moduleBaseDir, module, genOptions);
+ jarTag.add(new ZipFileSet(relativePluginXMLPath, "META-INF/plugin.xml", false));
+
+ //manifest
+ final Manifest manifestTag = new Manifest();
+ jarTag.add(manifestTag);
+ final java.util.jar.Manifest manifest;
+ try {
+ manifest = PrepareToDeployAction.createOrFindManifest(moduleBuildProperties);
+ }
+ catch (IOException e) {
+ return jarTag;
+ }
+ manifestTag.applyAttributes(manifest);
+ return jarTag;
+ }
+
+}
\ No newline at end of file
diff --git a/plugins/devkit/src/build/ant/ChunkBuildPluginExtension.java b/plugins/devkit/src/build/ant/ChunkBuildPluginExtension.java
new file mode 100644
index 0000000..355c6ce
--- /dev/null
+++ b/plugins/devkit/src/build/ant/ChunkBuildPluginExtension.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * User: anna
+ * Date: 19-Dec-2006
+ */
+package org.jetbrains.idea.devkit.build.ant;
+
+import com.intellij.compiler.ant.*;
+import com.intellij.compiler.ant.taskdefs.Property;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.util.ArrayUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.build.PluginBuildConfiguration;
+import org.jetbrains.idea.devkit.build.PluginBuildUtil;
+import org.jetbrains.idea.devkit.module.PluginModuleType;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class ChunkBuildPluginExtension extends ChunkBuildExtension {
+
+ @NotNull
+ public String[] getTargets(final ModuleChunk chunk) {
+ return isPlugins(chunk.getModules()) ? new String[] {PluginBuildProperties.getBuildJarTargetName(chunk.getName())}
+ : ArrayUtil.EMPTY_STRING_ARRAY;
+ }
+
+ public void process(Project project, ModuleChunk chunk, GenerationOptions genOptions, CompositeGenerator generator) {
+
+ final Module[] modules = chunk.getModules();
+ if (isPlugins(modules)) {
+ final BuildTargetsFactory factory = BuildTargetsFactory.getInstance();
+ final Module module = modules[0];
+ PluginBuildConfiguration buildProperties = PluginBuildConfiguration.getInstance(module);
+
+ final Set<Library> libs = new HashSet<Library>();
+ PluginBuildUtil.getLibraries(module, libs);
+ @NonNls String jarPath = chunk.getBaseDir().getPath() + "/" + chunk.getName();
+ if (libs.isEmpty()) {
+ jarPath += ".jar";
+ } else {
+ jarPath += ".zip";
+ }
+
+ generator.add(new Property(PluginBuildProperties.getJarPathProperty(chunk.getName()), GenerationUtils.toRelativePath(jarPath, chunk, genOptions)), 1);
+
+ generator.add(factory.createComment(DevKitBundle.message("ant.build.jar.comment", chunk.getName())), 1);
+ generator.add(new BuildJarTarget(chunk, genOptions, buildProperties));
+ }
+ }
+
+ private static boolean isPlugins(final Module[] modules) {
+ return modules.length == 1 && PluginModuleType.isOfType(modules[0]);
+ }
+
+}
\ No newline at end of file
diff --git a/plugins/devkit/src/build/ant/PluginBuildProperties.java b/plugins/devkit/src/build/ant/PluginBuildProperties.java
new file mode 100644
index 0000000..b709f18
--- /dev/null
+++ b/plugins/devkit/src/build/ant/PluginBuildProperties.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.idea.devkit.build.ant;
+
+import com.intellij.compiler.ant.BuildProperties;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author nik
+ */
+public class PluginBuildProperties {
+ private PluginBuildProperties() {
+ }
+
+ @NonNls
+ public static String getBuildJarTargetName(final String configurationName) {
+ return "plugin.build.jar." + BuildProperties.convertName(configurationName);
+ }
+
+ @NonNls
+ public static String getJarPathProperty(final String configurationName) {
+ return BuildProperties.convertName(configurationName) + ".plugin.path.jar";
+ }
+
+}
diff --git a/plugins/devkit/src/dom/Action.java b/plugins/devkit/src/dom/Action.java
new file mode 100644
index 0000000..89f3546
--- /dev/null
+++ b/plugins/devkit/src/dom/Action.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.psi.PsiClass;
+import com.intellij.util.xml.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.dom.impl.PluginPsiClassConverter;
+
+import java.util.List;
+
+/**
+ * plugin.dtd:action interface.
+ */
+public interface Action extends DomElement {
+
+ /**
+ * Returns the value of the popup child.
+ * Attribute popup
+ * @return the value of the popup child.
+ */
+ @NotNull
+ GenericAttributeValue<String> getPopup();
+
+
+ /**
+ * Returns the value of the icon child.
+ * Attribute icon
+ * @return the value of the icon child.
+ */
+ @NotNull
+ GenericAttributeValue<String> getIcon();
+
+
+ /**
+ * Returns the value of the description child.
+ * Attribute description
+ * @return the value of the description child.
+ */
+ @NotNull
+ GenericAttributeValue<String> getDescription();
+
+
+ /**
+ * Returns the value of the class child.
+ * Attribute class
+ * @return the value of the class child.
+ */
+ @NotNull
+ @Attribute ("class")
+ @Required
+ @ExtendClass(value = "com.intellij.openapi.actionSystem.AnAction",
+ instantiatable = true, allowNonPublic = true, allowAbstract = false, allowInterface = false)
+ @Convert(PluginPsiClassConverter.class)
+ GenericAttributeValue<PsiClass> getClazz();
+
+
+ /**
+ * Returns the value of the text child.
+ * Attribute text
+ * @return the value of the text child.
+ */
+ @NotNull
+ GenericAttributeValue<String> getText();
+
+
+ /**
+ * Returns the value of the id child.
+ * Attribute id
+ * @return the value of the id child.
+ */
+ @NotNull
+ @Required
+ GenericAttributeValue<String> getId();
+
+
+ /**
+ * Returns the list of keyboard-shortcut children.
+ * @return the list of keyboard-shortcut children.
+ */
+ @NotNull
+ List<KeyboardShortcut> getKeyboardShortcuts();
+ /**
+ * Adds new child to the list of keyboard-shortcut children.
+ * @return created child
+ */
+ KeyboardShortcut addKeyboardShortcut();
+
+
+ /**
+ * Returns the list of mouse-shortcut children.
+ * @return the list of mouse-shortcut children.
+ */
+ @NotNull
+ List<MouseShortcut> getMouseShortcuts();
+ /**
+ * Adds new child to the list of mouse-shortcut children.
+ * @return created child
+ */
+ MouseShortcut addMouseShortcut();
+
+
+ /**
+ * Returns the list of shortcut children.
+ * @return the list of shortcut children.
+ */
+ @NotNull
+ List<Shortcut> getShortcuts();
+ /**
+ * Adds new child to the list of shortcut children.
+ * @return created child
+ */
+ Shortcut addShortcut();
+
+
+ /**
+ * Returns the list of add-to-group children.
+ * @return the list of add-to-group children.
+ */
+ @NotNull
+ List<AddToGroup> getAddToGroups();
+ /**
+ * Adds new child to the list of add-to-group children.
+ * @return created child
+ */
+ AddToGroup addAddToGroup();
+
+ @NotNull
+ GenericAttributeValue<String> getUseShortcutOf();
+
+ @NotNull
+ GenericAttributeValue<String> getKeymap();
+}
diff --git a/plugins/devkit/src/dom/Actions.java b/plugins/devkit/src/dom/Actions.java
new file mode 100644
index 0000000..3fa4320
--- /dev/null
+++ b/plugins/devkit/src/dom/Actions.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.DomElement;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public interface Actions extends DomElement {
+
+ @NotNull
+ List<Action> getActions();
+
+ Action addAction();
+
+
+ @NotNull
+ List<Group> getGroups();
+
+ Group addGroup();
+
+ @NotNull
+ List<Reference> getReferences();
+
+ Reference addReference();
+}
diff --git a/plugins/devkit/src/dom/AddToGroup.java b/plugins/devkit/src/dom/AddToGroup.java
new file mode 100644
index 0000000..629f5f0
--- /dev/null
+++ b/plugins/devkit/src/dom/AddToGroup.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.GenericAttributeValue;
+import com.intellij.util.xml.Required;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * plugin.dtd:add-to-group interface.
+ */
+public interface AddToGroup extends DomElement {
+
+ /**
+ * Returns the value of the anchor child.
+ * Attribute anchor
+ * @return the value of the anchor child.
+ */
+ @NotNull
+ GenericAttributeValue<Anchor> getAnchor();
+
+
+ /**
+ * Returns the value of the relative-to-action child.
+ * Attribute relative-to-action
+ * @return the value of the relative-to-action child.
+ */
+ @NotNull
+ GenericAttributeValue<String> getRelativeToAction();
+
+
+ /**
+ * Returns the value of the group-id child.
+ * Attribute group-id
+ * @return the value of the group-id child.
+ */
+ @NotNull
+ @Required
+ GenericAttributeValue<String> getGroupId();
+
+
+}
diff --git a/plugins/devkit/src/dom/Anchor.java b/plugins/devkit/src/dom/Anchor.java
new file mode 100644
index 0000000..e388d43
--- /dev/null
+++ b/plugins/devkit/src/dom/Anchor.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2000-2010 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.dom;
+
+/**
+ * @author Dmitry Avdeev
+ */
+public enum Anchor {
+
+ first, last, before, after
+}
diff --git a/plugins/devkit/src/dom/ApplicationComponents.java b/plugins/devkit/src/dom/ApplicationComponents.java
new file mode 100644
index 0000000..b6a29d4
--- /dev/null
+++ b/plugins/devkit/src/dom/ApplicationComponents.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.DomElement;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * plugin.dtd:application-components interface.
+ */
+public interface ApplicationComponents extends DomElement {
+
+ /**
+ * Returns the list of component children.
+ * @return the list of component children.
+ */
+ @NotNull
+ List<Component.Application> getComponents();
+ /**
+ * Adds new child to the list of component children.
+ * @return created child
+ */
+ Component.Application addComponent();
+
+
+}
diff --git a/plugins/devkit/src/dom/Component.java b/plugins/devkit/src/dom/Component.java
new file mode 100644
index 0000000..ddb9cec
--- /dev/null
+++ b/plugins/devkit/src/dom/Component.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.psi.PsiClass;
+import com.intellij.util.xml.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.dom.impl.PluginPsiClassConverter;
+
+import java.util.List;
+
+/**
+ * plugin.dtd:component interface.
+ */
+public interface Component extends DomElement {
+
+ @NotNull
+ @Required
+ @Convert(PluginPsiClassConverter.class)
+ GenericDomValue<PsiClass> getImplementationClass();
+
+
+ @NotNull
+ @ExtendClass(instantiatable = false)
+ @Convert(PluginPsiClassConverter.class)
+ GenericDomValue<PsiClass> getInterfaceClass();
+
+ @NotNull
+ @Convert(PluginPsiClassConverter.class)
+ GenericDomValue<PsiClass> getHeadlessImplementationClass();
+
+ @NotNull
+ List<Option> getOptions();
+
+ Option addOption();
+
+ interface Application extends Component {
+ }
+
+ interface Module extends Component {
+ }
+
+ interface Project extends Component {
+ @NotNull
+ @SubTag(value = "skipForDefaultProject", indicator = true)
+ GenericDomValue<Boolean> getSkipForDefaultProject();
+
+ @NotNull
+ @SubTag(value = "loadForDefaultProject", indicator = true)
+ GenericDomValue<Boolean> getLoadForDefaultProject();
+ }
+}
diff --git a/plugins/devkit/src/dom/Dependency.java b/plugins/devkit/src/dom/Dependency.java
new file mode 100644
index 0000000..b2375c8
--- /dev/null
+++ b/plugins/devkit/src/dom/Dependency.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.Convert;
+import com.intellij.util.xml.GenericAttributeValue;
+import com.intellij.util.xml.GenericDomValue;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.dom.impl.IdeaPluginConverter;
+
+@Convert(IdeaPluginConverter.class)
+public interface Dependency extends GenericDomValue<IdeaPlugin> {
+ @NotNull
+ GenericAttributeValue<String> getOptional();
+ @NotNull
+ GenericAttributeValue<String> getConfigFile();
+}
diff --git a/plugins/devkit/src/dom/Extension.java b/plugins/devkit/src/dom/Extension.java
new file mode 100644
index 0000000..e7b5d66
--- /dev/null
+++ b/plugins/devkit/src/dom/Extension.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.GenericAttributeValue;
+import com.intellij.util.xml.NameValue;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author mike
+ */
+public interface Extension extends DomElement {
+
+ @NameValue
+ GenericAttributeValue<String> getId();
+
+ GenericAttributeValue<String> getOrder();
+
+ @Nullable
+ ExtensionPoint getExtensionPoint();
+}
diff --git a/plugins/devkit/src/dom/ExtensionPoint.java b/plugins/devkit/src/dom/ExtensionPoint.java
new file mode 100644
index 0000000..1c5662e
--- /dev/null
+++ b/plugins/devkit/src/dom/ExtensionPoint.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.ide.presentation.Presentation;
+import com.intellij.psi.PsiClass;
+import com.intellij.util.xml.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.dom.impl.PluginPsiClassConverter;
+
+/**
+ * @author mike
+ */
+@Presentation(typeName = "Extension Point")
+public interface ExtensionPoint extends DomElement {
+ enum Area {
+ IDEA_PROJECT,
+ IDEA_MODULE,
+ IDEA_APPLICATION
+ }
+
+ @NotNull
+ @NameValue
+ GenericAttributeValue<String> getName();
+
+ @Attribute("qualifiedName")
+ GenericAttributeValue<String> getQualifiedName();
+
+ @NotNull
+ @Convert(PluginPsiClassConverter.class)
+ GenericAttributeValue<PsiClass> getInterface();
+
+ @NotNull
+ @Attribute("beanClass")
+ @Convert(PluginPsiClassConverter.class)
+ GenericAttributeValue<PsiClass> getBeanClass();
+
+ @NotNull
+ GenericAttributeValue<Area> getArea();
+}
diff --git a/plugins/devkit/src/dom/ExtensionPoints.java b/plugins/devkit/src/dom/ExtensionPoints.java
new file mode 100644
index 0000000..cfc51e0
--- /dev/null
+++ b/plugins/devkit/src/dom/ExtensionPoints.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.SubTagList;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author mike
+ */
+public interface ExtensionPoints extends DomElement {
+ @NotNull
+ @SubTagList("extensionPoint")
+ List<ExtensionPoint> getExtensionPoints();
+ ExtensionPoint addExtensionPoint();
+}
diff --git a/plugins/devkit/src/dom/Extensions.java b/plugins/devkit/src/dom/Extensions.java
new file mode 100644
index 0000000..6428d35
--- /dev/null
+++ b/plugins/devkit/src/dom/Extensions.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.Attribute;
+import com.intellij.util.xml.Convert;
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.GenericAttributeValue;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.dom.impl.ExtensionNsConverter;
+
+import java.util.List;
+
+public interface Extensions extends DomElement {
+ @NotNull
+ @Attribute("defaultExtensionNs")
+ @Convert(value=ExtensionNsConverter.class, soft=true)
+ GenericAttributeValue<IdeaPlugin> getDefaultExtensionNs();
+
+ @NotNull
+ @Convert(value=ExtensionNsConverter.class, soft=true)
+ GenericAttributeValue<IdeaPlugin> getXmlns();
+
+ List<Extension> getExtensions();
+ Extension addExtension();
+
+ Extension addExtension(String name);
+}
diff --git a/plugins/devkit/src/dom/Group.java b/plugins/devkit/src/dom/Group.java
new file mode 100644
index 0000000..451e036
--- /dev/null
+++ b/plugins/devkit/src/dom/Group.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.psi.PsiClass;
+import com.intellij.util.xml.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.dom.impl.PluginPsiClassConverter;
+
+import java.util.List;
+
+/**
+ * plugin.dtd:group interface.
+ */
+public interface Group extends Actions {
+
+ /**
+ * Returns the value of the popup child.
+ * Attribute popup
+ * @return the value of the popup child.
+ */
+ @NotNull
+ GenericAttributeValue<String> getPopup();
+
+
+ /**
+ * Returns the value of the icon child.
+ * Attribute icon
+ * @return the value of the icon child.
+ */
+ @NotNull
+ GenericAttributeValue<String> getIcon();
+
+
+ /**
+ * Returns the value of the description child.
+ * Attribute description
+ * @return the value of the description child.
+ */
+ @NotNull
+ GenericAttributeValue<String> getDescription();
+
+
+ /**
+ * Returns the value of the class child.
+ * Attribute class
+ * @return the value of the class child.
+ */
+ @NotNull
+ @com.intellij.util.xml.Attribute ("class")
+ @ExtendClass(value = "com.intellij.openapi.actionSystem.ActionGroup",
+ instantiatable = true, allowAbstract = false, allowInterface = false)
+ @Convert(PluginPsiClassConverter.class)
+ GenericAttributeValue<PsiClass> getClazz();
+
+
+ /**
+ * Returns the value of the text child.
+ * Attribute text
+ * @return the value of the text child.
+ */
+ @NotNull
+ GenericAttributeValue<String> getText();
+
+
+ @NotNull
+ GenericAttributeValue<String> getId();
+
+ /**
+ * Returns the list of reference children.
+ * @return the list of reference children.
+ */
+ @NotNull
+ List<Reference> getReferences();
+ /**
+ * Adds new child to the list of reference children.
+ * @return created child
+ */
+ Reference addReference();
+
+
+ /**
+ * Returns the value of the separator child.
+ * @return the value of the separator child.
+ */
+ @NotNull
+ List<GenericDomValue<String>> getSeparators();
+
+
+ /**
+ * Returns the list of action children.
+ * @return the list of action children.
+ */
+ @NotNull
+ List<Action> getActions();
+ /**
+ * Adds new child to the list of action children.
+ * @return created child
+ */
+ Action addAction();
+
+
+ /**
+ * Returns the list of add-to-group children.
+ * @return the list of add-to-group children.
+ */
+ @NotNull
+ List<AddToGroup> getAddToGroups();
+ /**
+ * Adds new child to the list of add-to-group children.
+ * @return created child
+ */
+ AddToGroup addAddToGroup();
+
+
+}
diff --git a/plugins/devkit/src/dom/Helpset.java b/plugins/devkit/src/dom/Helpset.java
new file mode 100644
index 0000000..24005f1
--- /dev/null
+++ b/plugins/devkit/src/dom/Helpset.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.GenericAttributeValue;
+import com.intellij.util.xml.Required;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * plugin.dtd:helpset interface.
+ * Type helpset documentation
+ * <pre>
+ * helpset is a name of file from PLUGIN/help/ folder
+ * Example: <helpset file="myhelp.jar" path="/Help.hs"/>
+ *
+ * </pre>
+ */
+public interface Helpset extends DomElement {
+
+ /**
+ * Returns the value of the file child.
+ * Attribute file
+ * @return the value of the file child.
+ */
+ @NotNull
+ @Required
+ GenericAttributeValue<String> getFile();
+
+
+ /**
+ * Returns the value of the path child.
+ * Attribute path
+ * @return the value of the path child.
+ */
+ @NotNull
+ @Required
+ GenericAttributeValue<String> getPath();
+
+
+}
diff --git a/plugins/devkit/src/dom/IdeaPlugin.java b/plugins/devkit/src/dom/IdeaPlugin.java
new file mode 100644
index 0000000..1e93ca3
--- /dev/null
+++ b/plugins/devkit/src/dom/IdeaPlugin.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * plugin.dtd:idea-plugin interface.
+ */
+@DefinesXml
+public interface IdeaPlugin extends DomElement {
+ @Nullable
+ String getPluginId();
+
+ @NotNull
+ @NameValue
+ GenericDomValue<String> getId();
+
+ @NotNull
+ GenericAttributeValue<String> getVersion();
+
+ @NotNull
+ GenericAttributeValue<String> getUrl();
+
+ @NotNull
+ GenericAttributeValue<Boolean> getUseIdeaClassloader();
+
+ @NotNull
+ GenericDomValue<String> getName();
+
+
+ @NotNull
+ List<GenericDomValue<String>> getDescriptions();
+ GenericDomValue<String> addDescription();
+
+
+ @NotNull
+ List<GenericDomValue<String>> getVersions();
+ GenericDomValue<String> addVersion();
+
+
+ @NotNull
+ List<Vendor> getVendors();
+ Vendor addVendor();
+
+
+ @NotNull
+ List<GenericDomValue<String>> getChangeNotess();
+ GenericDomValue<String> addChangeNotes();
+
+
+ @NotNull
+ List<IdeaVersion> getIdeaVersions();
+ IdeaVersion addIdeaVersion();
+
+
+ @NotNull
+ List<GenericDomValue<String>> getCategories();
+ GenericDomValue<String> addCategory();
+
+
+ @NotNull
+ @SubTagList("resource-bundle")
+ List<GenericDomValue<String>> getResourceBundles();
+ GenericDomValue<String> addResourceBundle();
+
+
+ @NotNull
+ @SubTagList("depends")
+ List<Dependency> getDependencies();
+ @SubTagList("depends")
+ Dependency addDependency();
+
+ @NotNull
+ @SubTagList("module")
+ List<PluginModule> getModules();
+
+ @NotNull
+ @SubTagList("extensions")
+ List<Extensions> getExtensions();
+ Extensions addExtensions();
+
+ @NotNull
+ @SubTagList("extensionPoints")
+ List<ExtensionPoints> getExtensionPoints();
+ ExtensionPoints addExtensionPoints();
+
+
+ @NotNull
+ ApplicationComponents getApplicationComponents();
+
+ @NotNull
+ ProjectComponents getProjectComponents();
+
+ @NotNull
+ ModuleComponents getModuleComponents();
+
+
+ @NotNull
+ @SubTagList("actions")
+ List<Actions> getActions();
+ Actions addActions();
+
+
+ @NotNull
+ List<Helpset> getHelpsets();
+
+ Helpset addHelpset();
+}
diff --git a/plugins/devkit/src/dom/IdeaVersion.java b/plugins/devkit/src/dom/IdeaVersion.java
new file mode 100644
index 0000000..88ca04a
--- /dev/null
+++ b/plugins/devkit/src/dom/IdeaVersion.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.GenericAttributeValue;
+import com.intellij.util.xml.Required;
+import org.jetbrains.annotations.NotNull;
+
+public interface IdeaVersion extends DomElement {
+ @NotNull
+ GenericAttributeValue<String> getMax();
+
+ @NotNull
+ @Required
+ GenericAttributeValue<String> getSinceBuild();
+
+ @NotNull
+ GenericAttributeValue<String> getUntilBuild();
+
+
+ @NotNull
+ GenericAttributeValue<String> getMin();
+}
diff --git a/plugins/devkit/src/dom/KeyboardShortcut.java b/plugins/devkit/src/dom/KeyboardShortcut.java
new file mode 100644
index 0000000..c603567
--- /dev/null
+++ b/plugins/devkit/src/dom/KeyboardShortcut.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.GenericAttributeValue;
+import com.intellij.util.xml.Required;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * plugin.dtd:keyboard-shortcut interface.
+ */
+public interface KeyboardShortcut extends DomElement {
+
+ /**
+ * Returns the value of the first-keystroke child.
+ * Attribute first-keystroke
+ * @return the value of the first-keystroke child.
+ */
+ @NotNull
+ @Required
+ GenericAttributeValue<String> getFirstKeystroke();
+
+
+ /**
+ * Returns the value of the keymap child.
+ * Attribute keymap
+ * @return the value of the keymap child.
+ */
+ @NotNull
+ @Required
+ GenericAttributeValue<String> getKeymap();
+
+
+ /**
+ * Returns the value of the use-shortcut-of child.
+ * Attribute use-shortcut-of
+ * @return the value of the use-shortcut-of child.
+ */
+ @NotNull
+ GenericAttributeValue<String> getUseShortcutOf();
+
+
+ /**
+ * Returns the value of the second-keystroke child.
+ * Attribute second-keystroke
+ * @return the value of the second-keystroke child.
+ */
+ @NotNull
+ GenericAttributeValue<String> getSecondKeystroke();
+
+
+ /**
+ * Returns the value of the should current shortcut be removed or not.
+ * Attribute remove option
+ * @return the value of the should current shortcut be removed or not.
+ */
+ @NotNull
+ GenericAttributeValue<String> getRemove();
+
+ /**
+ * Returns the value of the should all previous shortcuts be removed by that one or not.
+ * Attribute remove option
+ * @return the value of the should all previous shortcuts be removed by that one or not.
+ */
+ @NotNull
+ GenericAttributeValue<String> getReplaceAll();
+
+}
diff --git a/plugins/devkit/src/dom/ModuleComponents.java b/plugins/devkit/src/dom/ModuleComponents.java
new file mode 100644
index 0000000..62f898c
--- /dev/null
+++ b/plugins/devkit/src/dom/ModuleComponents.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.DomElement;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * plugin.dtd:module-components interface.
+ */
+public interface ModuleComponents extends DomElement {
+
+ /**
+ * Returns the list of component children.
+ * @return the list of component children.
+ */
+ @NotNull
+ List<Component.Module> getComponents();
+ /**
+ * Adds new child to the list of component children.
+ * @return created child
+ */
+ Component.Module addComponent();
+
+
+}
diff --git a/plugins/devkit/src/dom/MouseShortcut.java b/plugins/devkit/src/dom/MouseShortcut.java
new file mode 100644
index 0000000..fc0544a
--- /dev/null
+++ b/plugins/devkit/src/dom/MouseShortcut.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.GenericAttributeValue;
+import com.intellij.util.xml.Required;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * plugin.dtd:mouse-shortcut interface.
+ */
+public interface MouseShortcut extends DomElement {
+
+ /**
+ * Returns the value of the keymap child.
+ * Attribute keymap
+ * @return the value of the keymap child.
+ */
+ @NotNull
+ @Required
+ GenericAttributeValue<String> getKeymap();
+
+
+ /**
+ * Returns the value of the keystroke child.
+ * Attribute keystroke
+ * @return the value of the keystroke child.
+ */
+ @NotNull
+ @Required
+ GenericAttributeValue<String> getKeystroke();
+
+
+}
diff --git a/plugins/devkit/src/dom/Option.java b/plugins/devkit/src/dom/Option.java
new file mode 100644
index 0000000..d7ca965
--- /dev/null
+++ b/plugins/devkit/src/dom/Option.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.GenericAttributeValue;
+import com.intellij.util.xml.Required;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * plugin.dtd:option interface.
+ */
+public interface Option extends DomElement {
+
+ /**
+ * Returns the value of the name child.
+ * Attribute name
+ * @return the value of the name child.
+ */
+ @NotNull
+ @Required
+ GenericAttributeValue<String> getName();
+
+
+ /**
+ * Returns the value of the value child.
+ * Attribute value
+ * @return the value of the value child.
+ */
+ @NotNull
+ @Required
+ GenericAttributeValue<String> getValue();
+
+
+}
diff --git a/plugins/devkit/src/dom/PluginModule.java b/plugins/devkit/src/dom/PluginModule.java
new file mode 100644
index 0000000..2efc1ca
--- /dev/null
+++ b/plugins/devkit/src/dom/PluginModule.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.GenericAttributeValue;
+import com.intellij.util.xml.NameValue;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Dmitry Avdeev
+ */
+public interface PluginModule extends DomElement {
+
+ @NotNull
+ @NameValue
+ GenericAttributeValue<String> getValue();
+
+}
diff --git a/plugins/devkit/src/dom/ProjectComponents.java b/plugins/devkit/src/dom/ProjectComponents.java
new file mode 100644
index 0000000..d87425f
--- /dev/null
+++ b/plugins/devkit/src/dom/ProjectComponents.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.DomElement;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * plugin.dtd:project-components interface.
+ */
+public interface ProjectComponents extends DomElement {
+
+ @NotNull
+ List<Component.Project> getComponents();
+
+ Component.Project addComponent();
+}
diff --git a/plugins/devkit/src/dom/Reference.java b/plugins/devkit/src/dom/Reference.java
new file mode 100644
index 0000000..57f1bea
--- /dev/null
+++ b/plugins/devkit/src/dom/Reference.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.GenericAttributeValue;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+
+/**
+ * plugin.dtd:reference interface.
+ */
+public interface Reference extends DomElement {
+
+ /**
+ * Returns the value of the id child.
+ * Attribute id
+ *
+ * @return the value of the id child.
+ */
+ @NotNull
+ GenericAttributeValue<String> getRef();
+
+ @NotNull
+ GenericAttributeValue<String> getId();
+
+ @NotNull
+ Collection<AddToGroup> getAddToGroups();
+
+}
diff --git a/plugins/devkit/src/dom/Shortcut.java b/plugins/devkit/src/dom/Shortcut.java
new file mode 100644
index 0000000..0b4fb7d
--- /dev/null
+++ b/plugins/devkit/src/dom/Shortcut.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.GenericAttributeValue;
+import com.intellij.util.xml.Required;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * plugin.dtd:shortcut interface.
+ */
+public interface Shortcut extends DomElement {
+
+ /**
+ * Returns the value of the first-keystroke child.
+ * Attribute first-keystroke
+ * @return the value of the first-keystroke child.
+ */
+ @NotNull
+ @Required
+ GenericAttributeValue<String> getFirstKeystroke();
+
+
+ /**
+ * Returns the value of the keymap child.
+ * Attribute keymap
+ * @return the value of the keymap child.
+ */
+ @NotNull
+ @Required
+ GenericAttributeValue<String> getKeymap();
+
+
+ /**
+ * Returns the value of the second-keystroke child.
+ * Attribute second-keystroke
+ * @return the value of the second-keystroke child.
+ */
+ @NotNull
+ GenericAttributeValue<String> getSecondKeystroke();
+
+
+}
diff --git a/plugins/devkit/src/dom/Vendor.java b/plugins/devkit/src/dom/Vendor.java
new file mode 100644
index 0000000..b0eb0251
--- /dev/null
+++ b/plugins/devkit/src/dom/Vendor.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated on Wed Nov 07 17:26:02 MSK 2007
+// DTD/Schema : plugin.dtd
+
+package org.jetbrains.idea.devkit.dom;
+
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.GenericAttributeValue;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * plugin.dtd:vendor interface.
+ * Type vendor documentation
+ * <pre>
+ * <vendor> tag now could have 'url', 'email' and 'logo' attributes;
+ * 'logo' should contain path to a 16 x 16 icon that will appear near the plugin name in the IDEA Welcome Screen
+ * </pre>
+ */
+public interface Vendor extends DomElement {
+
+ /**
+ * Returns the value of the simple content.
+ * @return the value of the simple content.
+ */
+ @NotNull
+ String getValue();
+ /**
+ * Sets the value of the simple content.
+ * @param value the new value to set
+ */
+ void setValue(String value);
+
+
+ /**
+ * Returns the value of the email child.
+ * Attribute email
+ * @return the value of the email child.
+ */
+ @NotNull
+ GenericAttributeValue<String> getEmail();
+
+
+ /**
+ * Returns the value of the url child.
+ * Attribute url
+ * @return the value of the url child.
+ */
+ @NotNull
+ GenericAttributeValue<String> getUrl();
+
+
+ /**
+ * Returns the value of the logo child.
+ * Attribute logo
+ * @return the value of the logo child.
+ */
+ @NotNull
+ GenericAttributeValue<String> getLogo();
+
+
+}
diff --git a/plugins/devkit/src/dom/impl/ExtensionDomExtender.java b/plugins/devkit/src/dom/impl/ExtensionDomExtender.java
new file mode 100644
index 0000000..2ea061c
--- /dev/null
+++ b/plugins/devkit/src/dom/impl/ExtensionDomExtender.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.dom.impl;
+
+import com.intellij.ide.plugins.PluginManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.util.PsiTypesUtil;
+import com.intellij.psi.xml.XmlElement;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.refactoring.psi.PropertyUtils;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.LinkedMultiMap;
+import com.intellij.util.containers.MultiMap;
+import com.intellij.util.xml.*;
+import com.intellij.util.xml.reflect.DomExtender;
+import com.intellij.util.xml.reflect.DomExtension;
+import com.intellij.util.xml.reflect.DomExtensionsRegistrar;
+import com.intellij.util.xmlb.Constants;
+import com.intellij.util.xmlb.annotations.AbstractCollection;
+import com.intellij.util.xmlb.annotations.Attribute;
+import com.intellij.util.xmlb.annotations.Property;
+import com.intellij.util.xmlb.annotations.Tag;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.dom.*;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author mike
+ */
+public class ExtensionDomExtender extends DomExtender<Extensions> {
+ private static final PsiClassConverter CLASS_CONVERTER = new PluginPsiClassConverter();
+ private static final DomExtender EXTENSION_EXTENDER = new DomExtender() {
+ public void registerExtensions(@NotNull final DomElement domElement, @NotNull final DomExtensionsRegistrar registrar) {
+ final ExtensionPoint extensionPoint = (ExtensionPoint)domElement.getChildDescription().getDomDeclaration();
+ assert extensionPoint != null;
+
+ String interfaceName = extensionPoint.getInterface().getStringValue();
+ final Project project = extensionPoint.getManager().getProject();
+
+ if (interfaceName != null) {
+ registrar.registerGenericAttributeValueChildExtension(new XmlName("implementation"), PsiClass.class).setConverter(CLASS_CONVERTER);
+ registerXmlb(registrar, JavaPsiFacade.getInstance(project).findClass(interfaceName, GlobalSearchScope.allScope(project)));
+ }
+ else {
+ final String beanClassName = extensionPoint.getBeanClass().getStringValue();
+ if (beanClassName != null) {
+ registerXmlb(registrar, JavaPsiFacade.getInstance(project).findClass(beanClassName, GlobalSearchScope.allScope(project)));
+ }
+ }
+ }
+ };
+
+
+ public void registerExtensions(@NotNull final Extensions extensions, @NotNull final DomExtensionsRegistrar registrar) {
+ final XmlElement xmlElement = extensions.getXmlElement();
+ if (xmlElement == null) return;
+
+ IdeaPlugin ideaPlugin = extensions.getParentOfType(IdeaPlugin.class, true);
+
+ if (ideaPlugin == null) return;
+
+ String prefix = getEpPrefix(extensions);
+ for (IdeaPlugin plugin : getVisiblePlugins(ideaPlugin)) {
+ final String pluginId = StringUtil.notNullize(plugin.getPluginId(), "com.intellij");
+ for (ExtensionPoints points : plugin.getExtensionPoints()) {
+ for (ExtensionPoint point : points.getExtensionPoints()) {
+ registerExtensionPoint(registrar, point, prefix, pluginId);
+ }
+ }
+ }
+ }
+
+ private static String getEpPrefix(Extensions extensions) {
+ String prefix = extensions.getDefaultExtensionNs().getStringValue();
+ if (prefix == null) prefix = extensions.getXmlns().getStringValue();
+ return prefix != null ? prefix + "." : "";
+ }
+
+ private static Set<IdeaPlugin> getVisiblePlugins(IdeaPlugin ideaPlugin) {
+ Set<IdeaPlugin> result = ContainerUtil.newHashSet();
+ MultiMap<String, IdeaPlugin> byId = getPluginMap(ideaPlugin.getManager().getProject());
+ collectDependencies(ideaPlugin, result, byId);
+ //noinspection NullableProblems
+ result.addAll(byId.get(null));
+ return result;
+ }
+
+ private static MultiMap<String, IdeaPlugin> getPluginMap(final Project project) {
+ MultiMap<String, IdeaPlugin> byId = new LinkedMultiMap<String, IdeaPlugin>();
+ for (IdeaPlugin each : IdeaPluginConverter.getAllPlugins(project)) {
+ byId.putValue(each.getPluginId(), each);
+ }
+ return byId;
+ }
+
+ private static void collectDependencies(final IdeaPlugin ideaPlugin, Set<IdeaPlugin> result, final MultiMap<String, IdeaPlugin> byId) {
+ if (!result.add(ideaPlugin)) {
+ return;
+ }
+
+ for (String id : getDependencies(ideaPlugin)) {
+ for (IdeaPlugin dep : byId.get(id)) {
+ collectDependencies(dep, result, byId);
+ }
+ }
+ }
+
+ private static void registerExtensionPoint(final DomExtensionsRegistrar registrar,
+ final ExtensionPoint extensionPoint,
+ String prefix,
+ @Nullable String pluginId) {
+ final XmlTag tag = extensionPoint.getXmlTag();
+ String epName = tag.getAttributeValue("name");
+ if (epName != null && StringUtil.isNotEmpty(pluginId)) epName = pluginId + "." + epName;
+ if (epName == null) epName = tag.getAttributeValue("qualifiedName");
+ if (epName == null) return;
+ if (!epName.startsWith(prefix)) return;
+
+ final DomExtension domExtension = registrar.registerCollectionChildrenExtension(new XmlName(epName.substring(prefix.length())), Extension.class);
+ domExtension.setDeclaringElement(extensionPoint);
+ domExtension.addExtender(EXTENSION_EXTENDER);
+ }
+
+ private static void registerXmlb(final DomExtensionsRegistrar registrar, @Nullable final PsiClass beanClass) {
+ if (beanClass == null) return;
+
+ for (PsiField field : beanClass.getAllFields()) {
+ registerField(registrar, field);
+ }
+ }
+
+ private static void registerField(final DomExtensionsRegistrar registrar, @NotNull final PsiField field) {
+ final PsiMethod getter = PropertyUtils.findGetterForField(field);
+ final PsiMethod setter = PropertyUtils.findSetterForField(field);
+ if (!field.hasModifierProperty(PsiModifier.PUBLIC) && (getter == null || setter == null)) {
+ return;
+ }
+
+ final String fieldName = field.getName();
+ final PsiConstantEvaluationHelper evalHelper = JavaPsiFacade.getInstance(field.getProject()).getConstantEvaluationHelper();
+ final PsiAnnotation attrAnno = findAnnotation(Attribute.class, field, getter, setter);
+ if (attrAnno != null) {
+ final String attrName = getStringAttribute(attrAnno, "value", evalHelper);
+ if (attrName != null) {
+ final DomExtension extension =
+ registrar.registerGenericAttributeValueChildExtension(new XmlName(attrName), String.class).setDeclaringElement(field);
+ if (fieldName.endsWith("Class")) {
+ extension.setConverter(CLASS_CONVERTER);
+ }
+ }
+ return;
+ }
+ final PsiAnnotation tagAnno = findAnnotation(Tag.class, field, getter, setter);
+ final PsiAnnotation propAnno = findAnnotation(Property.class, field, getter, setter);
+ final PsiAnnotation absColAnno = findAnnotation(AbstractCollection.class, field, getter, setter);
+ //final PsiAnnotation colAnno = modifierList.findAnnotation(Collection.class.getName()); // todo
+ final String tagName = tagAnno != null? getStringAttribute(tagAnno, "value", evalHelper) :
+ propAnno != null && getBooleanAttribute(propAnno, "surroundWithTag", evalHelper)? Constants.OPTION : null;
+ if (tagName != null) {
+ if (absColAnno == null) {
+ final DomExtension extension =
+ registrar.registerFixedNumberChildExtension(new XmlName(tagName), SimpleTagValue.class).setDeclaringElement(field);
+ if (fieldName.endsWith("Class")) {
+ extension.setConverter(CLASS_CONVERTER);
+ }
+ }
+ else {
+ registrar.registerFixedNumberChildExtension(new XmlName(tagName), DomElement.class).addExtender(new DomExtender() {
+ @Override
+ public void registerExtensions(@NotNull DomElement domElement, @NotNull DomExtensionsRegistrar registrar) {
+ registerCollectionBinding(field.getType(), registrar, absColAnno, evalHelper);
+ }
+ });
+ }
+ }
+ else if (absColAnno != null) {
+ registerCollectionBinding(field.getType(), registrar, absColAnno, evalHelper);
+ }
+ }
+
+ @Nullable
+ private static PsiAnnotation findAnnotation(final Class<?> annotationClass, PsiMember... members) {
+ for (PsiMember member : members) {
+ if (member != null) {
+ final PsiModifierList modifierList = member.getModifierList();
+ if (modifierList != null) {
+ final PsiAnnotation annotation = modifierList.findAnnotation(annotationClass.getName());
+ if (annotation != null) {
+ return annotation;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private static void registerCollectionBinding(PsiType type,
+ DomExtensionsRegistrar registrar,
+ PsiAnnotation anno,
+ PsiConstantEvaluationHelper evalHelper) {
+ final boolean surroundWithTag = getBooleanAttribute(anno, "surroundWithTag", evalHelper);
+ if (surroundWithTag) return; // todo Set, List, Array
+ final String tagName = getStringAttribute(anno, "elementTag", evalHelper);
+ final String attrName = getStringAttribute(anno, "elementValueAttribute", evalHelper);
+ final PsiClass psiClass = getElementType(type);
+ if (tagName != null && attrName == null) {
+ registrar.registerCollectionChildrenExtension(new XmlName(tagName), SimpleTagValue.class);
+ }
+ else if (tagName != null) {
+ registrar.registerCollectionChildrenExtension(new XmlName(tagName), DomElement.class).addExtender(new DomExtender() {
+ @Override
+ public void registerExtensions(@NotNull DomElement domElement, @NotNull DomExtensionsRegistrar registrar) {
+ registrar.registerGenericAttributeValueChildExtension(new XmlName(attrName), String.class);
+ }
+ });
+ }
+ else if (psiClass != null) {
+ final PsiModifierList modifierList = psiClass.getModifierList();
+ final PsiAnnotation tagAnno = modifierList == null? null : modifierList.findAnnotation(Tag.class.getName());
+ final String classTagName = tagAnno == null? psiClass.getName() : getStringAttribute(tagAnno, "value", evalHelper);
+ if (classTagName != null) {
+ registrar.registerCollectionChildrenExtension(new XmlName(classTagName), DomElement.class).addExtender(new DomExtender() {
+ @Override
+ public void registerExtensions(@NotNull DomElement domElement, @NotNull DomExtensionsRegistrar registrar) {
+ registerXmlb(registrar, psiClass);
+ }
+ });
+ }
+ }
+ }
+
+ @Nullable
+ private static String getStringAttribute(final PsiAnnotation annotation,
+ final String name,
+ final PsiConstantEvaluationHelper evalHelper) {
+ String value = getAttributeValue(annotation, name);
+ if (value != null) return value;
+ final Object o = evalHelper.computeConstantExpression(annotation.findAttributeValue(name), false);
+ return o instanceof String && StringUtil.isNotEmpty((String)o)? (String)o : null;
+ }
+
+ private static boolean getBooleanAttribute(final PsiAnnotation annotation,
+ final String name,
+ final PsiConstantEvaluationHelper evalHelper) {
+ String value = getAttributeValue(annotation, name);
+ if (value != null) return Boolean.parseBoolean(value);
+ final Object o = evalHelper.computeConstantExpression(annotation.findAttributeValue(name), false);
+ return o instanceof Boolean && ((Boolean)o).booleanValue();
+ }
+
+ @Nullable
+ private static String getAttributeValue(PsiAnnotation annotation, String name) {
+ for (PsiNameValuePair attribute : annotation.getParameterList().getAttributes()) {
+ if (name.equals(attribute.getName())) {
+ return attribute.getLiteralValue();
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ public static PsiClass getElementType(final PsiType psiType) {
+ final PsiType elementType;
+ if (psiType instanceof PsiArrayType) elementType = ((PsiArrayType)psiType).getComponentType();
+ else if (psiType instanceof PsiClassType) {
+ final PsiType[] types = ((PsiClassType)psiType).getParameters();
+ elementType = types.length == 1? types[0] : null;
+ }
+ else elementType = null;
+ return PsiTypesUtil.getPsiClass(elementType);
+ }
+
+
+ public static Collection<String> getDependencies(IdeaPlugin ideaPlugin) {
+ Set<String> result = new HashSet<String>();
+
+ result.add(PluginManager.CORE_PLUGIN_ID);
+
+ for (Dependency dependency : ideaPlugin.getDependencies()) {
+ ContainerUtil.addIfNotNull(dependency.getStringValue(), result);
+ }
+
+ if (ideaPlugin.getPluginId() == null) {
+ final VirtualFile file = DomUtil.getFile(ideaPlugin).getOriginalFile().getVirtualFile();
+ if (file != null) {
+ final String fileName = file.getName();
+ if (!"plugin.xml".equals(fileName)) {
+ final VirtualFile mainPluginXml = file.findFileByRelativePath("../plugin.xml");
+ if (mainPluginXml != null) {
+ final PsiFile psiFile = PsiManager.getInstance(ideaPlugin.getManager().getProject()).findFile(mainPluginXml);
+ if (psiFile instanceof XmlFile) {
+ final XmlFile xmlFile = (XmlFile)psiFile;
+ final DomFileElement<IdeaPlugin> fileElement = ideaPlugin.getManager().getFileElement(xmlFile, IdeaPlugin.class);
+ if (fileElement != null) {
+ final IdeaPlugin mainPlugin = fileElement.getRootElement();
+ ContainerUtil.addIfNotNull(mainPlugin.getPluginId(), result);
+ for (Dependency dependency : mainPlugin.getDependencies()) {
+ ContainerUtil.addIfNotNull(dependency.getStringValue(), result);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ interface SimpleTagValue extends DomElement {
+ @SuppressWarnings("UnusedDeclaration")
+ @TagValue
+ String getTagValue();
+ }
+
+}
diff --git a/plugins/devkit/src/dom/impl/ExtensionImpl.java b/plugins/devkit/src/dom/impl/ExtensionImpl.java
new file mode 100644
index 0000000..28ff67f
--- /dev/null
+++ b/plugins/devkit/src/dom/impl/ExtensionImpl.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2000-2011 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.dom.impl;
+
+import com.intellij.pom.PomTarget;
+import com.intellij.pom.PomTargetPsiElement;
+import com.intellij.psi.PsiElement;
+import com.intellij.util.xml.DomTarget;
+import org.jetbrains.idea.devkit.dom.Extension;
+import org.jetbrains.idea.devkit.dom.ExtensionPoint;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 10/10/11
+ */
+public abstract class ExtensionImpl implements Extension {
+
+ @Override
+ public ExtensionPoint getExtensionPoint() {
+ PsiElement declaration = getChildDescription().getDeclaration(getManager().getProject());
+ if (declaration instanceof PomTargetPsiElement) {
+ PomTarget target = ((PomTargetPsiElement)declaration).getTarget();
+ return target instanceof DomTarget ? (ExtensionPoint)((DomTarget)target).getDomElement() : null;
+ }
+ return null;
+ }
+}
diff --git a/plugins/devkit/src/dom/impl/ExtensionNsConverter.java b/plugins/devkit/src/dom/impl/ExtensionNsConverter.java
new file mode 100644
index 0000000..60a5af9
--- /dev/null
+++ b/plugins/devkit/src/dom/impl/ExtensionNsConverter.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.dom.impl;
+
+import com.intellij.openapi.util.Condition;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.xml.ConvertContext;
+import com.intellij.util.xml.ResolvingConverter;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.dom.IdeaPlugin;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/*
+* Created by IntelliJ IDEA.
+* User: sweinreuter
+* Date: 06.12.2007
+*/
+public class ExtensionNsConverter extends ResolvingConverter<IdeaPlugin> {
+ @NotNull
+ public Collection<? extends IdeaPlugin> getVariants(ConvertContext context) {
+ final IdeaPlugin ideaPlugin = context.getInvocationElement().getParentOfType(IdeaPlugin.class, true);
+ if (ideaPlugin == null) return Collections.emptyList();
+
+ final Collection<String> dependencies = ExtensionDomExtender.getDependencies(ideaPlugin);
+ final List<IdeaPlugin> depPlugins = new ArrayList<IdeaPlugin>();
+ for (IdeaPlugin plugin : IdeaPluginConverter.getAllPlugins(context.getProject())) {
+ final String value = plugin.getPluginId();
+ if (value != null && dependencies.contains(value)) {
+ depPlugins.add(plugin);
+ }
+ }
+ return depPlugins;
+ }
+
+ public IdeaPlugin fromString(@Nullable @NonNls final String s, ConvertContext context) {
+ final IdeaPlugin ideaPlugin = context.getInvocationElement().getParentOfType(IdeaPlugin.class, true);
+ if (ideaPlugin == null) return null;
+ if (s != null && s.equals(ideaPlugin.getPluginId())) {
+ // a plugin can extend itself
+ return ideaPlugin;
+ }
+ return ContainerUtil.find(getVariants(context), new Condition<IdeaPlugin>() {
+ public boolean value(IdeaPlugin o) {
+ final String id = o.getPluginId();
+ return id != null && id.equals(s);
+ }
+ });
+ }
+
+ public String toString(@Nullable IdeaPlugin ideaPlugin, ConvertContext context) {
+ return ideaPlugin != null ? ideaPlugin.getPluginId() : null;
+ }
+}
diff --git a/plugins/devkit/src/dom/impl/ExtensionsImpl.java b/plugins/devkit/src/dom/impl/ExtensionsImpl.java
new file mode 100644
index 0000000..2de0640
--- /dev/null
+++ b/plugins/devkit/src/dom/impl/ExtensionsImpl.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.dom.impl;
+
+import com.intellij.psi.xml.XmlTag;
+import org.jetbrains.idea.devkit.dom.Extension;
+import org.jetbrains.idea.devkit.dom.Extensions;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 1/20/12
+ */
+public abstract class ExtensionsImpl implements Extensions {
+
+ @Override
+ public Extension addExtension(String name) {
+ Extension extension = addExtension();
+ XmlTag tag = extension.getXmlTag();
+ tag.setName(name.substring(getDefaultExtensionNs().getStringValue().length() + 1));
+ return extension;
+ }
+}
diff --git a/plugins/devkit/src/dom/impl/IdeaPluginConverter.java b/plugins/devkit/src/dom/impl/IdeaPluginConverter.java
new file mode 100644
index 0000000..bc7e1f8
--- /dev/null
+++ b/plugins/devkit/src/dom/impl/IdeaPluginConverter.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.dom.impl;
+
+import com.intellij.openapi.project.DumbService;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.xml.ConvertContext;
+import com.intellij.util.xml.DomFileElement;
+import com.intellij.util.xml.DomService;
+import com.intellij.util.xml.ResolvingConverter;
+import gnu.trove.THashSet;
+import gnu.trove.TObjectHashingStrategy;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.dom.IdeaPlugin;
+import org.jetbrains.idea.devkit.dom.PluginModule;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author mike
+ */
+public class IdeaPluginConverter extends ResolvingConverter<IdeaPlugin> {
+
+ @NotNull
+ public Collection<? extends IdeaPlugin> getVariants(final ConvertContext context) {
+ Collection<IdeaPlugin> plugins = getAllPlugins(context.getProject());
+ return new THashSet<IdeaPlugin>(plugins, new TObjectHashingStrategy<IdeaPlugin>() {
+ @Override
+ public int computeHashCode(IdeaPlugin object) {
+ return StringUtil.notNullize(object.getPluginId()).hashCode();
+ }
+
+ @Override
+ public boolean equals(IdeaPlugin o1, IdeaPlugin o2) {
+ return StringUtil.notNullize(o1.getPluginId()).equals(o2.getPluginId());
+ }
+ });
+ }
+
+ @NotNull
+ @Override
+ public Set<String> getAdditionalVariants(@NotNull final ConvertContext context) {
+ final THashSet<String> result = new THashSet<String>();
+ for (IdeaPlugin ideaPlugin : getVariants(context)) {
+ for (PluginModule module : ideaPlugin.getModules()) {
+ ContainerUtil.addIfNotNull(module.getValue().getValue(), result);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public String getErrorMessage(@Nullable final String s, final ConvertContext context) {
+ return DevKitBundle.message("error.cannot.resolve.plugin", s);
+ }
+
+ public static Collection<IdeaPlugin> getAllPlugins(final Project project) {
+ if (DumbService.isDumb(project)) return Collections.emptyList();
+ GlobalSearchScope scope = GlobalSearchScope.allScope(project);
+ List<DomFileElement<IdeaPlugin>> files = DomService.getInstance().getFileElements(IdeaPlugin.class, project, scope);
+ return ContainerUtil.map(files, new Function<DomFileElement<IdeaPlugin>, IdeaPlugin>() {
+ public IdeaPlugin fun(DomFileElement<IdeaPlugin> ideaPluginDomFileElement) {
+ return ideaPluginDomFileElement.getRootElement();
+ }
+ });
+ }
+
+ public IdeaPlugin fromString(@Nullable @NonNls final String s, final ConvertContext context) {
+ for (IdeaPlugin ideaPlugin : getVariants(context)) {
+ final String otherId = ideaPlugin.getPluginId();
+ if (otherId == null) continue;
+ if (otherId.equals(s)) return ideaPlugin;
+ for (PluginModule module : ideaPlugin.getModules()) {
+ final String moduleName = module.getValue().getValue();
+ if (moduleName != null && moduleName.equals(s)) return ideaPlugin;
+ }
+ }
+ return null;
+ }
+
+ public String toString(@Nullable final IdeaPlugin ideaPlugin, final ConvertContext context) {
+ return ideaPlugin != null ? ideaPlugin.getPluginId() : null;
+ }
+}
diff --git a/plugins/devkit/src/dom/impl/IdeaPluginImpl.java b/plugins/devkit/src/dom/impl/IdeaPluginImpl.java
new file mode 100644
index 0000000..bb21349
--- /dev/null
+++ b/plugins/devkit/src/dom/impl/IdeaPluginImpl.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.dom.impl;
+
+import com.intellij.psi.xml.XmlTag;
+import org.jetbrains.idea.devkit.dom.IdeaPlugin;
+
+/*
+* Created by IntelliJ IDEA.
+* User: sweinreuter
+* Date: 06.12.2007
+*/
+public abstract class IdeaPluginImpl implements IdeaPlugin {
+ public String getPluginId() {
+ final XmlTag tag = getXmlTag();
+ if (tag == null) {
+ return null;
+ }
+
+ final XmlTag idTag = tag.findFirstSubTag("id");
+ if (idTag != null) {
+ return idTag.getValue().getTrimmedText();
+ }
+
+ final XmlTag name = tag.findFirstSubTag("name");
+ return name != null ? name.getValue().getTrimmedText() : null;
+ }
+}
diff --git a/plugins/devkit/src/dom/impl/InspectionsKeyPropertiesReferenceProvider.java b/plugins/devkit/src/dom/impl/InspectionsKeyPropertiesReferenceProvider.java
new file mode 100644
index 0000000..7ca68f0
--- /dev/null
+++ b/plugins/devkit/src/dom/impl/InspectionsKeyPropertiesReferenceProvider.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2000-2011 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.dom.impl;
+
+import com.intellij.lang.properties.BundleNameEvaluator;
+import com.intellij.lang.properties.IProperty;
+import com.intellij.lang.properties.PropertiesReferenceManager;
+import com.intellij.lang.properties.psi.PropertiesFile;
+import com.intellij.lang.properties.references.PropertyReference;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.PsiReferenceProvider;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.xml.XmlAttribute;
+import com.intellij.psi.xml.XmlAttributeValue;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.util.ProcessingContext;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * User: anna
+ * Date: 10/7/11
+ */
+public class InspectionsKeyPropertiesReferenceProvider extends PsiReferenceProvider {
+
+ private final boolean myDefaultSoft;
+
+ public InspectionsKeyPropertiesReferenceProvider() {
+ this(false);
+ }
+
+ public InspectionsKeyPropertiesReferenceProvider(final boolean defaultSoft) {
+ myDefaultSoft = defaultSoft;
+ }
+
+ @Override
+ public boolean acceptsTarget(@NotNull PsiElement target) {
+ return target instanceof IProperty;
+ }
+
+ @NotNull
+ public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull final ProcessingContext context) {
+ boolean soft = myDefaultSoft;
+
+ if (element instanceof XmlAttributeValue) {
+ final XmlAttribute xmlAttribute = (XmlAttribute)element.getParent();
+ if (element.getTextLength() < 2) {
+ return PsiReference.EMPTY_ARRAY;
+ }
+
+ final XmlTag tag = xmlAttribute.getParent();
+ String value = null;
+ String bundle = tag.getAttributeValue("bundle");
+ if ("key".equals(xmlAttribute.getName())) {
+ value = xmlAttribute.getValue();
+ }
+ else if ("groupKey".equals(xmlAttribute.getName())) {
+ value = xmlAttribute.getValue();
+ final String groupBundle = tag.getAttributeValue("groupBundle");
+ if (groupBundle != null) {
+ bundle = groupBundle;
+ }
+ }
+ if (value != null) {
+ return new PsiReference[]{new PropertyReference(value, xmlAttribute.getValueElement(), bundle, soft){
+ @Override
+ protected List<PropertiesFile> retrievePropertyFilesByBundleName(String bundleName, PsiElement element) {
+ final Project project = element.getProject();
+ return PropertiesReferenceManager.getInstance(project)
+ .findPropertiesFiles(GlobalSearchScope.projectScope(project), bundleName, BundleNameEvaluator.DEFAULT);
+ }
+ }};
+ }
+ }
+ return PsiReference.EMPTY_ARRAY;
+ }
+}
\ No newline at end of file
diff --git a/plugins/devkit/src/dom/impl/InspectionsPropertiesReferenceProviderContributor.java b/plugins/devkit/src/dom/impl/InspectionsPropertiesReferenceProviderContributor.java
new file mode 100644
index 0000000..662e788
--- /dev/null
+++ b/plugins/devkit/src/dom/impl/InspectionsPropertiesReferenceProviderContributor.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.dom.impl;
+
+import com.intellij.patterns.ElementPattern;
+import com.intellij.patterns.XmlPatterns;
+import com.intellij.psi.PsiReferenceContributor;
+import com.intellij.psi.PsiReferenceRegistrar;
+
+import static com.intellij.patterns.StandardPatterns.string;
+
+/**
+ * User: anna
+ * Date: 10/7/11
+ */
+public class InspectionsPropertiesReferenceProviderContributor extends PsiReferenceContributor {
+ @Override
+ public void registerReferenceProviders(PsiReferenceRegistrar registrar) {
+ ElementPattern pattern = XmlPatterns.xmlAttributeValue()
+ .withParent(XmlPatterns.xmlAttribute().withLocalName(string().oneOf("key", "groupKey"))
+ .withParent(XmlPatterns.xmlTag().withName(string().oneOf("localInspection", "globalInspection"))
+ .withSuperParent(2, XmlPatterns.xmlTag().withName("idea-plugin"))));
+ registrar.registerReferenceProvider(pattern, new InspectionsKeyPropertiesReferenceProvider(false),
+ PsiReferenceRegistrar.DEFAULT_PRIORITY);
+ }
+}
\ No newline at end of file
diff --git a/plugins/devkit/src/dom/impl/PluginPsiClassConverter.java b/plugins/devkit/src/dom/impl/PluginPsiClassConverter.java
new file mode 100644
index 0000000..be26a74
--- /dev/null
+++ b/plugins/devkit/src/dom/impl/PluginPsiClassConverter.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.dom.impl;
+
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReferenceProvider;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.util.xml.ConvertContext;
+import com.intellij.util.xml.ExtendClass;
+import com.intellij.util.xml.GenericDomValue;
+import com.intellij.util.xml.PsiClassConverter;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author peter
+*/
+public class PluginPsiClassConverter extends PsiClassConverter {
+ protected GlobalSearchScope getScope(@NotNull ConvertContext context) {
+ return GlobalSearchScope.allScope(context.getProject());
+ }
+
+ @Override
+ protected JavaClassReferenceProvider createClassReferenceProvider(GenericDomValue<PsiClass> genericDomValue,
+ ConvertContext context,
+ ExtendClass extendClass) {
+ final JavaClassReferenceProvider provider = super.createClassReferenceProvider(genericDomValue, context, extendClass);
+ provider.setOption(JavaClassReferenceProvider.JVM_FORMAT, Boolean.TRUE);
+ provider.setOption(JavaClassReferenceProvider.ALLOW_DOLLAR_NAMES, Boolean.TRUE);
+ return provider;
+ }
+}
diff --git a/plugins/devkit/src/dom/impl/PluginXmlDomFileDescription.java b/plugins/devkit/src/dom/impl/PluginXmlDomFileDescription.java
new file mode 100644
index 0000000..5531df0
--- /dev/null
+++ b/plugins/devkit/src/dom/impl/PluginXmlDomFileDescription.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.dom.impl;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.util.Iconable;
+import com.intellij.util.xml.DomFileDescription;
+import org.jetbrains.idea.devkit.dom.IdeaPlugin;
+
+import javax.swing.*;
+
+/**
+ * @author mike
+ */
+public class PluginXmlDomFileDescription extends DomFileDescription<IdeaPlugin> {
+
+ public PluginXmlDomFileDescription() {
+ super(IdeaPlugin.class, "idea-plugin");
+ }
+
+ @Override
+ public Icon getFileIcon(@Iconable.IconFlags int flags) {
+ return AllIcons.Nodes.Plugin;
+ }
+}
diff --git a/plugins/devkit/src/inspections/ComponentNotRegisteredInspection.java b/plugins/devkit/src/inspections/ComponentNotRegisteredInspection.java
new file mode 100644
index 0000000..aca8a5d
--- /dev/null
+++ b/plugins/devkit/src/inspections/ComponentNotRegisteredInspection.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections;
+
+import com.intellij.codeInspection.InspectionManager;
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.codeInspection.ProblemHighlightType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.util.PsiUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.inspections.quickfix.RegisterActionFix;
+import org.jetbrains.idea.devkit.inspections.quickfix.RegisterComponentFix;
+import org.jetbrains.idea.devkit.module.PluginModuleType;
+import org.jetbrains.idea.devkit.util.ComponentType;
+
+import javax.swing.*;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+/**
+ * @author swr
+ */
+public class ComponentNotRegisteredInspection extends DevKitInspectionBase {
+ public boolean CHECK_ACTIONS = true;
+ public boolean IGNORE_NON_PUBLIC = true;
+ private static final Logger LOG = Logger.getInstance("org.jetbrains.idea.devkit.inspections.ComponentNotRegisteredInspection");
+
+ @NotNull
+ public String getDisplayName() {
+ return DevKitBundle.message("inspections.component.not.registered.name");
+ }
+
+ @NotNull
+ @NonNls
+ public String getShortName() {
+ return "ComponentNotRegistered";
+ }
+
+ public boolean isEnabledByDefault() {
+ return true;
+ }
+
+ @Nullable
+ public JComponent createOptionsPanel() {
+ final JPanel jPanel = new JPanel();
+ jPanel.setLayout(new BoxLayout(jPanel, BoxLayout.Y_AXIS));
+
+ final JCheckBox ignoreNonPublic = new JCheckBox(
+ DevKitBundle.message("inspections.component.not.registered.option.ignore.non.public"),
+ IGNORE_NON_PUBLIC);
+ ignoreNonPublic.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ IGNORE_NON_PUBLIC = ignoreNonPublic.isSelected();
+ }
+ });
+
+ final JCheckBox checkJavaActions = new JCheckBox(
+ DevKitBundle.message("inspections.component.not.registered.option.check.actions"),
+ CHECK_ACTIONS);
+ checkJavaActions.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ final boolean selected = checkJavaActions.isSelected();
+ CHECK_ACTIONS = selected;
+ ignoreNonPublic.setEnabled(selected);
+ }
+ });
+
+ jPanel.add(checkJavaActions);
+ jPanel.add(ignoreNonPublic);
+ return jPanel;
+ }
+
+ @Nullable
+ public ProblemDescriptor[] checkClass(@NotNull PsiClass checkedClass, @NotNull InspectionManager manager, boolean isOnTheFly) {
+ final PsiFile psiFile = checkedClass.getContainingFile();
+ final PsiIdentifier classIdentifier = checkedClass.getNameIdentifier();
+ if (checkedClass.getQualifiedName() != null &&
+ classIdentifier != null &&
+ psiFile != null &&
+ psiFile.getVirtualFile() != null &&
+ !isAbstract(checkedClass))
+ {
+ if (PsiUtil.isInnerClass(checkedClass)) {
+ // don't check inner classes (make this an option?)
+ return null;
+ }
+
+ final PsiManager psiManager = checkedClass.getManager();
+ final GlobalSearchScope scope = checkedClass.getResolveScope();
+
+ if (CHECK_ACTIONS) {
+ final PsiClass actionClass = JavaPsiFacade.getInstance(psiManager.getProject()).findClass(AnAction.class.getName(), scope);
+ if (actionClass == null) {
+ // stop if action class cannot be found (non-devkit module/project)
+ return null;
+ }
+ if (checkedClass.isInheritor(actionClass, true)) {
+ if (IGNORE_NON_PUBLIC && !isPublic(checkedClass)) {
+ return null;
+ }
+ if (!isActionRegistered(checkedClass) && canFix(checkedClass)) {
+ final LocalQuickFix fix = new RegisterActionFix(checkedClass);
+ final ProblemDescriptor problem = manager.createProblemDescriptor(
+ classIdentifier,
+ DevKitBundle.message("inspections.component.not.registered.message",
+ DevKitBundle.message("new.menu.action.text")),
+ fix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly);
+ return new ProblemDescriptor[]{problem};
+ } else {
+ // action IS registered, stop here
+ return null;
+ }
+ }
+ }
+
+ final ComponentType[] types = ComponentType.values();
+ for (ComponentType type : types) {
+ final PsiClass compClass = JavaPsiFacade.getInstance(psiManager.getProject()).findClass(type.myClassName, scope);
+ if (compClass == null) {
+ // stop if component classes cannot be found (non-devkit module/project)
+ return null;
+ }
+ if (checkedClass.isInheritor(compClass, true)) {
+ if (getRegistrationTypes(checkedClass, false) == null && canFix(checkedClass)) {
+ final LocalQuickFix fix = new RegisterComponentFix(type, checkedClass);
+ final ProblemDescriptor problem = manager.createProblemDescriptor(classIdentifier,
+ DevKitBundle.message("inspections.component.not.registered.message",
+ DevKitBundle.message(type.myPropertyKey)),
+ fix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly);
+ return new ProblemDescriptor[]{problem};
+ } else {
+ // component IS registered, stop here
+ return null;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private static boolean canFix(PsiClass psiClass) {
+ final Project project = psiClass.getProject();
+ final PsiFile psiFile = psiClass.getContainingFile();
+ LOG.assertTrue(psiFile != null);
+ final Module module = ModuleUtil.findModuleForFile(psiFile.getVirtualFile(), project);
+ return module != null && PluginModuleType.isPluginModuleOrDependency(module);
+ }
+}
diff --git a/plugins/devkit/src/inspections/DevKitEntryPoints.java b/plugins/devkit/src/inspections/DevKitEntryPoints.java
new file mode 100644
index 0000000..cabcbe6
--- /dev/null
+++ b/plugins/devkit/src/inspections/DevKitEntryPoints.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2000-2011 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections;
+
+import com.intellij.codeInsight.daemon.ImplicitUsageProvider;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+
+/**
+ * User: anna
+ */
+public class DevKitEntryPoints implements ImplicitUsageProvider {
+ @Override
+ public boolean isImplicitUsage(PsiElement element) {
+ if (element instanceof PsiClass) {
+ final PsiClass domClass =
+ JavaPsiFacade.getInstance(element.getProject()).findClass("com.intellij.util.xml.DomElement", element.getResolveScope());
+ if (domClass != null && ((PsiClass)element).isInheritor(domClass, true)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isImplicitRead(PsiElement element) {
+ return false;
+ }
+
+ @Override
+ public boolean isImplicitWrite(PsiElement element) {
+ return false;
+ }
+}
diff --git a/plugins/devkit/src/inspections/DevKitInspectionBase.java b/plugins/devkit/src/inspections/DevKitInspectionBase.java
new file mode 100644
index 0000000..c2e77ba
--- /dev/null
+++ b/plugins/devkit/src/inspections/DevKitInspectionBase.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections;
+
+import com.intellij.codeInspection.BaseJavaLocalInspectionTool;
+import com.intellij.openapi.actionSystem.ActionGroup;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.xml.*;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.module.PluginModuleType;
+import org.jetbrains.idea.devkit.util.ActionType;
+import org.jetbrains.idea.devkit.util.ComponentType;
+import org.jetbrains.idea.devkit.util.DescriptorUtil;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author swr
+ */
+public abstract class DevKitInspectionBase extends BaseJavaLocalInspectionTool {
+
+ @NotNull
+ public String getGroupDisplayName() {
+ return DevKitBundle.message("inspections.group.name");
+ }
+
+ @Nullable
+ protected static Set<PsiClass> getRegistrationTypes(PsiClass psiClass, boolean includeActions) {
+ final Project project = psiClass.getProject();
+ final PsiFile psiFile = psiClass.getContainingFile();
+
+ assert psiFile != null;
+
+ final VirtualFile virtualFile = psiFile.getVirtualFile();
+ if (virtualFile == null) return null;
+ final Module module = ModuleUtil.findModuleForFile(virtualFile, project);
+
+ if (module == null) return null;
+
+ if (PluginModuleType.isOfType(module)) {
+ return checkModule(module, psiClass, null, includeActions);
+ } else {
+ Set<PsiClass> types = null;
+ final List<Module> modules = PluginModuleType.getCandidateModules(module);
+ for (Module m : modules) {
+ types = checkModule(m, psiClass, types, includeActions);
+ }
+ return types;
+ }
+ }
+
+ @Nullable
+ private static Set<PsiClass> checkModule(Module module, PsiClass psiClass, @Nullable Set<PsiClass> types, boolean includeActions) {
+ final XmlFile pluginXml = PluginModuleType.getPluginXml(module);
+ if (!isPluginXml(pluginXml)) return types;
+ assert pluginXml != null;
+
+ final XmlDocument document = pluginXml.getDocument();
+ assert document != null;
+
+ final XmlTag rootTag = document.getRootTag();
+ assert rootTag != null;
+
+ final String qualifiedName = psiClass.getQualifiedName();
+ if (qualifiedName != null) {
+ final RegistrationTypeFinder finder = new RegistrationTypeFinder(psiClass, types);
+
+ DescriptorUtil.processComponents(rootTag, finder);
+
+ if (includeActions) {
+ DescriptorUtil.processActions(rootTag, finder);
+ }
+
+ types = finder.getTypes();
+ }
+
+ return types;
+ }
+
+ public static boolean isPluginXml(PsiFile file) {
+ if (!(file instanceof XmlFile)) return false;
+ final XmlFile pluginXml = (XmlFile)file;
+
+ final XmlDocument document = pluginXml.getDocument();
+ if (document == null) return false;
+ final XmlTag rootTag = document.getRootTag();
+ return rootTag != null && "idea-plugin".equals(rootTag.getLocalName());
+
+ }
+
+ @Nullable
+ protected static PsiElement getAttValueToken(@NotNull XmlAttribute attribute) {
+ final XmlAttributeValue valueElement = attribute.getValueElement();
+ if (valueElement == null) return null;
+
+ final PsiElement[] children = valueElement.getChildren();
+ if (children.length == 3 && children[1] instanceof XmlToken) {
+ return children[1];
+ }
+ if (children.length == 1 && children[0] instanceof PsiErrorElement) return null;
+ return valueElement;
+ }
+
+ protected static boolean isAbstract(PsiModifierListOwner checkedClass) {
+ return checkedClass.hasModifierProperty(PsiModifier.ABSTRACT);
+ }
+
+ protected static boolean isPublic(PsiModifierListOwner checkedClass) {
+ return checkedClass.hasModifierProperty(PsiModifier.PUBLIC);
+ }
+
+ protected static boolean isActionRegistered(PsiClass psiClass) {
+ final Set<PsiClass> registrationTypes = getRegistrationTypes(psiClass, true);
+ if (registrationTypes != null) {
+ for (PsiClass type : registrationTypes) {
+ if (AnAction.class.getName().equals(type.getQualifiedName())) return true;
+ if (ActionGroup.class.getName().equals(type.getQualifiedName())) return true;
+ }
+ }
+ return false;
+ }
+
+ static class RegistrationTypeFinder implements ComponentType.Processor, ActionType.Processor {
+ private Set<PsiClass> myTypes;
+ private final String myQualifiedName;
+ private final PsiManager myManager;
+ private final GlobalSearchScope myScope;
+
+ public RegistrationTypeFinder(PsiClass psiClass, Set<PsiClass> types) {
+ myTypes = types;
+ myQualifiedName = psiClass.getQualifiedName();
+ myManager = psiClass.getManager();
+ myScope = psiClass.getResolveScope();
+ }
+
+ public boolean process(ComponentType type, XmlTag component, XmlTagValue impl, XmlTagValue intf) {
+ if (impl != null && myQualifiedName.equals(impl.getTrimmedText())) {
+ final PsiClass clazz = JavaPsiFacade.getInstance(myManager.getProject()).findClass(type.myClassName, myScope);
+ if (clazz != null) {
+ addType(clazz);
+ }
+ }
+ return true;
+ }
+
+ public boolean process(ActionType type, XmlTag action) {
+ final String actionClass = action.getAttributeValue("class");
+ if (actionClass != null) {
+ if (actionClass.trim().equals(myQualifiedName)) {
+ final PsiClass clazz = JavaPsiFacade.getInstance(myManager.getProject()).findClass(type.myClassName, myScope);
+ if (clazz != null) {
+ addType(clazz);
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private void addType(PsiClass clazz) {
+ if (myTypes == null) {
+ //noinspection unchecked
+ myTypes = ContainerUtil.<PsiClass>newIdentityTroveSet(2);
+ }
+ myTypes.add(clazz);
+ }
+
+ public Set<PsiClass> getTypes() {
+ return myTypes;
+ }
+ }
+}
diff --git a/plugins/devkit/src/inspections/InspectionDescriptionNotFoundInspection.java b/plugins/devkit/src/inspections/InspectionDescriptionNotFoundInspection.java
new file mode 100644
index 0000000..56431bb
--- /dev/null
+++ b/plugins/devkit/src/inspections/InspectionDescriptionNotFoundInspection.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.idea.devkit.inspections;
+
+import com.intellij.codeInspection.*;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.inspections.quickfix.CreateHtmlDescriptionFix;
+import org.jetbrains.idea.devkit.util.PsiUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class InspectionDescriptionNotFoundInspection extends DevKitInspectionBase{
+ @NonNls static final String INSPECTION_PROFILE_ENTRY = InspectionProfileEntry.class.getName();
+ @NonNls private static final String INSPECTION_DESCRIPTIONS = "inspectionDescriptions";
+
+ @Override
+ public ProblemDescriptor[] checkClass(@NotNull PsiClass aClass, @NotNull InspectionManager manager, boolean isOnTheFly) {
+ final Project project = aClass.getProject();
+ final PsiIdentifier nameIdentifier = aClass.getNameIdentifier();
+ final Module module = ModuleUtil.findModuleForPsiElement(aClass);
+
+ if (nameIdentifier == null || module == null || !PsiUtil.isInstantiatable(aClass)) return null;
+
+ final PsiClass base = JavaPsiFacade.getInstance(project).findClass(INSPECTION_PROFILE_ENTRY, GlobalSearchScope.allScope(project));
+
+ if (base == null || ! aClass.isInheritor(base, true) || isPathMethodsAreOverridden(aClass)) return null;
+
+ PsiMethod method = findNearestMethod("getShortName", aClass);
+ if (method != null && method.getContainingClass().getQualifiedName().equals(INSPECTION_PROFILE_ENTRY)) {
+ method = null;
+ }
+ final String filename = method == null ? InspectionProfileEntry.getShortName(aClass.getName()) : PsiUtil.getReturnedLiteral(method, aClass);
+ if (filename == null) return null;
+
+
+ for (PsiDirectory description : getInspectionDescriptionsDirs(module)) {
+ final PsiFile file = description.findFile(filename + ".html");
+ if (file == null) continue;
+ final VirtualFile vf = file.getVirtualFile();
+ if (vf == null) continue;
+ if (vf.getNameWithoutExtension().equals(filename)) {
+ return null;
+ }
+ }
+
+
+ final PsiElement problem = getProblemElement(aClass, method);
+ final ProblemDescriptor problemDescriptor = manager
+ .createProblemDescriptor(problem == null ? nameIdentifier : problem,
+ "Inspection does not have a description", isOnTheFly, new LocalQuickFix[]{new CreateHtmlDescriptionFix(filename, module, false)},
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
+ return new ProblemDescriptor[]{problemDescriptor};
+ }
+
+ @Nullable
+ private static PsiElement getProblemElement(PsiClass aClass, @Nullable PsiMethod method) {
+ if (method != null && method.getContainingClass() == aClass) {
+ return PsiUtil.getReturnedExpression(method);
+ } else {
+ return aClass.getNameIdentifier();
+ }
+ }
+
+ private static boolean isPathMethodsAreOverridden(PsiClass aClass) {
+ return! ( isLastMethodDefinitionIn("getStaticDescription", INSPECTION_PROFILE_ENTRY, aClass)
+ && isLastMethodDefinitionIn("getDescriptionUrl", INSPECTION_PROFILE_ENTRY, aClass)
+ && isLastMethodDefinitionIn("getDescriptionContextClass", INSPECTION_PROFILE_ENTRY, aClass)
+ && isLastMethodDefinitionIn("getDescriptionFileName", INSPECTION_PROFILE_ENTRY, aClass));
+ }
+
+ private static boolean isLastMethodDefinitionIn(@NotNull String methodName, @NotNull String classFQN, PsiClass cls) {
+ if (cls == null) return false;
+ for (PsiMethod method : cls.getMethods()) {
+ if (method.getName().equals(methodName)) {
+ final PsiClass containingClass = method.getContainingClass();
+ if (containingClass == null) return false;
+ return classFQN.equals(containingClass.getQualifiedName());
+ }
+ }
+ return isLastMethodDefinitionIn(methodName, classFQN, cls.getSuperClass());
+ }
+
+ public static List<VirtualFile> getPotentialRoots(Module module) {
+ final PsiDirectory[] dirs = getInspectionDescriptionsDirs(module);
+ final List<VirtualFile> result = new ArrayList<VirtualFile>();
+ if (dirs.length != 0) {
+ for (PsiDirectory dir : dirs) {
+ final PsiDirectory parent = dir.getParentDirectory();
+ if (parent != null) result.add(parent.getVirtualFile());
+ }
+ } else {
+ ContainerUtil.addAll(result, ModuleRootManager.getInstance(module).getSourceRoots());
+ }
+ return result;
+ }
+
+ public static PsiDirectory[] getInspectionDescriptionsDirs(Module module) {
+ final PsiPackage aPackage = JavaPsiFacade.getInstance(module.getProject()).findPackage(INSPECTION_DESCRIPTIONS);
+ if (aPackage != null) {
+ return aPackage.getDirectories(GlobalSearchScope.moduleWithDependenciesScope(module));
+ } else {
+ return PsiDirectory.EMPTY_ARRAY;
+ }
+ }
+
+ @Nullable
+ private static PsiMethod findNearestMethod(String name, @Nullable PsiClass cls) {
+ if (cls == null) return null;
+ for (PsiMethod method : cls.getMethods()) {
+ if (method.getParameterList().getParametersCount() == 0 && method.getName().equals(name)) {
+ return method.getModifierList().hasModifierProperty(PsiModifier.ABSTRACT) ? null : method;
+ }
+ }
+ return findNearestMethod(name, cls.getSuperClass());
+ }
+
+ @Nls
+ @NotNull
+ public String getDisplayName() {
+ return "Inspection Description Checker";
+ }
+
+ @NotNull
+ public String getShortName() {
+ return "InspectionDescriptionNotFoundInspection";
+ }
+
+ @Override
+ public boolean isEnabledByDefault() {
+ return true;
+ }
+}
diff --git a/plugins/devkit/src/inspections/InspectionMappingConsistencyInspection.java b/plugins/devkit/src/inspections/InspectionMappingConsistencyInspection.java
new file mode 100644
index 0000000..572414d
--- /dev/null
+++ b/plugins/devkit/src/inspections/InspectionMappingConsistencyInspection.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2000-2011 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections;
+
+import com.intellij.codeInsight.daemon.impl.analysis.InsertRequiredAttributeFix;
+import com.intellij.codeInspection.LocalInspectionToolSession;
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementVisitor;
+import com.intellij.psi.XmlElementVisitor;
+import com.intellij.psi.util.InheritanceUtil;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.xml.DomElement;
+import com.intellij.util.xml.DomUtil;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.dom.Extension;
+import org.jetbrains.idea.devkit.dom.ExtensionPoint;
+import org.jetbrains.idea.devkit.dom.IdeaPlugin;
+
+import java.text.MessageFormat;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 10/10/11
+ */
+public class InspectionMappingConsistencyInspection extends DevKitInspectionBase {
+
+ @NotNull
+ @Override
+ public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder,
+ boolean isOnTheFly,
+ @NotNull LocalInspectionToolSession session) {
+ return new XmlElementVisitor() {
+ @Override
+ public void visitXmlTag(XmlTag tag) {
+ DomElement element = DomUtil.getDomElement(tag);
+ if (element instanceof Extension) {
+ ExtensionPoint extensionPoint = ((Extension)element).getExtensionPoint();
+ if (extensionPoint != null && InheritanceUtil.isInheritor(extensionPoint.getBeanClass().getValue(), "com.intellij.codeInspection.InspectionEP")) {
+ boolean key = tag.getAttribute("key") != null;
+ boolean groupKey = tag.getAttribute("groupKey") != null;
+ if (key) {
+ if (tag.getAttribute("bundle") == null) {
+ checkDefaultBundle(element, holder);
+ }
+ }
+ else if (tag.getAttribute("displayName") == null) {
+ registerProblem(element, holder, "displayName or key should be specified", "displayName", "key");
+ }
+ if (groupKey) {
+ if (tag.getAttribute("bundle") == null && tag.getAttribute("groupBundle") == null) {
+ checkDefaultBundle(element, holder);
+ }
+ }
+ else if (tag.getAttribute("groupName") == null) {
+ registerProblem(element, holder, "groupName or groupKey should be specified", "groupName", "groupKey");
+ }
+ }
+ }
+ }
+ };
+ }
+
+ private static void checkDefaultBundle(DomElement element, ProblemsHolder holder) {
+ IdeaPlugin plugin = DomUtil.getParentOfType(element, IdeaPlugin.class, true);
+ if (plugin != null && plugin.getResourceBundles().isEmpty()) {
+ registerProblem(element, holder, "Bundle should be specified");
+ }
+ }
+
+ private static void registerProblem(DomElement element, ProblemsHolder holder, String message, String... createAttrs) {
+ final Pair<TextRange,PsiElement> range = DomUtil.getProblemRange(element.getXmlTag());
+ holder.registerProblem(range.second, range.first, message, ContainerUtil.map(createAttrs, new Function<String, LocalQuickFix>() {
+ @Override
+ public LocalQuickFix fun(final String s) {
+ return new InsertRequiredAttributeFix(PsiTreeUtil.getParentOfType(range.second, XmlTag.class, false), s, null) {
+ @NotNull
+ @Override
+ public String getText() {
+ return MessageFormat.format("Insert ''{0}'' attribute", s);
+ }
+ };
+ }
+ }, new LocalQuickFix[createAttrs.length]));
+ }
+
+ @Nls
+ @NotNull
+ @Override
+ public String getDisplayName() {
+ return "<inspection> tag consistency";
+ }
+
+ @NotNull
+ @Override
+ public String getShortName() {
+ return "InspectionMappingConsistency";
+ }
+}
diff --git a/plugins/devkit/src/inspections/IntentionDescriptionNotFoundInspection.java b/plugins/devkit/src/inspections/IntentionDescriptionNotFoundInspection.java
new file mode 100644
index 0000000..2ad9f71
--- /dev/null
+++ b/plugins/devkit/src/inspections/IntentionDescriptionNotFoundInspection.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2000-2010 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections;
+
+import com.intellij.codeInspection.*;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.inspections.quickfix.CreateHtmlDescriptionFix;
+import org.jetbrains.idea.devkit.util.PsiUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class IntentionDescriptionNotFoundInspection extends DevKitInspectionBase{
+ @NonNls private static final String INTENTION = "com.intellij.codeInsight.intention.IntentionAction";
+ @NonNls private static final String INSPECTION_DESCRIPTIONS = "intentionDescriptions";
+
+ @Override
+ public ProblemDescriptor[] checkClass(@NotNull PsiClass aClass, @NotNull InspectionManager manager, boolean isOnTheFly) {
+ final Project project = aClass.getProject();
+ final PsiIdentifier nameIdentifier = aClass.getNameIdentifier();
+ final Module module = ModuleUtil.findModuleForPsiElement(aClass);
+
+ if (nameIdentifier == null || module == null || !PsiUtil.isInstantiatable(aClass)) return null;
+
+ final PsiClass base = JavaPsiFacade.getInstance(project).findClass(INTENTION, GlobalSearchScope.allScope(project));
+
+ if (base == null || ! aClass.isInheritor(base, true)) return null;
+
+ String descriptionDir = getDescriptionDirName(aClass);
+ if (StringUtil.isEmptyOrSpaces(descriptionDir)) {
+ return null;
+ }
+
+ for (PsiDirectory description : getIntentionDescriptionsDirs(module)) {
+ PsiDirectory dir = description.findSubdirectory(descriptionDir);
+ if (dir == null) continue;
+ final PsiFile descr = dir.findFile("description.html");
+ if (descr != null) {
+ if (!hasBeforeAndAfterTemplate(dir.getVirtualFile())) {
+ PsiElement problem = aClass.getNameIdentifier();
+ ProblemDescriptor problemDescriptor = manager.createProblemDescriptor(problem == null ? nameIdentifier : problem,
+ "Intention must have 'before.*.template' and 'after.*.template' beside 'description.html'",
+ isOnTheFly,
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
+ return new ProblemDescriptor[]{problemDescriptor};
+ }
+
+ return null;
+ }
+ }
+
+
+ final PsiElement problem = aClass.getNameIdentifier();
+ final ProblemDescriptor problemDescriptor = manager
+ .createProblemDescriptor(problem == null ? nameIdentifier : problem,
+ "Intention does not have a description", isOnTheFly, new LocalQuickFix[]{new CreateHtmlDescriptionFix(descriptionDir, module, true)},
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
+ return new ProblemDescriptor[]{problemDescriptor};
+ }
+
+ @Nullable
+ private static String getDescriptionDirName(PsiClass aClass) {
+ String descriptionDir = "";
+ PsiClass each = aClass;
+ while (each != null) {
+ String name = each.getName();
+ if (StringUtil.isEmptyOrSpaces(name)) {
+ return null;
+ }
+ descriptionDir = name + descriptionDir;
+ each = each.getContainingClass();
+ }
+ return descriptionDir;
+ }
+
+ private static boolean hasBeforeAndAfterTemplate(@NotNull VirtualFile dir) {
+ boolean hasBefore = false;
+ boolean hasAfter = false;
+
+ for (VirtualFile file : dir.getChildren()) {
+ String name = file.getName();
+ if (name.endsWith(".template")) {
+ if (name.startsWith("before.")) {
+ hasBefore = true;
+ }
+ else if (name.startsWith("after.")) {
+ hasAfter = true;
+ }
+ }
+ }
+
+ return hasBefore && hasAfter;
+ }
+
+ public static List<VirtualFile> getPotentialRoots(Module module) {
+ final PsiDirectory[] dirs = getIntentionDescriptionsDirs(module);
+ final List<VirtualFile> result = new ArrayList<VirtualFile>();
+ if (dirs.length != 0) {
+ for (PsiDirectory dir : dirs) {
+ final PsiDirectory parent = dir.getParentDirectory();
+ if (parent != null) result.add(parent.getVirtualFile());
+ }
+ } else {
+ ContainerUtil.addAll(result, ModuleRootManager.getInstance(module).getSourceRoots());
+ }
+ return result;
+ }
+
+ public static PsiDirectory[] getIntentionDescriptionsDirs(Module module) {
+ final PsiPackage aPackage = JavaPsiFacade.getInstance(module.getProject()).findPackage(INSPECTION_DESCRIPTIONS);
+ if (aPackage != null) {
+ return aPackage.getDirectories(GlobalSearchScope.moduleWithDependenciesScope(module));
+ } else {
+ return PsiDirectory.EMPTY_ARRAY;
+ }
+ }
+
+ @Nls
+ @NotNull
+ public String getDisplayName() {
+ return "Intention Description Checker";
+ }
+
+ @NotNull
+ public String getShortName() {
+ return "IntentionDescriptionNotFoundInspection";
+ }
+
+ @Override
+ public boolean isEnabledByDefault() {
+ return true;
+ }
+}
diff --git a/plugins/devkit/src/inspections/PluginXmlDomInspection.java b/plugins/devkit/src/inspections/PluginXmlDomInspection.java
new file mode 100644
index 0000000..b160cbf
--- /dev/null
+++ b/plugins/devkit/src/inspections/PluginXmlDomInspection.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections;
+
+import com.intellij.util.xml.highlighting.BasicDomElementsInspection;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.dom.IdeaPlugin;
+
+/**
+ * @author mike
+ */
+public class PluginXmlDomInspection extends BasicDomElementsInspection<IdeaPlugin> {
+ public PluginXmlDomInspection() {
+ super(IdeaPlugin.class);
+ }
+
+ @Nls
+ @NotNull
+ public String getGroupDisplayName() {
+ return DevKitBundle.message("inspections.group.name");
+ }
+
+ @Nls
+ @NotNull
+ public String getDisplayName() {
+ return "Plugin.xml Validity";
+ }
+
+ @NonNls
+ @NotNull
+ public String getShortName() {
+ return "PluginXmlValidity";
+ }
+}
diff --git a/plugins/devkit/src/inspections/RegistrationProblemsInspection.java b/plugins/devkit/src/inspections/RegistrationProblemsInspection.java
new file mode 100644
index 0000000..8473dc0
--- /dev/null
+++ b/plugins/devkit/src/inspections/RegistrationProblemsInspection.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections;
+
+import com.intellij.codeHighlighting.HighlightDisplayLevel;
+import com.intellij.codeInsight.intention.QuickFixFactory;
+import com.intellij.codeInspection.InspectionManager;
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.codeInspection.ProblemHighlightType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.util.ClassUtil;
+import com.intellij.psi.xml.*;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.SmartList;
+import gnu.trove.THashSet;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.inspections.quickfix.CreateConstructorFix;
+import org.jetbrains.idea.devkit.inspections.quickfix.ImplementOrExtendFix;
+import org.jetbrains.idea.devkit.util.ActionType;
+import org.jetbrains.idea.devkit.util.ComponentType;
+import org.jetbrains.idea.devkit.util.DescriptorUtil;
+
+import javax.swing.*;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author swr
+ */
+public class RegistrationProblemsInspection extends DevKitInspectionBase {
+
+ public boolean CHECK_PLUGIN_XML = true;
+ public boolean CHECK_JAVA_CODE = true;
+ public boolean CHECK_ACTIONS = true;
+
+ @NotNull
+ public HighlightDisplayLevel getDefaultLevel() {
+ return HighlightDisplayLevel.ERROR;
+ }
+
+ public boolean isEnabledByDefault() {
+ return true;
+ }
+
+ @NotNull
+ public String getDisplayName() {
+ return DevKitBundle.message("inspections.registration.problems.name");
+ }
+
+ @NotNull
+ @NonNls
+ public String getShortName() {
+ return "ComponentRegistrationProblems";
+ }
+
+ @Nullable
+ public JComponent createOptionsPanel() {
+ final JPanel jPanel = new JPanel();
+ jPanel.setLayout(new BoxLayout(jPanel, BoxLayout.Y_AXIS));
+
+ final JCheckBox checkPluginXml = new JCheckBox(
+ DevKitBundle.message("inspections.registration.problems.option.check.plugin.xml"),
+ CHECK_PLUGIN_XML);
+ checkPluginXml.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ CHECK_PLUGIN_XML = checkPluginXml.isSelected();
+ }
+ });
+
+ final JCheckBox checkJavaActions = new JCheckBox(
+ DevKitBundle.message("inspections.registration.problems.option.check.java.actions"),
+ CHECK_ACTIONS);
+ checkJavaActions.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ CHECK_ACTIONS = checkJavaActions.isSelected();
+ }
+ });
+
+ final JCheckBox checkJavaCode = new JCheckBox(
+ DevKitBundle.message("inspections.registration.problems.option.check.java.code"),
+ CHECK_JAVA_CODE);
+ checkJavaCode.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ final boolean selected = checkJavaCode.isSelected();
+ CHECK_JAVA_CODE = selected;
+ checkJavaActions.setEnabled(selected);
+ }
+ });
+
+ jPanel.add(checkPluginXml);
+ jPanel.add(checkJavaCode);
+ jPanel.add(checkJavaActions);
+ return jPanel;
+ }
+
+ @Nullable
+ public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
+ if (CHECK_PLUGIN_XML && isPluginXml(file)) {
+ return checkPluginXml((XmlFile)file, manager, isOnTheFly);
+ }
+ return null;
+ }
+
+ @Nullable
+ public ProblemDescriptor[] checkClass(@NotNull PsiClass checkedClass, @NotNull InspectionManager manager, boolean isOnTheFly) {
+ final PsiIdentifier nameIdentifier = checkedClass.getNameIdentifier();
+
+ if (CHECK_JAVA_CODE &&
+ nameIdentifier != null &&
+ checkedClass.getQualifiedName() != null &&
+ checkedClass.getContainingFile().getVirtualFile() != null)
+ {
+ final Set<PsiClass> componentClasses = getRegistrationTypes(checkedClass, CHECK_ACTIONS);
+ if (componentClasses != null) {
+ List<ProblemDescriptor> problems = null;
+
+ for (PsiClass compClass : componentClasses) {
+ if (!checkedClass.isInheritor(compClass, true)) {
+ problems = addProblem(problems, manager.createProblemDescriptor(nameIdentifier,
+ DevKitBundle.message("inspections.registration.problems.incompatible.message",
+ compClass.isInterface() ?
+ DevKitBundle.message("keyword.implement") :
+ DevKitBundle.message("keyword.extend"),
+ compClass.getQualifiedName()), isOnTheFly, ImplementOrExtendFix.createFix(compClass, checkedClass, isOnTheFly),
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING));
+ }
+ }
+ if (ActionType.ACTION.isOfType(checkedClass)) {
+ if (ConstructorType.getNoArgCtor(checkedClass) == null) {
+ problems = addProblem(problems, manager.createProblemDescriptor(nameIdentifier,
+ DevKitBundle.message("inspections.registration.problems.missing.noarg.ctor"),
+ new CreateConstructorFix(checkedClass, isOnTheFly),
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly));
+ }
+ }
+ if (isAbstract(checkedClass)) {
+ problems = addProblem(problems, manager.createProblemDescriptor(nameIdentifier,
+ DevKitBundle.message("inspections.registration.problems.abstract"), isOnTheFly, LocalQuickFix.EMPTY_ARRAY, ProblemHighlightType.GENERIC_ERROR_OR_WARNING));
+ }
+ return problems != null ? problems.toArray(new ProblemDescriptor[problems.size()]) : null;
+ }
+ }
+ return null;
+ }
+
+ private List<ProblemDescriptor> addProblem(List<ProblemDescriptor> problems, ProblemDescriptor problemDescriptor) {
+ if (problems == null) problems = new SmartList<ProblemDescriptor>();
+ problems.add(problemDescriptor);
+ return problems;
+ }
+
+ @Nullable
+ private ProblemDescriptor[] checkPluginXml(XmlFile xmlFile, InspectionManager manager, boolean isOnTheFly) {
+ final XmlDocument document = xmlFile.getDocument();
+ if (document == null) {
+ return null;
+ }
+
+ final XmlTag rootTag = document.getRootTag();
+ assert rootTag != null;
+
+ final RegistrationChecker checker = new RegistrationChecker(manager, xmlFile, isOnTheFly);
+
+ DescriptorUtil.processComponents(rootTag, checker);
+
+ DescriptorUtil.processActions(rootTag, checker);
+
+ return checker.getProblems();
+ }
+
+ static class RegistrationChecker implements ComponentType.Processor, ActionType.Processor {
+ private List<ProblemDescriptor> myList;
+ private final InspectionManager myManager;
+ private final XmlFile myXmlFile;
+ private final PsiManager myPsiManager;
+ private final GlobalSearchScope myScope;
+ private final Set<String> myInterfaceClasses = new THashSet<String>();
+ private final boolean myOnTheFly;
+
+ public RegistrationChecker(InspectionManager manager, XmlFile xmlFile, boolean onTheFly) {
+ myManager = manager;
+ myXmlFile = xmlFile;
+ myOnTheFly = onTheFly;
+ myPsiManager = xmlFile.getManager();
+ myScope = xmlFile.getResolveScope();
+ }
+
+ public boolean process(ComponentType type, XmlTag component, @Nullable XmlTagValue impl, @Nullable XmlTagValue intf) {
+ if (impl == null) {
+ addProblem(component,
+ DevKitBundle.message("inspections.registration.problems.missing.implementation.class"),
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING, myOnTheFly);
+ } else {
+ String intfName = null;
+ PsiClass intfClass = null;
+ if (intf != null) {
+ intfName = intf.getTrimmedText();
+ intfClass = JavaPsiFacade.getInstance(myPsiManager.getProject()).findClass(intfName, myScope);
+ }
+ final String implClassName = impl.getTrimmedText();
+ final PsiClass implClass = JavaPsiFacade.getInstance(myPsiManager.getProject()).findClass(implClassName, myScope);
+ if (implClass == null) {
+ addProblem(impl,
+ DevKitBundle.message("inspections.registration.problems.cannot.resolve.class",
+ DevKitBundle.message("class.implementation")),
+ ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, myOnTheFly, ((LocalQuickFix)QuickFixFactory.getInstance()
+ .createCreateClassOrInterfaceFix(myXmlFile, implClassName, true, intfClass != null ? intfName : type.myClassName)));
+ } else {
+ if (isAbstract(implClass)) {
+ addProblem(impl,
+ DevKitBundle.message("inspections.registration.problems.abstract"),
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING, myOnTheFly);
+ }
+ }
+ if (intfName != null) {
+ if (intfClass == null) {
+ addProblem(intf,
+ DevKitBundle.message("inspections.registration.problems.cannot.resolve.class",
+ DevKitBundle.message("class.interface")),
+ ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, myOnTheFly, ((LocalQuickFix)QuickFixFactory.getInstance()
+ .createCreateClassOrInterfaceFix(myXmlFile, intfName, false, type.myClassName)),
+ ((LocalQuickFix)QuickFixFactory.getInstance()
+ .createCreateClassOrInterfaceFix(myXmlFile, intfName, true, type.myClassName)));
+ } else if (implClass != null) {
+ final String fqn = intfClass.getQualifiedName();
+
+ if (type == ComponentType.MODULE) {
+ if (!checkInterface(fqn, intf)) {
+ // module components can be restricted to modules of certain types
+ final String[] keys = makeQualifiedModuleInterfaceNames(component, fqn);
+ for (String key : keys) {
+ checkInterface(key, intf);
+ myInterfaceClasses.add(key);
+ }
+ }
+ } else {
+ checkInterface(fqn, intf);
+ myInterfaceClasses.add(fqn);
+ }
+
+ if (intfClass != implClass && !implClass.isInheritor(intfClass, true)) {
+ addProblem(impl,
+ DevKitBundle.message("inspections.registration.problems.component.incompatible.interface", fqn),
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING, myOnTheFly);
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean checkInterface(String fqn, XmlTagValue value) {
+ if (myInterfaceClasses.contains(fqn)) {
+ addProblem(value,
+ DevKitBundle.message("inspections.registration.problems.component.duplicate.interface", fqn),
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING, myOnTheFly);
+ return true;
+ }
+ return false;
+ }
+
+ private static String[] makeQualifiedModuleInterfaceNames(XmlTag component, String fqn) {
+ final XmlTag[] children = component.findSubTags("option");
+ for (XmlTag child : children) {
+ if ("type".equals(child.getAttributeValue("name"))) {
+ final String value = child.getAttributeValue("value");
+ final SmartList<String> names = new SmartList<String>();
+ if (value != null) {
+ final String[] moduleTypes = value.split(";");
+ for (String moduleType : moduleTypes) {
+ names.add(fqn + "#" + moduleType);
+ }
+ }
+ return ArrayUtil.toStringArray(names);
+ }
+ }
+ return new String[]{ fqn };
+ }
+
+ public boolean process(ActionType type, XmlTag action) {
+ final XmlAttribute attribute = action.getAttribute("class");
+ if (attribute != null) {
+ final PsiElement token = getAttValueToken(attribute);
+ if (token != null) {
+ final String actionClassName = attribute.getValue().trim();
+ final PsiClass actionClass = ClassUtil.findPsiClass(myPsiManager, actionClassName, null, true, myScope);
+ if (actionClass == null) {
+ addProblem(token,
+ DevKitBundle.message("inspections.registration.problems.cannot.resolve.class",
+ DevKitBundle.message("class.action")),
+ ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, myOnTheFly, ((LocalQuickFix)QuickFixFactory.getInstance()
+ .createCreateClassOrInterfaceFix(token, actionClassName, true, AnAction.class.getName())));
+ } else {
+ if (!type.isOfType(actionClass)) {
+ final PsiClass psiClass = JavaPsiFacade.getInstance(myPsiManager.getProject()).findClass(type.myClassName, myScope);
+ if (psiClass != null && !actionClass.isInheritor(psiClass, true)) {
+ addProblem(token,
+ DevKitBundle.message("inspections.registration.problems.action.incompatible.class", type.myClassName),
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING, myOnTheFly, ImplementOrExtendFix.createFix(psiClass, actionClass, myOnTheFly));
+ }
+ }
+ final ConstructorType noArgCtor = ConstructorType.getNoArgCtor(actionClass);
+ if (noArgCtor == null) {
+ addProblem(token,
+ DevKitBundle.message("inspections.registration.problems.missing.noarg.ctor"),
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING, myOnTheFly, new CreateConstructorFix(actionClass, myOnTheFly));
+ }
+ if (isAbstract(actionClass)) {
+ addProblem(token,
+ DevKitBundle.message("inspections.registration.problems.abstract"),
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING, myOnTheFly);
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ private void addProblem(XmlTagValue impl, String problem, ProblemHighlightType type, boolean isOnTheFly, LocalQuickFix... fixes) {
+ final XmlText[] textElements = impl.getTextElements();
+ for (XmlText text : textElements) {
+ if (text.getValue().trim().length() > 0) {
+ addProblem(text, problem, type, isOnTheFly, fixes);
+ }
+ }
+ }
+
+ private void addProblem(PsiElement element, String problem, ProblemHighlightType type, boolean onTheFly, LocalQuickFix... fixes) {
+ if (myList == null) myList = new SmartList<ProblemDescriptor>();
+ myList.add(myManager.createProblemDescriptor(element, problem, onTheFly, fixes, type));
+ }
+
+ @Nullable
+ public ProblemDescriptor[] getProblems() {
+ return myList != null ? myList.toArray(new ProblemDescriptor[myList.size()]) : null;
+ }
+ }
+
+ static class ConstructorType {
+ static final ConstructorType DEFAULT = new ConstructorType();
+ final PsiMethod myCtor;
+
+ private ConstructorType() {
+ myCtor = null;
+ }
+
+ protected ConstructorType(PsiMethod ctor) {
+ assert ctor != null;
+ myCtor = ctor;
+ }
+
+ public static ConstructorType getNoArgCtor(PsiClass checkedClass) {
+ final PsiMethod[] constructors = checkedClass.getConstructors();
+ if (constructors.length > 0) {
+ for (PsiMethod ctor : constructors) {
+ if (ctor.getParameterList().getParametersCount() == 0) {
+ return new ConstructorType(ctor);
+ }
+ }
+ return null;
+ }
+ return ConstructorType.DEFAULT;
+ }
+ }
+}
diff --git a/plugins/devkit/src/inspections/TitleCapitalizationInspection.java b/plugins/devkit/src/inspections/TitleCapitalizationInspection.java
new file mode 100644
index 0000000..7f9af13
--- /dev/null
+++ b/plugins/devkit/src/inspections/TitleCapitalizationInspection.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2000-2011 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections;
+
+import com.intellij.codeInspection.*;
+import com.intellij.lang.properties.psi.Property;
+import com.intellij.lang.properties.references.PropertyReference;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.ReadonlyStatusHandler;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.refactoring.psi.PropertyUtils;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author yole
+ */
+public class TitleCapitalizationInspection extends BaseJavaLocalInspectionTool {
+ @Override
+ public boolean isEnabledByDefault() {
+ return true;
+ }
+
+ @Nls
+ @NotNull
+ @Override
+ public String getGroupDisplayName() {
+ return "Plugin DevKit";
+ }
+
+ @Nls
+ @NotNull
+ @Override
+ public String getDisplayName() {
+ return "Incorrect dialog title capitalization";
+ }
+
+ @NotNull
+ @Override
+ public String getShortName() {
+ return "DialogTitleCapitalization";
+ }
+
+ @NotNull
+ @Override
+ public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
+ return new JavaElementVisitor() {
+ @Override
+ public void visitMethodCallExpression(PsiMethodCallExpression expression) {
+ PsiReferenceExpression methodExpression = expression.getMethodExpression();
+ String calledName = methodExpression.getReferenceName();
+ if (calledName == null) return;
+ if ("setTitle".equals(calledName)) {
+ if (!isMethodOfClass(expression, DialogWrapper.class.getName(), FileChooserDescriptor.class.getName())) return;
+ PsiExpression[] args = expression.getArgumentList().getExpressions();
+ if (args.length == 0) {
+ return;
+ }
+ String titleValue = getTitleValue(args [0]);
+ if (!hasTitleCapitalization(titleValue)) {
+ holder.registerProblem(args [0], "Dialog title '" + titleValue + "' is not properly capitalized. It should have title capitalization",
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new TitleCapitalizationFix(titleValue));
+ }
+ }
+ else if (calledName.startsWith("show") && (calledName.endsWith("Dialog") || calledName.endsWith("Message"))) {
+ if (!isMethodOfClass(expression, Messages.class.getName())) return;
+ PsiExpression[] args = expression.getArgumentList().getExpressions();
+ PsiMethod psiMethod = expression.resolveMethod();
+ assert psiMethod != null;
+ PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
+ for (int i = 0, parametersLength = parameters.length; i < parametersLength; i++) {
+ PsiParameter parameter = parameters[i];
+ if ("title".equals(parameter.getName()) && i < args.length) {
+ String titleValue = getTitleValue(args [i]);
+ if (!hasTitleCapitalization(titleValue)) {
+ holder.registerProblem(args [i], "Message title '" + titleValue + "' is not properly capitalized. It should have title capitalization",
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new TitleCapitalizationFix(titleValue));
+ }
+ break;
+ }
+ }
+ }
+ }
+ };
+ }
+
+ private static boolean isMethodOfClass(PsiMethodCallExpression expression, String... classNames) {
+ PsiMethod psiMethod = expression.resolveMethod();
+ if (psiMethod == null) {
+ return false;
+ }
+ PsiClass containingClass = psiMethod.getContainingClass();
+ if (containingClass == null) {
+ return false;
+ }
+ String name = containingClass.getQualifiedName();
+ return ArrayUtil.contains(name, classNames);
+ }
+
+ @Nullable
+ private static String getTitleValue(PsiExpression arg) {
+ if (arg instanceof PsiLiteralExpression) {
+ Object value = ((PsiLiteralExpression)arg).getValue();
+ if (value instanceof String) {
+ return (String) value;
+ }
+ }
+ if (arg instanceof PsiMethodCallExpression) {
+ PsiMethod psiMethod = ((PsiMethodCallExpression)arg).resolveMethod();
+ PsiExpression returnValue = PropertyUtils.getGetterReturnExpression(psiMethod);
+ if (returnValue != null) {
+ return getTitleValue(returnValue);
+ }
+ Property propertyArgument = getPropertyArgument((PsiMethodCallExpression)arg);
+ if (propertyArgument != null) {
+ return propertyArgument.getUnescapedValue();
+ }
+ }
+ if (arg instanceof PsiReferenceExpression) {
+ PsiElement result = ((PsiReferenceExpression)arg).resolve();
+ if (result instanceof PsiVariable && ((PsiVariable)result).hasModifierProperty(PsiModifier.FINAL)) {
+ return getTitleValue(((PsiVariable) result).getInitializer());
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ private static Property getPropertyArgument(PsiMethodCallExpression arg) {
+ PsiExpression[] args = arg.getArgumentList().getExpressions();
+ if (args.length > 0) {
+ PsiReference[] references = args[0].getReferences();
+ for (PsiReference reference : references) {
+ if (reference instanceof PropertyReference) {
+ ResolveResult[] resolveResults = ((PropertyReference)reference).multiResolve(false);
+ if (resolveResults.length == 1 && resolveResults[0].isValidResult()) {
+ PsiElement element = resolveResults[0].getElement();
+ if (element instanceof Property) {
+ return (Property) element;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private static boolean hasTitleCapitalization(String value) {
+ if (value == null) {
+ return true;
+ }
+ value = value.replace("&", "");
+ return StringUtil.wordsToBeginFromUpperCase(value).equals(value);
+ }
+
+ private static class TitleCapitalizationFix implements LocalQuickFix {
+
+ private final String myTitleValue;
+
+ public TitleCapitalizationFix(String titleValue) {
+ myTitleValue = titleValue;
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return "Properly capitalize '" + myTitleValue + '\'';
+ }
+
+ public final void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
+ final PsiElement problemElement = descriptor.getPsiElement();
+ if (problemElement == null || !problemElement.isValid()) {
+ return;
+ }
+ if (isQuickFixOnReadOnlyFile(problemElement)) {
+ return;
+ }
+ try {
+ doFix(project, problemElement);
+ }
+ catch (IncorrectOperationException e) {
+ final Class<? extends TitleCapitalizationFix> aClass = getClass();
+ final String className = aClass.getName();
+ final Logger logger = Logger.getInstance(className);
+ logger.error(e);
+ }
+ }
+
+ protected void doFix(Project project, PsiElement element) throws IncorrectOperationException {
+ if (element instanceof PsiLiteralExpression) {
+ final PsiLiteralExpression literalExpression = (PsiLiteralExpression)element;
+ final Object value = literalExpression.getValue();
+ if (!(value instanceof String)) {
+ return;
+ }
+ final String string = (String)value;
+ final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
+ final PsiExpression
+ newExpression = factory.createExpressionFromText('"' + StringUtil.wordsToBeginFromUpperCase(string) + '"', element);
+ literalExpression.replace(newExpression);
+ }else if (element instanceof PsiMethodCallExpression) {
+ final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)element;
+ final PsiMethod method = methodCallExpression.resolveMethod();
+ final PsiExpression returnValue = PropertyUtils.getGetterReturnExpression(method);
+ if (returnValue != null) {
+ doFix(project, returnValue);
+ }
+ final Property property = getPropertyArgument(methodCallExpression);
+ if (property == null) {
+ return;
+ }
+ final String value = property.getUnescapedValue();
+ if (value == null) {
+ return;
+ }
+ final String capitalizedString = StringUtil.wordsToBeginFromUpperCase(value);
+ property.setValue(capitalizedString);
+ } else if (element instanceof PsiReferenceExpression) {
+ final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)element;
+ final PsiElement target = referenceExpression.resolve();
+ if (!(target instanceof PsiVariable)) {
+ return;
+ }
+ final PsiVariable variable = (PsiVariable)target;
+ if (variable.hasModifierProperty(PsiModifier.FINAL)) {
+ doFix(project, variable.getInitializer());
+ }
+ }
+ }
+
+ protected static boolean isQuickFixOnReadOnlyFile(PsiElement problemElement) {
+ final PsiFile containingPsiFile = problemElement.getContainingFile();
+ if (containingPsiFile == null) {
+ return false;
+ }
+ final VirtualFile virtualFile = containingPsiFile.getVirtualFile();
+ if (virtualFile == null) {
+ return false;
+ }
+ final Project project = problemElement.getProject();
+ final ReadonlyStatusHandler handler = ReadonlyStatusHandler.getInstance(project);
+ final ReadonlyStatusHandler.OperationStatus status = handler.ensureFilesWritable(virtualFile);
+ return status.hasReadonlyFiles();
+ }
+
+ @NotNull
+ @Override
+ public String getFamilyName() {
+ return "Properly capitalize";
+ }
+ }
+}
diff --git a/plugins/devkit/src/inspections/UseGrayInspection.java b/plugins/devkit/src/inspections/UseGrayInspection.java
new file mode 100644
index 0000000..18326f0
--- /dev/null
+++ b/plugins/devkit/src/inspections/UseGrayInspection.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections;
+
+import com.intellij.codeInspection.InspectionManager;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.codeInspection.ProblemHighlightType;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.NullUtils;
+import com.intellij.psi.*;
+import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.ui.Gray;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.inspections.quickfix.ConvertToGrayQuickFix;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class UseGrayInspection extends DevKitInspectionBase {
+ @NotNull
+ @Override
+ public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
+ return new JavaElementVisitor() {
+ @Override
+ public void visitNewExpression(PsiNewExpression expression) {
+ final ProblemDescriptor descriptor = checkNewExpression(expression, holder.getManager(), isOnTheFly);
+ if (descriptor != null) {
+ holder.registerProblem(descriptor);
+ }
+ }
+ };
+ }
+
+ @Nullable
+ private static ProblemDescriptor checkNewExpression(PsiNewExpression expression, InspectionManager manager, boolean isOnTheFly) {
+ final Project project = manager.getProject();
+ final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
+ final PsiClass grayClass = facade.findClass(Gray.class.getName(), GlobalSearchScope.allScope(project));
+ final PsiType type = expression.getType();
+ if (type != null && grayClass != null) {
+ final PsiExpressionList arguments = expression.getArgumentList();
+ if (arguments != null) {
+ final PsiExpression[] expressions = arguments.getExpressions();
+ if (expressions.length == 3 && "java.awt.Color".equals(type.getCanonicalText())) {
+ if (! facade.getResolveHelper().isAccessible(grayClass, expression, grayClass)) return null;
+ final PsiExpression r = expressions[0];
+ final PsiExpression g = expressions[1];
+ final PsiExpression b = expressions[2];
+ if (r instanceof PsiLiteralExpression
+ && g instanceof PsiLiteralExpression
+ && b instanceof PsiLiteralExpression) {
+ final Object red = JavaConstantExpressionEvaluator.computeConstantExpression(r, false);
+ final Object green = JavaConstantExpressionEvaluator.computeConstantExpression(g, false);
+ final Object blue = JavaConstantExpressionEvaluator.computeConstantExpression(b, false);
+ if (NullUtils.notNull(red, green, blue)) {
+ try {
+ int rr = Integer.parseInt(red.toString());
+ int gg = Integer.parseInt(green.toString());
+ int bb = Integer.parseInt(blue.toString());
+ if (rr == gg && gg == bb && 0 <= rr && rr < 256) {
+ return manager.createProblemDescriptor(expression, "Convert to Gray._" + rr, new ConvertToGrayQuickFix(rr), ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly);
+ }
+ } catch (Exception ignore){}
+ }
+ }
+ } else if (expressions.length == 1 && "com.intellij.ui.Gray".equals(type.getCanonicalText())) {
+ final PsiExpression e = expressions[0];
+ if (e instanceof PsiLiteralExpression) {
+ final Object literal = JavaConstantExpressionEvaluator.computeConstantExpression(e, false);
+ if (literal != null) {
+ try {
+ int num = Integer.parseInt(literal.toString());
+ if (0 <= num && num < 256) {
+ return manager.createProblemDescriptor(expression, "Convert to Gray_" + num, new ConvertToGrayQuickFix(num), ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly);
+ }
+ } catch (Exception ignore){}
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ @Nls
+ @NotNull
+ @Override
+ public String getDisplayName() {
+ return "Using new Color(a,a,a)";
+ }
+
+ @NotNull
+ @Override
+ public String getShortName() {
+ return "InspectionUsingGrayColors";
+ }
+}
diff --git a/plugins/devkit/src/inspections/UseJBColorInspection.java b/plugins/devkit/src/inspections/UseJBColorInspection.java
new file mode 100644
index 0000000..833b774
--- /dev/null
+++ b/plugins/devkit/src/inspections/UseJBColorInspection.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections;
+
+import com.intellij.codeInspection.InspectionManager;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.codeInspection.ProblemHighlightType;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtilCore;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.ui.JBColor;
+import com.intellij.util.PlatformUtils;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.inspections.quickfix.ConvertToJBColorConstantQuickFix;
+import org.jetbrains.idea.devkit.inspections.quickfix.ConvertToJBColorQuickFix;
+import org.jetbrains.idea.devkit.module.PluginModuleType;
+
+import java.awt.*;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class UseJBColorInspection extends DevKitInspectionBase {
+ @NotNull
+ @Override
+ public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
+ final Module module = ModuleUtilCore.findModuleForPsiElement(holder.getFile());
+ if (module == null
+ || !(PlatformUtils.isIdeaProject(holder.getProject()) || PluginModuleType.isPluginModuleOrDependency(module))) {
+ return new JavaElementVisitor(){};
+ }
+ return new JavaElementVisitor() {
+ @Override
+ public void visitNewExpression(PsiNewExpression expression) {
+ final ProblemDescriptor descriptor = checkNewExpression(expression, holder.getManager(), isOnTheFly);
+ if (descriptor != null) {
+ holder.registerProblem(descriptor);
+ }
+ super.visitNewExpression(expression);
+ }
+
+ @Override
+ public void visitReferenceExpression(PsiReferenceExpression expression) {
+ super.visitReferenceExpression(expression);
+ final PsiElement colorField = expression.resolve();
+ if (colorField != null && colorField instanceof PsiField && ((PsiField)colorField).hasModifierProperty(PsiModifier.STATIC)) {
+ final PsiClass colorClass = ((PsiField)colorField).getContainingClass();
+ if (colorClass != null && Color.class.getName().equals(colorClass.getQualifiedName())) {
+ String text = expression.getText();
+ if (text.contains(".")) {
+ text = text.substring(text.lastIndexOf('.'));
+ }
+ if (text.startsWith(".")) {
+ text = text.substring(1);
+ }
+ if (text.equalsIgnoreCase("lightGray")) {
+ text = "LIGHT_GRAY";
+ } else if (text.equalsIgnoreCase("darkGray")) {
+ text = "DARK_GRAY";
+ }
+ final ProblemDescriptor descriptor = holder.getManager()
+ .createProblemDescriptor(expression, "Change to JBColor." + text.toUpperCase(), new ConvertToJBColorConstantQuickFix(text.toUpperCase()),
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly);
+ holder.registerProblem(descriptor);
+ }
+ }
+ }
+ };
+ }
+
+ @Nullable
+ private static ProblemDescriptor checkNewExpression(PsiNewExpression expression, InspectionManager manager, boolean isOnTheFly) {
+ final Project project = manager.getProject();
+ final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
+ final PsiClass jbColorClass = facade.findClass(JBColor.class.getName(), GlobalSearchScope.allScope(project));
+ final PsiType type = expression.getType();
+ if (type != null && jbColorClass != null) {
+ if (!facade.getResolveHelper().isAccessible(jbColorClass, expression, jbColorClass)) return null;
+ final PsiExpressionList arguments = expression.getArgumentList();
+ if (arguments != null) {
+ if ("java.awt.Color".equals(type.getCanonicalText())) {
+ final PsiElement parent = expression.getParent();
+ if (parent instanceof PsiExpressionList && parent.getParent() instanceof PsiNewExpression) {
+ final PsiType parentType = ((PsiNewExpression)parent.getParent()).getType();
+ if (parentType == null || JBColor.class.getName().equals(parentType.getCanonicalText())) return null;
+ }
+ return manager.createProblemDescriptor(expression, "Replace with JBColor", new ConvertToJBColorQuickFix(),
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly);
+ }
+ }
+ }
+ return null;
+ }
+
+ @Nls
+ @NotNull
+ @Override
+ public String getDisplayName() {
+ return "Use Darcula aware JBColor";
+ }
+
+ @NotNull
+ @Override
+ public String getShortName() {
+ return "UseJBColor";
+ }
+}
diff --git a/plugins/devkit/src/inspections/quickfix/AbstractRegisterFix.java b/plugins/devkit/src/inspections/quickfix/AbstractRegisterFix.java
new file mode 100644
index 0000000..7bf3d9f
--- /dev/null
+++ b/plugins/devkit/src/inspections/quickfix/AbstractRegisterFix.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections.quickfix;
+
+import com.intellij.codeInsight.CodeInsightUtilBase;
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.module.PluginModuleType;
+import org.jetbrains.idea.devkit.util.ChooseModulesDialog;
+import org.jetbrains.idea.devkit.util.DescriptorUtil;
+
+import java.util.List;
+
+abstract class AbstractRegisterFix implements LocalQuickFix, DescriptorUtil.Patcher {
+ protected final PsiClass myClass;
+ private static final Logger LOG = Logger.getInstance("org.jetbrains.idea.devkit.inspections.quickfix.AbstractRegisterFix");
+
+ public AbstractRegisterFix(PsiClass klass) {
+ myClass = klass;
+ }
+
+ @NotNull
+ public String getFamilyName() {
+ return DevKitBundle.message("inspections.component.not.registered.quickfix.family");
+ }
+
+ @NotNull
+ public String getName() {
+ return DevKitBundle.message("inspections.component.not.registered.quickfix.name", getType());
+ }
+
+ protected abstract String getType();
+
+ // copy of com.intellij.ide.actions.CreateElementActionBase.filterMessage()
+ protected static String filterMessage(String message) {
+ if (message == null) return null;
+ @NonNls final String ioExceptionPrefix = "java.io.IOException:";
+ if (message.startsWith(ioExceptionPrefix)) {
+ message = message.substring(ioExceptionPrefix.length());
+ }
+ return message;
+ }
+
+ public void applyFix(@NotNull final Project project, @NotNull ProblemDescriptor descriptor) {
+ if (!CodeInsightUtilBase.preparePsiElementForWrite(descriptor.getPsiElement())) return;
+ final PsiFile psiFile = myClass.getContainingFile();
+ LOG.assertTrue(psiFile != null);
+ final Module module = ModuleUtil.findModuleForFile(psiFile.getVirtualFile(), project);
+
+ Runnable command = new Runnable() {
+ public void run() {
+ try {
+ if (PluginModuleType.isOfType(module)) {
+ final XmlFile pluginXml = PluginModuleType.getPluginXml(module);
+ if (pluginXml != null) {
+ DescriptorUtil.patchPluginXml(AbstractRegisterFix.this, myClass, pluginXml);
+ }
+ }
+ else {
+ List<Module> modules = PluginModuleType.getCandidateModules(module);
+ if (modules.size() > 1) {
+ final ChooseModulesDialog dialog = new ChooseModulesDialog(project, modules, getName());
+ dialog.show();
+
+ if (!dialog.isOK()) {
+ return;
+ }
+ modules = dialog.getSelectedModules();
+ }
+ final XmlFile[] pluginXmls = new XmlFile[modules.size()];
+ for (int i = 0; i < pluginXmls.length; i++) {
+ pluginXmls[i] = PluginModuleType.getPluginXml(modules.get(i));
+ }
+
+ DescriptorUtil.patchPluginXml(AbstractRegisterFix.this, myClass, pluginXmls);
+ }
+ CommandProcessor.getInstance().markCurrentCommandAsGlobal(project);
+ }
+ catch (IncorrectOperationException e) {
+ Messages.showMessageDialog(project, filterMessage(e.getMessage()),
+ DevKitBundle.message("inspections.component.not.registered.quickfix.error", getType()),
+ Messages.getErrorIcon());
+ }
+ }
+ };
+ CommandProcessor.getInstance().executeCommand(project, command, getName(), null);
+ }
+}
diff --git a/plugins/devkit/src/inspections/quickfix/BaseFix.java b/plugins/devkit/src/inspections/quickfix/BaseFix.java
new file mode 100644
index 0000000..6dd7757
--- /dev/null
+++ b/plugins/devkit/src/inspections/quickfix/BaseFix.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections.quickfix;
+
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.vfs.ReadonlyStatusHandler;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author swr
+ */
+abstract class BaseFix implements LocalQuickFix {
+ protected final PsiElement myElement;
+ protected final boolean myOnTheFly;
+
+ protected BaseFix(PsiElement element, boolean onTheFly) {
+ myElement = element;
+ myOnTheFly = onTheFly;
+ }
+
+ public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
+ // can happen during batch-inspection if resolution has already been applied
+ // to plugin.xml or java class
+ if (!myElement.isValid()) return;
+
+ final boolean external = descriptor.getPsiElement().getContainingFile() != myElement.getContainingFile();
+ if (external) {
+ final PsiClass clazz = PsiTreeUtil.getParentOfType(myElement, PsiClass.class, false);
+ final ReadonlyStatusHandler readonlyStatusHandler = ReadonlyStatusHandler.getInstance(project);
+ final VirtualFile[] files = new VirtualFile[]{myElement.getContainingFile().getVirtualFile()};
+ final ReadonlyStatusHandler.OperationStatus status = readonlyStatusHandler.ensureFilesWritable(files);
+
+ if (status.hasReadonlyFiles()) {
+ final String className = clazz != null ? clazz.getQualifiedName() : myElement.getContainingFile().getName();
+
+ Messages.showMessageDialog(project,
+ DevKitBundle.message("inspections.registration.problems.quickfix.read-only",
+ className),
+ getName(),
+ Messages.getErrorIcon());
+ return;
+ }
+ }
+
+ try {
+ doFix(project, descriptor, external);
+ }
+ catch (IncorrectOperationException e) {
+ Logger.getInstance("#" + getClass().getName()).error(e);
+ }
+ }
+
+ protected abstract void doFix(Project project, ProblemDescriptor descriptor, boolean external) throws IncorrectOperationException;
+}
diff --git a/plugins/devkit/src/inspections/quickfix/ConvertToGrayQuickFix.java b/plugins/devkit/src/inspections/quickfix/ConvertToGrayQuickFix.java
new file mode 100644
index 0000000..0b9dbb9
--- /dev/null
+++ b/plugins/devkit/src/inspections/quickfix/ConvertToGrayQuickFix.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections.quickfix;
+
+import com.intellij.codeInspection.LocalQuickFixBase;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementFactory;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.codeStyle.JavaCodeStyleManager;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class ConvertToGrayQuickFix extends LocalQuickFixBase {
+ private final int myNum;
+
+ public ConvertToGrayQuickFix(int num) {
+ super("Convert to Gray._" + num, "Convert to Gray");
+ myNum = num;
+ }
+
+ @Override
+ public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
+ final PsiElement element = descriptor.getPsiElement();
+ final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
+ final PsiExpression expression = factory.createExpressionFromText("com.intellij.ui.Gray._" + myNum, element.getContext());
+ final PsiElement newElement = element.replace(expression);
+ JavaCodeStyleManager.getInstance(project).shortenClassReferences(newElement);
+ }
+}
diff --git a/plugins/devkit/src/inspections/quickfix/ConvertToJBColorConstantQuickFix.java b/plugins/devkit/src/inspections/quickfix/ConvertToJBColorConstantQuickFix.java
new file mode 100644
index 0000000..fa65789
--- /dev/null
+++ b/plugins/devkit/src/inspections/quickfix/ConvertToJBColorConstantQuickFix.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections.quickfix;
+
+import com.intellij.codeInspection.LocalQuickFixBase;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementFactory;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.codeStyle.JavaCodeStyleManager;
+import com.intellij.ui.JBColor;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class ConvertToJBColorConstantQuickFix extends LocalQuickFixBase {
+ private final String myConstantName;
+
+ public ConvertToJBColorConstantQuickFix(String constantName) {
+ super("Convert to JBColor." + constantName, "Convert to JBColor");
+ myConstantName = constantName;
+ }
+
+ @Override
+ public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
+ final PsiElement element = descriptor.getPsiElement();
+ final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
+ final String jbColorConstant = String.format("%s.%s", JBColor.class.getName(), myConstantName);
+ final PsiExpression expression = factory.createExpressionFromText(jbColorConstant, element.getContext());
+ final PsiElement newElement = element.replace(expression);
+ JavaCodeStyleManager.getInstance(project).shortenClassReferences(newElement);
+ }
+}
diff --git a/plugins/devkit/src/inspections/quickfix/ConvertToJBColorQuickFix.java b/plugins/devkit/src/inspections/quickfix/ConvertToJBColorQuickFix.java
new file mode 100644
index 0000000..2f6187b
--- /dev/null
+++ b/plugins/devkit/src/inspections/quickfix/ConvertToJBColorQuickFix.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections.quickfix;
+
+import com.intellij.codeInspection.LocalQuickFixBase;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementFactory;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.codeStyle.JavaCodeStyleManager;
+import com.intellij.psi.util.PsiUtilBase;
+import com.intellij.ui.JBColor;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class ConvertToJBColorQuickFix extends LocalQuickFixBase {
+ public ConvertToJBColorQuickFix() {
+ super("Convert to JBColor");
+ }
+
+ @Override
+ public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
+ final PsiElement element = descriptor.getPsiElement();
+ final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
+ final String newJBColor = String.format("new %s(%s, new java.awt.Color())", JBColor.class.getName(), element.getText());
+ final PsiExpression expression = factory.createExpressionFromText(newJBColor, element.getContext());
+ final PsiElement newElement = element.replace(expression);
+ final PsiElement el = JavaCodeStyleManager.getInstance(project).shortenClassReferences(newElement);
+ final int offset = el.getTextOffset() + el.getText().length() - 2;
+ final Editor editor = PsiUtilBase.findEditor(el);
+ if (editor != null) {
+ editor.getCaretModel().moveToOffset(offset);
+ }
+ }
+}
diff --git a/plugins/devkit/src/inspections/quickfix/CreateConstructorFix.java b/plugins/devkit/src/inspections/quickfix/CreateConstructorFix.java
new file mode 100644
index 0000000..cce73cd
--- /dev/null
+++ b/plugins/devkit/src/inspections/quickfix/CreateConstructorFix.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections.quickfix;
+
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiModifier;
+import com.intellij.psi.util.PsiUtil;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.DevKitBundle;
+
+/**
+ * @author swr
+ */
+public class CreateConstructorFix extends BaseFix {
+
+ public CreateConstructorFix(PsiClass checkedClass, boolean onTheFly) {
+ super(checkedClass, onTheFly);
+ }
+
+ @NotNull
+ public String getName() {
+ return DevKitBundle.message("inspections.registration.problems.quickfix.create.constructor");
+ }
+
+ @NotNull
+ public String getFamilyName() {
+ return getName();
+ }
+
+ protected void doFix(Project project, ProblemDescriptor descriptor, boolean external) throws IncorrectOperationException {
+ final PsiClass clazz = (PsiClass)myElement;
+
+ PsiMethod ctor = JavaPsiFacade.getInstance(clazz.getProject()).getElementFactory().createConstructor();
+ PsiUtil.setModifierProperty(ctor, PsiModifier.PUBLIC, true);
+
+ final PsiMethod[] constructors = clazz.getConstructors();
+ if (constructors.length > 0) {
+ ctor = (PsiMethod)clazz.addBefore(ctor, constructors[0]);
+ } else {
+ // shouldn't get here - it's legal if there's no ctor present at all
+ ctor = (PsiMethod)clazz.add(ctor);
+ }
+
+ if (myOnTheFly) ctor.navigate(true);
+ }
+}
diff --git a/plugins/devkit/src/inspections/quickfix/CreateHtmlDescriptionFix.java b/plugins/devkit/src/inspections/quickfix/CreateHtmlDescriptionFix.java
new file mode 100644
index 0000000..aeaeb87
--- /dev/null
+++ b/plugins/devkit/src/inspections/quickfix/CreateHtmlDescriptionFix.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.idea.devkit.inspections.quickfix;
+
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.ide.fileTemplates.FileTemplate;
+import com.intellij.ide.fileTemplates.FileTemplateManager;
+import com.intellij.ide.fileTemplates.FileTemplateUtil;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.popup.JBPopup;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.util.Iconable;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.ui.components.JBList;
+import com.intellij.util.ArrayUtil;
+import icons.DevkitIcons;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.inspections.InspectionDescriptionNotFoundInspection;
+import org.jetbrains.idea.devkit.inspections.IntentionDescriptionNotFoundInspection;
+
+import javax.swing.*;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class CreateHtmlDescriptionFix implements LocalQuickFix, Iconable {
+ private final String myFilename;
+ private final Module myModule;
+ @NonNls private static final String TEMPLATE_NAME = "InspectionDescription.html";
+ private final boolean isIntention;
+
+ public CreateHtmlDescriptionFix(String filename, Module module, boolean isIntention) {
+ myModule = module;
+ this.isIntention = isIntention;
+ myFilename = isIntention ? filename : filename + ".html";
+ }
+
+ @NotNull
+ public String getName() {
+ return DevKitBundle.message("create.description.file");
+ }
+
+ @NotNull
+ public String getFamilyName() {
+ return "DevKit";
+ }
+
+ public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
+ final List<VirtualFile> virtualFiles = isIntention ?
+ IntentionDescriptionNotFoundInspection.getPotentialRoots(myModule)
+ :
+ InspectionDescriptionNotFoundInspection.getPotentialRoots(myModule);
+ final VirtualFile[] roots = prepare(VfsUtil.toVirtualFileArray(virtualFiles));
+ if (roots.length == 1) {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ public void run() {
+ createDescription(roots[0]);
+ }
+ });
+
+ }
+ else {
+ List<String> options = new ArrayList<String>();
+ for (VirtualFile file : roots) {
+ String path = file.getPresentableUrl() + File.separator + getDescriptionFolderName() + File.separator + myFilename;
+ if (isIntention) {
+ path += File.separator + "description.html";
+ }
+ options.add(path);
+ }
+ final JBList files = new JBList(ArrayUtil.toStringArray(options));
+ files.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ final JBPopup popup = JBPopupFactory.getInstance()
+ .createListPopupBuilder(files)
+ .setTitle(DevKitBundle.message("select.target.location.of.description", myFilename))
+ .setItemChoosenCallback(new Runnable() {
+ public void run() {
+ final int index = files.getSelectedIndex();
+ if (0 <= index && index < roots.length) {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ public void run() {
+ createDescription(roots[index]);
+ }
+ });
+ }
+ }
+ }).createPopup();
+ final Editor editor = FileEditorManager.getInstance(myModule.getProject()).getSelectedTextEditor();
+ if (editor == null) return;
+ popup.showInBestPositionFor(editor);
+ }
+ }
+
+ private void createDescription(VirtualFile root) {
+ if (!root.isDirectory()) return;
+ final PsiManager psiManager = PsiManager.getInstance(myModule.getProject());
+ final PsiDirectory psiRoot = psiManager.findDirectory(root);
+ PsiDirectory descrRoot = null;
+ if (psiRoot == null) return;
+ for (PsiDirectory dir : psiRoot.getSubdirectories()) {
+ if (getDescriptionFolderName().equals(dir.getName())) {
+ descrRoot = dir;
+ break;
+ }
+ }
+
+ try {
+ descrRoot = descrRoot == null ? psiRoot.createSubdirectory(getDescriptionFolderName()) : descrRoot;
+ if (isIntention) {
+ PsiDirectory dir = descrRoot.findSubdirectory(myFilename);
+ if (dir == null) {
+ descrRoot = descrRoot.createSubdirectory(myFilename);
+ }
+ }
+ final FileTemplate descrTemplate = FileTemplateManager.getInstance().getJ2eeTemplate(TEMPLATE_NAME);
+ final PsiElement template = FileTemplateUtil.createFromTemplate(descrTemplate, isIntention? "description.html" : myFilename, null, descrRoot);
+ if (template instanceof PsiFile) {
+ final VirtualFile file = ((PsiFile)template).getVirtualFile();
+ if (file != null) {
+ FileEditorManager.getInstance(myModule.getProject()).openFile(file, true);
+ }
+ }
+ }
+ catch (Exception e) {//
+ }
+ }
+
+ public Icon getIcon(int flags) {
+ return DevkitIcons.New_html;
+ }
+
+ private VirtualFile[] prepare(VirtualFile[] roots) {
+ List<VirtualFile> found = new ArrayList<VirtualFile>();
+ for (VirtualFile root : roots) {
+ if (containsDescriptionDir(root)) {
+ found.add(root);
+ }
+ }
+ return found.size() > 0 ? VfsUtil.toVirtualFileArray(found) : roots;
+ }
+
+ private boolean containsDescriptionDir(VirtualFile root) {
+ if (!root.isDirectory()) return false;
+ for (VirtualFile file : root.getChildren()) {
+ if (file.isDirectory() && getDescriptionFolderName().equals(file.getName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private String getDescriptionFolderName() {
+ return isIntention ? "intentionDescriptions" : "inspectionDescriptions";
+ }
+}
diff --git a/plugins/devkit/src/inspections/quickfix/ImplementOrExtendFix.java b/plugins/devkit/src/inspections/quickfix/ImplementOrExtendFix.java
new file mode 100644
index 0000000..3169319
--- /dev/null
+++ b/plugins/devkit/src/inspections/quickfix/ImplementOrExtendFix.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections.quickfix;
+
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.pom.Navigatable;
+import com.intellij.psi.*;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.DevKitBundle;
+
+/**
+ * @author swr
+ */
+public class ImplementOrExtendFix extends BaseFix {
+
+ private final PsiClass myCompClass;
+
+ private ImplementOrExtendFix(PsiClass compClass, PsiClass checkedClass, boolean onTheFly) {
+ super(checkedClass, onTheFly);
+ myCompClass = compClass;
+ }
+
+ public static LocalQuickFix[] createFix(PsiClass compClass, PsiClass checkedClass, boolean onTheFly) {
+ ImplementOrExtendFix fix = null;
+
+ if (compClass.isInterface() && compClass.getImplementsList() != null) {
+ fix = new ImplementOrExtendFix(compClass, checkedClass, onTheFly);
+ } else if (!compClass.isInterface()) {
+ final PsiReferenceList extendsList = checkedClass.getExtendsList();
+ if (extendsList != null) {
+ if (extendsList.getReferenceElements().length == 0) {
+ fix = new ImplementOrExtendFix(compClass, checkedClass, onTheFly);
+ } else if (extendsList.getReferenceElements().length == 1) {
+ // check for explicit "extends Object" case
+ final PsiClassType javaLangObject = PsiType.getJavaLangObject(checkedClass.getManager(),
+ checkedClass.getResolveScope());
+ if (extendsList.getReferencedTypes()[0].equals(javaLangObject)) {
+ fix = new ImplementOrExtendFix(compClass, checkedClass, onTheFly);
+ }
+ }
+ }
+ }
+ return fix != null ? new LocalQuickFix[]{fix} : LocalQuickFix.EMPTY_ARRAY;
+ }
+
+ @NotNull
+ public String getName() {
+ return (myCompClass.isInterface()
+ ? StringUtil.capitalize(DevKitBundle.message("keyword.implement"))
+ : StringUtil.capitalize(DevKitBundle.message("keyword.extend")))
+ + " '" + myCompClass.getQualifiedName() + "'";
+ }
+
+ @NotNull
+ public String getFamilyName() {
+ return getName();
+ }
+
+ protected void doFix(Project project, ProblemDescriptor descriptor, boolean external) throws IncorrectOperationException {
+ final PsiClass clazz = (PsiClass)myElement;
+ final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(clazz.getProject()).getElementFactory();
+ final PsiClassType compType = elementFactory.createType(myCompClass);
+
+ final PsiReferenceList list;
+ if (myCompClass.isInterface()) {
+ list = clazz.getImplementsList();
+ assert list != null;
+ } else {
+ final PsiReferenceList extendsList = clazz.getExtendsList();
+ assert extendsList != null;
+ if (extendsList.getReferencedTypes().length > 0) {
+ extendsList.getReferenceElements()[0].delete();
+ }
+ list = extendsList;
+ }
+
+ final PsiElement e = list.add(elementFactory.createReferenceElementByType(compType));
+ if (myOnTheFly && external && e instanceof Navigatable) ((Navigatable)e).navigate(true);
+ }
+}
diff --git a/plugins/devkit/src/inspections/quickfix/RegisterActionFix.java b/plugins/devkit/src/inspections/quickfix/RegisterActionFix.java
new file mode 100644
index 0000000..061deef
--- /dev/null
+++ b/plugins/devkit/src/inspections/quickfix/RegisterActionFix.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections.quickfix;
+
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.actions.NewActionDialog;
+import org.jetbrains.idea.devkit.util.ActionType;
+import org.jetbrains.annotations.NotNull;
+
+public class RegisterActionFix extends AbstractRegisterFix {
+ private NewActionDialog myDialog;
+
+ public RegisterActionFix(PsiClass klass) {
+ super(klass);
+ }
+
+ protected String getType() {
+ return DevKitBundle.message("new.menu.action.text");
+ }
+
+ public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
+ try {
+ myDialog = new NewActionDialog(myClass);
+ myDialog.show();
+
+ if (myDialog.isOK()) {
+ super.applyFix(project, descriptor);
+ }
+ }
+ finally {
+ myDialog = null;
+ }
+ }
+
+ public void patchPluginXml(XmlFile pluginXml, PsiClass aClass) throws IncorrectOperationException {
+ if (ActionType.GROUP.isOfType(aClass)) {
+ ActionType.GROUP.patchPluginXml(pluginXml, aClass, myDialog);
+ } else {
+ ActionType.ACTION.patchPluginXml(pluginXml, aClass, myDialog);
+ }
+ }
+}
diff --git a/plugins/devkit/src/inspections/quickfix/RegisterComponentFix.java b/plugins/devkit/src/inspections/quickfix/RegisterComponentFix.java
new file mode 100644
index 0000000..0c3d0fd
--- /dev/null
+++ b/plugins/devkit/src/inspections/quickfix/RegisterComponentFix.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections.quickfix;
+
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.util.ComponentType;
+
+public class RegisterComponentFix extends AbstractRegisterFix {
+ private final ComponentType myType;
+
+ public RegisterComponentFix(ComponentType type, PsiClass klass) {
+ super(klass);
+ myType = type;
+ }
+
+ protected String getType() {
+ return DevKitBundle.message(myType.myPropertyKey);
+ }
+
+ public void patchPluginXml(XmlFile pluginXml, PsiClass aClass) throws IncorrectOperationException {
+ myType.patchPluginXml(pluginXml, aClass);
+ }
+}
diff --git a/plugins/devkit/src/inspections/quickfix/RegisterInspectionFix.java b/plugins/devkit/src/inspections/quickfix/RegisterInspectionFix.java
new file mode 100644
index 0000000..b709bf8
--- /dev/null
+++ b/plugins/devkit/src/inspections/quickfix/RegisterInspectionFix.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections.quickfix;
+
+import com.intellij.codeInsight.hint.HintManager;
+import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.codeInspection.InspectionEP;
+import com.intellij.ide.TypePresentationService;
+import com.intellij.ide.plugins.PluginManager;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.command.WriteCommandAction;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.ui.popup.PopupStep;
+import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.util.IncorrectOperationException;
+import com.intellij.util.PsiNavigateUtil;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.xml.DomFileElement;
+import com.intellij.util.xml.DomService;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.dom.Extension;
+import org.jetbrains.idea.devkit.dom.Extensions;
+import org.jetbrains.idea.devkit.dom.IdeaPlugin;
+
+import javax.swing.*;
+import java.util.List;
+
+/**
+* @author Dmitry Avdeev
+* Date: 1/20/12
+*/
+class RegisterInspectionFix implements IntentionAction {
+
+ private final PsiClass myPsiClass;
+ private final ExtensionPointName<? extends InspectionEP> myEp;
+
+ RegisterInspectionFix(PsiClass psiClass, ExtensionPointName<? extends InspectionEP> ep) {
+ myPsiClass = psiClass;
+ myEp = ep;
+ }
+
+ @NotNull
+ @Override
+ public String getText() {
+ return "Register inspection";
+ }
+
+ @NotNull
+ @Override
+ public String getFamilyName() {
+ return DevKitBundle.message("inspections.component.not.registered.quickfix.family");
+ }
+
+ @Override
+ public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
+ return true;
+ }
+
+ @Override
+ public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException {
+ Module module = ModuleUtil.findModuleForPsiElement(file);
+ assert module != null;
+ List<DomFileElement<IdeaPlugin>> elements =
+ DomService.getInstance().getFileElements(IdeaPlugin.class, project, module.getModuleContentWithDependenciesScope());
+
+ elements = ContainerUtil.filter(elements, new Condition<DomFileElement<IdeaPlugin>>() {
+ @Override
+ public boolean value(DomFileElement<IdeaPlugin> element) {
+ VirtualFile virtualFile = element.getFile().getVirtualFile();
+ return virtualFile != null && ProjectRootManager.getInstance(project).getFileIndex().isInContent(virtualFile);
+ }
+ });
+
+ if (elements.isEmpty()) {
+ HintManager.getInstance().showErrorHint(editor, "Cannot find plugin descriptor");
+ return;
+ }
+
+ if (elements.size() == 1) {
+ doFix(elements.get(0), project, file);
+ return;
+ }
+
+ final BaseListPopupStep<DomFileElement<IdeaPlugin>> popupStep =
+ new BaseListPopupStep<DomFileElement<IdeaPlugin>>("Choose Plugin Descriptor", elements) {
+ @Override
+ public Icon getIconFor(DomFileElement<IdeaPlugin> aValue) {
+ return TypePresentationService.getService().getIcon(aValue);
+ }
+
+ @NotNull
+ @Override
+ public String getTextFor(DomFileElement<IdeaPlugin> value) {
+ final String name = value.getFile().getName();
+ if (!Comparing.equal(PluginManager.PLUGIN_XML, name)) {
+ return name;
+ }
+ final Module module = value.getModule();
+ return module != null ? name + " (" + module.getName() + ")" : name;
+ }
+
+ @Override
+ public PopupStep onChosen(DomFileElement<IdeaPlugin> selectedValue, boolean finalChoice) {
+ doFix(selectedValue, project, file);
+ return FINAL_CHOICE;
+ }
+ };
+ JBPopupFactory.getInstance().createListPopup(popupStep)
+ .showInBestPositionFor(editor);
+ }
+
+ private void doFix(DomFileElement<IdeaPlugin> selectedValue, final Project project, final PsiFile file) {
+ final IdeaPlugin plugin = selectedValue.getRootElement();
+ final List<Extensions> extensionsList = plugin.getExtensions();
+ Extension extension = new WriteCommandAction<Extension>(project, file) {
+
+ @Override
+ protected void run(Result<Extension> result) throws Throwable {
+ final Extensions extensions = getExtension(plugin, extensionsList);
+ Extension extension = extensions.addExtension(myEp.getName());
+ XmlTag tag = extension.getXmlTag();
+ tag.setAttribute("implementationClass", myPsiClass.getQualifiedName());
+ result.setResult(extension);
+ }
+ }.execute().throwException().getResultObject();
+ PsiNavigateUtil.navigate(extension.getXmlTag());
+ }
+
+ private Extensions getExtension(IdeaPlugin plugin, List<Extensions> extensionsList) {
+ Extensions extensions = null;
+ for (Extensions e : extensionsList) {
+ String s = e.getDefaultExtensionNs().getStringValue();
+ if (s != null && myEp.getName().startsWith(s)) {
+ extensions = e;
+ break;
+ }
+ }
+ if (extensions == null) {
+ extensions = plugin.addExtensions();
+ extensions.getDefaultExtensionNs().setStringValue("com.intellij");
+ }
+ return extensions;
+ }
+
+ @Override
+ public boolean startInWriteAction() {
+ return false;
+ }
+}
diff --git a/plugins/devkit/src/inspections/quickfix/RegisterInspectionFixProvider.java b/plugins/devkit/src/inspections/quickfix/RegisterInspectionFixProvider.java
new file mode 100644
index 0000000..c136d27
--- /dev/null
+++ b/plugins/devkit/src/inspections/quickfix/RegisterInspectionFixProvider.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.inspections.quickfix;
+
+import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.codeInspection.GlobalInspectionTool;
+import com.intellij.codeInspection.InspectionEP;
+import com.intellij.codeInspection.LocalInspectionEP;
+import com.intellij.codeInspection.LocalInspectionTool;
+import com.intellij.codeInspection.reference.UnusedDeclarationFixProvider;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiIdentifier;
+import com.intellij.psi.util.InheritanceUtil;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 1/19/12
+ */
+public class RegisterInspectionFixProvider implements UnusedDeclarationFixProvider {
+
+ @NotNull
+ @Override
+ public IntentionAction[] getQuickFixes(PsiElement element) {
+ if (!(element instanceof PsiIdentifier)) return IntentionAction.EMPTY_ARRAY;
+ PsiElement parent = element.getParent();
+ if (!(parent instanceof PsiClass)) return IntentionAction.EMPTY_ARRAY;
+ if (InheritanceUtil.isInheritor((PsiClass)parent, LocalInspectionTool.class.getName())) {
+ return new IntentionAction[] { new RegisterInspectionFix((PsiClass)parent, LocalInspectionEP.LOCAL_INSPECTION) };
+ }
+ if (InheritanceUtil.isInheritor((PsiClass)parent, GlobalInspectionTool.class.getName())) {
+ return new IntentionAction[] { new RegisterInspectionFix((PsiClass)parent, InspectionEP.GLOBAL_INSPECTION) };
+ }
+ return IntentionAction.EMPTY_ARRAY;
+ }
+}
diff --git a/plugins/devkit/src/module/PluginDescriptorConstants.java b/plugins/devkit/src/module/PluginDescriptorConstants.java
new file mode 100644
index 0000000..3949a85
--- /dev/null
+++ b/plugins/devkit/src/module/PluginDescriptorConstants.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.module;
+
+import com.intellij.util.descriptors.ConfigFileVersion;
+import com.intellij.util.descriptors.ConfigFileMetaData;
+import org.jetbrains.idea.devkit.DevKitBundle;
+
+public interface PluginDescriptorConstants {
+ String VERSION = "1.0";
+ ConfigFileVersion[] VERSIONS = {
+ new ConfigFileVersion(VERSION, "plugin.xml")
+ };
+
+ ConfigFileMetaData META_DATA =
+ new ConfigFileMetaData(DevKitBundle.message("plugin.descriptor"), "plugin.xml", "META-INF", VERSIONS, null, false, true, true);
+
+}
\ No newline at end of file
diff --git a/plugins/devkit/src/module/PluginModuleBuilder.java b/plugins/devkit/src/module/PluginModuleBuilder.java
new file mode 100644
index 0000000..2552b2b
--- /dev/null
+++ b/plugins/devkit/src/module/PluginModuleBuilder.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.module;
+
+import com.intellij.ide.util.projectWizard.JavaModuleBuilder;
+import com.intellij.openapi.module.JavaModuleType;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.projectRoots.SdkTypeId;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.startup.StartupManager;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.idea.devkit.build.PluginBuildConfiguration;
+import org.jetbrains.idea.devkit.projectRoots.IdeaJdk;
+
+public class PluginModuleBuilder extends JavaModuleBuilder{
+ @NonNls private static final String META_INF = "META-INF";
+ @NonNls private static final String PLUGIN_XML = "plugin.xml";
+
+
+ public ModuleType getModuleType() {
+ return PluginModuleType.getInstance();
+ }
+
+ public void setupRootModel(final ModifiableRootModel rootModel) throws ConfigurationException {
+ super.setupRootModel(rootModel);
+ final String defaultPluginXMLLocation = getModuleFileDirectory() + '/' + META_INF + '/' + PLUGIN_XML;
+ final Module module = rootModel.getModule();
+ StartupManager.getInstance(module.getProject()).runWhenProjectIsInitialized(new Runnable() {
+ public void run() {
+ final PluginBuildConfiguration buildConfiguration = PluginBuildConfiguration.getInstance(module);
+ if (buildConfiguration != null) {
+ buildConfiguration.setPluginXmlPathAndCreateDescriptorIfDoesntExist(defaultPluginXMLLocation);
+ }
+ }
+ });
+ }
+
+ @Override
+ public boolean isSuitableSdkType(SdkTypeId sdk) {
+ return sdk == IdeaJdk.getInstance();
+ }
+
+ @Override
+ public String getGroupName() {
+ return JavaModuleType.JAVA_GROUP;
+ }
+}
diff --git a/plugins/devkit/src/module/PluginModuleEditorsProvider.java b/plugins/devkit/src/module/PluginModuleEditorsProvider.java
new file mode 100644
index 0000000..2e91b57
--- /dev/null
+++ b/plugins/devkit/src/module/PluginModuleEditorsProvider.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.module;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleConfigurationEditor;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.roots.ui.configuration.DefaultModuleConfigurationEditorFactory;
+import com.intellij.openapi.roots.ui.configuration.ModuleConfigurationEditorProvider;
+import com.intellij.openapi.roots.ui.configuration.ModuleConfigurationState;
+import org.jetbrains.idea.devkit.build.PluginModuleBuildConfEditor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PluginModuleEditorsProvider implements ModuleConfigurationEditorProvider{
+
+ public ModuleConfigurationEditor[] createEditors(ModuleConfigurationState state) {
+ final Module module = state.getRootModel().getModule();
+ if (ModuleType.get(module) != PluginModuleType.getInstance()) return ModuleConfigurationEditor.EMPTY;
+
+ final DefaultModuleConfigurationEditorFactory editorFactory = DefaultModuleConfigurationEditorFactory.getInstance();
+ List<ModuleConfigurationEditor> editors = new ArrayList<ModuleConfigurationEditor>();
+ editors.add(editorFactory.createModuleContentRootsEditor(state));
+ editors.add(editorFactory.createOutputEditor(state));
+ editors.add(editorFactory.createClasspathEditor(state));
+ editors.add(new PluginModuleBuildConfEditor(state));
+ return editors.toArray(new ModuleConfigurationEditor[editors.size()]);
+ }
+}
\ No newline at end of file
diff --git a/plugins/devkit/src/module/PluginModuleType.java b/plugins/devkit/src/module/PluginModuleType.java
new file mode 100644
index 0000000..855f4b1
--- /dev/null
+++ b/plugins/devkit/src/module/PluginModuleType.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2000-2011 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.module;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.module.*;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.util.IconLoader;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.util.containers.HashSet;
+import com.intellij.util.descriptors.ConfigFile;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.build.PluginBuildConfiguration;
+import org.jetbrains.idea.devkit.build.PluginBuildUtil;
+import org.jetbrains.idea.devkit.projectRoots.IdeaJdk;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+public class PluginModuleType extends ModuleType<PluginModuleBuilder> {
+ private static final Icon ADD_PLUGIN_MODULE_ICON = IconLoader.getIcon("/add_plugin_modulewizard.png");
+ @NonNls private static final String ID = "PLUGIN_MODULE";
+
+ public PluginModuleType() {
+ super(ID);
+ }
+
+ public static PluginModuleType getInstance() {
+ return (PluginModuleType) ModuleTypeManager.getInstance().findByID(ID);
+ }
+
+ public static boolean isOfType(Module module) {
+ return get(module) instanceof PluginModuleType;
+ }
+
+ public PluginModuleBuilder createModuleBuilder() {
+ return new PluginModuleBuilder();
+ }
+
+ public String getName() {
+ return DevKitBundle.message("module.title");
+ }
+
+ public String getDescription() {
+ return DevKitBundle.message("module.description");
+ }
+
+ public Icon getBigIcon() {
+ return AllIcons.Modules.Types.PluginModule;
+ }
+
+ public Icon getNodeIcon(boolean isOpened) {
+ return AllIcons.Nodes.Plugin;
+ }
+
+ @Nullable
+ public static XmlFile getPluginXml(Module module) {
+ if (module == null) return null;
+ if (!(get(module) instanceof PluginModuleType)) return null;
+
+ final PluginBuildConfiguration buildConfiguration = PluginBuildConfiguration.getInstance(module);
+ if (buildConfiguration == null) return null;
+ final ConfigFile configFile = buildConfiguration.getPluginXmlConfigFile();
+ return configFile != null ? configFile.getXmlFile() : null;
+}
+
+ public static boolean isPluginModuleOrDependency(@NotNull Module module) {
+ if (isOfType(module)) return true;
+
+ return getCandidateModules(module).size() > 0;
+ }
+
+ public static List<Module> getCandidateModules(Module module) {
+ final ModuleRootManager manager = ModuleRootManager.getInstance(module);
+
+ final Sdk jdk = manager.getSdk();
+ // don't allow modules that don't use an IDEA-JDK
+ if (IdeaJdk.findIdeaJdk(jdk) == null) {
+ return Collections.emptyList();
+ }
+
+ final Module[] modules = ModuleManager.getInstance(module.getProject()).getModules();
+ final List<Module> candidates = new ArrayList<Module>(modules.length);
+ final Set<Module> deps = new HashSet<Module>(modules.length);
+ for (Module m : modules) {
+ if (get(m) == getInstance()) {
+ deps.clear();
+ PluginBuildUtil.getDependencies(m, deps);
+
+ if (deps.contains(module) && getPluginXml(m) != null) {
+ candidates.add(m);
+ }
+ }
+ }
+ return candidates;
+ }
+
+ @Override
+ public boolean isValidSdk(final Module module, final Sdk projectSdk) {
+ return JavaModuleType.isValidJavaSdk(module);
+ }
+
+ public static Module[] getAllPluginModules(final Project project) {
+ List<Module> modules = new ArrayList<Module>();
+ Module[] allModules = ModuleManager.getInstance(project).getModules();
+ for (Module module : allModules) {
+ if (get(module) == getInstance()) {
+ modules.add(module);
+ }
+ }
+ return modules.toArray(new Module[modules.size()]);
+ }
+}
diff --git a/plugins/devkit/src/projectRoots/IdeaJdk.java b/plugins/devkit/src/projectRoots/IdeaJdk.java
new file mode 100644
index 0000000..6b40ced
--- /dev/null
+++ b/plugins/devkit/src/projectRoots/IdeaJdk.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.projectRoots;
+
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.projectRoots.*;
+import com.intellij.openapi.projectRoots.impl.JavaDependentSdkType;
+import com.intellij.openapi.roots.AnnotationOrderRootType;
+import com.intellij.openapi.roots.JavadocOrderRootType;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.*;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.cls.BytePointer;
+import com.intellij.util.cls.ClsFormatException;
+import com.intellij.util.cls.ClsUtil;
+import icons.DevkitIcons;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.DevKitBundle;
+
+import javax.swing.*;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * User: anna
+ * Date: Nov 22, 2004
+ */
+public class IdeaJdk extends JavaDependentSdkType implements JavaSdkType {
+ private static final Icon ADD_SDK = DevkitIcons.Add_sdk;
+ private static final Icon SDK_CLOSED = DevkitIcons.Sdk_closed;
+
+ private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.devkit.projectRoots.IdeaJdk");
+ @NonNls private static final String LIB_DIR_NAME = "lib";
+ @NonNls private static final String SRC_DIR_NAME = "src";
+ @NonNls private static final String PLUGINS_DIR = "plugins";
+
+ public IdeaJdk() {
+ super("IDEA JDK");
+ }
+
+ public Icon getIcon() {
+ return SDK_CLOSED;
+ }
+
+ @NotNull
+ @Override
+ public String getHelpTopic() {
+ return "reference.project.structure.sdk.idea";
+ }
+
+ public Icon getIconForAddAction() {
+ return ADD_SDK;
+ }
+
+ public String suggestHomePath() {
+ return PathManager.getHomePath().replace(File.separatorChar, '/');
+ }
+
+ public boolean isValidSdkHome(String path) {
+ if (isFromIDEAProject(path)) {
+ return true;
+ }
+ File home = new File(path);
+ if (!home.exists()) {
+ return false;
+ }
+ if (getBuildNumber(path) == null || getOpenApiJar(path) == null) {
+ return false;
+ }
+ return true;
+ }
+
+ @Nullable
+ private static File getOpenApiJar(String home) {
+ @NonNls final String openapiJar = "openapi.jar";
+ @NonNls final String platformApiJar = "platform-api.jar";
+ final File libDir = new File(home, LIB_DIR_NAME);
+ File f = new File(libDir, openapiJar);
+ if (f.exists()) return f;
+ f = new File(libDir, platformApiJar);
+ if (f.exists()) return f;
+ return null;
+ }
+
+ public static boolean isFromIDEAProject(String path) {
+ File home = new File(path);
+ File[] openapiDir = home.listFiles(new FileFilter() {
+ public boolean accept(File pathname) {
+ @NonNls final String name = pathname.getName();
+ if (name.equals("openapi") && pathname.isDirectory()) return true; //todo
+ return false;
+ }
+ });
+ return openapiDir != null && openapiDir.length != 0;
+ }
+
+ @Nullable
+ public final String getVersionString(final Sdk sdk) {
+ final Sdk internalJavaSdk = getInternalJavaSdk(sdk);
+ return internalJavaSdk != null ? internalJavaSdk.getVersionString() : null;
+ }
+
+ @Nullable
+ private static Sdk getInternalJavaSdk(final Sdk sdk) {
+ final SdkAdditionalData data = sdk.getSdkAdditionalData();
+ if (data instanceof Sandbox) {
+ return ((Sandbox)data).getJavaSdk();
+ }
+ return null;
+ }
+
+ public String suggestSdkName(String currentSdkName, String sdkHome) {
+ @NonNls final String productName;
+ if (new File(sdkHome, "lib/rubymine.jar").exists()) {
+ productName = "RubyMine ";
+ }
+ else if (new File(sdkHome, "lib/pycharm.jar").exists()) {
+ productName = "PyCharm ";
+ }
+ else if (new File(sdkHome, "lib/webide.jar").exists()) {
+ productName = "WebStorm/PhpStorm ";
+ }
+ else if (new File(sdkHome, "license/AppCode_license.txt").exists()) {
+ productName = "AppCode ";
+ }
+ else {
+ productName = "IDEA ";
+ }
+ String buildNumber = getBuildNumber(sdkHome);
+ return productName + (buildNumber != null ? buildNumber : "");
+ }
+
+ @Nullable
+ public static String getBuildNumber(String ideaHome) {
+ try {
+ @NonNls final String buildTxt = "/build.txt";
+ return FileUtil.loadFile(new File(ideaHome + buildTxt)).trim();
+ }
+ catch (IOException e) {
+ return null;
+ }
+ }
+
+ private static VirtualFile[] getIdeaLibrary(String home) {
+ String plugins = home + File.separator + PLUGINS_DIR + File.separator;
+ ArrayList<VirtualFile> result = new ArrayList<VirtualFile>();
+ appendIdeaLibrary(home, result, "junit.jar");
+ appendIdeaLibrary(plugins + "JavaEE", result, "javaee-impl.jar", "jpa-console.jar");
+ appendIdeaLibrary(plugins + "JSF", result, "jsf-impl.jar");
+ appendIdeaLibrary(plugins + "PersistenceSupport", result, "persistence-impl.jar");
+ appendIdeaLibrary(plugins + "DatabaseSupport", result, "database-impl.jar", "jdbc-console.jar");
+ appendIdeaLibrary(plugins + "css", result, "css.jar");
+ appendIdeaLibrary(plugins + "uml", result, "uml-support.jar");
+ return VfsUtilCore.toVirtualFileArray(result);
+ }
+
+ private static void appendIdeaLibrary(final String libDirPath,
+ final ArrayList<VirtualFile> result,
+ @NonNls final String... forbidden) {
+ final String path = libDirPath + File.separator + LIB_DIR_NAME;
+ final JarFileSystem jfs = JarFileSystem.getInstance();
+ final File lib = new File(path);
+ if (lib.isDirectory()) {
+ File[] jars = lib.listFiles();
+ if (jars != null) {
+ for (File jar : jars) {
+ @NonNls String name = jar.getName();
+ if (jar.isFile() && Arrays.binarySearch(forbidden, name) < 0 && (name.endsWith(".jar") || name.endsWith(".zip"))) {
+ result.add(jfs.findFileByPath(jar.getPath() + JarFileSystem.JAR_SEPARATOR));
+ }
+ }
+ }
+ }
+ }
+
+
+ public boolean setupSdkPaths(final Sdk sdk, SdkModel sdkModel) {
+ final Sandbox additionalData = (Sandbox)sdk.getSdkAdditionalData();
+ if (additionalData != null) {
+ additionalData.cleanupWatchedRoots();
+ }
+
+ final SdkModificator sdkModificator = sdk.getSdkModificator();
+
+ final List<String> javaSdks = new ArrayList<String>();
+ final Sdk[] sdks = sdkModel.getSdks();
+ for (Sdk jdk : sdks) {
+ if (isValidInternalJdk(sdk, jdk)) {
+ javaSdks.add(jdk.getName());
+ }
+ }
+ if (javaSdks.isEmpty()){
+ JavaSdkVersion requiredVersion = getRequiredJdkVersion(sdk);
+ if (requiredVersion != null) {
+ Messages.showErrorDialog(DevKitBundle.message("no.java.sdk.for.idea.sdk.found", requiredVersion), "No Java SDK Found");
+ }
+ else {
+ Messages.showErrorDialog(DevKitBundle.message("no.idea.sdk.version.found"), "No Java SDK Found");
+ }
+ return false;
+ }
+
+ final int choice = Messages
+ .showChooseDialog("Select Java SDK to be used as IDEA internal platform", "Select Internal Java Platform", ArrayUtil.toStringArray(javaSdks), javaSdks.get(0), Messages.getQuestionIcon());
+
+ if (choice != -1) {
+ final String name = javaSdks.get(choice);
+ final Sdk jdk = sdkModel.findSdk(name);
+ LOG.assertTrue(jdk != null);
+ setupSdkPaths(sdkModificator, sdk.getHomePath(), jdk);
+ sdkModificator.setSdkAdditionalData(new Sandbox(getDefaultSandbox(), jdk, sdk));
+ sdkModificator.setVersionString(jdk.getVersionString());
+ sdkModificator.commitChanges();
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean isValidInternalJdk(Sdk ideaSdk, Sdk sdk) {
+ final SdkTypeId sdkType = sdk.getSdkType();
+ if (sdkType instanceof JavaSdk) {
+ final JavaSdkVersion version = JavaSdk.getInstance().getVersion(sdk);
+ JavaSdkVersion requiredVersion = getRequiredJdkVersion(ideaSdk);
+ if (version != null && requiredVersion != null) {
+ return version.isAtLeast(requiredVersion);
+ }
+ }
+ return false;
+ }
+
+ private static int getIdeaClassFileVersion(final Sdk ideaSdk) {
+ int result = -1;
+ File apiJar = getOpenApiJar(ideaSdk.getHomePath());
+ if (apiJar == null) return -1;
+ final VirtualFile mainClassFile = JarFileSystem.getInstance().findFileByPath(FileUtil.toSystemIndependentName(apiJar.getPath()) +
+ "!/com/intellij/psi/PsiManager.class");
+ if (mainClassFile != null) {
+ final BytePointer ptr;
+ try {
+ ptr = new BytePointer(mainClassFile.contentsToByteArray(), 6);
+ result = ClsUtil.readU2(ptr);
+ }
+ catch (IOException e) {
+ // ignore
+ }
+ catch (ClsFormatException e) {
+ // ignore
+ }
+ }
+ return result;
+ }
+
+ @Nullable
+ private static JavaSdkVersion getRequiredJdkVersion(final Sdk ideaSdk) {
+ int classFileVersion = getIdeaClassFileVersion(ideaSdk);
+ switch(classFileVersion) {
+ case 48: return JavaSdkVersion.JDK_1_4;
+ case 49: return JavaSdkVersion.JDK_1_5;
+ case 50: return JavaSdkVersion.JDK_1_6;
+ case 51: return JavaSdkVersion.JDK_1_7;
+ }
+ return null;
+ }
+
+ public static void setupSdkPaths(final SdkModificator sdkModificator, final String sdkHome, final Sdk internalJava) {
+ //roots from internal jre
+ addClasses(sdkModificator, internalJava);
+ addDocs(sdkModificator, internalJava);
+ addSources(sdkModificator, internalJava);
+ //roots for openapi and other libs
+ if (!isFromIDEAProject(sdkHome)) {
+ final VirtualFile[] ideaLib = getIdeaLibrary(sdkHome);
+ if (ideaLib != null) {
+ for (VirtualFile aIdeaLib : ideaLib) {
+ sdkModificator.addRoot(aIdeaLib, OrderRootType.CLASSES);
+ }
+ }
+ addSources(new File(sdkHome), sdkModificator);
+ }
+ }
+
+ static String getDefaultSandbox() {
+ @NonNls String defaultSandbox = "";
+ try {
+ defaultSandbox = new File(PathManager.getSystemPath()).getCanonicalPath() + File.separator + "plugins-sandbox";
+ }
+ catch (IOException e) {
+ //can't be on running instance
+ }
+ return defaultSandbox;
+ }
+
+ private static void addSources(File file, SdkModificator sdkModificator) {
+ final File src = new File(new File(file, LIB_DIR_NAME), SRC_DIR_NAME);
+ if (!src.exists()) return;
+ File[] srcs = src.listFiles(new FileFilter() {
+ public boolean accept(File pathname) {
+ @NonNls final String path = pathname.getPath();
+ //noinspection SimplifiableIfStatement
+ if (path.contains("generics")) return false;
+ return path.endsWith(".jar") || path.endsWith(".zip");
+ }
+ });
+ for (int i = 0; srcs != null && i < srcs.length; i++) {
+ File jarFile = srcs[i];
+ if (jarFile.exists()) {
+ JarFileSystem jarFileSystem = JarFileSystem.getInstance();
+ String path = jarFile.getAbsolutePath().replace(File.separatorChar, '/') + JarFileSystem.JAR_SEPARATOR;
+ jarFileSystem.setNoCopyJarForPath(path);
+ VirtualFile vFile = jarFileSystem.findFileByPath(path);
+ sdkModificator.addRoot(vFile, OrderRootType.SOURCES);
+ }
+ }
+ }
+
+ private static void addClasses(SdkModificator sdkModificator, final Sdk javaSdk) {
+ addOrderEntries(OrderRootType.CLASSES, javaSdk, sdkModificator);
+ }
+
+ private static void addDocs(SdkModificator sdkModificator, final Sdk javaSdk) {
+ if (!addOrderEntries(JavadocOrderRootType.getInstance(), javaSdk, sdkModificator) &&
+ SystemInfo.isMac){
+ Sdk[] jdks = ProjectJdkTable.getInstance().getAllJdks();
+ for (Sdk jdk : jdks) {
+ if (jdk.getSdkType() instanceof JavaSdk) {
+ addOrderEntries(JavadocOrderRootType.getInstance(), jdk, sdkModificator);
+ break;
+ }
+ }
+ }
+ }
+
+ private static void addSources(SdkModificator sdkModificator, final Sdk javaSdk) {
+ if (javaSdk != null) {
+ if (!addOrderEntries(OrderRootType.SOURCES, javaSdk, sdkModificator)){
+ if (SystemInfo.isMac) {
+ Sdk[] jdks = ProjectJdkTable.getInstance().getAllJdks();
+ for (Sdk jdk : jdks) {
+ if (jdk.getSdkType() instanceof JavaSdk) {
+ addOrderEntries(OrderRootType.SOURCES, jdk, sdkModificator);
+ break;
+ }
+ }
+ }
+ else {
+ final File jdkHome = new File(javaSdk.getHomePath()).getParentFile();
+ @NonNls final String srcZip = "src.zip";
+ final File jarFile = new File(jdkHome, srcZip);
+ if (jarFile.exists()){
+ JarFileSystem jarFileSystem = JarFileSystem.getInstance();
+ String path = jarFile.getAbsolutePath().replace(File.separatorChar, '/') + JarFileSystem.JAR_SEPARATOR;
+ jarFileSystem.setNoCopyJarForPath(path);
+ sdkModificator.addRoot(jarFileSystem.findFileByPath(path), OrderRootType.SOURCES);
+ }
+ }
+ }
+ }
+ }
+
+ private static boolean addOrderEntries(OrderRootType orderRootType, Sdk sdk, SdkModificator toModificator){
+ boolean wasSmthAdded = false;
+ final String[] entries = sdk.getRootProvider().getUrls(orderRootType);
+ for (String entry : entries) {
+ VirtualFile virtualFile = VirtualFileManager.getInstance().findFileByUrl(entry);
+ if (virtualFile != null) {
+ toModificator.addRoot(virtualFile, orderRootType);
+ wasSmthAdded = true;
+ }
+ }
+ return wasSmthAdded;
+ }
+
+ public AdditionalDataConfigurable createAdditionalDataConfigurable(final SdkModel sdkModel, SdkModificator sdkModificator) {
+ return new IdeaJdkConfigurable(sdkModel, sdkModificator);
+ }
+
+ @Nullable
+ public String getBinPath(Sdk sdk) {
+ final Sdk internalJavaSdk = getInternalJavaSdk(sdk);
+ return internalJavaSdk == null ? null : JavaSdk.getInstance().getBinPath(internalJavaSdk);
+ }
+
+ @Nullable
+ public String getToolsPath(Sdk sdk) {
+ final Sdk jdk = getInternalJavaSdk(sdk);
+ if (jdk != null && jdk.getVersionString() != null){
+ return JavaSdk.getInstance().getToolsPath(jdk);
+ }
+ return null;
+ }
+
+ @Nullable
+ public String getVMExecutablePath(Sdk sdk) {
+ final Sdk internalJavaSdk = getInternalJavaSdk(sdk);
+ return internalJavaSdk == null ? null : JavaSdk.getInstance().getVMExecutablePath(internalJavaSdk);
+ }
+
+ public void saveAdditionalData(SdkAdditionalData additionalData, Element additional) {
+ if (additionalData instanceof Sandbox) {
+ try {
+ ((Sandbox)additionalData).writeExternal(additional);
+ }
+ catch (WriteExternalException e) {
+ LOG.error(e);
+ }
+ }
+ }
+
+ public SdkAdditionalData loadAdditionalData(Sdk sdk, Element additional) {
+ Sandbox sandbox = new Sandbox(sdk);
+ try {
+ sandbox.readExternal(additional);
+ }
+ catch (InvalidDataException e) {
+ LOG.error(e);
+ }
+ return sandbox;
+ }
+
+ public String getPresentableName() {
+ return DevKitBundle.message("sdk.title");
+ }
+
+ @Nullable
+ public static Sdk findIdeaJdk(@Nullable Sdk jdk) {
+ if (jdk == null) return null;
+ if (jdk.getSdkType() instanceof IdeaJdk) return jdk;
+ return null;
+ }
+
+ public static SdkType getInstance() {
+ return SdkType.findInstance(IdeaJdk.class);
+ }
+
+ @Override
+ public boolean isRootTypeApplicable(OrderRootType type) {
+ return type == OrderRootType.CLASSES ||
+ type == OrderRootType.SOURCES ||
+ type == JavadocOrderRootType.getInstance() ||
+ type == AnnotationOrderRootType.getInstance();
+ }
+
+ public String getDefaultDocumentationUrl(final @NotNull Sdk sdk) {
+ return JavaSdk.getInstance().getDefaultDocumentationUrl(sdk);
+ }
+}
diff --git a/plugins/devkit/src/projectRoots/IdeaJdkConfigurable.java b/plugins/devkit/src/projectRoots/IdeaJdkConfigurable.java
new file mode 100644
index 0000000..3eb4c5f
--- /dev/null
+++ b/plugins/devkit/src/projectRoots/IdeaJdkConfigurable.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.projectRoots;
+
+import com.intellij.ui.ListCellRendererWrapper;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.projectRoots.*;
+import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.DocumentAdapter;
+import com.intellij.ui.GuiUtils;
+import com.intellij.ui.TextFieldWithStoredHistory;
+import com.intellij.util.ArrayUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.idea.devkit.DevKitBundle;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+/**
+ * @author anna
+ * Date: Nov 22, 2004
+ */
+public class IdeaJdkConfigurable implements AdditionalDataConfigurable {
+ private final JLabel mySandboxHomeLabel = new JLabel(DevKitBundle.message("sandbox.home.label"));
+ private final TextFieldWithStoredHistory mySandboxHome = new TextFieldWithStoredHistory(SANDBOX_HISTORY);
+
+ private final JLabel myInternalJreLabel = new JLabel("Internal Java Platform:");
+ private final DefaultComboBoxModel myJdksModel = new DefaultComboBoxModel();
+ private final JComboBox myInternalJres = new JComboBox(myJdksModel);
+
+ private Sdk myIdeaJdk;
+
+ private boolean myModified;
+ @NonNls private static final String SANDBOX_HISTORY = "DEVKIT_SANDBOX_HISTORY";
+
+ private final SdkModel mySdkModel;
+ private final SdkModificator mySdkModificator;
+ private boolean myFreeze = false;
+ private final SdkModel.Listener myListener;
+
+ public IdeaJdkConfigurable(final SdkModel sdkModel, final SdkModificator sdkModificator) {
+ mySdkModel = sdkModel;
+ mySdkModificator = sdkModificator;
+ myListener = new SdkModel.Listener() {
+ public void sdkAdded(Sdk sdk) {
+ if (sdk.getSdkType().equals(JavaSdk.getInstance())) {
+ addJavaSdk(sdk);
+ }
+ }
+
+ public void beforeSdkRemove(Sdk sdk) {
+ if (sdk.getSdkType().equals(JavaSdk.getInstance())) {
+ removeJavaSdk(sdk);
+ }
+ }
+
+ public void sdkChanged(Sdk sdk, String previousName) {
+ if (sdk.getSdkType().equals(JavaSdk.getInstance())) {
+ updateJavaSdkList(sdk, previousName);
+ }
+ }
+
+ public void sdkHomeSelected(final Sdk sdk, final String newSdkHome) {
+ if (sdk.getSdkType() instanceof IdeaJdk) {
+ internalJdkUpdate(sdk);
+ }
+ }
+ };
+ mySdkModel.addListener(myListener);
+ }
+
+ private void updateJdkList() {
+ myJdksModel.removeAllElements();
+ for (Sdk sdk : mySdkModel.getSdks()) {
+ if (IdeaJdk.isValidInternalJdk(myIdeaJdk, sdk)) {
+ myJdksModel.addElement(sdk);
+ }
+ }
+ }
+
+ public void setSdk(Sdk sdk) {
+ myIdeaJdk = sdk;
+ }
+
+ public JComponent createComponent() {
+ mySandboxHome.setHistorySize(5);
+ JPanel wholePanel = new JPanel(new GridBagLayout());
+ wholePanel.add(mySandboxHomeLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 1.0, GridBagConstraints.WEST,
+ GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ wholePanel.add(GuiUtils.constructFieldWithBrowseButton(mySandboxHome, new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
+ descriptor.setTitle(DevKitBundle.message("sandbox.home"));
+ descriptor.setDescription(DevKitBundle.message("sandbox.purpose"));
+ VirtualFile file = FileChooser.chooseFile(descriptor, mySandboxHome, null, null);
+ if (file != null) {
+ mySandboxHome.setText(FileUtil.toSystemDependentName(file.getPath()));
+ }
+ myModified = true;
+ }
+ }), new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0, GridBagConstraints.EAST,
+ GridBagConstraints.HORIZONTAL, new Insets(0, 30, 0, 0), 0, 0));
+
+ wholePanel.add(myInternalJreLabel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0, 1, GridBagConstraints.WEST,
+ GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ wholePanel.add(myInternalJres, new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 1, 1, GridBagConstraints.EAST,
+ GridBagConstraints.HORIZONTAL, new Insets(0, 30, 0, 0), 0, 0));
+ myInternalJres.setRenderer(new ListCellRendererWrapper() {
+ @Override
+ public void customize(JList list, Object value, int index, boolean selected, boolean hasFocus) {
+ if (value instanceof Sdk) {
+ setText(((Sdk)value).getName());
+ }
+ }
+ });
+
+ myInternalJres.addItemListener(new ItemListener() {
+ public void itemStateChanged(final ItemEvent e) {
+ if (myFreeze) return;
+ final Sdk javaJdk = (Sdk)e.getItem();
+ for (OrderRootType type : OrderRootType.getAllTypes()) {
+ if (!((SdkType) javaJdk.getSdkType()).isRootTypeApplicable(type)) {
+ continue;
+ }
+ final VirtualFile[] internalRoots = javaJdk.getSdkModificator().getRoots(type);
+ final VirtualFile[] configuredRoots = mySdkModificator.getRoots(type);
+ for (VirtualFile file : internalRoots) {
+ if (e.getStateChange() == ItemEvent.DESELECTED) {
+ mySdkModificator.removeRoot(file, type);
+ } else {
+ if (ArrayUtil.find(configuredRoots, file) == -1) {
+ mySdkModificator.addRoot(file, type);
+ }
+ }
+ }
+ }
+ }
+ });
+
+ mySandboxHome.addDocumentListener(new DocumentAdapter() {
+ protected void textChanged(DocumentEvent e) {
+ myModified = true;
+ }
+ });
+ mySandboxHome.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ myModified = true;
+ }
+ });
+ mySandboxHome.setText("");
+ myModified = true;
+ return wholePanel;
+ }
+
+ private void internalJdkUpdate(final Sdk sdk) {
+ final Sdk javaSdk = ((Sandbox)sdk.getSdkAdditionalData()).getJavaSdk();
+ if (myJdksModel.getIndexOf(javaSdk) == -1) {
+ myJdksModel.addElement(javaSdk);
+ } else {
+ myJdksModel.setSelectedItem(javaSdk);
+ }
+ }
+
+ public boolean isModified() {
+ return myModified;
+ }
+
+ public void apply() throws ConfigurationException {
+ /*if (mySandboxHome.getText() == null || mySandboxHome.getText().length() == 0) {
+ throw new ConfigurationException(DevKitBundle.message("sandbox.specification"));
+ }*/
+ mySandboxHome.addCurrentTextToHistory();
+ final Sandbox additionalData = (Sandbox)myIdeaJdk.getSdkAdditionalData();
+ if (additionalData != null) {
+ additionalData.cleanupWatchedRoots();
+ }
+ Sandbox sandbox = new Sandbox(mySandboxHome.getText(), (Sdk)myInternalJres.getSelectedItem(), myIdeaJdk);
+ final SdkModificator modificator = myIdeaJdk.getSdkModificator();
+ modificator.setSdkAdditionalData(sandbox);
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ public void run() {
+ modificator.commitChanges();
+ }
+ });
+ ((ProjectJdkImpl) myIdeaJdk).resetVersionString();
+ myModified = false;
+ }
+
+ public void reset() {
+ myFreeze = true;
+ updateJdkList();
+ myFreeze = false;
+ mySandboxHome.reset();
+ if (myIdeaJdk != null && myIdeaJdk.getSdkAdditionalData() instanceof Sandbox) {
+ final Sandbox sandbox = (Sandbox)myIdeaJdk.getSdkAdditionalData();
+ final String sandboxHome = sandbox.getSandboxHome();
+ mySandboxHome.setText(sandboxHome);
+ mySandboxHome.setSelectedItem(sandboxHome);
+ final Sdk internalJava = sandbox.getJavaSdk();
+ if (internalJava != null) {
+ for (int i = 0; i < myJdksModel.getSize(); i++) {
+ if (Comparing.strEqual(((Sdk)myJdksModel.getElementAt(i)).getName(), internalJava.getName())){
+ myInternalJres.setSelectedIndex(i);
+ break;
+ }
+ }
+ }
+ myModified = false;
+ } else {
+ mySandboxHome.setText(IdeaJdk.getDefaultSandbox());
+ }
+ }
+
+ public void disposeUIResources() {
+ mySdkModel.removeListener(myListener);
+ }
+
+ private void addJavaSdk(final Sdk sdk) {
+ myJdksModel.addElement(sdk);
+ }
+
+ private void removeJavaSdk(final Sdk sdk) {
+ myJdksModel.removeElement(sdk);
+ }
+
+ private void updateJavaSdkList(Sdk sdk, String previousName) {
+ final Sdk[] sdks = mySdkModel.getSdks();
+ for (Sdk currentSdk : sdks) {
+ if (currentSdk.getSdkType() instanceof IdeaJdk){
+ final Sandbox sandbox = (Sandbox)currentSdk.getSdkAdditionalData();
+ final Sdk internalJava = sandbox.getJavaSdk();
+ if (internalJava != null && Comparing.equal(internalJava.getName(), previousName)){
+ sandbox.setJavaSdk(sdk);
+ }
+ }
+ }
+ updateJdkList();
+ }
+}
diff --git a/plugins/devkit/src/projectRoots/Sandbox.java b/plugins/devkit/src/projectRoots/Sandbox.java
new file mode 100644
index 0000000..f148c82
--- /dev/null
+++ b/plugins/devkit/src/projectRoots/Sandbox.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.projectRoots;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.projectRoots.*;
+import com.intellij.openapi.util.DefaultJDOMExternalizer;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.JDOMExternalizable;
+import com.intellij.openapi.util.WriteExternalException;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.DevKitBundle;
+
+/**
+ * User: anna
+ * Date: Nov 22, 2004
+ */
+public class Sandbox implements ValidatableSdkAdditionalData, JDOMExternalizable{
+ private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.devkit.projectRoots.Sandbox");
+
+ @SuppressWarnings({"WeakerAccess"})
+ public String mySandboxHome;
+ private final Sdk myCurrentJdk;
+
+ private String myJavaSdkName;
+ private Sdk myJavaSdk;
+
+ private LocalFileSystem.WatchRequest mySandboxRoot = null;
+ @NonNls private static final String SDK = "sdk";
+
+ public Sandbox(String sandboxHome, Sdk javaSdk, Sdk currentJdk) {
+ mySandboxHome = sandboxHome;
+ myCurrentJdk = currentJdk;
+ if (mySandboxHome != null) {
+ mySandboxRoot = LocalFileSystem.getInstance().addRootToWatch(mySandboxHome, true);
+ }
+ myJavaSdk = javaSdk;
+ }
+
+ //readExternal()
+ public Sandbox(Sdk currentSdk) {
+ myCurrentJdk = currentSdk;
+ }
+
+ public String getSandboxHome() {
+ return mySandboxHome;
+ }
+
+ public Object clone() throws CloneNotSupportedException {
+ return new Sandbox(mySandboxHome, getJavaSdk(), myCurrentJdk);
+ }
+
+ public void checkValid(SdkModel sdkModel) throws ConfigurationException {
+ if (mySandboxHome == null || mySandboxHome.length() == 0 || getJavaSdk() == null){
+ throw new ConfigurationException(DevKitBundle.message("sandbox.specification"));
+ }
+ }
+
+ public void readExternal(Element element) throws InvalidDataException {
+ DefaultJDOMExternalizer.readExternal(this, element);
+ LOG.assertTrue(mySandboxRoot == null);
+ myJavaSdkName = element.getAttributeValue(SDK);
+ if (mySandboxHome != null) {
+ mySandboxRoot = LocalFileSystem.getInstance().addRootToWatch(mySandboxHome, true);
+ }
+ }
+
+ public void writeExternal(Element element) throws WriteExternalException {
+ DefaultJDOMExternalizer.writeExternal(this, element);
+ final Sdk sdk = getJavaSdk();
+ if (sdk != null) {
+ element.setAttribute(SDK, sdk.getName());
+ }
+ }
+
+ void cleanupWatchedRoots() {
+ if (mySandboxRoot != null) {
+ LocalFileSystem.getInstance().removeWatchedRoot(mySandboxRoot);
+ }
+ }
+
+ @Nullable
+ public Sdk getJavaSdk() {
+ final ProjectJdkTable jdkTable = ProjectJdkTable.getInstance();
+ if (myJavaSdk == null) {
+ if (myJavaSdkName != null) {
+ myJavaSdk = jdkTable.findJdk(myJavaSdkName);
+ myJavaSdkName = null;
+ }
+ else {
+ for (Sdk jdk : jdkTable.getAllJdks()) {
+ if (IdeaJdk.isValidInternalJdk(myCurrentJdk, jdk)) {
+ myJavaSdk = jdk;
+ break;
+ }
+ }
+ }
+ }
+ return myJavaSdk;
+ }
+
+ public void setJavaSdk(final Sdk javaSdk) {
+ myJavaSdk = javaSdk;
+ }
+}
diff --git a/plugins/devkit/src/references/IconsReferencesContributor.java b/plugins/devkit/src/references/IconsReferencesContributor.java
new file mode 100644
index 0000000..7aebbd4
--- /dev/null
+++ b/plugins/devkit/src/references/IconsReferencesContributor.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.references;
+
+import com.intellij.find.FindModel;
+import com.intellij.find.impl.FindInProjectUtil;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.module.ModuleUtilCore;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.IconLoader;
+import com.intellij.openapi.util.ProperTextRange;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.patterns.*;
+import com.intellij.psi.*;
+import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReference;
+import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet;
+import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceUtil;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.search.searches.ReferencesSearch;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.psi.xml.XmlAttribute;
+import com.intellij.psi.xml.XmlAttributeValue;
+import com.intellij.usageView.UsageInfo;
+import com.intellij.util.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static com.intellij.patterns.PsiJavaPatterns.*;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class IconsReferencesContributor extends PsiReferenceContributor implements QueryExecutor<PsiReference, ReferencesSearch.SearchParameters> {
+ @Override
+ public void registerReferenceProviders(PsiReferenceRegistrar registrar) {
+ final StringPattern methodName = string().oneOf("findIcon", "getIcon");
+ final PsiMethodPattern method = psiMethod().withName(methodName).definedInClass(IconLoader.class.getName());
+ final PsiJavaElementPattern.Capture<PsiLiteralExpression> javaFile
+ = literalExpression().and(psiExpression().methodCallParameter(0, method));
+
+ final PsiJavaElementPattern.Capture<PsiLiteralExpression> annotationValue
+ = literalExpression().annotationParam("com.intellij.ide.presentation.Presentation", "icon");
+
+ final XmlAttributeValuePattern pluginXml = XmlPatterns.xmlAttributeValue().withLocalName("icon");
+
+ registrar.registerReferenceProvider(annotationValue, new PsiReferenceProvider() {
+ @NotNull
+ @Override
+ public PsiReference[] getReferencesByElement(@NotNull final PsiElement element, @NotNull ProcessingContext context) {
+ if (!PlatformUtils.isIdeaProject(element.getProject())) return PsiReference.EMPTY_ARRAY;
+ return new PsiReference[] {
+ new PsiReferenceBase<PsiElement>(element, true) {
+ @Override
+ public PsiElement resolve() {
+ String value = (String)((PsiLiteralExpression)element).getValue();
+ if (value != null) {
+ List<String> path = StringUtil.split(value, ".");
+ if (path.size() > 1 && path.get(0).endsWith("Icons")) {
+ Project project = element.getProject();
+ PsiClass cur = JavaPsiFacade.getInstance(project).findClass(fqnIconsClass(path.get(0)),
+ GlobalSearchScope.projectScope(project));
+ if (cur == null) {
+ return null;
+ }
+
+ for (int i = 1; i < path.size() - 1; i++) {
+ cur = cur.findInnerClassByName(path.get(i), false);
+ if (cur == null) {
+ return null;
+ }
+ }
+
+ return cur.findFieldByName(path.get(path.size() - 1), false);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
+ PsiElement field = resolve();
+ if (field instanceof PsiField) {
+ String fqn = ((PsiField)field).getContainingClass().getQualifiedName();
+
+ if (fqn.startsWith("com.intellij.icons.")) {
+ return replace(newElementName, fqn, "com.intellij.icons.", element);
+ }
+ else if (fqn.startsWith("icons.")) {
+ return replace(newElementName, fqn, "icons.", element);
+ }
+ }
+
+ return super.handleElementRename(newElementName);
+ }
+
+ @Override
+ public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
+ if (element instanceof PsiField) {
+ String fqn = ((PsiField)element).getContainingClass().getQualifiedName();
+
+ String newElementName = ((PsiField)element).getName();
+ if (fqn.startsWith("com.intellij.icons.")) {
+ return replace(newElementName, fqn, "com.intellij.icons.", getElement());
+ }
+ else if (fqn.startsWith("icons.")) {
+ return replace(newElementName, fqn, "icons.", getElement());
+ }
+ }
+
+ return super.bindToElement(element);
+ }
+
+ private PsiElement replace(String newElementName, String fqn, String pckg, PsiElement container) {
+ String newValue = "\"" + fqn.substring(pckg.length()) + "." + newElementName + "\"";
+ return getElement().replace(
+ JavaPsiFacade.getElementFactory(container.getProject()).createExpressionFromText(newValue, container.getParent()));
+ }
+
+ @NotNull
+ @Override
+ public Object[] getVariants() {
+ return EMPTY_ARRAY;
+ }
+ }
+ };
+ }
+ });
+
+ registrar.registerReferenceProvider(javaFile, new PsiReferenceProvider() {
+ @NotNull
+ @Override
+ public PsiReference[] getReferencesByElement(@NotNull final PsiElement element, @NotNull ProcessingContext context) {
+ if (!PlatformUtils.isIdeaProject(element.getProject())) return PsiReference.EMPTY_ARRAY;
+ return new FileReferenceSet(element) {
+ @Override
+ protected Collection<PsiFileSystemItem> getExtraContexts() {
+ final Module icons = ModuleManager.getInstance(element.getProject()).findModuleByName("icons");
+ if (icons != null) {
+ final ArrayList<PsiFileSystemItem> result = new ArrayList<PsiFileSystemItem>();
+ final VirtualFile[] roots = ModuleRootManager.getInstance(icons).getSourceRoots();
+ final PsiManager psiManager = element.getManager();
+ for (VirtualFile root : roots) {
+ final PsiDirectory directory = psiManager.findDirectory(root);
+ if (directory != null) {
+ result.add(directory);
+ }
+ }
+ return result;
+ }
+ return super.getExtraContexts();
+ }
+ }.getAllReferences();
+ }
+ });
+
+ registrar.registerReferenceProvider(pluginXml, new PsiReferenceProvider() {
+ @NotNull
+ @Override
+ public PsiReference[] getReferencesByElement(@NotNull final PsiElement element, @NotNull ProcessingContext context) {
+ return new PsiReference[] {
+ new PsiReferenceBase<PsiElement>(element, true) {
+ @Override
+ public PsiElement resolve() {
+ String value = ((XmlAttributeValue)element).getValue();
+ if (value.startsWith("/")) {
+ FileReference lastRef = new FileReferenceSet(element).getLastReference();
+ return lastRef != null ? lastRef.resolve() : null;
+ }
+ else {
+ List<String> path = StringUtil.split(value, ".");
+ if (path.size() > 1 && path.get(0).endsWith("Icons")) {
+ Project project = element.getProject();
+ PsiClass cur = JavaPsiFacade.getInstance(project).findClass(fqnIconsClass(path.get(0)),
+ GlobalSearchScope.projectScope(project));
+ if (cur == null) return null;
+
+ for (int i = 1; i < path.size() - 1; i++) {
+ cur = cur.findInnerClassByName(path.get(i), false);
+ if (cur == null) return null;
+ }
+
+ return cur.findFieldByName(path.get(path.size() - 1), false);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
+ PsiElement element = resolve();
+ if (element instanceof PsiFile) {
+ FileReference lastRef = new FileReferenceSet(element).getLastReference();
+ return lastRef.handleElementRename(newElementName);
+ }
+ else if (element instanceof PsiField) {
+ String fqn = ((PsiField)element).getContainingClass().getQualifiedName();
+
+ if (fqn.startsWith("com.intellij.icons.")) {
+ return replace(fqn, newElementName, "com.intellij.icons.");
+ }
+ else if (fqn.startsWith("icons.")) {
+ return replace(fqn, newElementName, "icons.");
+ }
+ }
+
+ return super.handleElementRename(newElementName);
+ }
+
+ @Override
+ public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
+ if (element instanceof PsiFile) {
+ FileReference lastRef = new FileReferenceSet(element).getLastReference();
+ return lastRef.bindToElement(element);
+ }
+ else if (element instanceof PsiField) {
+ String fqn = ((PsiField)element).getContainingClass().getQualifiedName();
+
+ String newName = ((PsiField)element).getName();
+ if (fqn.startsWith("com.intellij.icons.")) {
+ return replace(fqn, newName, "com.intellij.icons.");
+ }
+ else if (fqn.startsWith("icons.")) {
+ return replace(fqn, newName, "icons.");
+ }
+ }
+
+ return super.bindToElement(element);
+ }
+
+ private PsiElement replace(String fqn, String newName, String pckg) {
+ XmlAttribute parent = (XmlAttribute)getElement().getParent();
+ parent.setValue(fqn.substring(pckg.length()) + "." + newName);
+ return parent.getValueElement();
+ }
+
+ @NotNull
+ @Override
+ public Object[] getVariants() {
+ return EMPTY_ARRAY;
+ }
+ }
+ };
+ }
+ });
+ }
+
+ private static String fqnIconsClass(String className) {
+ return "AllIcons".equals(className) ? "com.intellij.icons.AllIcons" : "icons." + className;
+ }
+
+ @Override
+ public boolean execute(@NotNull ReferencesSearch.SearchParameters queryParameters, @NotNull final Processor<PsiReference> consumer) {
+ final PsiElement file = queryParameters.getElementToSearch();
+ if (file instanceof PsiBinaryFile) {
+ final Module module = ApplicationManager.getApplication().runReadAction(new Computable<Module>() {
+ @Override
+ public Module compute() {
+ return ModuleUtilCore.findModuleForPsiElement(file);
+ }
+ });
+
+ final VirtualFile image = ((PsiBinaryFile)file).getVirtualFile();
+ if (isImage(image) && isIconsModule(module)) {
+ final Project project = file.getProject();
+ final FindModel model = new FindModel();
+ final String path = getPathToImage(image, module);
+ if (path == null) return true;
+ model.setStringToFind(path);
+ model.setCaseSensitive(true);
+ model.setFindAll(true);
+ model.setWholeWordsOnly(true);
+ FindInProjectUtil.findUsages(model, FindInProjectUtil.getPsiDirectory(model, project), project, false, new Processor<UsageInfo>() {
+ @Override
+ public boolean process(final UsageInfo usage) {
+ ApplicationManager.getApplication().runReadAction(new Runnable() {
+ public void run() {
+ final PsiElement element = usage.getElement();
+
+ final ProperTextRange textRange = usage.getRangeInElement();
+ if (element != null && textRange != null) {
+ final PsiElement start = element.findElementAt(textRange.getStartOffset());
+ final PsiElement end = element.findElementAt(textRange.getEndOffset());
+ if (start != null && end != null) {
+ PsiElement value = PsiTreeUtil.findCommonParent(start, end);
+ if (value instanceof PsiJavaToken) {
+ value = value.getParent();
+ }
+ if (value != null) {
+ final FileReference reference = FileReferenceUtil.findFileReference(value);
+ if (reference != null) {
+ consumer.process(reference);
+ }
+ }
+ }
+ }
+ }
+ });
+ return true;
+ }
+ });
+ }
+ }
+ return true;
+ }
+
+ @Nullable
+ private static String getPathToImage(VirtualFile image, Module module) {
+ final String path = ModuleRootManager.getInstance(module).getSourceRoots()[0].getPath();
+ return "/" + FileUtil.getRelativePath(path, image.getPath(), '/');
+ }
+
+ private static boolean isIconsModule(Module module) {
+ return module != null && "icons".equals(module.getName())
+ && ModuleRootManager.getInstance(module).getSourceRoots().length == 1;
+ }
+
+ private static boolean isImage(VirtualFile image) {
+ final FileTypeManager mgr = FileTypeManager.getInstance();
+ return image != null && mgr.getFileTypeByFile(image) == mgr.getFileTypeByExtension("png");
+ }
+}
diff --git a/plugins/devkit/src/references/extensions/ExtensionPointQuickDocProvider.java b/plugins/devkit/src/references/extensions/ExtensionPointQuickDocProvider.java
new file mode 100644
index 0000000..9e5ce03
--- /dev/null
+++ b/plugins/devkit/src/references/extensions/ExtensionPointQuickDocProvider.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.references.extensions;
+
+import com.intellij.lang.documentation.DocumentationProvider;
+import com.intellij.lang.java.JavaDocumentationProvider;
+import com.intellij.lang.xml.XMLLanguage;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.xml.XmlAttribute;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.psi.xml.XmlToken;
+import com.intellij.psi.xml.XmlTokenType;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.inspections.DevKitInspectionBase;
+
+import java.util.List;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class ExtensionPointQuickDocProvider implements DocumentationProvider {
+ @Override
+ public String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) {
+ return null;
+ }
+
+ @Override
+ public List<String> getUrlFor(PsiElement element, PsiElement originalElement) {
+ return null;
+ }
+
+ @Override
+ public String generateDoc(PsiElement element, @Nullable PsiElement originalElement) {
+ if (originalElement == null) return null;
+ if (originalElement.getLanguage() == XMLLanguage.INSTANCE || DevKitInspectionBase.isPluginXml(originalElement.getContainingFile())) {
+ final PsiElement context = element.getContext();
+ String fqn = null;
+ if (originalElement instanceof XmlToken && ((XmlToken)originalElement).getTokenType() == XmlTokenType.XML_NAME) {
+ PsiElement attr;
+ PsiElement tag;
+ if (context != null
+ && (attr = context.getParent()) instanceof XmlAttribute
+ && (tag = attr.getParent()) instanceof XmlTag) {
+ final String interfaceFqn = ((XmlTag)tag).getAttributeValue("interface");
+ final String beanClassFqn = ((XmlTag)tag).getAttributeValue("beanClass");
+ fqn = interfaceFqn == null ? beanClassFqn : interfaceFqn;
+ }
+ }
+
+ if (fqn != null) {
+ final Project project = element.getProject();
+ final PsiClass psiClass = JavaPsiFacade.getInstance(project).findClass(fqn, GlobalSearchScope.allScope(project));
+ if (psiClass != null) {
+ return new JavaDocumentationProvider().generateExternalJavadoc(psiClass);
+ }
+ }
+
+ }
+ return null;
+ }
+
+ @Override
+ public PsiElement getDocumentationElementForLookupItem(PsiManager psiManager, Object object, PsiElement element) {
+ return null;
+ }
+
+ @Override
+ public PsiElement getDocumentationElementForLink(PsiManager psiManager, String link, PsiElement context) {
+ return null;
+ }
+}
diff --git a/plugins/devkit/src/run/IdeaLicenseHelper.java b/plugins/devkit/src/run/IdeaLicenseHelper.java
new file mode 100644
index 0000000..ca8e311
--- /dev/null
+++ b/plugins/devkit/src/run/IdeaLicenseHelper.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.run;
+
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.util.io.FileUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * User: anna
+ * Date: Dec 3, 2004
+ */
+public class IdeaLicenseHelper {
+ @NonNls private static final String LICENSE_PATH_PREFERRED = "idea80.key";
+ @NonNls private static final String LICENSE_PATH_70 = "idea70.key";
+ @NonNls private static final String LICENSE_PATH_60 = "idea60.key";
+ @NonNls private static final String LICENSE_PATH_50 = "idea50.key";
+ @NonNls private static final String LICENSE_PATH_40 = "idea40.key";
+ @NonNls private static final String LICENSE_PATH_SYSTEM = "idea.license";
+
+ @NonNls private static final String CONFIG_DIR_NAME = "config";
+
+ private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.devkit.run.IdeaLicenseHelper");
+
+ @Nullable
+ public static File isIDEALicenseInSandbox(@NonNls final String configPath, @NonNls final String systemPath, @NonNls final String binPath){
+ final File config = new File(configPath, LICENSE_PATH_PREFERRED);
+ if (config.exists()){
+ return config;
+ }
+ final File idea70 = new File(configPath, LICENSE_PATH_70);
+ if (idea70.exists()){
+ return idea70;
+ }
+ final File idea60 = new File(configPath, LICENSE_PATH_60);
+ if (idea60.exists()){
+ return idea60;
+ }
+ final File idea5 = new File(configPath, LICENSE_PATH_50);
+ if (idea5.exists()){
+ return idea5;
+ }
+ final File idea4 = new File(configPath, LICENSE_PATH_40);
+ if (idea4.exists()){
+ return idea4;
+ }
+ final File system = new File(systemPath, LICENSE_PATH_SYSTEM);
+ if (system.exists()){
+ return system;
+ }
+ final File bin = new File(binPath, LICENSE_PATH_SYSTEM);
+ if (bin.exists()){
+ return bin;
+ }
+ return null;
+ }
+
+ public static void copyIDEALicense(final String sandboxHome, Sdk jdk){
+ if (isIDEALicenseInSandbox(sandboxHome + File.separator + CONFIG_DIR_NAME, sandboxHome + File.separator + "system", jdk.getHomePath() + File.separator + "bin") == null){
+ final File ideaLicense = isIDEALicenseInSandbox(PathManager.getConfigPath(), PathManager.getSystemPath(), PathManager.getBinPath());
+ if (ideaLicense != null){
+ try {
+ FileUtil.copy(ideaLicense, new File(new File(sandboxHome, CONFIG_DIR_NAME), LICENSE_PATH_PREFERRED));
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/devkit/src/run/JUnitDevKitPatcher.java b/plugins/devkit/src/run/JUnitDevKitPatcher.java
new file mode 100644
index 0000000..cc274f0
--- /dev/null
+++ b/plugins/devkit/src/run/JUnitDevKitPatcher.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.run;
+
+import com.intellij.execution.JUnitPatcher;
+import com.intellij.execution.configurations.JavaParameters;
+import com.intellij.execution.configurations.ParametersList;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.projectRoots.JavaSdkType;
+import com.intellij.openapi.projectRoots.Sdk;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.module.PluginModuleType;
+import org.jetbrains.idea.devkit.projectRoots.IdeaJdk;
+import org.jetbrains.idea.devkit.projectRoots.Sandbox;
+import org.jetbrains.idea.devkit.util.DescriptorUtil;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * User: anna
+ * Date: Mar 4, 2005
+ */
+public class JUnitDevKitPatcher extends JUnitPatcher{
+
+ public void patchJavaParameters(@Nullable Module module, JavaParameters javaParameters) {
+ Sdk jdk = javaParameters.getJdk();
+ jdk = IdeaJdk.findIdeaJdk(jdk);
+ if (jdk == null) return;
+
+ @NonNls String libPath = jdk.getHomePath() + File.separator + "lib";
+
+ final ParametersList vm = javaParameters.getVMParametersList();
+ vm.add("-Xbootclasspath/a:" + libPath + File.separator + "boot.jar");
+ if (!vm.hasProperty("idea.load.plugins.id") && module != null && PluginModuleType.isOfType(module)) {
+ final String id = DescriptorUtil.getPluginId(module);
+ if (id != null) {
+ vm.defineProperty("idea.load.plugins.id", id);
+ }
+ }
+
+ final String sandboxHome = getSandboxPath(jdk);
+ if (sandboxHome != null) {
+ if (!vm.hasProperty("idea.home.path")) {
+ vm.defineProperty("idea.home.path", sandboxHome + File.separator + "test");
+ }
+ if (!vm.hasProperty("idea.plugins.path")) {
+ vm.defineProperty("idea.plugins.path", sandboxHome + File.separator + "plugins");
+ }
+ }
+
+ javaParameters.getClassPath().addFirst(libPath + File.separator + "idea.jar");
+ javaParameters.getClassPath().addFirst(libPath + File.separator + "resources.jar");
+ javaParameters.getClassPath().addFirst(((JavaSdkType)jdk.getSdkType()).getToolsPath(jdk));
+ }
+
+ @Nullable
+ private static String getSandboxPath(final Sdk jdk) {
+ String sandboxHome = ((Sandbox)jdk.getSdkAdditionalData()).getSandboxHome();
+ if (sandboxHome != null) {
+ try {
+ sandboxHome = new File(sandboxHome).getCanonicalPath();
+ }
+ catch (IOException e) {
+ sandboxHome = new File(sandboxHome).getAbsolutePath();
+ }
+ }
+ return sandboxHome;
+ }
+}
diff --git a/plugins/devkit/src/run/PluginConfigurationType.java b/plugins/devkit/src/run/PluginConfigurationType.java
new file mode 100644
index 0000000..569c5cc
--- /dev/null
+++ b/plugins/devkit/src/run/PluginConfigurationType.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.run;
+
+import com.intellij.diagnostic.VMOptions;
+import com.intellij.execution.configurations.ConfigurationFactory;
+import com.intellij.execution.configurations.ConfigurationType;
+import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.io.FileUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.module.PluginModuleType;
+
+import javax.swing.*;
+import java.io.File;
+import java.io.IOException;
+
+public class PluginConfigurationType implements ConfigurationType {
+ private final ConfigurationFactory myFactory;
+ private String myVmParameters;
+
+ PluginConfigurationType() {
+ myFactory = new ConfigurationFactory(this) {
+ public RunConfiguration createTemplateConfiguration(Project project) {
+ final PluginRunConfiguration runConfiguration = new PluginRunConfiguration(project, this, "");
+ if (runConfiguration.VM_PARAMETERS == null) {
+ runConfiguration.VM_PARAMETERS = getVmParameters();
+ } else {
+ runConfiguration.VM_PARAMETERS += getVmParameters();
+ }
+ return runConfiguration;
+ }
+
+ public RunConfiguration createConfiguration(String name, RunConfiguration template) {
+ final PluginRunConfiguration pluginRunConfiguration = (PluginRunConfiguration)template;
+ if (pluginRunConfiguration.getModule() == null) {
+ final Module[] modules = PluginModuleType.getAllPluginModules(pluginRunConfiguration.getProject());
+ if (modules.length > 0){
+ pluginRunConfiguration.setModule(modules[0]);
+ }
+ }
+ return super.createConfiguration(name, pluginRunConfiguration);
+ }
+ };
+ }
+
+ public String getDisplayName() {
+ return DevKitBundle.message("run.configuration.title");
+ }
+
+ public String getConfigurationTypeDescription() {
+ return DevKitBundle.message("run.configuration.type.description");
+ }
+
+ public Icon getIcon() {
+ return AllIcons.Nodes.Plugin;
+ }
+
+ public ConfigurationFactory[] getConfigurationFactories() {
+ return new ConfigurationFactory[] {myFactory};
+ }
+
+ @NotNull
+ public String getId() {
+ return "#org.jetbrains.idea.devkit.run.PluginConfigurationType";
+ }
+
+ @NotNull
+ private String getVmParameters() {
+ if (myVmParameters == null) {
+ String vmOptions;
+ try {
+ vmOptions = FileUtil.loadFile(new File(PathManager.getBinPath(), "idea.plugins.vmoptions")).replaceAll("\\s+", " ");
+ }
+ catch (IOException e) {
+ vmOptions = VMOptions.read();
+ }
+ myVmParameters = vmOptions != null ? vmOptions.trim() : "";
+ }
+
+ return myVmParameters;
+ }
+}
diff --git a/plugins/devkit/src/run/PluginRunConfiguration.java b/plugins/devkit/src/run/PluginRunConfiguration.java
new file mode 100644
index 0000000..597eb02
--- /dev/null
+++ b/plugins/devkit/src/run/PluginRunConfiguration.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.run;
+
+import com.intellij.execution.CantRunException;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.*;
+import com.intellij.execution.filters.TextConsoleBuilderFactory;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.execution.runners.ProgramRunner;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.options.SettingsEditor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.JavaSdkType;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkModificator;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.util.*;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.projectRoots.IdeaJdk;
+import org.jetbrains.idea.devkit.projectRoots.Sandbox;
+
+import java.io.File;
+import java.io.IOException;
+
+public class PluginRunConfiguration extends RunConfigurationBase implements ModuleRunConfiguration {
+ private Module myModule;
+ private String myModuleName;
+
+ public String VM_PARAMETERS;
+ public String PROGRAM_PARAMETERS;
+ @NonNls private static final String NAME = "name";
+ @NonNls private static final String MODULE = "module";
+ @NonNls private static final String ALTERNATIVE_PATH_ELEMENT = "alternative-path";
+ @NonNls private static final String PATH = "path";
+ @NonNls private static final String ALTERNATIVE_PATH_ENABLED_ATTR = "alternative-path-enabled";
+ private String ALTERNATIVE_JRE_PATH = null;
+ private boolean ALTERNATIVE_JRE_PATH_ENABLED = false;
+
+ public PluginRunConfiguration(final Project project, final ConfigurationFactory factory, final String name) {
+ super(project, factory, name);
+ }
+
+ public SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
+ return new PluginRunConfigurationEditor(this);
+ }
+
+ public JDOMExternalizable createRunnerSettings(ConfigurationInfoProvider provider) {
+ return null;
+ }
+
+ public SettingsEditor<JDOMExternalizable> getRunnerSettingsEditor(ProgramRunner runner) {
+ return null;
+ }
+
+ public RunProfileState getState(@NotNull final Executor executor, @NotNull final ExecutionEnvironment env) throws ExecutionException {
+ if (getModule() == null){
+ throw new ExecutionException(DevKitBundle.message("run.configuration.no.module.specified"));
+ }
+ final ModuleRootManager rootManager = ModuleRootManager.getInstance(getModule());
+ final Sdk jdk = rootManager.getSdk();
+ if (jdk == null) {
+ throw CantRunException.noJdkForModule(getModule());
+ }
+
+ final Sdk ideaJdk = IdeaJdk.findIdeaJdk(jdk);
+ if (ideaJdk == null) {
+ throw new ExecutionException(DevKitBundle.message("jdk.type.incorrect.common"));
+ }
+ String sandboxHome = ((Sandbox)ideaJdk.getSdkAdditionalData()).getSandboxHome();
+
+ if (sandboxHome == null){
+ throw new ExecutionException(DevKitBundle.message("sandbox.no.configured"));
+ }
+
+ try {
+ sandboxHome = new File(sandboxHome).getCanonicalPath();
+ }
+ catch (IOException e) {
+ throw new ExecutionException(DevKitBundle.message("sandbox.no.configured"));
+ }
+ final String canonicalSandbox = sandboxHome;
+
+ //copy license from running instance of idea
+ IdeaLicenseHelper.copyIDEALicense(sandboxHome, ideaJdk);
+
+ final JavaCommandLineState state = new JavaCommandLineState(env) {
+ protected JavaParameters createJavaParameters() throws ExecutionException {
+
+ final JavaParameters params = new JavaParameters();
+
+ ParametersList vm = params.getVMParametersList();
+
+ fillParameterList(vm, VM_PARAMETERS);
+ fillParameterList(params.getProgramParametersList(), PROGRAM_PARAMETERS);
+ Sdk usedIdeaJdk = ideaJdk;
+ if (isAlternativeJreEnabled() && !StringUtil.isEmptyOrSpaces(getAlternativeJrePath())) {
+ try {
+ usedIdeaJdk = (Sdk)usedIdeaJdk.clone();
+ }
+ catch (CloneNotSupportedException e) {
+ throw new ExecutionException(e.getMessage());
+ }
+ final SdkModificator sdkToSetUp = usedIdeaJdk.getSdkModificator();
+ sdkToSetUp.setHomePath(getAlternativeJrePath());
+ sdkToSetUp.commitChanges();
+ }
+ @NonNls String libPath = usedIdeaJdk.getHomePath() + File.separator + "lib";
+ vm.add("-Xbootclasspath/a:" + libPath + File.separator + "boot.jar");
+
+ vm.defineProperty("idea.config.path", canonicalSandbox + File.separator + "config");
+ vm.defineProperty("idea.system.path", canonicalSandbox + File.separator + "system");
+ vm.defineProperty("idea.plugins.path", canonicalSandbox + File.separator + "plugins");
+
+ if (SystemInfo.isMac) {
+ vm.defineProperty("idea.smooth.progress", "false");
+ vm.defineProperty("apple.laf.useScreenMenuBar", "true");
+ }
+
+ if (SystemInfo.isXWindow) {
+ if (VM_PARAMETERS == null || !VM_PARAMETERS.contains("-Dsun.awt.disablegrab")) {
+ vm.defineProperty("sun.awt.disablegrab", "true"); // See http://devnet.jetbrains.net/docs/DOC-1142
+ }
+ }
+
+ String buildNumber = IdeaJdk.getBuildNumber(usedIdeaJdk.getHomePath());
+ if (buildNumber != null) {
+ if (buildNumber.startsWith("IC")) {
+ vm.defineProperty("idea.platform.prefix", "Idea");
+ }
+ else if (buildNumber.startsWith("PY")) {
+ vm.defineProperty("idea.platform.prefix", "Python");
+ }
+ else if (buildNumber.startsWith("RM")) {
+ vm.defineProperty("idea.platform.prefix", "Ruby");
+ }
+ else if (buildNumber.startsWith("PS")) {
+ vm.defineProperty("idea.platform.prefix", "PhpStorm");
+ }
+ else if (buildNumber.startsWith("WS")) {
+ vm.defineProperty("idea.platform.prefix", "WebStorm");
+ }
+ else if (buildNumber.startsWith("OC")) {
+ vm.defineProperty("idea.platform.prefix", "AppCode");
+ }
+ }
+
+ params.setWorkingDirectory(usedIdeaJdk.getHomePath() + File.separator + "bin" + File.separator);
+
+ params.setJdk(usedIdeaJdk);
+
+ params.getClassPath().addFirst(libPath + File.separator + "log4j.jar");
+ params.getClassPath().addFirst(libPath + File.separator + "jdom.jar");
+ params.getClassPath().addFirst(libPath + File.separator + "trove4j.jar");
+ params.getClassPath().addFirst(libPath + File.separator + "openapi.jar");
+ params.getClassPath().addFirst(libPath + File.separator + "util.jar");
+ params.getClassPath().addFirst(libPath + File.separator + "extensions.jar");
+ params.getClassPath().addFirst(libPath + File.separator + "bootstrap.jar");
+ params.getClassPath().addFirst(libPath + File.separator + "idea.jar");
+ params.getClassPath().addFirst(libPath + File.separator + "idea_rt.jar");
+ params.getClassPath().addFirst(((JavaSdkType)usedIdeaJdk.getSdkType()).getToolsPath(usedIdeaJdk));
+
+ params.setMainClass("com.intellij.idea.Main");
+
+ return params;
+ }
+ };
+
+ state.setConsoleBuilder(TextConsoleBuilderFactory.getInstance().createBuilder(getProject()));
+ return state;
+ }
+
+ public String getAlternativeJrePath() {
+ return ALTERNATIVE_JRE_PATH;
+ }
+
+ public void setAlternativeJrePath(String ALTERNATIVE_JRE_PATH) {
+ this.ALTERNATIVE_JRE_PATH = ALTERNATIVE_JRE_PATH;
+ }
+
+ public boolean isAlternativeJreEnabled() {
+ return ALTERNATIVE_JRE_PATH_ENABLED;
+ }
+
+ public void setAlternativeJreEnabled(boolean ALTERNATIVE_JRE_PATH_ENABLED) {
+ this.ALTERNATIVE_JRE_PATH_ENABLED = ALTERNATIVE_JRE_PATH_ENABLED;
+ }
+
+ private static void fillParameterList(ParametersList list, @Nullable String value) {
+ if (value == null) return;
+
+ for (String parameter : value.split(" ")) {
+ if (parameter != null && parameter.length() > 0) {
+ list.add(parameter);
+ }
+ }
+ }
+
+ public void checkConfiguration() throws RuntimeConfigurationException {
+ if (getModule() == null) {
+ throw new RuntimeConfigurationException(DevKitBundle.message("run.configuration.no.module.specified"));
+ }
+ String moduleName = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
+ public String compute() {
+ return getModule().getName();
+ }
+ });
+ if (ModuleManager.getInstance(getProject()).findModuleByName(moduleName) == null){
+ throw new RuntimeConfigurationException(DevKitBundle.message("run.configuration.no.module.specified"));
+ }
+ final ModuleRootManager rootManager = ModuleRootManager.getInstance(getModule());
+ final Sdk jdk = rootManager.getSdk();
+ if (jdk == null) {
+ throw new RuntimeConfigurationException(DevKitBundle.message("jdk.no.specified", moduleName));
+ }
+ if (IdeaJdk.findIdeaJdk(jdk) == null) {
+ throw new RuntimeConfigurationException(DevKitBundle.message("jdk.type.incorrect", moduleName));
+ }
+ }
+
+
+ @NotNull
+ public Module[] getModules() {
+ final Module module = getModule();
+ return module != null ? new Module[]{module} : Module.EMPTY_ARRAY;
+ }
+
+ public void readExternal(Element element) throws InvalidDataException {
+ Element module = element.getChild(MODULE);
+ if (module != null) {
+ myModuleName = module.getAttributeValue(NAME);
+ }
+ DefaultJDOMExternalizer.readExternal(this, element);
+ final Element altElement = element.getChild(ALTERNATIVE_PATH_ELEMENT);
+ if (altElement != null) {
+ ALTERNATIVE_JRE_PATH = altElement.getAttributeValue(PATH);
+ final String enabledAttr = altElement.getAttributeValue(ALTERNATIVE_PATH_ENABLED_ATTR);
+ ALTERNATIVE_JRE_PATH_ENABLED = enabledAttr != null && Boolean.parseBoolean(enabledAttr);
+ }
+ super.readExternal(element);
+ }
+
+ public void writeExternal(Element element) throws WriteExternalException {
+ Element moduleElement = new Element(MODULE);
+ moduleElement.setAttribute(NAME, ApplicationManager.getApplication().runReadAction(new Computable<String>() {
+ public String compute() {
+ final Module module = getModule();
+ return module != null ? module.getName()
+ : myModuleName != null ? myModuleName : "";
+ }
+ }));
+ element.addContent(moduleElement);
+ DefaultJDOMExternalizer.writeExternal(this, element);
+ if (!StringUtil.isEmptyOrSpaces(ALTERNATIVE_JRE_PATH)) {
+ Element altElement = new Element(ALTERNATIVE_PATH_ELEMENT);
+ altElement.setAttribute(PATH, ALTERNATIVE_JRE_PATH);
+ altElement.setAttribute(ALTERNATIVE_PATH_ENABLED_ATTR, String.valueOf(ALTERNATIVE_JRE_PATH_ENABLED));
+ element.addContent(altElement);
+ }
+ super.writeExternal(element);
+ }
+
+ @Nullable
+ public Module getModule() {
+ if (myModule == null && myModuleName != null){
+ myModule = ModuleManager.getInstance(getProject()).findModuleByName(myModuleName);
+ }
+ if (myModule != null && myModule.isDisposed()) {
+ myModule = null;
+ }
+
+ return myModule;
+ }
+
+ public void setModule(Module module) {
+ myModule = module;
+ }
+}
diff --git a/plugins/devkit/src/run/PluginRunConfigurationEditor.java b/plugins/devkit/src/run/PluginRunConfigurationEditor.java
new file mode 100644
index 0000000..8b4ae5b
--- /dev/null
+++ b/plugins/devkit/src/run/PluginRunConfigurationEditor.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2000-2011 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.run;
+
+import com.intellij.execution.ExecutionBundle;
+import com.intellij.execution.configurations.LogFileOptions;
+import com.intellij.execution.ui.AlternativeJREPanel;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.SettingsEditor;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.ui.configuration.ModulesCombobox;
+import com.intellij.openapi.ui.LabeledComponent;
+import com.intellij.ui.PanelWithAnchor;
+import com.intellij.ui.RawCommandLineEditor;
+import com.intellij.ui.components.JBLabel;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.module.PluginModuleType;
+import org.jetbrains.idea.devkit.projectRoots.IdeaJdk;
+import org.jetbrains.idea.devkit.projectRoots.Sandbox;
+
+import javax.swing.*;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class PluginRunConfigurationEditor extends SettingsEditor<PluginRunConfiguration> implements PanelWithAnchor {
+ private final ModulesCombobox myModules = new ModulesCombobox();
+ private final JBLabel myModuleLabel = new JBLabel(ExecutionBundle.message("application.configuration.use.classpath.and.jdk.of.module.label"));
+ private final LabeledComponent<RawCommandLineEditor> myVMParameters = new LabeledComponent<RawCommandLineEditor>();
+ private final LabeledComponent<RawCommandLineEditor> myProgramParameters = new LabeledComponent<RawCommandLineEditor>();
+ private JComponent anchor;
+ private AlternativeJREPanel myAlternativeJREPanel = new AlternativeJREPanel();
+
+ @NonNls private final JCheckBox myShowLogs = new JCheckBox(DevKitBundle.message("show.smth", "idea.log"));
+
+ private final PluginRunConfiguration myPRC;
+ private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.devkit.run.PluginRunConfigurationEditor");
+
+ public PluginRunConfigurationEditor(final PluginRunConfiguration prc) {
+ myPRC = prc;
+ myShowLogs.setSelected(isShow(prc));
+ myShowLogs.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ setShow(prc, myShowLogs.isSelected());
+ }
+ });
+ myModules.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ final Module selectedModule = myModules.getSelectedModule();
+ if (selectedModule != null){
+ prc.removeAllLogFiles();
+ Sdk jdk = ModuleRootManager.getInstance(selectedModule).getSdk();
+ jdk = IdeaJdk.findIdeaJdk(jdk);
+ if (jdk != null) {
+ final String sandboxHome = ((Sandbox)jdk.getSdkAdditionalData()).getSandboxHome();
+ if (sandboxHome == null){
+ return;
+ }
+ try {
+ @NonNls final String file = new File(sandboxHome).getCanonicalPath() + File.separator + "system" + File.separator + "log" + File.separator +
+ "idea.log";
+ if (new File(file).exists()){
+ prc.addLogFile(file, DevKitBundle.message("idea.log.tab.title"), myShowLogs.isSelected());
+ }
+ }
+ catch (IOException e1) {
+ LOG.error(e1);
+ }
+ }
+ }
+ }
+ });
+
+ setAnchor(myModuleLabel);
+ }
+
+ @Override
+ public JComponent getAnchor() {
+ return anchor;
+ }
+
+ @Override
+ public void setAnchor(@Nullable JComponent anchor) {
+ this.anchor = anchor;
+ myModuleLabel.setAnchor(anchor);
+ myVMParameters.setAnchor(anchor);
+ myProgramParameters.setAnchor(anchor);
+ }
+
+ private static void setShow(PluginRunConfiguration prc, boolean show){
+ final ArrayList<LogFileOptions> logFiles = prc.getLogFiles();
+ for (LogFileOptions logFile: logFiles) {
+ logFile.setEnable(show);
+ }
+ }
+
+ private static boolean isShow(PluginRunConfiguration prc){
+ final ArrayList<LogFileOptions> logFiles = prc.getLogFiles();
+ for (LogFileOptions logFile : logFiles) {
+ if (logFile.isEnabled()) return true;
+ }
+ return false;
+ }
+
+ public void resetEditorFrom(PluginRunConfiguration prc) {
+ myModules.setSelectedModule(prc.getModule());
+ getVMParameters().setText(prc.VM_PARAMETERS);
+ getProgramParameters().setText(prc.PROGRAM_PARAMETERS);
+ myAlternativeJREPanel.init(prc.getAlternativeJrePath(), prc.isAlternativeJreEnabled());
+ }
+
+
+ public void applyEditorTo(PluginRunConfiguration prc) throws ConfigurationException {
+ prc.setModule(myModules.getSelectedModule());
+ prc.VM_PARAMETERS = getVMParameters().getText();
+ prc.PROGRAM_PARAMETERS = getProgramParameters().getText();
+ prc.setAlternativeJrePath(myAlternativeJREPanel.getPath());
+ prc.setAlternativeJreEnabled(myAlternativeJREPanel.isPathEnabled());
+ }
+
+ @NotNull
+ public JComponent createEditor() {
+ myModules.fillModules(myPRC.getProject(), PluginModuleType.getInstance());
+ JPanel wholePanel = new JPanel(new GridBagLayout());
+ myVMParameters.setText(DevKitBundle.message("vm.parameters"));
+ myVMParameters.setComponent(new RawCommandLineEditor());
+ myVMParameters.getComponent().setDialogCaption(myVMParameters.getRawText());
+ myVMParameters.setLabelLocation(BorderLayout.WEST);
+
+ myProgramParameters.setText(DevKitBundle.message("program.parameters"));
+ myProgramParameters.setComponent(new RawCommandLineEditor());
+ myProgramParameters.getComponent().setDialogCaption(myProgramParameters.getRawText());
+ myProgramParameters.setLabelLocation(BorderLayout.WEST);
+
+ GridBagConstraints gc = new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(2, 0, 0, 0), UIUtil.DEFAULT_HGAP, UIUtil.DEFAULT_VGAP);
+ wholePanel.add(myVMParameters, gc);
+ wholePanel.add(myProgramParameters, gc);
+ gc.gridwidth = 1;
+ gc.gridy = 3;
+ gc.weightx = 0;
+ wholePanel.add(myModuleLabel, gc);
+ gc.weighty = 1;
+ gc.gridx = 1;
+ gc.weightx = 1;
+ wholePanel.add(myModules, gc);
+ gc.gridx = 0;
+ gc.gridy = 4;
+ gc.gridwidth = 2;
+
+ wholePanel.add(myAlternativeJREPanel, gc);
+ gc.gridy = 5;
+ wholePanel.add(myShowLogs, gc);
+ return wholePanel;
+ }
+
+ public RawCommandLineEditor getVMParameters() {
+ return myVMParameters.getComponent();
+ }
+
+ public RawCommandLineEditor getProgramParameters() {
+ return myProgramParameters.getComponent();
+ }
+
+ public void disposeEditor() {
+ }
+}
diff --git a/plugins/devkit/src/util/ActionData.java b/plugins/devkit/src/util/ActionData.java
new file mode 100644
index 0000000..445fc93
--- /dev/null
+++ b/plugins/devkit/src/util/ActionData.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.util;
+
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author swr
+ */
+public interface ActionData {
+ @NotNull
+ String getActionId();
+
+ @NotNull
+ String getActionText();
+
+ String getActionDescription();
+
+ @Nullable
+ String getSelectedGroupId();
+
+ @Nullable
+ String getSelectedActionId();
+
+ String getSelectedAnchor();
+
+ @Nullable
+ String getFirstKeyStroke();
+
+ @Nullable
+ String getSecondKeyStroke();
+}
diff --git a/plugins/devkit/src/util/ActionType.java b/plugins/devkit/src/util/ActionType.java
new file mode 100644
index 0000000..f703709
--- /dev/null
+++ b/plugins/devkit/src/util/ActionType.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.idea.devkit.util;
+
+import com.intellij.openapi.actionSystem.ActionGroup;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.keymap.KeymapManager;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author swr
+ */
+public enum ActionType {
+ ACTION(AnAction.class, "action"),
+ GROUP(ActionGroup.class, "group");
+
+ public final String myClassName;
+ private final String myName;
+
+ public interface Processor {
+ boolean process(ActionType type, XmlTag action);
+ }
+
+ ActionType(Class<? extends AnAction> clazz, @NonNls String name) {
+ myClassName = clazz.getName();
+ myName = name;
+ }
+
+ public boolean isOfType(PsiClass klass) {
+ final PsiClass psiClass = JavaPsiFacade.getInstance(klass.getProject()).findClass(myClassName, klass.getResolveScope());
+ return psiClass != null && klass.isInheritor(psiClass, true);
+ }
+
+ public void process(XmlTag rootTag, Processor processor) {
+ final XmlTag[] actions = rootTag.findSubTags("actions");
+ for (XmlTag tag : actions) {
+ if (!tag.isPhysical()) continue;
+ final XmlTag[] components = tag.getSubTags();
+ for (XmlTag actionOrGroup : components) {
+ if (myName.equals(actionOrGroup.getName())) {
+ if (!processor.process(this, actionOrGroup)) {
+ return;
+ }
+ } else if (this == ACTION && GROUP.myName.equals(actionOrGroup.getName())) {
+ final XmlTag[] groupActions = actionOrGroup.findSubTags(myName);
+ for (XmlTag a : groupActions) {
+ if (!processor.process(this, a)) {
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public void patchPluginXml(XmlFile pluginXml, PsiClass klass, ActionData dialog) throws IncorrectOperationException {
+ final XmlTag rootTag = pluginXml.getDocument().getRootTag();
+ if (rootTag != null && "idea-plugin".equals(rootTag.getName())) {
+ XmlTag actions = rootTag.findFirstSubTag("actions");
+ if (actions == null || !actions.isPhysical()) {
+ actions = (XmlTag)rootTag.add(rootTag.createChildTag("actions", rootTag.getNamespace(), null, false));
+ }
+
+ XmlTag actionTag = (XmlTag)actions.add(actions.createChildTag(myName, actions.getNamespace(), null, false));
+ actionTag.setAttribute("id", dialog.getActionId());
+ actionTag.setAttribute("class", klass.getQualifiedName());
+ actionTag.setAttribute("text", dialog.getActionText());
+ String description = dialog.getActionDescription();
+ if (description != null && description.length() > 0) {
+ actionTag.setAttribute("description", description);
+ }
+
+ String groupId = dialog.getSelectedGroupId();
+ if (groupId != null) {
+ XmlTag groupTag = (XmlTag)actionTag.add(actionTag.createChildTag("add-to-group", actions.getNamespace(), null, false));
+ groupTag.setAttribute("group-id", groupId);
+ @NonNls final String anchor = dialog.getSelectedAnchor();
+ groupTag.setAttribute("anchor", anchor);
+ if (anchor.equals("before") || anchor.equals("after")) {
+ groupTag.setAttribute("relative-to-action", dialog.getSelectedActionId());
+ }
+ }
+
+ String firstKeyStroke = dialog.getFirstKeyStroke();
+ if (firstKeyStroke != null && firstKeyStroke.length() > 0) {
+ XmlTag keyTag = (XmlTag)actionTag.add(actionTag.createChildTag("keyboard-shortcut", actions.getNamespace(), null, false));
+ keyTag.setAttribute("keymap", KeymapManager.DEFAULT_IDEA_KEYMAP);
+ keyTag.setAttribute("first-keystroke", firstKeyStroke);
+ final String secondKeyStroke = dialog.getSecondKeyStroke();
+ if (secondKeyStroke != null && secondKeyStroke.length() > 0) {
+ keyTag.setAttribute("second-keystroke", secondKeyStroke);
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/devkit/src/util/ChooseModulesDialog.java b/plugins/devkit/src/util/ChooseModulesDialog.java
new file mode 100644
index 0000000..28eabdb
--- /dev/null
+++ b/plugins/devkit/src/util/ChooseModulesDialog.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.util;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.MultiLineLabelUI;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.ui.ColoredListCellRenderer;
+import com.intellij.ui.ScrollPaneFactory;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.ui.components.JBList;
+import com.intellij.ui.table.JBTable;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.module.PluginModuleType;
+
+import javax.swing.*;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableCellRenderer;
+import java.awt.*;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * @author swr
+ */
+public class ChooseModulesDialog extends DialogWrapper {
+ private final Icon myIcon;
+ private final String myMessage;
+ private final JTable myView;
+ private final List<Module> myCandidateModules;
+ private final boolean[] myStates;
+
+ public ChooseModulesDialog(final Project project, List<Module> candidateModules, @NonNls String title) {
+ this ( project, candidateModules, title, DevKitBundle.message("select.plugin.modules.to.patch"));
+ }
+
+ public ChooseModulesDialog(final Project project, List<Module> candidateModules, @NonNls String title, final String message) {
+ super(project, false);
+ setTitle(title);
+
+ myCandidateModules = candidateModules;
+ myIcon = Messages.getQuestionIcon();
+ myMessage = message;
+ myView = new JBTable(new AbstractTableModel() {
+ public int getRowCount() {
+ return myCandidateModules.size();
+ }
+
+ public int getColumnCount() {
+ return 2;
+ }
+
+ public boolean isCellEditable(int rowIndex, int columnIndex) {
+ return columnIndex == 0;
+ }
+
+ public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+ myStates[rowIndex] = (Boolean)aValue;
+ fireTableCellUpdated(rowIndex, columnIndex);
+ }
+
+ public Class<?> getColumnClass(int columnIndex) {
+ return columnIndex == 0 ? Boolean.class : Module.class;
+ }
+
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ return columnIndex == 0 ? myStates[rowIndex] : myCandidateModules.get(rowIndex);
+ }
+ });
+
+ myView.setShowGrid(false);
+ myView.setTableHeader(null);
+ myView.setIntercellSpacing(new Dimension(0, 0));
+ myView.getColumnModel().getColumn(0).setMaxWidth(new JCheckBox().getPreferredSize().width);
+ myView.getModel().addTableModelListener(new TableModelListener() {
+ public void tableChanged(TableModelEvent e) {
+ getOKAction().setEnabled(getSelectedModules().size() > 0);
+ }
+ });
+ myView.addKeyListener(new KeyAdapter() {
+ public void keyTyped(KeyEvent e) {
+ if (e.getKeyCode() == KeyEvent.VK_ENTER
+ || e.getKeyChar() == '\n') {
+ doOKAction();
+ }
+ }
+ });
+ myView.setDefaultRenderer(Module.class, new MyTableCellRenderer(project));
+
+ myStates = new boolean[candidateModules.size()];
+ Arrays.fill(myStates, true);
+
+ init();
+ }
+
+ protected JComponent createNorthPanel() {
+ JPanel panel = new JPanel(new BorderLayout(15, 10));
+ if (myIcon != null) {
+ JLabel iconLabel = new JLabel(myIcon);
+ Container container = new Container();
+ container.setLayout(new BorderLayout());
+ container.add(iconLabel, BorderLayout.NORTH);
+ panel.add(container, BorderLayout.WEST);
+ }
+
+ JPanel messagePanel = new JPanel(new BorderLayout());
+ if (myMessage != null) {
+ JLabel textLabel = new JLabel(myMessage);
+ textLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
+ textLabel.setUI(new MultiLineLabelUI());
+ messagePanel.add(textLabel, BorderLayout.NORTH);
+ }
+ panel.add(messagePanel, BorderLayout.CENTER);
+
+ final JScrollPane jScrollPane = ScrollPaneFactory.createScrollPane();
+ jScrollPane.setViewportView(myView);
+ jScrollPane.setPreferredSize(new Dimension(300, 80));
+ panel.add(jScrollPane, BorderLayout.SOUTH);
+ return panel;
+ }
+
+ public JComponent getPreferredFocusedComponent() {
+ return myView;
+ }
+
+
+ protected JComponent createCenterPanel() {
+ return null;
+ }
+
+ public List<Module> getSelectedModules() {
+ final ArrayList<Module> list = new ArrayList<Module>(myCandidateModules);
+ final Iterator<Module> modules = list.iterator();
+ for (boolean b : myStates) {
+ modules.next();
+ if (!b) {
+ modules.remove();
+ }
+ }
+ return list;
+ }
+
+ private static class MyTableCellRenderer implements TableCellRenderer {
+ private final JList myList;
+ private final Project myProject;
+ private final ColoredListCellRenderer myCellRenderer;
+
+ public MyTableCellRenderer(Project project) {
+ myProject = project;
+ myList = new JBList();
+ myCellRenderer = new ColoredListCellRenderer() {
+ protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
+ final Module module = ((Module)value);
+ setIcon(ModuleType.get(module).getIcon());
+ append(module.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
+
+ final XmlFile pluginXml = PluginModuleType.getPluginXml(module);
+ assert pluginXml != null;
+
+ final VirtualFile virtualFile = pluginXml.getVirtualFile();
+ assert virtualFile != null;
+ final VirtualFile projectPath = myProject.getBaseDir();
+ assert projectPath != null;
+ if (VfsUtilCore.isAncestor(projectPath, virtualFile, false)) {
+ append(" (" + VfsUtilCore.getRelativePath(virtualFile, projectPath, File.separatorChar) + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
+ } else {
+ append(" (" + virtualFile.getPresentableUrl() + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
+ }
+ }
+ };
+ }
+
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+ return myCellRenderer.getListCellRendererComponent(myList, value, row, isSelected, hasFocus);
+ }
+ }
+}
diff --git a/plugins/devkit/src/util/ComponentType.java b/plugins/devkit/src/util/ComponentType.java
new file mode 100644
index 0000000..2982700
--- /dev/null
+++ b/plugins/devkit/src/util/ComponentType.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.util;
+
+import com.intellij.openapi.components.ApplicationComponent;
+import com.intellij.openapi.components.BaseComponent;
+import com.intellij.openapi.components.ProjectComponent;
+import com.intellij.openapi.module.ModuleComponent;
+import com.intellij.psi.*;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.psi.xml.XmlTagValue;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.PropertyKey;
+
+/**
+ * @author swr
+ */
+public enum ComponentType {
+ APPLICATION(ApplicationComponent.class, "application-components", "new.menu.application.component.text"),
+ PROJECT(ProjectComponent.class, "project-components", "new.menu.project.component.text"),
+ MODULE(ModuleComponent.class, "module-components", "new.menu.module.component.text");
+
+ public final String myClassName;
+ public final String myPropertyKey;
+ private final String myName;
+
+ public interface Processor {
+ boolean process(ComponentType type, XmlTag component, @Nullable XmlTagValue impl, @Nullable XmlTagValue intf);
+ }
+
+ ComponentType(Class<? extends BaseComponent> clazz, @NonNls String name,
+ @PropertyKey(resourceBundle = "org.jetbrains.idea.devkit.DevKitBundle") String propertyKey)
+ {
+ myPropertyKey = propertyKey;
+ myClassName = clazz.getName();
+ myName = name;
+ }
+
+ public void patchPluginXml(XmlFile pluginXml, PsiClass klass) throws IncorrectOperationException {
+ final XmlTag rootTag = pluginXml.getDocument().getRootTag();
+ if (rootTag != null && "idea-plugin".equals(rootTag.getName())) {
+ XmlTag components = rootTag.findFirstSubTag(myName);
+ if (components == null || !components.isPhysical()) {
+ components = (XmlTag)rootTag.add(rootTag.createChildTag(myName, rootTag.getNamespace(), null, false));
+ }
+
+ XmlTag cmp = (XmlTag)components.add(components.createChildTag("component", components.getNamespace(), null, false));
+ cmp.add(cmp.createChildTag("implementation-class", cmp.getNamespace(), klass.getQualifiedName(), false));
+
+ // some magic to figure out interface-class
+ final PsiMethod[] methods = klass.findMethodsByName("getInstance", true);
+ for (PsiMethod method : methods) {
+ final PsiParameter[] parameters = method.getParameterList().getParameters();
+ if (parameters.length <= 1) {
+ final PsiType returnType = method.getReturnType();
+ if (returnType instanceof PsiClassType) {
+ final String intf = returnType.getCanonicalText();
+ cmp.add(cmp.createChildTag("interface-class", cmp.getNamespace(), intf, false));
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ public void process(XmlTag rootTag, Processor processor) {
+ final XmlTag[] compGroup = rootTag.findSubTags(myName);
+ for (XmlTag tag : compGroup) {
+ if (!tag.isPhysical()) continue; //skip included tags
+ final XmlTag[] components = tag.findSubTags("component");
+ for (XmlTag component : components) {
+ final XmlTag impl = component.findFirstSubTag("implementation-class");
+ final XmlTag intf = component.findFirstSubTag("interface-class");
+ if (!processor.process(this, component,
+ impl != null ? impl.getValue() : null,
+ intf != null ? intf.getValue() : null))
+ {
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/devkit/src/util/DescriptorUtil.java b/plugins/devkit/src/util/DescriptorUtil.java
new file mode 100644
index 0000000..4fca895
--- /dev/null
+++ b/plugins/devkit/src/util/DescriptorUtil.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.util;
+
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.psi.PsiClass;
+import com.intellij.util.IncorrectOperationException;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.ReadonlyStatusHandler;
+import com.intellij.openapi.module.Module;
+import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.module.PluginModuleType;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author swr
+ */
+public class DescriptorUtil {
+
+ public static void processComponents(XmlTag root, ComponentType.Processor processor) {
+ final ComponentType[] types = ComponentType.values();
+ for (ComponentType type : types) {
+ type.process(root, processor);
+ }
+ }
+
+ public static void processActions(XmlTag root, ActionType.Processor processor) {
+ final ActionType[] types = ActionType.values();
+ for (ActionType type : types) {
+ type.process(root, processor);
+ }
+ }
+
+ public interface Patcher {
+ void patchPluginXml(XmlFile pluginXml, PsiClass klass) throws IncorrectOperationException;
+ }
+
+ public static void patchPluginXml(Patcher patcher, PsiClass klass, XmlFile... pluginXmls) throws IncorrectOperationException {
+ final VirtualFile[] files = new VirtualFile[pluginXmls.length];
+ int i = 0;
+ for (XmlFile pluginXml : pluginXmls) {
+ files[i++] = pluginXml.getVirtualFile();
+ }
+
+ final ReadonlyStatusHandler readonlyStatusHandler = ReadonlyStatusHandler.getInstance(klass.getProject());
+ final ReadonlyStatusHandler.OperationStatus status = readonlyStatusHandler.ensureFilesWritable(files);
+ if (status.hasReadonlyFiles()) {
+ throw new IncorrectOperationException(DevKitBundle.message("error.plugin.xml.readonly"));
+ }
+
+ for (XmlFile pluginXml : pluginXmls) {
+ patcher.patchPluginXml(pluginXml, klass);
+ }
+ }
+
+ @Nullable
+ public static String getPluginId(Module plugin) {
+ assert PluginModuleType.isOfType(plugin);
+
+ final XmlFile pluginXml = PluginModuleType.getPluginXml(plugin);
+ if (pluginXml != null) {
+ final XmlTag rootTag = pluginXml.getDocument().getRootTag();
+ if (rootTag != null) {
+ final XmlTag idTag = rootTag.findFirstSubTag("id");
+ if (idTag != null) return idTag.getValue().getTrimmedText();
+
+ final XmlTag nameTag = rootTag.findFirstSubTag("name");
+ if (nameTag != null) return nameTag.getValue().getTrimmedText();
+ }
+ }
+ return null;
+ }
+}
diff --git a/plugins/devkit/src/util/PsiUtil.java b/plugins/devkit/src/util/PsiUtil.java
new file mode 100644
index 0000000..490f94f
--- /dev/null
+++ b/plugins/devkit/src/util/PsiUtil.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.idea.devkit.util;
+
+import com.intellij.psi.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class PsiUtil {
+ private PsiUtil() {
+ }
+
+ public static boolean isInstantiatable(@NotNull PsiClass cls) {
+ final PsiModifierList modifiers = cls.getModifierList();
+
+ if (modifiers == null
+ || cls.isInterface()
+ || modifiers.hasModifierProperty(PsiModifier.ABSTRACT)
+ || !isPublicOrStaticInnerClass(cls)) {
+ return false;
+ }
+
+ final PsiMethod[] constructors = cls.getConstructors();
+
+ if (constructors.length == 0) return true;
+
+ for (PsiMethod constructor : constructors) {
+ if (constructor.getParameterList().getParameters().length == 0
+ && constructor.hasModifierProperty(PsiModifier.PUBLIC)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean isPublicOrStaticInnerClass(@NotNull PsiClass cls) {
+ final PsiModifierList modifiers = cls.getModifierList();
+ if (modifiers == null) return false;
+
+ return modifiers.hasModifierProperty(PsiModifier.PUBLIC) &&
+ (cls.getParent() instanceof PsiFile || modifiers.hasModifierProperty(PsiModifier.STATIC));
+ }
+
+ public static boolean isOneStatementMethod(@NotNull PsiMethod method) {
+ final PsiCodeBlock body = method.getBody();
+ return body != null
+ && body.getStatements().length == 1
+ && body.getStatements()[0] instanceof PsiReturnStatement;
+ }
+
+ @Nullable
+ public static String getReturnedLiteral(PsiMethod method, PsiClass cls) {
+ if (isOneStatementMethod(method)) {
+ final PsiExpression value = ((PsiReturnStatement)method.getBody().getStatements()[0]).getReturnValue();
+ if (value instanceof PsiLiteralExpression) {
+ final Object str = ((PsiLiteralExpression)value).getValue();
+ return str == null ? null : str.toString();
+ } else if (value instanceof PsiMethodCallExpression) {
+ if (isSimpleClassNameExpression((PsiMethodCallExpression)value)) {
+ return cls.getName();
+ }
+ }
+ }
+ return null;
+ }
+
+ private static boolean isSimpleClassNameExpression(PsiMethodCallExpression expr) {
+ String text = expr.getText();
+ if (text == null) return false;
+ text = text.replaceAll(" ", "")
+ .replaceAll("\n", "")
+ .replaceAll("\t", "")
+ .replaceAll("\r", "");
+ return "getClass().getSimpleName()".equals(text) || "this.getClass().getSimpleName()".equals(text);
+ }
+
+ @Nullable
+ public static PsiExpression getReturnedExpression(PsiMethod method) {
+ if (isOneStatementMethod(method)) {
+ return ((PsiReturnStatement)method.getBody().getStatements()[0]).getReturnValue();
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/plugins/devkit/testData/build/simple/META-INF/plugin.xml b/plugins/devkit/testData/build/simple/META-INF/plugin.xml
new file mode 100644
index 0000000..e5b561a
--- /dev/null
+++ b/plugins/devkit/testData/build/simple/META-INF/plugin.xml
@@ -0,0 +1,9 @@
+<idea-plugin version="2">
+ <id>test.plugin</id>
+ <name>Test Plugin</name>
+ <version>1.0</version>
+
+ <actions>
+ <action id="xxx.MyAction" class="xxx.MyAction" text="Something"/>
+ </actions>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/build/simple/pluginProject.iml b/plugins/devkit/testData/build/simple/pluginProject.iml
new file mode 100644
index 0000000..953b0b8
--- /dev/null
+++ b/plugins/devkit/testData/build/simple/pluginProject.iml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="PLUGIN_MODULE" version="4">
+ <component name="DevKit.ModuleBuildProperties" url="file://$MODULE_DIR$/META-INF/plugin.xml" />
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ </content>
+ <orderEntry type="jdk" jdkName="IDEA plugin SDK" jdkType="IDEA JDK" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
+
diff --git a/plugins/devkit/testData/build/simple/src/xxx/MyAction.java b/plugins/devkit/testData/build/simple/src/xxx/MyAction.java
new file mode 100644
index 0000000..997befe
--- /dev/null
+++ b/plugins/devkit/testData/build/simple/src/xxx/MyAction.java
@@ -0,0 +1,9 @@
+package xxx;
+
+import com.intellij.openapi.util.io.FileUtilRt ;
+
+public class MyAction {
+ public void actionPerformed() {
+ FileUtilRt.toSystemDependentName("myPath");
+ }
+}
diff --git a/plugins/devkit/testData/codeInsight/DependsHighlighting.xml b/plugins/devkit/testData/codeInsight/DependsHighlighting.xml
new file mode 100644
index 0000000..243fc2f
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/DependsHighlighting.xml
@@ -0,0 +1,9 @@
+<idea-plugin>
+ <id>com.intellij.myPlugin</id>
+
+ <depends>com.intellij</depends>
+ <depends>com.intellij.modules.vcs</depends>
+ <depends>com.intellij.custom</depends>
+ <depends><error descr="Cannot resolve plugin com.intellij.xxx">com.intellij.xxx</error></depends>
+
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/ExtBeanWithAccessors.java b/plugins/devkit/testData/codeInsight/ExtBeanWithAccessors.java
new file mode 100644
index 0000000..bf06af52
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/ExtBeanWithAccessors.java
@@ -0,0 +1,14 @@
+import java.lang.String;
+
+public class ExtBeanWithAccessors {
+ private String field;
+
+ @com.intellij.util.xmlb.annotations.Attribute("param")
+ public String getField() {
+ return field;
+ }
+
+ public void setField(String field) {
+ this.field = field;
+ }
+}
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/ExtensionQualifiedName.xml b/plugins/devkit/testData/codeInsight/ExtensionQualifiedName.xml
new file mode 100644
index 0000000..e74bde4
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/ExtensionQualifiedName.xml
@@ -0,0 +1,12 @@
+<idea-plugin>
+ <id>com.intellij.myPlugin</id>
+
+ <extensionPoints>
+ <extensionPoint qualifiedName="com.intellij.myPlugin.ext" interface="java.lang.Runnable"/>
+ </extensionPoints>
+
+ <extensions defaultExtensionNs="com.intellij.myPlugin">
+ <ext implementation="java.lang.Runnable"/>
+ </extensions>
+
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/ExtensionsHighlighting.xml b/plugins/devkit/testData/codeInsight/ExtensionsHighlighting.xml
new file mode 100644
index 0000000..b88b914
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/ExtensionsHighlighting.xml
@@ -0,0 +1,16 @@
+<idea-plugin>
+ <id>com.intellij.myPlugin</id>
+ <extensionPoints>
+ <extensionPoint name="foo.bar"/>
+ </extensionPoints>
+
+ <depends>com.intellij.custom</depends>
+
+ <extensions defaultExtensionNs="com.intellij">
+ <myPlugin.foo.bar/>
+ <completion.contributor/>
+ <custom.custom/>
+ <indirect.indirect/>
+ <<error descr="Element custom.error is not allowed here">custom.error</error>/>
+ </extensions>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/InnerClassCompletion.xml b/plugins/devkit/testData/codeInsight/InnerClassCompletion.xml
new file mode 100644
index 0000000..2bb61f9
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/InnerClassCompletion.xml
@@ -0,0 +1,5 @@
+<idea-plugin>
+ <actions>
+ <action class="Fuba<caret>"/>
+ </actions>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/InnerClassCompletion_after.xml b/plugins/devkit/testData/codeInsight/InnerClassCompletion_after.xml
new file mode 100644
index 0000000..db6fd9e
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/InnerClassCompletion_after.xml
@@ -0,0 +1,5 @@
+<idea-plugin>
+ <actions>
+ <action class="foo.Foo$Fubar<caret>"/>
+ </actions>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/LoadForDefaultProject.xml b/plugins/devkit/testData/codeInsight/LoadForDefaultProject.xml
new file mode 100644
index 0000000..bceef14
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/LoadForDefaultProject.xml
@@ -0,0 +1,10 @@
+<idea-plugin version="2">
+ <project-components>
+ <component>
+ <implementation-class><error descr="Cannot resolve class or package 'foo'">foo</error>.<error descr="Cannot resolve class 'Bar'">Bar</error></implementation-class>
+ <skipForDefaultProject/>
+ <loadForDefaultProject/>
+ <<error descr="Element unknown is not allowed here">unknown</error>/>
+ </component>
+ </project-components>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/MyExtBean.java b/plugins/devkit/testData/codeInsight/MyExtBean.java
new file mode 100644
index 0000000..5c8a9f1
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/MyExtBean.java
@@ -0,0 +1,8 @@
+public class MyExtBean {
+ @java.lang.Deprecated
+ @com.intellij.util.xmlb.annotations.Attribute("old")
+ public String myOld;
+
+ @com.intellij.util.xmlb.annotations.Attribute("attr")
+ public String myAttr;
+}
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/NoClassCompletionOutsideJavaReferences.xml b/plugins/devkit/testData/codeInsight/NoClassCompletionOutsideJavaReferences.xml
new file mode 100644
index 0000000..38159d5
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/NoClassCompletionOutsideJavaReferences.xml
@@ -0,0 +1,6 @@
+<idea-plugin>
+ <extensionPoints>
+ <extensionPoint name="ext" interface="foo.FooFooFooFooFoo"/>
+ <extensionPoint name="FFFFoo<caret>"/>
+ </extensionPoints>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/NoClassCompletionOutsideJavaReferences_after.xml b/plugins/devkit/testData/codeInsight/NoClassCompletionOutsideJavaReferences_after.xml
new file mode 100644
index 0000000..daf574b
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/NoClassCompletionOutsideJavaReferences_after.xml
@@ -0,0 +1,6 @@
+<idea-plugin>
+ <extensionPoints>
+ <extensionPoint name="ext" interface="foo.FooFooFooFooFoo"/>
+ <extensionPoint name="FooFooFooFooFoo<caret>"/>
+ </extensionPoints>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/NoWordCompletionInClassPlaces.xml b/plugins/devkit/testData/codeInsight/NoWordCompletionInClassPlaces.xml
new file mode 100644
index 0000000..d896fb5
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/NoWordCompletionInClassPlaces.xml
@@ -0,0 +1,6 @@
+<idea-plugin>
+ <extensionPoints>
+ <extensionPoint name="ext" interface="foo.FooFooFooFooFoo"/>
+ <extensionPoint name="ext2" interface="FFFFoo<caret>"/>
+ </extensionPoints>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/NoWordCompletionInClassPlaces_after.xml b/plugins/devkit/testData/codeInsight/NoWordCompletionInClassPlaces_after.xml
new file mode 100644
index 0000000..1ac1737
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/NoWordCompletionInClassPlaces_after.xml
@@ -0,0 +1,6 @@
+<idea-plugin>
+ <extensionPoints>
+ <extensionPoint name="ext" interface="foo.FooFooFooFooFoo"/>
+ <extensionPoint name="ext2" interface="foo.FooFooFooFooFoo"<caret>/>
+ </extensionPoints>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/ResolveExtensionsFromDependentDescriptor_dependent.xml b/plugins/devkit/testData/codeInsight/ResolveExtensionsFromDependentDescriptor_dependent.xml
new file mode 100644
index 0000000..2e91973
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/ResolveExtensionsFromDependentDescriptor_dependent.xml
@@ -0,0 +1,6 @@
+<idea-plugin>
+ <extensions defaultExtensionNs="com.intellij">
+ <xxx.completion.contributor/>
+ <yyy.myExtension/>
+ </extensions>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/ResolveExtensionsFromDependentDescriptor_main.xml b/plugins/devkit/testData/codeInsight/ResolveExtensionsFromDependentDescriptor_main.xml
new file mode 100644
index 0000000..fd829b4
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/ResolveExtensionsFromDependentDescriptor_main.xml
@@ -0,0 +1,9 @@
+<idea-plugin>
+ <id>com.intellij.yyy</id>
+
+ <extensionPoints>
+ <extensionPoint name="myExtension"/>
+ </extensionPoints>
+
+ <depends optional="true" config-file="dep.xml">com.intellij.xxx</depends>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/ShowAnActionInheritorsOnSmartCompletion.xml b/plugins/devkit/testData/codeInsight/ShowAnActionInheritorsOnSmartCompletion.xml
new file mode 100644
index 0000000..e201819
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/ShowAnActionInheritorsOnSmartCompletion.xml
@@ -0,0 +1,5 @@
+<idea-plugin>
+ <actions>
+ <action class="foo.<caret>"/>
+ </actions>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/ShowPackagesInActionClass.xml b/plugins/devkit/testData/codeInsight/ShowPackagesInActionClass.xml
new file mode 100644
index 0000000..e201819
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/ShowPackagesInActionClass.xml
@@ -0,0 +1,5 @@
+<idea-plugin>
+ <actions>
+ <action class="foo.<caret>"/>
+ </actions>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/bundle.properties b/plugins/devkit/testData/codeInsight/bundle.properties
new file mode 100644
index 0000000..c9f0304
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/bundle.properties
@@ -0,0 +1 @@
+foo=bar
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/deprecatedExtensionAttribute.xml b/plugins/devkit/testData/codeInsight/deprecatedExtensionAttribute.xml
new file mode 100644
index 0000000..6ea4867
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/deprecatedExtensionAttribute.xml
@@ -0,0 +1,12 @@
+<idea-plugin>
+ <id>com.intellij.myPlugin</id>
+
+ <extensionPoints>
+ <extensionPoint name="myExt" beanClass="MyExtBean"/>
+ </extensionPoints>
+
+ <extensions defaultExtensionNs="com.intellij.myPlugin">
+ <myExt <warning descr="'myOld' is deprecated">old</warning>="java.lang.Runnable" attr="value"/>
+ </extensions>
+
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/extensionAttributeWithAccessors.xml b/plugins/devkit/testData/codeInsight/extensionAttributeWithAccessors.xml
new file mode 100644
index 0000000..843d95a
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/extensionAttributeWithAccessors.xml
@@ -0,0 +1,12 @@
+<idea-plugin>
+ <id>com.intellij.myPlugin</id>
+
+ <extensionPoints>
+ <extensionPoint name="acc" beanClass="ExtBeanWithAccessors"/>
+ </extensionPoints>
+
+ <extensions defaultExtensionNs="com.intellij.myPlugin">
+ <acc param="123"/>
+ </extensions>
+
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/extensionPointPresentation.xml b/plugins/devkit/testData/codeInsight/extensionPointPresentation.xml
new file mode 100644
index 0000000..62e15ab
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/extensionPointPresentation.xml
@@ -0,0 +1,14 @@
+<idea-plugin version="2" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <id>foo</id>
+
+ <extensionPoints>
+ <extensionPoint name="bar"/>
+ </extensionPoints>
+
+ <extensions defaultExtensionNs="foo">
+
+ <b<caret>ar/>
+ </extensions>
+
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/extensionPoints.xml b/plugins/devkit/testData/codeInsight/extensionPoints.xml
new file mode 100644
index 0000000..d568f48
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/extensionPoints.xml
@@ -0,0 +1,3 @@
+<extensionPoints>
+ <extensionPoint name="extension" beanClass="foo.Bar"/>
+</extensionPoints>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/inspectionMapping.xml b/plugins/devkit/testData/codeInsight/inspectionMapping.xml
new file mode 100644
index 0000000..e4ab77c
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/inspectionMapping.xml
@@ -0,0 +1,21 @@
+<idea-plugin version="2">
+
+ <id>com.intellij</id>
+
+ <extensionPoints>
+ <extensionPoint name="localInspection"
+ beanClass="com.intellij.codeInspection.LocalInspectionEP"/>
+ </extensionPoints>
+
+ <extensions defaultExtensionNs="com.intellij">
+ <<warning descr="groupName or groupKey should be specified"><warning descr="Bundle should be specified">localInspection</warning></warning> key="foo"/>
+ <<warning descr="displayName or key should be specified"><warning descr="Bundle should be specified">localInspection</warning></warning> groupKey="foo"/>
+ <<warning descr="groupName or groupKey should be specified"><warning descr="Bundle should be specified">localInspection</warning></warning> key="foo" groupBundle="bundle"/>
+
+ <<warning descr="groupName or groupKey should be specified">localInspection</warning> key="foo" bundle="bundle"/>
+ <<warning descr="displayName or key should be specified">localInspection</warning> groupKey="foo" bundle="bundle"/>
+
+ <localInspection key="foo" bundle="bundle" groupName="groupName"/>
+ <localInspection groupKey="foo" bundle="bundle" displayName="displayName"/>
+ </extensions>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/inspectionMappingWithDefaultBundle.xml b/plugins/devkit/testData/codeInsight/inspectionMappingWithDefaultBundle.xml
new file mode 100644
index 0000000..851e097
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/inspectionMappingWithDefaultBundle.xml
@@ -0,0 +1,23 @@
+<idea-plugin version="2">
+
+ <id>com.intellij</id>
+ <resource-bundle>bundle</resource-bundle>
+
+ <extensionPoints>
+ <extensionPoint name="localInspection"
+ beanClass="com.intellij.codeInspection.LocalInspectionEP"/>
+ </extensionPoints>
+
+ <extensions defaultExtensionNs="com.intellij">
+ <<warning descr="groupName or groupKey should be specified">localInspection</warning> key="foo"/>
+ <<warning descr="displayName or key should be specified">localInspection</warning> groupKey="foo"/>
+ <<warning descr="groupName or groupKey should be specified">localInspection</warning> key="foo" groupBundle="bundle"/>
+
+ <<warning descr="groupName or groupKey should be specified">localInspection</warning> key="foo" bundle="bundle"/>
+ <<warning descr="displayName or key should be specified">localInspection</warning> groupKey="foo" bundle="bundle"/>
+
+ <localInspection key="foo" bundle="bundle" groupName="groupName"/>
+ <localInspection groupKey="foo" bundle="bundle" displayName="displayName"/>
+
+ </extensions>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/pluginAction.xml b/plugins/devkit/testData/codeInsight/pluginAction.xml
new file mode 100644
index 0000000..34395c5
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/pluginAction.xml
@@ -0,0 +1,7 @@
+<!DOCTYPE idea-plugin PUBLIC "Plugin/DTD" "http://plugins.intellij.net/plugin.dtd">
+<idea-plugin>
+ <idea-version since-build="3000"/>
+ <actions>
+ <action class="<caret>Action" text="" id=""/>
+ </actions>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/pluginImpl.xml b/plugins/devkit/testData/codeInsight/pluginImpl.xml
new file mode 100644
index 0000000..33e32c5
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/pluginImpl.xml
@@ -0,0 +1,9 @@
+<!DOCTYPE idea-plugin PUBLIC "Plugin/DTD" "http://plugins.intellij.net/plugin.dtd">
+<idea-plugin>
+ <idea-version since-build="3000"/>
+ <application-components>
+ <component>
+ <implementation-class>Impl</implementation-class>
+ </component>
+ </application-components>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/pluginIntf.xml b/plugins/devkit/testData/codeInsight/pluginIntf.xml
new file mode 100644
index 0000000..49f8c07
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/pluginIntf.xml
@@ -0,0 +1,10 @@
+<!DOCTYPE idea-plugin PUBLIC "Plugin/DTD" "http://plugins.intellij.net/plugin.dtd">
+<idea-plugin>
+ <idea-version since-build="3000"/>
+ <application-components>
+ <component>
+ <implementation-class></implementation-class>
+ <interface-class>Intf</interface-class>
+ </component>
+ </application-components>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/pluginWithModules.xml b/plugins/devkit/testData/codeInsight/pluginWithModules.xml
new file mode 100644
index 0000000..d277c55
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/pluginWithModules.xml
@@ -0,0 +1,13 @@
+<idea-plugin version="2" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <id>com.intellij</id>
+ <name>IDEA CORE</name>
+
+ <module value="com.intellij.modules.platform"/>
+ <module value="com.intellij.modules.lang"/>
+ <module value="com.intellij.modules.vcs"/>
+ <module value="com.intellij.modules.xdebugger"/>
+ <module value="com.intellij.modules.xml"/>
+ <module value="com.intellij.modules.java"/>
+ <module value="com.intellij.modules.all"/>
+
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/codeInsight/pluginWithXInclude.xml b/plugins/devkit/testData/codeInsight/pluginWithXInclude.xml
new file mode 100644
index 0000000..bea4d7b
--- /dev/null
+++ b/plugins/devkit/testData/codeInsight/pluginWithXInclude.xml
@@ -0,0 +1,16 @@
+<idea-plugin version="2" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <id>foo</id>
+
+ <extensionPoints>
+ <extensionPoint name="bar"/>
+ <xi:include href="/extensionPoints.xml" xpointer="xpointer(/extensionPoints/*)"/>
+ </extensionPoints>
+
+ <extensions defaultExtensionNs="foo">
+
+ <bar/>
+ <extension/>
+ </extensions>
+
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/extensions/GlobalInspection.java b/plugins/devkit/testData/extensions/GlobalInspection.java
new file mode 100644
index 0000000..9fe0a4a
--- /dev/null
+++ b/plugins/devkit/testData/extensions/GlobalInspection.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import com.intellij.codeInspection.GlobalInspectionTool;
+
+public class <warning descr="Class 'GlobalInspection' is never used">Global<caret>Inspection</warning> extends GlobalInspectionTool {
+}
\ No newline at end of file
diff --git a/plugins/devkit/testData/extensions/LocalInspection.java b/plugins/devkit/testData/extensions/LocalInspection.java
new file mode 100644
index 0000000..a08a6c3
--- /dev/null
+++ b/plugins/devkit/testData/extensions/LocalInspection.java
@@ -0,0 +1,4 @@
+import com.intellij.codeInspection.LocalInspectionTool;
+
+public class <warning descr="Class 'LocalInspection' is never used">Local<caret>Inspection</warning> extends LocalInspectionTool {
+}
\ No newline at end of file
diff --git a/plugins/devkit/testData/extensions/globalInspection.xml b/plugins/devkit/testData/extensions/globalInspection.xml
new file mode 100644
index 0000000..9f64986
--- /dev/null
+++ b/plugins/devkit/testData/extensions/globalInspection.xml
@@ -0,0 +1,6 @@
+<idea-plugin>
+ <extensions defaultExtensionNs="com.intellij">
+ <localInspection implementationClass="LocalInspection"/>
+ <globalInspection implementationClass="GlobalInspection"/>
+ </extensions>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/extensions/localInspection.xml b/plugins/devkit/testData/extensions/localInspection.xml
new file mode 100644
index 0000000..b14a539
--- /dev/null
+++ b/plugins/devkit/testData/extensions/localInspection.xml
@@ -0,0 +1,5 @@
+<idea-plugin>
+ <extensions defaultExtensionNs="com.intellij">
+ <localInspection implementationClass="LocalInspection"/>
+ </extensions>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/extensions/plugin.xml b/plugins/devkit/testData/extensions/plugin.xml
new file mode 100644
index 0000000..6fb76a6
--- /dev/null
+++ b/plugins/devkit/testData/extensions/plugin.xml
@@ -0,0 +1,2 @@
+<idea-plugin>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testSources/PluginProjectWizardTest.java b/plugins/devkit/testSources/PluginProjectWizardTest.java
new file mode 100644
index 0000000..6331769
--- /dev/null
+++ b/plugins/devkit/testSources/PluginProjectWizardTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.ide.projectWizard.ProjectWizardTestCase;
+import com.intellij.openapi.module.JavaModuleType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.idea.devkit.module.PluginModuleType;
+import org.jetbrains.idea.devkit.projectRoots.IdeaJdk;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 11/8/12
+ */
+public class PluginProjectWizardTest extends ProjectWizardTestCase {
+
+ public void testPluginProject() throws Exception {
+ createSdk("devkit", IdeaJdk.getInstance());
+ Project project = createProjectFromTemplate(JavaModuleType.JAVA_GROUP, PluginModuleType.getInstance().getName(), null);
+ VirtualFile baseDir = project.getBaseDir();
+ VirtualFile virtualFile = VfsUtil.findRelativeFile("META-INF/plugin.xml", baseDir);
+ assertNotNull(virtualFile);
+ }
+
+ public void testProjectWithoutSdk() throws Exception {
+ try {
+ createProjectFromTemplate(JavaModuleType.JAVA_GROUP, PluginModuleType.getInstance().getName(), null);
+ fail("Exception should be thrown");
+ }
+ catch (Exception e) {
+ assertEquals(IdeBundle.message("prompt.confirm.project.no.jdk"), e.getMessage());
+ }
+ }
+}
diff --git a/plugins/devkit/testSources/build/GenerateAntTest.java b/plugins/devkit/testSources/build/GenerateAntTest.java
new file mode 100644
index 0000000..fa0f30e
--- /dev/null
+++ b/plugins/devkit/testSources/build/GenerateAntTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * User: anna
+ * Date: 20-Dec-2006
+ */
+package org.jetbrains.idea.devkit.build;
+
+import com.intellij.compiler.ant.BuildTargetsFactory;
+import com.intellij.compiler.ant.ModuleChunk;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.XmlElementFactory;
+import com.intellij.psi.codeStyle.CodeStyleManager;
+import com.intellij.psi.xml.XmlAttribute;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.testFramework.IdeaTestCase;
+import com.intellij.testFramework.PsiTestUtil;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.idea.devkit.build.ant.BuildJarTarget;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+public class GenerateAntTest extends IdeaTestCase {
+
+ public void testP1() throws Exception {
+ final VirtualFile parent = myModule.getModuleFile().getParent();
+ assertNotNull(parent);
+ PsiTestUtil.setCompilerOutputPath(myModule, parent.getUrl() + "/classes", false);
+ checkJarTarget(new ModuleChunk(new Module[]{getModule()}));
+ }
+
+ private void checkJarTarget(ModuleChunk chunk) throws Exception {
+ final StringWriter targetText = new StringWriter();
+ final PrintWriter dataOutput = new PrintWriter(targetText);
+ new BuildJarTarget(chunk, BuildTargetsFactory.getInstance().getDefaultOptions(getProject()), new PluginBuildConfiguration(getModule())).generate(dataOutput);
+ dataOutput.flush();
+ final String lowercased = StringUtil.toLowerCase(myModule.getName());
+ final String expected = "<target name=\"plugin.build.jar."+
+ lowercased + "\" depends=\"compile.module." + lowercased +
+ "\" description=\"Build plugin archive for module '" + myModule.getName() + "'\">\n" +
+ " <jar destfile=\"${"+ lowercased + ".plugin.path.jar}\" duplicate=\"preserve\">\n" +
+ " <zipfileset dir=\"${module." + lowercased + ".basedir}/classes\"/>\n" +
+ " <zipfileset file=\"${module." + lowercased + ".basedir}/META-INF/plugin.xml\" prefix=\"META-INF\"/>\n" +
+ " <manifest>\n" +
+ " <attribute name=\"Created-By\" value=\"IntelliJ IDEA\"/>\n" +
+ " <attribute name=\"Manifest-Version\" value=\"1.0\"/>\n" +
+ " </manifest>\n" +
+ " </jar>\n" +
+ "</target>";
+ checkBuildsEqual(targetText.toString(), expected);
+ }
+
+ private void checkBuildsEqual(String generated, String expected) throws IncorrectOperationException {
+ final CodeStyleManager manager = CodeStyleManager.getInstance(myProject);
+ XmlTag genTag = XmlElementFactory.getInstance(myProject).createTagFromText(StringUtil.convertLineSeparators(generated));
+ XmlTag expTag = XmlElementFactory.getInstance(myProject).createTagFromText(StringUtil.convertLineSeparators(expected));
+ if (!tagsEqual(genTag, expTag)) {
+ genTag = (XmlTag)manager.reformat(manager.reformat(genTag));
+ expTag = (XmlTag)manager.reformat(manager.reformat(expTag));
+ assertEquals("Text mismatch: ", expTag.getText(), genTag.getText());
+ }
+ }
+
+ private static boolean tagsEqual(XmlTag genTag, XmlTag expTag) {
+ if (!attributesEqual(genTag, expTag)) return false;
+ final XmlTag[] gsubTags = genTag.getSubTags();
+ final XmlTag[] esubTags = expTag.getSubTags();
+ if (gsubTags.length != esubTags.length) return false;
+ for (int i = 0; i < esubTags.length; i++) {
+ XmlTag esubTag = esubTags[i];
+ XmlTag gsubTag = gsubTags[i];
+ if (!tagsEqual(gsubTag, esubTag)) return false;
+ }
+ return true;
+ }
+
+ private static boolean attributesEqual(XmlTag genTag, XmlTag expTag) {
+ final XmlAttribute[] gattributes = genTag.getAttributes();
+ final XmlAttribute[] eattributes = expTag.getAttributes();
+ if (gattributes.length != eattributes.length) return false;
+ for (int i = 0; i < eattributes.length; i++) {
+ XmlAttribute eattribute = eattributes[i];
+ XmlAttribute gattribute = gattributes[i];
+ // logical comparison of the attributes (namespace:localname and display value)
+ if (!Comparing.strEqual(gattribute.getLocalName(), eattribute.getLocalName())) return false;
+ if (!Comparing.strEqual(gattribute.getNamespace(), eattribute.getNamespace())) return false;
+ if (!Comparing.strEqual(gattribute.getDisplayValue(), eattribute.getDisplayValue())) return false;
+ }
+ return true;
+ }
+}
diff --git a/plugins/devkit/testSources/build/PluginModuleCompilationTest.java b/plugins/devkit/testSources/build/PluginModuleCompilationTest.java
new file mode 100644
index 0000000..6053792
--- /dev/null
+++ b/plugins/devkit/testSources/build/PluginModuleCompilationTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.build;
+
+import com.intellij.compiler.BaseCompilerTestCase;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.impl.ModuleImpl;
+import com.intellij.openapi.projectRoots.ProjectJdkTable;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkModificator;
+import com.intellij.openapi.projectRoots.SdkType;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.io.FileUtilRt;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import org.jetbrains.idea.devkit.projectRoots.IdeaJdk;
+import org.jetbrains.idea.devkit.projectRoots.Sandbox;
+
+import java.io.File;
+import java.util.Arrays;
+
+import static com.intellij.util.io.TestFileSystemBuilder.fs;
+
+/**
+ * @author nik
+ */
+public class PluginModuleCompilationTest extends BaseCompilerTestCase {
+ private Sdk myPluginSdk;
+
+ @Override
+ protected void setUpJdk() {
+ super.setUpJdk();
+ new WriteAction() {
+ protected void run(final Result result) {
+ ProjectJdkTable table = ProjectJdkTable.getInstance();
+ myPluginSdk = table.createSdk("IDEA plugin SDK", SdkType.findInstance(IdeaJdk.class));
+ SdkModificator modificator = myPluginSdk.getSdkModificator();
+ modificator.setSdkAdditionalData(new Sandbox(getSandboxPath(), getTestProjectJdk(), myPluginSdk));
+ String rootPath = FileUtil.toSystemIndependentName(PathManager.getJarPathForClass(FileUtilRt.class));
+ modificator.addRoot(LocalFileSystem.getInstance().refreshAndFindFileByPath(rootPath), OrderRootType.CLASSES);
+ modificator.commitChanges();
+ table.addJdk(myPluginSdk);
+ }
+ }.execute();
+ }
+
+ private String getSandboxPath() {
+ return getProjectBasePath() + "/sandbox";
+ }
+
+ @Override
+ protected boolean useExternalCompiler() {
+ return true;
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ new WriteAction() {
+ protected void run(final Result result) {
+ ProjectJdkTable.getInstance().removeJdk(myPluginSdk);
+ }
+ }.execute();
+
+ super.tearDown();
+ }
+
+ public void testMakeModule() {
+ Module module = setupPluginProject();
+ make(module);
+ assertOutput(module, fs().dir("xxx").file("MyAction.class"));
+
+ File sandbox = new File(FileUtil.toSystemDependentName(getSandboxPath()));
+ assertTrue(sandbox.exists());
+ fs().dir("plugins")
+ .dir("pluginProject")
+ .dir("META-INF").file("plugin.xml").end()
+ .dir("classes")
+ .dir("xxx").file("MyAction.class")
+ .build().assertDirectoryEqual(sandbox);
+ }
+
+ public void testRebuild() {
+ setupPluginProject();
+ CompilationLog log = rebuild();
+ assertTrue("Rebuild finished with warnings: " + Arrays.toString(log.getWarnings()), log.getWarnings().length == 0);
+ }
+
+ private Module setupPluginProject() {
+ copyToProject("plugins/devkit/testData/build/simple");
+ Module module = loadModule(getProjectBasePath() + "/pluginProject.iml");
+ readJdomExternalizables((ModuleImpl)module);
+ return module;
+ }
+}
diff --git a/plugins/devkit/testSources/codeInsight/CreateClassFixTest.java b/plugins/devkit/testSources/codeInsight/CreateClassFixTest.java
new file mode 100644
index 0000000..9391039
--- /dev/null
+++ b/plugins/devkit/testSources/codeInsight/CreateClassFixTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * User: anna
+ * Date: 14-Jun-2007
+ */
+package org.jetbrains.idea.devkit.codeInsight;
+
+import com.intellij.codeInsight.daemon.QuickFixBundle;
+import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.openapi.application.PluginPathManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.testFramework.builders.JavaModuleFixtureBuilder;
+import com.intellij.testFramework.fixtures.CodeInsightTestFixture;
+import com.intellij.testFramework.fixtures.IdeaProjectTestFixture;
+import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory;
+import com.intellij.testFramework.fixtures.TestFixtureBuilder;
+import org.jetbrains.idea.devkit.inspections.RegistrationProblemsInspection;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.util.List;
+
+public class CreateClassFixTest {
+ protected CodeInsightTestFixture myFixture;
+
+ @BeforeMethod
+ public void setUp() throws Exception {
+ final IdeaTestFixtureFactory fixtureFactory = IdeaTestFixtureFactory.getFixtureFactory();
+ final TestFixtureBuilder<IdeaProjectTestFixture> testFixtureBuilder = fixtureFactory.createFixtureBuilder();
+ myFixture = fixtureFactory.createCodeInsightFixture(testFixtureBuilder.getFixture());
+ myFixture.setTestDataPath(PluginPathManager.getPluginHomePath("devkit") + "/testData");
+
+ testFixtureBuilder.addModule(JavaModuleFixtureBuilder.class)
+ .addContentRoot(myFixture.getTempDirPath()).addSourceRoot(getSourceRoot());
+ myFixture.enableInspections(new RegistrationProblemsInspection());
+ myFixture.setUp();
+ }
+
+ @AfterMethod
+ public void tearDown() throws Exception {
+ myFixture.tearDown();
+ myFixture = null;
+ }
+
+ private static String getSourceRoot() {
+ return "codeInsight";
+ }
+
+ @DataProvider
+ public Object[][] data() {
+ return new Object[][]{{"Action", true}, {"Impl", true}, {"Intf", true}, {"Intf", false}};
+ }
+
+
+ @Test(dataProvider = "data", enabled = false)
+ public void test(String testName, boolean createClass) throws Throwable {
+ IntentionAction resultAction = null;
+ final String createAction = QuickFixBundle.message(createClass ? "create.class.text" : "create.interface.text", testName);
+ final List<IntentionAction> actions = myFixture.getAvailableIntentions(getSourceRoot() + "/plugin" + testName + ".xml");
+ for (IntentionAction action : actions) {
+ if (Comparing.strEqual(action.getText(), createAction)) {
+ resultAction = action;
+ break;
+ }
+ }
+ Assert.assertNotNull(resultAction);
+ myFixture.launchAction(resultAction);
+ final Project project = myFixture.getProject();
+ Assert.assertNotNull(JavaPsiFacade.getInstance(project).findClass(testName, GlobalSearchScope.allScope(project)));
+ }
+}
diff --git a/plugins/devkit/testSources/codeInsight/CreateExtensionTest.java b/plugins/devkit/testSources/codeInsight/CreateExtensionTest.java
new file mode 100644
index 0000000..41d5b96
--- /dev/null
+++ b/plugins/devkit/testSources/codeInsight/CreateExtensionTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.codeInsight;
+
+import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection;
+import com.intellij.codeInspection.unusedSymbol.UnusedSymbolLocalInspection;
+import com.intellij.openapi.application.PluginPathManager;
+import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
+
+/**
+ * @author Dmitry Avdeev
+ * Date: 1/20/12
+ */
+public class CreateExtensionTest extends JavaCodeInsightFixtureTestCase {
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ myFixture.addClass("package com.intellij.codeInspection; public class LocalInspectionTool {} ");
+ myFixture.addClass("package com.intellij.codeInspection; public class GlobalInspectionTool {} ");
+ myFixture.enableInspections(new UnusedDeclarationInspection(), new UnusedSymbolLocalInspection());
+ }
+
+ public void testCreateLocalInspectionMapping() throws Exception {
+ myFixture.testHighlighting("LocalInspection.java", "plugin.xml");
+ IntentionAction intention = myFixture.findSingleIntention("Register inspection");
+ myFixture.launchAction(intention);
+ myFixture.checkResultByFile("plugin.xml", "localInspection.xml", true);
+ }
+
+ public void testCreateGlobalInspectionMapping() throws Exception {
+ myFixture.testHighlighting("GlobalInspection.java", "localInspection.xml");
+ IntentionAction intention = myFixture.findSingleIntention("Register inspection");
+ myFixture.launchAction(intention);
+ myFixture.checkResultByFile("localInspection.xml", "globalInspection.xml", true);
+ }
+
+ @Override
+ protected String getBasePath() {
+ return PluginPathManager.getPluginHomePathRelative("devkit") + "/testData/extensions";
+ }
+
+}
diff --git a/plugins/devkit/testSources/codeInsight/ExtensionsTest.java b/plugins/devkit/testSources/codeInsight/ExtensionsTest.java
new file mode 100644
index 0000000..eff87a8
--- /dev/null
+++ b/plugins/devkit/testSources/codeInsight/ExtensionsTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.codeInsight;
+
+import com.intellij.codeInspection.LocalInspectionEP;
+import com.intellij.openapi.application.PluginPathManager;
+import com.intellij.testFramework.builders.JavaModuleFixtureBuilder;
+import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
+import com.intellij.util.PathUtil;
+
+/**
+ * @author Dmitry Avdeev
+ * @since 10/11/11
+ */
+public class ExtensionsTest extends JavaCodeInsightFixtureTestCase {
+ public void testInspectionMappings() throws Throwable {
+ myFixture.testHighlighting("inspectionMapping.xml", "bundle.properties");
+ }
+
+ public void testInspectionMappingsWithDefaultBundle() throws Throwable {
+ myFixture.testHighlighting("inspectionMappingWithDefaultBundle.xml", "bundle.properties");
+ }
+
+ @Override
+ protected void tuneFixture(JavaModuleFixtureBuilder moduleBuilder) throws Exception {
+ String pathForClass = PathUtil.getJarPathForClass(LocalInspectionEP.class);
+ moduleBuilder.addLibrary("lang-api", pathForClass);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ myFixture.enableInspections(PluginXmlFunctionalTest.getInspectionClasses());
+ }
+
+ @Override
+ protected String getBasePath() {
+ return PluginPathManager.getPluginHomePathRelative("devkit") + "/testData/codeInsight";
+ }
+}
diff --git a/plugins/devkit/testSources/codeInsight/PluginXmlAutoPopupTest.groovy b/plugins/devkit/testSources/codeInsight/PluginXmlAutoPopupTest.groovy
new file mode 100644
index 0000000..46a5a83
--- /dev/null
+++ b/plugins/devkit/testSources/codeInsight/PluginXmlAutoPopupTest.groovy
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.codeInsight
+
+import com.intellij.codeInsight.completion.CompletionAutoPopupTestCase
+
+/**
+ * @author peter
+ */
+class PluginXmlAutoPopupTest extends CompletionAutoPopupTestCase {
+
+ public void "test autopopup for class references"() {
+ myFixture.addClass("public class FooFooFooFooFoo { }");
+ myFixture.configureByText 'plugin.xml', '''
+<idea-plugin>
+ <extensionPoints>
+ <extensionPoint name="FooFooFooFooFox" interface="FFFF<caret>"/>
+ </extensionPoints>
+</idea-plugin>
+'''
+ type 'o'
+ assert lookup
+ assert myFixture.lookupElementStrings == ['FooFooFooFooFoo']
+ }
+
+ public void "test no autopopup when only word completion is available"() {
+ myFixture.configureByText 'plugin.xml', '''
+<idea-plugin>
+ <extensionPoints>
+ <extensionPoint name="<caret>"/>
+ </extensionPoints>
+</idea-plugin>
+'''
+ type 'e'
+ assert !lookup
+ }
+
+}
diff --git a/plugins/devkit/testSources/codeInsight/PluginXmlFunctionalTest.groovy b/plugins/devkit/testSources/codeInsight/PluginXmlFunctionalTest.groovy
new file mode 100644
index 0000000..25c19d6
--- /dev/null
+++ b/plugins/devkit/testSources/codeInsight/PluginXmlFunctionalTest.groovy
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.idea.devkit.codeInsight
+import com.intellij.codeInsight.TargetElementUtilBase
+import com.intellij.codeInsight.completion.CompletionType
+import com.intellij.codeInspection.LocalInspectionTool
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.application.PluginPathManager
+import com.intellij.psi.ElementDescriptionUtil
+import com.intellij.psi.PsiElement
+import com.intellij.testFramework.PsiTestUtil
+import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory
+import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase
+import com.intellij.testFramework.fixtures.TempDirTestFixture
+import com.intellij.usageView.UsageViewNodeTextLocation
+import com.intellij.usageView.UsageViewTypeLocation
+import com.intellij.util.xml.DeprecatedClassUsageInspection
+import org.jetbrains.idea.devkit.inspections.*
+/**
+ * @author peter
+ */
+public class PluginXmlFunctionalTest extends JavaCodeInsightFixtureTestCase {
+ private TempDirTestFixture myTempDirFixture;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ myTempDirFixture = IdeaTestFixtureFactory.getFixtureFactory().createTempDirTestFixture();
+ myFixture.enableInspections(getInspectionClasses());
+ }
+
+ @Override
+ protected String getBasePath() {
+ return PluginPathManager.getPluginHomePathRelative("devkit") + "/testData/codeInsight";
+ }
+
+ public void testExtensionsHighlighting() throws Throwable {
+ final String root = "idea_core";
+ addPluginXml(root, "<idea-plugin>\n" +
+ " <id>com.intellij</id>\n" +
+ " <extensionPoints>\n" +
+ " <extensionPoint name=\"completion.contributor\"/>\n" +
+ " </extensionPoints>\n" +
+ "</idea-plugin>");
+ addPluginXml("indirect", "<idea-plugin>\n" +
+ " <id>com.intellij.indirect</id>\n" +
+ " <extensionPoints>\n" +
+ " <extensionPoint name=\"indirect\"/>\n" +
+ " </extensionPoints>\n" +
+ "</idea-plugin>");
+ addPluginXml("custom", "<idea-plugin>\n" +
+ " <id>com.intellij.custom</id>\n" +
+ " <depends>com.intellij.indirect</depends>\n" +
+ " <extensionPoints>\n" +
+ " <extensionPoint name=\"custom\"/>\n" +
+ " </extensionPoints>\n" +
+ "</idea-plugin>");
+
+ configureByFile();
+ myFixture.checkHighlighting(false, false, false);
+ }
+
+ public void testDependsHighlighting() throws Throwable {
+ final String root = "idea_core";
+ addPluginXml(root, "<idea-plugin>\n" +
+ " <id>com.intellij</id>\n" +
+ " <module value=\"com.intellij.modules.vcs\"/>\n" +
+ "</idea-plugin>");
+ addPluginXml("custom", "<idea-plugin>\n" +
+ " <id>com.intellij.custom</id>\n" +
+ "</idea-plugin>");
+
+ configureByFile();
+ myFixture.checkHighlighting(false, false, false);
+ }
+
+ private void configureByFile() {
+ myFixture.configureFromExistingVirtualFile(myFixture.copyFileToProject(getTestName(false) + ".xml", "META-INF/plugin.xml"));
+ }
+
+ public void testExtensionQualifiedName() throws Throwable {
+ configureByFile();
+ myFixture.checkHighlighting(false, false, false);
+ }
+
+ public void testInnerClassCompletion() throws Throwable {
+ myFixture.addClass("package foo; public class Foo { public static class Fubar {} }");
+ myFixture.configureByFile(getTestName(false) + ".xml");
+ myFixture.completeBasic();
+ myFixture.type('\n');
+ myFixture.checkResultByFile(getTestName(false) + "_after.xml");
+ }
+
+ public void testResolveExtensionsFromDependentDescriptor() throws Throwable {
+ addPluginXml("xxx", "<idea-plugin>\n" +
+ " <id>com.intellij.xxx</id>\n" +
+ " <extensionPoints>\n" +
+ " <extensionPoint name=\"completion.contributor\"/>\n" +
+ " </extensionPoints>\n" +
+ "</idea-plugin>");
+
+ myFixture.copyFileToProject(getTestName(false) + "_main.xml", "META-INF/plugin.xml");
+ myFixture.configureFromExistingVirtualFile(myFixture.copyFileToProject(getTestName(false) + "_dependent.xml", "META-INF/dep.xml"));
+ myFixture.checkHighlighting(false, false, false);
+ }
+
+ private void addPluginXml(final String root, final String text) throws IOException {
+ myTempDirFixture.createFile(root + "/META-INF/plugin.xml", text);
+ ApplicationManager.application.runWriteAction { PsiTestUtil.addSourceContentToRoots(myModule, myTempDirFixture.getFile(root)) }
+ }
+
+ public void testNoWordCompletionInClassPlaces() throws Throwable {
+ myFixture.addClass("package foo; public class FooFooFooFooFoo { }");
+ myFixture.addClass("package foo; public interface ExtIntf { }");
+
+ myFixture.configureByFile(getTestName(false) + ".xml");
+ myFixture.completeBasic();
+ myFixture.type('\'');
+ myFixture.checkResultByFile(getTestName(false) + "_after.xml");
+ }
+
+ public void testNoClassCompletionOutsideJavaReferences() throws Throwable {
+ myFixture.addClass("package foo; public class FooFooFooFooFoo { }");
+
+ myFixture.configureByFile(getTestName(false) + ".xml");
+ myFixture.completeBasic();
+ myFixture.checkResultByFile(getTestName(false) + "_after.xml");
+ }
+
+ public void testShowPackagesInActionClass() {
+ myFixture.addClass("package com.intellij.openapi.actionSystem; public class AnAction { }");
+ myFixture.addClass("package foo.bar; public class BarAction extends com.intellij.openapi.actionSystem.AnAction { }");
+ myFixture.addClass("package foo.goo; public class GooAction extends com.intellij.openapi.actionSystem.AnAction { }");
+ myFixture.configureByFile(getTestName(false) + ".xml");
+ myFixture.completeBasic();
+ assert myFixture.lookupElementStrings == ['bar', 'goo']
+ assert myFixture.lookup.advertisements.find { it.contains('to see inheritors of com.intellij.openapi.actionSystem.AnAction') }
+ }
+
+ public void testShowAnActionInheritorsOnSmartCompletion() {
+ myFixture.addClass("package com.intellij.openapi.actionSystem; public class AnAction { }");
+ myFixture.addClass("package foo.bar; public class BarAction extends com.intellij.openapi.actionSystem.AnAction { }");
+ myFixture.addClass("package foo.goo; public class GooAction extends com.intellij.openapi.actionSystem.AnAction { }");
+ myFixture.addClass("package another.goo; public class AnotherAction extends com.intellij.openapi.actionSystem.AnAction { }");
+ myFixture.configureByFile(getTestName(false) + ".xml");
+ myFixture.complete(CompletionType.SMART);
+ assert myFixture.lookupElementStrings == ['foo.bar.BarAction', 'foo.goo.GooAction']
+ assert !myFixture.lookup.advertisements.find { it.contains('to see inheritors of com.intellij.openapi.actionSystem.AnAction') }
+ }
+
+ public void testDeprecatedExtensionAttribute() {
+ myFixture.enableInspections(DeprecatedClassUsageInspection.class);
+ myFixture.testHighlighting("deprecatedExtensionAttribute.xml", "MyExtBean.java");
+ }
+
+ public void testExtensionAttributeDeclaredUsingAccessors() {
+ myFixture.testHighlighting("extensionAttributeWithAccessors.xml", "ExtBeanWithAccessors.java");
+ }
+
+ public void testPluginModule() throws Throwable {
+ myFixture.testHighlighting("pluginWithModules.xml");
+ }
+
+ public void testPluginWithModules() throws Throwable {
+ myFixture.testHighlighting("pluginWithModules.xml");
+ }
+
+ public void testPluginWithXInclude() throws Throwable {
+ myFixture.testHighlighting("pluginWithXInclude.xml", "extensionPoints.xml");
+ }
+
+ public void testExtensionPointPresentation() {
+ myFixture.configureByFile(getTestName(true) + ".xml");
+ final PsiElement element =
+ TargetElementUtilBase.findTargetElement(myFixture.getEditor(), TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED);
+ assert element != null;
+ assertEquals("Extension Point", ElementDescriptionUtil.getElementDescription(element, UsageViewTypeLocation.INSTANCE));
+ assertEquals("Extension Point bar", ElementDescriptionUtil.getElementDescription(element, UsageViewNodeTextLocation.INSTANCE));
+ }
+
+ public void testLoadForDefaultProject() throws Exception {
+ configureByFile();
+ myFixture.testHighlighting(true, true, true);
+ }
+
+ static Collection<Class<? extends LocalInspectionTool>> getInspectionClasses() {
+ return Arrays.asList(
+ //RegistrationProblemsInspection.class,
+ PluginXmlDomInspection.class,
+ ComponentNotRegisteredInspection.class,
+ InspectionDescriptionNotFoundInspection.class,
+ IntentionDescriptionNotFoundInspection.class,
+ InspectionMappingConsistencyInspection.class
+ );
+ }
+}
diff --git a/plugins/devkit/testSources/testng.xml b/plugins/devkit/testSources/testng.xml
new file mode 100644
index 0000000..fd97179
--- /dev/null
+++ b/plugins/devkit/testSources/testng.xml
@@ -0,0 +1,8 @@
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="DevKit Suite">
+ <test name="DevKit CodeInsight Tests">
+ <classes>
+ <class name="org.jetbrains.idea.devkit.codeInsight.CreateClassFixTest"/>
+ </classes>
+ </test>
+</suite>
\ No newline at end of file