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="&lt;inspection&gt; 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 &apos;" + myModule.getName() + "&apos;\">\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