Snapshot of commit d5ec1d5018ed24f1b4f32b1d09df6dbd7e2fc425

from branch master of git://git.jetbrains.org/idea/community.git
diff --git a/java/compiler/forms-compiler/forms-compiler.iml b/java/compiler/forms-compiler/forms-compiler.iml
new file mode 100644
index 0000000..1a851bf
--- /dev/null
+++ b/java/compiler/forms-compiler/forms-compiler.iml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_4" 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="library" name="JDOM" level="project" />
+    <orderEntry type="module" module-name="forms_rt" />
+    <orderEntry type="library" name="asm4" level="project" />
+    <orderEntry type="library" scope="TEST" name="JUnit3" level="project" />
+    <orderEntry type="library" name="jgoodies-forms" level="project" />
+    <orderEntry type="module" module-name="instrumentation-util" exported="" />
+  </component>
+</module>
+
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/AlienFormFileException.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/AlienFormFileException.java
new file mode 100644
index 0000000..f3b2eed
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/AlienFormFileException.java
@@ -0,0 +1,22 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+public final class AlienFormFileException extends UIDesignerException{
+  public AlienFormFileException(){
+    super("Not IntelliJ IDEA GUI Designer form file");
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/AsmCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/AsmCodeGenerator.java
new file mode 100644
index 0000000..6b782b4
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/AsmCodeGenerator.java
@@ -0,0 +1,988 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.compiler.instrumentation.InstrumentationClassFinder;
+import com.intellij.uiDesigner.UIFormXmlConstants;
+import com.intellij.uiDesigner.lw.*;
+import com.intellij.uiDesigner.shared.BorderType;
+import org.jetbrains.asm4.*;
+import org.jetbrains.asm4.Label;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+import javax.swing.*;
+import javax.swing.border.Border;
+import java.awt.*;
+import java.io.*;
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+/**
+ * @author yole
+ */
+public class AsmCodeGenerator {
+  private final LwRootContainer myRootContainer;
+  private final InstrumentationClassFinder myFinder;
+  private final ArrayList myErrors;
+  private final ArrayList myWarnings;
+
+  private final Map myIdToLocalMap = new HashMap();
+
+  private static final String CONSTRUCTOR_NAME = "<init>";
+  private String myClassToBind;
+  private byte[] myPatchedData;
+
+  private static final Map myContainerLayoutCodeGenerators = new HashMap();
+  private static final Map myComponentLayoutCodeGenerators = new HashMap();
+  private static final Map myPropertyCodeGenerators = new LinkedHashMap();  // need LinkedHashMap for deterministic iteration
+  public static final String SETUP_METHOD_NAME = "$$$setupUI$$$";
+  public static final String GET_ROOT_COMPONENT_METHOD_NAME = "$$$getRootComponent$$$";
+  public static final String CREATE_COMPONENTS_METHOD_NAME = "createUIComponents";
+  public static final String LOAD_LABEL_TEXT_METHOD = "$$$loadLabelText$$$";
+  public static final String LOAD_BUTTON_TEXT_METHOD = "$$$loadButtonText$$$";
+
+  private static final Type ourButtonGroupType = Type.getType(ButtonGroup.class);
+  private static final Type ourBorderFactoryType = Type.getType(BorderFactory.class);
+  private static final Type ourBorderType = Type.getType(Border.class);
+  private static final Method ourCreateTitledBorderMethod = Method.getMethod(
+    "javax.swing.border.TitledBorder createTitledBorder(javax.swing.border.Border,java.lang.String,int,int,java.awt.Font,java.awt.Color)");
+
+  private static final String ourBorderFactoryClientProperty = "BorderFactoryClass";
+
+  private final NestedFormLoader myFormLoader;
+  private final boolean myIgnoreCustomCreation;
+  private final ClassWriter myClassWriter;
+
+  static {
+    myContainerLayoutCodeGenerators.put(UIFormXmlConstants.LAYOUT_INTELLIJ, new GridLayoutCodeGenerator());
+    myContainerLayoutCodeGenerators.put(UIFormXmlConstants.LAYOUT_GRIDBAG, new GridBagLayoutCodeGenerator());
+    myContainerLayoutCodeGenerators.put(UIFormXmlConstants.LAYOUT_BORDER, new SimpleLayoutCodeGenerator(Type.getType(BorderLayout.class)));
+    myContainerLayoutCodeGenerators.put(UIFormXmlConstants.LAYOUT_CARD, new CardLayoutCodeGenerator());
+    myContainerLayoutCodeGenerators.put(UIFormXmlConstants.LAYOUT_FLOW, new FlowLayoutCodeGenerator());
+
+    myComponentLayoutCodeGenerators.put(LwSplitPane.class, new SplitPaneLayoutCodeGenerator());
+    myComponentLayoutCodeGenerators.put(LwTabbedPane.class, new TabbedPaneLayoutCodeGenerator());
+    myComponentLayoutCodeGenerators.put(LwScrollPane.class, new ScrollPaneLayoutCodeGenerator());
+    myComponentLayoutCodeGenerators.put(LwToolBar.class, new ToolBarLayoutCodeGenerator());
+
+    myPropertyCodeGenerators.put(String.class.getName(), new StringPropertyCodeGenerator());
+    myPropertyCodeGenerators.put(Dimension.class.getName(), new DimensionPropertyCodeGenerator());
+    myPropertyCodeGenerators.put(Insets.class.getName(), new InsetsPropertyCodeGenerator());
+    myPropertyCodeGenerators.put(Rectangle.class.getName(), new RectanglePropertyCodeGenerator());
+    myPropertyCodeGenerators.put(Color.class.getName(), new ColorPropertyCodeGenerator());
+    myPropertyCodeGenerators.put(Font.class.getName(), new FontPropertyCodeGenerator());
+    myPropertyCodeGenerators.put(Icon.class.getName(), new IconPropertyCodeGenerator());
+    myPropertyCodeGenerators.put(ListModel.class.getName(), new ListModelPropertyCodeGenerator(DefaultListModel.class));
+    myPropertyCodeGenerators.put(ComboBoxModel.class.getName(), new ListModelPropertyCodeGenerator(DefaultComboBoxModel.class));
+    myPropertyCodeGenerators.put("java.lang.Enum", new EnumPropertyCodeGenerator());
+  }
+
+  public AsmCodeGenerator(LwRootContainer rootContainer,
+                          InstrumentationClassFinder finder,
+                          NestedFormLoader formLoader,
+                          final boolean ignoreCustomCreation,
+                          final ClassWriter classWriter) {
+    myFormLoader = formLoader;
+    myIgnoreCustomCreation = ignoreCustomCreation;
+    if (finder == null){
+      throw new IllegalArgumentException("loader cannot be null");
+    }
+    if (rootContainer == null){
+      throw new IllegalArgumentException("rootContainer cannot be null");
+    }
+    myRootContainer = rootContainer;
+    myFinder = finder;
+
+    myErrors = new ArrayList();
+    myWarnings = new ArrayList();
+    myClassWriter = classWriter;
+  }
+
+  public void patchFile(final File classFile) {
+    if (!classFile.exists()) {
+      myErrors.add(new FormErrorInfo(null, "Class to bind does not exist: " + myRootContainer.getClassToBind()));
+      return;
+    }
+
+    FileInputStream fis;
+    try {
+      byte[] patchedData;
+      fis = new FileInputStream(classFile);
+      try {
+        patchedData = patchClass(fis);
+        if (patchedData == null) {
+          return;
+        }
+      }
+      finally {
+        fis.close();
+      }
+
+      FileOutputStream fos = new FileOutputStream(classFile);
+      try {
+        fos.write(patchedData);
+      }
+      finally {
+        fos.close();
+      }
+    }
+    catch (IOException e) {
+      myErrors.add(new FormErrorInfo(null, "Cannot read or write class file " + classFile.getPath() + ": " + e.toString()));
+    }
+    catch(IllegalStateException e) {
+      myErrors.add(new FormErrorInfo(null, "Unexpected data in form file when patching class " + classFile.getPath() + ": " + e.toString()));
+    }
+  }
+
+  public byte[] patchClass(InputStream classStream) {
+    try {
+      final ClassReader reader = new ClassReader(classStream);
+      return patchClass(reader);
+    }
+    catch (IOException e) {
+      myErrors.add(new FormErrorInfo(null, "Error reading class data stream"));
+      return null;
+    }
+  }
+
+  public byte[] patchClass(ClassReader reader) {
+    myClassToBind = myRootContainer.getClassToBind();
+    if (myClassToBind == null){
+      myWarnings.add(new FormErrorInfo(null, "No class to bind specified"));
+      return null;
+    }
+
+    if (myRootContainer.getComponentCount() != 1) {
+      myErrors.add(new FormErrorInfo(null, "There should be only one component at the top level"));
+      return null;
+    }
+
+    String nonEmptyPanel = Utils.findNotEmptyPanelWithXYLayout(myRootContainer.getComponent(0));
+    if (nonEmptyPanel != null) {
+      myErrors.add(new FormErrorInfo(nonEmptyPanel,
+                                     "There are non empty panels with XY layout. Please lay them out in a grid."));
+      return null;
+    }
+
+    FirstPassClassVisitor visitor = new FirstPassClassVisitor();
+    reader.accept(visitor, 0);
+
+    reader.accept(new FormClassVisitor(myClassWriter, visitor.isExplicitSetupCall()), 0);
+    myPatchedData = myClassWriter.toByteArray();
+    return myPatchedData;
+  }
+
+  public FormErrorInfo[] getErrors() {
+    return (FormErrorInfo[])myErrors.toArray(new FormErrorInfo[myErrors.size()]);
+  }
+
+  public FormErrorInfo[] getWarnings() {
+    return (FormErrorInfo[])myWarnings.toArray(new FormErrorInfo[myWarnings.size()]);
+  }
+
+  public byte[] getPatchedData() {
+    return myPatchedData;
+  }
+
+  static void pushPropValue(GeneratorAdapter generator, String propertyClass, Object value) {
+    PropertyCodeGenerator codeGen = (PropertyCodeGenerator)myPropertyCodeGenerators.get(propertyClass);
+    if (codeGen == null) {
+      throw new RuntimeException("Unknown property class " + propertyClass);
+    }
+    codeGen.generatePushValue(generator, value);
+  }
+
+  static InstrumentationClassFinder.PseudoClass getComponentClass(String className, final InstrumentationClassFinder finder) throws CodeGenerationException {
+    try {
+      return finder.loadClass(className);
+    }
+    catch (ClassNotFoundException e) {
+      throw new CodeGenerationException(null, "Class not found: " + className);
+    }
+    catch(UnsupportedClassVersionError e) {
+      throw new CodeGenerationException(null, "Unsupported class version error: " + className);
+    }
+    catch (IOException e) {
+      throw new CodeGenerationException(null, e.getMessage(), e);
+    }
+  }
+
+  public static Type typeFromClassName(final String className) {
+    return Type.getType("L" + className.replace('.', '/') + ";");
+  }
+
+  class FormClassVisitor extends ClassVisitor {
+    private String myClassName;
+    private String mySuperName;
+    private final Map myFieldDescMap = new HashMap();
+    private final Map myFieldAccessMap = new HashMap();
+    private boolean myHaveCreateComponentsMethod = false;
+    private int myCreateComponentsAccess;
+    private final boolean myExplicitSetupCall;
+
+    public FormClassVisitor(final ClassVisitor cv, final boolean explicitSetupCall) {
+      super(Opcodes.ASM4, cv);
+      myExplicitSetupCall = explicitSetupCall;
+    }
+
+    public void visit(final int version,
+                      final int access,
+                      final String name,
+                      final String signature,
+                      final String superName,
+                      final String[] interfaces) {
+      super.visit(version, access, name, signature, superName, interfaces);
+      myClassName = name;
+      mySuperName = superName;
+
+      for (Iterator iterator = myPropertyCodeGenerators.values().iterator(); iterator.hasNext();) {
+        PropertyCodeGenerator propertyCodeGenerator = (PropertyCodeGenerator)iterator.next();
+        propertyCodeGenerator.generateClassStart(this, name, myFinder);
+      }
+    }
+
+    public String getClassName() {
+      return myClassName;
+    }
+
+    public MethodVisitor visitMethod(final int access,
+                                     final String name,
+                                     final String desc,
+                                     final String signature,
+                                     final String[] exceptions) {
+
+      if (name.equals(SETUP_METHOD_NAME) || name.equals(GET_ROOT_COMPONENT_METHOD_NAME) ||
+          name.equals(LOAD_BUTTON_TEXT_METHOD) || name.equals(LOAD_LABEL_TEXT_METHOD)) {
+        return null;
+      }
+      if (name.equals(CREATE_COMPONENTS_METHOD_NAME) && desc.equals("()V")) {
+        myHaveCreateComponentsMethod = true;
+        myCreateComponentsAccess = access;
+      }
+
+      final MethodVisitor methodVisitor = super.visitMethod(access, name, desc, signature, exceptions);
+      if (name.equals(CONSTRUCTOR_NAME) && !myExplicitSetupCall) {
+        return new FormConstructorVisitor(methodVisitor, myClassName, mySuperName);
+      }
+      return methodVisitor;
+    }
+
+    MethodVisitor visitNewMethod(final int access,
+                                 final String name,
+                                 final String desc,
+                                 final String signature,
+                                 final String[] exceptions) {
+      return super.visitMethod(access, name, desc, signature, exceptions);
+    }
+
+    public FieldVisitor visitField(final int access, final String name, final String desc, final String signature, final Object value) {
+      myFieldDescMap.put(name, desc);
+      myFieldAccessMap.put(name, new Integer(access));
+      return super.visitField(access, name, desc, signature, value);
+    }
+
+    public void visitEnd() {
+      final boolean haveCustomCreateComponents = Utils.getCustomCreateComponentCount(myRootContainer) > 0 &&
+                                                 !myIgnoreCustomCreation;
+      if (haveCustomCreateComponents && !myHaveCreateComponentsMethod) {
+        myErrors.add(new FormErrorInfo(null, "Form contains components with Custom Create option but no createUIComponents() method"));
+      }
+
+      Method method = Method.getMethod("void " + SETUP_METHOD_NAME + " ()");
+      GeneratorAdapter generator = new GeneratorAdapter(Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC, method, null, null, cv);
+      if (haveCustomCreateComponents && myHaveCreateComponentsMethod) {
+        generator.visitVarInsn(Opcodes.ALOAD, 0);
+        int opcode = myCreateComponentsAccess == Opcodes.ACC_PRIVATE ? Opcodes.INVOKESPECIAL : Opcodes.INVOKEVIRTUAL;
+        generator.visitMethodInsn(opcode, myClassName, CREATE_COMPONENTS_METHOD_NAME, "()V");
+      }
+      buildSetupMethod(generator);
+
+      final String rootBinding = myRootContainer.getComponent(0).getBinding();
+      if (rootBinding != null && myFieldDescMap.containsKey(rootBinding)) {
+        buildGetRootComponenMethod();
+      }
+
+      for (Iterator iterator = myPropertyCodeGenerators.values().iterator(); iterator.hasNext();) {
+        PropertyCodeGenerator propertyCodeGenerator = (PropertyCodeGenerator)iterator.next();
+        propertyCodeGenerator.generateClassEnd(this);
+      }
+
+      super.visitEnd();
+    }
+
+    private void buildGetRootComponenMethod() {
+      final Type componentType = Type.getType(JComponent.class);
+      final Method method = new Method(GET_ROOT_COMPONENT_METHOD_NAME, componentType, new Type[0]);
+      GeneratorAdapter generator = new GeneratorAdapter(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, method, null, null, cv);
+
+      final LwComponent topComponent = (LwComponent)myRootContainer.getComponent(0);
+      final String binding = topComponent.getBinding();
+
+      generator.loadThis();
+      generator.getField(typeFromClassName(myClassName), binding,
+                         Type.getType((String) myFieldDescMap.get(binding)));
+      generator.returnValue();
+      generator.endMethod();
+    }
+
+    private void buildSetupMethod(final GeneratorAdapter generator) {
+      try {
+        final LwComponent topComponent = (LwComponent)myRootContainer.getComponent(0);
+        generateSetupCodeForComponent(topComponent, generator, -1);
+        generateComponentReferenceProperties(topComponent, generator);
+        generateButtonGroups(myRootContainer, generator);
+      }
+      catch (CodeGenerationException e) {
+        myErrors.add(new FormErrorInfo(e.getComponentId(), e.getMessage()));
+      }
+      generator.returnValue();
+      generator.endMethod();
+    }
+
+    private void generateSetupCodeForComponent(final LwComponent lwComponent,
+                                               final GeneratorAdapter generator,
+                                               final int parentLocal) throws CodeGenerationException {
+
+      String className;
+      if (lwComponent instanceof LwNestedForm) {
+        LwRootContainer nestedFormContainer;
+        LwNestedForm nestedForm = (LwNestedForm) lwComponent;
+        if (myFormLoader == null) {
+          throw new CodeGenerationException(null, "Attempt to compile nested form with no nested form loader specified");
+        }
+        try {
+          nestedFormContainer = myFormLoader.loadForm(nestedForm.getFormFileName());
+        }
+        catch (Exception e) {
+          throw new CodeGenerationException(lwComponent.getId(), e.getMessage());
+        }
+        // if nested form is empty, ignore
+        if (nestedFormContainer.getComponentCount() == 0) {
+          return;
+        }
+        if (nestedFormContainer.getComponent(0).getBinding() == null) {
+          throw new CodeGenerationException(lwComponent.getId(), "No binding on root component of nested form " + nestedForm.getFormFileName());
+        }
+        try {
+          Utils.validateNestedFormLoop(nestedForm.getFormFileName(), myFormLoader);
+        }
+        catch (RecursiveFormNestingException e) {
+          throw new CodeGenerationException(lwComponent.getId(), "Recursive form nesting is not allowed");
+        }
+        className = myFormLoader.getClassToBindName(nestedFormContainer);
+      }
+      else {
+        className = getComponentCodeGenerator(lwComponent.getParent()).mapComponentClass(lwComponent.getComponentClassName());
+      }
+      Type componentType = typeFromClassName(className);
+      int componentLocal = generator.newLocal(componentType);
+
+      myIdToLocalMap.put(lwComponent.getId(), new Integer(componentLocal));
+
+      InstrumentationClassFinder.PseudoClass componentClass = getComponentClass(className, myFinder);
+      validateFieldBinding(lwComponent, componentClass);
+
+      if (myIgnoreCustomCreation) {
+        try {
+          boolean creatable = true;
+          if ((componentClass.getModifiers() & (Modifier.PRIVATE | Modifier.ABSTRACT)) != 0) {
+            creatable = false;
+          }
+          else {
+            if (!componentClass.hasDefaultPublicConstructor()) {
+              creatable = false;
+            }
+          }
+          if (!creatable) {
+            componentClass = Utils.suggestReplacementClass(componentClass);
+            componentType = Type.getType(componentClass.getDescriptor());
+          }
+        }
+        catch (ClassNotFoundException e) {
+          throw new CodeGenerationException(lwComponent.getId(), e.getMessage(), e);
+        }
+        catch (IOException e) {
+          throw new CodeGenerationException(lwComponent.getId(), e.getMessage(), e);
+        }
+      }
+
+      if (!lwComponent.isCustomCreate() || myIgnoreCustomCreation) {
+        generator.newInstance(componentType);
+        generator.dup();
+        generator.invokeConstructor(componentType, Method.getMethod("void <init>()"));
+        generator.storeLocal(componentLocal);
+
+        generateFieldBinding(lwComponent, generator, componentLocal);
+      }
+      else {
+        final String binding = lwComponent.getBinding();
+        if (binding == null) {
+          throw new CodeGenerationException(lwComponent.getId(),
+                                            "Only components bound to fields can have custom creation code");
+        }
+        generator.loadThis();
+        generator.getField(getMainClassType(), binding,
+                           Type.getType((String)myFieldDescMap.get(binding)));
+        generator.storeLocal(componentLocal);
+      }
+
+      if (lwComponent instanceof LwContainer) {
+        LwContainer lwContainer = (LwContainer) lwComponent;
+        if (!lwContainer.isCustomCreate() || lwContainer.getComponentCount() > 0) {
+          getComponentCodeGenerator(lwContainer).generateContainerLayout(lwContainer, generator, componentLocal);
+        }
+      }
+
+      generateComponentProperties(lwComponent, componentClass, generator, componentLocal);
+
+      // add component to parent
+      if (!(lwComponent.getParent() instanceof LwRootContainer)) {
+        final LayoutCodeGenerator parentCodeGenerator = getComponentCodeGenerator(lwComponent.getParent());
+        if (lwComponent instanceof LwNestedForm) {
+          componentLocal = getNestedFormComponent(generator, componentClass, componentLocal);
+        }
+        parentCodeGenerator.generateComponentLayout(lwComponent, generator, componentLocal, parentLocal);
+      }
+
+      if (lwComponent instanceof LwContainer) {
+        LwContainer container = (LwContainer) lwComponent;
+
+        generateBorder(container, generator, componentLocal);
+
+        for(int i=0; i<container.getComponentCount(); i++) {
+          generateSetupCodeForComponent((LwComponent) container.getComponent(i), generator, componentLocal);
+        }
+      }
+    }
+
+    private int getNestedFormComponent(GeneratorAdapter generator, InstrumentationClassFinder.PseudoClass componentClass, int formLocal) throws CodeGenerationException {
+      final Type componentType = Type.getType(JComponent.class);
+      int componentLocal = generator.newLocal(componentType);
+      generator.loadLocal(formLocal);
+      generator.invokeVirtual(Type.getType(componentClass.getDescriptor()), new Method(GET_ROOT_COMPONENT_METHOD_NAME, componentType, new Type[0]));
+      generator.storeLocal(componentLocal);
+      return componentLocal;
+    }
+
+    private LayoutCodeGenerator getComponentCodeGenerator(final LwContainer container) {
+      LayoutCodeGenerator generator = (LayoutCodeGenerator) myComponentLayoutCodeGenerators.get(container.getClass());
+      if (generator != null) {
+        return generator;
+      }
+      LwContainer parent = container;
+      while(parent != null) {
+        final String layoutManager = parent.getLayoutManager();
+        if (layoutManager != null && layoutManager.length() > 0) {
+          if (layoutManager.equals(UIFormXmlConstants.LAYOUT_FORM) &&
+              !myContainerLayoutCodeGenerators.containsKey(UIFormXmlConstants.LAYOUT_FORM)) {
+            myContainerLayoutCodeGenerators.put(UIFormXmlConstants.LAYOUT_FORM, new FormLayoutCodeGenerator());
+          }
+          generator = (LayoutCodeGenerator) myContainerLayoutCodeGenerators.get(layoutManager);
+          if (generator != null) {
+            return generator;
+          }
+        }
+        parent = parent.getParent();
+      }
+      return GridLayoutCodeGenerator.INSTANCE;
+    }
+
+    private void generateComponentProperties(final LwComponent lwComponent,
+                                             final InstrumentationClassFinder.PseudoClass componentClass,
+                                             final GeneratorAdapter generator,
+                                             final int componentLocal) throws CodeGenerationException {
+      // introspected properties
+      final LwIntrospectedProperty[] introspectedProperties = lwComponent.getAssignedIntrospectedProperties();
+      for (int i = 0; i < introspectedProperties.length; i++) {
+        final LwIntrospectedProperty property = introspectedProperties[i];
+        if (property instanceof LwIntroComponentProperty) {
+          continue;
+        }
+        final String propertyClass = property.getCodeGenPropertyClassName();
+        if (myIgnoreCustomCreation) {
+          try {
+            Class setterClass;
+            if (propertyClass.equals(Integer.class.getName())) {
+              setterClass = int.class;
+            }
+            else if (propertyClass.equals(Boolean.class.getName())) {
+              setterClass = boolean.class;
+            }
+            else if (propertyClass.equals(Double.class.getName())) {
+              setterClass = double.class;
+            }
+            else if (propertyClass.equals(Float.class.getName())) {
+              setterClass = float.class;
+            }
+            else if (propertyClass.equals(Long.class.getName())) {
+              setterClass = long.class;
+            }
+            else if (propertyClass.equals(Byte.class.getName())) {
+              setterClass = byte.class;
+            }
+            else if (propertyClass.equals(Short.class.getName())) {
+              setterClass = short.class;
+            }
+            else if (propertyClass.equals(Character.class.getName())) {
+              setterClass = char.class;
+            }
+            else {
+              setterClass = Class.forName(propertyClass);
+            }
+            //componentClass.getMethod(property.getWriteMethodName(), new Class[] { setterClass } );
+            final String descriptor = "(L"+setterClass.getName().replace('.', '/') + ";)V";
+            final InstrumentationClassFinder.PseudoMethod setter = componentClass.findMethod(property.getWriteMethodName(), descriptor);
+            if (setter == null) {
+              continue;
+            }
+          }
+          catch (Exception e) {
+            continue;
+          }
+        }
+        final PropertyCodeGenerator propGen = (PropertyCodeGenerator) myPropertyCodeGenerators.get(propertyClass);
+
+        try {
+          if (propGen != null && propGen.generateCustomSetValue(lwComponent, componentClass, property, generator, componentLocal, myClassName)) {
+            continue;
+          }
+        }
+        catch (IOException e) {
+          throw new CodeGenerationException(lwComponent.getId(), e.getMessage(), e);
+        }
+        catch (ClassNotFoundException e) {
+          throw new CodeGenerationException(lwComponent.getId(), e.getMessage(), e);
+        }
+
+        generator.loadLocal(componentLocal);
+
+        Object value = lwComponent.getPropertyValue(property);
+        Type setterArgType;
+        if (propertyClass.equals(Integer.class.getName())) {
+          generator.push(((Integer) value).intValue());
+          setterArgType = Type.INT_TYPE;
+        }
+        else if (propertyClass.equals(Boolean.class.getName())) {
+          generator.push(((Boolean) value).booleanValue());
+          setterArgType = Type.BOOLEAN_TYPE;
+        }
+        else if (propertyClass.equals(Double.class.getName())) {
+          generator.push(((Double) value).doubleValue());
+          setterArgType = Type.DOUBLE_TYPE;
+        }
+        else if (propertyClass.equals(Float.class.getName())) {
+          generator.push(((Float) value).floatValue());
+          setterArgType = Type.FLOAT_TYPE;
+        }
+        else if (propertyClass.equals(Long.class.getName())) {
+          generator.push(((Long) value).longValue());
+          setterArgType = Type.LONG_TYPE;
+        }
+        else if (propertyClass.equals(Short.class.getName())) {
+          generator.push(((Short) value).intValue());
+          setterArgType = Type.SHORT_TYPE;
+        }
+        else if (propertyClass.equals(Byte.class.getName())) {
+          generator.push(((Byte) value).intValue());
+          setterArgType = Type.BYTE_TYPE;
+        }
+        else if (propertyClass.equals(Character.class.getName())) {
+          generator.push(((Character) value).charValue());
+          setterArgType = Type.CHAR_TYPE;
+        }
+        else {
+          if (propGen == null) {
+            continue;
+          }
+          propGen.generatePushValue(generator, value);
+          setterArgType = typeFromClassName(property.getPropertyClassName());
+        }
+
+        Type declaringType = (property.getDeclaringClassName() != null)
+                             ? typeFromClassName(property.getDeclaringClassName())
+                             : Type.getType(componentClass.getDescriptor());
+        generator.invokeVirtual(declaringType, new Method(property.getWriteMethodName(),
+                                                          Type.VOID_TYPE, new Type[] { setterArgType } ));
+      }
+
+      generateClientProperties(lwComponent, componentClass, generator, componentLocal);
+    }
+
+    private void generateClientProperties(final LwComponent lwComponent,
+                                          final InstrumentationClassFinder.PseudoClass componentClass,
+                                          final GeneratorAdapter generator,
+                                          final int componentLocal) throws CodeGenerationException {
+      HashMap props = lwComponent.getDelegeeClientProperties();
+      for (Iterator iterator = props.entrySet().iterator(); iterator.hasNext();) {
+        Map.Entry e = (Map.Entry) iterator.next();
+        generator.loadLocal(componentLocal);
+
+        generator.push((String) e.getKey());
+
+        Object value = e.getValue();
+        if (value instanceof StringDescriptor) {
+          generator.push(((StringDescriptor) value).getValue());
+        }
+        else if (value instanceof Boolean) {
+          boolean boolValue = ((Boolean) value).booleanValue();
+          Type booleanType = Type.getType(Boolean.class);
+          if (boolValue) {
+            generator.getStatic(booleanType, "TRUE", booleanType);
+          }
+          else {
+            generator.getStatic(booleanType, "FALSE", booleanType);
+          }
+        }
+        else {
+          Type valueType = Type.getType(value.getClass());
+          generator.newInstance(valueType);
+          generator.dup();
+          if (value instanceof Integer) {
+            generator.push(((Integer)value).intValue());
+            generator.invokeConstructor(valueType, Method.getMethod("void <init>(int)"));
+          }
+          else if (value instanceof Double) {
+            generator.push(((Double)value).doubleValue());
+            generator.invokeConstructor(valueType, Method.getMethod("void <init>(double)"));
+          }
+          else {
+            throw new CodeGenerationException(lwComponent.getId(), "Unknown client property value type");
+          }
+        }
+
+        Type componentType = Type.getType(componentClass.getDescriptor());
+        Type objectType = Type.getType(Object.class);
+        generator.invokeVirtual(componentType, new Method("putClientProperty",
+                                                          Type.VOID_TYPE, new Type[] { objectType, objectType } ));
+      }
+    }
+
+    private void generateComponentReferenceProperties(final LwComponent component,
+                                                      final GeneratorAdapter generator) throws CodeGenerationException {
+      if (component instanceof LwNestedForm) return;
+      int componentLocal = ((Integer) myIdToLocalMap.get(component.getId())).intValue();
+      final LayoutCodeGenerator layoutCodeGenerator = getComponentCodeGenerator(component.getParent());
+      InstrumentationClassFinder.PseudoClass componentClass = getComponentClass(layoutCodeGenerator.mapComponentClass(component.getComponentClassName()), myFinder);
+
+      final LwIntrospectedProperty[] introspectedProperties = component.getAssignedIntrospectedProperties();
+      for (int i = 0; i < introspectedProperties.length; i++) {
+        final LwIntrospectedProperty property = introspectedProperties[i];
+        if (property instanceof LwIntroComponentProperty) {
+          String targetId = (String) component.getPropertyValue(property);
+          if (targetId != null && targetId.length() > 0) {
+            // we may have a reference property pointing to a component which is
+            // no longer valid
+            final Integer targetLocalInt = (Integer)myIdToLocalMap.get(targetId);
+            if (targetLocalInt != null) {
+              int targetLocal = targetLocalInt.intValue();
+              generator.loadLocal(componentLocal);
+              generator.loadLocal(targetLocal);
+              Type declaringType = (property.getDeclaringClassName() != null)
+                                   ? typeFromClassName(property.getDeclaringClassName())
+                                   : Type.getType(componentClass.getDescriptor());
+              generator.invokeVirtual(declaringType,
+                                      new Method(property.getWriteMethodName(),
+                                                 Type.VOID_TYPE, new Type[] { typeFromClassName(property.getPropertyClassName()) } ));
+            }
+          }
+        }
+      }
+
+      if (component instanceof LwContainer) {
+        LwContainer container = (LwContainer) component;
+
+        for(int i=0; i<container.getComponentCount(); i++) {
+          generateComponentReferenceProperties((LwComponent) container.getComponent(i), generator);
+        }
+      }
+    }
+
+    private void generateButtonGroups(final LwRootContainer rootContainer, final GeneratorAdapter generator) throws CodeGenerationException {
+      IButtonGroup[] groups = rootContainer.getButtonGroups();
+      if (groups.length > 0) {
+        try {
+          InstrumentationClassFinder.PseudoClass buttonGroupClass = null; // cached
+          int groupLocal = generator.newLocal(ourButtonGroupType);
+          for(int groupIndex=0; groupIndex<groups.length; groupIndex++) {
+            String[] ids = groups [groupIndex].getComponentIds();
+
+            if (ids.length > 0) {
+              generator.newInstance(ourButtonGroupType);
+              generator.dup();
+              generator.invokeConstructor(ourButtonGroupType, Method.getMethod("void <init>()"));
+              generator.storeLocal(groupLocal);
+
+              if (groups [groupIndex].isBound() && !myIgnoreCustomCreation) {
+                if (buttonGroupClass == null) {
+                  buttonGroupClass = myFinder.loadClass(ButtonGroup.class.getName());
+                }
+                validateFieldClass(groups [groupIndex].getName(), buttonGroupClass, null);
+                generator.loadThis();
+                generator.loadLocal(groupLocal);
+                generator.putField(getMainClassType(), groups [groupIndex].getName(), ourButtonGroupType);
+              }
+
+              for(int i = 0; i<ids.length; i++) {
+                Integer localInt = (Integer) myIdToLocalMap.get(ids [i]);
+                if (localInt != null) {
+                  generator.loadLocal(groupLocal);
+                  generator.loadLocal(localInt.intValue());
+                  generator.invokeVirtual(ourButtonGroupType, Method.getMethod("void add(javax.swing.AbstractButton)"));
+                }
+              }
+            }
+          }
+        }
+        catch (IOException e) {
+          throw new CodeGenerationException(rootContainer.getId(), e.getMessage(), e);
+        }
+        catch (ClassNotFoundException e) {
+          throw new CodeGenerationException(rootContainer.getId(), e.getMessage(), e);
+        }
+      }
+    }
+
+    private void generateFieldBinding(final LwComponent lwComponent,
+                                      final GeneratorAdapter generator,
+                                      final int componentLocal) throws CodeGenerationException {
+      final String binding = lwComponent.getBinding();
+      if (binding != null) {
+        Integer access = (Integer) myFieldAccessMap.get(binding);
+        if ((access.intValue() & Opcodes.ACC_STATIC) != 0) {
+          throw new CodeGenerationException(lwComponent.getId(), "Cannot bind: field is static: " + myClassToBind + "." + binding);
+        }
+        if ((access.intValue() & Opcodes.ACC_FINAL) != 0) {
+          throw new CodeGenerationException(lwComponent.getId(), "Cannot bind: field is final: " + myClassToBind + "." + binding);
+        }
+
+        generator.loadThis();
+        generator.loadLocal(componentLocal);
+        generator.putField(getMainClassType(), binding,
+                           Type.getType((String)myFieldDescMap.get(binding)));
+      }
+    }
+
+    private Type getMainClassType() {
+      return Type.getType("L" + myClassName + ";");
+    }
+
+    private void validateFieldBinding(LwComponent component, final InstrumentationClassFinder.PseudoClass componentClass) throws CodeGenerationException {
+      String binding = component.getBinding();
+      if (binding == null) return;
+
+      validateFieldClass(binding, componentClass, component.getId());
+    }
+
+    private void validateFieldClass(String binding, InstrumentationClassFinder.PseudoClass componentClass, String componentId) throws CodeGenerationException {
+      if (!myFieldDescMap.containsKey(binding)) {
+        throw new CodeGenerationException(componentId, "Cannot bind: field does not exist: " + myClassToBind + "." + binding);
+      }
+
+      final Type fieldType = Type.getType((String)myFieldDescMap.get(binding));
+      if (fieldType.getSort() != Type.OBJECT) {
+        throw new CodeGenerationException(componentId, "Cannot bind: field is of primitive type: " + myClassToBind + "." + binding);
+      }
+
+      try {
+        final InstrumentationClassFinder.PseudoClass fieldClass = myFinder.loadClass(fieldType.getClassName());
+        if (!fieldClass.isAssignableFrom(componentClass)) {
+          throw new CodeGenerationException(componentId, "Cannot bind: Incompatible types. Cannot assign " + componentClass.getName().replace('/', '.') + " to field " + myClassToBind + "." + binding);
+        }
+      }
+      catch (ClassNotFoundException e) {
+        throw new CodeGenerationException(componentId, "Class not found: " + fieldType.getClassName());
+      }
+      catch (IOException e) {
+        throw new CodeGenerationException(componentId, e.getMessage(), e);
+      }
+    }
+
+    private void generateBorder(final LwContainer container, final GeneratorAdapter generator, final int componentLocal) {
+      final BorderType borderType = container.getBorderType();
+      final StringDescriptor borderTitle = container.getBorderTitle();
+      final String borderFactoryMethodName = borderType.getBorderFactoryMethodName();
+
+      final boolean borderNone = borderType.equals(BorderType.NONE);
+      if (!borderNone || borderTitle != null) {
+        // object to invoke setBorder
+        generator.loadLocal(componentLocal);
+
+        if (!borderNone) {
+          if (borderType.equals(BorderType.LINE)) {
+            if (container.getBorderColor() == null) {
+              Type colorType = Type.getType(Color.class);
+              generator.getStatic(colorType, "black", colorType);
+            }
+            else {
+              pushPropValue(generator, Color.class.getName(), container.getBorderColor());
+            }
+            generator.invokeStatic(ourBorderFactoryType,
+                                   new Method(borderFactoryMethodName, ourBorderType,
+                                              new Type[] { Type.getType(Color.class) } ));
+          }
+          else if (borderType.equals(BorderType.EMPTY) && container.getBorderSize() != null) {
+            Insets size = container.getBorderSize();
+            generator.push(size.top);
+            generator.push(size.left);
+            generator.push(size.bottom);
+            generator.push(size.right);
+            generator.invokeStatic(ourBorderFactoryType,
+                                   new Method(borderFactoryMethodName, ourBorderType,
+                                              new Type[] { Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE }));
+          }
+          else {
+            generator.invokeStatic(ourBorderFactoryType,
+                                   new Method(borderFactoryMethodName, ourBorderType, new Type[0]));
+          }
+        }
+        else {
+          generator.push((String) null);
+        }
+        pushBorderProperties(container, generator, borderTitle, componentLocal);
+
+
+        Type borderFactoryType = ourBorderFactoryType;
+        StringDescriptor borderFactoryValue = (StringDescriptor)container.getDelegeeClientProperties().get(ourBorderFactoryClientProperty);
+        if (borderFactoryValue == null && borderTitle != null && Boolean.valueOf(System.getProperty("idea.is.internal")).booleanValue()) {
+          borderFactoryValue = StringDescriptor.create("com.intellij.ui.IdeBorderFactory$PlainSmallWithIndent");
+          container.getDelegeeClientProperties().put(ourBorderFactoryClientProperty, borderFactoryValue);
+        }
+        if (borderFactoryValue != null && borderFactoryValue.getValue().length() != 0) {
+          borderFactoryType = typeFromClassName(borderFactoryValue.getValue());
+        }
+
+        generator.invokeStatic(borderFactoryType, ourCreateTitledBorderMethod);
+
+        // set border
+        generator.invokeVirtual(Type.getType(JComponent.class),
+                                Method.getMethod("void setBorder(javax.swing.border.Border)"));
+      }
+    }
+
+    private void pushBorderProperties(final LwContainer container, final GeneratorAdapter generator, final StringDescriptor borderTitle,
+                                      final int componentLocal) {
+      pushPropValue(generator, "java.lang.String", borderTitle);
+      generator.push(container.getBorderTitleJustification());
+      generator.push(container.getBorderTitlePosition());
+      final FontDescriptor font = container.getBorderTitleFont();
+      if (font == null) {
+        generator.push((String) null);
+      }
+      else {
+        FontPropertyCodeGenerator.generatePushFont(generator, componentLocal, container, font, "getFont");
+      }
+      if (container.getBorderTitleColor() == null) {
+        generator.push((String) null);
+      }
+      else {
+        pushPropValue(generator, Color.class.getName(), container.getBorderTitleColor());
+      }
+    }
+  }
+
+  private class FormConstructorVisitor extends MethodVisitor {
+    private final String myClassName;
+    private final String mySuperName;
+    private boolean callsSelfConstructor = false;
+    private boolean mySetupCalled = false;
+    private boolean mySuperCalled = false;
+
+    public FormConstructorVisitor(final MethodVisitor mv, final String className, final String superName) {
+      super(Opcodes.ASM4, mv);
+      myClassName = className;
+      mySuperName = superName;
+    }
+
+    public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
+      if (opcode == Opcodes.GETFIELD && !mySetupCalled && !callsSelfConstructor && Utils.isBoundField(myRootContainer, name)) {
+        callSetupUI();
+      }
+      super.visitFieldInsn(opcode, owner, name, desc);
+    }
+
+    public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) {
+      if (opcode == Opcodes.INVOKESPECIAL && name.equals(CONSTRUCTOR_NAME)) {
+        if (owner.equals(myClassName)) {
+          callsSelfConstructor = true;
+        }
+        else if (owner.equals(mySuperName)) {
+          mySuperCalled = true;
+        }
+        else if (mySuperCalled) {
+          callSetupUI();
+        }
+      }
+      else if (mySuperCalled) {
+        callSetupUI();
+      }
+      super.visitMethodInsn(opcode, owner, name, desc);
+    }
+
+    public void visitJumpInsn(final int opcode, final Label label) {
+      if (mySuperCalled) {
+        callSetupUI();
+      }
+      super.visitJumpInsn(opcode, label);
+    }
+
+    private void callSetupUI() {
+      if (!mySetupCalled) {
+        mv.visitVarInsn(Opcodes.ALOAD, 0);
+        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, myClassName, SETUP_METHOD_NAME, "()V");
+        mySetupCalled = true;
+      }
+    }
+
+    public void visitInsn(final int opcode) {
+      if (opcode == Opcodes.RETURN && !mySetupCalled && !callsSelfConstructor) {
+        callSetupUI();
+      }
+      super.visitInsn(opcode);
+    }
+  }
+
+  private static class FirstPassClassVisitor extends ClassVisitor {
+    private boolean myExplicitSetupCall = false;
+
+    public FirstPassClassVisitor() {
+      super(Opcodes.ASM4, new ClassVisitor(Opcodes.ASM4){});
+    }
+
+    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+      if (name.equals(CONSTRUCTOR_NAME)) {
+        return new FirstPassConstructorVisitor();
+      }
+      return null;
+    }
+
+    public boolean isExplicitSetupCall() {
+      return myExplicitSetupCall;
+    }
+
+    private class FirstPassConstructorVisitor extends MethodVisitor {
+      public FirstPassConstructorVisitor() {
+        super(Opcodes.ASM4, new MethodVisitor(Opcodes.ASM4){});
+      }
+
+      public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) {
+        if (name.equals(SETUP_METHOD_NAME)) {
+          myExplicitSetupCall = true;
+        }
+      }
+    }
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/CardLayoutCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/CardLayoutCodeGenerator.java
new file mode 100644
index 0000000..86b6e42
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/CardLayoutCodeGenerator.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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.uiDesigner.UIFormXmlConstants;
+import com.intellij.uiDesigner.lw.LwComponent;
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+import java.awt.*;
+
+/**
+ * @author Alexander Lobas
+ */
+public class CardLayoutCodeGenerator extends SimpleLayoutCodeGenerator {
+  private static final Method ourGetLayoutMethod = Method.getMethod("java.awt.LayoutManager getLayout()");
+  private static final Method ourShowMethod = Method.getMethod("void show(java.awt.Container,java.lang.String)");
+
+  public CardLayoutCodeGenerator() {
+    super(Type.getType(CardLayout.class));
+  }
+
+  public void generateComponentLayout(LwComponent lwComponent,
+                                      GeneratorAdapter generator,
+                                      int componentLocal,
+                                      int parentLocal) {
+    super.generateComponentLayout(lwComponent, generator, componentLocal, parentLocal);
+
+    String defaultCard = (String)lwComponent.getParent().getClientProperty(UIFormXmlConstants.LAYOUT_CARD);
+    if (lwComponent.getId().equals(defaultCard)) {
+      generator.loadLocal(parentLocal);
+      generator.invokeVirtual(ourContainerType, ourGetLayoutMethod);
+      generator.checkCast(myLayoutType);
+      generator.loadLocal(parentLocal);
+      generator.push((String) lwComponent.getCustomLayoutConstraints());
+      generator.invokeVirtual(myLayoutType, ourShowMethod);
+    }
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/ClassToBindNotFoundException.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/ClassToBindNotFoundException.java
new file mode 100644
index 0000000..9a0c87d
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/ClassToBindNotFoundException.java
@@ -0,0 +1,22 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+public final class ClassToBindNotFoundException extends CodeGenerationException{
+  public ClassToBindNotFoundException(final String message) {
+    super(null, message);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/CodeGenerationException.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/CodeGenerationException.java
new file mode 100644
index 0000000..3920479
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/CodeGenerationException.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.
+ */
+package com.intellij.uiDesigner.compiler;
+
+public class CodeGenerationException extends UIDesignerException {
+  private final String myComponentId;
+
+  public CodeGenerationException(final String componentId, final String message) {
+    super(message);
+    myComponentId = componentId;
+  }
+
+  public CodeGenerationException(final String componentId, final String message, final Throwable cause) {
+    super(message, cause);
+    myComponentId = componentId;
+  }
+
+  public String getComponentId() {
+    return myComponentId;
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/ColorPropertyCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/ColorPropertyCodeGenerator.java
new file mode 100644
index 0000000..8443020
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/ColorPropertyCodeGenerator.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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.uiDesigner.lw.ColorDescriptor;
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+import java.awt.*;
+
+/**
+ * @author yole
+ * @noinspection HardCodedStringLiteral
+ */
+public class ColorPropertyCodeGenerator extends PropertyCodeGenerator {
+  private static final Type ourColorType = Type.getType(Color.class);
+  private static final Type ourObjectType = Type.getType(Object.class);
+  private static final Type ourUIManagerType = Type.getType("Ljavax/swing/UIManager;");
+  private static final Type ourSystemColorType = Type.getType(SystemColor.class);
+
+  private static final Method ourInitMethod = Method.getMethod("void <init>(int)");
+  private static final Method ourGetColorMethod = new Method("getColor", ourColorType, new Type[] { ourObjectType } );
+
+  public void generatePushValue(final GeneratorAdapter generator, final Object value) {
+    ColorDescriptor descriptor = (ColorDescriptor) value;
+    if (descriptor.getColor() != null) {
+      generator.newInstance(ourColorType);
+      generator.dup();
+      generator.push(descriptor.getColor().getRGB());
+      generator.invokeConstructor(ourColorType, ourInitMethod);
+    }
+    else if (descriptor.getSwingColor() != null) {
+      generator.push(descriptor.getSwingColor());
+      generator.invokeStatic(ourUIManagerType, ourGetColorMethod);
+    }
+    else if (descriptor.getSystemColor() != null) {
+      generator.getStatic(ourSystemColorType, descriptor.getSystemColor(), ourSystemColorType);
+    }
+    else if (descriptor.getAWTColor() != null) {
+      generator.getStatic(ourColorType, descriptor.getAWTColor(), ourColorType);
+    }
+    else if (descriptor.isColorSet()) {
+      throw new IllegalStateException("Unknown color type");
+    }
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/DimensionPropertyCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/DimensionPropertyCodeGenerator.java
new file mode 100644
index 0000000..7a0fd4f
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/DimensionPropertyCodeGenerator.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 com.intellij.uiDesigner.compiler;
+
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+import java.awt.*;
+
+/**
+ * @author yole
+ * @noinspection HardCodedStringLiteral
+ */
+public class DimensionPropertyCodeGenerator extends PropertyCodeGenerator {
+  private static final Type myDimensionType = Type.getType(Dimension.class);
+  private static final Method myInitMethod = Method.getMethod("void <init>(int,int)");
+
+  public void generatePushValue(final GeneratorAdapter generator, final Object value) {
+    Dimension dimension = (Dimension) value;
+    generator.newInstance(myDimensionType);
+    generator.dup();
+    generator.push(dimension.width);
+    generator.push(dimension.height);
+    generator.invokeConstructor(myDimensionType, myInitMethod);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/EnumPropertyCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/EnumPropertyCodeGenerator.java
new file mode 100644
index 0000000..720d4c0
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/EnumPropertyCodeGenerator.java
@@ -0,0 +1,30 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+
+/**
+ * @author yole
+ */
+public class EnumPropertyCodeGenerator extends PropertyCodeGenerator {
+  public void generatePushValue(final GeneratorAdapter generator, final Object value) {
+    final Type enumType = Type.getType(value.getClass());
+    generator.getStatic(enumType, value.toString(), enumType);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/FlowLayoutCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/FlowLayoutCodeGenerator.java
new file mode 100644
index 0000000..34a41d8
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/FlowLayoutCodeGenerator.java
@@ -0,0 +1,55 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.uiDesigner.lw.LwComponent;
+import com.intellij.uiDesigner.lw.LwContainer;
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+import java.awt.*;
+
+/**
+ * @author yole
+ */
+public class FlowLayoutCodeGenerator extends LayoutCodeGenerator {
+  private static final Type ourFlowLayoutType = Type.getType(FlowLayout.class);
+  private static final Method ourConstructor = Method.getMethod("void <init>(int,int,int)");
+
+  public void generateContainerLayout(final LwContainer lwContainer, final GeneratorAdapter generator, final int componentLocal) {
+    generator.loadLocal(componentLocal);
+
+    FlowLayout flowLayout = (FlowLayout) lwContainer.getLayout();
+    generator.newInstance(ourFlowLayoutType);
+    generator.dup();
+    generator.push(flowLayout.getAlignment());
+    generator.push(flowLayout.getHgap());
+    generator.push(flowLayout.getVgap());
+    generator.invokeConstructor(ourFlowLayoutType, ourConstructor);
+
+    generator.invokeVirtual(ourContainerType, ourSetLayoutMethod);
+  }
+  public void generateComponentLayout(final LwComponent lwComponent,
+                                      final GeneratorAdapter generator,
+                                      final int componentLocal,
+                                      final int parentLocal) {
+    generator.loadLocal(parentLocal);
+    generator.loadLocal(componentLocal);
+    generator.invokeVirtual(ourContainerType, ourAddNoConstraintMethod);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/FontPropertyCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/FontPropertyCodeGenerator.java
new file mode 100644
index 0000000..f15a8d4
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/FontPropertyCodeGenerator.java
@@ -0,0 +1,119 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.compiler.instrumentation.InstrumentationClassFinder;
+import com.intellij.uiDesigner.lw.FontDescriptor;
+import com.intellij.uiDesigner.lw.LwComponent;
+import com.intellij.uiDesigner.lw.LwIntrospectedProperty;
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+import java.awt.*;
+
+/**
+ * @author yole
+ * @noinspection HardCodedStringLiteral
+ */
+public class FontPropertyCodeGenerator extends PropertyCodeGenerator {
+  private static final Type ourFontType = Type.getType(Font.class);
+  private static final Type ourUIManagerType = Type.getType("Ljavax/swing/UIManager;");
+  private static final Type ourObjectType = Type.getType(Object.class);
+  private static final Type ourStringType = Type.getType(String.class);
+
+  private static final Method ourInitMethod = Method.getMethod("void <init>(java.lang.String,int,int)");
+  private static final Method ourUIManagerGetFontMethod = new Method("getFont", ourFontType, new Type[] { ourObjectType } );
+  private static final Method ourGetNameMethod = new Method("getName", ourStringType, new Type[0] );
+  private static final Method ourGetSizeMethod = new Method("getSize", Type.INT_TYPE, new Type[0] );
+  private static final Method ourGetStyleMethod = new Method("getStyle", Type.INT_TYPE, new Type[0] );
+
+  public boolean generateCustomSetValue(final LwComponent lwComponent,
+                                        final InstrumentationClassFinder.PseudoClass componentClass,
+                                        final LwIntrospectedProperty property,
+                                        final GeneratorAdapter generator,
+                                        final int componentLocal, final String formClassName) {
+    FontDescriptor descriptor = (FontDescriptor) property.getPropertyValue(lwComponent);
+    if (descriptor.isFixedFont() && !descriptor.isFullyDefinedFont()) {
+      generator.loadLocal(componentLocal);
+      generatePushFont(generator, componentLocal, lwComponent, descriptor, property.getReadMethodName());
+
+      Method setFontMethod = new Method(property.getWriteMethodName(), Type.VOID_TYPE, new Type[] { ourFontType } );
+      Type componentType = AsmCodeGenerator.typeFromClassName(lwComponent.getComponentClassName());
+      generator.invokeVirtual(componentType, setFontMethod);
+      return true;
+    }
+    return false;
+  }
+
+  public static void generatePushFont(final GeneratorAdapter generator, final int componentLocal, final LwComponent lwComponent,
+                                      final FontDescriptor descriptor, final String readMethodName) {
+    final int fontLocal = generator.newLocal(ourFontType);
+
+    generator.loadLocal(componentLocal);
+    Type componentType = AsmCodeGenerator.typeFromClassName(lwComponent.getComponentClassName());
+    Method getFontMethod = new Method(readMethodName, ourFontType, new Type[0] );
+    generator.invokeVirtual(componentType, getFontMethod);
+    generator.storeLocal(fontLocal);
+
+    generator.newInstance(ourFontType);
+    generator.dup();
+    if (descriptor.getFontName() != null) {
+      generator.push(descriptor.getFontName());
+    }
+    else {
+      generator.loadLocal(fontLocal);
+      generator.invokeVirtual(ourFontType, ourGetNameMethod);
+    }
+
+    if (descriptor.getFontStyle() >= 0) {
+      generator.push(descriptor.getFontStyle());
+    }
+    else {
+      generator.loadLocal(fontLocal);
+      generator.invokeVirtual(ourFontType, ourGetStyleMethod);
+    }
+
+    if (descriptor.getFontSize() >= 0) {
+      generator.push(descriptor.getFontSize());
+    }
+    else {
+      generator.loadLocal(fontLocal);
+      generator.invokeVirtual(ourFontType, ourGetSizeMethod);
+    }
+    generator.invokeConstructor(ourFontType, ourInitMethod);
+  }
+
+  public void generatePushValue(final GeneratorAdapter generator, final Object value) {
+    FontDescriptor descriptor = (FontDescriptor) value;
+    if (descriptor.isFixedFont()) {
+      if (!descriptor.isFullyDefinedFont()) throw new IllegalStateException("Unexpected font state");
+      generator.newInstance(ourFontType);
+      generator.dup();
+      generator.push(descriptor.getFontName());
+      generator.push(descriptor.getFontStyle());
+      generator.push(descriptor.getFontSize());
+      generator.invokeConstructor(ourFontType, ourInitMethod);
+    }
+    else if (descriptor.getSwingFont() != null) {
+      generator.push(descriptor.getSwingFont());
+      generator.invokeStatic(ourUIManagerType, ourUIManagerGetFontMethod);
+    }
+    else {
+      throw new IllegalStateException("Unknown font type");
+    }
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/FormErrorInfo.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/FormErrorInfo.java
new file mode 100644
index 0000000..133d94b
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/FormErrorInfo.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 com.intellij.uiDesigner.compiler;
+
+/**
+ * @author yole
+ */
+public class FormErrorInfo {
+  private String myComponentId;
+  private String myErrorMessage;
+
+  public FormErrorInfo(final String componentId, final String errorMessage) {
+    myComponentId = componentId;
+    myErrorMessage = errorMessage;
+  }
+
+  public String getComponentId() {
+    return myComponentId;
+  }
+
+  public void setComponentId(final String componentId) {
+    myComponentId = componentId;
+  }
+
+  public String getErrorMessage() {
+    return myErrorMessage;
+  }
+
+  public void setErrorMessage(final String errorMessage) {
+    myErrorMessage = errorMessage;
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/FormLayoutCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/FormLayoutCodeGenerator.java
new file mode 100644
index 0000000..a30061f
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/FormLayoutCodeGenerator.java
@@ -0,0 +1,125 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.uiDesigner.core.GridConstraints;
+import com.intellij.uiDesigner.lw.LwComponent;
+import com.intellij.uiDesigner.lw.LwContainer;
+import com.jgoodies.forms.layout.CellConstraints;
+import com.jgoodies.forms.layout.FormLayout;
+import org.jetbrains.asm4.Opcodes;
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+import java.awt.*;
+
+/**
+ * @author yole
+ */
+public class FormLayoutCodeGenerator extends LayoutCodeGenerator {
+  private static final Type ourFormLayoutType = Type.getType(FormLayout.class);
+  private static final Type ourCellConstraintsType = Type.getType(CellConstraints.class);
+  private static final Type ourCellAlignmentType = Type.getType(CellConstraints.Alignment.class);
+  private static final Method ourFormLayoutConstructor = Method.getMethod("void <init>(java.lang.String,java.lang.String)");
+  private static final Method ourCellConstraintsConstructor = Method.getMethod("void <init>(int,int,int,int,com.jgoodies.forms.layout.CellConstraints$Alignment,com.jgoodies.forms.layout.CellConstraints$Alignment,java.awt.Insets)");
+  private static final Method ourSetRowGroupsMethod = Method.getMethod("void setRowGroups(int[][])");
+  private static final Method ourSetColumnGroupsMethod = Method.getMethod("void setColumnGroups(int[][])");
+
+  public static String[] HORZ_ALIGN_FIELDS = new String[] { "LEFT", "CENTER", "RIGHT", "FILL" };
+  public static String[] VERT_ALIGN_FIELDS = new String[] { "TOP", "CENTER", "BOTTOM", "FILL" };
+
+  public void generateContainerLayout(final LwContainer lwContainer, final GeneratorAdapter generator, final int componentLocal) {
+    FormLayout formLayout = (FormLayout) lwContainer.getLayout();
+
+    generator.loadLocal(componentLocal);
+
+    generator.newInstance(ourFormLayoutType);
+    generator.dup();
+    generator.push(FormLayoutUtils.getEncodedColumnSpecs(formLayout));
+    generator.push(FormLayoutUtils.getEncodedRowSpecs(formLayout));
+
+    generator.invokeConstructor(ourFormLayoutType, ourFormLayoutConstructor);
+
+    generateGroups(generator, formLayout.getRowGroups(), ourSetRowGroupsMethod);
+    generateGroups(generator, formLayout.getColumnGroups(), ourSetColumnGroupsMethod);
+
+    generator.invokeVirtual(ourContainerType, ourSetLayoutMethod);
+  }
+
+  private static void generateGroups(final GeneratorAdapter generator, final int[][] groups, final Method setGroupsMethod) {
+    if (groups.length == 0) return;
+    int groupLocal = generator.newLocal(Type.getType("[I"));
+    generator.dup();   // duplicate FormLayout reference
+    generator.push(groups.length);
+    generator.newArray(Type.getType("[I"));
+    for(int i=0; i<groups.length; i++) {
+      generator.dup();
+      generator.push(groups [i].length);
+      generator.newArray(Type.INT_TYPE);
+      generator.storeLocal(groupLocal);
+      for(int j=0; j<groups [i].length; j++) {
+        generator.loadLocal(groupLocal);
+        generator.push(j);
+        generator.push(groups [i][j]);
+        generator.visitInsn(Opcodes.IASTORE);
+      }
+      generator.push(i);
+      generator.loadLocal(groupLocal);
+      generator.visitInsn(Opcodes.AASTORE);
+    }
+    generator.invokeVirtual(ourFormLayoutType, setGroupsMethod);
+  }
+
+  public void generateComponentLayout(final LwComponent lwComponent, final GeneratorAdapter generator, final int componentLocal, final int parentLocal) {
+    generator.loadLocal(parentLocal);
+    generator.loadLocal(componentLocal);
+    addNewCellConstraints(generator, lwComponent);
+    generator.invokeVirtual(ourContainerType, ourAddMethod);
+  }
+
+  private static void addNewCellConstraints(final GeneratorAdapter generator, final LwComponent lwComponent) {
+    final GridConstraints constraints = lwComponent.getConstraints();
+    final CellConstraints cc = (CellConstraints) lwComponent.getCustomLayoutConstraints();
+
+    generator.newInstance(ourCellConstraintsType);
+    generator.dup();
+    generator.push(constraints.getColumn()+1);
+    generator.push(constraints.getRow()+1);
+    generator.push(constraints.getColSpan());
+    generator.push(constraints.getRowSpan());
+
+    if (cc.hAlign == CellConstraints.DEFAULT) {
+      generator.getStatic(ourCellConstraintsType, "DEFAULT", ourCellAlignmentType);
+    }
+    else {
+      int hAlign = Utils.alignFromConstraints(constraints, true);
+      generator.getStatic(ourCellConstraintsType, HORZ_ALIGN_FIELDS[hAlign], ourCellAlignmentType);
+    }
+    if (cc.vAlign == CellConstraints.DEFAULT) {
+      generator.getStatic(ourCellConstraintsType, "DEFAULT", ourCellAlignmentType);
+    }
+    else {
+      int vAlign = Utils.alignFromConstraints(constraints, false);
+      generator.getStatic(ourCellConstraintsType, VERT_ALIGN_FIELDS[vAlign], ourCellAlignmentType);
+    }
+
+    AsmCodeGenerator.pushPropValue(generator, Insets.class.getName(), cc.insets);
+
+    generator.invokeConstructor(ourCellConstraintsType, ourCellConstraintsConstructor);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/FormLayoutUtils.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/FormLayoutUtils.java
new file mode 100644
index 0000000..64099e9
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/FormLayoutUtils.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+/*
+ * Created by IntelliJ IDEA.
+ * User: yole
+ * Date: 25.07.2006
+ * Time: 18:27:54
+ */
+package com.intellij.uiDesigner.compiler;
+
+import com.jgoodies.forms.layout.FormLayout;
+import com.jgoodies.forms.layout.FormSpec;
+
+public class FormLayoutUtils {
+  private FormLayoutUtils() {
+  }
+
+  public static String getEncodedRowSpecs(final FormLayout formLayout) {
+    StringBuffer result = new StringBuffer();
+    for(int i=1; i<=formLayout.getRowCount(); i++) {
+      if (result.length() > 0) {
+        result.append(",");
+      }
+      result.append(getEncodedSpec(formLayout.getRowSpec(i)));
+    }
+    return result.toString();
+  }
+
+  public static String getEncodedColumnSpecs(final FormLayout formLayout) {
+    StringBuffer result = new StringBuffer();
+    for(int i=1; i<=formLayout.getColumnCount(); i++) {
+      if (result.length() > 0) {
+        result.append(",");
+      }
+      result.append(getEncodedSpec(formLayout.getColumnSpec(i)));
+    }
+    return result.toString();
+  }
+
+  public static String getEncodedSpec(final FormSpec formSpec) {
+    String result = formSpec.toString();
+    while(true) {
+      int pos = result.indexOf("dluX");
+      if (pos < 0) {
+        pos = result.indexOf("dluY");
+      }
+      if (pos < 0) {
+        break;
+      }
+      result = result.substring(0, pos+3) + result.substring(pos+4);
+    }
+
+
+    return result;
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/GridBagConverter.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/GridBagConverter.java
new file mode 100644
index 0000000..6a971ff
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/GridBagConverter.java
@@ -0,0 +1,323 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.uiDesigner.core.GridConstraints;
+import com.intellij.uiDesigner.core.GridLayoutManager;
+import com.intellij.uiDesigner.core.AbstractLayout;
+import com.intellij.uiDesigner.core.Util;
+import com.intellij.uiDesigner.lw.*;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * @author yole
+ */
+public class GridBagConverter {
+  private final Insets myInsets;
+  private int myHGap;
+  private int myVGap;
+  private boolean mySameSizeHorz;
+  private boolean mySameSizeVert;
+  private final ArrayList myComponents = new ArrayList();
+  private final ArrayList myConstraints = new ArrayList();
+  private int myLastRow = -1;
+  private int myLastCol = -1;
+
+  public GridBagConverter() {
+    myInsets = new Insets(0, 0, 0, 0);
+  }
+
+  public GridBagConverter(final Insets insets, final int hgap, final int vgap, final boolean sameSizeHorz, final boolean sameSizeVert) {
+    myInsets = insets;
+    myHGap = hgap;
+    myVGap = vgap;
+    mySameSizeHorz = sameSizeHorz;
+    mySameSizeVert = sameSizeVert;
+  }
+
+  public void addComponent(final JComponent component, final GridConstraints constraints) {
+    myComponents.add(component);
+    myConstraints.add(constraints);
+  }
+
+  public static void prepareConstraints(final LwContainer container, final Map idToConstraintsMap) {
+    GridLayoutManager gridLayout = (GridLayoutManager) container.getLayout();
+    GridBagConverter converter = new GridBagConverter(gridLayout.getMargin(),
+                                                      getGap(container, true),
+                                                      getGap(container, false),
+                                                      gridLayout.isSameSizeHorizontally(),
+                                                      gridLayout.isSameSizeVertically());
+    for(int i=0; i<container.getComponentCount(); i++) {
+      final LwComponent component = (LwComponent)container.getComponent(i);
+      if (component instanceof LwHSpacer || component instanceof LwVSpacer) {
+        GridConstraints constraints = component.getConstraints().store();
+        constraints.setHSizePolicy(constraints.getHSizePolicy() & ~GridConstraints.SIZEPOLICY_WANT_GROW);
+        constraints.setVSizePolicy(constraints.getVSizePolicy() & ~GridConstraints.SIZEPOLICY_WANT_GROW);
+        converter.addComponent(null, constraints);
+      }
+      else {
+        converter.addComponent(null, component.getConstraints());
+      }
+    }
+    Result[] results = converter.convert();
+    int componentIndex = 0;
+    for(int i=0; i<results.length; i++) {
+      if (!results [i].isFillerPanel) {
+        final LwComponent component = (LwComponent)container.getComponent(componentIndex++);
+        idToConstraintsMap.put(component.getId(), results [i]);
+      }
+      // else generateFillerPanel(generator, componentLocal, results [i]);
+    }
+  }
+
+  private static int getGap(LwContainer container, final boolean horizontal) {
+    while(container != null) {
+      final LayoutManager layout = container.getLayout();
+      if (layout instanceof AbstractLayout) {
+        AbstractLayout aLayout = (AbstractLayout) layout;
+        final int gap = horizontal ? aLayout.getHGap() : aLayout.getVGap();
+        if (gap >= 0) {
+          return gap;
+        }
+      }
+      container = container.getParent();
+    }
+    return horizontal ? AbstractLayout.DEFAULT_HGAP : AbstractLayout.DEFAULT_VGAP;
+  }
+
+  public static class Result {
+    public JComponent component;
+    public boolean isFillerPanel;
+    public GridBagConstraints constraints;
+    public Dimension preferredSize;
+    public Dimension minimumSize;
+    public Dimension maximumSize;
+
+    public Result(final JComponent component) {
+      this.component = component;
+      constraints = new GridBagConstraints();
+    }
+  }
+
+  public Result[] convert() {
+    ArrayList results = new ArrayList();
+    for(int i=0; i<myComponents.size(); i++) {
+      results.add(convert((JComponent) myComponents.get(i), (GridConstraints) myConstraints.get(i)));
+    }
+    //addFillerPanels(results);
+    final Result[] resultArray = (Result[])results.toArray(new Result[results.size()]);
+    if (myHGap > 0 || myVGap > 0) {
+      applyGaps(resultArray);
+    }
+    if (mySameSizeHorz) {
+      makeSameSizes(resultArray, true);
+    }
+    if (mySameSizeVert) {
+      makeSameSizes(resultArray, false);
+    }
+
+    return resultArray;
+  }
+
+  private void applyGaps(final Result[] resultArray) {
+    int leftGap = myHGap/2;
+    int rightGap = myHGap - myHGap/2;
+    int topGap = myVGap / 2;
+    int bottomGap = myVGap - myVGap/2;
+    for(int i=0; i<resultArray.length; i++) {
+      Result result = resultArray [i];
+      if (result.constraints.gridx > 0) {
+        result.constraints.insets.left += leftGap;
+      }
+      if (result.constraints.gridx + result.constraints.gridwidth-1 < myLastCol) {
+        result.constraints.insets.right += rightGap;
+      }
+      if (result.constraints.gridy > 0) {
+        result.constraints.insets.top += topGap;
+      }
+      if (result.constraints.gridy + result.constraints.gridheight-1 < myLastRow) {
+        result.constraints.insets.bottom += bottomGap;
+      }
+    }
+  }
+
+  private static void makeSameSizes(final Result[] resultArray, boolean horizontal) {
+    int minimum = -1;
+    int preferred = -1;
+    for(int i=0; i<resultArray.length; i++) {
+      Result result = resultArray [i];
+      Dimension minSize = result.minimumSize != null || result.component == null
+                          ? result.minimumSize
+                          : result.component.getMinimumSize();
+      Dimension prefSize = result.preferredSize != null || result.component == null
+                           ? result.preferredSize
+                           : result.component.getPreferredSize();
+      if (minSize != null) {
+        minimum = Math.max(minimum, horizontal ? minSize.width : minSize.height);
+      }
+      if (prefSize != null) {
+        preferred = Math.max(preferred, horizontal ? prefSize.width : prefSize.height);
+      }
+    }
+
+    if (minimum >= 0 || preferred >= 0) {
+      for(int i=0; i<resultArray.length; i++) {
+        Result result = resultArray [i];
+
+        if ((result.minimumSize != null || result.component != null) && minimum >= 0) {
+          if (result.minimumSize == null) {
+            result.minimumSize = result.component.getMinimumSize();
+          }
+          if (horizontal) {
+            result.minimumSize.width = minimum;
+          }
+          else {
+            result.minimumSize.height = minimum;
+          }
+        }
+
+        if ((result.preferredSize != null || result.component != null) && preferred >= 0) {
+          if (result.preferredSize == null) {
+            result.preferredSize = result.component.getPreferredSize();
+          }
+          if (horizontal) {
+            result.preferredSize.width = preferred;
+          }
+          else {
+            result.preferredSize.height = preferred;
+          }
+        }
+      }
+    }
+  }
+
+  private Result convert(final JComponent component, final GridConstraints constraints) {
+    final Result result = new Result(component);
+
+    int endRow = constraints.getRow() + constraints.getRowSpan()-1;
+    myLastRow = Math.max(myLastRow, endRow);
+    int endCol = constraints.getColumn() + constraints.getColSpan()-1;
+    myLastCol = Math.max(myLastCol, endCol);
+
+    int indent = Util.DEFAULT_INDENT * constraints.getIndent();
+
+    constraintsToGridBag(constraints, result.constraints);
+    result.constraints.weightx = getWeight(constraints, true);
+    result.constraints.weighty = getWeight(constraints, false);
+    result.constraints.insets = new Insets(myInsets.top, myInsets.left + indent, myInsets.bottom, myInsets.right);
+
+    Dimension minSize = constraints.myMinimumSize;
+    if (component != null && minSize.width <= 0 && minSize.height <= 0) {
+      minSize = component.getMinimumSize();
+    }
+
+    if ((constraints.getHSizePolicy() & GridConstraints.SIZEPOLICY_CAN_SHRINK) == 0) {
+      minSize.width = constraints.myPreferredSize.width > 0 || component == null
+                      ? constraints.myPreferredSize.width
+                      : component.getPreferredSize().width;
+    }
+    if ((constraints.getVSizePolicy() & GridConstraints.SIZEPOLICY_CAN_SHRINK) == 0) {
+      minSize.height = constraints.myPreferredSize.height > 0 || component == null
+                       ? constraints.myPreferredSize.height
+                       : component.getPreferredSize().height;
+    }
+
+    if (minSize.width != -1 || minSize.height != -1) {
+      result.minimumSize = minSize;
+    }
+
+    if (constraints.myPreferredSize.width > 0 && constraints.myPreferredSize.height > 0) {
+      result.preferredSize = constraints.myPreferredSize;
+    }
+    if (constraints.myMaximumSize.width > 0 && constraints.myMaximumSize.height > 0) {
+      result.maximumSize = constraints.myMaximumSize;
+    }
+
+    return result;
+  }
+
+  public static GridBagConstraints getGridBagConstraints(IComponent component) {
+    final GridBagConstraints gbc;
+    if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
+      gbc = (GridBagConstraints) component.getCustomLayoutConstraints();
+    }
+    else {
+      gbc = new GridBagConstraints();
+    }
+    constraintsToGridBag(component.getConstraints(), gbc);
+    return gbc;
+  }
+
+  public static void constraintsToGridBag(final GridConstraints constraints, final GridBagConstraints result) {
+    result.gridx = constraints.getColumn();
+    result.gridy = constraints.getRow();
+    result.gridwidth = constraints.getColSpan();
+    result.gridheight = constraints.getRowSpan();
+    switch(constraints.getFill()) {
+      case GridConstraints.FILL_HORIZONTAL: result.fill = GridBagConstraints.HORIZONTAL; break;
+      case GridConstraints.FILL_VERTICAL:   result.fill = GridBagConstraints.VERTICAL; break;
+      case GridConstraints.FILL_BOTH:       result.fill = GridBagConstraints.BOTH; break;
+      default:                              result.fill = GridBagConstraints.NONE; break;
+    }
+    switch(constraints.getAnchor()) {
+      case GridConstraints.ANCHOR_NORTHWEST: result.anchor = GridBagConstraints.NORTHWEST; break;
+      case GridConstraints.ANCHOR_NORTH:     result.anchor = GridBagConstraints.NORTH; break;
+      case GridConstraints.ANCHOR_NORTHEAST: result.anchor = GridBagConstraints.NORTHEAST; break;
+      case GridConstraints.ANCHOR_EAST:      result.anchor = GridBagConstraints.EAST; break;
+      case GridConstraints.ANCHOR_SOUTHEAST: result.anchor = GridBagConstraints.SOUTHEAST; break;
+      case GridConstraints.ANCHOR_SOUTH:     result.anchor = GridBagConstraints.SOUTH; break;
+      case GridConstraints.ANCHOR_SOUTHWEST: result.anchor = GridBagConstraints.SOUTHWEST; break;
+      case GridConstraints.ANCHOR_WEST:      result.anchor = GridBagConstraints.WEST; break;
+    }
+  }
+
+  private double getWeight(final GridConstraints constraints, final boolean horizontal) {
+    int policy = horizontal ? constraints.getHSizePolicy() : constraints.getVSizePolicy();
+    if ((policy & GridConstraints.SIZEPOLICY_WANT_GROW) != 0) {
+      return 1.0;
+    }
+    boolean canGrow = ((policy & GridConstraints.SIZEPOLICY_CAN_GROW) != 0);
+    for (Iterator iterator = myConstraints.iterator(); iterator.hasNext();) {
+      GridConstraints otherConstraints = (GridConstraints)iterator.next();
+
+      if (!constraintsIntersect(horizontal, constraints, otherConstraints)) {
+        int otherPolicy = horizontal ? otherConstraints.getHSizePolicy() : otherConstraints.getVSizePolicy();
+        if ((otherPolicy & GridConstraints.SIZEPOLICY_WANT_GROW) != 0) {
+          return 0.0;
+        }
+        if (!canGrow && ((otherPolicy & GridConstraints.SIZEPOLICY_CAN_GROW) != 0)) {
+          return 0.0;
+        }
+      }
+    }
+    return 1.0;
+  }
+
+  private boolean constraintsIntersect(final boolean horizontal,
+                                       final GridConstraints constraints,
+                                       final GridConstraints otherConstraints) {
+    int start = constraints.getCell(!horizontal);
+    int end = start + constraints.getSpan(!horizontal) - 1;
+    int otherStart = otherConstraints.getCell(!horizontal);
+    int otherEnd = otherStart + otherConstraints.getSpan(!horizontal) - 1;
+    return start <= otherEnd && otherStart <= end;
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/GridBagLayoutCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/GridBagLayoutCodeGenerator.java
new file mode 100644
index 0000000..711d2c4
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/GridBagLayoutCodeGenerator.java
@@ -0,0 +1,166 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.uiDesigner.core.Spacer;
+import com.intellij.uiDesigner.lw.LwComponent;
+import com.intellij.uiDesigner.lw.LwContainer;
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * @author yole
+ */
+public class GridBagLayoutCodeGenerator extends LayoutCodeGenerator {
+  private static final Type ourGridBagLayoutType = Type.getType(GridBagLayout.class);
+  private static final Type ourGridBagConstraintsType = Type.getType(GridBagConstraints.class);
+  private static final Method ourDefaultConstructor = Method.getMethod("void <init> ()");
+
+  private static final Type myPanelType = Type.getType(JPanel.class);
+
+  public String mapComponentClass(final String componentClassName) {
+    if (componentClassName.equals(Spacer.class.getName())) {
+      return JPanel.class.getName();
+    }
+    return super.mapComponentClass(componentClassName);
+  }
+
+  public void generateContainerLayout(final LwContainer lwContainer, final GeneratorAdapter generator, final int componentLocal) {
+    generator.loadLocal(componentLocal);
+
+    generator.newInstance(ourGridBagLayoutType);
+    generator.dup();
+    generator.invokeConstructor(ourGridBagLayoutType, ourDefaultConstructor);
+
+    generator.invokeVirtual(ourContainerType, ourSetLayoutMethod);
+  }
+
+  private void generateFillerPanel(final GeneratorAdapter generator, final int parentLocal, final GridBagConverter.Result result) {
+    int panelLocal = generator.newLocal(myPanelType);
+
+    generator.newInstance(myPanelType);
+    generator.dup();
+    generator.invokeConstructor(myPanelType, ourDefaultConstructor);
+    generator.storeLocal(panelLocal);
+
+    generateConversionResult(generator, result, panelLocal, parentLocal);
+
+  }
+
+  public void generateComponentLayout(final LwComponent component,
+                                      final GeneratorAdapter generator,
+                                      final int componentLocal,
+                                      final int parentLocal) {
+    GridBagConstraints gbc;
+    if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
+      gbc = (GridBagConstraints) component.getCustomLayoutConstraints();
+    }
+    else {
+      gbc = new GridBagConstraints();
+    }
+
+    GridBagConverter.constraintsToGridBag(component.getConstraints(), gbc);
+
+    generateGridBagConstraints(generator, gbc, componentLocal, parentLocal);
+  }
+
+  private static void generateConversionResult(final GeneratorAdapter generator, final GridBagConverter.Result result,
+                                               final int componentLocal, final int parentLocal) {
+    checkSetSize(generator, componentLocal, "setMinimumSize", result.minimumSize);
+    checkSetSize(generator, componentLocal, "setPreferredSize", result.preferredSize);
+    checkSetSize(generator, componentLocal, "setMaximumSize", result.maximumSize);
+
+    generateGridBagConstraints(generator, result.constraints, componentLocal, parentLocal);
+  }
+
+  private static void generateGridBagConstraints(final GeneratorAdapter generator, GridBagConstraints constraints, final int componentLocal,
+                                                 final int parentLocal) {
+    int gbcLocal = generator.newLocal(ourGridBagConstraintsType);
+
+    generator.newInstance(ourGridBagConstraintsType);
+    generator.dup();
+    generator.invokeConstructor(ourGridBagConstraintsType, ourDefaultConstructor);
+    generator.storeLocal(gbcLocal);
+
+    GridBagConstraints defaults = new GridBagConstraints();
+    if (defaults.gridx != constraints.gridx) {
+      setIntField(generator, gbcLocal, "gridx", constraints.gridx);
+    }
+    if (defaults.gridy != constraints.gridy) {
+      setIntField(generator, gbcLocal, "gridy", constraints.gridy);
+    }
+    if (defaults.gridwidth != constraints.gridwidth) {
+      setIntField(generator, gbcLocal, "gridwidth", constraints.gridwidth);
+    }
+    if (defaults.gridheight != constraints.gridheight) {
+      setIntField(generator, gbcLocal, "gridheight", constraints.gridheight);
+    }
+    if (defaults.weightx != constraints.weightx) {
+      setDoubleField(generator, gbcLocal, "weightx", constraints.weightx);
+    }
+    if (defaults.weighty != constraints.weighty) {
+      setDoubleField(generator, gbcLocal, "weighty", constraints.weighty);
+    }
+    if (defaults.anchor != constraints.anchor) {
+      setIntField(generator, gbcLocal, "anchor", constraints.anchor);
+    }
+    if (defaults.fill != constraints.fill) {
+      setIntField(generator, gbcLocal, "fill", constraints.fill);
+    }
+    if (defaults.ipadx != constraints.ipadx) {
+      setIntField(generator, gbcLocal, "ipadx", constraints.ipadx);
+    }
+    if (defaults.ipady != constraints.ipady) {
+      setIntField(generator, gbcLocal, "ipady", constraints.ipady);
+    }
+    if (!defaults.insets.equals(constraints.insets)) {
+      generator.loadLocal(gbcLocal);
+      AsmCodeGenerator.pushPropValue(generator, Insets.class.getName(), constraints.insets);
+      generator.putField(ourGridBagConstraintsType, "insets", Type.getType(Insets.class));
+    }
+
+    generator.loadLocal(parentLocal);
+    generator.loadLocal(componentLocal);
+    generator.loadLocal(gbcLocal);
+
+    generator.invokeVirtual(ourContainerType, ourAddMethod);
+  }
+
+  private static void checkSetSize(final GeneratorAdapter generator, final int componentLocal, final String methodName, final Dimension dimension) {
+    if (dimension != null) {
+      generator.loadLocal(componentLocal);
+      AsmCodeGenerator.pushPropValue(generator, "java.awt.Dimension", dimension);
+      generator.invokeVirtual(Type.getType(Component.class),
+                              new Method(methodName, Type.VOID_TYPE, new Type[] { Type.getType(Dimension.class) }));
+    }
+  }
+
+  private static void setIntField(final GeneratorAdapter generator, final int local, final String fieldName, final int value) {
+    generator.loadLocal(local);
+    generator.push(value);
+    generator.putField(ourGridBagConstraintsType, fieldName, Type.INT_TYPE);
+  }
+
+  private static void setDoubleField(final GeneratorAdapter generator, final int local, final String fieldName, final double value) {
+    generator.loadLocal(local);
+    generator.push(value);
+    generator.putField(ourGridBagConstraintsType, fieldName, Type.DOUBLE_TYPE);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/GridLayoutCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/GridLayoutCodeGenerator.java
new file mode 100644
index 0000000..954f166
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/GridLayoutCodeGenerator.java
@@ -0,0 +1,103 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.uiDesigner.core.GridConstraints;
+import com.intellij.uiDesigner.core.GridLayoutManager;
+import com.intellij.uiDesigner.lw.LwComponent;
+import com.intellij.uiDesigner.lw.LwContainer;
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+/**
+ * @author yole
+ * @noinspection HardCodedStringLiteral
+ */
+public class GridLayoutCodeGenerator extends LayoutCodeGenerator {
+  private static final Method myInitConstraintsMethod = Method.getMethod("void <init> (int,int,int,int,int,int,int,int,java.awt.Dimension,java.awt.Dimension,java.awt.Dimension)");
+  private static final Method myInitConstraintsIndentMethod = Method.getMethod("void <init> (int,int,int,int,int,int,int,int,java.awt.Dimension,java.awt.Dimension,java.awt.Dimension,int)");
+  private static final Method myInitConstraintsIndentParentMethod = Method.getMethod("void <init> (int,int,int,int,int,int,int,int,java.awt.Dimension,java.awt.Dimension,java.awt.Dimension,int,boolean)");
+  private static final Method ourGridLayoutManagerConstructor = Method.getMethod("void <init> (int,int,java.awt.Insets,int,int,boolean,boolean)");
+  private static final Type myGridLayoutManagerType = Type.getType(GridLayoutManager.class);
+  private static final Type myGridConstraintsType = Type.getType(GridConstraints.class);
+
+  public static GridLayoutCodeGenerator INSTANCE = new GridLayoutCodeGenerator();
+
+  public void generateContainerLayout(final LwContainer lwContainer, final GeneratorAdapter generator, final int componentLocal) {
+    if (lwContainer.isGrid()) {
+      // arg 0: object
+      generator.loadLocal(componentLocal);
+
+      // arg 1: layout
+      final GridLayoutManager layout = (GridLayoutManager)lwContainer.getLayout();
+
+      generator.newInstance(myGridLayoutManagerType);
+      generator.dup();
+      generator.push(layout.getRowCount());
+      generator.push(layout.getColumnCount());
+      AsmCodeGenerator.pushPropValue(generator, "java.awt.Insets", layout.getMargin());
+      generator.push(layout.getHGap());
+      generator.push(layout.getVGap());
+      generator.push(layout.isSameSizeHorizontally());
+      generator.push(layout.isSameSizeVertically());
+      generator.invokeConstructor(myGridLayoutManagerType, ourGridLayoutManagerConstructor);
+
+      generator.invokeVirtual(ourContainerType, ourSetLayoutMethod);
+    }
+  }
+
+  public void generateComponentLayout(final LwComponent lwComponent,
+                                      final GeneratorAdapter generator,
+                                      final int componentLocal,
+                                      final int parentLocal) {
+    generator.loadLocal(parentLocal);
+    generator.loadLocal(componentLocal);
+    addNewGridConstraints(generator, lwComponent);
+    generator.invokeVirtual(ourContainerType, ourAddMethod);
+  }
+
+  private static void addNewGridConstraints(final GeneratorAdapter generator, final LwComponent lwComponent) {
+    final GridConstraints constraints = lwComponent.getConstraints();
+
+    generator.newInstance(myGridConstraintsType);
+    generator.dup();
+    generator.push(constraints.getRow());
+    generator.push(constraints.getColumn());
+    generator.push(constraints.getRowSpan());
+    generator.push(constraints.getColSpan());
+    generator.push(constraints.getAnchor());
+    generator.push(constraints.getFill());
+    generator.push(constraints.getHSizePolicy());
+    generator.push(constraints.getVSizePolicy());
+    newDimensionOrNull(generator, constraints.myMinimumSize);
+    newDimensionOrNull(generator, constraints.myPreferredSize);
+    newDimensionOrNull(generator, constraints.myMaximumSize);
+
+    if (constraints.isUseParentLayout()) {
+      generator.push(constraints.getIndent());
+      generator.push(constraints.isUseParentLayout());
+      generator.invokeConstructor(myGridConstraintsType, myInitConstraintsIndentParentMethod);
+    }
+    else if (constraints.getIndent() != 0) {
+      generator.push(constraints.getIndent());
+      generator.invokeConstructor(myGridConstraintsType, myInitConstraintsIndentMethod);
+    }
+    else {
+      generator.invokeConstructor(myGridConstraintsType, myInitConstraintsMethod);
+    }
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/IconPropertyCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/IconPropertyCodeGenerator.java
new file mode 100644
index 0000000..c435704
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/IconPropertyCodeGenerator.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.
+ */
+package com.intellij.uiDesigner.compiler;
+
+import com.intellij.uiDesigner.lw.IconDescriptor;
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+import javax.swing.*;
+
+/**
+ * @author yole
+ */
+public class IconPropertyCodeGenerator extends PropertyCodeGenerator {
+  private static final Type ourImageIconType = Type.getType(ImageIcon.class);
+  private static final Method ourInitMethod = Method.getMethod("void <init>(java.net.URL)");
+  private static final Method ourGetResourceMethod = Method.getMethod("java.net.URL getResource(java.lang.String)");
+  private static final Method ourGetClassMethod = new Method("getClass", "()Ljava/lang/Class;");
+  private static final Type ourObjectType = Type.getType(Object.class);
+  private static final Type ourClassType = Type.getType(Class.class);
+
+  public void generatePushValue(final GeneratorAdapter generator, final Object value) {
+    IconDescriptor descriptor = (IconDescriptor) value;
+    generator.newInstance(ourImageIconType);
+    generator.dup();
+
+    generator.loadThis();
+    generator.invokeVirtual(ourObjectType, ourGetClassMethod);
+    generator.push("/" + descriptor.getIconPath());
+    generator.invokeVirtual(ourClassType, ourGetResourceMethod);
+
+    generator.invokeConstructor(ourImageIconType, ourInitMethod);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/InsetsPropertyCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/InsetsPropertyCodeGenerator.java
new file mode 100644
index 0000000..fb516ca
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/InsetsPropertyCodeGenerator.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 com.intellij.uiDesigner.compiler;
+
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+import java.awt.*;
+
+/**
+ * @author yole
+ */
+public class InsetsPropertyCodeGenerator extends PropertyCodeGenerator {
+  private final Type myInsetsType = Type.getType(Insets.class);
+
+  public void generatePushValue(final GeneratorAdapter generator, final Object value) {
+    final Insets insets = (Insets)value;
+    generator.newInstance(myInsetsType);
+    generator.dup();
+    generator.push(insets.top);
+    generator.push(insets.left);
+    generator.push(insets.bottom);
+    generator.push(insets.right);
+    generator.invokeConstructor(myInsetsType, Method.getMethod("void <init>(int,int,int,int)"));
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/LayoutCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/LayoutCodeGenerator.java
new file mode 100644
index 0000000..f5d2594
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/LayoutCodeGenerator.java
@@ -0,0 +1,55 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.uiDesigner.lw.LwComponent;
+import com.intellij.uiDesigner.lw.LwContainer;
+import org.jetbrains.asm4.Opcodes;
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+import java.awt.*;
+
+/**
+ * @author yole
+ * @noinspection HardCodedStringLiteral
+ */
+public abstract class LayoutCodeGenerator {
+  protected static final Method ourSetLayoutMethod = Method.getMethod("void setLayout(java.awt.LayoutManager)");
+  protected static final Type ourContainerType = Type.getType(Container.class);
+  protected static final Method ourAddMethod = Method.getMethod("void add(java.awt.Component,java.lang.Object)");
+  protected static final Method ourAddNoConstraintMethod = Method.getMethod("java.awt.Component add(java.awt.Component)");
+
+  public void generateContainerLayout(final LwContainer lwContainer, final GeneratorAdapter generator, final int componentLocal) {
+  }
+
+  public abstract void generateComponentLayout(final LwComponent lwComponent, final GeneratorAdapter generator, final int componentLocal,
+                                               final int parentLocal);
+
+  protected static void newDimensionOrNull(final GeneratorAdapter generator, final Dimension dimension) {
+    if (dimension.width == -1 && dimension.height == -1) {
+      generator.visitInsn(Opcodes.ACONST_NULL);
+    }
+    else {
+      AsmCodeGenerator.pushPropValue(generator, "java.awt.Dimension", dimension);
+    }
+  }
+
+  public String mapComponentClass(final String componentClassName) {
+    return componentClassName;
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/ListModelPropertyCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/ListModelPropertyCodeGenerator.java
new file mode 100644
index 0000000..93ed130
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/ListModelPropertyCodeGenerator.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.
+ */
+
+package com.intellij.uiDesigner.compiler;
+
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+/**
+ * @author yole
+ */
+public class ListModelPropertyCodeGenerator extends PropertyCodeGenerator {
+  private final Type myListModelType;
+  private static final Method ourInitMethod = Method.getMethod("void <init>()");
+  private static final Method ourAddElementMethod = Method.getMethod("void addElement(java.lang.Object)");
+
+  public ListModelPropertyCodeGenerator(final Class aClass) {
+    myListModelType = Type.getType(aClass);
+  }
+
+  public void generatePushValue(final GeneratorAdapter generator, final Object value) {
+    String[] items = (String[]) value;
+    int listModelLocal = generator.newLocal(myListModelType);
+
+    generator.newInstance(myListModelType);
+    generator.dup();
+    generator.invokeConstructor(myListModelType, ourInitMethod);
+    generator.storeLocal(listModelLocal);
+
+    for(int i=0; i<items.length; i++) {
+      generator.loadLocal(listModelLocal);
+      generator.push(items [i]);
+      generator.invokeVirtual(myListModelType, ourAddElementMethod);
+    }
+
+    generator.loadLocal(listModelLocal);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/NestedFormLoader.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/NestedFormLoader.java
new file mode 100644
index 0000000..f3f0c23
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/NestedFormLoader.java
@@ -0,0 +1,27 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.uiDesigner.lw.LwRootContainer;
+
+/**
+ * @author yole
+ */
+public interface NestedFormLoader {
+  LwRootContainer loadForm(String formFileName) throws Exception;
+  String getClassToBindName(LwRootContainer container);
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/PropertyCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/PropertyCodeGenerator.java
new file mode 100644
index 0000000..b3c1aeb
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/PropertyCodeGenerator.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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.compiler.instrumentation.InstrumentationClassFinder;
+import com.intellij.uiDesigner.lw.LwComponent;
+import com.intellij.uiDesigner.lw.LwIntrospectedProperty;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+
+import java.io.IOException;
+
+/**
+ * @author yole
+ */
+public abstract class PropertyCodeGenerator {
+  public abstract void generatePushValue(final GeneratorAdapter generator, final Object value);
+
+  public boolean generateCustomSetValue(final LwComponent lwComponent,
+                                        final InstrumentationClassFinder.PseudoClass componentClass, final LwIntrospectedProperty property,
+                                        final GeneratorAdapter generator,
+                                        final int componentLocal, final String formClassName) throws IOException, ClassNotFoundException {
+    return false;
+  }
+
+  public void generateClassStart(AsmCodeGenerator.FormClassVisitor visitor, final String name, final InstrumentationClassFinder classFinder) {
+  }
+
+  public void generateClassEnd(AsmCodeGenerator.FormClassVisitor visitor) {
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/RectanglePropertyCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/RectanglePropertyCodeGenerator.java
new file mode 100644
index 0000000..fb86b08
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/RectanglePropertyCodeGenerator.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 com.intellij.uiDesigner.compiler;
+
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+import java.awt.*;
+
+/**
+ * @author yole
+ */
+public class RectanglePropertyCodeGenerator extends PropertyCodeGenerator {
+  private static final Type myRectangleType = Type.getType(Rectangle.class);
+  private static final Method myInitMethod = Method.getMethod("void <init>(int,int,int,int)");
+
+  public void generatePushValue(final GeneratorAdapter generator, final Object value) {
+    final Rectangle rc = (Rectangle) value;
+    generator.newInstance(myRectangleType);
+    generator.dup();
+    generator.push(rc.x);
+    generator.push(rc.y);
+    generator.push(rc.width);
+    generator.push(rc.height);
+    generator.invokeConstructor(myRectangleType, myInitMethod);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/RecursiveFormNestingException.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/RecursiveFormNestingException.java
new file mode 100644
index 0000000..49450e0
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/RecursiveFormNestingException.java
@@ -0,0 +1,26 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+/**
+ * @author yole
+ */
+public class RecursiveFormNestingException extends UIDesignerException{
+  public RecursiveFormNestingException() {
+    super("Recursive form nesting is not allowed");
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/ScrollPaneLayoutCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/ScrollPaneLayoutCodeGenerator.java
new file mode 100644
index 0000000..6872500
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/ScrollPaneLayoutCodeGenerator.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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.uiDesigner.lw.LwComponent;
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+import javax.swing.*;
+
+/**
+ * @author yole
+ */
+public class ScrollPaneLayoutCodeGenerator extends LayoutCodeGenerator {
+  private final Type myScrollPaneType = Type.getType(JScrollPane.class);
+  private final Method mySetViewportViewMethod = Method.getMethod("void setViewportView(java.awt.Component)");
+
+  public void generateComponentLayout(final LwComponent lwComponent,
+                                      final GeneratorAdapter generator,
+                                      final int componentLocal,
+                                      final int parentLocal) {
+    generator.loadLocal(parentLocal);
+    generator.loadLocal(componentLocal);
+    generator.invokeVirtual(myScrollPaneType, mySetViewportViewMethod);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/SimpleLayoutCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/SimpleLayoutCodeGenerator.java
new file mode 100644
index 0000000..5836b6c
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/SimpleLayoutCodeGenerator.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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.uiDesigner.lw.LwComponent;
+import com.intellij.uiDesigner.lw.LwContainer;
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+/**
+ * Layout code generator shared between BorderLayout and CardLayout.
+ *
+ * @author yole
+ */
+public class SimpleLayoutCodeGenerator extends LayoutCodeGenerator {
+  protected final Type myLayoutType;
+  private static final Method ourConstructor = Method.getMethod("void <init>(int,int)");
+
+  public SimpleLayoutCodeGenerator(final Type layoutType) {
+    myLayoutType = layoutType;
+  }
+
+  public void generateContainerLayout(final LwContainer lwContainer, final GeneratorAdapter generator, final int componentLocal) {
+    generator.loadLocal(componentLocal);
+
+    generator.newInstance(myLayoutType);
+    generator.dup();
+    generator.push(Utils.getHGap(lwContainer.getLayout()));
+    generator.push(Utils.getVGap(lwContainer.getLayout()));
+
+    generator.invokeConstructor(myLayoutType, ourConstructor);
+
+    generator.invokeVirtual(ourContainerType, ourSetLayoutMethod);
+  }
+
+  public void generateComponentLayout(final LwComponent lwComponent,
+                                      final GeneratorAdapter generator,
+                                      final int componentLocal,
+                                      final int parentLocal) {
+    generator.loadLocal(parentLocal);
+    generator.loadLocal(componentLocal);
+    generator.push((String) lwComponent.getCustomLayoutConstraints());
+    generator.invokeVirtual(ourContainerType, ourAddMethod);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/SplitPaneLayoutCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/SplitPaneLayoutCodeGenerator.java
new file mode 100644
index 0000000..28533f4
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/SplitPaneLayoutCodeGenerator.java
@@ -0,0 +1,47 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.uiDesigner.lw.LwComponent;
+import com.intellij.uiDesigner.lw.LwSplitPane;
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+import javax.swing.*;
+
+/**
+ * @author yole
+ */
+public class SplitPaneLayoutCodeGenerator extends LayoutCodeGenerator {
+  private final Type mySplitPaneType = Type.getType(JSplitPane.class);
+  private final Method mySetLeftMethod = Method.getMethod("void setLeftComponent(java.awt.Component)");
+  private final Method mySetRightMethod = Method.getMethod("void setRightComponent(java.awt.Component)");
+
+  public void generateComponentLayout(final LwComponent lwComponent,
+                                      final GeneratorAdapter generator,
+                                      final int componentLocal,
+                                      final int parentLocal) {
+    generator.loadLocal(parentLocal);
+    generator.loadLocal(componentLocal);
+    if (LwSplitPane.POSITION_LEFT.equals(lwComponent.getCustomLayoutConstraints())) {
+      generator.invokeVirtual(mySplitPaneType, mySetLeftMethod);
+    }
+    else {
+      generator.invokeVirtual(mySplitPaneType, mySetRightMethod);
+    }
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/StringPropertyCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/StringPropertyCodeGenerator.java
new file mode 100644
index 0000000..4438fd0
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/StringPropertyCodeGenerator.java
@@ -0,0 +1,244 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.compiler.instrumentation.InstrumentationClassFinder;
+import com.intellij.uiDesigner.core.SupportCode;
+import com.intellij.uiDesigner.lw.LwComponent;
+import com.intellij.uiDesigner.lw.LwIntrospectedProperty;
+import com.intellij.uiDesigner.lw.StringDescriptor;
+import org.jetbrains.asm4.Label;
+import org.jetbrains.asm4.MethodVisitor;
+import org.jetbrains.asm4.Opcodes;
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+import javax.swing.*;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.ResourceBundle;
+import java.util.Set;
+
+/**
+ * @author yole
+ */
+public class StringPropertyCodeGenerator extends PropertyCodeGenerator implements Opcodes {
+  private static final Type myResourceBundleType = Type.getType(ResourceBundle.class);
+  private final Method myGetBundleMethod = Method.getMethod("java.util.ResourceBundle getBundle(java.lang.String)");
+  private final Method myGetStringMethod = Method.getMethod("java.lang.String getString(java.lang.String)");
+  private static final Method myLoadLabelTextMethod = new Method(AsmCodeGenerator.LOAD_LABEL_TEXT_METHOD, Type.VOID_TYPE,
+                                                                 new Type[] { Type.getType(JLabel.class), Type.getType(String.class) } );
+  private static final Method myLoadButtonTextMethod = new Method(AsmCodeGenerator.LOAD_BUTTON_TEXT_METHOD, Type.VOID_TYPE,
+                                                                 new Type[] { Type.getType(AbstractButton.class), Type.getType(String.class) } );
+
+  private final Set myClassesRequiringLoadLabelText = new HashSet();
+  private final Set myClassesRequiringLoadButtonText = new HashSet();
+  private boolean myHaveSetDisplayedMnemonicIndex = false;
+
+  public void generateClassStart(AsmCodeGenerator.FormClassVisitor visitor, final String name, final InstrumentationClassFinder classFinder) {
+    myClassesRequiringLoadLabelText.remove(name);
+    myClassesRequiringLoadButtonText.remove(name);
+    try {
+      InstrumentationClassFinder.PseudoClass pseudo = classFinder.loadClass(AbstractButton.class.getName());
+      if (!pseudo.findMethods("getDisplayedMnemonicIndex").isEmpty()) {
+        myHaveSetDisplayedMnemonicIndex = true;
+      }
+    }
+    catch (Exception e) {
+      // ignore
+    }
+  }
+
+  public boolean generateCustomSetValue(final LwComponent lwComponent,
+                                        final InstrumentationClassFinder.PseudoClass componentClass,
+                                        final LwIntrospectedProperty property,
+                                        final GeneratorAdapter generator,
+                                        final int componentLocal,
+                                        final String formClassName) throws IOException, ClassNotFoundException {
+    final InstrumentationClassFinder.PseudoClass abstractButtonClass = componentClass.getFinder().loadClass(AbstractButton.class.getName());
+    final InstrumentationClassFinder.PseudoClass jLabelClass = componentClass.getFinder().loadClass(JLabel.class.getName());
+    if ("text".equals(property.getName()) &&
+        (abstractButtonClass.isAssignableFrom(componentClass) || jLabelClass.isAssignableFrom(componentClass))) {
+      final StringDescriptor propertyValue = (StringDescriptor)lwComponent.getPropertyValue(property);
+      if (propertyValue.getValue() != null) {
+        final SupportCode.TextWithMnemonic textWithMnemonic = SupportCode.parseText(propertyValue.getValue());
+        if (textWithMnemonic.myMnemonicIndex >= 0) {
+          generator.loadLocal(componentLocal);
+          generator.push(textWithMnemonic.myText);
+          generator.invokeVirtual(Type.getType(componentClass.getDescriptor()),
+                                  new Method(property.getWriteMethodName(),
+                                             Type.VOID_TYPE, new Type[] { Type.getType(String.class) } ));
+
+          String setMnemonicMethodName;
+          if (abstractButtonClass.isAssignableFrom(componentClass)) {
+            setMnemonicMethodName = "setMnemonic";
+          }
+          else {
+            setMnemonicMethodName = "setDisplayedMnemonic";
+          }
+
+          generator.loadLocal(componentLocal);
+          generator.push(textWithMnemonic.getMnemonicChar());
+          generator.invokeVirtual(Type.getType(componentClass.getDescriptor()),
+                                  new Method(setMnemonicMethodName,
+                                             Type.VOID_TYPE, new Type[] { Type.CHAR_TYPE } ));
+
+          if (myHaveSetDisplayedMnemonicIndex) {
+            generator.loadLocal(componentLocal);
+            generator.push(textWithMnemonic.myMnemonicIndex);
+            generator.invokeVirtual(Type.getType(componentClass.getDescriptor()),
+                                    new Method("setDisplayedMnemonicIndex",
+                                               Type.VOID_TYPE, new Type[] { Type.INT_TYPE } ));
+          }
+          return true;
+        }
+      }
+      else {
+        Method method;
+        if (abstractButtonClass.isAssignableFrom(componentClass)) {
+          myClassesRequiringLoadButtonText.add(formClassName);
+          method = myLoadButtonTextMethod;
+        }
+        else {
+          myClassesRequiringLoadLabelText.add(formClassName);
+          method = myLoadLabelTextMethod;
+        }
+
+        generator.loadThis();
+        generator.loadLocal(componentLocal);
+        generator.push(propertyValue.getBundleName());
+        generator.invokeStatic(myResourceBundleType, myGetBundleMethod);
+        generator.push(propertyValue.getKey());
+        generator.invokeVirtual(myResourceBundleType, myGetStringMethod);
+        generator.invokeVirtual(Type.getType("L" + formClassName + ";"), method);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public void generatePushValue(final GeneratorAdapter generator, final Object value) {
+    StringDescriptor descriptor = (StringDescriptor) value;
+    if (descriptor == null) {
+      generator.push((String)null);
+    }
+    else if (descriptor.getValue() !=null) {
+      generator.push(descriptor.getValue());
+    }
+    else {
+      generator.push(descriptor.getBundleName());
+      generator.invokeStatic(myResourceBundleType, myGetBundleMethod);
+      generator.push(descriptor.getKey());
+      generator.invokeVirtual(myResourceBundleType, myGetStringMethod);
+    }
+  }
+
+  public void generateClassEnd(AsmCodeGenerator.FormClassVisitor visitor) {
+    if (myClassesRequiringLoadLabelText.contains(visitor.getClassName())) {
+      generateLoadTextMethod(visitor, AsmCodeGenerator.LOAD_LABEL_TEXT_METHOD, "javax/swing/JLabel", "setDisplayedMnemonic");
+      myClassesRequiringLoadLabelText.remove(visitor.getClassName());
+    }
+    if (myClassesRequiringLoadButtonText.contains(visitor.getClassName())) {
+      generateLoadTextMethod(visitor, AsmCodeGenerator.LOAD_BUTTON_TEXT_METHOD, "javax/swing/AbstractButton", "setMnemonic");
+      myClassesRequiringLoadButtonText.remove(visitor.getClassName());
+    }
+  }
+
+  private void generateLoadTextMethod(final AsmCodeGenerator.FormClassVisitor visitor, final String methodName, final String componentClass,
+                                      final String setMnemonicMethodName) {
+    MethodVisitor mv = visitor.visitNewMethod(ACC_PRIVATE | ACC_SYNTHETIC, methodName, "(L" + componentClass + ";Ljava/lang/String;)V", null, null);
+    mv.visitCode();
+    mv.visitTypeInsn(NEW, "java/lang/StringBuffer");
+    mv.visitInsn(DUP);
+    mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "()V");
+    mv.visitVarInsn(ASTORE, 3);
+    mv.visitInsn(ICONST_0);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitInsn(ICONST_0);
+    mv.visitVarInsn(ISTORE, 5);
+    mv.visitInsn(ICONST_M1);
+    mv.visitVarInsn(ISTORE, 6);
+    mv.visitInsn(ICONST_0);
+    mv.visitVarInsn(ISTORE, 7);
+    Label l0 = new Label();
+    mv.visitLabel(l0);
+    mv.visitVarInsn(ILOAD, 7);
+    mv.visitVarInsn(ALOAD, 2);
+    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "length", "()I");
+    Label l1 = new Label();
+    mv.visitJumpInsn(IF_ICMPGE, l1);
+    mv.visitVarInsn(ALOAD, 2);
+    mv.visitVarInsn(ILOAD, 7);
+    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "charAt", "(I)C");
+    mv.visitIntInsn(BIPUSH, 38);
+    Label l2 = new Label();
+    mv.visitJumpInsn(IF_ICMPNE, l2);
+    mv.visitIincInsn(7, 1);
+    mv.visitVarInsn(ILOAD, 7);
+    mv.visitVarInsn(ALOAD, 2);
+    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "length", "()I");
+    Label l3 = new Label();
+    mv.visitJumpInsn(IF_ICMPNE, l3);
+    mv.visitJumpInsn(GOTO, l1);
+    mv.visitLabel(l3);
+    mv.visitVarInsn(ILOAD, 4);
+    mv.visitJumpInsn(IFNE, l2);
+    mv.visitVarInsn(ALOAD, 2);
+    mv.visitVarInsn(ILOAD, 7);
+    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "charAt", "(I)C");
+    mv.visitIntInsn(BIPUSH, 38);
+    mv.visitJumpInsn(IF_ICMPEQ, l2);
+    mv.visitInsn(ICONST_1);
+    mv.visitVarInsn(ISTORE, 4);
+    mv.visitVarInsn(ALOAD, 2);
+    mv.visitVarInsn(ILOAD, 7);
+    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "charAt", "(I)C");
+    mv.visitVarInsn(ISTORE, 5);
+    mv.visitVarInsn(ALOAD, 3);
+    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "length", "()I");
+    mv.visitVarInsn(ISTORE, 6);
+    mv.visitLabel(l2);
+    mv.visitVarInsn(ALOAD, 3);
+    mv.visitVarInsn(ALOAD, 2);
+    mv.visitVarInsn(ILOAD, 7);
+    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "charAt", "(I)C");
+    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(C)Ljava/lang/StringBuffer;");
+    mv.visitInsn(POP);
+    mv.visitIincInsn(7, 1);
+    mv.visitJumpInsn(GOTO, l0);
+    mv.visitLabel(l1);
+    mv.visitVarInsn(ALOAD, 1);
+    mv.visitVarInsn(ALOAD, 3);
+    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "toString", "()Ljava/lang/String;");
+    mv.visitMethodInsn(INVOKEVIRTUAL, componentClass, "setText", "(Ljava/lang/String;)V");
+    mv.visitVarInsn(ILOAD, 4);
+    Label l4 = new Label();
+    mv.visitJumpInsn(IFEQ, l4);
+    mv.visitVarInsn(ALOAD, 1);
+    mv.visitVarInsn(ILOAD, 5);
+    mv.visitMethodInsn(INVOKEVIRTUAL, componentClass, setMnemonicMethodName, "(C)V");
+    if (myHaveSetDisplayedMnemonicIndex) {
+      mv.visitVarInsn(ALOAD, 1);
+      mv.visitVarInsn(ILOAD, 6);
+      mv.visitMethodInsn(INVOKEVIRTUAL, componentClass, "setDisplayedMnemonicIndex", "(I)V");
+    }
+    mv.visitLabel(l4);
+    mv.visitInsn(RETURN);
+    mv.visitMaxs(3, 8);
+    mv.visitEnd();
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/TabbedPaneLayoutCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/TabbedPaneLayoutCodeGenerator.java
new file mode 100644
index 0000000..ee867bf
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/TabbedPaneLayoutCodeGenerator.java
@@ -0,0 +1,74 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.uiDesigner.lw.LwComponent;
+import com.intellij.uiDesigner.lw.LwTabbedPane;
+import org.jetbrains.asm4.Type;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+import javax.swing.*;
+
+/**
+ * @author yole
+ */
+public class TabbedPaneLayoutCodeGenerator extends LayoutCodeGenerator {
+  private final Type myTabbedPaneType = Type.getType(JTabbedPane.class);
+  private final Method myAddTabMethod = Method.getMethod("void addTab(java.lang.String,javax.swing.Icon,java.awt.Component,java.lang.String)");
+  private final Method mySetDisabledIconAtMethod = Method.getMethod("void setDisabledIconAt(int,javax.swing.Icon)");
+  private final Method mySetEnabledAtMethod = Method.getMethod("void setEnabledAt(int,boolean)");
+
+  public void generateComponentLayout(final LwComponent lwComponent,
+                                      final GeneratorAdapter generator,
+                                      final int componentLocal,
+                                      final int parentLocal) {
+    generator.loadLocal(parentLocal);
+    final LwTabbedPane.Constraints tabConstraints = (LwTabbedPane.Constraints)lwComponent.getCustomLayoutConstraints();
+    if (tabConstraints == null){
+      throw new IllegalArgumentException("tab constraints cannot be null: " + lwComponent.getId());
+    }
+    AsmCodeGenerator.pushPropValue(generator, String.class.getName(), tabConstraints.myTitle);
+    if (tabConstraints.myIcon == null) {
+      generator.push((String) null);
+    }
+    else {
+      AsmCodeGenerator.pushPropValue(generator, Icon.class.getName(), tabConstraints.myIcon);
+    }
+    generator.loadLocal(componentLocal);
+    if (tabConstraints.myToolTip == null) {
+      generator.push((String) null);
+    }
+    else {
+      AsmCodeGenerator.pushPropValue(generator, String.class.getName(), tabConstraints.myToolTip);
+    }
+    generator.invokeVirtual(myTabbedPaneType, myAddTabMethod);
+
+    int index = lwComponent.getParent().indexOfComponent(lwComponent);
+    if (tabConstraints.myDisabledIcon != null) {
+      generator.loadLocal(parentLocal);
+      generator.push(index);
+      AsmCodeGenerator.pushPropValue(generator, Icon.class.getName(), tabConstraints.myDisabledIcon);
+      generator.invokeVirtual(myTabbedPaneType, mySetDisabledIconAtMethod);
+    }
+    if (!tabConstraints.myEnabled) {
+      generator.loadLocal(parentLocal);
+      generator.push(index);
+      generator.push(tabConstraints.myEnabled);
+      generator.invokeVirtual(myTabbedPaneType, mySetEnabledAtMethod);
+    }
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/ToolBarLayoutCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/ToolBarLayoutCodeGenerator.java
new file mode 100644
index 0000000..b90a778
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/ToolBarLayoutCodeGenerator.java
@@ -0,0 +1,37 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.uiDesigner.lw.LwComponent;
+import org.jetbrains.asm4.commons.GeneratorAdapter;
+import org.jetbrains.asm4.commons.Method;
+
+/**
+ * @author yole
+ */
+public class ToolBarLayoutCodeGenerator extends LayoutCodeGenerator {
+  private final static Method ourAddMethod = Method.getMethod("java.awt.Component add(java.awt.Component)");
+
+  public void generateComponentLayout(final LwComponent lwComponent,
+                                      final GeneratorAdapter generator,
+                                      final int componentLocal,
+                                      final int parentLocal) {
+    generator.loadLocal(parentLocal);
+    generator.loadLocal(componentLocal);
+    generator.invokeVirtual(ourContainerType, ourAddMethod);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/UIDesignerException.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/UIDesignerException.java
new file mode 100644
index 0000000..4cfff00
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/UIDesignerException.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 com.intellij.uiDesigner.compiler;
+
+/**
+ * Marker interface for all UI Designer exceptions
+ * @author Eugene Zhuravlev
+ *         Date: 11/5/12
+ */
+public abstract class UIDesignerException extends Exception {
+
+  protected UIDesignerException() {
+  }
+
+  protected UIDesignerException(String message) {
+    super(message);
+  }
+
+  protected UIDesignerException(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+  protected UIDesignerException(Throwable cause) {
+    super(cause);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/UnexpectedFormElementException.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/UnexpectedFormElementException.java
new file mode 100644
index 0000000..d87e549
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/UnexpectedFormElementException.java
@@ -0,0 +1,27 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+/**
+ * @author yole
+ */
+public class UnexpectedFormElementException extends RuntimeException {
+
+  public UnexpectedFormElementException(String message) {
+    super(message);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/Utils.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/Utils.java
new file mode 100644
index 0000000..79fb947
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/Utils.java
@@ -0,0 +1,367 @@
+/*
+ * 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 com.intellij.uiDesigner.compiler;
+
+import com.intellij.compiler.instrumentation.InstrumentationClassFinder;
+import com.intellij.uiDesigner.core.GridConstraints;
+import com.intellij.uiDesigner.lw.*;
+import org.jdom.Document;
+import org.jdom.input.SAXBuilder;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import javax.swing.*;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import java.awt.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Anton Katilin
+ * @author Vladimir Kondratyev
+ *         <p/>
+ *         NOTE: the class must be compilable with JDK 1.3, so any methods and filds introduced in 1.4 or later must not be used
+ */
+public final class Utils {
+  public static final String FORM_NAMESPACE = "http://www.intellij.com/uidesigner/form/";
+  private static final SAXParser SAX_PARSER = createParser();
+
+  private Utils() {
+  }
+
+  private static SAXParser createParser() {
+    try {
+      return SAXParserFactory.newInstance().newSAXParser();
+    }
+    catch (Exception e) {
+      return null;
+    }
+  }
+
+  /**
+   * @param provider if null, no classes loaded and no properties read
+   */
+  public static LwRootContainer getRootContainer(final String formFileContent, final PropertiesProvider provider) throws Exception {
+    if (formFileContent.indexOf(FORM_NAMESPACE) == -1) {
+      throw new AlienFormFileException();
+    }
+
+    final Document document = new SAXBuilder().build(new StringReader(formFileContent), "UTF-8");
+
+    return getRootContainerFromDocument(document, provider);
+  }
+
+  /**
+   * Get root from the url
+   *
+   * @param formFile the document URL
+   * @param provider the provider
+   * @return the root container
+   * @throws Exception if there is a problem with parsing DOM
+   */
+  public static LwRootContainer getRootContainer(final URL formFile, final PropertiesProvider provider) throws Exception {
+    final Document document = new SAXBuilder().build(formFile);
+    return getRootContainerFromDocument(document, provider);
+  }
+
+
+  /**
+   * Get root from the document
+   *
+   * @param document the parsed document
+   * @param provider the provider
+   * @return the root container
+   * @throws Exception if there is a problem with parsing DOM
+   */
+  private static LwRootContainer getRootContainerFromDocument(Document document, PropertiesProvider provider) throws Exception {
+    final LwRootContainer root = new LwRootContainer();
+    root.read(document.getRootElement(), provider);
+    return root;
+  }
+
+  public static LwRootContainer getRootContainer(final InputStream stream, final PropertiesProvider provider) throws Exception {
+    final Document document = new SAXBuilder().build(stream, "UTF-8");
+
+    return getRootContainerFromDocument(document, provider);
+  }
+
+  public synchronized static String getBoundClassName(final String formFileContent) throws Exception {
+    if (formFileContent.indexOf(FORM_NAMESPACE) == -1) {
+      throw new AlienFormFileException();
+    }
+
+    final String[] className = new String[]{null};
+    try {
+      SAX_PARSER.parse(new InputSource(new StringReader(formFileContent)), new DefaultHandler() {
+        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+          if ("form".equals(qName)) {
+            className[0] = attributes.getValue("", "bind-to-class");
+            throw new SAXException("stop parsing");
+          }
+        }
+      });
+    }
+    catch (Exception e) {
+      // Do nothing.
+    }
+
+    return className[0];
+  }
+
+  /**
+   * Validates that specified class represents {@link javax.swing.JComponent} with
+   * empty constructor.
+   *
+   * @return descriptive human readable error message or <code>null</code> if
+   *         no errors were detected.
+   */
+    public static String validateJComponentClass(final ClassLoader loader, final String className, final boolean validateConstructor) {
+        if (loader == null) {
+      throw new IllegalArgumentException("loader cannot be null");
+    }
+    if (className == null) {
+      throw new IllegalArgumentException("className cannot be null");
+    }
+
+    // These classes are not visible for passed class loader!
+    if ("com.intellij.uiDesigner.HSpacer".equals(className) || "com.intellij.uiDesigner.VSpacer".equals(className)) {
+      return null;
+    }
+
+        final Class aClass;
+    try {
+            aClass = Class.forName(className, false, loader);
+    }
+    catch (final ClassNotFoundException exc) {
+      return "Class \"" + className + "\"not found";
+    }
+    catch (NoClassDefFoundError exc) {
+      return "Cannot load class " + className + ": " + exc.getMessage();
+    }
+    catch (ExceptionInInitializerError exc) {
+      return "Cannot initialize class " + className + ": " + exc.getMessage();
+    }
+    catch (UnsupportedClassVersionError exc) {
+      return "Unsupported class version error: " + className;
+    }
+
+    if (validateConstructor) {
+            try {
+                final Constructor constructor = aClass.getConstructor(new Class[0]);
+                if ((constructor.getModifiers() & Modifier.PUBLIC) == 0) {
+        return "Class \"" + className + "\" does not have default public constructor";
+      }
+    }
+            catch (final Exception exc) {
+                return "Class \"" + className + "\" does not have default constructor";
+            }
+        }
+
+    // Check that JComponent is accessible via the loader
+
+        if (!JComponent.class.isAssignableFrom(aClass)) {
+        return "Class \"" + className + "\" is not an instance of javax.swing.JComponent";
+      }
+
+    return null;
+  }
+
+  public static void validateNestedFormLoop(final String formName, final NestedFormLoader nestedFormLoader)
+    throws CodeGenerationException, RecursiveFormNestingException {
+    validateNestedFormLoop(formName, nestedFormLoader, null);
+  }
+
+  public static void validateNestedFormLoop(final String formName, final NestedFormLoader nestedFormLoader, final String targetForm)
+    throws CodeGenerationException, RecursiveFormNestingException {
+    HashSet usedFormNames = new HashSet();
+    if (targetForm != null) {
+      usedFormNames.add(targetForm);
+    }
+    validateNestedFormLoop(usedFormNames, formName, nestedFormLoader);
+  }
+
+  private static void validateNestedFormLoop(final Set usedFormNames, final String formName, final NestedFormLoader nestedFormLoader)
+    throws CodeGenerationException, RecursiveFormNestingException {
+    if (usedFormNames.contains(formName)) {
+      throw new RecursiveFormNestingException();
+    }
+    usedFormNames.add(formName);
+    final LwRootContainer rootContainer;
+    try {
+      rootContainer = nestedFormLoader.loadForm(formName);
+    }
+    catch (Exception e) {
+      throw new CodeGenerationException(null, "Error loading nested form: " + e.getMessage(), e);
+    }
+    final Set thisFormNestedForms = new HashSet();
+    final CodeGenerationException[] validateExceptions = new CodeGenerationException[1];
+    final RecursiveFormNestingException[] recursiveNestingExceptions = new RecursiveFormNestingException[1];
+    rootContainer.accept(new ComponentVisitor() {
+      public boolean visit(final IComponent component) {
+        if (component instanceof LwNestedForm) {
+          LwNestedForm nestedForm = (LwNestedForm)component;
+          if (!thisFormNestedForms.contains(nestedForm.getFormFileName())) {
+            thisFormNestedForms.add(nestedForm.getFormFileName());
+            try {
+              validateNestedFormLoop(usedFormNames, nestedForm.getFormFileName(), nestedFormLoader);
+            }
+            catch (RecursiveFormNestingException e) {
+              recursiveNestingExceptions[0] = e;
+              return false;
+            }
+            catch (CodeGenerationException e) {
+              validateExceptions[0] = e;
+              return false;
+            }
+          }
+        }
+        return true;
+      }
+    });
+    if (recursiveNestingExceptions[0] != null) {
+      throw recursiveNestingExceptions[0];
+    }
+    if (validateExceptions[0] != null) {
+      throw validateExceptions[0];
+    }
+  }
+
+  public static String findNotEmptyPanelWithXYLayout(final IComponent component) {
+    if (!(component instanceof IContainer)) {
+      return null;
+    }
+    final IContainer container = (IContainer)component;
+    if (container.getComponentCount() == 0) {
+      return null;
+    }
+    if (container.isXY()) {
+      return container.getId();
+    }
+    for (int i = 0; i < container.getComponentCount(); i++) {
+      String id = findNotEmptyPanelWithXYLayout(container.getComponent(i));
+      if (id != null) {
+        return id;
+      }
+    }
+    return null;
+  }
+
+  public static int getHGap(LayoutManager layout) {
+    if (layout instanceof BorderLayout) {
+      return ((BorderLayout)layout).getHgap();
+    }
+    if (layout instanceof CardLayout) {
+      return ((CardLayout)layout).getHgap();
+    }
+    return 0;
+  }
+
+  public static int getVGap(LayoutManager layout) {
+    if (layout instanceof BorderLayout) {
+      return ((BorderLayout)layout).getVgap();
+    }
+    if (layout instanceof CardLayout) {
+      return ((CardLayout)layout).getVgap();
+    }
+    return 0;
+  }
+
+  public static int getCustomCreateComponentCount(final IContainer container) {
+    final int[] result = new int[1];
+    result[0] = 0;
+    container.accept(new ComponentVisitor() {
+      public boolean visit(IComponent c) {
+        if (c.isCustomCreate()) {
+          result[0]++;
+        }
+        return true;
+      }
+    });
+    return result[0];
+  }
+
+  public static Class suggestReplacementClass(Class componentClass) {
+    while (true) {
+      componentClass = componentClass.getSuperclass();
+      if (componentClass.equals(JComponent.class)) {
+        return JPanel.class;
+      }
+      if ((componentClass.getModifiers() & (Modifier.ABSTRACT | Modifier.PRIVATE)) != 0) {
+        continue;
+      }
+      try {
+        componentClass.getConstructor(new Class[]{});
+      }
+      catch (NoSuchMethodException ex) {
+        continue;
+      }
+      return componentClass;
+    }
+  }
+
+  public static InstrumentationClassFinder.PseudoClass suggestReplacementClass(InstrumentationClassFinder.PseudoClass componentClass) throws ClassNotFoundException, IOException {
+    final InstrumentationClassFinder.PseudoClass jComponentClass = componentClass.getFinder().loadClass(JComponent.class.getName());
+    while (true) {
+      componentClass = componentClass.getSuperClass();
+      if (componentClass.equals(jComponentClass)) {
+        return componentClass.getFinder().loadClass(JPanel.class.getName());
+      }
+      if ((componentClass.getModifiers() & (Modifier.ABSTRACT | Modifier.PRIVATE)) != 0) {
+        continue;
+      }
+      if (!componentClass.hasDefaultPublicConstructor()) {
+        continue;
+      }
+      return componentClass;
+    }
+  }
+
+  public static int alignFromConstraints(final GridConstraints gc, final boolean horizontal) {
+    int anchor = gc.getAnchor();
+    int fill = gc.getFill();
+    int leftMask = horizontal ? GridConstraints.ANCHOR_WEST : GridConstraints.ANCHOR_NORTH;
+    int rightMask = horizontal ? GridConstraints.ANCHOR_EAST : GridConstraints.ANCHOR_SOUTH;
+    int fillMask = horizontal ? GridConstraints.FILL_HORIZONTAL : GridConstraints.FILL_VERTICAL;
+    if ((fill & fillMask) != 0) return GridConstraints.ALIGN_FILL;
+    if ((anchor & rightMask) != 0) return GridConstraints.ALIGN_RIGHT;
+    if ((anchor & leftMask) != 0) return GridConstraints.ALIGN_LEFT;
+    return GridConstraints.ALIGN_CENTER;
+  }
+
+  public static boolean isBoundField(IComponent component, String fieldName) {
+    if (fieldName.equals(component.getBinding())) {
+      return true;
+    }
+    if (component instanceof IContainer) {
+      IContainer container = (IContainer)component;
+      for (int i = 0; i < container.getComponentCount(); i++) {
+        if (isBoundField(container.getComponent(i), fieldName)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/BorderLayoutSerializer.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/BorderLayoutSerializer.java
new file mode 100644
index 0000000..b2b769c
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/BorderLayoutSerializer.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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+import com.intellij.uiDesigner.UIFormXmlConstants;
+
+import java.awt.*;
+
+/**
+ * @author yole
+ */
+public class BorderLayoutSerializer extends LayoutSerializer {
+  public static final BorderLayoutSerializer INSTANCE = new BorderLayoutSerializer();
+
+  private BorderLayoutSerializer() {
+  }
+
+  void readLayout(Element element, LwContainer container) {
+    final int hGap = LwXmlReader.getOptionalInt(element, UIFormXmlConstants.ATTRIBUTE_HGAP, 0);
+    final int vGap = LwXmlReader.getOptionalInt(element, UIFormXmlConstants.ATTRIBUTE_VGAP, 0);
+    container.setLayout(new BorderLayout(hGap, vGap));
+  }
+
+  void readChildConstraints(final Element constraintsElement, final LwComponent component) {
+    component.setCustomLayoutConstraints(LwXmlReader.getRequiredString(constraintsElement,
+                                                                       UIFormXmlConstants.ATTRIBUTE_BORDER_CONSTRAINT));
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/CardLayoutSerializer.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/CardLayoutSerializer.java
new file mode 100644
index 0000000..9371b5c
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/CardLayoutSerializer.java
@@ -0,0 +1,47 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import com.intellij.uiDesigner.UIFormXmlConstants;
+import org.jdom.Element;
+
+import java.awt.CardLayout;
+
+/**
+ * @author yole
+ */
+public class CardLayoutSerializer extends LayoutSerializer {
+  public static final CardLayoutSerializer INSTANCE = new CardLayoutSerializer();
+
+  private CardLayoutSerializer() {
+  }
+
+  void readLayout(Element element, LwContainer container) {
+    final int hGap = LwXmlReader.getOptionalInt(element, UIFormXmlConstants.ATTRIBUTE_HGAP, 0);
+    final int vGap = LwXmlReader.getOptionalInt(element, UIFormXmlConstants.ATTRIBUTE_VGAP, 0);
+    container.setLayout(new CardLayout(hGap, vGap));
+
+    String defaultCard = LwXmlReader.getOptionalString(element, UIFormXmlConstants.ATTRIBUTE_SHOW, null);
+    container.putClientProperty(UIFormXmlConstants.LAYOUT_CARD, defaultCard);
+  }
+
+  void readChildConstraints(final Element constraintsElement, final LwComponent component) {
+    final Element cardChild = LwXmlReader.getRequiredChild(constraintsElement, UIFormXmlConstants.ELEMENT_CARD);
+    final String name = LwXmlReader.getRequiredString(cardChild, UIFormXmlConstants.ATTRIBUTE_NAME);
+    component.setCustomLayoutConstraints(name);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/ColorDescriptor.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/ColorDescriptor.java
new file mode 100644
index 0000000..79dd473
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/ColorDescriptor.java
@@ -0,0 +1,137 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import javax.swing.*;
+import java.awt.*;
+import java.lang.reflect.Field;
+
+/**
+ * @author yole
+ */
+public class ColorDescriptor {
+  private final Color myColor;
+  private String mySwingColor;
+  private String mySystemColor;
+  private String myAWTColor;
+
+  public ColorDescriptor(final Color color) {
+    myColor = color;
+  }
+
+  public static ColorDescriptor fromSwingColor(final String swingColor) {
+    ColorDescriptor result = new ColorDescriptor(null);
+    result.mySwingColor = swingColor;
+    return result;
+  }
+
+  public static ColorDescriptor fromSystemColor(final String systemColor) {
+    ColorDescriptor result = new ColorDescriptor(null);
+    result.mySystemColor = systemColor;
+    return result;
+  }
+
+  public static ColorDescriptor fromAWTColor(final String awtColor) {
+    ColorDescriptor result = new ColorDescriptor(null);
+    result.myAWTColor = awtColor;
+    return result;
+  }
+
+  private static Color getColorField(final Class aClass, final String fieldName) {
+    try {
+      final Field field = aClass.getDeclaredField(fieldName);
+      return (Color)field.get(null);
+    }
+    catch (NoSuchFieldException e) {
+      return Color.black;
+    }
+    catch (IllegalAccessException e) {
+      return Color.black;
+    }
+  }
+
+  public Color getResolvedColor() {
+    if (myColor != null) {
+      return myColor;
+    }
+    if (mySwingColor != null) {
+      return UIManager.getColor(mySwingColor);
+    }
+    if (mySystemColor != null) {
+      return getColorField(SystemColor.class, mySystemColor);
+    }
+    if (myAWTColor != null) {
+      return getColorField(Color.class, myAWTColor);
+    }
+    return null;
+  }
+
+  public Color getColor() {
+    return myColor;
+  }
+
+  public String getSwingColor() {
+    return mySwingColor;
+  }
+
+  public String getSystemColor() {
+    return mySystemColor;
+  }
+
+  public String getAWTColor() {
+    return myAWTColor;
+  }
+
+  public String toString() {
+    if (mySwingColor != null) {
+      return mySwingColor;
+    }
+    if (mySystemColor != null) {
+      return mySystemColor;
+    }
+    if (myAWTColor != null) {
+      return myAWTColor;
+    }
+    if (myColor != null) {
+      return "[" + myColor.getRed() + "," + myColor.getGreen() + "," + myColor.getBlue() + "]";
+    }
+    return "null";
+  }
+
+  public boolean equals(Object obj) {
+    if (obj == null || !(obj instanceof ColorDescriptor)) {
+      return false;
+    }
+    ColorDescriptor rhs = (ColorDescriptor) obj;
+    if (myColor != null) {
+      return myColor.equals(rhs.myColor);
+    }
+    if (mySwingColor != null) {
+      return mySwingColor.equals(rhs.mySwingColor);
+    }
+    if (mySystemColor != null) {
+      return mySystemColor.equals(rhs.mySystemColor);
+    }
+    if (myAWTColor != null) {
+      return myAWTColor.equals(rhs.myAWTColor);
+    }
+    return false;
+  }
+
+  public boolean isColorSet() {
+    return myColor != null || mySwingColor != null || mySystemColor != null || myAWTColor != null;
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/CompiledClassPropertiesProvider.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/CompiledClassPropertiesProvider.java
new file mode 100644
index 0000000..bea75bb
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/CompiledClassPropertiesProvider.java
@@ -0,0 +1,163 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import com.intellij.uiDesigner.compiler.Utils;
+
+import javax.swing.*;
+import java.awt.*;
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+/**
+ * @author Anton Katilin
+ * @author Vladimir Kondratyev
+ */
+public final class CompiledClassPropertiesProvider implements PropertiesProvider {
+  private final ClassLoader myLoader;
+  private final HashMap myCache;
+
+  public CompiledClassPropertiesProvider(final ClassLoader loader) {
+    if (loader == null) {
+      throw new IllegalArgumentException("loader cannot be null");
+    }
+    myLoader = loader;
+    myCache = new HashMap();
+  }
+
+  public HashMap getLwProperties(final String className) {
+    if (myCache.containsKey(className)) {
+      return (HashMap)myCache.get(className);
+    }
+
+    if (Utils.validateJComponentClass(myLoader, className, false) != null) {
+      return null;
+    }
+
+    final Class aClass;
+    try {
+      aClass = Class.forName(className, false, myLoader);
+    }
+    catch (final ClassNotFoundException exc) {
+      throw new RuntimeException(exc.toString()); // should never happen
+    }
+
+    final BeanInfo beanInfo;
+    try {
+      beanInfo = Introspector.getBeanInfo(aClass);
+    }
+    catch (Throwable e) {
+      return null;
+    }
+
+    final HashMap result = new HashMap();
+    final PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
+    for (int i = 0; i < descriptors.length; i++) {
+      final PropertyDescriptor descriptor = descriptors[i];
+
+      final Method readMethod = descriptor.getReadMethod();
+      final Method writeMethod = descriptor.getWriteMethod();
+      if (writeMethod == null || readMethod == null) {
+        continue;
+      }
+
+      final String name = descriptor.getName();
+
+      final LwIntrospectedProperty property = propertyFromClass(descriptor.getPropertyType(), name);
+
+      if (property != null) {
+        property.setDeclaringClassName(descriptor.getReadMethod().getDeclaringClass().getName());
+        result.put(name, property);
+      }
+    }
+
+    myCache.put(className, result);
+
+    return result;
+  }
+
+  public static LwIntrospectedProperty propertyFromClass(final Class propertyType, final String name) {
+    LwIntrospectedProperty property = propertyFromClassName(propertyType.getName(), name);
+    if (property == null) {
+      if (Component.class.isAssignableFrom(propertyType)) {
+        property = new LwIntroComponentProperty(name, propertyType.getName());
+      }
+      else if (ListModel.class.isAssignableFrom(propertyType)) {
+        property = new LwIntroListModelProperty(name, propertyType.getName());
+      }
+      else if (propertyType.getSuperclass() != null && "java.lang.Enum".equals(propertyType.getSuperclass().getName())) {
+        property = new LwIntroEnumProperty(name, propertyType);
+      }
+    }
+    return property;
+  }
+
+  public static LwIntrospectedProperty propertyFromClassName(final String propertyClassName, final String name) {
+    final LwIntrospectedProperty property;
+    if (int.class.getName().equals(propertyClassName)) { // int
+      property = new LwIntroIntProperty(name);
+    }
+    else if (boolean.class.getName().equals(propertyClassName)) { // boolean
+      property = new LwIntroBooleanProperty(name);
+    }
+    else if (double.class.getName().equals(propertyClassName)) { // double
+      property = new LwIntroPrimitiveTypeProperty(name, Double.class);
+    }
+    else if (float.class.getName().equals(propertyClassName)) {
+      property = new LwIntroPrimitiveTypeProperty(name, Float.class);
+    }
+    else if (long.class.getName().equals(propertyClassName)) {
+      property = new LwIntroPrimitiveTypeProperty(name, Long.class);
+    }
+    else if (byte.class.getName().equals(propertyClassName)) {
+      property = new LwIntroPrimitiveTypeProperty(name, Byte.class);
+    }
+    else if (short.class.getName().equals(propertyClassName)) {
+      property = new LwIntroPrimitiveTypeProperty(name, Short.class);
+    }
+    else if (char.class.getName().equals(propertyClassName)) {
+      property = new LwIntroCharProperty(name);
+    }
+    else if (String.class.getName().equals(propertyClassName)) { // java.lang.String
+      property = new LwRbIntroStringProperty(name);
+    }
+    else if ("java.awt.Insets".equals(propertyClassName)) { // java.awt.Insets
+      property = new LwIntroInsetsProperty(name);
+    }
+    else if ("java.awt.Dimension".equals(propertyClassName)) { // java.awt.Dimension
+      property = new LwIntroDimensionProperty(name);
+    }
+    else if ("java.awt.Rectangle".equals(propertyClassName)) { // java.awt.Rectangle
+      property = new LwIntroRectangleProperty(name);
+    }
+    else if ("java.awt.Color".equals(propertyClassName)) {
+      property = new LwIntroColorProperty(name);
+    }
+    else if ("java.awt.Font".equals(propertyClassName)) {
+      property = new LwIntroFontProperty(name);
+    }
+    else if ("javax.swing.Icon".equals(propertyClassName)) {
+      property = new LwIntroIconProperty(name);
+    }
+    else {
+      property = null;
+    }
+    return property;
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/ComponentVisitor.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/ComponentVisitor.java
new file mode 100644
index 0000000..8f94061
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/ComponentVisitor.java
@@ -0,0 +1,27 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+/**
+ * @author yole
+ */
+public interface ComponentVisitor {
+  /**
+   * @return true if iteration should continue
+   */
+  boolean visit(IComponent c);
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/FlowLayoutSerializer.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/FlowLayoutSerializer.java
new file mode 100644
index 0000000..8933ee3
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/FlowLayoutSerializer.java
@@ -0,0 +1,42 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import com.intellij.uiDesigner.UIFormXmlConstants;
+import org.jdom.Element;
+
+import java.awt.FlowLayout;
+
+/**
+ * @author yole
+ */
+public class FlowLayoutSerializer extends LayoutSerializer {
+  public static final FlowLayoutSerializer INSTANCE = new FlowLayoutSerializer();
+
+  private FlowLayoutSerializer() {
+  }
+
+  void readLayout(Element element, LwContainer container) {
+    final int hGap = LwXmlReader.getOptionalInt(element, UIFormXmlConstants.ATTRIBUTE_HGAP, 5);
+    final int vGap = LwXmlReader.getOptionalInt(element, UIFormXmlConstants.ATTRIBUTE_VGAP, 5);
+    final int flowAlign = LwXmlReader.getOptionalInt(element, UIFormXmlConstants.ATTRIBUTE_FLOW_ALIGN, FlowLayout.CENTER);
+    container.setLayout(new FlowLayout(flowAlign, hGap, vGap));
+  }
+
+  void readChildConstraints(final Element constraintsElement, final LwComponent component) {
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/FontDescriptor.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/FontDescriptor.java
new file mode 100644
index 0000000..a6250cb
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/FontDescriptor.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 com.intellij.uiDesigner.lw;
+
+import javax.swing.*;
+import java.awt.Font;
+
+/**
+ * @author yole
+ */
+public class FontDescriptor {
+  private String myFontName;
+  private int myFontSize;
+  private int myFontStyle;
+  private String mySwingFont;
+
+  private FontDescriptor() {
+  }
+
+  public FontDescriptor(final String fontName, final int fontStyle, final int fontSize) {
+    myFontName = fontName;
+    myFontSize = fontSize;
+    myFontStyle = fontStyle;
+  }
+
+  public boolean isFixedFont() {
+    return mySwingFont == null;
+  }
+
+  public boolean isFullyDefinedFont() {
+    return myFontName != null && myFontSize >= 0 && myFontStyle >= 0;
+  }
+
+  public static FontDescriptor fromSwingFont(String swingFont) {
+    FontDescriptor result = new FontDescriptor();
+    result.mySwingFont = swingFont;
+    return result;
+  }
+
+  public String getFontName() {
+    return myFontName;
+  }
+
+  public int getFontSize() {
+    return myFontSize;
+  }
+
+  public int getFontStyle() {
+    return myFontStyle;
+  }
+
+  public Font getFont(Font defaultFont) {
+    return new Font(myFontName != null ? myFontName : defaultFont.getFontName(),
+                    myFontStyle >= 0 ? myFontStyle : defaultFont.getStyle(),
+                    myFontSize >= 0 ? myFontSize : defaultFont.getSize());
+  }
+
+  public String getSwingFont() {
+    return mySwingFont;
+  }
+
+  public Font getResolvedFont(Font defaultFont) {
+    if (mySwingFont != null) {
+      return UIManager.getFont(mySwingFont);
+    }
+    return getFont(defaultFont);
+  }
+
+  public boolean equals(Object obj) {
+    if (obj == null || !(obj instanceof FontDescriptor)) {
+      return false;
+    }
+    FontDescriptor rhs = (FontDescriptor) obj;
+    if (mySwingFont != null) {
+      return mySwingFont.equals(rhs.mySwingFont);
+    }
+    else {
+      if (myFontName == null && rhs.myFontName != null) return false;
+      if (myFontName != null && rhs.myFontName == null) return false;
+      if (myFontName != null && !myFontName.equals(rhs.myFontName)) return false;
+      return myFontSize == rhs.myFontSize && myFontStyle == rhs.myFontStyle;
+    }
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/FormLayoutSerializer.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/FormLayoutSerializer.java
new file mode 100644
index 0000000..86544b9
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/FormLayoutSerializer.java
@@ -0,0 +1,105 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+import com.jgoodies.forms.layout.FormLayout;
+import com.jgoodies.forms.layout.RowSpec;
+import com.jgoodies.forms.layout.ColumnSpec;
+import com.jgoodies.forms.layout.CellConstraints;
+import com.intellij.uiDesigner.UIFormXmlConstants;
+import com.intellij.uiDesigner.compiler.Utils;
+
+import java.util.List;
+import java.util.Iterator;
+
+/**
+ * @author yole
+ */
+public class FormLayoutSerializer extends GridLayoutSerializer {
+  private FormLayoutSerializer() {
+  }
+
+  public static FormLayoutSerializer INSTANCE = new FormLayoutSerializer();
+
+  public static CellConstraints.Alignment[] ourHorizontalAlignments = {
+    CellConstraints.LEFT, CellConstraints.CENTER, CellConstraints.RIGHT, CellConstraints.FILL
+  };
+  public static CellConstraints.Alignment[] ourVerticalAlignments = {
+    CellConstraints.TOP, CellConstraints.CENTER, CellConstraints.BOTTOM, CellConstraints.FILL
+  };
+
+  void readLayout(Element element, LwContainer container) {
+    FormLayout layout = new FormLayout();
+    final List rowSpecs = element.getChildren(UIFormXmlConstants.ELEMENT_ROWSPEC, element.getNamespace());
+    for (Iterator iterator = rowSpecs.iterator(); iterator.hasNext();) {
+      Element rowSpecElement = (Element) iterator.next();
+      final String spec = LwXmlReader.getRequiredString(rowSpecElement, UIFormXmlConstants.ATTRIBUTE_VALUE);
+      layout.appendRow(new RowSpec(spec));
+    }
+
+    final List colSpecs = element.getChildren(UIFormXmlConstants.ELEMENT_COLSPEC, element.getNamespace());
+    for (Iterator iterator = colSpecs.iterator(); iterator.hasNext();) {
+      Element colSpecElement = (Element) iterator.next();
+      final String spec = LwXmlReader.getRequiredString(colSpecElement, UIFormXmlConstants.ATTRIBUTE_VALUE);
+      layout.appendColumn(new ColumnSpec(spec));
+    }
+
+    int[][] rowGroups = readGroups(element, UIFormXmlConstants.ELEMENT_ROWGROUP);
+    int[][] colGroups = readGroups(element, UIFormXmlConstants.ELEMENT_COLGROUP);
+    if (rowGroups != null) {
+      layout.setRowGroups(rowGroups);
+    }
+    if (colGroups != null) {
+      layout.setColumnGroups(colGroups);
+    }
+    container.setLayout(layout);
+  }
+
+  private static int[][] readGroups(final Element element, final String elementName) {
+    final List groupElements = element.getChildren(elementName, element.getNamespace());
+    if (groupElements.size() == 0) return null;
+    int[][] groups = new int[groupElements.size()][];
+    for(int i=0; i<groupElements.size(); i++) {
+      Element groupElement = (Element) groupElements.get(i);
+      List groupMembers = groupElement.getChildren(UIFormXmlConstants.ELEMENT_MEMBER, element.getNamespace());
+      groups [i] = new int[groupMembers.size()];
+      for(int j=0; j<groupMembers.size(); j++) {
+        groups [i][j] = LwXmlReader.getRequiredInt((Element) groupMembers.get(j), UIFormXmlConstants.ATTRIBUTE_INDEX);
+      }
+    }
+    return groups;
+  }
+
+  void readChildConstraints(final Element constraintsElement, final LwComponent component) {
+    super.readChildConstraints(constraintsElement, component);
+    CellConstraints cc = new CellConstraints();
+    final Element formsElement = LwXmlReader.getChild(constraintsElement, UIFormXmlConstants.ELEMENT_FORMS);
+    if (formsElement != null) {
+      if (formsElement.getAttributeValue(UIFormXmlConstants.ATTRIBUTE_TOP) != null) {
+        cc.insets = LwXmlReader.readInsets(formsElement);
+      }
+      if (!LwXmlReader.getOptionalBoolean(formsElement, UIFormXmlConstants.ATTRIBUTE_DEFAULTALIGN_HORZ, true)) {
+        cc.hAlign = ourHorizontalAlignments [Utils.alignFromConstraints(component.getConstraints(), true)];
+      }
+      if (!LwXmlReader.getOptionalBoolean(formsElement, UIFormXmlConstants.ATTRIBUTE_DEFAULTALIGN_VERT, true)) {
+        cc.vAlign = ourVerticalAlignments [Utils.alignFromConstraints(component.getConstraints(), false)];
+      }
+    }
+    component.setCustomLayoutConstraints(cc);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/GridBagLayoutSerializer.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/GridBagLayoutSerializer.java
new file mode 100644
index 0000000..a895b5d
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/GridBagLayoutSerializer.java
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+/*
+ * Created by IntelliJ IDEA.
+ * User: yole
+ * Date: 30.08.2006
+ * Time: 13:02:12
+ */
+package com.intellij.uiDesigner.lw;
+
+import com.intellij.uiDesigner.compiler.GridBagConverter;
+import com.intellij.uiDesigner.UIFormXmlConstants;
+import org.jdom.Element;
+
+import java.awt.*;
+
+public class GridBagLayoutSerializer extends GridLayoutSerializer {
+  private GridBagLayoutSerializer() {
+  }
+
+  public static GridBagLayoutSerializer INSTANCE = new GridBagLayoutSerializer();
+
+  void readLayout(Element element, LwContainer container) {
+    container.setLayout(new GridBagLayout());
+  }
+
+  void readChildConstraints(final Element constraintsElement, final LwComponent component) {
+    super.readChildConstraints(constraintsElement, component);
+    GridBagConstraints gbc = new GridBagConstraints();
+    GridBagConverter.constraintsToGridBag(component.getConstraints(), gbc);
+    final Element gridBagElement = LwXmlReader.getChild(constraintsElement, UIFormXmlConstants.ELEMENT_GRIDBAG);
+    if (gridBagElement != null) {
+      if (gridBagElement.getAttributeValue(UIFormXmlConstants.ATTRIBUTE_TOP) != null) {
+        gbc.insets = LwXmlReader.readInsets(gridBagElement);
+      }
+      gbc.weightx = LwXmlReader.getOptionalDouble(gridBagElement, UIFormXmlConstants.ATTRIBUTE_WEIGHTX, 0.0);
+      gbc.weighty = LwXmlReader.getOptionalDouble(gridBagElement, UIFormXmlConstants.ATTRIBUTE_WEIGHTY, 0.0);
+      gbc.ipadx = LwXmlReader.getOptionalInt(gridBagElement, UIFormXmlConstants.ATTRIBUTE_IPADX, 0);
+      gbc.ipady = LwXmlReader.getOptionalInt(gridBagElement, UIFormXmlConstants.ATTRIBUTE_IPADY, 0);
+    }
+    component.setCustomLayoutConstraints(gbc);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/GridLayoutSerializer.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/GridLayoutSerializer.java
new file mode 100644
index 0000000..274daa9
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/GridLayoutSerializer.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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+import com.intellij.uiDesigner.UIFormXmlConstants;
+import com.intellij.uiDesigner.core.GridLayoutManager;
+import com.intellij.uiDesigner.core.GridConstraints;
+
+import java.awt.*;
+
+/**
+ * @author yole
+ */
+public class GridLayoutSerializer extends LayoutSerializer {
+  protected GridLayoutSerializer() {
+  }
+
+  public static GridLayoutSerializer INSTANCE = new GridLayoutSerializer();
+
+  void readLayout(Element element, LwContainer container) {
+    final int rowCount = LwXmlReader.getRequiredInt(element, UIFormXmlConstants.ATTRIBUTE_ROW_COUNT);
+    final int columnCount = LwXmlReader.getRequiredInt(element, UIFormXmlConstants.ATTRIBUTE_COLUMN_COUNT);
+
+    final int hGap = LwXmlReader.getOptionalInt(element, UIFormXmlConstants.ATTRIBUTE_HGAP, -1);
+    final int vGap = LwXmlReader.getOptionalInt(element, UIFormXmlConstants.ATTRIBUTE_VGAP, -1);
+
+    // attribute is optional for compatibility with IDEA 4.0 forms
+    final boolean sameSizeHorizontally = LwXmlReader.getOptionalBoolean(element, UIFormXmlConstants.ATTRIBUTE_SAME_SIZE_HORIZONTALLY, false);
+    final boolean sameSizeVertically = LwXmlReader.getOptionalBoolean(element, UIFormXmlConstants.ATTRIBUTE_SAME_SIZE_VERTICALLY, false);
+
+    final Element marginElement = LwXmlReader.getRequiredChild(element, "margin");
+    final Insets margin = new Insets(
+      LwXmlReader.getRequiredInt(marginElement,"top"),
+      LwXmlReader.getRequiredInt(marginElement,"left"),
+      LwXmlReader.getRequiredInt(marginElement,"bottom"),
+      LwXmlReader.getRequiredInt(marginElement,"right")
+    );
+
+    final GridLayoutManager layout = new GridLayoutManager(rowCount, columnCount);
+    layout.setMargin(margin);
+    layout.setVGap(vGap);
+    layout.setHGap(hGap);
+    layout.setSameSizeHorizontally(sameSizeHorizontally);
+    layout.setSameSizeVertically(sameSizeVertically);
+    container.setLayout(layout);
+  }
+
+  void readChildConstraints(final Element constraintsElement, final LwComponent component) {
+    // Read Grid constraints
+    final Element gridElement = LwXmlReader.getChild(constraintsElement, "grid");
+    if(gridElement != null){
+      final GridConstraints constraints=new GridConstraints();
+
+      constraints.setRow(LwXmlReader.getRequiredInt(gridElement, "row"));
+      constraints.setColumn(LwXmlReader.getRequiredInt(gridElement, "column"));
+      constraints.setRowSpan(LwXmlReader.getRequiredInt(gridElement, "row-span"));
+      constraints.setColSpan(LwXmlReader.getRequiredInt(gridElement, "col-span"));
+      constraints.setVSizePolicy(LwXmlReader.getRequiredInt(gridElement, "vsize-policy"));
+      constraints.setHSizePolicy(LwXmlReader.getRequiredInt(gridElement, "hsize-policy"));
+      constraints.setAnchor(LwXmlReader.getRequiredInt(gridElement, "anchor"));
+      constraints.setFill(LwXmlReader.getRequiredInt(gridElement, "fill"));
+      constraints.setIndent(LwXmlReader.getOptionalInt(gridElement, UIFormXmlConstants.ATTRIBUTE_INDENT, 0));
+      constraints.setUseParentLayout(LwXmlReader.getOptionalBoolean(gridElement, UIFormXmlConstants.ATTRIBUTE_USE_PARENT_LAYOUT, false));
+
+      // minimum size
+      final Element minSizeElement = LwXmlReader.getChild(gridElement, "minimum-size");
+      if (minSizeElement != null) {
+        constraints.myMinimumSize.width = LwXmlReader.getRequiredInt(minSizeElement, "width");
+        constraints.myMinimumSize.height = LwXmlReader.getRequiredInt(minSizeElement, "height");
+      }
+
+      // preferred size
+      final Element prefSizeElement = LwXmlReader.getChild(gridElement, "preferred-size");
+      if (prefSizeElement != null) {
+        constraints.myPreferredSize.width = LwXmlReader.getRequiredInt(prefSizeElement, "width");
+        constraints.myPreferredSize.height = LwXmlReader.getRequiredInt(prefSizeElement, "height");
+      }
+
+      // maximum size
+      final Element maxSizeElement = LwXmlReader.getChild(gridElement, "maximum-size");
+      if (maxSizeElement != null) {
+        constraints.myMaximumSize.width = LwXmlReader.getRequiredInt(maxSizeElement, "width");
+        constraints.myMaximumSize.height = LwXmlReader.getRequiredInt(maxSizeElement, "height");
+      }
+
+      component.getConstraints().restore(constraints);
+    }
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/IButtonGroup.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/IButtonGroup.java
new file mode 100644
index 0000000..ed7e156
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/IButtonGroup.java
@@ -0,0 +1,26 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+/**
+ * @author yole
+ */
+public interface IButtonGroup {
+  String getName();
+  boolean isBound();
+  String[] getComponentIds();
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/IComponent.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/IComponent.java
new file mode 100644
index 0000000..d5e87e0
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/IComponent.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.
+ */
+package com.intellij.uiDesigner.lw;
+
+import com.intellij.uiDesigner.core.GridConstraints;
+
+/**
+ * @author Anton Katilin
+ * @author Vladimir Kondratyev
+ */
+public interface IComponent {
+  Object getClientProperty(Object key);
+
+  void putClientProperty(Object key, Object value);
+
+  /**
+   * @return name of the field (in bound class). Returns <code>null</code>
+   * if the component is not bound to any field.
+   */
+  String getBinding();
+
+  String getComponentClassName();
+
+  String getId();
+
+  boolean isCustomCreate();
+
+  IProperty[] getModifiedProperties();
+
+  IContainer getParentContainer();
+
+  GridConstraints getConstraints();
+
+  Object getCustomLayoutConstraints();
+
+  boolean accept(ComponentVisitor visitor);
+
+  /**
+   * Returns true if only one of the children of the component can be visible at a time
+   * (for example, the component is a tabbed pane or a container with CardLayout).
+   *
+   * @return true if children are exclusive, false otherwise.
+   */
+  boolean areChildrenExclusive();
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/IContainer.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/IContainer.java
new file mode 100644
index 0000000..2ae0814
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/IContainer.java
@@ -0,0 +1,27 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import com.intellij.uiDesigner.shared.BorderType;
+
+public interface IContainer extends IComponent {
+  int getComponentCount();
+  IComponent getComponent(int index);
+  int indexOfComponent(final IComponent lwComponent);
+  boolean isXY();
+  StringDescriptor getBorderTitle();
+  BorderType getBorderType();
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/IProperty.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/IProperty.java
new file mode 100644
index 0000000..b976d1f
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/IProperty.java
@@ -0,0 +1,25 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+/**
+ * @author yole
+ */
+public interface IProperty {
+  String getName();
+
+  Object getPropertyValue(final IComponent component);
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/IRootContainer.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/IRootContainer.java
new file mode 100644
index 0000000..67622d8
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/IRootContainer.java
@@ -0,0 +1,24 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+public interface IRootContainer extends IContainer {
+  String getClassToBind();
+  String getButtonGroupName(IComponent component);
+  String[] getButtonGroupComponentIds(String groupName);
+  boolean isInspectionSuppressed(final String inspectionId, final String componentId);
+  IButtonGroup[] getButtonGroups();
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/ITabbedPane.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/ITabbedPane.java
new file mode 100644
index 0000000..a070e69
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/ITabbedPane.java
@@ -0,0 +1,26 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+/**
+ * @author yole
+ */
+public interface ITabbedPane extends IContainer {
+  String TAB_TITLE_PROPERTY = "Tab Title";
+  String TAB_TOOLTIP_PROPERTY = "Tab Tooltip";
+
+  StringDescriptor getTabProperty(IComponent component, final String propName);
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/IconDescriptor.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/IconDescriptor.java
new file mode 100644
index 0000000..8510121
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/IconDescriptor.java
@@ -0,0 +1,42 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import javax.swing.*;
+
+/**
+ * @author yole
+ */
+public class IconDescriptor {
+  private final String myIconPath;
+  private Icon myIcon;
+
+  public IconDescriptor(final String iconPath) {
+    myIconPath = iconPath;
+  }
+
+  public String getIconPath() {
+    return myIconPath;
+  }
+
+  public Icon getIcon() {
+    return myIcon;
+  }
+
+  public void setIcon(final Icon icon) {
+    myIcon = icon;
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LayoutSerializer.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LayoutSerializer.java
new file mode 100644
index 0000000..e4f77dd
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LayoutSerializer.java
@@ -0,0 +1,28 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+
+/**
+ * @author yole
+ */
+public abstract class LayoutSerializer {
+  abstract void readLayout(Element element, LwContainer container);
+
+  abstract void readChildConstraints(final Element constraintsElement, final LwComponent component);
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwAtomicComponent.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwAtomicComponent.java
new file mode 100644
index 0000000..23d32b5
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwAtomicComponent.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.
+ */
+package com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+
+/**
+ * @author Anton Katilin
+ * @author Vladimir Kondratyev
+ */
+public class LwAtomicComponent extends LwComponent {
+  public LwAtomicComponent(final String className){
+    super(className);
+  }
+
+  public void read(final Element element, final PropertiesProvider provider) throws Exception{
+    readBase(element);
+    readConstraints(element);
+    readProperties(element, provider);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwButtonGroup.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwButtonGroup.java
new file mode 100644
index 0000000..b3e999d
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwButtonGroup.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.
+ */
+package com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+import com.intellij.uiDesigner.UIFormXmlConstants;
+
+import java.util.Iterator;
+import java.util.ArrayList;
+
+/**
+ * @author yole
+ */
+public class LwButtonGroup implements IButtonGroup {
+  private String myName;
+  private final ArrayList myComponentIds = new ArrayList();
+  private boolean myBound;
+
+  public void read(final Element element) {
+    myName = element.getAttributeValue(UIFormXmlConstants.ATTRIBUTE_NAME);
+    myBound = LwXmlReader.getOptionalBoolean(element, UIFormXmlConstants.ATTRIBUTE_BOUND, false);
+    for(Iterator i=element.getChildren().iterator(); i.hasNext();){
+      final Element child = (Element)i.next();
+      myComponentIds.add(child.getAttributeValue(UIFormXmlConstants.ATTRIBUTE_ID));
+    }
+  }
+
+  public String getName() {
+    return myName;
+  }
+
+  public String[] getComponentIds() {
+    return (String[])myComponentIds.toArray(new String[myComponentIds.size()]);
+  }
+
+  public boolean isBound() {
+    return myBound;
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwComponent.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwComponent.java
new file mode 100644
index 0000000..cddfc5a
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwComponent.java
@@ -0,0 +1,327 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import com.intellij.uiDesigner.core.GridConstraints;
+import com.intellij.uiDesigner.UIFormXmlConstants;
+import org.jdom.Element;
+
+import java.awt.*;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;/**
+ * @author Anton Katilin
+ * @author Vladimir Kondratyev
+ */
+public abstract class LwComponent implements IComponent{
+  /**
+   *  Component's ID. Cannot be null.
+   */
+  private String myId;
+  /**
+   * may be null
+   */
+  private String myBinding;
+  /**
+   * Component class
+   */
+  private final String myClassName;
+  /**
+   * Parent LwContainer. This field is always not <code>null</code>
+   * is the component is in hierarchy. But the root of hierarchy
+   * has <code>null</code> parent indeed.
+   */
+  private LwContainer myParent;
+  /**
+   * never <code>null</code>
+   */
+  private final GridConstraints myConstraints;
+
+  private Object myCustomLayoutConstraints;
+  /**
+   * Bounds in XY layout
+   */
+  private final Rectangle myBounds;
+
+  private final HashMap myIntrospectedProperty2Value;
+  /**
+   * if class is unknown (cannot be loaded), properties tag is stored as is
+   */
+  private Element myErrorComponentProperties;
+  protected final HashMap myClientProperties;
+  protected final HashMap myDelegeeClientProperties;
+  private boolean myCustomCreate = false;
+  private boolean myDefaultBinding = false;
+
+  public LwComponent(final String className){
+    if (className == null){
+      throw new IllegalArgumentException("className cannot be null");
+    }
+    myBounds = new Rectangle();
+    myConstraints = new GridConstraints();
+    myIntrospectedProperty2Value = new HashMap();
+    myClassName = className;
+    myClientProperties = new HashMap();
+    myDelegeeClientProperties = new HashMap();
+  }
+
+  public final String getId() {
+    return myId;
+  }
+
+  public final void setId(final String id){
+    if (id == null) {
+      throw new IllegalArgumentException("id cannot be null");
+    }
+    myId = id;
+  }
+
+  public final String getBinding(){
+    return myBinding;
+  }
+
+  public final void setBinding(final String binding){
+    myBinding = binding;
+  }
+
+  public final Object getCustomLayoutConstraints(){
+    return myCustomLayoutConstraints;
+  }
+
+  public final void setCustomLayoutConstraints(final Object customLayoutConstraints){
+    myCustomLayoutConstraints = customLayoutConstraints;
+  }
+
+  /**
+   * @return never null
+   */
+  public final String getComponentClassName(){
+    return myClassName;
+  }
+
+  public IProperty[] getModifiedProperties() {
+    return getAssignedIntrospectedProperties();
+  }
+
+  /**
+   * @return component's constraints in XY layout. This method rever
+   * returns <code>null</code>.
+   */
+  public final Rectangle getBounds(){
+    return (Rectangle)myBounds.clone();
+  }
+
+  /**
+   * @return component's constraints in GridLayoutManager. This method never
+   * returns <code>null</code>.
+   */
+  public final GridConstraints getConstraints(){
+    return myConstraints;
+  }
+
+  public boolean isCustomCreate() {
+    return myCustomCreate;
+  }
+
+  public boolean isDefaultBinding() {
+    return myDefaultBinding;
+  }
+
+  public boolean accept(ComponentVisitor visitor) {
+    return visitor.visit(this);
+  }
+
+  public boolean areChildrenExclusive() {
+    return false;
+  }
+
+  public final LwContainer getParent(){
+    return myParent;
+  }
+
+  public IContainer getParentContainer() {
+    return myParent;
+  }
+
+  protected final void setParent(final LwContainer parent){
+    myParent = parent;
+  }
+
+  public final void setBounds(final Rectangle bounds) {
+    myBounds.setBounds(bounds);
+  }
+
+  public final Object getPropertyValue(final LwIntrospectedProperty property){
+    return myIntrospectedProperty2Value.get(property);
+  }
+
+  public final void setPropertyValue(final LwIntrospectedProperty property, final Object value){
+    myIntrospectedProperty2Value.put(property, value);
+  }
+
+  /**
+   * @return <code>null</code> only if component class is not valid.
+   * Class validation is performed with {@link com.intellij.uiDesigner.compiler.Utils#validateJComponentClass(ClassLoader,String,boolean)}
+   */
+  public final Element getErrorComponentProperties(){
+    return myErrorComponentProperties;
+  }
+
+  public final LwIntrospectedProperty[] getAssignedIntrospectedProperties() {
+    final LwIntrospectedProperty[] properties = new LwIntrospectedProperty[myIntrospectedProperty2Value.size()];
+    final Iterator iterator = myIntrospectedProperty2Value.keySet().iterator();
+    //noinspection ForLoopThatDoesntUseLoopVariable
+    for (int i=0; iterator.hasNext(); i++) {
+      properties[i] = (LwIntrospectedProperty)iterator.next();
+    }
+    return properties;
+  }
+
+  /**
+   * 'id' is required attribute
+   * 'binding' is optional attribute
+   */
+  protected final void readBase(final Element element) {
+    setId(LwXmlReader.getRequiredString(element, UIFormXmlConstants.ATTRIBUTE_ID));
+    setBinding(element.getAttributeValue(UIFormXmlConstants.ATTRIBUTE_BINDING));
+    myCustomCreate = LwXmlReader.getOptionalBoolean(element, UIFormXmlConstants.ATTRIBUTE_CUSTOM_CREATE, false);
+    myDefaultBinding = LwXmlReader.getOptionalBoolean(element, UIFormXmlConstants.ATTRIBUTE_DEFAULT_BINDING, false);
+  }
+
+  /**
+   * 'properties' is not required subtag
+   * @param provider can be null if no properties should be read 
+   */
+  protected final void readProperties(final Element element, final PropertiesProvider provider) {
+    if (provider == null) {
+      // do not read properties 
+      return;
+    }
+
+    Element propertiesElement = LwXmlReader.getChild(element, UIFormXmlConstants.ELEMENT_PROPERTIES);
+    if(propertiesElement == null){
+      propertiesElement = new Element(UIFormXmlConstants.ELEMENT_PROPERTIES, element.getNamespace());
+    }
+
+    final HashMap name2property = provider.getLwProperties(getComponentClassName());
+    if (name2property == null) {
+      myErrorComponentProperties = (Element)propertiesElement.clone();
+      return;
+    }
+
+    final List propertyElements = propertiesElement.getChildren();
+    for (int i = 0; i < propertyElements.size(); i++) {
+      final Element t = (Element)propertyElements.get(i);
+      final String name = t.getName();
+      final LwIntrospectedProperty property = (LwIntrospectedProperty)name2property.get(name);
+      if (property == null){
+        continue;
+      }
+      try {
+        final Object value = property.read(t);
+        setPropertyValue(property, value);
+      }
+      catch (final Exception exc) {
+        // Skip non readable properties
+      }
+    }
+
+    readClientProperties(element);
+  }
+
+  private void readClientProperties(final Element element) {
+    Element propertiesElement = LwXmlReader.getChild(element, UIFormXmlConstants.ELEMENT_CLIENT_PROPERTIES);
+    if (propertiesElement == null) return;
+    final List clientPropertyList = propertiesElement.getChildren();
+    for(int i=0; i<clientPropertyList.size(); i++) {
+      final Element prop = (Element) clientPropertyList.get(i);
+      final String propName = prop.getName();
+      final String className = LwXmlReader.getRequiredString(prop, UIFormXmlConstants.ATTRIBUTE_CLASS);
+
+      LwIntrospectedProperty lwProp;
+      if (className.equals(Integer.class.getName())) {
+        lwProp = new LwIntroIntProperty(propName);
+      }
+      else if (className.equals(Boolean.class.getName())) {
+        lwProp = new LwIntroBooleanProperty(propName);
+      }
+      else if (className.equals(Double.class.getName())) {
+        lwProp = new LwIntroPrimitiveTypeProperty(propName, Double.class);
+      }
+      else {
+        Class propClass;
+        try {
+          propClass = Class.forName(className);
+        }
+        catch (ClassNotFoundException e) {
+          continue;
+        }
+        lwProp = CompiledClassPropertiesProvider.propertyFromClass(propClass, propName);
+      }
+
+      if (lwProp != null) {
+        final Object value;
+        try {
+          value = lwProp.read(prop);
+        }
+        catch (Exception e) {
+          continue;
+        }
+        myDelegeeClientProperties.put(propName, value);
+      }
+    }
+  }
+
+  /**
+   * Delegates reading of constraints to the parent container
+   */
+  protected final void readConstraints(final Element element){
+    final LwContainer parent = getParent();
+    if(parent == null){
+      throw new IllegalStateException("component must be in LW tree: "+this);
+    }
+    parent.readConstraintsForChild(element, this);
+  }
+
+  /**
+   * @param provider can be null if no component classes should not be created
+   */
+  public abstract void read(Element element, PropertiesProvider provider) throws Exception;
+
+  /**
+   * @see javax.swing.JComponent#getClientProperty(Object)
+   */
+  public final Object getClientProperty(final Object key){
+    if (key == null) {
+      throw new IllegalArgumentException("key cannot be null");
+    }
+    return myClientProperties.get(key);
+  }
+
+  /**
+   * @see javax.swing.JComponent#putClientProperty(Object, Object)
+   */
+  public final void putClientProperty(final Object key, final Object value){
+    if (key == null) {
+      throw new IllegalArgumentException("key cannot be null");
+    }
+    myClientProperties.put(key, value);
+  }
+
+  public HashMap getDelegeeClientProperties() {
+    return myDelegeeClientProperties;
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwContainer.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwContainer.java
new file mode 100644
index 0000000..4e70b4f
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwContainer.java
@@ -0,0 +1,385 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import com.intellij.uiDesigner.UIFormXmlConstants;
+import com.intellij.uiDesigner.compiler.UnexpectedFormElementException;
+import com.intellij.uiDesigner.core.GridLayoutManager;
+import com.intellij.uiDesigner.shared.BorderType;
+import com.intellij.uiDesigner.shared.XYLayoutManager;
+import org.jdom.Element;
+
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * @author Anton Katilin
+ * @author Vladimir Kondratyev
+ */
+public class LwContainer extends LwComponent implements IContainer{
+  // PLEASE DO NOT USE GENERICS IN THIS FILE AS IT IS USED IN JAVAC2 ANT TASK THAT SHOULD BE RUNNABLE WITH JDK 1.3
+
+  /**
+   * Children components
+   */
+  private final ArrayList myComponents;
+  /**
+   * Describes border's type. This member is never <code>null</code>
+   */
+  private BorderType myBorderType;
+  /**
+   * Border's title. If border doesn't have any title then
+   * this member is <code>null</code>.
+   */
+  private StringDescriptor myBorderTitle;
+  private int myBorderTitleJustification;
+  private int myBorderTitlePosition;
+  private FontDescriptor myBorderTitleFont;
+  private ColorDescriptor myBorderTitleColor;
+  private Insets myBorderSize;
+  private ColorDescriptor myBorderColor;
+  private LayoutManager myLayout;
+  private String myLayoutManager;
+  protected LayoutSerializer myLayoutSerializer;
+
+  public LwContainer(final String className){
+    super(className);
+    myComponents = new ArrayList();
+
+    // By default container doesn't have any special border
+    setBorderType(BorderType.NONE);
+
+    myLayout = createInitialLayout();
+  }
+
+
+  protected LayoutManager createInitialLayout(){
+    return new XYLayoutManager();
+  }
+
+  public final LayoutManager getLayout() {
+    return myLayout;
+  }
+
+  public final void setLayout(final LayoutManager layout) {
+    myLayout = layout;
+  }
+
+  public String getLayoutManager() {
+    return myLayoutManager;
+  }
+
+  public final boolean isGrid(){
+    return getLayout() instanceof GridLayoutManager;
+  }
+
+  public final boolean isXY(){
+    return getLayout() instanceof XYLayoutManager;
+  }
+
+  /**
+   * @param component component to be added.
+   *
+   * @exception IllegalArgumentException if <code>component</code> is <code>null</code>
+   * @exception IllegalArgumentException if <code>component</code> already exist in the
+   * container
+   */
+  public final void addComponent(final LwComponent component){
+    if (component == null) {
+      throw new IllegalArgumentException("component cannot be null");
+    }
+    if (myComponents.contains(component)) {
+      throw new IllegalArgumentException("component is already added: " + component);
+    }
+    if (component.getParent() != null) {
+      throw new IllegalArgumentException("component already added to another container");
+    }
+
+    // Attach to new parent
+    myComponents.add(component);
+    component.setParent(this);
+  }
+
+  public final IComponent getComponent(final int index) {
+    return (IComponent)myComponents.get(index);
+  }
+
+  public final int getComponentCount() {
+    return myComponents.size();
+  }
+
+  public int indexOfComponent(final IComponent lwComponent) {
+    return myComponents.indexOf(lwComponent);
+  }
+
+  /**
+   * @return border's type. The method never return <code>null</code>.
+   *
+   * @see BorderType
+   */
+  public final BorderType getBorderType(){
+    return myBorderType;
+  }
+
+  public boolean accept(ComponentVisitor visitor) {
+    if (!super.accept(visitor)) {
+      return false;
+    }
+
+    for (int i = 0; i < getComponentCount(); i++) {
+      final IComponent c = getComponent(i);
+      if (!c.accept(visitor)) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  /**
+   * @see BorderType
+   *
+   * @exception IllegalArgumentException if <code>type</code>
+   * is <code>null</code>
+   */
+  public final void setBorderType(final BorderType type){
+    if(type==null){
+      throw new IllegalArgumentException("type cannot be null");
+    }
+    myBorderType=type;
+  }
+
+  /**
+   * @return border's title. If the container doesn't have any title then the
+   * method returns <code>null</code>.
+   */
+  public final StringDescriptor getBorderTitle(){
+    return myBorderTitle;
+  }
+
+  /**
+   * @param title new border's title. <code>null</code> means that
+   * the containr doesn't have have titled border.
+   */
+  public final void setBorderTitle(final StringDescriptor title){
+    myBorderTitle=title;
+  }
+
+  public int getBorderTitleJustification() {
+    return myBorderTitleJustification;
+  }
+
+  public int getBorderTitlePosition() {
+    return myBorderTitlePosition;
+  }
+
+  public FontDescriptor getBorderTitleFont() {
+    return myBorderTitleFont;
+  }
+
+  public ColorDescriptor getBorderTitleColor() {
+    return myBorderTitleColor;
+  }
+
+  public Insets getBorderSize() {
+    return myBorderSize;
+  }
+
+  public ColorDescriptor getBorderColor() {
+    return myBorderColor;
+  }
+
+  /**
+   * TODO[anton,vova] looks like it is better to pass contraints tag
+   * 
+   * @param element XML element which should contains 'constraints' tag
+   */
+  protected void readConstraintsForChild(final Element element, final LwComponent component){
+    if (myLayoutSerializer != null) {
+      final Element constraintsElement = LwXmlReader.getRequiredChild(element, "constraints");
+      myLayoutSerializer.readChildConstraints(constraintsElement, component);
+    }
+  }
+
+  /**
+   * 'border' is required subtag
+   */
+  protected final void readBorder(final Element element) {
+    final Element borderElement = LwXmlReader.getRequiredChild(element, UIFormXmlConstants.ELEMENT_BORDER);
+    setBorderType(BorderType.valueOf(LwXmlReader.getRequiredString(borderElement, UIFormXmlConstants.ATTRIBUTE_TYPE)));
+
+    StringDescriptor descriptor = LwXmlReader.getStringDescriptor(borderElement,
+                                                                  UIFormXmlConstants.ATTRIBUTE_TITLE,
+                                                                  UIFormXmlConstants.ATTRIBUTE_TITLE_RESOURCE_BUNDLE,
+                                                                  UIFormXmlConstants.ATTRIBUTE_TITLE_KEY);
+    if (descriptor != null) {
+      setBorderTitle(descriptor);
+    }
+
+    myBorderTitleJustification = LwXmlReader.getOptionalInt(borderElement, UIFormXmlConstants.ATTRIBUTE_TITLE_JUSTIFICATION, 0);
+    myBorderTitlePosition = LwXmlReader.getOptionalInt(borderElement, UIFormXmlConstants.ATTRIBUTE_TITLE_POSITION, 0);
+    Element fontElement = LwXmlReader.getChild(borderElement, UIFormXmlConstants.ELEMENT_FONT);
+    if (fontElement != null) {
+      myBorderTitleFont = LwXmlReader.getFontDescriptor(fontElement);
+    }
+    myBorderTitleColor = LwXmlReader.getOptionalColorDescriptor(LwXmlReader.getChild(borderElement, UIFormXmlConstants.ELEMENT_TITLE_COLOR));
+    myBorderColor = LwXmlReader.getOptionalColorDescriptor(LwXmlReader.getChild(borderElement, UIFormXmlConstants.ELEMENT_COLOR));
+    Element sizeElement = LwXmlReader.getChild(borderElement, UIFormXmlConstants.ELEMENT_SIZE);
+    if (sizeElement != null) {
+      try {
+        myBorderSize = LwXmlReader.readInsets(sizeElement);
+      }
+      catch(Exception e) {
+        myBorderSize = null;
+      }
+    }
+  }
+
+  /**
+   * 'children' is required attribute
+   */
+  protected final void readChildren(final Element element, final PropertiesProvider provider) throws Exception{
+    final Element childrenElement = LwXmlReader.getRequiredChild(element, "children");
+    for(Iterator i=childrenElement.getChildren().iterator(); i.hasNext();){
+      final Element child = (Element)i.next();
+      final LwComponent component = createComponentFromTag(child);
+      addComponent(component);
+      component.read(child, provider);
+    }
+  }
+
+  public static LwComponent createComponentFromTag(final Element child) throws Exception {
+    final String name = child.getName();
+    final LwComponent component;
+    if("component".equals(name)){
+      final String className = LwXmlReader.getRequiredString(child, UIFormXmlConstants.ATTRIBUTE_CLASS);
+      component = new LwAtomicComponent(className);
+    }
+    else if (UIFormXmlConstants.ELEMENT_NESTED_FORM.equals(name)) {
+      component = new LwNestedForm();
+    }
+    else if("vspacer".equals(name)){
+      component = new LwVSpacer();
+    }
+    else if("hspacer".equals(name)){
+      component = new LwHSpacer();
+    }
+    else if("xy".equals(name) || "grid".equals(name)){
+      String className = LwXmlReader.getOptionalString(child, UIFormXmlConstants.ATTRIBUTE_CLASS, "javax.swing.JPanel");
+      component = new LwContainer(className);
+    }
+    else if(UIFormXmlConstants.ELEMENT_SCROLLPANE.equals(name)) {
+      String className = LwXmlReader.getOptionalString(child, UIFormXmlConstants.ATTRIBUTE_CLASS, "javax.swing.JScrollPane");
+      component = new LwScrollPane(className);
+    }
+    else if(UIFormXmlConstants.ELEMENT_TABBEDPANE.equals(name)){
+      String className = LwXmlReader.getOptionalString(child, UIFormXmlConstants.ATTRIBUTE_CLASS, "javax.swing.JTabbedPane");
+      component = new LwTabbedPane(className);
+    }
+    else if(UIFormXmlConstants.ELEMENT_SPLITPANE.equals(name)){
+      String className = LwXmlReader.getOptionalString(child, UIFormXmlConstants.ATTRIBUTE_CLASS, "javax.swing.JSplitPane");
+      component = new LwSplitPane(className);
+    }
+    else if (UIFormXmlConstants.ELEMENT_TOOLBAR.equals(name)) {
+      String className = LwXmlReader.getOptionalString(child, UIFormXmlConstants.ATTRIBUTE_CLASS, "javax.swing.JToolBar");
+      component = new LwToolBar(className);
+    }
+    else{
+      throw new UnexpectedFormElementException("unexpected element: "+child);
+    }
+    return component;
+  }
+
+  /**
+   * 'xy' or 'grid'
+   */
+  protected final void readLayout(final Element element){
+    myLayoutManager = element.getAttributeValue("layout-manager");
+    if("xy".equals(element.getName())){
+      myLayoutSerializer = XYLayoutSerializer.INSTANCE;
+    }
+    else if("grid".equals(element.getName())){
+      createLayoutSerializer();
+    }
+    else{
+      throw new UnexpectedFormElementException("unexpected element: "+element);
+    }
+    myLayoutSerializer.readLayout(element, this);
+  }
+
+  public void setLayoutManager(final String layoutManager) {
+    myLayoutManager = layoutManager;
+    createLayoutSerializer();
+  }
+
+  private void createLayoutSerializer() {
+    if (UIFormXmlConstants.LAYOUT_BORDER.equals(myLayoutManager)) {
+      myLayoutSerializer = BorderLayoutSerializer.INSTANCE;
+    }
+    else if (UIFormXmlConstants.LAYOUT_FLOW.equals(myLayoutManager)) {
+      myLayoutSerializer = FlowLayoutSerializer.INSTANCE;
+    }
+    else if (UIFormXmlConstants.LAYOUT_CARD.equals(myLayoutManager)) {
+      myLayoutSerializer = CardLayoutSerializer.INSTANCE;
+    }
+    else if (UIFormXmlConstants.LAYOUT_XY.equals(myLayoutManager)) {
+      myLayoutSerializer = XYLayoutSerializer.INSTANCE;
+    }
+    else if (UIFormXmlConstants.LAYOUT_FORM.equals(myLayoutManager)) {
+      myLayoutSerializer = FormLayoutSerializer.INSTANCE;
+    }
+    else if (UIFormXmlConstants.LAYOUT_GRIDBAG.equals(myLayoutManager)) {
+      myLayoutSerializer = GridBagLayoutSerializer.INSTANCE;      
+    }
+    else {
+      myLayoutSerializer = GridLayoutSerializer.INSTANCE;
+    }
+  }
+
+  public void read(final Element element, final PropertiesProvider provider) throws Exception {
+    readBase(element);
+
+    // Layout
+    readLayout(element);
+
+    // Constraints and properties
+    readConstraints(element);
+    readProperties(element, provider);
+
+    // Border
+    readBorder(element);
+
+    readChildren(element, provider);
+  }
+
+  protected void readNoLayout(final Element element, final PropertiesProvider provider) throws Exception {
+    readBase(element);
+
+    // Constraints and properties
+    readConstraints(element);
+    readProperties(element, provider);
+
+    // Border
+    readBorder(element);
+
+    readChildren(element, provider);
+  }
+
+  public boolean areChildrenExclusive() {
+    return UIFormXmlConstants.LAYOUT_CARD.equals(myLayoutManager);
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwHSpacer.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwHSpacer.java
new file mode 100644
index 0000000..4be52ac
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwHSpacer.java
@@ -0,0 +1,33 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+
+/**
+ * @author Anton Katilin
+ * @author Vladimir Kondratyev
+ */
+public final class LwHSpacer extends LwAtomicComponent {
+  public LwHSpacer() throws Exception{
+    super("com.intellij.uiDesigner.core.Spacer");
+  }
+  
+  public void read(final Element element, final PropertiesProvider provider) throws Exception{
+    readBase(element);
+    readConstraints(element);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwInspectionSuppression.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwInspectionSuppression.java
new file mode 100644
index 0000000..e0722c8
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwInspectionSuppression.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 com.intellij.uiDesigner.lw;
+
+/**
+ * @author yole
+ */
+public class LwInspectionSuppression {
+  public static final LwInspectionSuppression[] EMPTY_ARRAY = new LwInspectionSuppression[0];
+
+  private final String myInspectionId;
+  private final String myComponentId;
+
+  public LwInspectionSuppression(final String inspectionId, final String componentId) {
+    myInspectionId = inspectionId;
+    myComponentId = componentId;
+  }
+
+  public String getInspectionId() {
+    return myInspectionId;
+  }
+
+  public String getComponentId() {
+    return myComponentId;
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroBooleanProperty.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroBooleanProperty.java
new file mode 100644
index 0000000..394beb7
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroBooleanProperty.java
@@ -0,0 +1,30 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+import com.intellij.uiDesigner.UIFormXmlConstants;
+
+public final class LwIntroBooleanProperty extends LwIntrospectedProperty {
+  public LwIntroBooleanProperty(final String name){
+    super(name, Boolean.class.getName());
+  }
+
+  public Object read(final Element element) throws Exception{
+    return Boolean.valueOf(LwXmlReader.getRequiredString(element, UIFormXmlConstants.ATTRIBUTE_VALUE));
+  }
+}
+
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroCharProperty.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroCharProperty.java
new file mode 100644
index 0000000..cb5287c
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroCharProperty.java
@@ -0,0 +1,14 @@
+package com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+import com.intellij.uiDesigner.UIFormXmlConstants;
+
+public final class LwIntroCharProperty extends LwIntrospectedProperty {
+  public LwIntroCharProperty(final String name){
+    super(name, Character.class.getName());
+  }
+
+  public Object read(final Element element) throws Exception{
+    return Character.valueOf(LwXmlReader.getRequiredString(element, UIFormXmlConstants.ATTRIBUTE_VALUE).charAt(0));
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroColorProperty.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroColorProperty.java
new file mode 100644
index 0000000..d310928
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroColorProperty.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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+
+/**
+ * @author yole
+ */
+public class LwIntroColorProperty extends LwIntrospectedProperty {
+  public LwIntroColorProperty(final String name) {
+    super(name, "java.awt.Color");
+  }
+
+  public Object read(Element element) throws Exception {
+    return LwXmlReader.getColorDescriptor(element);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroComponentProperty.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroComponentProperty.java
new file mode 100644
index 0000000..660e5ec
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroComponentProperty.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 com.intellij.uiDesigner.lw;
+
+import com.intellij.uiDesigner.UIFormXmlConstants;
+import org.jdom.Element;
+
+/**
+ * @author yole
+ */
+public class LwIntroComponentProperty extends LwIntrospectedProperty {
+  public LwIntroComponentProperty(final String name, final String propertyClassName) {
+    super(name, propertyClassName);
+  }
+
+  public Object read(Element element) throws Exception {
+    return LwXmlReader.getRequiredString(element, UIFormXmlConstants.ATTRIBUTE_VALUE);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroDimensionProperty.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroDimensionProperty.java
new file mode 100644
index 0000000..e150c84
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroDimensionProperty.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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+
+import java.awt.*;
+
+/**
+ * @author Anton Katilin
+ */
+public final class LwIntroDimensionProperty extends LwIntrospectedProperty {
+  public LwIntroDimensionProperty(final String name){
+    super(name, "java.awt.Dimension");
+  }
+
+  public Object read(final Element element) throws Exception{
+    final int width = LwXmlReader.getRequiredInt(element, "width");
+    final int height = LwXmlReader.getRequiredInt(element, "height");
+    return new Dimension(width, height);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroEnumProperty.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroEnumProperty.java
new file mode 100644
index 0000000..ff2ac74
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroEnumProperty.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 com.intellij.uiDesigner.lw;
+
+import com.intellij.uiDesigner.UIFormXmlConstants;
+import org.jdom.Element;
+
+import java.lang.reflect.Method;
+
+/**
+ * @author yole
+ */
+public class LwIntroEnumProperty extends LwIntrospectedProperty {
+  private final Class myEnumClass;
+
+  public LwIntroEnumProperty(final String name, final Class enumClass) {
+    super(name, enumClass.getName());
+    myEnumClass = enumClass;
+  }
+
+  public Object read(Element element) throws Exception {
+    String value = element.getAttributeValue(UIFormXmlConstants.ATTRIBUTE_VALUE);
+    final Method method = myEnumClass.getMethod("valueOf", new Class[] { String.class} );
+    return method.invoke(null, new Object[] { value } );
+  }
+
+  public String getCodeGenPropertyClassName() {
+    return "java.lang.Enum";
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroFontProperty.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroFontProperty.java
new file mode 100644
index 0000000..01e1151
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroFontProperty.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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+
+/**
+ * @author yole
+ */
+public class LwIntroFontProperty extends LwIntrospectedProperty {
+  public LwIntroFontProperty(final String name) {
+    super(name, "java.awt.Font");
+  }
+
+  public Object read(Element element) throws Exception {
+    return LwXmlReader.getFontDescriptor(element);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroIconProperty.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroIconProperty.java
new file mode 100644
index 0000000..3fcd11c
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroIconProperty.java
@@ -0,0 +1,33 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import com.intellij.uiDesigner.UIFormXmlConstants;
+import org.jdom.Element;
+
+/**
+ * @author yole
+ */
+public class LwIntroIconProperty extends LwIntrospectedProperty {
+  public LwIntroIconProperty(final String name) {
+    super(name, "javax.swing.Icon");
+  }
+
+  public Object read(Element element) throws Exception {
+    String value = LwXmlReader.getRequiredString(element, UIFormXmlConstants.ATTRIBUTE_VALUE);
+    return new IconDescriptor(value);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroInsetsProperty.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroInsetsProperty.java
new file mode 100644
index 0000000..826577c
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroInsetsProperty.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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+
+/**
+ * @author Vladimir Kondratyev
+ */
+public final class LwIntroInsetsProperty extends LwIntrospectedProperty{
+  public LwIntroInsetsProperty(final String name){
+    super(name, "java.awt.Insets");
+  }
+
+  public Object read(final Element element) throws Exception{
+    return LwXmlReader.readInsets(element);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroIntProperty.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroIntProperty.java
new file mode 100644
index 0000000..d6b9857
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroIntProperty.java
@@ -0,0 +1,28 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+
+public final class LwIntroIntProperty extends LwIntrospectedProperty {
+  public LwIntroIntProperty(final String name){
+    super(name, Integer.class.getName());
+  }
+
+  public Object read(final Element element) throws Exception{
+    return new Integer(LwXmlReader.getRequiredInt(element, "value"));
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroListModelProperty.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroListModelProperty.java
new file mode 100644
index 0000000..1c5fddd
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroListModelProperty.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 com.intellij.uiDesigner.lw;
+
+import com.intellij.uiDesigner.UIFormXmlConstants;
+import org.jdom.Element;
+
+import java.util.List;
+
+/**
+ * @author yole
+ */
+public class LwIntroListModelProperty extends LwIntrospectedProperty {
+  public LwIntroListModelProperty(final String name, final String propertyClassName) {
+    super(name, propertyClassName);
+  }
+
+  public Object read(Element element) throws Exception {
+    final List list = element.getChildren(UIFormXmlConstants.ELEMENT_ITEM, element.getNamespace());
+    String[] result = new String[list.size()];
+    for(int i=0; i<list.size(); i++) {
+      Element itemElement = (Element) list.get(i);
+      result [i] = LwXmlReader.getRequiredString(itemElement, UIFormXmlConstants.ATTRIBUTE_VALUE);
+    }
+    return result;
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroPrimitiveTypeProperty.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroPrimitiveTypeProperty.java
new file mode 100644
index 0000000..da7c160
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroPrimitiveTypeProperty.java
@@ -0,0 +1,17 @@
+package com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+import com.intellij.uiDesigner.UIFormXmlConstants;
+
+public final class LwIntroPrimitiveTypeProperty extends LwIntrospectedProperty {
+  private final Class myValueClass;
+
+  public LwIntroPrimitiveTypeProperty(final String name, final Class valueClass){
+    super(name, valueClass.getName());
+    myValueClass = valueClass;
+  }
+
+  public Object read(final Element element) throws Exception{
+    return LwXmlReader.getRequiredPrimitiveTypeValue(element, UIFormXmlConstants.ATTRIBUTE_VALUE, myValueClass);
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroRectangleProperty.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroRectangleProperty.java
new file mode 100644
index 0000000..d43a499
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntroRectangleProperty.java
@@ -0,0 +1,37 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+
+import java.awt.*;
+
+/**
+ * @author Vladimir Kondratyev
+ */
+public final class LwIntroRectangleProperty extends LwIntrospectedProperty{
+  public LwIntroRectangleProperty(final String name){
+    super(name, "java.awt.Rectangle");
+  }
+
+  public Object read(final Element element) throws Exception{
+    final int x = LwXmlReader.getRequiredInt(element, "x");
+    final int y = LwXmlReader.getRequiredInt(element, "y");
+    final int width = LwXmlReader.getRequiredInt(element, "width");
+    final int height = LwXmlReader.getRequiredInt(element, "height");
+    return new Rectangle(x, y, width, height);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntrospectedProperty.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntrospectedProperty.java
new file mode 100644
index 0000000..c39f97b
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwIntrospectedProperty.java
@@ -0,0 +1,89 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+
+public abstract class LwIntrospectedProperty implements IProperty {
+
+  private final String myName;
+  private final String myPropertyClassName;
+  private String myDeclaringClassName;
+
+  public LwIntrospectedProperty(
+    final String name,
+    final String propertyClassName
+  ){
+    if (name == null){
+      throw new IllegalArgumentException("name cannot be null");
+    }
+    if (propertyClassName == null){
+      throw new IllegalArgumentException("propertyClassName cannot be null");
+    }
+
+    myName = name;
+    myPropertyClassName = propertyClassName;
+  }
+
+  /**
+   * @return never null
+   */ 
+  public final String getName(){
+    return myName;
+  }
+
+  /**
+   * @return never null
+   */ 
+  public final String getPropertyClassName(){
+    return myPropertyClassName;
+  }
+  
+  public final String getReadMethodName() {
+    return "get" + Character.toUpperCase(myName.charAt(0)) + myName.substring(1);
+  }
+
+  public final String getWriteMethodName() {
+    return "set" + Character.toUpperCase(myName.charAt(0)) + myName.substring(1);
+  }
+
+  public String getDeclaringClassName() {
+    return myDeclaringClassName;
+  }
+
+  public void setDeclaringClassName(final String definingClassName) {
+    myDeclaringClassName = definingClassName;
+  }
+
+  /**
+   * @param element element that contains serialized property data. This element was
+   * written by {@link com.intellij.uiDesigner.propertyInspector.IntrospectedProperty#write(Object, com.intellij.uiDesigner.XmlWriter)}
+   * method. So <code>read</code> and <code>write</code> methods should be consistent.
+   *
+   * @return property value. Should never return <code>null</code>. For example,
+   * value can be <code>java.lang.Integer</code> for <code>IntroIntProperty</code>.
+   *
+   */
+  public abstract Object read(Element element) throws Exception;
+
+  public Object getPropertyValue(final IComponent component) {
+    return ((LwComponent) component).getPropertyValue(this);
+  }
+
+  public String getCodeGenPropertyClassName() {
+    return getPropertyClassName();
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwNestedForm.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwNestedForm.java
new file mode 100644
index 0000000..11dcb14
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwNestedForm.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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+import com.intellij.uiDesigner.UIFormXmlConstants;
+
+/**
+ * @author yole
+ */
+public class LwNestedForm extends LwComponent {
+  private String myFormFileName;
+
+  public LwNestedForm() {
+    super("");
+  }
+
+  public void read(Element element, PropertiesProvider provider) throws Exception {
+    myFormFileName = LwXmlReader.getRequiredString(element, UIFormXmlConstants.ATTRIBUTE_FORM_FILE);
+    readBase(element);
+    readConstraints(element);
+  }
+
+  public String getFormFileName() {
+    return myFormFileName;
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwRbIntroStringProperty.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwRbIntroStringProperty.java
new file mode 100644
index 0000000..d12187b
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwRbIntroStringProperty.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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+import com.intellij.uiDesigner.UIFormXmlConstants;
+
+/**
+ * @author Vladimir Kondratyev
+ */
+public final class LwRbIntroStringProperty extends LwIntrospectedProperty {
+  public LwRbIntroStringProperty(final String name){
+    super(name, String.class.getName());
+  }
+
+  /**
+   * @return instance of {@link com.intellij.uiDesigner.lw.StringDescriptor}
+   */
+  public Object read(final Element element) throws Exception{
+    final StringDescriptor descriptor = LwXmlReader.getStringDescriptor(element,
+                                                                        UIFormXmlConstants.ATTRIBUTE_VALUE,
+                                                                        UIFormXmlConstants.ATTRIBUTE_RESOURCE_BUNDLE,
+                                                                        UIFormXmlConstants.ATTRIBUTE_KEY);
+    if (descriptor == null) {
+      throw new IllegalArgumentException("String descriptor value required");
+    }
+    return descriptor;
+  }
+}
+
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwRootContainer.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwRootContainer.java
new file mode 100644
index 0000000..3ee850e
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwRootContainer.java
@@ -0,0 +1,148 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import com.intellij.uiDesigner.UIFormXmlConstants;
+import com.intellij.uiDesigner.compiler.AlienFormFileException;
+import com.intellij.uiDesigner.compiler.UnexpectedFormElementException;
+import com.intellij.uiDesigner.compiler.Utils;
+import org.jdom.Element;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+
+/**
+ * @author Anton Katilin
+ * @author Vladimir Kondratyev
+  */
+public final class LwRootContainer extends LwContainer implements IRootContainer{
+  private String myClassToBind;
+  private String myMainComponentBinding;
+  private final ArrayList myButtonGroups = new ArrayList();
+  private final ArrayList myInspectionSuppressions = new ArrayList();
+
+  public LwRootContainer() throws Exception{
+    super("javax.swing.JPanel");
+    myLayoutSerializer = XYLayoutSerializer.INSTANCE;
+  }
+
+  public String getMainComponentBinding(){
+    return myMainComponentBinding;
+  }
+
+  public String getClassToBind(){
+    return myClassToBind;
+  }
+
+  public void setClassToBind(final String classToBind) {
+    myClassToBind = classToBind;
+  }
+
+  public void read(final Element element, final PropertiesProvider provider) throws Exception {
+    if (element == null) {
+      throw new IllegalArgumentException("element cannot be null");
+    }
+    if (!Utils.FORM_NAMESPACE.equals(element.getNamespace().getURI())) {
+      throw new AlienFormFileException();
+    }
+    if(!"form".equals(element.getName())){
+      throw new UnexpectedFormElementException("unexpected element: "+element);
+    }
+
+    setId("root");
+
+    myClassToBind = element.getAttributeValue(UIFormXmlConstants.ATTRIBUTE_BIND_TO_CLASS);
+
+    // Constraints and properties
+    for(Iterator i=element.getChildren().iterator(); i.hasNext();){
+      final Element child = (Element)i.next();
+      if (child.getName().equals(UIFormXmlConstants.ELEMENT_BUTTON_GROUPS)) {
+        readButtonGroups(child);
+      }
+      else if (child.getName().equals(UIFormXmlConstants.ELEMENT_INSPECTION_SUPPRESSIONS)) {
+        readInspectionSuppressions(child);
+      }
+      else {
+        final LwComponent component = createComponentFromTag(child);
+        addComponent(component);
+        component.read(child, provider);
+      }
+    }
+
+    myMainComponentBinding = element.getAttributeValue("stored-main-component-binding");
+  }
+
+  private void readButtonGroups(final Element element) {
+    for(Iterator i=element.getChildren().iterator(); i.hasNext();){
+      final Element child = (Element)i.next();
+      LwButtonGroup group = new LwButtonGroup();
+      group.read(child);
+      myButtonGroups.add(group);
+    }
+  }
+
+  private void readInspectionSuppressions(final Element element) {
+    for(Iterator i=element.getChildren().iterator(); i.hasNext();){
+      final Element child = (Element)i.next();
+      String inspectionId = LwXmlReader.getRequiredString(child, UIFormXmlConstants.ATTRIBUTE_INSPECTION);
+      String componentId = LwXmlReader.getString(child, UIFormXmlConstants.ATTRIBUTE_ID);
+      myInspectionSuppressions.add(new LwInspectionSuppression(inspectionId, componentId));
+    }
+  }
+
+  public IButtonGroup[] getButtonGroups() {
+    return (LwButtonGroup[])myButtonGroups.toArray(new LwButtonGroup[myButtonGroups.size()]);
+  }
+
+  public String getButtonGroupName(IComponent component) {
+    for(int i=0; i<myButtonGroups.size(); i++) {
+      LwButtonGroup group = (LwButtonGroup) myButtonGroups.get(i);
+      final String[] ids = group.getComponentIds();
+      for(int j=0; j<ids.length; j++) {
+        if (ids [j].equals(component.getId())) {
+          return group.getName();
+        }
+      }
+    }
+    return null;
+  }
+
+  public String[] getButtonGroupComponentIds(String groupName) {
+    for(int i=0; i<myButtonGroups.size(); i++) {
+      LwButtonGroup group = (LwButtonGroup) myButtonGroups.get(i);
+      if (group.getName().equals(groupName)) {
+        return group.getComponentIds();
+      }
+    }
+    throw new IllegalArgumentException("Cannot find group " + groupName);
+  }
+
+  public boolean isInspectionSuppressed(final String inspectionId, final String componentId) {
+    for (Iterator iterator = myInspectionSuppressions.iterator(); iterator.hasNext();) {
+      LwInspectionSuppression suppression = (LwInspectionSuppression)iterator.next();
+      if ((suppression.getComponentId() == null || suppression.getComponentId().equals(componentId)) &&
+          suppression.getInspectionId().equals(inspectionId)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public LwInspectionSuppression[] getInspectionSuppressions() {
+    return (LwInspectionSuppression[]) myInspectionSuppressions.toArray(new LwInspectionSuppression[myInspectionSuppressions.size()]);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwScrollPane.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwScrollPane.java
new file mode 100644
index 0000000..1a68041
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwScrollPane.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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+
+import java.awt.*;
+
+/**
+ * @author Anton Katilin
+ * @author Vladimir Kondratyev
+ */
+public final class LwScrollPane extends LwContainer {
+  public LwScrollPane(String className) {
+    super(className);
+  }
+
+  protected LayoutManager createInitialLayout(){
+    return null;
+  }
+
+  public void read(final Element element, final PropertiesProvider provider) throws Exception {
+    readNoLayout(element, provider);
+  }
+
+  protected void readConstraintsForChild(final Element element, final LwComponent component) {}
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwSplitPane.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwSplitPane.java
new file mode 100644
index 0000000..edf0828
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwSplitPane.java
@@ -0,0 +1,57 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import com.intellij.uiDesigner.compiler.UnexpectedFormElementException;
+import org.jdom.Element;
+
+import java.awt.*;
+
+/**
+ * @author Anton Katilin
+ * @author Vladimir Kondratyev
+ */
+public final class LwSplitPane extends LwContainer{
+  public static final String POSITION_LEFT = "left";
+  public static final String POSITION_RIGHT = "right";
+
+  public LwSplitPane(String className) {
+    super(className);
+  }
+
+  protected LayoutManager createInitialLayout(){
+    return null;
+  }
+
+  public void read(final Element element, final PropertiesProvider provider) throws Exception {
+    readNoLayout(element, provider);
+  }
+
+  protected void readConstraintsForChild(final Element element, final LwComponent component) {
+    final Element constraintsElement = LwXmlReader.getRequiredChild(element, "constraints");
+    final Element splitterChild = LwXmlReader.getRequiredChild(constraintsElement, "splitpane");
+    final String position = LwXmlReader.getRequiredString(splitterChild, "position");
+    if ("left".equals(position)) {
+      component.setCustomLayoutConstraints(POSITION_LEFT);
+    }
+    else if ("right".equals(position)) {
+      component.setCustomLayoutConstraints(POSITION_RIGHT);
+    }
+    else {
+      throw new UnexpectedFormElementException("unexpected position: " + position);
+    }
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwTabbedPane.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwTabbedPane.java
new file mode 100644
index 0000000..2414857
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwTabbedPane.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 com.intellij.uiDesigner.lw;
+
+import com.intellij.uiDesigner.UIFormXmlConstants;
+import org.jdom.Element;
+
+import java.awt.*;
+
+/**
+ * @author Anton Katilin
+ * @author Vladimir Kondratyev
+ */
+public final class LwTabbedPane extends LwContainer implements ITabbedPane {
+  public LwTabbedPane(String className) {
+    super(className);
+  }
+
+  protected LayoutManager createInitialLayout() {
+    return null;
+  }
+
+  public void read(final Element element, final PropertiesProvider provider) throws Exception {
+    readNoLayout(element, provider);
+  }
+
+  protected void readConstraintsForChild(final Element element, final LwComponent component) {
+    final Element constraintsElement = LwXmlReader.getRequiredChild(element, UIFormXmlConstants.ELEMENT_CONSTRAINTS);
+    final Element tabbedPaneChild = LwXmlReader.getRequiredChild(constraintsElement, UIFormXmlConstants.ELEMENT_TABBEDPANE);
+
+    final StringDescriptor descriptor = LwXmlReader.getStringDescriptor(tabbedPaneChild,
+                                                                        UIFormXmlConstants.ATTRIBUTE_TITLE,
+                                                                        UIFormXmlConstants.ATTRIBUTE_TITLE_RESOURCE_BUNDLE,
+                                                                        UIFormXmlConstants.ATTRIBUTE_TITLE_KEY);
+    if (descriptor == null) {
+      throw new IllegalArgumentException("String descriptor value required");
+    }
+    final Constraints constraints = new Constraints(descriptor);
+
+    final Element tooltipElement = LwXmlReader.getChild(tabbedPaneChild, UIFormXmlConstants.ELEMENT_TOOLTIP);
+    if (tooltipElement != null) {
+      constraints.myToolTip = LwXmlReader.getStringDescriptor(tooltipElement,
+                                                              UIFormXmlConstants.ATTRIBUTE_VALUE,
+                                                              UIFormXmlConstants.ATTRIBUTE_RESOURCE_BUNDLE,
+                                                              UIFormXmlConstants.ATTRIBUTE_KEY);
+    }
+
+    String icon = tabbedPaneChild.getAttributeValue(UIFormXmlConstants.ATTRIBUTE_ICON);
+    if (icon != null) {
+      constraints.myIcon = new IconDescriptor(icon);
+    }
+    icon = tabbedPaneChild.getAttributeValue(UIFormXmlConstants.ATTRIBUTE_DISABLED_ICON);
+    if (icon != null) {
+      constraints.myDisabledIcon = new IconDescriptor(icon);
+    }
+    constraints.myEnabled = LwXmlReader.getOptionalBoolean(tabbedPaneChild, UIFormXmlConstants.ATTRIBUTE_ENABLED, true);
+
+    component.setCustomLayoutConstraints(constraints);
+  }
+
+  public static final class Constraints {
+    /**
+     * never null
+     */
+    public StringDescriptor myTitle;
+    public StringDescriptor myToolTip;
+    public IconDescriptor myIcon;
+    public IconDescriptor myDisabledIcon;
+    public boolean myEnabled = true;
+
+    public Constraints(final StringDescriptor title){
+      if (title == null){
+        throw new IllegalArgumentException("title cannot be null");
+      }
+      myTitle = title;
+    }
+
+    public StringDescriptor getProperty(final String propName) {
+      if (propName.equals(TAB_TITLE_PROPERTY)) {
+        return myTitle;
+      }
+      if (propName.equals(TAB_TOOLTIP_PROPERTY)) {
+        return myToolTip;
+      }
+      throw new IllegalArgumentException("Unknown property name " + propName);
+    }
+  }
+
+  public StringDescriptor getTabProperty(IComponent component, final String propName) {
+    LwComponent lwComponent = (LwComponent) component;
+    LwTabbedPane.Constraints constraints = (LwTabbedPane.Constraints) lwComponent.getCustomLayoutConstraints();
+    if (constraints == null) {
+      return null;
+    }
+    return constraints.getProperty(propName);
+  }
+
+  public boolean areChildrenExclusive() {
+    return true;
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwToolBar.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwToolBar.java
new file mode 100644
index 0000000..1849b14
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwToolBar.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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+
+import java.awt.*;
+
+/**
+ * @author yole
+ */
+public class LwToolBar extends LwContainer {
+  public LwToolBar(String className) {
+    super(className);
+  }
+
+  protected LayoutManager createInitialLayout() {
+    return null;
+  }
+
+  public void read(final Element element, final PropertiesProvider provider) throws Exception {
+    readNoLayout(element, provider);
+  }
+
+  protected void readConstraintsForChild(final Element element, final LwComponent component) {
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwVSpacer.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwVSpacer.java
new file mode 100644
index 0000000..71ef7cb
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwVSpacer.java
@@ -0,0 +1,33 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+
+/**
+ * @author Anton Katilin
+ * @author Vladimir Kondratyev
+ */
+public final class LwVSpacer extends LwAtomicComponent {
+  public LwVSpacer() throws Exception{
+    super("com.intellij.uiDesigner.core.Spacer");
+  }
+  
+  public void read(final Element element, final PropertiesProvider provider) throws Exception{
+    readBase(element);
+    readConstraints(element);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwXmlReader.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwXmlReader.java
new file mode 100644
index 0000000..e8e84df
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/LwXmlReader.java
@@ -0,0 +1,220 @@
+/*
+ * 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 com.intellij.uiDesigner.lw;
+
+import com.intellij.uiDesigner.UIFormXmlConstants;
+import org.jdom.Attribute;
+import org.jdom.Element;
+
+import java.awt.*;
+import java.lang.reflect.Method;
+
+public final class LwXmlReader {
+  private LwXmlReader() {
+  }
+
+  /**
+   * @return can be <code>null</code>.
+   */
+  public static Element getChild(final Element element, final String childName) {
+    return element.getChild(childName, element.getNamespace());
+  }
+
+  /**
+   * @return never <code>null</code>.
+   */
+  public static Element getRequiredChild(final Element element, final String childName) {
+    final Element child = getChild(element, childName);
+    if(child == null){
+      throw new IllegalArgumentException("subtag '" + childName + "' is required: "+element);
+    }
+    return child;
+  }
+
+  /**
+   * @return <code>null</code> or trimmed attribute value.
+   */
+  public static String getString(final Element element, final String attributeName){
+    final String value = element.getAttributeValue(attributeName);
+    return value != null ? value.trim() : null;
+  }
+
+  /**
+   * @return never <code>null</code> trimmed attribute value.
+   */
+  public static String getRequiredString(final Element element, final String attributeName) {
+    final String value = getString(element, attributeName);
+    if(value != null){
+      return value;
+    }
+    else{
+      throw new IllegalArgumentException("attribute '" + attributeName + "' is required: "+element);
+    }
+  }
+
+  public static String getOptionalString(final Element element, final String attributeName, final String defaultValue) {
+    final String value = element.getAttributeValue(attributeName);
+    return value != null ? value.trim() : defaultValue;
+  }
+
+  public static int getRequiredInt(final Element element, final String attributeName) {
+    final String str = getRequiredString(element, attributeName);
+    try {
+      return Integer.parseInt(str);
+    }
+    catch (NumberFormatException e) {
+      throw new IllegalArgumentException("attribute '" + attributeName + "' is not a proper integer: " + str);
+    }
+  }
+
+  public static int getOptionalInt(final Element element, final String attributeName, final int defaultValue) {
+    final String str = element.getAttributeValue(attributeName);
+    if (str == null) {
+      return defaultValue;
+    }
+    try {
+      return Integer.parseInt(str);
+    }
+    catch (NumberFormatException e) {
+      throw new IllegalArgumentException("attribute '" + attributeName + "' is not a proper integer: " + str);
+    }
+  }
+
+  public static boolean getOptionalBoolean(final Element element, final String attributeName, final boolean defaultValue) {
+    final String str = element.getAttributeValue(attributeName);
+    if (str == null) {
+      return defaultValue;
+    }
+    return Boolean.valueOf(str).booleanValue();
+  }
+
+  public static double getRequiredDouble(final Element element, final String attributeName) {
+    final String str = getRequiredString(element, attributeName);
+    try {
+      return Double.parseDouble(str);
+    }
+    catch (NumberFormatException e) {
+      throw new IllegalArgumentException("attribute '" + attributeName + "' is not a proper double: " + str);
+    }
+  }
+
+  public static double getOptionalDouble(final Element element, final String attributeName, double defaultValue) {
+    final String str = element.getAttributeValue(attributeName);
+    if (str == null) {
+      return defaultValue;
+    }
+    try {
+      return Double.parseDouble(str);
+    }
+    catch (NumberFormatException e) {
+      throw new IllegalArgumentException("attribute '" + attributeName + "' is not a proper double: " + str);
+    }
+  }
+
+  public static float getRequiredFloat(final Element element, final String attributeName) {
+    final String str = getRequiredString(element, attributeName);
+    try {
+      return Float.parseFloat(str);
+    }
+    catch (NumberFormatException e) {
+      throw new IllegalArgumentException("attribute '" + attributeName + "' is not a proper float: " + str);
+    }
+  }
+
+  public static Object getRequiredPrimitiveTypeValue(final Element element, final String attributeName, final Class valueClass) {
+    final String str = getRequiredString(element, attributeName);
+    try {
+      final Method method = valueClass.getMethod("valueOf", new Class[]{String.class});
+      //noinspection unchecked
+      return method.invoke(null, new Object[]{str});
+    }
+    catch (NumberFormatException e) {
+      throw new IllegalArgumentException("attribute '" + attributeName + "' is not a proper float: " + str);
+    }
+    catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public static StringDescriptor getStringDescriptor(final Element element, final String valueAttr,
+                                                     final String bundleAttr, final String keyAttr) {
+    final String title = element.getAttributeValue(valueAttr);
+    if (title != null) {
+      StringDescriptor descriptor = StringDescriptor.create(title);
+      descriptor.setNoI18n(getOptionalBoolean(element, UIFormXmlConstants.ATTRIBUTE_NOI18N, false));
+      return descriptor;
+    }
+    else {
+      final String bundle = element.getAttributeValue(bundleAttr);
+      if (bundle != null) {
+        final String key = getRequiredString(element, keyAttr);
+        return new StringDescriptor(bundle, key);
+      }
+    }
+
+    return null;
+  }
+
+  public static FontDescriptor getFontDescriptor(final Element element) {
+    String swingFont = element.getAttributeValue(UIFormXmlConstants.ATTRIBUTE_SWING_FONT);
+    if (swingFont != null) {
+      return FontDescriptor.fromSwingFont(swingFont);
+    }
+
+    String fontName = element.getAttributeValue(UIFormXmlConstants.ATTRIBUTE_NAME);
+    int fontStyle = getOptionalInt(element, UIFormXmlConstants.ATTRIBUTE_STYLE, -1);
+    int fontSize = getOptionalInt(element, UIFormXmlConstants.ATTRIBUTE_SIZE, -1);
+    return new FontDescriptor(fontName, fontStyle, fontSize);
+  }
+
+  public static ColorDescriptor getColorDescriptor(final Element element) throws Exception {
+    Attribute attr = element.getAttribute(UIFormXmlConstants.ATTRIBUTE_COLOR);
+    if (attr != null) {
+      return new ColorDescriptor(new Color(attr.getIntValue()));
+    }
+    String swingColor = element.getAttributeValue(UIFormXmlConstants.ATTRIBUTE_SWING_COLOR);
+    if (swingColor != null) {
+      return ColorDescriptor.fromSwingColor(swingColor);
+    }
+    String systemColor = element.getAttributeValue(UIFormXmlConstants.ATTRIBUTE_SYSTEM_COLOR);
+    if (systemColor != null) {
+      return ColorDescriptor.fromSystemColor(systemColor);
+    }
+    String awtColor = element.getAttributeValue(UIFormXmlConstants.ATTRIBUTE_AWT_COLOR);
+    if (awtColor != null) {
+      return ColorDescriptor.fromAWTColor(awtColor);
+    }
+    return new ColorDescriptor(null);
+  }
+
+  public static ColorDescriptor getOptionalColorDescriptor(final Element element) {
+    if (element == null) return null;
+    try {
+      return getColorDescriptor(element);
+    }
+    catch(Exception ex) {
+      return null;
+    }
+  }
+
+  public static Insets readInsets(final Element element) {
+    final int top = getRequiredInt(element, UIFormXmlConstants.ATTRIBUTE_TOP);
+    final int left = getRequiredInt(element, UIFormXmlConstants.ATTRIBUTE_LEFT);
+    final int bottom = getRequiredInt(element, UIFormXmlConstants.ATTRIBUTE_BOTTOM);
+    final int right = getRequiredInt(element, UIFormXmlConstants.ATTRIBUTE_RIGHT);
+    return new Insets(top, left, bottom, right);
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/PropertiesProvider.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/PropertiesProvider.java
new file mode 100644
index 0000000..e342631
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/PropertiesProvider.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 com.intellij.uiDesigner.lw;
+
+import java.util.HashMap;
+
+/**
+ * @author Anton Katilin
+ * @author Vladimir Kondratyev
+ */
+
+public interface PropertiesProvider {
+  /**
+   * @return key - property name (String), value - LwProperty. If class cannot be inspected for some reason,
+   * returns null 
+   */
+  HashMap getLwProperties(String className);
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/StringDescriptor.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/StringDescriptor.java
new file mode 100644
index 0000000..dd46dda
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/StringDescriptor.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.
+ */
+package com.intellij.uiDesigner.lw;
+
+/**
+ * @author Vladimir Kondratyev
+ */
+public final class StringDescriptor {
+  /**
+   * Name of resource bundle
+   */
+  private final String myBundleName;
+  /**
+   * Key in the resource bundle
+   */
+  private final String myKey;
+  /**
+   * Value has sense if it's calculated not via resource bundle
+   */
+  private final String myValue;
+  /**
+   * Cached resolved value. We need it here to speed up property inspector
+   * painting.
+   */
+  private String myResolvedValue;
+
+  /**
+   * Marker for string values which do not need internationalization
+   */
+  private boolean myNoI18n;
+
+  private StringDescriptor(final String value){
+    if (value == null) {
+      throw new IllegalArgumentException("value cannot be null");
+    }
+    myBundleName = null;
+    myKey = null;
+    myValue = value;
+  }
+
+  public StringDescriptor(final String bundleName, final String key) {
+    if (bundleName == null) {
+      throw new IllegalArgumentException("bundleName cannot be null");
+    }
+    if (key == null) {
+      throw new IllegalArgumentException("key cannot be null");
+    }
+    myBundleName = bundleName.replace('.', '/');
+    myKey = key;
+    myValue = null;
+  }
+
+  /**
+   * Creates "trivial" StringDescriptor.
+   */
+  public static StringDescriptor create(final String value){
+    return value != null ? new StringDescriptor(value) : null;
+  }
+
+  /**
+   * @return not <code>null</code> value if this is "trivial" StringDescriptor.
+   * If StringDescriptor is "trivial" then {@link #getBundleName()} and {@link #getKey()}
+   * return <code>null</code>.
+   */
+  public String getValue(){
+    return myValue;
+  }
+
+  /**
+   * @return not <code>null</code> value if this is non "trivial" StringDescriptor.
+   */
+  public String getBundleName() {
+    return myBundleName;
+  }
+
+  public String getDottedBundleName() {
+    return myBundleName == null ? null : myBundleName.replace('/', '.');
+  }
+
+  /**
+   * @return not <code>null</code> value if this is non "trivial" StringDescriptor.
+   */
+  public String getKey() {
+    return myKey;
+  }
+
+  /**
+   * @return can be null
+   */
+  public String getResolvedValue() {
+    return myResolvedValue;
+  }
+
+  /**
+   * @param resolvedValue can be null
+   */
+  public void setResolvedValue(final String resolvedValue) {
+    myResolvedValue = resolvedValue;
+  }
+
+  public boolean isNoI18n() {
+    return myNoI18n;
+  }
+
+  public void setNoI18n(final boolean noI18n) {
+    myNoI18n = noI18n;
+  }
+
+  public boolean equals(final Object o) {
+    if (this == o) return true;
+    if (!(o instanceof StringDescriptor)) return false;
+
+    final StringDescriptor descriptor = (StringDescriptor)o;
+
+    if (myBundleName != null ? !myBundleName.equals(descriptor.myBundleName) : descriptor.myBundleName != null) return false;
+    if (myKey != null ? !myKey.equals(descriptor.myKey) : descriptor.myKey != null) return false;
+    if (myValue != null ? !myValue.equals(descriptor.myValue) : descriptor.myValue != null) return false;
+    if (myNoI18n != descriptor.myNoI18n) return false;
+
+    return true;
+  }
+
+  public int hashCode() {
+    int result;
+    result = (myBundleName != null ? myBundleName.hashCode() : 0);
+    result = 29 * result + (myKey != null ? myKey.hashCode() : 0);
+    result = 29 * result + (myValue != null ? myValue.hashCode() : 0);
+    return result;
+  }
+
+  public String toString() {
+    if (myValue != null) {
+      return "[StringDescriptor:" + myValue + "]";
+    }
+    return "[StringDescriptor" + myBundleName + ":" + myKey + "]";
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/XYLayoutSerializer.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/XYLayoutSerializer.java
new file mode 100644
index 0000000..f637d8f
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/lw/XYLayoutSerializer.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 com.intellij.uiDesigner.lw;
+
+import org.jdom.Element;
+import com.intellij.uiDesigner.shared.XYLayoutManager;
+
+import java.awt.*;
+
+/**
+ * @author yole
+ */
+public class XYLayoutSerializer extends LayoutSerializer {
+  static XYLayoutSerializer INSTANCE = new XYLayoutSerializer();
+
+  private XYLayoutSerializer() {
+  }
+
+  void readLayout(Element element, LwContainer container) {
+    container.setLayout(new XYLayoutManager());
+  }
+
+  void readChildConstraints(final Element constraintsElement, final LwComponent component) {
+    final Element xyElement = LwXmlReader.getChild(constraintsElement, "xy");
+    if(xyElement != null){
+      component.setBounds(
+        new Rectangle(
+          LwXmlReader.getRequiredInt(xyElement, "x"),
+          LwXmlReader.getRequiredInt(xyElement, "y"),
+          LwXmlReader.getRequiredInt(xyElement, "width"),
+          LwXmlReader.getRequiredInt(xyElement, "height")
+        )
+      );
+    }
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/shared/BorderType.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/shared/BorderType.java
new file mode 100644
index 0000000..fe72072
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/shared/BorderType.java
@@ -0,0 +1,111 @@
+/*
+ * 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 com.intellij.uiDesigner.shared;
+
+import com.intellij.uiDesigner.compiler.UnexpectedFormElementException;
+
+import javax.swing.*;
+import javax.swing.border.Border;
+import java.awt.*;
+
+/**
+ * @author Vladimir Kondratyev
+ */
+public final class BorderType {
+  public static final BorderType NONE = new BorderType("none", "None",null,null);
+  public static final BorderType BEVEL_LOWERED = new BorderType("bevel-lowered", "Bevel Lowered", BorderFactory.createLoweredBevelBorder(), "createLoweredBevelBorder");
+  public static final BorderType BEVEL_RAISED = new BorderType("bevel-raised", "Bevel Raised", BorderFactory.createRaisedBevelBorder(), "createRaisedBevelBorder");
+  public static final BorderType ETCHED = new BorderType("etched", "Etched", BorderFactory.createEtchedBorder(), "createEtchedBorder");
+  public static final BorderType LINE = new BorderType("line", "Line", BorderFactory.createLineBorder(Color.BLACK), "createLineBorder");
+  public static final BorderType EMPTY = new BorderType("empty", "Empty", BorderFactory.createEmptyBorder(0, 0, 0, 0), "createEmptyBorder");
+
+  private final String myId;
+  private final String myName;
+  private final Border myBorder;
+  private final String myBorderFactoryMethodName;
+
+  private BorderType(final String id, final String name, final Border border, final String borderFactoryMethodName) {
+    myId=id;
+    myName=name;
+    myBorder=border;
+    myBorderFactoryMethodName = borderFactoryMethodName;
+  }
+
+  public String getId(){
+    return myId;
+  }
+
+  public String getName(){
+    return myName;
+  }
+
+  public Border createBorder(final String title,
+                             final int titleJustification,
+                             final int titlePosition,
+                             final Font titleFont,
+                             final Color titleColor,
+                             final Insets borderSize, 
+                             final Color borderColor) {
+    Border baseBorder = myBorder;
+    if (equals(EMPTY) && borderSize != null) {
+      baseBorder = BorderFactory.createEmptyBorder(borderSize.top, borderSize.left, borderSize.bottom, borderSize.right);
+    }
+    else if (equals(LINE) && borderColor != null) {
+      baseBorder = BorderFactory.createLineBorder(borderColor);
+    }
+
+    if (title != null) {
+      return BorderFactory.createTitledBorder(baseBorder, title, titleJustification, titlePosition, titleFont, titleColor);
+    }
+    else {
+      return baseBorder;
+    }
+  }
+
+  public String getBorderFactoryMethodName(){
+    return myBorderFactoryMethodName;
+  }
+
+  public boolean equals(final Object o){
+    if (o instanceof BorderType){
+      return myId.equals(((BorderType)o).myId);
+    }
+    return false;
+  }
+
+  public int hashCode(){
+    return 0;
+  }
+
+  public static BorderType valueOf(final String name){
+    BorderType[] allTypes = getAllTypes();
+    for(int i=0; i<allTypes.length; i++) {
+      if (allTypes [i].getId().equals(name)) return allTypes [i];
+    }
+    throw new UnexpectedFormElementException("unknown type: "+name);
+  }
+
+  public static BorderType[] getAllTypes() {
+    return new BorderType[]{
+          BorderType.NONE,
+          BorderType.EMPTY,
+          BorderType.BEVEL_LOWERED,
+          BorderType.BEVEL_RAISED,
+          BorderType.ETCHED,
+          BorderType.LINE
+        };
+  }
+}
diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/shared/XYLayoutManager.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/shared/XYLayoutManager.java
new file mode 100644
index 0000000..bdc5a41
--- /dev/null
+++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/shared/XYLayoutManager.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.
+ */
+package com.intellij.uiDesigner.shared;
+
+import com.intellij.uiDesigner.core.AbstractLayout;
+
+import java.awt.*;
+
+public class XYLayoutManager extends AbstractLayout {
+  public XYLayoutManager(){
+  }
+
+  public Dimension maximumLayoutSize(final Container target){
+    throw new UnsupportedOperationException();
+  }
+
+  public Dimension preferredLayoutSize(final Container parent){
+    throw new UnsupportedOperationException();
+  }
+
+  public Dimension minimumLayoutSize(final Container parent){
+    throw new UnsupportedOperationException();
+  }
+
+  public void layoutContainer(final Container parent){
+    throw new UnsupportedOperationException();
+  }
+  
+  public void setPreferredSize(final Dimension size){
+    throw new UnsupportedOperationException();
+  }
+
+  public final void invalidateLayout(final Container target){
+  }
+}
diff --git a/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/EliminateTest.java b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/EliminateTest.java
new file mode 100644
index 0000000..2594945
--- /dev/null
+++ b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/EliminateTest.java
@@ -0,0 +1,205 @@
+/*
+ * 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 com.intellij.uiDesigner.core;
+
+import junit.framework.TestCase;
+
+/**
+ * @author Anton Katilin
+ * @author Vladimir Kondratyev
+ */
+public final class EliminateTest extends TestCase{
+  public void test1() {
+    // 11
+    //  2
+    doTest(
+      new int[][]{
+        {0, 2},
+        {1, 1}
+      },
+      new int[][]{
+        {0, 1},
+        {0, 1}
+      }
+    );
+  }
+
+  public void test2(){
+    // 111
+    //  2
+    doTest(
+      new int[][]{
+        {0, 3},
+        {1, 1}
+      },
+      new int[][]{
+        {0, 1},
+        {0, 1}
+      }
+    );
+  }
+
+  public void test3(){
+    // 11
+    // 2
+    doTest(
+      new int[][]{
+        {0,2},
+        {0,1}
+      },
+      new int[][]{
+        {0, 1},
+        {0, 1}
+      }
+    );
+  }
+
+  public void test4(){
+    // 12
+    // 33
+    doTest(
+      new int[][]{
+        {0, 1},
+        {1, 1},
+        {0, 2}
+      },
+      new int[][]{
+        {0, 1},
+        {1, 1},
+        {0, 2}
+      }
+    );
+  }
+
+  public void test5(){
+    // 112
+    //  333
+    doTest(
+      new int[][]{
+        {0, 2},
+        {2, 1},
+        {1, 3}
+      },
+      new int[][]{
+        {0, 1},
+        {1, 1},
+        {0, 2}
+      }
+    );
+  }
+
+  public void test7(){
+    // 11 222
+    //  3333
+    doTest(
+      new int[][]{
+        {0, 2},
+        {3, 3},
+        {1, 4}
+      },
+      new int[][]{
+        {0, 1},
+        {1, 1},
+        {0, 2}
+      }
+    );
+  }
+
+  public void test8(){
+    //   111
+    // 222
+    doTest(
+      new int[][]{
+        {2, 3},
+        {0, 3}
+      },
+      new int[][]{
+        {0, 1},
+        {0, 1}
+      }
+    );
+  }
+
+  public void test9(){
+    // 1 22
+    // 333
+    doTest(
+      new int[][]{
+        {0, 1},
+        {2, 2},
+        {0, 3}
+      },
+      new int[][]{
+        {0, 1},
+        {1, 1},
+        {0, 2}
+      }
+    );
+  }
+
+  public void test9a(){
+    // 122
+    // 33
+    doTest(
+      new int[][]{
+        {0, 1},
+        {1, 2},
+        {0, 2}
+      },
+      new int[][]{
+        {0, 1},
+        {1, 1},
+        {0, 2}
+      }
+    );
+  }
+
+  public void test10(){
+    // 1 2
+    doTest(
+      new int[][]{
+        {0, 1},
+        {2, 1},
+      },
+      new int[][]{
+        {0, 1},
+        {1, 1},
+      }
+    );
+  }
+
+  private static void doTest(final int[][] src, final int[][] expected) {
+    final int[] cells = new int[src.length];
+    final int[] spans = new int[src.length];
+    for (int i = 0; i < cells.length; i++) {
+       cells[i] = src[i][0];
+       spans[i] = src[i][1];
+    }
+
+    Util.eliminate(cells, spans, null);
+
+    for (int i = 0; i < spans.length; i++) {
+      if (cells[i] != expected[i][0] ||
+        spans[i] != expected[i][1]
+      ){
+        for (int j = 0; j < spans.length; j++) {
+          System.out.println("i=" + j + " expected ("+expected[j][0] + "," + expected[j][1] +"), result (" + cells[j] + "," + spans[j]+")");
+        }
+        assertTrue(false);
+      }
+    }
+  }
+}
diff --git a/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/EmptyPanelTest.java b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/EmptyPanelTest.java
new file mode 100644
index 0000000..603c005
--- /dev/null
+++ b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/EmptyPanelTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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 com.intellij.uiDesigner.core;
+
+import junit.framework.TestCase;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.Arrays;
+
+public final class EmptyPanelTest extends TestCase {
+
+  public void test1() {
+    final GridLayoutManager layoutManager = new GridLayoutManager(2, 3, new Insets(0, 0, 0, 0), 0, 0);
+    final JPanel panel = new JPanel(layoutManager);
+
+    assertEquals(0, panel.getPreferredSize().width);
+    assertEquals(0, panel.getPreferredSize().height);
+
+    panel.setSize(90, 200);
+    panel.doLayout(); // should not crash with exception
+
+    assertTrue(Arrays.equals(new int[]{0, 30, 60}, layoutManager.getXs()));
+    assertTrue(Arrays.equals(new int[]{30, 30, 30}, layoutManager.getWidths()));
+
+    assertTrue(Arrays.equals(new int[]{0, 100}, layoutManager.getYs()));
+    assertTrue(Arrays.equals(new int[]{100, 100}, layoutManager.getHeights()));
+
+    // add component 
+    final JButton button = new JButton();
+    button.setPreferredSize(new Dimension(100, 20));
+    panel.add(button,
+              new GridConstraints(0, 1, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_GROW,
+                                  GridConstraints.SIZEPOLICY_FIXED, null, null, null,
+                                  0));
+
+    // wisdom
+    layoutManager.invalidateLayout(panel);
+
+    if (SystemInfo.isMac) {
+      assertEquals(new Dimension(100, 29), panel.getPreferredSize());
+      panel.setSize(panel.getPreferredSize());
+      panel.doLayout();
+      assertEquals(100, button.getWidth());
+      assertEquals(29, panel.getHeight());
+
+      assertTrue(Arrays.equals(new int[]{0, 29}, layoutManager.getYs()));
+      assertTrue(Arrays.equals(new int[]{29, 0}, layoutManager.getHeights()));
+    }
+    else {
+      assertEquals(new Dimension(100, 20), panel.getPreferredSize());
+      panel.setSize(panel.getPreferredSize());
+      panel.doLayout();
+      assertEquals(100, button.getWidth());
+      assertEquals(20, panel.getHeight());
+
+      assertTrue(Arrays.equals(new int[]{0, 20}, layoutManager.getYs()));
+      assertTrue(Arrays.equals(new int[]{20, 0}, layoutManager.getHeights()));
+    }
+
+    assertTrue(Arrays.equals(new int[]{0, 0, 100}, layoutManager.getXs()));
+    assertTrue(Arrays.equals(new int[]{0, 100, 0}, layoutManager.getWidths()));
+  }
+}
diff --git a/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/EqualSizeCellsTest.java b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/EqualSizeCellsTest.java
new file mode 100644
index 0000000..43e156f
--- /dev/null
+++ b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/EqualSizeCellsTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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 com.intellij.uiDesigner.core;
+
+import junit.framework.TestCase;
+
+import javax.swing.*;
+import java.awt.*;
+
+public final class EqualSizeCellsTest extends TestCase{
+  /**
+   * field1 | field2 | field3
+   */ 
+  public void test1() {
+    final GridLayoutManager layout = new GridLayoutManager(1,3, new Insets(0,0,0,0), 7, 0);
+
+    layout.setSameSizeHorizontally(true);
+
+    final JPanel panel = new JPanel(layout);
+
+    final JTextField field1 = new JTextField();
+    field1.setMinimumSize(new Dimension(5,20));
+    field1.setPreferredSize(new Dimension(10,20));
+
+    final JTextField field2 = new JTextField();
+    field2.setMinimumSize(new Dimension(25,20));
+    field2.setPreferredSize(new Dimension(50,20));
+
+    final JTextField field3 = new JTextField();
+    field3.setMinimumSize(new Dimension(70,20));
+    field3.setPreferredSize(new Dimension(100,20));
+
+    panel.add(field1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(field2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(field3, new GridConstraints(0,2,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    final Dimension minimumSize = panel.getMinimumSize();
+    assertEquals(70 + 7 + 70 + 7 + 70, minimumSize.width);
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(100 + 7 + 100 + 7 + 100, preferredSize.width);
+
+    //
+    panel.setSize(panel.getPreferredSize());
+    panel.doLayout();
+
+    assertEquals(100, field1.getWidth());
+    assertEquals(100, field2.getWidth());
+    assertEquals(100, field3.getWidth());
+
+    //
+    panel.setSize(new Dimension(1000, 1000));
+    panel.doLayout();
+
+    assertEquals(329, field1.getWidth());
+    assertEquals(329, field2.getWidth());
+    assertEquals(328, field3.getWidth());
+  }
+
+
+  /**
+   * field1
+   * ------
+   * field2
+   * ------
+   * field3
+   */
+  public void test2() {
+    final GridLayoutManager layout = new GridLayoutManager(3,1, new Insets(0,0,0,0), 0, 7);
+
+    layout.setSameSizeVertically(true);
+
+    final JPanel panel = new JPanel(layout);
+
+    final JTextField field1 = new JTextField();
+    field1.setMinimumSize(new Dimension(20, 5));
+    field1.setPreferredSize(new Dimension(20, 10));
+
+    final JTextField field2 = new JTextField();
+    field2.setMinimumSize(new Dimension(20, 25));
+    field2.setPreferredSize(new Dimension(20, 50));
+
+    final JTextField field3 = new JTextField();
+    field3.setMinimumSize(new Dimension(20, 70));
+    field3.setPreferredSize(new Dimension(20, 100));
+
+    panel.add(field1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_WANT_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, null, null, null, 0));
+
+    panel.add(field2, new GridConstraints(1,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_WANT_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, null, null, null, 0));
+
+    panel.add(field3, new GridConstraints(2,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_WANT_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, null, null, null, 0));
+
+    final Dimension minimumSize = panel.getMinimumSize();
+    assertEquals(70 + 7 + 70 + 7 + 70, minimumSize.height);
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(100 + 7 + 100 + 7 + 100, preferredSize.height);
+
+    panel.setSize(panel.getPreferredSize());
+    panel.doLayout();
+
+    assertEquals(100, field1.getHeight());
+    assertEquals(100, field2.getHeight());
+    assertEquals(100, field3.getHeight());
+  }
+}
diff --git a/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/GapsTest.java b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/GapsTest.java
new file mode 100644
index 0000000..bdc045c
--- /dev/null
+++ b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/GapsTest.java
@@ -0,0 +1,275 @@
+/*
+ * 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 com.intellij.uiDesigner.core;
+
+import junit.framework.TestCase;
+
+import javax.swing.*;
+import java.awt.*;
+
+public final class GapsTest extends TestCase{
+  public void test1() {
+    final JPanel panel = new JPanel(new GridLayoutManager(1,2, new Insets(0,0,0,0), 10, 0));
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(100,20));
+    final JTextField field2 = new JTextField();
+    field2.setPreferredSize(new Dimension(100,20));
+
+    panel.add(field1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(field2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(210, preferredSize.width);
+  }
+
+  /**
+   * field (span 2) | field (span 1)
+   */
+  public void test2() {
+    final JPanel panel = new JPanel(new GridLayoutManager(1,3, new Insets(0,0,0,0), 7, 0));
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(100,20));
+    final JTextField field2 = new JTextField();
+    field2.setPreferredSize(new Dimension(100,20));
+
+    panel.add(field1, new GridConstraints(0,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(field2, new GridConstraints(0,2,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(207, preferredSize.width);
+
+    panel.setSize(panel.getPreferredSize());
+    panel.doLayout();
+
+    if (SystemInfo.isMac) {
+      assertEquals(new Rectangle(0,0,100,28), field1.getBounds());
+      assertEquals(new Rectangle(107,0,100,28), field2.getBounds());
+    } else {
+      assertEquals(new Rectangle(0,0,100,20), field1.getBounds());
+      assertEquals(new Rectangle(107,0,100,20), field2.getBounds());
+    }
+  }
+
+
+  /**
+   *
+   * btn1   |    btn2  | btn4
+   *  btn3 (span 2)    |
+   */
+  public void test3() {
+    final JPanel panel = new JPanel(new GridLayoutManager(2,3, new Insets(0,0,0,0), 7, 0));
+
+    final JButton btn1 = new JButton();
+    btn1.setPreferredSize(new Dimension(100,20));
+    final JButton btn2 = new JButton();
+    btn2.setPreferredSize(new Dimension(100,20));
+    final JButton btn3 = new JButton();
+    btn3.setPreferredSize(new Dimension(100,20));
+    final JButton btn4 = new JButton();
+    btn4.setPreferredSize(new Dimension(100,20));
+
+    panel.add(btn1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(btn2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(btn3, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(btn4, new GridConstraints(0,2,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(314, preferredSize.width);
+
+    panel.setSize(panel.getPreferredSize());
+    panel.doLayout();
+  }
+
+  /**
+   *
+   * btn1   |    btn2  | btn4
+   *  btn3 (span 2)    |
+   */
+  public void test3a() {
+    final JPanel panel = new JPanel(new GridLayoutManager(2,3, new Insets(0,0,0,0), 1000, 0));
+
+    final JButton btn1 = new JButton();
+    btn1.setPreferredSize(new Dimension(100,20));
+    final JButton btn2 = new JButton();
+    btn2.setPreferredSize(new Dimension(100,20));
+    final JButton btn3 = new JButton();
+    btn3.setPreferredSize(new Dimension(100,20));
+    final JButton btn4 = new JButton();
+    btn4.setPreferredSize(new Dimension(100,20));
+
+    panel.add(btn1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(btn2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(btn3, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(btn4, new GridConstraints(0,2,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(2300, preferredSize.width);
+
+    panel.setSize(panel.getPreferredSize());
+    panel.doLayout();
+  }
+
+  /**
+   *
+   * btn1   |    btn2
+   *  btn3 (span 2)
+   */
+  public void test3b() {
+    final JPanel panel = new JPanel(new GridLayoutManager(2,3, new Insets(0,0,0,0), 1000, 0));
+
+    final JButton btn1 = new JButton();
+    btn1.setPreferredSize(new Dimension(100,20));
+    final JButton btn2 = new JButton();
+    btn2.setPreferredSize(new Dimension(100,20));
+    final JButton btn3 = new JButton();
+    btn3.setPreferredSize(new Dimension(100,20));
+
+    panel.add(btn1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(btn2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(btn3, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(1200, preferredSize.width);
+
+    panel.setSize(panel.getPreferredSize());
+    panel.doLayout();
+  }
+
+  /**
+   * btn1
+   * -----
+   * empty
+   * ----
+   * btn2
+   */
+  public void test4() {
+    final JPanel panel = new JPanel(new GridLayoutManager(3,1, new Insets(0,0,0,0), 0, 7));
+
+    final JButton btn1 = new JButton();
+    btn1.setPreferredSize(new Dimension(100,20));
+    final JButton btn2 = new JButton();
+    btn2.setPreferredSize(new Dimension(100,20));
+
+    panel.add(btn1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(btn2, new GridConstraints(2,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    if (SystemInfo.isMac) {
+      assertEquals(65, preferredSize.height);
+    } else {
+      assertEquals(47, preferredSize.height);
+    }
+
+    panel.setSize(panel.getPreferredSize());
+    panel.doLayout();
+  }
+
+  /**
+   * btn1
+   * -----
+   * spacer
+   * ----
+   * btn2
+   */
+  public void test5() {
+    final JPanel panel = new JPanel(new GridLayoutManager(3,1, new Insets(0,0,0,0), 0, 7));
+
+    final JButton btn1 = new JButton();
+    btn1.setPreferredSize(new Dimension(100,20));
+    final JButton btn2 = new JButton();
+    btn2.setPreferredSize(new Dimension(100,20));
+
+    panel.add(btn1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(new Spacer(), new GridConstraints(1,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0));
+
+    panel.add(btn2, new GridConstraints(2,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    if (SystemInfo.isMac) {
+      assertEquals(65, preferredSize.height);
+    } else {
+      assertEquals(47, preferredSize.height);
+    }
+
+    panel.setSize(panel.getPreferredSize());
+    panel.doLayout();
+  }
+
+  /**
+   * btn1
+   * ----- (very big gap)
+   * btn2
+   */
+  public void test6() {
+    final JPanel panel = new JPanel(new GridLayoutManager(2,1, new Insets(0,0,0,0), 0, 500));
+
+    final JButton btn1 = new JButton();
+    btn1.setPreferredSize(new Dimension(100,20));
+    final JButton btn2 = new JButton();
+    btn2.setPreferredSize(new Dimension(100,20));
+
+    panel.add(btn1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(btn2, new GridConstraints(1,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    if (SystemInfo.isMac) {
+      assertEquals(558, preferredSize.height);
+    } else {
+      assertEquals(540, preferredSize.height);
+    }
+
+    panel.setSize(panel.getPreferredSize());
+    panel.doLayout(); // should not crash
+  }
+
+}
diff --git a/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/GridBagConverterTest.java b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/GridBagConverterTest.java
new file mode 100644
index 0000000..1785247
--- /dev/null
+++ b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/GridBagConverterTest.java
@@ -0,0 +1,905 @@
+/*
+ * 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 com.intellij.uiDesigner.core;
+
+import com.intellij.uiDesigner.compiler.GridBagConverter;
+import junit.framework.TestCase;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * @author yole
+ */
+public class GridBagConverterTest extends TestCase {
+  /**
+   * button 1
+   * <empty>
+   * button 2
+   */
+  public void testLayout2() {
+    final GridBagLayout layoutManager = new GridBagLayout();
+    final JPanel panel = new JPanel(layoutManager);
+
+    final JButton button1 = new JButton();
+    button1.setMinimumSize(new Dimension(9, 7));
+    button1.setPreferredSize(new Dimension(50, 10));
+
+    final JButton button2 = new JButton();
+    button2.setMinimumSize(new Dimension(15, 6));
+    button2.setPreferredSize(new Dimension(50, 10));
+
+    GridBagConverter converter = new GridBagConverter();
+    final GridConstraints button1Constraints = new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+                                                                   GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK,
+                                                                   null, null, null, 0);
+    converter.addComponent(button1, button1Constraints);
+
+    final GridConstraints button2Constraints = new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+                                                                   GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK,
+                                                                   GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0);
+
+    converter.addComponent(button2, button2Constraints);
+
+    applyConversionResults(panel, converter);
+
+    assertEquals(20, panel.getPreferredSize().height);
+    assertEquals(50, panel.getPreferredSize().width);
+
+    assertEquals(17, panel.getMinimumSize().height);
+    assertEquals(50, panel.getMinimumSize().width);
+
+    panel.setSize(new Dimension(500, 100));
+    panel.doLayout();
+
+    assertEquals(50, button1.getHeight());
+    assertEquals(50, button2.getHeight());
+  }
+
+  public void testLayout2ByConstraints() {
+    final GridBagLayout layoutManager = new GridBagLayout();
+    final JPanel panel = new JPanel(layoutManager);
+    final JButton button1 = new JButton();
+    final JButton button2 = new JButton();
+
+    GridBagConverter converter = new GridBagConverter();
+    final GridConstraints button1Constraints = new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+                                                                   GridConstraints.SIZEPOLICY_CAN_GROW,
+                                                                   GridConstraints.SIZEPOLICY_CAN_SHRINK,
+                                                                   new Dimension(9, 7), new Dimension(50, 10), null, 0);
+    converter.addComponent(button1, button1Constraints);
+
+    final GridConstraints button2Constraints = new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
+                                                                   GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK,
+                                                                   GridConstraints.SIZEPOLICY_FIXED,
+                                                                   new Dimension(15, 6), new Dimension(50, 10), null, 0);
+
+    converter.addComponent(button2, button2Constraints);
+
+    applyConversionResults(panel, converter);
+
+    assertEquals(20, panel.getPreferredSize().height);
+    assertEquals(50, panel.getPreferredSize().width);
+
+    assertEquals(17, panel.getMinimumSize().height);
+    assertEquals(50, panel.getMinimumSize().width);
+
+    panel.setSize(new Dimension(500, 100));
+    panel.doLayout();
+
+    assertEquals(50, button1.getHeight());
+    assertEquals(50, button2.getHeight());
+  }
+
+  public void testLayout3() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JButton button1 = new JButton();
+    button1.setPreferredSize(new Dimension(100,20));
+    final JButton button2 = new JButton();
+    button2.setPreferredSize(new Dimension(100,100));
+
+    GridBagConverter converter = new GridBagConverter();
+    converter.addComponent(button1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(button2, new GridConstraints(1,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    applyConversionResults(panel, converter);
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(120, preferredSize.height);
+  }
+
+  public void testLayout4() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    // button 1  button 3
+    // button 2  button 3
+
+    final JButton button1 = new JButton();
+    button1.setPreferredSize(new Dimension(100,10));
+    final JButton button2 = new JButton();
+    button2.setPreferredSize(new Dimension(100,10));
+    final JButton button3 = new JButton();
+    button3.setPreferredSize(new Dimension(100,200));
+
+    GridBagConverter converter = new GridBagConverter();
+    converter.addComponent(button1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW + GridConstraints.SIZEPOLICY_CAN_SHRINK, null, null, null,
+      0));
+
+    converter.addComponent(button2, new GridConstraints(1,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW + GridConstraints.SIZEPOLICY_CAN_SHRINK, null, null, null,
+      0));
+
+    converter.addComponent(button3, new GridConstraints(0,1,2,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW + GridConstraints.SIZEPOLICY_CAN_SHRINK, null, null, null,
+      0));
+
+    applyConversionResults(panel, converter);
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(200, preferredSize.height);
+  }
+
+  /* TODO[yole]: this layout does not work as expected at runtime
+  public void testLayout5_1() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    // label textfield(span 2)
+    // textfield(span 2)
+
+    final JTextField label = new JTextField();
+    label.setPreferredSize(new Dimension(10,30));
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(100,30));
+
+    final JTextField field2 = new JTextField();
+    field2.setPreferredSize(new Dimension(100,30));
+
+    GridBagConverter converter = new GridBagConverter();
+    converter.addComponent(label, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null));
+
+    converter.addComponent(field1, new GridConstraints(0,1,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null));
+
+    converter.addComponent(field2, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null));
+
+    applyConversionResults(panel, converter);
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(110, preferredSize.width);
+    assertEquals(60, preferredSize.height);
+  }
+  */
+
+  public void testLayout7() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JLabel label = new JLabel();
+    label.setPreferredSize(new Dimension(50,10));
+
+    final JTextField field = new JTextField();
+    field.setPreferredSize(new Dimension(100,10));
+
+    final JTextField scroll = new JTextField();
+    scroll.setPreferredSize(new Dimension(503, 10));
+
+    GridBagConverter converter = new GridBagConverter();
+    converter.addComponent(label, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null, 0));
+
+    converter.addComponent(field, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null, 0));
+
+    converter.addComponent(scroll, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null, 0));
+
+    applyConversionResults(panel, converter);
+
+    assertEquals(503, panel.getMinimumSize().width);
+    assertEquals(503, panel.getPreferredSize().width);
+
+    panel.setSize(503, 100);
+    panel.doLayout();
+
+    assertEquals(50, label.getWidth());
+    assertEquals(453, field.getWidth());
+  }
+
+  public void testLayout8() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JLabel label1 = new JLabel();
+    label1.setMinimumSize(new Dimension(10,10));
+    label1.setPreferredSize(new Dimension(100,10));
+
+    final JLabel label2 = new JLabel();
+    label2.setMinimumSize(new Dimension(10,10));
+    label2.setPreferredSize(new Dimension(100,10));
+
+    GridBagConverter converter = new GridBagConverter();
+    converter.addComponent(label1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_SHRINK + GridConstraints.SIZEPOLICY_CAN_GROW,
+      GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(label2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_SHRINK,
+      GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(new JLabel(), new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_SHRINK + GridConstraints.SIZEPOLICY_CAN_GROW + GridConstraints.SIZEPOLICY_WANT_GROW,
+      GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0));
+    applyConversionResults(panel, converter);
+
+    assertEquals(20, panel.getMinimumSize().width);
+    assertEquals(200, panel.getPreferredSize().width);
+
+    // minimum
+    panel.setSize(20, 100);
+    panel.doLayout();
+    assertEquals(10, label1.getWidth());
+    assertEquals(10, label2.getWidth());
+
+    // between min and pref
+    /* TODO[yole]: GridBag honors weights in this situation, and GridLayout distributes evenly
+    panel.setSize(76, 100);
+    panel.doLayout();
+    assertEquals(38, label1.getWidth());
+    assertEquals(38, label2.getWidth());
+    */
+
+    // pref-1
+    /* TODO[yole]: investigate
+    panel.setSize(199, 100);
+    panel.doLayout();
+    assertEquals(100, label1.getWidth());
+    assertEquals(99, label2.getWidth());
+    */
+
+    // pref
+    panel.setSize(200, 100);
+    panel.doLayout();
+    assertEquals(100, label1.getWidth());
+    assertEquals(100, label2.getWidth());
+
+    // pref+1
+    panel.setSize(201, 100);
+    panel.doLayout();
+    assertEquals(101, label1.getWidth());
+    assertEquals(100, label2.getWidth());
+
+    // pref + few
+    panel.setSize(205, 100);
+    panel.doLayout();
+    assertEquals(105, label1.getWidth());
+    assertEquals(100, label2.getWidth());
+  }
+
+  public void testPrefSize1() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JTextField field1 = new JTextField();
+    field1.setMinimumSize(new Dimension(110,10));
+    field1.setPreferredSize(new Dimension(120,10));
+
+    final JTextField field2 = new JTextField();
+    field2.setMinimumSize(new Dimension(215,10));
+    field2.setPreferredSize(new Dimension(225,10));
+
+    GridBagConverter converter = new GridBagConverter();
+    converter.addComponent(field1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(field2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    applyConversionResults(panel, converter);
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(345, preferredSize.width);
+
+    final Dimension minSize = panel.getMinimumSize();
+    assertEquals(325, minSize.width);
+
+    panel.setSize(preferredSize.width, preferredSize.height);
+    panel.doLayout();
+
+    assertEquals(120, field1.getWidth());
+    assertEquals(225, field2.getWidth());
+
+    panel.setSize(400, panel.getWidth());
+    panel.invalidate(); // to invalidate layout
+    panel.doLayout();
+  }
+
+  /* TODO[yole]: this relies on strange myMinCellSize logic
+  public void testPrefSize2() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(100,10));
+
+    final JTextField field2 = new JTextField();
+    field2.setPreferredSize(new Dimension(200,10));
+
+    GridBagConverter converter = new GridBagConverter();
+    converter.addComponent(field1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null));
+
+    converter.addComponent(field2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null));
+
+    applyConversionResults(panel, converter);
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(300 + 20, preferredSize.width);
+
+    panel.setSize(preferredSize.width, preferredSize.height);
+    panel.doLayout();
+
+    assertEquals(100, field1.getWidth());
+    assertEquals(200, field2.getWidth());
+
+    panel.setSize(270, preferredSize.height);
+    panel.doLayout();   // should not fail
+  }
+  */
+
+  /**
+   * button(can grow) | text field (want grow)
+   *   text field (want grow, span 2)
+   */
+  public void testSpans1() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JButton button = new JButton();
+    button.setPreferredSize(new Dimension(50, 10));
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(50, 10));
+
+    final JTextField field2 = new JTextField();
+
+    GridBagConverter converter = new GridBagConverter();
+    converter.addComponent(button, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(field1, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(field2, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    applyConversionResults(panel, converter);
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(100, preferredSize.width);
+
+    panel.setSize(new Dimension(500, 100));
+    panel.doLayout();
+
+    assertEquals(500, field2.getWidth());
+    assertEquals(50, button.getWidth());
+    assertEquals(450, field1.getWidth());
+  }
+
+  /**
+   * button(can grow) | text field (can grow)
+   *   text field (want grow, span 2)
+   */
+  public void testSpans2() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JButton button = new JButton();
+    button.setPreferredSize(new Dimension(50, 10));
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(50, 10));
+
+    final JTextField field2 = new JTextField();
+
+    GridBagConverter converter = new GridBagConverter();
+    converter.addComponent(button, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(field1, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+                                          GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(field2, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    applyConversionResults(panel, converter);
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(100, preferredSize.width);
+
+    panel.setSize(new Dimension(500, 100));
+    panel.doLayout();
+
+    assertEquals(500, field2.getWidth());
+    assertEquals(250, button.getWidth());
+    assertEquals(250, field1.getWidth());
+  }
+
+  /**
+   * button(can grow) | text field (want grow, span 2)
+   */
+  public void testSpans3() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JButton button = new JButton();
+    button.setPreferredSize(new Dimension(50, 10));
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(110, 10));
+
+    GridBagConverter converter = new GridBagConverter();
+    converter.addComponent(button, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(field1, new GridConstraints(0,1,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    applyConversionResults(panel, converter);
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(160, preferredSize.width);
+
+    panel.setSize(new Dimension(500, 100));
+    panel.doLayout();
+
+    assertEquals(50, button.getWidth());
+    assertEquals(450, field1.getWidth());
+  }
+
+  /**
+   * button (can grow, span 2 )       | text field 1 (span 1)
+   * text field 2 (want grow, span 2) | empty
+   */
+  public void testSpans4() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JButton button = new JButton();
+    button.setPreferredSize(new Dimension(50, 10));
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(110, 10));
+
+    final JTextField field2 = new JTextField();
+    field2.setPreferredSize(new Dimension(110, 10));
+
+    GridBagConverter converter = new GridBagConverter();
+    converter.addComponent(button, new GridConstraints(0,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(field1, new GridConstraints(0,2,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(field2, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    applyConversionResults(panel, converter);
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(220, preferredSize.width);
+
+    panel.setSize(new Dimension(500, 100));
+    panel.doLayout();
+
+    assertEquals(250, button.getWidth());
+    assertEquals(250, field1.getWidth());
+    assertEquals(250, field2.getWidth());
+  }
+
+  /**
+   * label   |    label
+   * text area (span 2)
+   */
+  public void testTextAreas1() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JLabel label1 = new JLabel();
+    label1.setPreferredSize(new Dimension(15,20));
+    final JLabel label2 = new JLabel();
+    label2.setPreferredSize(new Dimension(15,20));
+    final JTextArea textArea = new JTextArea();
+    textArea.setLineWrap(true);
+
+    GridBagConverter converter = new GridBagConverter();
+    converter.addComponent(label1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(label2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(textArea, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+      GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0));
+
+    applyConversionResults(panel, converter);
+
+    assertEquals(100, textArea.getPreferredSize().width);
+
+    final Dimension initialPreferredSize = panel.getPreferredSize();
+    assertEquals(new Dimension(100,20 + textArea.getPreferredSize().height), initialPreferredSize);
+
+    panel.setSize(initialPreferredSize);
+    panel.invalidate();
+    panel.doLayout();
+
+    assertEquals(initialPreferredSize, panel.getPreferredSize());
+  }
+
+  /**
+   * textfield1 | textfield2
+   *  textfield3 (span 2)
+   *
+   * important: hspan should be greater than 0
+   */
+  public void testTextAreas2() {
+    final JPanel panel = new JPanel(/*new GridLayoutManager(2,2, new Insets(0,0,0,0), 11, 0)*/ new GridBagLayout());
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(15,20));
+    final JTextField field2 = new JTextField();
+    field2.setPreferredSize(new Dimension(15,20));
+    final JTextField field3 = new JTextField();
+    field3.setPreferredSize(new Dimension(100,20));
+
+    GridBagConverter converter = new GridBagConverter();
+    converter.addComponent(field1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(field2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(field3, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    applyConversionResults(panel, converter);
+
+    assertEquals(100, panel.getPreferredSize().width);
+  }
+
+  public void testGaps1() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(100,20));
+    final JTextField field2 = new JTextField();
+    field2.setPreferredSize(new Dimension(100,20));
+
+    GridBagConverter converter = new GridBagConverter(new Insets(0, 0, 0, 0), 10, 0, false, false);
+    converter.addComponent(field1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(field2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    applyConversionResults(panel, converter);
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(210, preferredSize.width);
+  }
+
+  /**
+   * field (span 2) | field (span 1)
+   */
+  public void testGaps2() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(100,20));
+    final JTextField field2 = new JTextField();
+    field2.setPreferredSize(new Dimension(100,20));
+
+    GridBagConverter converter = new GridBagConverter(new Insets(0, 0, 0, 0), 7, 0, false, false);
+    converter.addComponent(field1, new GridConstraints(0,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(field2, new GridConstraints(0,2,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    applyConversionResults(panel, converter);
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(207, preferredSize.width);
+
+    panel.setSize(panel.getPreferredSize());
+    panel.doLayout();
+
+    assertEquals(new Rectangle(0,0,100,20), field1.getBounds());
+    assertEquals(new Rectangle(107,0,100,20), field2.getBounds());
+  }
+
+  /**
+   *
+   * btn1   |    btn2  | btn4
+   *  btn3 (span 2)    |
+   */
+  public void testGaps3() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JButton btn1 = new JButton();
+    btn1.setPreferredSize(new Dimension(100,20));
+    final JButton btn2 = new JButton();
+    btn2.setPreferredSize(new Dimension(100,20));
+    final JButton btn3 = new JButton();
+    btn3.setPreferredSize(new Dimension(100,20));
+    final JButton btn4 = new JButton();
+    btn4.setPreferredSize(new Dimension(100,20));
+
+    GridBagConverter converter = new GridBagConverter(new Insets(0, 0, 0, 0), 7, 0, false, false);
+    converter.addComponent(btn1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(btn2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(btn3, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(btn4, new GridConstraints(0,2,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    applyConversionResults(panel, converter);
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(314, preferredSize.width);
+
+    panel.setSize(panel.getPreferredSize());
+    panel.doLayout();
+  }
+
+  /**
+   *
+   * btn1   |    btn2  | btn4
+   *  btn3 (span 2)    |
+   */
+  public void testGaps3a() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JButton btn1 = new JButton();
+    btn1.setPreferredSize(new Dimension(100,20));
+    final JButton btn2 = new JButton();
+    btn2.setPreferredSize(new Dimension(100,20));
+    final JButton btn3 = new JButton();
+    btn3.setPreferredSize(new Dimension(100,20));
+    final JButton btn4 = new JButton();
+    btn4.setPreferredSize(new Dimension(100,20));
+
+    GridBagConverter converter = new GridBagConverter(new Insets(0, 0, 0, 0), 1000, 0, false, false);
+    converter.addComponent(btn1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(btn2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(btn3, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(btn4, new GridConstraints(0,2,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    applyConversionResults(panel, converter);
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(2300, preferredSize.width);
+
+    panel.setSize(panel.getPreferredSize());
+    panel.doLayout();
+  }
+
+  /**
+   *
+   * btn1   |    btn2
+   *  btn3 (span 2)
+   */
+  public void testGaps3b() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JButton btn1 = new JButton();
+    btn1.setPreferredSize(new Dimension(100,20));
+    final JButton btn2 = new JButton();
+    btn2.setPreferredSize(new Dimension(100,20));
+    final JButton btn3 = new JButton();
+    btn3.setPreferredSize(new Dimension(100,20));
+
+    GridBagConverter converter = new GridBagConverter(new Insets(0, 0, 0, 0), 1000, 0, false, false);
+    converter.addComponent(btn1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(btn2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(btn3, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    applyConversionResults(panel, converter);
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(1200, preferredSize.width);
+
+    panel.setSize(panel.getPreferredSize());
+    panel.doLayout();
+  }
+
+  /**
+   * btn1
+   * -----
+   * empty
+   * ----
+   * btn2
+   */
+  public void testGaps4() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JButton btn1 = new JButton();
+    btn1.setPreferredSize(new Dimension(100,20));
+    final JButton btn2 = new JButton();
+    btn2.setPreferredSize(new Dimension(100,20));
+
+    GridBagConverter converter = new GridBagConverter(new Insets(0, 0, 0, 0), 0, 7, false, false);
+    converter.addComponent(btn1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(btn2, new GridConstraints(2,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    applyConversionResults(panel, converter);
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(47, preferredSize.height);
+
+    panel.setSize(panel.getPreferredSize());
+    panel.doLayout();
+  }
+
+  // skipped GapsTest.test5 because its only difference from test4 is spacer usage
+
+  public void testGaps6() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JButton btn1 = new JButton();
+    btn1.setPreferredSize(new Dimension(100,20));
+    final JButton btn2 = new JButton();
+    btn2.setPreferredSize(new Dimension(100,20));
+
+    GridBagConverter converter = new GridBagConverter(new Insets(0, 0, 0, 0), 0, 500, false, false);
+    converter.addComponent(btn1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(btn2, new GridConstraints(1,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    applyConversionResults(panel, converter);
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(540, preferredSize.height);
+
+    panel.setSize(panel.getPreferredSize());
+    panel.doLayout(); // should not crash
+  }
+
+  public void testEqualSizeCells1() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JTextField field1 = new JTextField();
+    field1.setMinimumSize(new Dimension(5,20));
+    field1.setPreferredSize(new Dimension(10,20));
+
+    final JTextField field2 = new JTextField();
+    field2.setMinimumSize(new Dimension(25,20));
+    field2.setPreferredSize(new Dimension(50,20));
+
+    final JTextField field3 = new JTextField();
+    field3.setMinimumSize(new Dimension(70,20));
+    field3.setPreferredSize(new Dimension(100,20));
+
+    GridBagConverter converter = new GridBagConverter(new Insets(0, 0, 0, 0), 7, 0, true, false);
+    converter.addComponent(field1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(field2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    converter.addComponent(field3, new GridConstraints(0,2,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    applyConversionResults(panel, converter);
+
+    final Dimension minimumSize = panel.getMinimumSize();
+    assertEquals(70 + 7 + 70 + 7 + 70, minimumSize.width);
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(100 + 7 + 100 + 7 + 100, preferredSize.width);
+
+    //
+    panel.setSize(panel.getPreferredSize());
+    panel.doLayout();
+
+    assertEquals(100, field1.getWidth());
+    assertEquals(100, field2.getWidth());
+    assertEquals(100, field3.getWidth());
+
+    //
+    panel.setSize(new Dimension(1000, 1000));
+    panel.doLayout();
+
+    assertEquals(329, field1.getWidth(), 1.0);
+    assertEquals(329, field2.getWidth(), 1.0);
+    assertEquals(328, field3.getWidth(), 1.0);
+  }
+
+  public void testEqualSizeCells2() {
+    final JPanel panel = new JPanel(new GridBagLayout());
+
+    final JTextField field1 = new JTextField();
+    field1.setMinimumSize(new Dimension(20, 5));
+    field1.setPreferredSize(new Dimension(20, 10));
+
+    final JTextField field2 = new JTextField();
+    field2.setMinimumSize(new Dimension(20, 25));
+    field2.setPreferredSize(new Dimension(20, 50));
+
+    final JTextField field3 = new JTextField();
+    field3.setMinimumSize(new Dimension(20, 70));
+    field3.setPreferredSize(new Dimension(20, 100));
+
+    GridBagConverter converter = new GridBagConverter(new Insets(0, 0, 0, 0), 0, 7, false, true);
+    converter.addComponent(field1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_WANT_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, null, null, null, 0));
+
+    converter.addComponent(field2, new GridConstraints(1,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_WANT_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, null, null, null, 0));
+
+    converter.addComponent(field3, new GridConstraints(2,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_WANT_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, null, null, null, 0));
+
+    applyConversionResults(panel, converter);
+
+    final Dimension minimumSize = panel.getMinimumSize();
+    assertEquals(70 + 7 + 70 + 7 + 70, minimumSize.height);
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(100 + 7 + 100 + 7 + 100, preferredSize.height);
+
+    panel.setSize(panel.getPreferredSize());
+    panel.doLayout();
+
+    assertEquals(100, field1.getHeight());
+    assertEquals(100, field2.getHeight());
+    assertEquals(100, field3.getHeight());
+  }
+
+  private static void applyConversionResults(final JPanel panel, final GridBagConverter converter) {
+    GridBagConverter.Result[] results = converter.convert();
+    for(int i=0; i<results.length; i++)  {
+      GridBagConverter.Result result = results [i];
+      JComponent component = result.isFillerPanel ? new JPanel() : result.component;
+      if (result.minimumSize != null) {
+        component.setMinimumSize(result.minimumSize);
+      }
+      if (result.preferredSize != null) {
+        component.setPreferredSize(result.preferredSize);
+      }
+      panel.add(component, result.constraints);
+    }
+  }
+}
diff --git a/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/IndentTest.java b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/IndentTest.java
new file mode 100644
index 0000000..076539d
--- /dev/null
+++ b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/IndentTest.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 com.intellij.uiDesigner.core;
+
+import junit.framework.TestCase;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * @author yole
+ */
+public class IndentTest extends TestCase {
+  public void testSimple() {
+    final GridLayoutManager layout = new GridLayoutManager(1,1, new Insets(0,0,0,0), 0, 0);
+    final JPanel panel = new JPanel(layout);
+
+    final JTextField field1 = new JTextField();
+    field1.setMinimumSize(new Dimension(5,20));
+    field1.setPreferredSize(new Dimension(10,20));
+
+    panel.add(field1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 1));
+    assertEquals(15, panel.getMinimumSize().width);
+    assertEquals(20, panel.getPreferredSize().width);
+
+    panel.setSize(new Dimension(100, 100));
+    panel.doLayout();
+
+    assertEquals(10, field1.getBounds().x);
+    assertEquals(90, field1.getBounds().width);
+  }
+}
diff --git a/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout2Test.java b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout2Test.java
new file mode 100644
index 0000000..c39845b
--- /dev/null
+++ b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout2Test.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 com.intellij.uiDesigner.core;
+
+import junit.framework.TestCase;
+
+import javax.swing.*;
+import java.awt.*;
+
+public final class Layout2Test extends TestCase{
+  /**
+   * button 1
+   * <empty>
+   * button 2 
+   */ 
+  public void test1() {
+    final GridLayoutManager layoutManager = new GridLayoutManager(3,1, new Insets(0,0,0,0), 0, 0);
+    final JPanel panel = new JPanel(layoutManager);
+
+    final JButton button1 = new JButton();
+    button1.setMinimumSize(new Dimension(9, 7));
+    button1.setPreferredSize(new Dimension(50, 10));
+
+    final JButton button2 = new JButton();
+    button2.setMinimumSize(new Dimension(15, 6));
+    button2.setPreferredSize(new Dimension(50, 10));
+    
+    panel.add(button1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK, null, null, null, 0));
+
+    panel.add(button2, new GridConstraints(2,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    assertEquals(20, panel.getPreferredSize().height);
+    assertEquals(50, panel.getPreferredSize().width);
+    
+    assertEquals(17, panel.getMinimumSize().height);
+    assertEquals(50, panel.getMinimumSize().width);
+    
+    panel.setSize(new Dimension(500, 100));
+    panel.doLayout();
+
+    assertEquals(50, button1.getHeight());
+    assertEquals(50, button2.getHeight());
+  }
+  
+}
diff --git a/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout3Test.java b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout3Test.java
new file mode 100644
index 0000000..60c335b
--- /dev/null
+++ b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout3Test.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 com.intellij.uiDesigner.core;
+
+import junit.framework.TestCase;
+
+import javax.swing.*;
+import java.awt.*;
+
+public final class Layout3Test extends TestCase{
+  public void test1() {
+    final JPanel panel = new JPanel(new GridLayoutManager(2,1, new Insets(0,0,0,0), 0, 0));
+
+    final JButton button1 = new JButton();
+    button1.setPreferredSize(new Dimension(100,20));
+    final JButton button2 = new JButton();
+    button2.setPreferredSize(new Dimension(100,100));
+
+    panel.add(button1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(button2, new GridConstraints(1,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(SystemInfo.isMac ? 129 : 120, preferredSize.height);
+  }
+}
diff --git a/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout4Test.java b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout4Test.java
new file mode 100644
index 0000000..7e2f477
--- /dev/null
+++ b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout4Test.java
@@ -0,0 +1,54 @@
+/*
+ * 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 com.intellij.uiDesigner.core;
+
+import junit.framework.TestCase;
+
+import javax.swing.*;
+import java.awt.*;
+
+public final class Layout4Test extends TestCase{
+  
+  
+  public void test1() {
+    final JPanel panel = new JPanel(new GridLayoutManager(2,2, new Insets(0,0,0,0), 0, 0));
+
+    // button 1  button 3
+    // button 2  button 3
+    
+    final JButton button1 = new JButton();
+    button1.setPreferredSize(new Dimension(100,10));
+    final JButton button2 = new JButton();
+    button2.setPreferredSize(new Dimension(100,10));
+    final JButton button3 = new JButton();
+    button3.setPreferredSize(new Dimension(100,200));
+
+    panel.add(button1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW + GridConstraints.SIZEPOLICY_CAN_SHRINK, null, null, null,
+      0));
+
+    panel.add(button2, new GridConstraints(1,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW + GridConstraints.SIZEPOLICY_CAN_SHRINK, null, null, null,
+      0));
+
+    panel.add(button3, new GridConstraints(0,1,2,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW + GridConstraints.SIZEPOLICY_CAN_SHRINK, null, null, null,
+      0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(200, preferredSize.height);
+  }
+}
diff --git a/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout5Test.java b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout5Test.java
new file mode 100644
index 0000000..f3dfc56
--- /dev/null
+++ b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout5Test.java
@@ -0,0 +1,142 @@
+/*
+ * 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 com.intellij.uiDesigner.core;
+
+import junit.framework.TestCase;
+
+import javax.swing.*;
+import java.awt.*;
+
+public final class Layout5Test extends TestCase{
+  
+  
+  public void test1() {
+    final JPanel panel = new JPanel(new GridLayoutManager(2,3, new Insets(0,0,0,0), 0, 0));
+
+    // label textfield(span 2)
+    // textfield(span 2)
+    
+    final JTextField label = new JTextField();
+    label.setPreferredSize(new Dimension(10,30));
+    
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(100,30));
+
+    final JTextField field2 = new JTextField();
+    field2.setPreferredSize(new Dimension(100,30));
+    
+    panel.add(label, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null, 0));
+
+    panel.add(field1, new GridConstraints(0,1,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null, 0));
+
+    panel.add(field2, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(110, preferredSize.width);
+    assertEquals(60, preferredSize.height);
+  }
+
+
+  public void test2() {
+    final GridLayoutManager layoutManager = new GridLayoutManager(2,3, new Insets(0,0,0,0), 0, 0);
+    final JPanel panel = new JPanel(layoutManager);
+
+    // empty textfield(span 2)
+    // textfield(span 2) empty
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(100,30));
+
+    final JTextField field2 = new JTextField();
+    field2.setPreferredSize(new Dimension(100,30));
+
+    panel.add(field1, new GridConstraints(0,1,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null, 0));
+
+    panel.add(field2, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+
+    // after getPreferredSize() invocation, the field should be not null
+    final DimensionInfo horizontalInfo = layoutManager.myHorizontalInfo;
+    assertEquals(3, horizontalInfo.getCellCount());
+    assertEquals(GridConstraints.SIZEPOLICY_CAN_SHRINK, horizontalInfo.getCellSizePolicy(0));
+    assertEquals(GridConstraints.SIZEPOLICY_WANT_GROW, horizontalInfo.getCellSizePolicy(1));
+    assertEquals(GridConstraints.SIZEPOLICY_CAN_SHRINK, horizontalInfo.getCellSizePolicy(2));
+
+    assertEquals(100, preferredSize.width);
+    assertEquals(60, preferredSize.height);
+
+    panel.setSize(400, 100);
+    panel.doLayout();
+    assertEquals(400, field1.getWidth());
+    assertEquals(400, field2.getWidth());
+  }
+
+  public void test3() {
+    final GridLayoutManager layoutManager = new GridLayoutManager(2,3, new Insets(0,0,0,0), 0, 0);
+    final JPanel panel = new JPanel(layoutManager);
+
+    // label textfield(span 2)
+    // textfield(span 2) label
+
+    final JTextField label1 = new JTextField();
+    label1.setPreferredSize(new Dimension(10,30));
+
+    final JTextField label2 = new JTextField();
+    label2.setPreferredSize(new Dimension(10,30));
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(100,30));
+
+    final JTextField field2 = new JTextField();
+    field2.setPreferredSize(new Dimension(100,30));
+
+    panel.add(field1, new GridConstraints(0,1,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null, 0));
+
+    panel.add(field2, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null, 0));
+
+    panel.add(label1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null, 0));
+
+    panel.add(label2, new GridConstraints(1,2,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+
+    // after getPreferredSize() invocation, the field should be not null 
+    final DimensionInfo horizontalInfo = layoutManager.myHorizontalInfo;
+    assertEquals(GridConstraints.SIZEPOLICY_FIXED, horizontalInfo.getCellSizePolicy(0));
+    assertEquals(GridConstraints.SIZEPOLICY_WANT_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, horizontalInfo.getCellSizePolicy(1));
+    assertEquals(GridConstraints.SIZEPOLICY_FIXED, horizontalInfo.getCellSizePolicy(2));
+    
+    assertEquals(110, preferredSize.width);
+    assertEquals(60, preferredSize.height);
+    
+    panel.setSize(400, 100);
+    panel.doLayout();
+    assertEquals(390, field1.getWidth());
+    assertEquals(390, field2.getWidth());
+    assertEquals(10, label1.getWidth());
+    assertEquals(10, label2.getWidth());
+  }
+}
diff --git a/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout6Test.java b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout6Test.java
new file mode 100644
index 0000000..493af8e
--- /dev/null
+++ b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout6Test.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 com.intellij.uiDesigner.core;
+
+import junit.framework.TestCase;
+
+import javax.swing.*;
+import java.awt.*;
+
+public final class Layout6Test extends TestCase{
+  
+
+  /**
+   * control(min size 10) control(same) control(same)
+   */ 
+  public void test1() {
+    final GridLayoutManager layoutManager = new GridLayoutManager(2,3, new Insets(0,0,0,0), 0, 0);
+    final JPanel panel = new JPanel(layoutManager);
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(10,30));
+    
+    final JTextField field2 = new JTextField();
+    field2.setPreferredSize(new Dimension(10,30));
+
+    final JTextField field3 = new JTextField();
+    field3.setPreferredSize(new Dimension(10,30));
+    
+    panel.add(field1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null, 0));
+
+    panel.add(field2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null, 0));
+
+    panel.add(field3, new GridConstraints(0,2,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(30, preferredSize.width);
+    
+    for (int size = 31; size < 100; size++) {
+      panel.setSize(size, 30);
+      layoutManager.invalidateLayout(panel); // wisdom
+      panel.doLayout();
+    }
+  }
+
+}
diff --git a/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout7Test.java b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout7Test.java
new file mode 100644
index 0000000..5970a7e
--- /dev/null
+++ b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout7Test.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 com.intellij.uiDesigner.core;
+
+import junit.framework.TestCase;
+
+import javax.swing.*;
+import java.awt.*;
+
+public final class Layout7Test extends TestCase{
+  /**
+   * label | text field
+   *    scroll pane
+   */
+  public void test1() {
+    final GridLayoutManager layoutManager = new GridLayoutManager(2,2, new Insets(0,0,0,0), 0, 0);
+    final JPanel panel = new JPanel(layoutManager);
+
+    final JLabel label = new JLabel();
+    label.setPreferredSize(new Dimension(50,10));
+    
+    final JTextField field = new JTextField();
+    field.setPreferredSize(new Dimension(100,10));
+
+    final JTextField scroll = new JTextField();
+    scroll.setPreferredSize(new Dimension(503, 10));
+
+    panel.add(label, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null, 0));
+
+    panel.add(field, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null, 0));
+
+    panel.add(scroll, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, new Dimension(0,0), null, null, 0));
+
+    assertEquals(503, panel.getMinimumSize().width);
+    assertEquals(503, panel.getPreferredSize().width);
+
+    panel.setSize(503, 100);
+    panel.doLayout();
+
+    assertEquals(50, label.getWidth());
+    assertEquals(453, field.getWidth());
+  }
+
+}
diff --git a/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout8Test.java b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout8Test.java
new file mode 100644
index 0000000..ef51c40
--- /dev/null
+++ b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/Layout8Test.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.
+ */
+package com.intellij.uiDesigner.core;
+
+import junit.framework.TestCase;
+
+import javax.swing.*;
+import java.awt.*;
+
+public final class Layout8Test extends TestCase{
+  /**
+   * label 1 (pref=100, min=10, can shrink, can grow) | label 2 (pref=100, min=10, can shrink)
+   *                                            (can grow, want grow)
+   */
+  public void test1() {
+    final GridLayoutManager layoutManager = new GridLayoutManager(2,2, new Insets(0,0,0,0), 0, 0);
+    final JPanel panel = new JPanel(layoutManager);
+
+    final JLabel label1 = new JLabel();
+    label1.setMinimumSize(new Dimension(10,10));
+    label1.setPreferredSize(new Dimension(100,10));
+
+    final JLabel label2 = new JLabel();
+    label2.setMinimumSize(new Dimension(10,10));
+    label2.setPreferredSize(new Dimension(100,10));
+
+    panel.add(label1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_SHRINK + GridConstraints.SIZEPOLICY_CAN_GROW,
+      GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(label2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_SHRINK,
+      GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(new JLabel(), new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_SHRINK + GridConstraints.SIZEPOLICY_CAN_GROW + GridConstraints.SIZEPOLICY_WANT_GROW,
+      GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0));
+
+    assertEquals(20, panel.getMinimumSize().width);
+    assertEquals(200, panel.getPreferredSize().width);
+
+    // minimum
+    panel.setSize(20, 100);
+    panel.doLayout();
+    assertEquals(10, label1.getWidth());
+    assertEquals(10, label2.getWidth());
+
+    // between min and pref
+    panel.setSize(76, 100);
+    panel.doLayout();
+    assertEquals(38, label1.getWidth());
+    assertEquals(38, label2.getWidth());
+
+    // pref-1
+    panel.setSize(199, 100);
+    panel.doLayout();
+    assertEquals(100, label1.getWidth());
+    assertEquals(99, label2.getWidth());
+
+    // pref
+    panel.setSize(200, 100);
+    panel.doLayout();
+    assertEquals(100, label1.getWidth());
+    assertEquals(100, label2.getWidth());
+
+    // pref+1
+    panel.setSize(201, 100);
+    panel.doLayout();
+    assertEquals(101, label1.getWidth());
+    assertEquals(100, label2.getWidth());
+
+    // pref + few
+    panel.setSize(205, 100);
+    panel.doLayout();
+    assertEquals(105, label1.getWidth());
+    assertEquals(100, label2.getWidth());
+  }
+
+}
diff --git a/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/PrefSizeTest.java b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/PrefSizeTest.java
new file mode 100644
index 0000000..f967839
--- /dev/null
+++ b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/PrefSizeTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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 com.intellij.uiDesigner.core;
+
+import junit.framework.TestCase;
+
+import javax.swing.*;
+import java.awt.*;
+
+public final class PrefSizeTest extends TestCase{
+  /**
+   * control(min size 110, pref size 120) control(min size 215, pref size 225)
+   */ 
+  public void test1() {
+    final GridLayoutManager layoutManager = new GridLayoutManager(1,2, new Insets(0,0,0,0), 0, 0);
+    final JPanel panel = new JPanel(layoutManager);
+
+    final JTextField field1 = new JTextField();
+    field1.setMinimumSize(new Dimension(110,10));
+    field1.setPreferredSize(new Dimension(120,10));
+    
+    final JTextField field2 = new JTextField();
+    field2.setMinimumSize(new Dimension(215,10));
+    field2.setPreferredSize(new Dimension(225,10));
+
+    panel.add(field1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(field2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(345, preferredSize.width);
+
+    final Dimension minSize = panel.getMinimumSize();
+    assertEquals(325, minSize.width);
+
+    panel.setSize(preferredSize.width, preferredSize.height);
+    panel.doLayout();
+
+    assertEquals(120, field1.getWidth());
+    assertEquals(225, field2.getWidth());
+
+    panel.setSize(400, panel.getWidth());
+    panel.invalidate(); // to invalidate layout
+    panel.doLayout();
+
+  }
+
+  public void test2() {
+    final GridLayoutManager layoutManager = new GridLayoutManager(1,3); // min cell size should
+    layoutManager.setHGap(0);
+    layoutManager.setVGap(0);
+    final JPanel panel = new JPanel(layoutManager);
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(100,10));
+
+    final JTextField field2 = new JTextField();
+    field2.setPreferredSize(new Dimension(200,10));
+
+    panel.add(field1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(field2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    assertEquals(300 + 20, preferredSize.width);
+
+    panel.setSize(preferredSize.width, preferredSize.height);
+    panel.doLayout();
+    
+    assertEquals(100, field1.getWidth());
+    assertEquals(200, field2.getWidth());
+
+    panel.setSize(270, preferredSize.height);
+    panel.doLayout();   // should not fail
+  }
+  
+}
diff --git a/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/SpansTest.java b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/SpansTest.java
new file mode 100644
index 0000000..4d9bfd0
--- /dev/null
+++ b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/SpansTest.java
@@ -0,0 +1,196 @@
+/*
+ * 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 com.intellij.uiDesigner.core;
+
+import junit.framework.TestCase;
+
+import javax.swing.*;
+import java.awt.*;
+
+public final class SpansTest extends TestCase{
+  
+  /**
+   * button(can grow) | text field (want grow)
+   *   text field (want grow, span 2) 
+   */ 
+  public void test1() {
+    final GridLayoutManager layout = new GridLayoutManager(2,2, new Insets(0,0,0,0), 0, 0);
+    final JPanel panel = new JPanel(layout);
+
+    final JButton button = new JButton();
+    button.setPreferredSize(new Dimension(50, 10));
+    
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(50, 10));
+
+    final JTextField field2 = new JTextField();
+
+    panel.add(button, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(field1, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(field2, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    if (SystemInfo.isMac) {
+      assertEquals(125, preferredSize.width);
+    } else {
+      assertEquals(100, preferredSize.width);
+    }
+
+    panel.setSize(new Dimension(500, panel.getHeight()));
+    panel.doLayout();
+
+    assertEquals(500, field2.getWidth());
+    if (SystemInfo.isMac) {
+      assertEquals(75, button.getWidth());
+      assertEquals(425, field1.getWidth());
+    } else {
+      assertEquals(50, button.getWidth());
+      assertEquals(450, field1.getWidth());
+    }
+  }
+
+
+  /**
+   * button(can grow) | text field (can grow)
+   *   text field (want grow, span 2)
+   */
+  public void test2() {
+    final JPanel panel = new JPanel(new GridLayoutManager(2,2, new Insets(0,0,0,0), 0, 0));
+
+    final JButton button = new JButton();
+    button.setPreferredSize(new Dimension(50, 10));
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(50, 10));
+
+    final JTextField field2 = new JTextField();
+
+    panel.add(button, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(field1, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(field2, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    if (SystemInfo.isMac) {
+      assertEquals(125, preferredSize.width);
+    } else {
+      assertEquals(100, preferredSize.width);
+    }
+
+    panel.setSize(new Dimension(500, panel.getHeight()));
+    panel.doLayout();
+
+    assertEquals(500, field2.getWidth());
+    if (SystemInfo.isMac) {
+      assertEquals(263, button.getWidth());
+      assertEquals(237, field1.getWidth());
+    } else {
+      assertEquals(250, button.getWidth());
+      assertEquals(250, field1.getWidth());
+    }
+  }
+
+  /**
+   * button(can grow) | text field (want grow, span 2)
+   */
+  public void test3() {
+    final JPanel panel = new JPanel(new GridLayoutManager(1,3, new Insets(0,0,0,0), 0, 0));
+
+    final JButton button = new JButton();
+    button.setPreferredSize(new Dimension(50, 10));
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(110, 10));
+
+    panel.add(button, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(field1, new GridConstraints(0,1,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+    if (SystemInfo.isMac) {
+      assertEquals(185, preferredSize.width);
+    } else {
+      assertEquals(160, preferredSize.width);
+    }
+
+    panel.setSize(new Dimension(500, panel.getHeight()));
+    panel.doLayout();
+
+    if (SystemInfo.isMac) {
+      assertEquals(75, button.getWidth());
+      assertEquals(425, field1.getWidth());
+    } else {
+      assertEquals(50, button.getWidth());
+      assertEquals(450, field1.getWidth());
+    }
+  }
+
+  /**
+   * button (can grow, span 2 )       | text field 1 (span 1)
+   * text field 2 (want grow, span 2) | empty
+   */
+  public void test4() {
+    final GridLayoutManager layoutManager = new GridLayoutManager(2,3, new Insets(0,0,0,0), 0, 0);
+    final JPanel panel = new JPanel(layoutManager);
+
+    final JButton button = new JButton();
+    button.setPreferredSize(new Dimension(50, 10));
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(110, 10));
+
+    final JTextField field2 = new JTextField();
+    field2.setPreferredSize(new Dimension(110, 10));
+
+    panel.add(button, new GridConstraints(0,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(field1, new GridConstraints(0,2,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(field2, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    final Dimension preferredSize = panel.getPreferredSize();
+
+    // field will be not null after getPreferredSize()
+    final DimensionInfo horizontalInfo = layoutManager.myHorizontalInfo;
+    assertEquals(GridConstraints.SIZEPOLICY_WANT_GROW | GridConstraints.SIZEPOLICY_CAN_GROW, horizontalInfo.getCellSizePolicy(0));
+    assertEquals(GridConstraints.SIZEPOLICY_CAN_SHRINK, horizontalInfo.getCellSizePolicy(1));
+    assertEquals(GridConstraints.SIZEPOLICY_WANT_GROW, horizontalInfo.getCellSizePolicy(2));
+
+    assertEquals(220, preferredSize.width);
+    
+    panel.setSize(new Dimension(500, panel.getHeight()));
+    panel.doLayout();
+
+    assertEquals(250, button.getWidth());
+    assertEquals(250, field1.getWidth());
+    assertEquals(250, field2.getWidth());
+  }
+  
+}
diff --git a/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/SystemInfo.java b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/SystemInfo.java
new file mode 100644
index 0000000..687a1d4
--- /dev/null
+++ b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/SystemInfo.java
@@ -0,0 +1,23 @@
+/*
+ * 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 com.intellij.uiDesigner.core;
+
+/**
+ * @author Alexander Lobas
+ */
+public class SystemInfo {
+  public static final boolean isMac = System.getProperty("os.name").toLowerCase().startsWith("mac");
+}
\ No newline at end of file
diff --git a/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/TextAreasTest.java b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/TextAreasTest.java
new file mode 100644
index 0000000..7fa442f
--- /dev/null
+++ b/java/compiler/forms-compiler/testSrc/com/intellij/uiDesigner/core/TextAreasTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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 com.intellij.uiDesigner.core;
+
+import junit.framework.TestCase;
+
+import javax.swing.*;
+import java.awt.*;
+
+public final class TextAreasTest extends TestCase{
+  /**
+   * label   |    label
+   * text area (span 2)
+   */ 
+  
+  public void test1() {
+    final JPanel panel = new JPanel(new GridLayoutManager(2,2, new Insets(0,0,0,0), 0, 0));
+
+    final JLabel label1 = new JLabel();
+    label1.setPreferredSize(new Dimension(15,20));
+    final JLabel label2 = new JLabel();
+    label2.setPreferredSize(new Dimension(15,20));
+    final JTextArea textArea = new JTextArea();
+    textArea.setLineWrap(true);
+
+    panel.add(label1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(label2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(textArea, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+      GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0));
+
+    assertEquals(100, textArea.getPreferredSize().width);
+
+    final Dimension initialPreferredSize = panel.getPreferredSize();
+    assertEquals(new Dimension(100,20 + textArea.getPreferredSize().height), initialPreferredSize);
+
+    panel.setSize(initialPreferredSize);
+    panel.invalidate();
+    panel.doLayout();
+
+    assertEquals(initialPreferredSize, panel.getPreferredSize());
+  }
+
+  /**
+   * textfield1 | textfield2
+   *  textfield3 (span 2)
+   *
+   * important: hspan should be greater than 0
+   */
+  public void test2() {
+    final JPanel panel = new JPanel(new GridLayoutManager(2,2, new Insets(0,0,0,0), 11, 0));
+
+    final JTextField field1 = new JTextField();
+    field1.setPreferredSize(new Dimension(15,20));
+    final JTextField field2 = new JTextField();
+    field2.setPreferredSize(new Dimension(15,20));
+    final JTextField field3 = new JTextField();
+    field3.setPreferredSize(new Dimension(100,20));
+
+    panel.add(field1, new GridConstraints(0,0,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(field2, new GridConstraints(0,1,1,1,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_HORIZONTAL,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    panel.add(field3, new GridConstraints(1,0,1,2,GridConstraints.ANCHOR_CENTER,GridConstraints.FILL_BOTH,
+      GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0));
+
+    assertEquals(100, panel.getPreferredSize().width);
+  }
+  
+}
diff --git a/java/compiler/impl/compiler-impl.iml b/java/compiler/impl/compiler-impl.iml
new file mode 100644
index 0000000..9bb8c9a
--- /dev/null
+++ b/java/compiler/impl/compiler-impl.iml
@@ -0,0 +1,36 @@
+<?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$/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="compiler-openapi" exported="" />
+    <orderEntry type="library" name="OroMatcher" level="project" />
+    <orderEntry type="library" name="JDOM" level="project" />
+    <orderEntry type="module" module-name="util" />
+    <orderEntry type="module" module-name="openapi" />
+    <orderEntry type="library" name="Trove4j" level="project" />
+    <orderEntry type="module" module-name="java-runtime" />
+    <orderEntry type="module" module-name="instrumentation-util" />
+    <orderEntry type="library" name="asm4" level="project" />
+    <orderEntry type="library" name="Eclipse" level="project" />
+    <orderEntry type="module" module-name="platform-api" />
+    <orderEntry type="module" module-name="lang-impl" />
+    <orderEntry type="module" module-name="jsp-openapi" />
+    <orderEntry type="module" module-name="java-impl" />
+    <orderEntry type="module" module-name="jps-builders" />
+    <orderEntry type="library" name="Netty" level="project" />
+    <orderEntry type="module" module-name="testFramework-java" scope="TEST" />
+    <orderEntry type="module" module-name="jps-model-impl" />
+  </component>
+  <component name="copyright">
+    <Base>
+      <setting name="state" value="1" />
+    </Base>
+  </component>
+</module>
+
diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java b/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java
new file mode 100644
index 0000000..cbf1b68
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/CompilerConfigurationImpl.java
@@ -0,0 +1,1000 @@
+
+/*
+ * 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.
+ */
+
+/**
+ * created at Jan 3, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler;
+
+import com.intellij.CommonBundle;
+import com.intellij.ProjectTopics;
+import com.intellij.compiler.impl.TranslatingCompilerFilesMonitor;
+import com.intellij.compiler.impl.javaCompiler.BackendCompiler;
+import com.intellij.compiler.impl.javaCompiler.api.CompilerAPICompiler;
+import com.intellij.compiler.impl.javaCompiler.eclipse.EclipseCompiler;
+import com.intellij.compiler.impl.javaCompiler.eclipse.EclipseEmbeddedCompiler;
+import com.intellij.compiler.impl.javaCompiler.javac.JavacCompiler;
+import com.intellij.compiler.options.ExternalBuildOptionListener;
+import com.intellij.compiler.server.BuildManager;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.application.ex.ApplicationManagerEx;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.compiler.options.ExcludedEntriesConfiguration;
+import com.intellij.openapi.components.*;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.ModuleAdapter;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.ui.InputValidator;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.*;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.messages.MessageBusConnection;
+import org.apache.oro.text.regex.*;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.model.java.compiler.ProcessorConfigProfile;
+import org.jetbrains.jps.model.java.impl.compiler.ProcessorConfigProfileImpl;
+import org.jetbrains.jps.model.serialization.java.compiler.AnnotationProcessorProfileSerializer;
+import org.jetbrains.jps.model.serialization.java.compiler.JpsJavaCompilerConfigurationSerializer;
+
+import java.io.File;
+import java.util.*;
+
+@State(
+  name = "CompilerConfiguration",
+  storages = {
+    @Storage( file = StoragePathMacros.PROJECT_FILE),
+    @Storage( file = StoragePathMacros.PROJECT_CONFIG_DIR + "/compiler.xml", scheme = StorageScheme.DIRECTORY_BASED)
+  }
+)
+public class CompilerConfigurationImpl extends CompilerConfiguration implements PersistentStateComponent<Element>, ProjectComponent {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.CompilerConfiguration");
+  @NonNls public static final String TESTS_EXTERNAL_COMPILER_HOME_PROPERTY_NAME = "tests.external.compiler.home";
+  public static final int DEPENDENCY_FORMAT_VERSION = 55;
+
+  @SuppressWarnings({"WeakerAccess"}) public String DEFAULT_COMPILER;
+  @NotNull private BackendCompiler myDefaultJavaCompiler;
+
+  // extensions of the files considered as resource files
+  private final List<Pattern> myRegexpResourcePatterns = new ArrayList<Pattern>();
+  // extensions of the files considered as resource files. If present, overrides patterns in old regexp format stored in myRegexpResourcePatterns
+  private final List<String> myWildcardPatterns = new ArrayList<String>();
+  private final List<CompiledPattern> myCompiledPatterns = new ArrayList<CompiledPattern>();
+  private final List<CompiledPattern> myNegatedCompiledPatterns = new ArrayList<CompiledPattern>();
+  private boolean myWildcardPatternsInitialized = false;
+  private final Project myProject;
+  private final ExcludedEntriesConfiguration myExcludedEntriesConfiguration;
+
+  private final Collection<BackendCompiler> myRegisteredCompilers = new ArrayList<BackendCompiler>();
+  private JavacCompiler JAVAC_EXTERNAL_BACKEND;
+  private final Perl5Matcher myPatternMatcher = new Perl5Matcher();
+
+  {
+    loadDefaultWildcardPatterns();
+  }
+  private boolean myAddNotNullAssertions = true;
+
+  private final ProcessorConfigProfile myDefaultProcessorsProfile = new ProcessorConfigProfileImpl("Default");
+  private final List<ProcessorConfigProfile> myModuleProcessorProfiles = new ArrayList<ProcessorConfigProfile>();
+
+  // the map is calculated by module processor profiles list for faster access to module settings
+  private Map<Module, ProcessorConfigProfile> myProcessorsProfilesMap = null;
+
+  @Nullable
+  private String myBytecodeTargetLevel = null;  // null means compiler default
+  private final Map<String, String> myModuleBytecodeTarget = new java.util.HashMap<String, String>();
+
+  public CompilerConfigurationImpl(Project project) {
+    myProject = project;
+    myExcludedEntriesConfiguration = new ExcludedEntriesConfiguration();
+    Disposer.register(project, myExcludedEntriesConfiguration);
+    MessageBusConnection connection = project.getMessageBus().connect(project);
+    connection.subscribe(ProjectTopics.MODULES, new ModuleAdapter() {
+      public void beforeModuleRemoved(Project project, Module module) {
+        getAnnotationProcessingConfiguration(module).removeModuleName(module.getName());
+      }
+
+      public void moduleAdded(Project project, Module module) {
+        myProcessorsProfilesMap = null; // clear cache
+      }
+    });
+
+    connection.subscribe(ExternalBuildOptionListener.TOPIC, new ExternalBuildOptionListener() {
+      @Override
+      public void externalBuildOptionChanged(boolean externalBuildEnabled) {
+    // this will schedule for compilation all files that might become compilable after resource patterns' changing
+        final TranslatingCompilerFilesMonitor monitor = TranslatingCompilerFilesMonitor.getInstance();
+        if (externalBuildEnabled) {
+          monitor.suspendProject(myProject);
+        }
+        else {
+          monitor.watchProject(myProject);
+          monitor.scanSourcesForCompilableFiles(myProject);
+          if (!myProject.isDefault()) {
+            final File buildSystem = BuildManager.getInstance().getBuildSystemDirectory();
+            final File[] subdirs = buildSystem.listFiles();
+            if (subdirs != null) {
+              final String prefix = myProject.getName().toLowerCase(Locale.US) + "_";
+              for (File subdir : subdirs) {
+                if (subdir.getName().startsWith(prefix)) {
+                  FileUtil.asyncDelete(subdir);
+                }
+              }
+            }
+          }
+        }
+      }
+    });
+  }
+
+  public Element getState() {
+    try {
+      @NonNls final Element e = new Element("state");
+      writeExternal(e);
+      return e;
+    }
+    catch (WriteExternalException e1) {
+      LOG.error(e1);
+      return null;
+    }
+  }
+
+  public void loadState(Element state) {
+    try {
+      readExternal(state);
+    }
+    catch (InvalidDataException e) {
+      LOG.error(e);
+    }
+  }
+
+  public void setProjectBytecodeTarget(@Nullable String level) {
+    myBytecodeTargetLevel = level;
+  }
+
+  @Override
+  @Nullable
+  public String getProjectBytecodeTarget() {
+    return myBytecodeTargetLevel;
+  }
+
+  public void setModulesBytecodeTargetMap(@NotNull Map<String, String> mapping) {
+    myModuleBytecodeTarget.clear();
+    myModuleBytecodeTarget.putAll(mapping);
+  }
+
+  public Map<String, String> getModulesBytecodeTargetMap() {
+    return myModuleBytecodeTarget;
+  }
+
+  public void setBytecodeTargetLevel(Module module, String level) {
+    final String previous;
+    if (StringUtil.isEmpty(level)) {
+      previous = myModuleBytecodeTarget.remove(module.getName());
+    }
+    else {
+      previous = myModuleBytecodeTarget.put(module.getName(), level);
+    }
+    // todo: mark module as dirty in order to rebuild it completely with the new target level
+    //if (!Comparing.equal(previous, level)) {
+    //  final Project project = module.getProject();
+    //
+    //}
+  }
+
+  @Override
+  @Nullable
+  public String getBytecodeTargetLevel(Module module) {
+    final String level = myModuleBytecodeTarget.get(module.getName());
+    if (level != null) {
+      return level.isEmpty() ? null : level;
+    }
+    return myBytecodeTargetLevel;
+  }
+
+  private void loadDefaultWildcardPatterns() {
+    if (!myWildcardPatterns.isEmpty()) {
+      removeWildcardPatterns();
+    }
+    try {
+      addWildcardResourcePattern("!?*.java");
+      addWildcardResourcePattern("!?*.form");
+      addWildcardResourcePattern("!?*.class");
+      addWildcardResourcePattern("!?*.groovy");
+      addWildcardResourcePattern("!?*.scala");
+      addWildcardResourcePattern("!?*.flex");
+      addWildcardResourcePattern("!?*.kt");
+      addWildcardResourcePattern("!?*.clj");
+    }
+    catch (MalformedPatternException e) {
+      LOG.error(e);
+    }
+  }
+
+  public static String getTestsExternalCompilerHome() {
+    String compilerHome = System.getProperty(TESTS_EXTERNAL_COMPILER_HOME_PROPERTY_NAME, null);
+    if (compilerHome == null) {
+      if (SystemInfo.isMac) {
+        compilerHome = new File(System.getProperty("java.home")).getAbsolutePath();
+      }
+      else {
+        compilerHome = new File(System.getProperty("java.home")).getParentFile().getAbsolutePath();        
+      }
+    }
+    return compilerHome;
+  }
+
+  private static Pattern compilePattern(@NonNls String s) throws MalformedPatternException {
+    try {
+      final PatternCompiler compiler = new Perl5Compiler();
+      return SystemInfo.isFileSystemCaseSensitive? compiler.compile(s) : compiler.compile(s, Perl5Compiler.CASE_INSENSITIVE_MASK);
+    }
+    catch (org.apache.oro.text.regex.MalformedPatternException ex) {
+      throw new MalformedPatternException(ex);
+    }
+  }
+
+  public void disposeComponent() {
+  }
+
+  public void initComponent() { }
+
+  public void projectClosed() {
+  }
+
+  public JavacCompiler getJavacCompiler() {
+    createCompilers();
+    return JAVAC_EXTERNAL_BACKEND;
+  }
+
+  public void projectOpened() {
+    createCompilers();
+  }
+
+  private void createCompilers() {
+    if (JAVAC_EXTERNAL_BACKEND != null) {
+      return;
+    }
+
+    JAVAC_EXTERNAL_BACKEND = new JavacCompiler(myProject);
+    myRegisteredCompilers.add(JAVAC_EXTERNAL_BACKEND);
+
+    if (!ApplicationManager.getApplication().isUnitTestMode()) {
+      //final BackendCompiler JIKES_BACKEND = new JikesCompiler(myProject);
+      //myRegisteredCompilers.add(JIKES_BACKEND);
+
+      if (EclipseCompiler.isInitialized()) {
+        final EclipseCompiler eclipse = new EclipseCompiler(myProject);
+        myRegisteredCompilers.add(eclipse);
+      }
+
+      if (ApplicationManagerEx.getApplicationEx().isInternal()) {
+        try {
+          final EclipseEmbeddedCompiler eclipseEmbedded = new EclipseEmbeddedCompiler(myProject);
+          myRegisteredCompilers.add(eclipseEmbedded);
+        }
+        catch (NoClassDefFoundError e) {
+          // eclipse jar must be not in the classpath
+        }
+        try {
+          CompilerAPICompiler inProcessJavaCompiler = new CompilerAPICompiler(myProject);
+          myRegisteredCompilers.add(inProcessJavaCompiler);
+        }
+        catch (NoClassDefFoundError e) {
+          // wrong JDK
+        }
+      }
+    }
+
+    final BackendCompiler[] compilers = Extensions.getExtensions(BackendCompiler.EP_NAME, myProject);
+    final Set<FileType> types = new HashSet<FileType>();
+    for (BackendCompiler compiler : compilers) {
+      myRegisteredCompilers.add(compiler);
+      types.addAll(compiler.getCompilableFileTypes());
+    }
+
+    final CompilerManager compilerManager = CompilerManager.getInstance(myProject);
+    for (FileType type : types) {
+      compilerManager.addCompilableFileType(type);
+    }
+
+    myDefaultJavaCompiler = JAVAC_EXTERNAL_BACKEND;
+    for (BackendCompiler compiler : myRegisteredCompilers) {
+      if (compiler.getId().equals(DEFAULT_COMPILER)) {
+        myDefaultJavaCompiler = compiler;
+        break;
+      }
+    }
+    DEFAULT_COMPILER = myDefaultJavaCompiler.getId();
+  }
+
+  public Collection<BackendCompiler> getRegisteredJavaCompilers() {
+    createCompilers();
+    return myRegisteredCompilers;
+  }
+
+  public String[] getResourceFilePatterns() {
+    return getWildcardPatterns();
+  }
+
+  private String[] getRegexpPatterns() {
+    String[] patterns = ArrayUtil.newStringArray(myRegexpResourcePatterns.size());
+    int index = 0;
+    for (final Pattern myRegexpResourcePattern : myRegexpResourcePatterns) {
+      patterns[index++] = myRegexpResourcePattern.getPattern();
+    }
+    return patterns;
+  }
+
+  private String[] getWildcardPatterns() {
+    return ArrayUtil.toStringArray(myWildcardPatterns);
+  }
+
+  public void addResourceFilePattern(String namePattern) throws MalformedPatternException {
+    addWildcardResourcePattern(namePattern);
+  }
+
+  // need this method only for handling patterns in old regexp format
+  private void addRegexpPattern(String namePattern) throws MalformedPatternException {
+    Pattern pattern = compilePattern(namePattern);
+    if (pattern != null) {
+      myRegexpResourcePatterns.add(pattern);
+    }
+  }
+
+  @Override
+  public ExcludedEntriesConfiguration getExcludedEntriesConfiguration() {
+    return myExcludedEntriesConfiguration;
+  }
+
+  public boolean isExcludedFromCompilation(final VirtualFile virtualFile) {
+    return myExcludedEntriesConfiguration.isExcluded(virtualFile);
+  }
+
+  @Override
+  public boolean isResourceFile(VirtualFile virtualFile) {
+    return isResourceFile(virtualFile.getName(), virtualFile.getParent());
+  }
+
+  @Override
+  public boolean isAddNotNullAssertions() {
+    return myAddNotNullAssertions;
+  }
+
+  @Override
+  public void setAddNotNullAssertions(boolean enabled) {
+    myAddNotNullAssertions = enabled;
+  }
+
+  @NotNull
+  public ProcessorConfigProfile getDefaultProcessorProfile() {
+    return myDefaultProcessorsProfile;
+  }
+
+  public void setDefaultProcessorProfile(ProcessorConfigProfile profile) {
+    myDefaultProcessorsProfile.initFrom(profile);
+  }
+
+  @NotNull
+  public List<ProcessorConfigProfile> getModuleProcessorProfiles() {
+    return myModuleProcessorProfiles;
+  }
+
+  public void setModuleProcessorProfiles(Collection<ProcessorConfigProfile> moduleProfiles) {
+    myModuleProcessorProfiles.clear();
+    myModuleProcessorProfiles.addAll(moduleProfiles);
+    myProcessorsProfilesMap = null;
+  }
+
+  @Nullable
+  public ProcessorConfigProfile findModuleProcessorProfile(@NotNull String name) {
+    for (ProcessorConfigProfile profile : myModuleProcessorProfiles) {
+      if (name.equals(profile.getName())) {
+        return profile;
+      }
+    }
+
+    return null;
+  }
+
+  public void removeModuleProcessorProfile(ProcessorConfigProfile profile) {
+    myModuleProcessorProfiles.remove(profile);
+    myProcessorsProfilesMap = null; // clear cache
+  }
+
+  public void addModuleProcessorProfile(@NotNull ProcessorConfigProfile profile) {
+    myModuleProcessorProfiles.add(profile);
+    myProcessorsProfilesMap = null; // clear cache
+  }
+
+  @Override
+  @NotNull
+  public ProcessorConfigProfile getAnnotationProcessingConfiguration(Module module) {
+    Map<Module, ProcessorConfigProfile> map = myProcessorsProfilesMap;
+    if (map == null) {
+      map = new HashMap<Module, ProcessorConfigProfile>();
+      final Map<String, Module> namesMap = new HashMap<String, Module>();
+      for (Module m : ModuleManager.getInstance(module.getProject()).getModules()) {
+        namesMap.put(m.getName(), m);
+      }
+      if (!namesMap.isEmpty()) {
+        for (ProcessorConfigProfile profile : myModuleProcessorProfiles) {
+          for (String name : profile.getModuleNames()) {
+            final Module mod = namesMap.get(name);
+            if (mod != null) {
+              map.put(mod, profile);
+            }
+          }
+        }
+      }
+      myProcessorsProfilesMap = map;
+    }
+    final ProcessorConfigProfile profile = map.get(module);
+    return profile != null? profile : myDefaultProcessorsProfile;
+  }
+
+  @Override
+  public boolean isAnnotationProcessorsEnabled() {
+    if (myDefaultProcessorsProfile.isEnabled()) {
+      return true;
+    }
+    for (ProcessorConfigProfile profile : myModuleProcessorProfiles) {
+      if (profile.isEnabled()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private void addWildcardResourcePattern(@NonNls final String wildcardPattern) throws MalformedPatternException {
+    final CompiledPattern pattern = convertToRegexp(wildcardPattern);
+    if (pattern != null) {
+      myWildcardPatterns.add(wildcardPattern);
+      if (isPatternNegated(wildcardPattern)) {
+        myNegatedCompiledPatterns.add(pattern);
+      }
+      else {
+        myCompiledPatterns.add(pattern);
+      }
+    }
+  }
+
+  public void removeResourceFilePatterns() {
+    removeWildcardPatterns();
+  }
+
+  private void removeRegexpPatterns() {
+    myRegexpResourcePatterns.clear();
+  }
+
+  private void removeWildcardPatterns() {
+    myWildcardPatterns.clear();
+    myCompiledPatterns.clear();
+    myNegatedCompiledPatterns.clear();
+  }
+
+  private static CompiledPattern convertToRegexp(String wildcardPattern) {
+    if (isPatternNegated(wildcardPattern)) {
+      wildcardPattern = wildcardPattern.substring(1);
+    }
+
+    wildcardPattern = FileUtil.toSystemIndependentName(wildcardPattern);
+
+    String srcRoot = null;
+    int colon = wildcardPattern.indexOf(":");
+    if (colon > 0) {
+      srcRoot = wildcardPattern.substring(0, colon);
+      wildcardPattern = wildcardPattern.substring(colon + 1);
+    }
+
+    String dirPattern = null;
+    int slash = wildcardPattern.lastIndexOf('/');
+    if (slash >= 0) {
+      dirPattern = wildcardPattern.substring(0, slash + 1);
+      wildcardPattern = wildcardPattern.substring(slash + 1);
+      if (!dirPattern.startsWith("/")) {
+        dirPattern = "/" + dirPattern;
+      }
+      //now dirPattern starts and ends with '/'
+
+      dirPattern = normalizeWildcards(dirPattern);
+
+      dirPattern = StringUtil.replace(dirPattern, "/.*.*/", "(/.*)?/");
+      dirPattern = StringUtil.trimEnd(dirPattern, "/");
+
+      dirPattern = optimize(dirPattern);
+    }
+
+    wildcardPattern = normalizeWildcards(wildcardPattern);
+    wildcardPattern = optimize(wildcardPattern);
+
+    final Pattern dirCompiled = dirPattern == null ? null : compilePattern(dirPattern);
+    final Pattern srcCompiled = srcRoot == null ? null : compilePattern(optimize(normalizeWildcards(srcRoot)));
+    return new CompiledPattern(compilePattern(wildcardPattern), dirCompiled, srcCompiled);
+  }
+
+  private static String optimize(String wildcardPattern) {
+    return wildcardPattern.replaceAll("(?:\\.\\*)+", ".*");
+  }
+
+  private static String normalizeWildcards(String wildcardPattern) {
+    wildcardPattern = StringUtil.replace(wildcardPattern, "\\!", "!");
+    wildcardPattern = StringUtil.replace(wildcardPattern, ".", "\\.");
+    wildcardPattern = StringUtil.replace(wildcardPattern, "*?", ".+");
+    wildcardPattern = StringUtil.replace(wildcardPattern, "?*", ".+");
+    wildcardPattern = StringUtil.replace(wildcardPattern, "*", ".*");
+    wildcardPattern = StringUtil.replace(wildcardPattern, "?", ".");
+    return wildcardPattern;
+  }
+
+  public static boolean isPatternNegated(String wildcardPattern) {
+    return wildcardPattern.length() > 1 && wildcardPattern.charAt(0) == '!';
+  }
+
+  public boolean isResourceFile(String name) {
+    return isResourceFile(name, null);
+  }
+
+  private boolean matches(String s, Pattern p) {
+    synchronized (myPatternMatcher) {
+      try {
+        return myPatternMatcher.matches(s, p);
+      }
+      catch (Exception e) {
+        LOG.error("Exception matching file name \"" + s + "\" against the pattern \"" + p + "\"", e);
+        return false;
+      }
+    }
+  }
+
+  private boolean isResourceFile(String name, @Nullable VirtualFile parent) {
+    final Ref<String> parentRef = Ref.create(null);
+    //noinspection ForLoopReplaceableByForEach
+    for (int i = 0; i < myCompiledPatterns.size(); i++) {
+      if (matches(name, parent, parentRef, myCompiledPatterns.get(i))) {
+        return true;
+      }
+    }
+
+    if (myNegatedCompiledPatterns.isEmpty()) {
+      return false;
+    }
+
+    //noinspection ForLoopReplaceableByForEach
+    for (int i = 0; i < myNegatedCompiledPatterns.size(); i++) {
+      if (matches(name, parent, parentRef, myNegatedCompiledPatterns.get(i))) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  private boolean matches(String name, VirtualFile parent, Ref<String> parentRef, CompiledPattern pair) {
+    if (!matches(name, pair.fileName)) {
+      return false;
+    }
+
+    if (parent != null && (pair.dir != null || pair.srcRoot != null)) {
+      VirtualFile srcRoot = ProjectRootManager.getInstance(myProject).getFileIndex().getSourceRootForFile(parent);
+      if (pair.dir != null) {
+        String parentPath = parentRef.get();
+        if (parentPath == null) {
+          parentRef.set(parentPath = srcRoot == null ? parent.getPath() : VfsUtilCore.getRelativePath(parent, srcRoot, '/'));
+        }
+        if (parentPath == null || !matches("/" + parentPath, pair.dir)) {
+          return false;
+        }
+      }
+
+      if (pair.srcRoot != null) {
+        String srcRootName = srcRoot == null ? null : srcRoot.getName();
+        if (srcRootName == null || !matches(srcRootName, pair.srcRoot)) {
+          return false;
+        }
+      }
+    }
+
+    return true;
+  }
+
+
+  public void readExternal(Element parentNode) throws InvalidDataException {
+    DefaultJDOMExternalizer.readExternal(this, parentNode);
+
+    final Element notNullAssertions = parentNode.getChild(JpsJavaCompilerConfigurationSerializer.ADD_NOTNULL_ASSERTIONS);
+    if (notNullAssertions != null) {
+      myAddNotNullAssertions = Boolean.valueOf(notNullAssertions.getAttributeValue(JpsJavaCompilerConfigurationSerializer.ENABLED, "true"));
+    }
+
+    Element node = parentNode.getChild(JpsJavaCompilerConfigurationSerializer.EXCLUDE_FROM_COMPILE);
+    if (node != null) {
+      myExcludedEntriesConfiguration.readExternal(node);
+    }
+
+    try {
+      removeRegexpPatterns();
+      node = parentNode.getChild(JpsJavaCompilerConfigurationSerializer.RESOURCE_EXTENSIONS);
+      if (node != null) {
+        for (final Object o : node.getChildren(JpsJavaCompilerConfigurationSerializer.ENTRY)) {
+          Element element = (Element)o;
+          String pattern = element.getAttributeValue(JpsJavaCompilerConfigurationSerializer.NAME);
+          if (!StringUtil.isEmpty(pattern)) {
+            addRegexpPattern(pattern);
+          }
+        }
+      }
+
+      removeWildcardPatterns();
+      node = parentNode.getChild(JpsJavaCompilerConfigurationSerializer.WILDCARD_RESOURCE_PATTERNS);
+      if (node != null) {
+        myWildcardPatternsInitialized = true;
+        for (final Object o : node.getChildren(JpsJavaCompilerConfigurationSerializer.ENTRY)) {
+          final Element element = (Element)o;
+          String pattern = element.getAttributeValue(JpsJavaCompilerConfigurationSerializer.NAME);
+          if (!StringUtil.isEmpty(pattern)) {
+            addWildcardResourcePattern(pattern);
+          }
+        }
+      }
+    }
+    catch (MalformedPatternException e) {
+      throw new InvalidDataException(e);
+    }
+
+
+    myModuleProcessorProfiles.clear();
+    myProcessorsProfilesMap = null;
+
+    final Element annotationProcessingSettings = parentNode.getChild(JpsJavaCompilerConfigurationSerializer.ANNOTATION_PROCESSING);
+    if (annotationProcessingSettings != null) {
+      final List profiles = annotationProcessingSettings.getChildren("profile");
+      if (!profiles.isEmpty()) {
+        for (Object elem : profiles) {
+          final Element profileElement = (Element)elem;
+          final boolean isDefault = "true".equals(profileElement.getAttributeValue("default"));
+          if (isDefault) {
+            AnnotationProcessorProfileSerializer.readExternal(myDefaultProcessorsProfile, profileElement);
+          }
+          else {
+            final ProcessorConfigProfile profile = new ProcessorConfigProfileImpl("");
+            AnnotationProcessorProfileSerializer.readExternal(profile, profileElement);
+            myModuleProcessorProfiles.add(profile);
+          }
+        }
+      }
+      else {
+        // assuming older format
+        loadProfilesFromOldFormat(annotationProcessingSettings);
+      }
+    }
+
+    myBytecodeTargetLevel = null;
+    myModuleBytecodeTarget.clear();
+    final Element bytecodeTargetElement = parentNode.getChild(JpsJavaCompilerConfigurationSerializer.BYTECODE_TARGET_LEVEL);
+    if (bytecodeTargetElement != null) {
+      myBytecodeTargetLevel = bytecodeTargetElement.getAttributeValue(JpsJavaCompilerConfigurationSerializer.TARGET_ATTRIBUTE);
+      for (Element elem : (Collection<Element>)bytecodeTargetElement.getChildren(JpsJavaCompilerConfigurationSerializer.MODULE)) {
+        final String name = elem.getAttributeValue(JpsJavaCompilerConfigurationSerializer.NAME);
+        if (name == null) {
+          continue;
+        }
+        final String target = elem.getAttributeValue(JpsJavaCompilerConfigurationSerializer.TARGET_ATTRIBUTE);
+        if (target == null) {
+          continue;
+        }
+        myModuleBytecodeTarget.put(name, target);
+      }
+    }
+  }
+
+  private void loadProfilesFromOldFormat(Element processing) {
+    // collect data
+    final boolean isEnabled = Boolean.parseBoolean(processing.getAttributeValue(JpsJavaCompilerConfigurationSerializer.ENABLED, "false"));
+    final boolean isUseClasspath = Boolean.parseBoolean(processing.getAttributeValue("useClasspath", "true"));
+    final StringBuilder processorPath = new StringBuilder();
+    final Set<String> optionPairs = new HashSet<String>();
+    final Set<String> processors = new HashSet<String>();
+    final List<Pair<String, String>> modulesToProcess = new ArrayList<Pair<String, String>>();
+
+    for (Object child : processing.getChildren("processorPath")) {
+      final Element pathElement = (Element)child;
+      final String path = pathElement.getAttributeValue("value", (String)null);
+      if (path != null) {
+        if (processorPath.length() > 0) {
+          processorPath.append(File.pathSeparator);
+        }
+        processorPath.append(path);
+      }
+    }
+
+    for (Object child : processing.getChildren("processor")) {
+      final Element processorElement = (Element)child;
+      final String proc = processorElement.getAttributeValue(JpsJavaCompilerConfigurationSerializer.NAME, (String)null);
+      if (proc != null) {
+        processors.add(proc);
+      }
+      final StringTokenizer tokenizer = new StringTokenizer(processorElement.getAttributeValue("options", ""), " ", false);
+      while (tokenizer.hasMoreTokens()) {
+        final String pair = tokenizer.nextToken();
+        optionPairs.add(pair);
+      }
+    }
+
+    for (Object child : processing.getChildren("processModule")) {
+      final Element moduleElement = (Element)child;
+      final String name = moduleElement.getAttributeValue(JpsJavaCompilerConfigurationSerializer.NAME, (String)null);
+      if (name == null) {
+        continue;
+      }
+      final String dir = moduleElement.getAttributeValue("generatedDirName", (String)null);
+      modulesToProcess.add(Pair.create(name, dir));
+    }
+
+    myDefaultProcessorsProfile.setEnabled(false);
+    myDefaultProcessorsProfile.setObtainProcessorsFromClasspath(isUseClasspath);
+    if (processorPath.length() > 0) {
+      myDefaultProcessorsProfile.setProcessorPath(processorPath.toString());
+    }
+    if (!optionPairs.isEmpty()) {
+      for (String pair : optionPairs) {
+        final int index = pair.indexOf("=");
+        if (index > 0) {
+          myDefaultProcessorsProfile.setOption(pair.substring(0, index), pair.substring(index + 1));
+        }
+      }
+    }
+    for (String processor : processors) {
+      myDefaultProcessorsProfile.addProcessor(processor);
+    }
+
+    final Map<String, Set<String>> dirNameToModulesMap = new HashMap<String, Set<String>>();
+    for (Pair<String, String> moduleDirPair : modulesToProcess) {
+      final String dir = moduleDirPair.getSecond();
+      Set<String> set = dirNameToModulesMap.get(dir);
+      if (set == null) {
+        set = new HashSet<String>();
+        dirNameToModulesMap.put(dir, set);
+      }
+      set.add(moduleDirPair.getFirst());
+    }
+
+    int profileIndex = 0;
+    for (Map.Entry<String, Set<String>> entry : dirNameToModulesMap.entrySet()) {
+      final String dirName = entry.getKey();
+      final ProcessorConfigProfile profile = new ProcessorConfigProfileImpl(myDefaultProcessorsProfile);
+      profile.setName("Profile" + (++profileIndex));
+      profile.setEnabled(isEnabled);
+      profile.setGeneratedSourcesDirectoryName(dirName, false);
+      for (String moduleName : entry.getValue()) {
+        profile.addModuleName(moduleName);
+      }
+      myModuleProcessorProfiles.add(profile);
+    }
+  }
+
+  public void writeExternal(Element parentNode) throws WriteExternalException {
+    DefaultJDOMExternalizer.writeExternal(this, parentNode);
+
+    if (myAddNotNullAssertions != true) {
+      addChild(parentNode, JpsJavaCompilerConfigurationSerializer.ADD_NOTNULL_ASSERTIONS).setAttribute(
+        JpsJavaCompilerConfigurationSerializer.ENABLED, String.valueOf(myAddNotNullAssertions));
+    }
+
+    if(myExcludedEntriesConfiguration.getExcludeEntryDescriptions().length > 0) {
+      myExcludedEntriesConfiguration.writeExternal(addChild(parentNode, JpsJavaCompilerConfigurationSerializer.EXCLUDE_FROM_COMPILE));
+    }
+
+    final Element newChild = addChild(parentNode, JpsJavaCompilerConfigurationSerializer.RESOURCE_EXTENSIONS);
+    for (final String pattern : getRegexpPatterns()) {
+      addChild(newChild, JpsJavaCompilerConfigurationSerializer.ENTRY).setAttribute(JpsJavaCompilerConfigurationSerializer.NAME, pattern);
+    }
+
+    if (myWildcardPatternsInitialized || !myWildcardPatterns.isEmpty()) {
+      final Element wildcardPatterns = addChild(parentNode, JpsJavaCompilerConfigurationSerializer.WILDCARD_RESOURCE_PATTERNS);
+      for (final String wildcardPattern : myWildcardPatterns) {
+        addChild(wildcardPatterns, JpsJavaCompilerConfigurationSerializer.ENTRY).setAttribute(JpsJavaCompilerConfigurationSerializer.NAME, wildcardPattern);
+      }
+    }
+
+    final Element annotationProcessingSettings = addChild(parentNode, JpsJavaCompilerConfigurationSerializer.ANNOTATION_PROCESSING);
+    final Element defaultProfileElem = addChild(annotationProcessingSettings, "profile").setAttribute("default", "true");
+    AnnotationProcessorProfileSerializer.writeExternal(myDefaultProcessorsProfile, defaultProfileElem);
+    for (ProcessorConfigProfile profile : myModuleProcessorProfiles) {
+      final Element profileElem = addChild(annotationProcessingSettings, "profile").setAttribute("default", "false");
+      AnnotationProcessorProfileSerializer.writeExternal(profile, profileElem);
+    }
+
+    if (!StringUtil.isEmpty(myBytecodeTargetLevel) || !myModuleBytecodeTarget.isEmpty()) {
+      final Element bytecodeTarget = addChild(parentNode, JpsJavaCompilerConfigurationSerializer.BYTECODE_TARGET_LEVEL);
+      if (!StringUtil.isEmpty(myBytecodeTargetLevel)) {
+        bytecodeTarget.setAttribute(JpsJavaCompilerConfigurationSerializer.TARGET_ATTRIBUTE, myBytecodeTargetLevel);
+      }
+      if (!myModuleBytecodeTarget.isEmpty()) {
+        final List<String> moduleNames = new ArrayList<String>(myModuleBytecodeTarget.keySet());
+        Collections.sort(moduleNames, String.CASE_INSENSITIVE_ORDER);
+        for (String name : moduleNames) {
+          final Element moduleElement = addChild(bytecodeTarget, JpsJavaCompilerConfigurationSerializer.MODULE);
+          moduleElement.setAttribute(JpsJavaCompilerConfigurationSerializer.NAME, name);
+          final String value = myModuleBytecodeTarget.get(name);
+          moduleElement.setAttribute(JpsJavaCompilerConfigurationSerializer.TARGET_ATTRIBUTE, value != null? value : "");
+        }
+      }
+    }
+  }
+
+  @NotNull @NonNls
+  public String getComponentName() {
+    return "CompilerConfiguration";
+  }
+
+  public BackendCompiler getDefaultCompiler() {
+    createCompilers();
+    return myDefaultJavaCompiler;
+  }
+
+  /**
+   * @param defaultCompiler The compiler that is passed as a parameter to setDefaultCompiler() 
+   * must be one of the registered compilers in compiler configuration.
+   * Otherwise because of lazy compiler initialization, the value of default compiler will point to some other compiler instance
+   */
+  public void setDefaultCompiler(BackendCompiler defaultCompiler) {
+    myDefaultJavaCompiler = defaultCompiler;
+    DEFAULT_COMPILER = defaultCompiler.getId();
+  }
+
+  public void convertPatterns() {
+    if (!needPatternConversion()) {
+      return;
+    }
+    try {
+      boolean ok;
+      try {
+        ok = doConvertPatterns();
+      }
+      catch (MalformedPatternException e) {
+        ok = false;
+      }
+      if (!ok) {
+        final String initialPatternString = patternsToString(getRegexpPatterns());
+        final String message = CompilerBundle.message(
+          "message.resource.patterns.format.changed",
+          ApplicationNamesInfo.getInstance().getProductName(),
+          initialPatternString,
+          CommonBundle.getOkButtonText(),
+          CommonBundle.getCancelButtonText()
+        );
+        final String wildcardPatterns = Messages.showInputDialog(
+          myProject, message, CompilerBundle.message("pattern.conversion.dialog.title"), Messages.getWarningIcon(), initialPatternString, new InputValidator() {
+          public boolean checkInput(String inputString) {
+            return true;
+          }
+          public boolean canClose(String inputString) {
+            final StringTokenizer tokenizer = new StringTokenizer(inputString, ";", false);
+            StringBuilder malformedPatterns = new StringBuilder();
+
+            while (tokenizer.hasMoreTokens()) {
+              String pattern = tokenizer.nextToken();
+              try {
+                addWildcardResourcePattern(pattern);
+              }
+              catch (MalformedPatternException e) {
+                malformedPatterns.append("\n\n");
+                malformedPatterns.append(pattern);
+                malformedPatterns.append(": ");
+                malformedPatterns.append(e.getMessage());
+              }
+            }
+
+            if (malformedPatterns.length() > 0) {
+              Messages.showErrorDialog(CompilerBundle.message("error.bad.resource.patterns", malformedPatterns.toString()),
+                                       CompilerBundle.message("bad.resource.patterns.dialog.title"));
+              removeWildcardPatterns();
+              return false;
+            }
+            return true;
+          }
+        });
+        if (wildcardPatterns == null) { // cancel pressed
+          loadDefaultWildcardPatterns();
+        }
+      }
+    }
+    finally {
+      myWildcardPatternsInitialized = true;
+    }
+  }
+
+  private boolean needPatternConversion() {
+    return !myWildcardPatternsInitialized && !myRegexpResourcePatterns.isEmpty();
+  }
+
+  private boolean doConvertPatterns() throws MalformedPatternException {
+    final String[] regexpPatterns = getRegexpPatterns();
+    final List<String> converted = new ArrayList<String>();
+    final Pattern multipleExtensionsPatternPattern = compilePattern("\\.\\+\\\\\\.\\((\\w+(?:\\|\\w+)*)\\)");
+    final Pattern singleExtensionPatternPattern = compilePattern("\\.\\+\\\\\\.(\\w+)");
+    final Perl5Matcher matcher = new Perl5Matcher();
+    for (final String regexpPattern : regexpPatterns) {
+      //final Matcher multipleExtensionsMatcher = multipleExtensionsPatternPattern.matcher(regexpPattern);
+      if (matcher.matches(regexpPattern, multipleExtensionsPatternPattern)) {
+        final MatchResult match = matcher.getMatch();
+        final StringTokenizer tokenizer = new StringTokenizer(match.group(1), "|", false);
+        while (tokenizer.hasMoreTokens()) {
+          converted.add("?*." + tokenizer.nextToken());
+        }
+      }
+      else {
+        //final Matcher singleExtensionMatcher = singleExtensionPatternPattern.matcher(regexpPattern);
+        if (matcher.matches(regexpPattern, singleExtensionPatternPattern)) {
+          final MatchResult match = matcher.getMatch();
+          converted.add("?*." + match.group(1));
+        }
+        else {
+          return false;
+        }
+      }
+    }
+    for (final String aConverted : converted) {
+      addWildcardResourcePattern(aConverted);
+    }
+    return true;
+  }
+
+  private static String patternsToString(final String[] patterns) {
+    final StringBuilder extensionsString = new StringBuilder();
+    for (int idx = 0; idx < patterns.length; idx++) {
+      if (idx > 0) {
+        extensionsString.append(";");
+      }
+      extensionsString.append(patterns[idx]);
+    }
+    return extensionsString.toString();
+  }
+  
+  private static class CompiledPattern {
+    @NotNull final Pattern fileName;
+    @Nullable final Pattern dir;
+    @Nullable final Pattern srcRoot;
+
+    private CompiledPattern(Pattern fileName, Pattern dir, Pattern srcRoot) {
+      this.fileName = fileName;
+      this.dir = dir;
+      this.srcRoot = srcRoot;
+    }
+  }
+
+  private static Element addChild(Element parent, final String childName) {
+    final Element child = new Element(childName);
+    parent.addContent(child);
+    return child;
+  }
+
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerEncodingService.java b/java/compiler/impl/src/com/intellij/compiler/CompilerEncodingService.java
new file mode 100644
index 0000000..9c88df8
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/CompilerEncodingService.java
@@ -0,0 +1,56 @@
+/*
+ * 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 com.intellij.compiler;
+
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.Chunk;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.nio.charset.Charset;
+import java.util.Collection;
+
+/**
+ * @author nik
+ */
+public abstract class CompilerEncodingService {
+  public static CompilerEncodingService getInstance(@NotNull Project project) {
+    return ServiceManager.getService(project, CompilerEncodingService.class);
+  }
+
+  @Nullable
+  public static Charset getPreferredModuleEncoding(Chunk<Module> chunk) {
+    CompilerEncodingService service = null;
+    for (Module module : chunk.getNodes()) {
+      if (service == null) {
+        service = getInstance(module.getProject());
+      }
+      final Charset charset = service.getPreferredModuleEncoding(module);
+      if (charset != null) {
+        return charset;
+      }
+    }
+    return null;
+  }
+
+  @Nullable
+  public abstract Charset getPreferredModuleEncoding(@NotNull Module module);
+
+  @NotNull
+  public abstract Collection<Charset> getAllModuleEncodings(@NotNull Module module);
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerException.java b/java/compiler/impl/src/com/intellij/compiler/CompilerException.java
new file mode 100644
index 0000000..d982f9c
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/CompilerException.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: Oct 15, 2002
+ * Time: 1:55:22 PM
+ */
+package com.intellij.compiler;
+
+public class CompilerException extends Exception {
+
+  public CompilerException(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerIOUtil.java b/java/compiler/impl/src/com/intellij/compiler/CompilerIOUtil.java
new file mode 100644
index 0000000..4616bc9
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/CompilerIOUtil.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 com.intellij.compiler;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+public class CompilerIOUtil {
+  private final static ThreadLocal<byte[]> myBuffer = new ThreadLocal<byte[]>() {
+    protected byte[] initialValue() {
+      return new byte[1024];
+    }
+  };
+
+  private CompilerIOUtil() {}
+
+  public static String readString(DataInput stream) throws IOException {
+    final int length = stream.readInt();
+    if (length == -1) {
+      return null;
+    }
+
+    if (length == 0) {
+      return "";
+    }
+
+    char[] chars = new char[length];
+    int charsRead = 0;
+
+    final byte[] buff = myBuffer.get();
+    while (charsRead < length) {
+      final int bytesRead = Math.min((length - charsRead) * 2, buff.length);
+      stream.readFully(buff, 0, bytesRead);
+      for (int i = 0 ; i < bytesRead; i += 2) {
+        chars[charsRead++] = (char)((buff[i] << 8) + (buff[i + 1] & 0xFF));
+      }
+    }
+
+    return new String(chars);
+  }
+
+  public static void writeString(String s, DataOutput stream) throws IOException {
+    if (s == null) {
+      stream.writeInt(-1);
+      return;
+    }
+
+    final int len = s.length();
+    stream.writeInt(len);
+    if (len == 0) {
+      return;
+    }
+
+    int charsWritten = 0;
+    final byte[] buff = myBuffer.get();
+    while (charsWritten < len) {
+      final int bytesWritten = Math.min((len - charsWritten) * 2, buff.length);
+      for (int i = 0; i < bytesWritten; i += 2) {
+        char aChar = s.charAt(charsWritten++);
+        buff[i] = (byte)((aChar >>> 8) & 0xFF);
+        buff[i + 1] = (byte)((aChar) & 0xFF);
+      }
+      stream.write(buff, 0, bytesWritten);
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerIconLayerProvider.java b/java/compiler/impl/src/com/intellij/compiler/CompilerIconLayerProvider.java
new file mode 100644
index 0000000..3f2b16d
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/CompilerIconLayerProvider.java
@@ -0,0 +1,66 @@
+/*
+ * 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 com.intellij.compiler;
+
+import com.intellij.codeInsight.CodeInsightBundle;
+import com.intellij.ide.IconLayerProvider;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.FileIndexFacade;
+import com.intellij.openapi.util.Iconable;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiModifierListOwner;
+import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+/**
+ * @author yole
+ */
+public class CompilerIconLayerProvider implements IconLayerProvider {
+  @Override
+  public Icon getLayerIcon(@NotNull Iconable element, boolean isLocked) {
+    VirtualFile vFile = null;
+    Project project = null;
+    if (element instanceof PsiModifierListOwner) {
+      project = ((PsiModifierListOwner) element).getProject();
+      final PsiFile containingFile = ((PsiModifierListOwner) element).getContainingFile();
+      vFile = containingFile == null ? null : containingFile.getVirtualFile();
+    }
+    else if (element instanceof PsiDirectory) {
+      project = ((PsiDirectory) element).getProject();
+      vFile = ((PsiDirectory) element).getVirtualFile();
+    }
+    if (vFile != null && isExcluded(vFile, project)) {
+      return PlatformIcons.EXCLUDED_FROM_COMPILE_ICON;
+    }
+    return null;
+  }
+
+  @Override
+  public String getLayerDescription() {
+    return CodeInsightBundle.message("node.excluded.flag.tooltip");
+  }
+
+  public static boolean isExcluded(final VirtualFile vFile, final Project project) {
+    return vFile != null
+           && ServiceManager.getService(project, FileIndexFacade.class).isInSource(vFile)
+           && CompilerConfiguration.getInstance(project).isExcludedFromCompilation(vFile);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java b/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java
new file mode 100644
index 0000000..f505855
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java
@@ -0,0 +1,466 @@
+/*
+ * 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 com.intellij.compiler;
+
+import com.intellij.compiler.impl.*;
+import com.intellij.compiler.impl.javaCompiler.AnnotationProcessingCompiler;
+import com.intellij.compiler.impl.javaCompiler.JavaCompiler;
+import com.intellij.compiler.impl.resourceCompiler.ResourceCompiler;
+import com.intellij.compiler.impl.rmiCompiler.RmicCompiler;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.*;
+import com.intellij.openapi.compiler.Compiler;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.Chunk;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.graph.CachingSemiGraph;
+import com.intellij.util.graph.Graph;
+import com.intellij.util.graph.GraphGenerator;
+import com.intellij.util.messages.MessageBus;
+import com.intellij.util.messages.MessageBusConnection;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.lang.reflect.Array;
+import java.util.*;
+import java.util.concurrent.Semaphore;
+
+public class CompilerManagerImpl extends CompilerManager {
+  private final Project myProject;
+
+  private final List<Compiler> myCompilers = new ArrayList<Compiler>();
+  private final List<TranslatingCompiler> myTranslators = new ArrayList<TranslatingCompiler>();
+
+  private final List<CompileTask> myBeforeTasks = new ArrayList<CompileTask>();
+  private final List<CompileTask> myAfterTasks = new ArrayList<CompileTask>();
+  private final Set<FileType> myCompilableTypes = new HashSet<FileType>();
+  private final CompilationStatusListener myEventPublisher;
+  private final Semaphore myCompilationSemaphore = new Semaphore(1, true);
+  private final Map<Compiler, Set<FileType>> myCompilerToInputTypes = new HashMap<Compiler, Set<FileType>>();
+  private final Map<Compiler, Set<FileType>> myCompilerToOutputTypes = new HashMap<Compiler, Set<FileType>>();
+  private final Set<ModuleType> myValidationDisabledModuleTypes = new HashSet<ModuleType>();
+  private final Set<LocalFileSystem.WatchRequest> myWatchRoots;
+
+  public CompilerManagerImpl(final Project project, CompilerConfigurationImpl compilerConfiguration, MessageBus messageBus) {
+    myProject = project;
+    myEventPublisher = messageBus.syncPublisher(CompilerTopics.COMPILATION_STATUS);
+
+    // predefined compilers
+    addTranslatingCompiler(new AnnotationProcessingCompiler(project), new HashSet<FileType>(Arrays.asList(StdFileTypes.JAVA)), new HashSet<FileType>(Arrays.asList(StdFileTypes.JAVA, StdFileTypes.CLASS)));
+    addTranslatingCompiler(new JavaCompiler(project), new HashSet<FileType>(Arrays.asList(StdFileTypes.JAVA)), new HashSet<FileType>(Arrays.asList(StdFileTypes.CLASS)));
+    addCompiler(new ResourceCompiler(project, compilerConfiguration));
+    addCompiler(new RmicCompiler());
+
+    for(Compiler compiler: Extensions.getExtensions(Compiler.EP_NAME, myProject)) {
+      addCompiler(compiler);
+    }
+    for(CompilerFactory factory: Extensions.getExtensions(CompilerFactory.EP_NAME, myProject)) {
+      Compiler[] compilers = factory.createCompilers(this);
+      for (Compiler compiler : compilers) {
+        addCompiler(compiler);
+      }
+    }
+
+    addCompilableFileType(StdFileTypes.JAVA);
+
+    final File projectGeneratedSrcRoot = CompilerPaths.getGeneratedDataDirectory(project);
+    projectGeneratedSrcRoot.mkdirs();
+    final LocalFileSystem lfs = LocalFileSystem.getInstance();
+    myWatchRoots = lfs.addRootsToWatch(Collections.singletonList(FileUtil.toCanonicalPath(projectGeneratedSrcRoot.getPath())), true);
+    Disposer.register(project, new Disposable() {
+      public void dispose() {
+        lfs.removeWatchedRoots(myWatchRoots);
+        if (ApplicationManager.getApplication().isUnitTestMode()) {    // force cleanup for created compiler system directory with generated sources
+          FileUtil.delete(CompilerPaths.getCompilerSystemDirectory(project));
+        }
+      }
+    });
+
+    //
+    //addCompiler(new DummyTransformingCompiler()); // this one is for testing purposes only
+    //addCompiler(new DummySourceGeneratingCompiler(myProject)); // this one is for testing purposes only
+    /*
+    // for testing only
+    ApplicationManager.getApplication().invokeLater(new Runnable() {
+      public void run() {
+        ApplicationManager.getApplication().runWriteAction(new Runnable() {
+          public void run() {
+            FileTypeManager.getInstance().registerFileType(DummyTranslatingCompiler.INPUT_FILE_TYPE, DummyTranslatingCompiler.FILETYPE_EXTENSION);
+            addTranslatingCompiler(
+              new DummyTranslatingCompiler(),
+              new HashSet<FileType>(Arrays.asList(DummyTranslatingCompiler.INPUT_FILE_TYPE)),
+              new HashSet<FileType>(Arrays.asList( StdFileTypes.JAVA))
+            );
+          }
+        });
+
+      }
+    });
+    */
+  }
+
+  public Semaphore getCompilationSemaphore() {
+    return myCompilationSemaphore;
+  }
+
+  public boolean isCompilationActive() {
+    return myCompilationSemaphore.availablePermits() == 0;
+  }
+
+  public void addTranslatingCompiler(@NotNull final TranslatingCompiler compiler, final Set<FileType> inputTypes, final Set<FileType> outputTypes) {
+    myTranslators.add(compiler);
+    myCompilerToInputTypes.put(compiler, inputTypes);
+    myCompilerToOutputTypes.put(compiler, outputTypes);
+
+    final List<Chunk<Compiler>> chunks = ModuleCompilerUtil.getSortedChunks(createCompilerGraph((List<Compiler>)(List)myTranslators));
+
+    myTranslators.clear();
+    for (Chunk<Compiler> chunk : chunks) {
+      for (Compiler chunkCompiler : chunk.getNodes()) {
+        myTranslators.add((TranslatingCompiler)chunkCompiler);
+      }
+    }
+  }
+
+  @NotNull
+  public Set<FileType> getRegisteredInputTypes(@NotNull final TranslatingCompiler compiler) {
+    final Set<FileType> inputs = myCompilerToInputTypes.get(compiler);
+    return inputs != null? Collections.unmodifiableSet(inputs) : Collections.<FileType>emptySet();
+  }
+
+  @NotNull
+  public Set<FileType> getRegisteredOutputTypes(@NotNull final TranslatingCompiler compiler) {
+    final Set<FileType> outs = myCompilerToOutputTypes.get(compiler);
+    return outs != null? Collections.unmodifiableSet(outs) : Collections.<FileType>emptySet();
+  }
+
+  public final void addCompiler(@NotNull Compiler compiler) {
+    if (compiler instanceof TranslatingCompiler) {
+      myTranslators.add((TranslatingCompiler)compiler);
+    }
+    else {
+      myCompilers.add(compiler);
+      // supporting file instrumenting compilers and validators for external build
+      // Since these compilers are IDE-specific and use PSI, it is ok to run them before and after the build in the IDE
+      if (compiler instanceof SourceInstrumentingCompiler) {
+        addBeforeTask(new FileProcessingCompilerAdapterTask((FileProcessingCompiler)compiler));
+      }
+      else if (compiler instanceof Validator) {
+        addAfterTask(new FileProcessingCompilerAdapterTask((FileProcessingCompiler)compiler));
+      }
+    }
+  }
+
+  public final void removeCompiler(@NotNull Compiler compiler) {
+    if (compiler instanceof TranslatingCompiler) {
+      myTranslators.remove(compiler);
+    }
+    else {
+      if (myCompilers.remove(compiler)) {
+        for (List<CompileTask> tasks : Arrays.asList(myBeforeTasks, myAfterTasks)) {
+          for (Iterator<CompileTask> iterator = tasks.iterator(); iterator.hasNext(); ) {
+            CompileTask task = iterator.next();
+            if (task instanceof FileProcessingCompilerAdapterTask && ((FileProcessingCompilerAdapterTask)task).getCompiler() == compiler) {
+              iterator.remove();
+            }
+          }
+        }
+      }
+    }
+    myCompilerToInputTypes.remove(compiler);
+    myCompilerToOutputTypes.remove(compiler);
+  }
+
+  @NotNull
+  public <T  extends Compiler> T[] getCompilers(@NotNull Class<T> compilerClass) {
+    return getCompilers(compilerClass, CompilerFilter.ALL);
+  }
+
+  @NotNull
+  public <T extends Compiler> T[] getCompilers(@NotNull Class<T> compilerClass, CompilerFilter filter) {
+    final List<T> compilers = new ArrayList<T>(myCompilers.size());
+    for (final Compiler item : myCompilers) {
+      if (compilerClass.isAssignableFrom(item.getClass()) && filter.acceptCompiler(item)) {
+        compilers.add((T)item);
+      }
+    }
+    for (final Compiler item : myTranslators) {
+      if (compilerClass.isAssignableFrom(item.getClass()) && filter.acceptCompiler(item)) {
+        compilers.add((T)item);
+      }
+    }
+    final T[] array = (T[])Array.newInstance(compilerClass, compilers.size());
+    return compilers.toArray(array);
+  }
+
+  public void addCompilableFileType(@NotNull FileType type) {
+    myCompilableTypes.add(type);
+  }
+
+  public void removeCompilableFileType(@NotNull FileType type) {
+    myCompilableTypes.remove(type);
+  }
+
+  public boolean isCompilableFileType(@NotNull FileType type) {
+    return myCompilableTypes.contains(type);
+  }
+
+  public final void addBeforeTask(@NotNull CompileTask task) {
+    myBeforeTasks.add(task);
+  }
+
+  public final void addAfterTask(@NotNull CompileTask task) {
+    myAfterTasks.add(task);
+  }
+
+  @NotNull
+  public CompileTask[] getBeforeTasks() {
+    return myBeforeTasks.toArray(new CompileTask[myBeforeTasks.size()]);
+  }
+
+  @NotNull
+  public CompileTask[] getAfterTasks() {
+    return myAfterTasks.toArray(new CompileTask[myAfterTasks.size()]);
+  }
+
+  public void compile(@NotNull VirtualFile[] files, CompileStatusNotification callback) {
+    compile(createFilesCompileScope(files), callback);
+  }
+
+  public void compile(@NotNull Module module, CompileStatusNotification callback) {
+    new CompileDriver(myProject).compile(createModuleCompileScope(module, false), new ListenerNotificator(callback), true);
+  }
+
+  public void compile(@NotNull CompileScope scope, CompileStatusNotification callback) {
+    new CompileDriver(myProject).compile(scope, new ListenerNotificator(callback), false);
+  }
+
+  public void make(CompileStatusNotification callback) {
+    new CompileDriver(myProject).make(createProjectCompileScope(myProject), new ListenerNotificator(callback));
+  }
+
+  public void make(@NotNull Module module, CompileStatusNotification callback) {
+    new CompileDriver(myProject).make(createModuleCompileScope(module, true), new ListenerNotificator(callback));
+  }
+
+  public void make(@NotNull Project project, @NotNull Module[] modules, CompileStatusNotification callback) {
+    new CompileDriver(myProject).make(createModuleGroupCompileScope(project, modules, true), new ListenerNotificator(callback));
+  }
+
+  public void make(@NotNull CompileScope scope, CompileStatusNotification callback) {
+    new CompileDriver(myProject).make(scope, new ListenerNotificator(callback));
+  }
+
+  public void make(@NotNull CompileScope scope, CompilerFilter filter, @Nullable CompileStatusNotification callback) {
+    final CompileDriver compileDriver = new CompileDriver(myProject);
+    compileDriver.setCompilerFilter(filter);
+    compileDriver.make(scope, new ListenerNotificator(callback));
+  }
+
+  public boolean isUpToDate(@NotNull final CompileScope scope) {
+    return new CompileDriver(myProject).isUpToDate(scope);
+  }
+
+  public void rebuild(CompileStatusNotification callback) {
+    new CompileDriver(myProject).rebuild(new ListenerNotificator(callback));
+  }
+
+  public void executeTask(@NotNull CompileTask task, @NotNull CompileScope scope, String contentName, Runnable onTaskFinished) {
+    final CompileDriver compileDriver = new CompileDriver(myProject);
+    compileDriver.executeCompileTask(task, scope, contentName, onTaskFinished);
+  }
+
+  private final Map<CompilationStatusListener, MessageBusConnection> myListenerAdapters = new HashMap<CompilationStatusListener, MessageBusConnection>();
+
+  public void addCompilationStatusListener(@NotNull final CompilationStatusListener listener) {
+    final MessageBusConnection connection = myProject.getMessageBus().connect();
+    myListenerAdapters.put(listener, connection);
+    connection.subscribe(CompilerTopics.COMPILATION_STATUS, listener);
+  }
+
+  @Override
+  public void addCompilationStatusListener(@NotNull CompilationStatusListener listener, @NotNull Disposable parentDisposable) {
+    final MessageBusConnection connection = myProject.getMessageBus().connect(parentDisposable);
+    connection.subscribe(CompilerTopics.COMPILATION_STATUS, listener);
+  }
+
+  public void removeCompilationStatusListener(@NotNull final CompilationStatusListener listener) {
+    final MessageBusConnection connection = myListenerAdapters.remove(listener);
+    if (connection != null) {
+      connection.disconnect();
+    }
+  }
+
+  // Compiler tests support
+
+  private static List<String> ourDeletedPaths;
+  private static List<String> ourRecompiledPaths;
+  private static List<String> ourCompiledPaths;
+
+  public static void testSetup() {
+    ourDeletedPaths = new ArrayList<String>();
+    ourRecompiledPaths = new ArrayList<String>();
+    ourCompiledPaths = new ArrayList<String>();
+  }
+
+  public static void addDeletedPath(String path) {
+    ourDeletedPaths.add(path);
+  }
+
+  public static void addRecompiledPath(String path) {
+    ourRecompiledPaths.add(path);
+  }
+
+  public static void addCompiledPath(String path) {
+    ourCompiledPaths.add(path);
+  }
+
+  public static String[] getPathsToDelete() {
+    return ArrayUtil.toStringArray(ourDeletedPaths);
+  }
+
+  public static String[] getPathsToRecompile() {
+    return ArrayUtil.toStringArray(ourRecompiledPaths);
+  }
+
+  public static String[] getPathsToCompile() {
+    return ArrayUtil.toStringArray(ourCompiledPaths);
+  }
+
+  public static void clearPathsToCompile() {
+    if (ourCompiledPaths != null) {
+      ourCompiledPaths.clear();
+    }
+  }
+
+  public boolean isExcludedFromCompilation(@NotNull VirtualFile file) {
+    return CompilerConfiguration.getInstance(myProject).isExcludedFromCompilation(file);
+  }
+
+  private static final OutputToSourceMapping OUTPUT_TO_SOURCE_MAPPING = new OutputToSourceMapping() {
+    public String getSourcePath(final String outputPath) {
+      final LocalFileSystem lfs = LocalFileSystem.getInstance();
+      final VirtualFile outputFile = lfs.findFileByPath(outputPath);
+
+      final VirtualFile sourceFile = outputFile != null ? TranslatingCompilerFilesMonitor.getSourceFileByOutput(outputFile) : null;
+      return sourceFile != null? sourceFile.getPath() : null;
+    }
+  };
+  @NotNull
+  public OutputToSourceMapping getJavaCompilerOutputMapping() {
+    return OUTPUT_TO_SOURCE_MAPPING;
+  }
+
+  @NotNull
+  public CompileScope createFilesCompileScope(@NotNull final VirtualFile[] files) {
+    CompileScope[] scopes = new CompileScope[files.length];
+    for(int i = 0; i < files.length; i++){
+      scopes[i] = new OneProjectItemCompileScope(myProject, files[i]);
+    }
+    return new CompositeScope(scopes);
+  }
+
+  @NotNull
+  public CompileScope createModuleCompileScope(@NotNull final Module module, final boolean includeDependentModules) {
+    return new ModuleCompileScope(module, includeDependentModules);
+  }
+
+  @NotNull
+  public CompileScope createModulesCompileScope(@NotNull final Module[] modules, final boolean includeDependentModules) {
+    return new ModuleCompileScope(myProject, modules, includeDependentModules);
+  }
+
+  @NotNull
+  public CompileScope createModuleGroupCompileScope(@NotNull final Project project, @NotNull final Module[] modules, final boolean includeDependentModules) {
+    return new ModuleCompileScope(project, modules, includeDependentModules);
+  }
+
+  @NotNull
+  public CompileScope createProjectCompileScope(@NotNull final Project project) {
+    return new ProjectCompileScope(project);
+  }
+
+  @Override
+  public void setValidationEnabled(ModuleType moduleType, boolean enabled) {
+    if (enabled) {
+      myValidationDisabledModuleTypes.remove(moduleType);
+    }
+    else {
+      myValidationDisabledModuleTypes.add(moduleType);
+    }
+  }
+
+  @Override
+  public boolean isValidationEnabled(Module module) {
+    if (myValidationDisabledModuleTypes.isEmpty()) {
+      return true; // optimization
+    }
+    return !myValidationDisabledModuleTypes.contains(ModuleType.get(module));
+  }
+
+  private Graph<Compiler> createCompilerGraph(final List<Compiler> compilers) {
+    return GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph<Compiler>() {
+      public Collection<Compiler> getNodes() {
+        return compilers;
+      }
+
+      public Iterator<Compiler> getIn(Compiler compiler) {
+        final Set<FileType> compilerInput = myCompilerToInputTypes.get(compiler);
+        if (compilerInput == null || compilerInput.isEmpty()) {
+          return Collections.<Compiler>emptySet().iterator();
+        }
+
+        final Set<Compiler> inCompilers = new HashSet<Compiler>();
+
+        for (Map.Entry<Compiler, Set<FileType>> entry : myCompilerToOutputTypes.entrySet()) {
+          final Set<FileType> outputs = entry.getValue();
+          Compiler comp = entry.getKey();
+          if (outputs != null && ContainerUtil.intersects(compilerInput, outputs)) {
+            inCompilers.add(comp);
+          }
+        }
+        return inCompilers.iterator();
+      }
+    }));
+  }
+  
+  private class ListenerNotificator implements CompileStatusNotification {
+    private final @Nullable CompileStatusNotification myDelegate;
+
+    private ListenerNotificator(@Nullable CompileStatusNotification delegate) {
+      myDelegate = delegate;
+    }
+
+    public void finished(boolean aborted, int errors, int warnings, final CompileContext compileContext) {
+      myEventPublisher.compilationFinished(aborted, errors, warnings, compileContext);
+      if (myDelegate != null) {
+        myDelegate.finished(aborted, errors, warnings, compileContext);
+      }
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerMessageImpl.java b/java/compiler/impl/src/com/intellij/compiler/CompilerMessageImpl.java
new file mode 100644
index 0000000..bb15576
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/CompilerMessageImpl.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.
+ */
+package com.intellij.compiler;
+
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.compiler.CompilerMessage;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.pom.Navigatable;
+import org.jetbrains.annotations.Nullable;
+
+public final class CompilerMessageImpl implements CompilerMessage {
+
+  private final Project myProject;
+  private final CompilerMessageCategory myCategory;
+  @Nullable private Navigatable myNavigatable;
+  private final String myMessage;
+  private VirtualFile myFile;
+  private final int myRow;
+  private final int myColumn;
+
+  public CompilerMessageImpl(Project project, CompilerMessageCategory category, String message) {
+    this(project, category, message, null, -1, -1, null);
+  }
+
+  public CompilerMessageImpl(Project project,
+                             CompilerMessageCategory category,
+                             String message,
+                             @Nullable final VirtualFile file,
+                             int row,
+                             int column,
+                             @Nullable final Navigatable navigatable) {
+    myProject = project;
+    myCategory = category;
+    myNavigatable = navigatable;
+    myMessage = message == null ? "" : message;
+    myRow = row;
+    myColumn = column;
+    myFile = file;
+  }
+
+  public CompilerMessageCategory getCategory() {
+    return myCategory;
+  }
+
+  public String getMessage() {
+    return myMessage;
+  }
+
+  public Navigatable getNavigatable() {
+    if (myNavigatable != null) {
+      return myNavigatable;
+    }
+    final VirtualFile virtualFile = getVirtualFile();
+    if (virtualFile != null && virtualFile.isValid()) {
+      final int line = getLine() - 1; // editor lines are zero-based
+      if (line >= 0) {
+        return myNavigatable = new OpenFileDescriptor(myProject, virtualFile, line, Math.max(0, getColumn()-1));
+      }
+    }
+    return null;
+  }
+
+  public VirtualFile getVirtualFile() {
+    return myFile;
+  }
+
+  public String getExportTextPrefix() {
+    if (getLine() >= 0) {
+      return CompilerBundle.message("compiler.results.export.text.prefix", getLine());
+    }
+    return "";
+  }
+
+  public String getRenderTextPrefix() {
+    if (getLine() >= 0) {
+      return "(" + getLine() + ", " + getColumn() + ")";
+    }
+    return "";
+  }
+
+  public int getLine() {
+    return myRow;
+  }
+
+  public int getColumn() {
+    return myColumn;
+  }
+
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (!(o instanceof CompilerMessage)) return false;
+
+    final CompilerMessageImpl compilerMessage = (CompilerMessageImpl)o;
+
+    if (myColumn != compilerMessage.myColumn) return false;
+    if (myRow != compilerMessage.myRow) return false;
+    if (!myCategory.equals(compilerMessage.myCategory)) return false;
+    if (myFile != null ? !myFile.equals(compilerMessage.myFile) : compilerMessage.myFile != null) return false;
+    if (!myMessage.equals(compilerMessage.myMessage)) return false;
+
+    return true;
+  }
+
+  public int hashCode() {
+    int result;
+    result = myCategory.hashCode();
+    result = 29 * result + myMessage.hashCode();
+    result = 29 * result + (myFile != null ? myFile.hashCode() : 0);
+    result = 29 * result + myRow;
+    result = 29 * result + myColumn;
+    return result;
+  }
+
+  public String toString() {
+    return myMessage;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/DependencyProcessor.java b/java/compiler/impl/src/com/intellij/compiler/DependencyProcessor.java
new file mode 100644
index 0000000..3907eb8
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/DependencyProcessor.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 com.intellij.compiler;
+
+import com.intellij.compiler.make.CacheCorruptedException;
+import com.intellij.compiler.make.CachingSearcher;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.extensions.ExtensionPointName;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Aug 19, 2008
+ */
+public interface DependencyProcessor {
+  ExtensionPointName<DependencyProcessor> EXTENSION_POINT_NAME = ExtensionPointName.create("com.intellij.compiler.makeDependencyProcessor");
+  
+  void processDependencies(CompileContext context, int classQualifiedName, CachingSearcher searcher) throws CacheCorruptedException;
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/HelpID.java b/java/compiler/impl/src/com/intellij/compiler/HelpID.java
new file mode 100644
index 0000000..9b34a51
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/HelpID.java
@@ -0,0 +1,23 @@
+/*
+ * 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 com.intellij.compiler;
+
+import org.jetbrains.annotations.NonNls;
+
+public interface HelpID {
+  @NonNls  String  COMPILER = "reference.toolWindows.messages";
+  @NonNls  String  GENERATE_ANT_BUILD = "editing.generateAntBuild";
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/OutputParser.java b/java/compiler/impl/src/com/intellij/compiler/OutputParser.java
new file mode 100644
index 0000000..37f6882
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/OutputParser.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 com.intellij.compiler;
+
+import com.intellij.compiler.impl.javaCompiler.FileObject;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NonNls;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class OutputParser {
+  protected final List<ParserAction> myParserActions = new ArrayList<ParserAction>(10);
+
+  public interface Callback {
+    @NonNls String getNextLine();        
+    @NonNls String getCurrentLine();
+    void pushBack(String line);
+    void setProgressText(String text);
+    void fileProcessed(@NonNls String path);
+    void fileGenerated(@NonNls FileObject path);
+    void message(CompilerMessageCategory category, String message, @NonNls String url, int lineNum, int columnNum);
+  }
+
+  public boolean processMessageLine(Callback callback) {
+    final String line = callback.getNextLine();
+    if(line == null) {
+      return false;
+    }
+    // common statistics messages (javac & jikes)
+    for (ParserAction action : myParserActions) {
+      if (action.execute(line, callback)) {
+        return true;
+      }
+    }
+    if (StringUtil.startsWithChar(line, '[') && StringUtil.endsWithChar(line, ']')) {
+      // at this point any meaningful output surrounded with '[' and ']' characters is processed, so
+      // suppress messages like "[total 4657ms]" or "[search path for source files: []]"
+      return true;
+    }
+    return false;
+  }
+
+  protected static void addMessage(Callback callback, CompilerMessageCategory type, String message) {
+    if(message == null || message.trim().length() == 0) {
+      return;
+    }
+    addMessage(callback, type, message, null, -1, -1);
+  }
+
+  protected static void addMessage(Callback callback, CompilerMessageCategory type, String text, String url, int line, int column){
+    callback.message(type, text, url, line, column);
+  }
+
+  public boolean isTrimLines() {
+    return true;
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/ParserAction.java b/java/compiler/impl/src/com/intellij/compiler/ParserAction.java
new file mode 100644
index 0000000..d9b0c29
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ParserAction.java
@@ -0,0 +1,26 @@
+/*
+ * 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 com.intellij.compiler;
+
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Sep 24, 2005
+ */
+public abstract class ParserAction {
+  public abstract boolean execute(@NonNls String line, final OutputParser.Callback callback);
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ProblemsView.java b/java/compiler/impl/src/com/intellij/compiler/ProblemsView.java
new file mode 100644
index 0000000..3948ac7
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ProblemsView.java
@@ -0,0 +1,93 @@
+/*
+ * 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 com.intellij.compiler;
+
+import com.intellij.compiler.progress.CompilerTask;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.CompilerMessage;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.pom.Navigatable;
+import com.intellij.util.ArrayUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.UUID;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: 9/18/12
+ */
+public abstract class ProblemsView {
+
+  private final Project myProject;
+
+  public static class SERVICE {
+    private SERVICE() {
+    }
+
+    public static ProblemsView getInstance(Project project) {
+      return ServiceManager.getService(project, ProblemsView.class);
+    }
+  }
+
+  protected ProblemsView(Project project) {
+    myProject = project;
+  }
+
+  public abstract void clearOldMessages(CompileScope scope, UUID currentSessionId);
+
+  public abstract void addMessage(int type, @NotNull String[] text, @Nullable String groupName, @Nullable Navigatable navigatable, @Nullable String exportTextPrefix, @Nullable String rendererTextPrefix, @NotNull UUID sessionId);
+
+  public final void addMessage(CompilerMessage message, @NotNull UUID sessionId) {
+    final VirtualFile file = message.getVirtualFile();
+    Navigatable navigatable = message.getNavigatable();
+    if (navigatable == null && file != null) {
+      navigatable = new OpenFileDescriptor(myProject, file, -1, -1);
+    }
+    final CompilerMessageCategory category = message.getCategory();
+    final int type = CompilerTask.translateCategory(category);
+    final String[] text = convertMessage(message);
+    final String groupName = file != null? file.getPresentableUrl() : category.getPresentableText();
+    addMessage(type, text, groupName, navigatable, message.getExportTextPrefix(), message.getRenderTextPrefix(), sessionId);
+  }
+
+  public abstract void setProgress(String text, float fraction);
+  
+  public abstract void setProgress(String text);
+
+  public abstract void clearProgress();
+
+  private static String[] convertMessage(final CompilerMessage message) {
+    String text = message.getMessage();
+    if (!text.contains("\n")) {
+      return new String[]{text};
+    }
+    final List<String> lines = new ArrayList<String>();
+    StringTokenizer tokenizer = new StringTokenizer(text, "\n", false);
+    while (tokenizer.hasMoreTokens()) {
+      lines.add(tokenizer.nextToken());
+    }
+    return ArrayUtil.toStringArray(lines);
+  }
+  
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/PsiClassWriter.java b/java/compiler/impl/src/com/intellij/compiler/PsiClassWriter.java
new file mode 100644
index 0000000..61d1e05
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/PsiClassWriter.java
@@ -0,0 +1,88 @@
+/*
+ * 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 com.intellij.compiler;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.JavaSdk;
+import com.intellij.openapi.projectRoots.JavaSdkVersion;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.util.Computable;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.util.ClassUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.asm4.ClassWriter;
+
+/**
+ * @author yole
+ */
+public class PsiClassWriter extends ClassWriter {
+  private final Project myProject;
+
+  public PsiClassWriter(final Project project, boolean isJava6) {
+    super(isJava6 ? ClassWriter.COMPUTE_FRAMES : ClassWriter.COMPUTE_MAXS);
+    myProject = project;
+  }
+
+  public PsiClassWriter(final Module module) {
+    this(module.getProject(), isJdk6(module));
+  }
+
+  private static boolean isJdk6(final Module module) {
+    final Sdk projectJdk = ModuleRootManager.getInstance(module).getSdk();
+    if (projectJdk == null) return false;
+    return JavaSdk.getInstance().isOfVersionOrHigher(projectJdk, JavaSdkVersion.JDK_1_6);
+  }
+
+  protected String getCommonSuperClass(final String type1, final String type2) {
+    //PsiManager.getInstance(myProject).findClass(type1.replace('/', '.').replace('$', '.'), myProject.getAllScope());
+    return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
+      @NonNls
+      public String compute() {
+        final PsiManager manager = PsiManager.getInstance(myProject);
+        PsiClass c = ClassUtil.findPsiClassByJVMName(manager, type1);
+        if (c == null) {
+          return "java/lang/Object";
+        }
+        PsiClass d = ClassUtil.findPsiClassByJVMName(manager, type2);
+        if (d == null) {
+          return "java/lang/Object";
+        }
+        if (c.isInheritor(d, true)) {
+          return ClassUtil.getJVMClassName(d).replace('.', '/');
+        }
+        if (d.isInheritor(c, true)) {
+          return ClassUtil.getJVMClassName(c).replace('.', '/');
+        }
+        if (c.isInterface() || d.isInterface()) {
+          return "java/lang/Object";
+        }
+        do {
+          c = c.getSuperClass();
+        }
+        while (c != null && !d.isInheritor(c, true));
+        if (c == null) {
+          return "java/lang/Object";
+        }
+        return ClassUtil.getJVMClassName(c).replace('.', '/');
+      }
+    });
+  }
+
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/SymbolTable.java b/java/compiler/impl/src/com/intellij/compiler/SymbolTable.java
new file mode 100644
index 0000000..48603f5
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/SymbolTable.java
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: Mar 3, 2003
+ * Time: 12:34:44 PM
+ */
+package com.intellij.compiler;
+
+import com.intellij.compiler.make.CacheCorruptedException;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.util.containers.SLRUCache;
+import com.intellij.util.io.PersistentEnumerator;
+import com.intellij.util.io.PersistentStringEnumerator;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.IOException;
+
+public class SymbolTable {
+  private final PersistentStringEnumerator myTrie;
+
+  // both caches should have equal size
+  private static final int STRING_CACHE_SIZE = 1024;
+
+  private final SLRUCache<Integer, String> myIndexStringCache = new SLRUCache<Integer, String>(STRING_CACHE_SIZE * 2, STRING_CACHE_SIZE) {
+    @NotNull
+    public String createValue(Integer key) {
+      try {
+        return myTrie.valueOf(key.intValue());
+      }
+      catch (IOException e) {
+        throw new RuntimeException(e);
+      }
+    }
+  };
+  
+  private final SLRUCache<String, Integer> myStringIndexCache = new SLRUCache<String, Integer>(STRING_CACHE_SIZE * 2, STRING_CACHE_SIZE) {
+    @NotNull
+    public Integer createValue(String key) {
+      try {
+        return myTrie.enumerate(key);
+      }
+      catch (IOException e) {
+        throw new RuntimeException(e);
+      }
+    }
+  };
+
+
+  public SymbolTable(File file) throws CacheCorruptedException {
+    try {
+      if (!file.exists()) {
+        FileUtil.createIfDoesntExist(file);
+      }
+      myTrie = new PersistentStringEnumerator(file);
+    }
+    catch (PersistentEnumerator.CorruptedException e) {
+      throw new CacheCorruptedException(CompilerBundle.message("error.compiler.caches.corrupted"), e);
+    }
+    catch (IOException e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public synchronized int getId(@NotNull String symbol) throws CacheCorruptedException {
+    if (symbol.length() == 0) {
+      return -1;
+    }
+    try {
+      return myStringIndexCache.get(symbol);
+    }
+    catch (RuntimeException e) {
+      if (e.getCause() instanceof IOException) {
+        throw new CacheCorruptedException(e.getCause());
+      }
+      throw e;
+    }
+  }
+
+  public synchronized String getSymbol(int id) throws CacheCorruptedException {
+    if (id == -1) {
+      return "";
+    }
+    try {
+      return myIndexStringCache.get(id);
+    }
+    catch (RuntimeException e) {
+      if (e.getCause() instanceof IOException) {
+        throw new CacheCorruptedException(e.getCause());
+      }
+      throw e;
+    }
+  }
+
+  public synchronized void dispose() throws CacheCorruptedException {
+    try {
+      myIndexStringCache.clear();
+      myStringIndexCache.clear();
+      myTrie.close(); // will call "flush()" if needed
+    }
+    catch (IOException e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/actions/AntBuildForm.form b/java/compiler/impl/src/com/intellij/compiler/actions/AntBuildForm.form
new file mode 100644
index 0000000..da94634
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/actions/AntBuildForm.form
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.compiler.actions.GenerateAntBuildDialog">
+  <grid id="3d49c" binding="myPanel" layout-manager="GridLayoutManager" row-count="6" 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="52" y="58" width="727" height="229"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <grid id="770dc" layout-manager="GridLayoutManager" row-count="3" column-count="4" 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="7d65" class="javax.swing.JRadioButton" binding="myRbGenerateMultipleFilesBuild">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <text resource-bundle="messages/CompilerBundle" key="label.generate.ant.script.generate.multiple.files"/>
+            </properties>
+          </component>
+          <component id="f1821" class="javax.swing.JRadioButton" binding="myRbGenerateSingleFileBuild">
+            <constraints>
+              <grid row="1" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <text resource-bundle="messages/CompilerBundle" key="label.generate.ant.script.single.file"/>
+            </properties>
+          </component>
+          <component id="81381" class="javax.swing.JRadioButton" binding="myRbBackupFiles">
+            <constraints>
+              <grid row="0" column="3" 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="messages/CompilerBundle" key="label.generate.ant.script.backup.files"/>
+            </properties>
+          </component>
+          <component id="a019e" class="javax.swing.JRadioButton" binding="myRbOverwriteFiles">
+            <constraints>
+              <grid row="1" column="3" 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="messages/CompilerBundle" key="label.generate.ant.script.overwrite.files"/>
+            </properties>
+          </component>
+          <component id="cd13a" class="javax.swing.JTextField" binding="myOutputFileNameField">
+            <constraints>
+              <grid row="2" column="2" 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="3a774" class="javax.swing.JLabel">
+            <constraints>
+              <grid row="2" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <text resource-bundle="messages/CompilerBundle" key="label.generate.ant.script.filename"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <component id="ec5e9" class="javax.swing.JCheckBox" binding="myCbEnableUIFormsCompilation">
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="9" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text resource-bundle="messages/CompilerBundle" key="label.generate.ant.script.enable.ui.forms.compilation"/>
+        </properties>
+      </component>
+      <xy id="6f3e8" binding="myChunksPanel" layout-manager="XYLayout" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="5" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children/>
+      </xy>
+      <component id="c9ba" class="javax.swing.JCheckBox" binding="myCbForceTargetJdk">
+        <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="messages/CompilerBundle" key="label.generate.ant.script.use.jdk.definitions"/>
+        </properties>
+      </component>
+      <component id="ca33b" class="javax.swing.JCheckBox" binding="myCbInlineRuntimeClasspath">
+        <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="messages/CompilerBundle" key="label.generate.ant.script.inline.runtime.classpaths"/>
+        </properties>
+      </component>
+      <component id="c612c" class="javax.swing.JCheckBox" binding="myGenerateIdeaHomeProperty" default-binding="true">
+        <constraints>
+          <grid row="4" 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>
+          <selected value="false"/>
+          <text resource-bundle="messages/CompilerBundle" key="label.generate.ant.script.generate.idea.home"/>
+        </properties>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/java/compiler/impl/src/com/intellij/compiler/actions/ArtifactAwareProjectSettingsService.java b/java/compiler/impl/src/com/intellij/compiler/actions/ArtifactAwareProjectSettingsService.java
new file mode 100644
index 0000000..f84bc59
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/actions/ArtifactAwareProjectSettingsService.java
@@ -0,0 +1,26 @@
+/*
+ * 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 com.intellij.compiler.actions;
+
+import com.intellij.packaging.artifacts.Artifact;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public interface ArtifactAwareProjectSettingsService {
+  void openArtifactSettings(@Nullable Artifact artifact);
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/actions/BuildArtifactAction.java b/java/compiler/impl/src/com/intellij/compiler/actions/BuildArtifactAction.java
new file mode 100644
index 0000000..6f660d1
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/actions/BuildArtifactAction.java
@@ -0,0 +1,369 @@
+/*
+ * 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 com.intellij.compiler.actions;
+
+import com.intellij.notification.NotificationGroup;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonShortcuts;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.keymap.KeymapUtil;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.ui.popup.ListSeparator;
+import com.intellij.openapi.ui.popup.MultiSelectionListPopupStep;
+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.Pair;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.impl.compiler.ArtifactCompileScope;
+import com.intellij.packaging.impl.compiler.ArtifactsWorkspaceSettings;
+import com.intellij.ui.popup.list.ListPopupImpl;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.ui.EmptyIcon;
+import gnu.trove.TIntArrayList;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class BuildArtifactAction extends DumbAwareAction {
+  private static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.balloonGroup("Clean artifact");
+
+  public BuildArtifactAction() {
+    super("Build Artifacts...", "Select and build artifacts configured in the project", null);
+  }
+  @Override
+  public void update(AnActionEvent e) {
+    final Project project = getEventProject(e);
+    final Presentation presentation = e.getPresentation();
+    presentation.setEnabled(project != null && !ArtifactUtil.getArtifactWithOutputPaths(project).isEmpty());
+  }
+
+  @Override
+  public void actionPerformed(AnActionEvent e) {
+    final Project project = getEventProject(e);
+    if (project == null) return;
+
+    final List<Artifact> artifacts = ArtifactUtil.getArtifactWithOutputPaths(project);
+    if (artifacts.isEmpty()) return;
+
+    List<ArtifactPopupItem> items = new ArrayList<ArtifactPopupItem>();
+    if (artifacts.size() > 1) {
+      items.add(0, new ArtifactPopupItem(null, "All Artifacts", EmptyIcon.ICON_16));
+    }
+    Set<Artifact> selectedArtifacts = new HashSet<Artifact>(ArtifactsWorkspaceSettings.getInstance(project).getArtifactsToBuild());
+    TIntArrayList selectedIndices = new TIntArrayList();
+    if (Comparing.haveEqualElements(artifacts, selectedArtifacts) && selectedArtifacts.size() > 1) {
+      selectedIndices.add(0);
+      selectedArtifacts.clear();
+    }
+    
+    for (Artifact artifact : artifacts) {
+      final ArtifactPopupItem item = new ArtifactPopupItem(artifact, artifact.getName(), artifact.getArtifactType().getIcon());
+      if (selectedArtifacts.contains(artifact)) {
+        selectedIndices.add(items.size());
+      }
+      items.add(item);
+    }
+    
+    final ProjectSettingsService projectSettingsService = ProjectSettingsService.getInstance(project);
+    final ArtifactAwareProjectSettingsService settingsService = projectSettingsService instanceof ArtifactAwareProjectSettingsService ? (ArtifactAwareProjectSettingsService)projectSettingsService : null;
+    
+    final ChooseArtifactStep step = new ChooseArtifactStep(items, artifacts.get(0), project, settingsService);
+    step.setDefaultOptionIndices(selectedIndices.toNativeArray());
+
+    final ListPopupImpl popup = (ListPopupImpl)JBPopupFactory.getInstance().createListPopup(step);
+    final KeyStroke editKeyStroke = KeymapUtil.getKeyStroke(CommonShortcuts.getEditSource());
+    if (settingsService != null && editKeyStroke != null) {
+      popup.registerAction("editArtifact", editKeyStroke, new AbstractAction() {
+        @Override
+        public void actionPerformed(ActionEvent e) {
+          Object[] values = popup.getSelectedValues();
+          popup.cancel();
+          settingsService.openArtifactSettings(values.length > 0 ? ((ArtifactPopupItem)values[0]).getArtifact() : null);
+        }
+      });
+    }
+    popup.showCenteredInCurrentWindow(project);
+  }
+
+  protected static void doBuild(@NotNull Project project, final @NotNull List<ArtifactPopupItem> items, boolean rebuild) {
+    final Set<Artifact> artifacts = getArtifacts(items, project);
+    final CompileScope scope = ArtifactCompileScope.createArtifactsScope(project, artifacts);
+
+    ArtifactsWorkspaceSettings.getInstance(project).setArtifactsToBuild(artifacts);
+    if (!rebuild) {
+      CompilerManager.getInstance(project).make(scope, null);
+    }
+    else {
+      CompilerManager.getInstance(project).compile(scope, null);
+    }
+  }
+
+  private static Set<Artifact> getArtifacts(final List<ArtifactPopupItem> items, final Project project) {
+    Set<Artifact> artifacts = new LinkedHashSet<Artifact>();
+    for (ArtifactPopupItem item : items) {
+      artifacts.addAll(item.getArtifacts(project));
+    }
+    return artifacts;
+  }
+
+  private static class BuildArtifactItem extends ArtifactActionItem {
+    private BuildArtifactItem(List<ArtifactPopupItem> item, Project project) {
+      super(item, project, "Build");
+    }
+
+    @Override
+    public void run() {
+      doBuild(myProject, myArtifactPopupItems, false);
+    }
+  }
+
+  private static class CleanArtifactItem extends ArtifactActionItem {
+    private CleanArtifactItem(@NotNull List<ArtifactPopupItem> item, @NotNull Project project) {
+      super(item, project, "Clean");
+    }
+
+    @Override
+    public void run() {
+      Set<VirtualFile> parents = new HashSet<VirtualFile>();
+      final VirtualFile[] roots = ProjectRootManager.getInstance(myProject).getContentSourceRoots();
+      for (VirtualFile root : roots) {
+        VirtualFile parent = root;
+        while (parent != null && !parents.contains(parent)) {
+          parents.add(parent);
+          parent = parent.getParent();
+        }
+      }
+
+      Map<String, String> outputPathContainingSourceRoots = new HashMap<String, String>();
+      final List<Pair<File, Artifact>> toClean = new ArrayList<Pair<File, Artifact>>();
+      Set<Artifact> artifacts = getArtifacts(myArtifactPopupItems, myProject);
+      for (Artifact artifact : artifacts) {
+        String outputPath = artifact.getOutputFilePath();
+        if (outputPath != null) {
+          toClean.add(Pair.create(new File(FileUtil.toSystemDependentName(outputPath)), artifact));
+          final VirtualFile outputFile = LocalFileSystem.getInstance().findFileByPath(outputPath);
+          if (parents.contains(outputFile)) {
+            outputPathContainingSourceRoots.put(artifact.getName(), outputPath);
+          }
+        }
+      }
+
+      if (!outputPathContainingSourceRoots.isEmpty()) {
+        final String message;
+        if (outputPathContainingSourceRoots.size() == 1 && outputPathContainingSourceRoots.values().size() == 1) {
+          final String name = ContainerUtil.getFirstItem(outputPathContainingSourceRoots.keySet());
+          final String output = outputPathContainingSourceRoots.get(name);
+          message = "The output directory '" + output + "' of '" + name + "' artifact contains source roots of the project. Do you want to continue and clear it?";
+        }
+        else {
+          StringBuilder info = new StringBuilder();
+          for (String name : outputPathContainingSourceRoots.keySet()) {
+            info.append(" '").append(name).append("' artifact ('").append(outputPathContainingSourceRoots.get(name)).append("')\n");
+          }
+          message = "The output directories of the following artifacts contains source roots:\n" +
+                    info + "Do you want to continue and clear these directories?";
+        }
+        final int answer = Messages.showYesNoDialog(myProject, message, "Clean Artifacts", null);
+        if (answer != 0) {
+          return;
+        }
+      }
+
+      new Task.Backgroundable(myProject, "Cleaning artifacts...", true) {
+        @Override
+        public void run(@NotNull ProgressIndicator indicator) {
+          List<File> deleted = new ArrayList<File>();
+          for (Pair<File, Artifact> pair : toClean) {
+            indicator.checkCanceled();
+            File file = pair.getFirst();
+            if (!FileUtil.delete(file)) {
+              NOTIFICATION_GROUP.createNotification("Cannot clean '" + pair.getSecond().getName() + "' artifact", "cannot delete '" + file.getAbsolutePath() + "'", NotificationType.ERROR, null).notify(myProject);
+            }
+            else {
+              deleted.add(file);
+            }
+          }
+          LocalFileSystem.getInstance().refreshIoFiles(deleted, true, true, null);
+        }
+      }.queue();
+    }
+  }
+
+  private static class RebuildArtifactItem extends ArtifactActionItem {
+    private RebuildArtifactItem(List<ArtifactPopupItem> item, Project project) {
+      super(item, project, "Rebuild");
+    }
+
+    @Override
+    public void run() {
+      doBuild(myProject, myArtifactPopupItems, true);
+    }
+  }
+
+  private static class EditArtifactItem extends ArtifactActionItem {
+    private final ArtifactAwareProjectSettingsService mySettingsService;
+
+    private EditArtifactItem(List<ArtifactPopupItem> item, Project project, final ArtifactAwareProjectSettingsService projectSettingsService) {
+      super(item, project, "Edit...");
+      mySettingsService = projectSettingsService;
+    }
+
+    @Override
+    public void run() {
+      mySettingsService.openArtifactSettings(myArtifactPopupItems.get(0).getArtifact());
+    }
+  }
+
+  private static abstract class ArtifactActionItem implements Runnable {
+    protected final List<ArtifactPopupItem> myArtifactPopupItems;
+    protected final Project myProject;
+    private String myActionName;
+
+    protected ArtifactActionItem(@NotNull List<ArtifactPopupItem> item, @NotNull Project project, @NotNull String name) {
+      myArtifactPopupItems = item;
+      myProject = project;
+      myActionName = name;
+    }
+
+    public String getActionName() {
+      return myActionName;
+    }
+  }
+
+  private static class ArtifactPopupItem {
+    @Nullable private final Artifact myArtifact;
+    private final String myText;
+    private final Icon myIcon;
+
+    private ArtifactPopupItem(@Nullable Artifact artifact, String text, Icon icon) {
+      myArtifact = artifact;
+      myText = text;
+      myIcon = icon;
+    }
+
+    @Nullable
+    public Artifact getArtifact() {
+      return myArtifact;
+    }
+
+    public String getText() {
+      return myText;
+    }
+
+    public Icon getIcon() {
+      return myIcon;
+    }
+
+    public List<Artifact> getArtifacts(Project project) {
+      final Artifact artifact = getArtifact();
+      return artifact != null ? Collections.singletonList(artifact) : ArtifactUtil.getArtifactWithOutputPaths(project);
+    }
+  }
+  
+  private static class ChooseArtifactStep extends MultiSelectionListPopupStep<ArtifactPopupItem> {
+    private final Artifact myFirst;
+    private final Project myProject;
+    private ArtifactAwareProjectSettingsService mySettingsService;
+
+    public ChooseArtifactStep(List<ArtifactPopupItem> artifacts,
+                              Artifact first,
+                              Project project, final ArtifactAwareProjectSettingsService settingsService) {
+      super("Build Artifact", artifacts);
+      myFirst = first;
+      myProject = project;
+      mySettingsService = settingsService;
+    }
+
+    @Override
+    public boolean isSpeedSearchEnabled() {
+      return true;
+    }
+
+    @Override
+    public Icon getIconFor(ArtifactPopupItem aValue) {
+      return aValue.getIcon();
+    }
+
+    @NotNull
+    @Override
+    public String getTextFor(ArtifactPopupItem value) {
+      return value.getText();
+    }
+
+    @Override
+    public boolean hasSubstep(List<ArtifactPopupItem> selectedValues) {
+      return true;
+    }
+
+    @Override
+    public ListSeparator getSeparatorAbove(ArtifactPopupItem value) {
+      return myFirst.equals(value.getArtifact()) ? new ListSeparator() : null;
+    }
+
+    @Override
+    public PopupStep<?> onChosen(final List<ArtifactPopupItem> selectedValues, boolean finalChoice) {
+      if (finalChoice) {
+        return doFinalStep(new Runnable() {
+          @Override
+          public void run() {
+            doBuild(myProject, selectedValues, false);
+          }
+        });
+      }
+      final List<ArtifactActionItem> actions = new ArrayList<ArtifactActionItem>();
+      actions.add(new BuildArtifactItem(selectedValues, myProject));
+      actions.add(new RebuildArtifactItem(selectedValues, myProject));
+      actions.add(new CleanArtifactItem(selectedValues, myProject));
+      if (mySettingsService != null) {
+        actions.add(new EditArtifactItem(selectedValues, myProject, mySettingsService));
+      }
+      return new BaseListPopupStep<ArtifactActionItem>(selectedValues.size() == 1 ? "Action" : "Action for " + selectedValues.size() + " artifacts", actions) {
+        @NotNull
+        @Override
+        public String getTextFor(ArtifactActionItem value) {
+          return value.getActionName();
+        }
+
+        @Override
+        public PopupStep onChosen(ArtifactActionItem selectedValue, boolean finalChoice) {
+          return doFinalStep(selectedValue);
+        }
+      };
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/actions/CompileAction.java b/java/compiler/impl/src/com/intellij/compiler/actions/CompileAction.java
new file mode 100644
index 0000000..2fe4bcd
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/actions/CompileAction.java
@@ -0,0 +1,196 @@
+/*
+ * 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 com.intellij.compiler.actions;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.idea.ActionsBundle;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.impl.artifacts.ArtifactBySourceFileFinder;
+import com.intellij.psi.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class CompileAction extends CompileActionBase {
+  protected void doAction(DataContext dataContext, Project project) {
+    final Module module = LangDataKeys.MODULE_CONTEXT.getData(dataContext);
+    if (module != null) {
+      CompilerManager.getInstance(project).compile(module, null);
+    }
+    else {
+      VirtualFile[] files = getCompilableFiles(project, PlatformDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext));
+      if (files.length > 0) {
+        CompilerManager.getInstance(project).compile(files, null);
+      }
+    }
+
+  }
+
+  public void update(AnActionEvent event) {
+    super.update(event);
+    Presentation presentation = event.getPresentation();
+    if (!presentation.isEnabled()) {
+      return;
+    }
+    DataContext dataContext = event.getDataContext();
+
+    presentation.setText(ActionsBundle.actionText(IdeActions.ACTION_COMPILE));
+    presentation.setVisible(true);
+
+    Project project = PlatformDataKeys.PROJECT.getData(dataContext);
+    if (project == null) {
+      presentation.setEnabled(false);
+      return;
+    }
+
+    CompilerConfiguration compilerConfiguration = CompilerConfiguration.getInstance(project);
+    final Module module = LangDataKeys.MODULE_CONTEXT.getData(dataContext);
+
+    final VirtualFile[] files = getCompilableFiles(project, PlatformDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext));
+    if (module == null && files.length == 0) {
+      presentation.setEnabled(false);
+      presentation.setVisible(!ActionPlaces.isPopupPlace(event.getPlace()));
+      return;
+    }
+
+    String elementDescription = null;
+    if (module != null) {
+      elementDescription = CompilerBundle.message("action.compile.description.module", module.getName());
+    }
+    else {
+      PsiPackage aPackage = null;
+      if (files.length == 1) {
+        final PsiDirectory directory = PsiManager.getInstance(project).findDirectory(files[0]);
+        if (directory != null) {
+          aPackage = JavaDirectoryService.getInstance().getPackage(directory);
+        }
+      }
+      else {
+        PsiElement element = LangDataKeys.PSI_ELEMENT.getData(dataContext);
+        if (element instanceof PsiPackage) {
+          aPackage = (PsiPackage)element;
+        }
+      }
+
+      if (aPackage != null) {
+        String name = aPackage.getQualifiedName();
+        if (name.length() == 0) {
+          //noinspection HardCodedStringLiteral
+          name = "<default>";
+        }
+        elementDescription = "'" + name + "'";
+      }
+      else if (files.length == 1) {
+        final VirtualFile file = files[0];
+        FileType fileType = file.getFileType();
+        if (CompilerManager.getInstance(project).isCompilableFileType(fileType) || isCompilableResourceFile(project, compilerConfiguration, file)) {
+          elementDescription = "'" + file.getName() + "'";
+        }
+        else {
+          if (!ActionPlaces.MAIN_MENU.equals(event.getPlace())) {
+            // the action should be invisible in popups for non-java files
+            presentation.setEnabled(false);
+            presentation.setVisible(false);
+            return;
+          }
+        }
+      }
+      else {
+        elementDescription = CompilerBundle.message("action.compile.description.selected.files");
+      }
+    }
+
+    if (elementDescription == null) {
+      presentation.setEnabled(false);
+      return;
+    }
+
+    presentation.setText(createPresentationText(elementDescription), true);
+    presentation.setEnabled(true);
+  }
+
+  private static String createPresentationText(String elementDescription) {
+    StringBuffer buffer = new StringBuffer(40);
+    buffer.append(ActionsBundle.actionText(IdeActions.ACTION_COMPILE)).append(" ");
+    int length = elementDescription.length();
+    if (length > 23) {
+      if (StringUtil.startsWithChar(elementDescription, '\'')) {
+        buffer.append("'");
+      }
+      buffer.append("...");
+      buffer.append(elementDescription.substring(length - 20, length));
+    }
+    else {
+      buffer.append(elementDescription);
+    }
+    return buffer.toString();
+  }
+
+  private static VirtualFile[] getCompilableFiles(Project project, VirtualFile[] files) {
+    if (files == null || files.length == 0) {
+      return VirtualFile.EMPTY_ARRAY;
+    }
+    final PsiManager psiManager = PsiManager.getInstance(project);
+    final CompilerConfiguration compilerConfiguration = CompilerConfiguration.getInstance(project);
+    final FileTypeManager typeManager = FileTypeManager.getInstance();
+    final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
+    final CompilerManager compilerManager = CompilerManager.getInstance(project);
+    final List<VirtualFile> filesToCompile = new ArrayList<VirtualFile>();
+    for (final VirtualFile file : files) {
+      if (!fileIndex.isInSourceContent(file)) {
+        continue;
+      }
+      if (!file.isInLocalFileSystem()) {
+        continue;
+      }
+      if (file.isDirectory()) {
+        final PsiDirectory directory = psiManager.findDirectory(file);
+        if (directory == null || JavaDirectoryService.getInstance().getPackage(directory) == null) {
+          continue;
+        }
+      }
+      else {
+        FileType fileType = file.getFileType();
+        if (!(compilerManager.isCompilableFileType(fileType) || isCompilableResourceFile(project, compilerConfiguration, file))) {
+          continue;
+        }
+      }
+      filesToCompile.add(file);
+    }
+    return VfsUtil.toVirtualFileArray(filesToCompile);
+  }
+
+  private static boolean isCompilableResourceFile(final Project project, final CompilerConfiguration compilerConfiguration, final VirtualFile file) {
+    if (!compilerConfiguration.isResourceFile(file)) {
+      return false;
+    }
+    final Collection<? extends Artifact> artifacts = ArtifactBySourceFileFinder.getInstance(project).findArtifacts(file);
+    return artifacts.isEmpty();
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/actions/CompileActionBase.java b/java/compiler/impl/src/com/intellij/compiler/actions/CompileActionBase.java
new file mode 100644
index 0000000..905dd92
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/actions/CompileActionBase.java
@@ -0,0 +1,54 @@
+/*
+ * 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 com.intellij.compiler.actions;
+
+import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.DumbService;
+import com.intellij.psi.PsiFile;
+
+public abstract class CompileActionBase extends AnAction implements DumbAware {
+  public void actionPerformed(AnActionEvent e) {
+    final DataContext dataContext = e.getDataContext();
+    final Project project = e.getData(PlatformDataKeys.PROJECT);
+    if (project == null) {
+      return;
+    }
+    Editor editor = e.getData(PlatformDataKeys.EDITOR);
+    PsiFile file = e.getData(LangDataKeys.PSI_FILE);
+    if (file != null && editor != null && !DumbService.getInstance(project).isDumb()) {
+      DaemonCodeAnalyzer.getInstance(project).autoImportReferenceAtCursor(editor, file); //let autoimport complete
+    }
+    doAction(dataContext, project);
+  }
+
+  protected abstract void doAction(final DataContext dataContext, final Project project);
+
+  public void update(final AnActionEvent e) {
+    super.update(e);
+    final Project project = e.getData(PlatformDataKeys.PROJECT);
+    if (project == null) {
+      e.getPresentation().setEnabled(false);
+    }
+    else {
+      e.getPresentation().setEnabled(!CompilerManager.getInstance(project).isCompilationActive());
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/actions/CompileDirtyAction.java b/java/compiler/impl/src/com/intellij/compiler/actions/CompileDirtyAction.java
new file mode 100644
index 0000000..8bea288
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/actions/CompileDirtyAction.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 com.intellij.compiler.actions;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.project.Project;
+
+public class CompileDirtyAction extends CompileActionBase {
+
+  protected void doAction(DataContext dataContext, Project project) {
+    CompilerManager.getInstance(project).make(null);
+  }
+
+  public void update(AnActionEvent event){
+    super.update(event);
+    Presentation presentation = event.getPresentation();
+    if (!presentation.isEnabled()) {
+      return;
+    }
+    presentation.setEnabled(PlatformDataKeys.PROJECT.getData(event.getDataContext()) != null);
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/actions/CompileProjectAction.java b/java/compiler/impl/src/com/intellij/compiler/actions/CompileProjectAction.java
new file mode 100644
index 0000000..fde4f55
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/actions/CompileProjectAction.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.
+ */
+package com.intellij.compiler.actions;
+
+import com.intellij.history.LocalHistory;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompileStatusNotification;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.project.Project;
+
+public class CompileProjectAction extends CompileActionBase {
+  protected void doAction(DataContext dataContext, final Project project) {
+    CompilerManager.getInstance(project).rebuild(new CompileStatusNotification() {
+      public void finished(boolean aborted, int errors, int warnings, final CompileContext compileContext) {
+        if (aborted) return;
+
+        String text = getTemplatePresentation().getText();
+        LocalHistory.getInstance().putSystemLabel(project, errors == 0
+                                       ? CompilerBundle.message("rebuild.lvcs.label.no.errors", text)
+                                       : CompilerBundle.message("rebuild.lvcs.label.with.errors", text));
+      }
+    });
+  }
+
+  public void update(AnActionEvent event) {
+    super.update(event);
+    Presentation presentation = event.getPresentation();
+    if (!presentation.isEnabled()) {
+      return;
+    }
+    Project project = PlatformDataKeys.PROJECT.getData(event.getDataContext());
+    presentation.setEnabled(project != null);
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/actions/GenerateAntBuildAction.java b/java/compiler/impl/src/com/intellij/compiler/actions/GenerateAntBuildAction.java
new file mode 100644
index 0000000..8af49a5
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/actions/GenerateAntBuildAction.java
@@ -0,0 +1,359 @@
+/*
+ * 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 com.intellij.compiler.actions;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.CompilerConfigurationImpl;
+import com.intellij.compiler.ant.*;
+import com.intellij.compiler.impl.CompilerUtil;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.ReadonlyStatusHandler;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.*;
+import java.util.*;
+
+public class GenerateAntBuildAction extends CompileActionBase {
+  @NonNls private static final String XML_EXTENSION = ".xml";
+
+  protected void doAction(DataContext dataContext, final Project project) {
+    ((CompilerConfigurationImpl)CompilerConfiguration.getInstance(project)).convertPatterns();
+    final GenerateAntBuildDialog dialog = new GenerateAntBuildDialog(project);
+    dialog.show();
+    if (dialog.isOK()) {
+      final String[] names = dialog.getRepresentativeModuleNames();
+      final GenerationOptionsImpl[] genOptions = {null};
+      Runnable runnable = new Runnable() {
+        public void run() {
+          genOptions[0] = new GenerationOptionsImpl(project, dialog.isGenerateSingleFileBuild(), dialog.isFormsCompilationEnabled(),
+                                                    dialog.isBackupFiles(), dialog.isForceTargetJdk(), dialog.isRuntimeClasspathInlined(),
+                                                    dialog.isIdeaHomeGenerated(), names, dialog.getOutputFileName());
+        }
+      };
+      if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(runnable, "Analyzing project structure...", true, project)) {
+        return;
+      }
+      if (!validateGenOptions(project, genOptions[0])) {
+        return;
+      }
+      generate(project, genOptions[0]);
+    }
+  }
+
+  /**
+   * Validate generation options and notify user about possible problems
+   *
+   * @param project    a context project
+   * @param genOptions a generation optiosn
+   * @return true if the generator should proceed with current options or if there is not conflict.
+   */
+  private static boolean validateGenOptions(Project project, GenerationOptionsImpl genOptions) {
+    final Collection<String> EMPTY = Collections.emptyList();
+    Collection<String> conflicts = EMPTY;
+    for (ModuleChunk chunk : genOptions.getModuleChunks()) {
+      final ChunkCustomCompilerExtension[] customeCompilers = chunk.getCustomCompilers();
+      if (customeCompilers.length > 1) {
+        if (conflicts == EMPTY) {
+          conflicts = new LinkedList<String>();
+        }
+        conflicts.add(chunk.getName());
+      }
+    }
+    if (!conflicts.isEmpty()) {
+      StringBuilder msg = new StringBuilder();
+      for (String conflictingChunk : conflicts) {
+        msg.append(CompilerBundle.message("generate.ant.build.custom.compiler.conflict.message.row", conflictingChunk));
+      }
+      int rc = Messages
+        .showOkCancelDialog(project, CompilerBundle.message("generate.ant.build.custom.compiler.conflict.message", msg.toString()),
+                            CompilerBundle.message("generate.ant.build.custom.compiler.conflict.title"), Messages.getErrorIcon());
+      if (rc != 0) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  public void update(AnActionEvent event) {
+    Presentation presentation = event.getPresentation();
+    Project project = PlatformDataKeys.PROJECT.getData(event.getDataContext());
+    presentation.setEnabled(project != null);
+  }
+
+  private void generate(final Project project, final GenerationOptions genOptions) {
+    ApplicationManager.getApplication().saveAll();
+    final List<File> filesToRefresh = new ArrayList<File>();
+    final IOException[] _ex = new IOException[]{null};
+    final List<File> _generated = new ArrayList<File>();
+
+    try {
+      if (genOptions.generateSingleFile) {
+        final File projectBuildFileDestDir = VfsUtil.virtualToIoFile(project.getBaseDir());
+        final File destFile = new File(projectBuildFileDestDir, genOptions.getBuildFileName());
+        final File propertiesFile = new File(projectBuildFileDestDir, genOptions.getPropertiesFileName());
+
+        ensureFilesWritable(project, new File[]{destFile, propertiesFile});
+      }
+      else {
+        final List<File> allFiles = new ArrayList<File>();
+
+        final File projectBuildFileDestDir = VfsUtil.virtualToIoFile(project.getBaseDir());
+        allFiles.add(new File(projectBuildFileDestDir, genOptions.getBuildFileName()));
+        allFiles.add(new File(projectBuildFileDestDir, genOptions.getPropertiesFileName()));
+
+        final ModuleChunk[] chunks = genOptions.getModuleChunks();
+        for (final ModuleChunk chunk : chunks) {
+          final File chunkBaseDir = BuildProperties.getModuleChunkBaseDir(chunk);
+          allFiles.add(new File(chunkBaseDir, BuildProperties.getModuleChunkBuildFileName(chunk) + XML_EXTENSION));
+        }
+
+        ensureFilesWritable(project, allFiles.toArray(new File[allFiles.size()]));
+      }
+
+      new Task.Modal(project, CompilerBundle.message("generate.ant.build.title"), false) {
+        public void run(@NotNull final ProgressIndicator indicator) {
+          indicator.setIndeterminate(true);
+          indicator.setText(CompilerBundle.message("generate.ant.build.progress.message"));
+          try {
+            final File[] generated;
+            if (genOptions.generateSingleFile) {
+              generated = generateSingleFileBuild(project, genOptions, filesToRefresh);
+            }
+            else {
+              generated = generateMultipleFileBuild(project, genOptions, filesToRefresh);
+            }
+            if (generated != null) {
+              ContainerUtil.addAll(_generated, generated);
+            }
+          }
+          catch (IOException e) {
+            _ex[0] = e;
+          }
+        }
+      }.queue();
+
+    }
+    catch (IOException e) {
+      _ex[0] = e;
+    }
+
+    if (_ex[0] != null) {
+      Messages.showErrorDialog(project, CompilerBundle.message("error.ant.files.generate.failed", _ex[0].getMessage()),
+                               CompilerBundle.message("generate.ant.build.title"));
+    }
+    else {
+      StringBuffer filesString = new StringBuffer();
+      for (int idx = 0; idx < _generated.size(); idx++) {
+        final File file = _generated.get(idx);
+        if (idx > 0) {
+          filesString.append(",\n");
+        }
+        filesString.append(file.getPath());
+      }
+      Messages.showInfoMessage(project, CompilerBundle.message("message.ant.files.generated.ok", filesString.toString()),
+                               CompilerBundle.message("generate.ant.build.title"));
+    }
+
+    if (filesToRefresh.size() > 0) {
+      CompilerUtil.refreshIOFiles(filesToRefresh);
+    }
+  }
+
+  private boolean backup(final File file, final Project project, GenerationOptions genOptions, List<File> filesToRefresh) {
+    if (!genOptions.backupPreviouslyGeneratedFiles || !file.exists()) {
+      return true;
+    }
+    final String path = file.getPath();
+    final int extensionIndex = path.lastIndexOf(".");
+    final String extension = path.substring(extensionIndex, path.length());
+    //noinspection HardCodedStringLiteral
+    final String backupPath = path.substring(0, extensionIndex) +
+                              "_" +
+                              new Date(file.lastModified()).toString().replaceAll("\\s+", "_").replaceAll(":", "-") +
+                              extension;
+    final File backupFile = new File(backupPath);
+    boolean ok;
+    try {
+      FileUtil.rename(file, backupFile);
+      ok = true;
+    }
+    catch (IOException e) {
+      Messages.showErrorDialog(project, CompilerBundle.message("error.ant.files.backup.failed", path),
+                               CompilerBundle.message("generate.ant.build.title"));
+      ok = false;
+    }
+    filesToRefresh.add(backupFile);
+    return ok;
+  }
+
+  private File[] generateSingleFileBuild(Project project, GenerationOptions genOptions, List<File> filesToRefresh) throws IOException {
+    final File projectBuildFileDestDir = VfsUtil.virtualToIoFile(project.getBaseDir());
+    projectBuildFileDestDir.mkdirs();
+    final File destFile = new File(projectBuildFileDestDir, genOptions.getBuildFileName());
+    final File propertiesFile = new File(projectBuildFileDestDir, genOptions.getPropertiesFileName());
+
+    if (!backup(destFile, project, genOptions, filesToRefresh)) {
+      return null;
+    }
+    if (!backup(propertiesFile, project, genOptions, filesToRefresh)) {
+      return null;
+    }
+
+    generateSingleFileBuild(project, genOptions, destFile, propertiesFile);
+
+    filesToRefresh.add(destFile);
+    filesToRefresh.add(propertiesFile);
+    return new File[]{destFile, propertiesFile};
+  }
+
+  public static void generateSingleFileBuild(final Project project,
+                                             final GenerationOptions genOptions,
+                                             final File buildxmlFile,
+                                             final File propertiesFile) throws IOException {
+    FileUtil.createIfDoesntExist(buildxmlFile);
+    FileUtil.createIfDoesntExist(propertiesFile);
+    final PrintWriter dataOutput = makeWriter(buildxmlFile);
+    try {
+      new SingleFileProjectBuild(project, genOptions).generate(dataOutput);
+    }
+    finally {
+      dataOutput.close();
+    }
+    final PrintWriter propertiesOut = makeWriter(propertiesFile);
+    try {
+      new PropertyFileGeneratorImpl(project, genOptions).generate(propertiesOut);
+    }
+    finally {
+      propertiesOut.close();
+    }
+  }
+
+  /**
+   * Create print writer over file with UTF-8 encoding
+   *
+   * @param buildxmlFile a file to write to
+   * @return a created print writer
+   * @throws UnsupportedEncodingException if endcoding not found
+   * @throws FileNotFoundException        if file not found
+   */
+  private static PrintWriter makeWriter(final File buildxmlFile) throws UnsupportedEncodingException, FileNotFoundException {
+    return new PrintWriter(new OutputStreamWriter(new FileOutputStream(buildxmlFile), "UTF-8"));
+  }
+
+  private void ensureFilesWritable(Project project, File[] files) throws IOException {
+    final List<VirtualFile> toCheck = new ArrayList<VirtualFile>(files.length);
+    final LocalFileSystem lfs = LocalFileSystem.getInstance();
+    for (File file : files) {
+      final VirtualFile vFile = lfs.findFileByIoFile(file);
+      if (vFile != null) {
+        toCheck.add(vFile);
+      }
+    }
+    final ReadonlyStatusHandler.OperationStatus status =
+      ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(VfsUtil.toVirtualFileArray(toCheck));
+    if (status.hasReadonlyFiles()) {
+      throw new IOException(status.getReadonlyFilesMessage());
+    }
+  }
+
+  public File[] generateMultipleFileBuild(Project project, GenerationOptions genOptions, List<File> filesToRefresh) throws IOException {
+    final File projectBuildFileDestDir = VfsUtil.virtualToIoFile(project.getBaseDir());
+    projectBuildFileDestDir.mkdirs();
+    final List<File> generated = new ArrayList<File>();
+    final File projectBuildFile = new File(projectBuildFileDestDir, genOptions.getBuildFileName());
+    final File propertiesFile = new File(projectBuildFileDestDir, genOptions.getPropertiesFileName());
+    final ModuleChunk[] chunks = genOptions.getModuleChunks();
+
+    final File[] chunkFiles = new File[chunks.length];
+    for (int idx = 0; idx < chunks.length; idx++) {
+      final ModuleChunk chunk = chunks[idx];
+      final File chunkBaseDir = BuildProperties.getModuleChunkBaseDir(chunk);
+      chunkFiles[idx] = new File(chunkBaseDir, BuildProperties.getModuleChunkBuildFileName(chunk) + XML_EXTENSION);
+    }
+
+    if (!backup(projectBuildFile, project, genOptions, filesToRefresh)) {
+      return null;
+    }
+    if (!backup(propertiesFile, project, genOptions, filesToRefresh)) {
+      return null;
+    }
+
+    FileUtil.createIfDoesntExist(projectBuildFile);
+    final PrintWriter mainDataOutput = makeWriter(projectBuildFile);
+    try {
+      final MultipleFileProjectBuild build = new MultipleFileProjectBuild(project, genOptions);
+      build.generate(mainDataOutput);
+      generated.add(projectBuildFile);
+
+      // the sequence in which modules are imported is important cause output path properties for dependent modules should be defined first
+
+      for (int idx = 0; idx < chunks.length; idx++) {
+        final ModuleChunk chunk = chunks[idx];
+        final File chunkBuildFile = chunkFiles[idx];
+        final File chunkBaseDir = chunkBuildFile.getParentFile();
+        if (chunkBaseDir != null) {
+          chunkBaseDir.mkdirs();
+        }
+        final boolean moduleBackupOk = backup(chunkBuildFile, project, genOptions, filesToRefresh);
+        if (!moduleBackupOk) {
+          return null;
+        }
+
+        FileUtil.createIfDoesntExist(chunkBuildFile);
+        final PrintWriter out = makeWriter(chunkBuildFile);
+        try {
+          new ModuleChunkAntProject(project, chunk, genOptions).generate(out);
+          generated.add(chunkBuildFile);
+        }
+        finally {
+          out.close();
+        }
+      }
+    }
+    finally {
+      mainDataOutput.close();
+    }
+    // properties
+    final PrintWriter propertiesOut = makeWriter(propertiesFile);
+    try {
+      new PropertyFileGeneratorImpl(project, genOptions).generate(propertiesOut);
+      generated.add(propertiesFile);
+    }
+    finally {
+      propertiesOut.close();
+    }
+
+    filesToRefresh.addAll(generated);
+    return generated.toArray(new File[generated.size()]);
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/actions/GenerateAntBuildDialog.java b/java/compiler/impl/src/com/intellij/compiler/actions/GenerateAntBuildDialog.java
new file mode 100644
index 0000000..1cd1c39
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/actions/GenerateAntBuildDialog.java
@@ -0,0 +1,360 @@
+/*
+ * 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 com.intellij.compiler.actions;
+
+import com.intellij.compiler.HelpID;
+import com.intellij.compiler.ModuleCompilerUtil;
+import com.intellij.compiler.ant.BuildProperties;
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.help.HelpManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.MultiLineLabelUI;
+import com.intellij.openapi.util.Pair;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.ui.ScrollPaneFactory;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.Chunk;
+import com.intellij.util.ListWithSelection;
+import com.intellij.util.ui.ComboBoxTableCellEditor;
+import com.intellij.util.ui.Table;
+import org.jetbrains.annotations.NonNls;
+
+import javax.swing.*;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.TableCellEditor;
+import javax.swing.table.TableColumn;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 29, 2004
+ */
+public class GenerateAntBuildDialog extends DialogWrapper {
+  private JPanel myPanel;
+  private JRadioButton myRbGenerateSingleFileBuild;
+  private JRadioButton myRbGenerateMultipleFilesBuild;
+  private JCheckBox myCbEnableUIFormsCompilation;
+  private JRadioButton myRbBackupFiles;
+  private JRadioButton myRbOverwriteFiles;
+  private JCheckBox myCbForceTargetJdk;
+  private JCheckBox myCbInlineRuntimeClasspath;
+  private JPanel myChunksPanel;
+  private JCheckBox myGenerateIdeaHomeProperty;
+  private JTextField myOutputFileNameField;
+  private final Project myProject;
+  @NonNls private static final String SINGLE_FILE_PROPERTY = "GenerateAntBuildDialog.generateSingleFile";
+  @NonNls private static final String UI_FORM_PROPERTY = "GenerateAntBuildDialog.enableUiFormCompile";
+  @NonNls private static final String FORCE_TARGET_JDK_PROPERTY = "GenerateAntBuildDialog.forceTargetJdk";
+  @NonNls private static final String BACKUP_FILES_PROPERTY = "GenerateAntBuildDialog.backupFiles";
+  @NonNls private static final String INLINE_RUNTIME_CLASSPATH_PROPERTY = "GenerateAntBuildDialog.inclineRuntiemClasspath";
+  @NonNls private static final String GENERATE_IDEA_HOME_PROPERTY = "GenerateAntBuildDialog.generateIdeaHomeProperty";
+  @NonNls private static final String OUTPUT_FILE_NAME_PROPERTY = "GenerateAntBuildDialog.outputFileNameProperty";
+  private MyTableModel myTableModel;
+  private Table myTable;
+
+  public GenerateAntBuildDialog(Project project) {
+    super(project, false);
+    myProject = project;
+    setTitle(CompilerBundle.message("generate.ant.build.title"));
+    init();
+    loadSettings();
+  }
+
+  private List<Chunk<Module>> getCycleChunks() {
+    List<Chunk<Module>> chunks =
+      ModuleCompilerUtil.getSortedModuleChunks(myProject, Arrays.asList(ModuleManager.getInstance(myProject).getModules()));
+    for (Iterator<Chunk<Module>> it = chunks.iterator(); it.hasNext();) {
+      final Chunk<Module> chunk = it.next();
+      if (chunk.getNodes().size() == 1) {
+        it.remove();
+      }
+    }
+    return chunks;
+  }
+
+  private void loadSettings() {
+    final PropertiesComponent properties = PropertiesComponent.getInstance(myProject);
+    if (properties.isValueSet(SINGLE_FILE_PROPERTY)) {
+      final boolean singleFile = properties.isTrueValue(SINGLE_FILE_PROPERTY);
+      myRbGenerateSingleFileBuild.setSelected(singleFile);
+      myRbGenerateMultipleFilesBuild.setSelected(!singleFile);
+    }
+    if (properties.isValueSet(UI_FORM_PROPERTY)) {
+      myCbEnableUIFormsCompilation.setSelected(properties.isTrueValue(UI_FORM_PROPERTY));
+    }
+    if (properties.isValueSet(FORCE_TARGET_JDK_PROPERTY)) {
+      myCbForceTargetJdk.setSelected(properties.isTrueValue(FORCE_TARGET_JDK_PROPERTY));
+    }
+    if (properties.isValueSet(BACKUP_FILES_PROPERTY)) {
+      final boolean backup = properties.isTrueValue(BACKUP_FILES_PROPERTY);
+      myRbBackupFiles.setSelected(backup);
+      myRbOverwriteFiles.setSelected(!backup);
+    }
+    if (properties.isValueSet(INLINE_RUNTIME_CLASSPATH_PROPERTY)) {
+      myCbInlineRuntimeClasspath.setSelected(properties.isTrueValue(INLINE_RUNTIME_CLASSPATH_PROPERTY));
+    }
+    if (properties.isValueSet(GENERATE_IDEA_HOME_PROPERTY)) {
+      myGenerateIdeaHomeProperty.setSelected(properties.isTrueValue(GENERATE_IDEA_HOME_PROPERTY));
+    }
+    if (properties.isValueSet(OUTPUT_FILE_NAME_PROPERTY)) {
+      myOutputFileNameField.setText(properties.getValue(OUTPUT_FILE_NAME_PROPERTY));
+    }
+    else {
+      myOutputFileNameField.setText(BuildProperties.getProjectBuildFileName(myProject));
+    }
+  }
+
+  private void saveSettings() {
+    final PropertiesComponent properties = PropertiesComponent.getInstance(myProject);
+    properties.setValue(SINGLE_FILE_PROPERTY, Boolean.toString(myRbGenerateSingleFileBuild.isSelected()));
+    properties.setValue(UI_FORM_PROPERTY, Boolean.toString(myCbEnableUIFormsCompilation.isSelected()));
+    properties.setValue(FORCE_TARGET_JDK_PROPERTY, Boolean.toString(myCbForceTargetJdk.isSelected()));
+    properties.setValue(BACKUP_FILES_PROPERTY, Boolean.toString(myRbBackupFiles.isSelected()));
+    properties.setValue(INLINE_RUNTIME_CLASSPATH_PROPERTY, Boolean.toString(myCbInlineRuntimeClasspath.isSelected()));
+    properties.setValue(GENERATE_IDEA_HOME_PROPERTY, Boolean.toString(myGenerateIdeaHomeProperty.isSelected()));
+    final String outputFileName = getOutputFileName();
+    if (outputFileName.length() > 0) {
+      properties.setValue(OUTPUT_FILE_NAME_PROPERTY, outputFileName);
+    }
+    else {
+      properties.unsetValue(OUTPUT_FILE_NAME_PROPERTY);
+    }
+  }
+
+  public void dispose() {
+    saveSettings();
+    super.dispose();
+  }
+
+  protected JComponent createCenterPanel() {
+    final ButtonGroup group = new ButtonGroup();
+    group.add(myRbGenerateMultipleFilesBuild);
+    group.add(myRbGenerateSingleFileBuild);
+
+    final ButtonGroup group1 = new ButtonGroup();
+    group1.add(myRbBackupFiles);
+    group1.add(myRbOverwriteFiles);
+
+    myRbGenerateMultipleFilesBuild.setSelected(true);
+    myRbBackupFiles.setSelected(true);
+    myCbEnableUIFormsCompilation.setSelected(true);
+    myCbForceTargetJdk.setSelected(true);
+    myCbInlineRuntimeClasspath.setSelected(false);
+
+    initChunksPanel();
+
+    return myPanel;
+  }
+
+  private void initChunksPanel() {
+    List<Chunk<Module>> chunks = getCycleChunks();
+    if (chunks.isEmpty()) {
+      return;
+    }
+    myChunksPanel.setLayout(new BorderLayout());
+    myChunksPanel.setBorder(
+      IdeBorderFactory.createTitledBorder(CompilerBundle.message("generate.ant.build.dialog.cyclic.modules.table.title"),
+                                          true));
+    JLabel textLabel = new JLabel(CompilerBundle.message("generate.ant.build.dialog.cyclic.modules.table.description"));
+    textLabel.setUI(new MultiLineLabelUI());
+    textLabel.setBorder(IdeBorderFactory.createEmptyBorder(4, 4, 6, 4));
+    myChunksPanel.add(textLabel, BorderLayout.NORTH);
+
+    myTableModel = new MyTableModel(chunks);
+    myTable = new Table(myTableModel);
+    final MyTableCellRenderer cellRenderer = new MyTableCellRenderer();
+    final TableColumn nameColumn = myTable.getColumnModel().getColumn(MyTableModel.NAME_COLUMN);
+    nameColumn.setCellEditor(ComboBoxTableCellEditor.INSTANCE);
+    nameColumn.setCellRenderer(cellRenderer);
+    final TableColumn labelColumn = myTable.getColumnModel().getColumn(MyTableModel.NUMBER_COLUMN);
+    labelColumn.setCellRenderer(cellRenderer);
+
+    final Dimension preferredSize = new Dimension(myTable.getPreferredSize());
+    preferredSize.height = (myTableModel.getRowCount() + 2) * myTable.getRowHeight() + myTable.getTableHeader().getHeight();
+
+    final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myTable);
+    scrollPane.setPreferredSize(preferredSize);
+    myChunksPanel.add(scrollPane, BorderLayout.CENTER);
+  }
+
+  protected void doOKAction() {
+    if (myTable != null) {
+      TableCellEditor cellEditor = myTable.getCellEditor();
+      if (cellEditor != null) {
+        cellEditor.stopCellEditing();
+      }
+    }
+    super.doOKAction();
+  }
+
+  public boolean isGenerateSingleFileBuild() {
+    return myRbGenerateSingleFileBuild.isSelected();
+  }
+
+  public boolean isFormsCompilationEnabled() {
+    return myCbEnableUIFormsCompilation.isSelected();
+  }
+
+  public boolean isForceTargetJdk() {
+    return myCbForceTargetJdk.isSelected();
+  }
+
+  public boolean isBackupFiles() {
+    return myRbBackupFiles.isSelected();
+  }
+
+  public boolean isRuntimeClasspathInlined() {
+    return myCbInlineRuntimeClasspath.isSelected();
+  }
+
+  public String[] getRepresentativeModuleNames() {
+    return myTableModel != null ? myTableModel.getModuleRepresentatives() : ArrayUtil.EMPTY_STRING_ARRAY;
+  }
+
+  /**
+   * @return true if user has selected to generate IDEA_HOME property
+   */
+  public boolean isIdeaHomeGenerated() {
+    return myGenerateIdeaHomeProperty.isSelected();
+  }
+  
+  public String getOutputFileName() {
+    return myOutputFileNameField.getText().trim();
+  }
+  
+  private static class MyTableModel extends AbstractTableModel {
+    private static final int NUMBER_COLUMN = 0;
+    private static final int NAME_COLUMN = 1;
+
+    private final List<Pair<String, ListWithSelection>> myItems = new ArrayList<Pair<String, ListWithSelection>>();
+
+    private MyTableModel(List<Chunk<Module>> chunks) {
+      for (final Chunk<Module> chunk : chunks) {
+        final ListWithSelection<String> item = new ListWithSelection<String>();
+        for (final Module module : chunk.getNodes()) {
+          item.add(module.getName());
+        }
+        item.selectFirst();
+        myItems.add(new Pair<String, ListWithSelection>(createCycleName(chunk), item));
+      }
+    }
+
+    private static String createCycleName(Chunk<Module> chunk) {
+      final StringBuilder buf = new StringBuilder();
+      for (Module module : chunk.getNodes()) {
+        if (buf.length() > 0) {
+          buf.append(", ");
+        }
+        buf.append(module.getName());
+      }
+      buf.insert(0, "[");
+      buf.append("]");
+      return buf.toString();
+    }
+
+    public String[] getModuleRepresentatives() {
+      final String[] names = new String[myItems.size()];
+      int index = 0;
+      for (final Pair<String, ListWithSelection> pair : myItems) {
+        names[index++] = (String)pair.getSecond().getSelection();
+      }
+      return names;
+    }
+
+    public int getColumnCount() {
+      return 2;
+    }
+
+    public int getRowCount() {
+      return myItems.size();
+    }
+
+    public boolean isCellEditable(int rowIndex, int columnIndex) {
+      return columnIndex == 1;
+    }
+
+    public Class getColumnClass(int columnIndex) {
+      switch (columnIndex) {
+        case NUMBER_COLUMN:
+          return String.class;
+        case NAME_COLUMN:
+          return ListWithSelection.class;
+        default:
+          return super.getColumnClass(columnIndex);
+      }
+    }
+
+    public Object getValueAt(int rowIndex, int columnIndex) {
+      switch (columnIndex) {
+        case NUMBER_COLUMN:
+          return myItems.get(rowIndex).getFirst();
+        case NAME_COLUMN:
+          return myItems.get(rowIndex).getSecond();
+        default:
+          return null;
+      }
+    }
+
+    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+      if (columnIndex == NAME_COLUMN) {
+        myItems.get(rowIndex).getSecond().select(aValue);
+      }
+    }
+
+    public String getColumnName(int columnIndex) {
+      switch (columnIndex) {
+        case NUMBER_COLUMN:
+          return CompilerBundle.message("generate.ant.build.dialog.cyclic.modules.table.number.column.header");
+        case NAME_COLUMN:
+          return CompilerBundle.message("generate.ant.build.dialog.cyclic.modules.table.name.column.header");
+      }
+      return super.getColumnName(columnIndex);
+    }
+  }
+
+  private static class MyTableCellRenderer extends DefaultTableCellRenderer {
+    public Component getTableCellRendererComponent(JTable table,
+                                                   Object value,
+                                                   boolean isSelected,
+                                                   boolean hasFocus,
+                                                   int row,
+                                                   int column) {
+      if (value instanceof ListWithSelection) {
+        value = ((ListWithSelection)value).getSelection();
+      }
+      final JLabel component = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+      component.setHorizontalAlignment(SwingConstants.CENTER);
+      return component;
+    }
+  }
+
+  protected Action[] createActions() {
+    return new Action[]{getOKAction(), getCancelAction(), getHelpAction()};
+  }
+
+  protected void doHelpAction() {
+    HelpManager.getInstance().invokeHelp(HelpID.GENERATE_ANT_BUILD);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/actions/MakeModuleAction.java b/java/compiler/impl/src/com/intellij/compiler/actions/MakeModuleAction.java
new file mode 100644
index 0000000..08b85a8
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/actions/MakeModuleAction.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 com.intellij.compiler.actions;
+
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+
+public class MakeModuleAction extends CompileActionBase {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.actions.MakeModuleAction");
+
+  protected void doAction(DataContext dataContext, Project project) {
+    Module[] modules = LangDataKeys.MODULE_CONTEXT_ARRAY.getData(dataContext);
+    Module module;
+    if (modules == null) {
+      module = LangDataKeys.MODULE.getData(dataContext);
+      if (module == null) {
+        return;
+      }
+      modules = new Module[]{module};
+    }
+    try {
+      CompilerManager.getInstance(project).make(modules[0].getProject(), modules, null);
+    }
+    catch (Exception e) {
+      LOG.error(e);
+    }
+  }
+
+  public void update(AnActionEvent event){
+    super.update(event);
+    Presentation presentation = event.getPresentation();
+    if (!presentation.isEnabled()) {
+      return;
+    }
+    final DataContext dataContext = event.getDataContext();
+    final Module module = LangDataKeys.MODULE.getData(dataContext);
+    Module[] modules = LangDataKeys.MODULE_CONTEXT_ARRAY.getData(dataContext);
+    final boolean isEnabled = module != null || modules != null;
+    presentation.setEnabled(isEnabled);
+    final String actionName = getTemplatePresentation().getTextWithMnemonic();
+
+    String presentationText;
+    if (modules != null) {
+      String text = actionName;
+      for (int i = 0; i < modules.length; i++) {
+        if (text.length() > 30) {
+          text = CompilerBundle.message("action.make.selected.modules.text");
+          break;
+        }
+        Module toMake = modules[i];
+        if (i!=0) {
+          text += ",";
+        }
+        text += " '" + toMake.getName() + "'";
+      }
+      presentationText = text;
+    }
+    else if (module != null) {
+      presentationText = actionName + " '" + module.getName() + "'";
+    }
+    else {
+      presentationText = actionName;
+    }
+    presentation.setText(presentationText);
+    presentation.setVisible(isEnabled || !ActionPlaces.PROJECT_VIEW_POPUP.equals(event.getPlace()));
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/actions/ProcessAnnotationsAction.java b/java/compiler/impl/src/com/intellij/compiler/actions/ProcessAnnotationsAction.java
new file mode 100644
index 0000000..bb59707
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/actions/ProcessAnnotationsAction.java
@@ -0,0 +1,203 @@
+/*
+ * 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 com.intellij.compiler.actions;
+
+import org.jetbrains.jps.model.java.compiler.AnnotationProcessingConfiguration;
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.impl.FileSetCompileScope;
+import com.intellij.compiler.impl.ModuleCompileScope;
+import com.intellij.compiler.impl.javaCompiler.AnnotationProcessingCompiler;
+import com.intellij.compiler.impl.resourceCompiler.ResourceCompiler;
+import com.intellij.idea.ActionsBundle;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.compiler.CompilerFilter;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.Nullable;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class ProcessAnnotationsAction extends CompileActionBase {
+
+  protected void doAction(DataContext dataContext, Project project) {
+    final Module module = LangDataKeys.MODULE_CONTEXT.getData(dataContext);
+    final CompilerFilter filter = new CompilerFilter() {
+      public boolean acceptCompiler(com.intellij.openapi.compiler.Compiler compiler) {
+        // EclipseLink CanonicalModelProcessor reads input from output hence adding ResourcesCompiler
+        return compiler instanceof AnnotationProcessingCompiler || compiler instanceof ResourceCompiler;
+      }
+    };
+    if (module != null) {
+      CompilerManager.getInstance(project).make(new ModuleCompileScope(module, false), filter, null);
+    }
+    else {
+      final FileSetCompileScope scope = getCompilableFiles(project, PlatformDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext));
+      if (scope != null) {
+        CompilerManager.getInstance(project).make(scope, filter, null);
+      }
+    }
+  }
+
+  public void update(AnActionEvent event) {
+    super.update(event);
+    Presentation presentation = event.getPresentation();
+    if (!presentation.isEnabled()) {
+      return;
+    }
+    DataContext dataContext = event.getDataContext();
+    presentation.setVisible(false);
+
+    Project project = PlatformDataKeys.PROJECT.getData(dataContext);
+    if (project == null) {
+      presentation.setEnabled(false);
+      return;
+    }
+
+    CompilerConfiguration compilerConfiguration = CompilerConfiguration.getInstance(project);
+    final Module module = LangDataKeys.MODULE.getData(dataContext);
+    final Module moduleContext = LangDataKeys.MODULE_CONTEXT.getData(dataContext);
+
+    if (module == null) {
+      presentation.setEnabled(false);
+      return;
+    }
+    final AnnotationProcessingConfiguration profile = compilerConfiguration.getAnnotationProcessingConfiguration(module);
+    if (!profile.isEnabled() || (!profile.isObtainProcessorsFromClasspath() && profile.getProcessors().isEmpty())) {
+      presentation.setEnabled(false);
+      return;
+    }
+
+    presentation.setVisible(true);
+    presentation.setText(createPresentationText(""), true);
+    final FileSetCompileScope scope = getCompilableFiles(project, PlatformDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext));
+    if (moduleContext == null && scope == null) {
+      presentation.setEnabled(false);
+      return;
+    }
+
+    String elementDescription = null;
+    if (moduleContext != null) {
+      elementDescription = CompilerBundle.message("action.compile.description.module", moduleContext.getName());
+    }
+    else {
+      PsiPackage aPackage = null;
+      final Collection<VirtualFile> files = scope.getRootFiles();
+      if (files.size() == 1) {
+        final PsiDirectory directory = PsiManager.getInstance(project).findDirectory(files.iterator().next());
+        if (directory != null) {
+          aPackage = JavaDirectoryService.getInstance().getPackage(directory);
+        }
+      }
+      else {
+        PsiElement element = LangDataKeys.PSI_ELEMENT.getData(dataContext);
+        if (element instanceof PsiPackage) {
+          aPackage = (PsiPackage)element;
+        }
+      }
+
+      if (aPackage != null) {
+        String name = aPackage.getQualifiedName();
+        if (name.length() == 0) {
+          //noinspection HardCodedStringLiteral
+          name = "<default>";
+        }
+        elementDescription = "'" + name + "'";
+      }
+      else if (files.size() == 1) {
+        final VirtualFile file = files.iterator().next();
+        FileType fileType = file.getFileType();
+        if (CompilerManager.getInstance(project).isCompilableFileType(fileType)) {
+          elementDescription = "'" + file.getName() + "'";
+        }
+        else {
+          if (!ActionPlaces.MAIN_MENU.equals(event.getPlace())) {
+            // the action should be invisible in popups for non-java files
+            presentation.setEnabled(false);
+            presentation.setVisible(false);
+            return;
+          }
+        }
+      }
+      else {
+        elementDescription = CompilerBundle.message("action.compile.description.selected.files");
+      }
+    }
+
+    if (elementDescription == null) {
+      presentation.setEnabled(false);
+      return;
+    }
+
+    presentation.setText(createPresentationText(elementDescription), true);
+    presentation.setEnabled(true);
+  }
+
+  private static String createPresentationText(final String elementDescription) {
+    int length = elementDescription.length();
+    String target = length > 23 ? (StringUtil.startsWithChar(elementDescription, '\'') ? "'..." : "...") + elementDescription.substring(length - 20, length) : elementDescription;
+    return MessageFormat.format(ActionsBundle.actionText(StringUtil.isEmpty(target)? "RunAPT" : "RunAPT.1"), target);
+  }
+
+  @Nullable
+  private static FileSetCompileScope getCompilableFiles(Project project, VirtualFile[] files) {
+    if (files == null || files.length == 0) {
+      return null;
+    }
+    final PsiManager psiManager = PsiManager.getInstance(project);
+    final FileTypeManager typeManager = FileTypeManager.getInstance();
+    final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
+    final CompilerManager compilerManager = CompilerManager.getInstance(project);
+    final List<VirtualFile> filesToCompile = new ArrayList<VirtualFile>();
+    final List<Module> affectedModules = new ArrayList<Module>();
+    for (final VirtualFile file : files) {
+      if (!fileIndex.isInSourceContent(file)) {
+        continue;
+      }
+      if (!file.isInLocalFileSystem()) {
+        continue;
+      }
+      if (file.isDirectory()) {
+        final PsiDirectory directory = psiManager.findDirectory(file);
+        if (directory == null || JavaDirectoryService.getInstance().getPackage(directory) == null) {
+          continue;
+        }
+      }
+      else {
+        FileType fileType = file.getFileType();
+        if (!(compilerManager.isCompilableFileType(fileType))) {
+          continue;
+        }
+      }
+      filesToCompile.add(file);
+      ContainerUtil.addIfNotNull(fileIndex.getModuleForFile(file), affectedModules);
+    }
+    if (filesToCompile.isEmpty()) return null;
+    return new FileSetCompileScope(filesToCompile, affectedModules.toArray(new Module[affectedModules.size()]));
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/BuildPropertiesImpl.java b/java/compiler/impl/src/com/intellij/compiler/ant/BuildPropertiesImpl.java
new file mode 100644
index 0000000..d89cd15
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/BuildPropertiesImpl.java
@@ -0,0 +1,188 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.compiler.ant.taskdefs.*;
+import com.intellij.compiler.impl.javaCompiler.javac.JavacConfiguration;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ex.ProjectEx;
+import com.intellij.openapi.projectRoots.JavaSdkType;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkTypeId;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerOptions;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 16, 2004
+ */
+// todo: move path variables properties and jdk home properties into te generated property file
+public class BuildPropertiesImpl extends BuildProperties {
+
+  public BuildPropertiesImpl(Project project, final GenerationOptions genOptions) {
+    add(new Property(genOptions.getPropertiesFileName()));
+
+    //noinspection HardCodedStringLiteral
+    add(new Comment(CompilerBundle.message("generated.ant.build.disable.tests.property.comment"),
+                    new Property(PROPERTY_SKIP_TESTS, "true")));
+    final JpsJavaCompilerOptions javacSettings = JavacConfiguration.getOptions(project, JavacConfiguration.class);
+    add(new Comment(CompilerBundle.message("generated.ant.build.compiler.options.comment")), 1);
+    //noinspection HardCodedStringLiteral
+    add(new Property(PROPERTY_COMPILER_GENERATE_DEBUG_INFO, javacSettings.DEBUGGING_INFO ? "on" : "off"), 1);
+    //noinspection HardCodedStringLiteral
+    add(new Property(PROPERTY_COMPILER_GENERATE_NO_WARNINGS, javacSettings.GENERATE_NO_WARNINGS ? "on" : "off"));
+    add(new Property(PROPERTY_COMPILER_ADDITIONAL_ARGS, javacSettings.ADDITIONAL_OPTIONS_STRING));
+    //noinspection HardCodedStringLiteral
+    add(new Property(PROPERTY_COMPILER_MAX_MEMORY, Integer.toString(javacSettings.MAXIMUM_HEAP_SIZE) + "m"));
+
+    add(new IgnoredFiles());
+
+    if (CompilerExcludes.isAvailable(project)) {
+      add(new CompilerExcludes(project, genOptions));
+    }
+
+    if (!genOptions.expandJarDirectories) {
+      add(new LibraryPatterns(project, genOptions));
+    }
+
+    add(new CompilerResourcePatterns(project));
+
+    if (genOptions.forceTargetJdk) {
+      createJdkGenerators(project);
+    }
+
+    LibraryDefinitionsGeneratorFactory factory = new LibraryDefinitionsGeneratorFactory((ProjectEx)project, genOptions);
+
+    final LibraryTablesRegistrar registrar = LibraryTablesRegistrar.getInstance();
+    final Generator projectLibs = factory.create(registrar.getLibraryTable(project), getProjectBaseDir(project),
+                                                 CompilerBundle.message("generated.ant.build.project.libraries.comment"));
+    if (projectLibs != null) {
+      add(projectLibs);
+    }
+
+    final Generator globalLibs =
+      factory.create(registrar.getLibraryTable(), null, CompilerBundle.message("generated.ant.build.global.libraries.comment"));
+    if (globalLibs != null) {
+      add(globalLibs);
+    }
+
+    for (final LibraryTable table : registrar.getCustomLibraryTables()) {
+      if (table.getLibraries().length != 0) {
+        final Generator appServerLibs = factory.create(table, null, table.getPresentation().getDisplayName(true));
+        if (appServerLibs != null) {
+          add(appServerLibs);
+        }
+      }
+    }
+
+    final ChunkCustomCompilerExtension[] customCompilers = genOptions.getCustomCompilers();
+    if (genOptions.enableFormCompiler || customCompilers.length > 0) {
+      add(new Comment(CompilerBundle.message("generated.ant.build.custom.compilers.comment")));
+      Target register = new Target(TARGET_REGISTER_CUSTOM_COMPILERS, null, null, null);
+      if (genOptions.enableFormCompiler) {
+        //noinspection HardCodedStringLiteral
+        add(new Property(PROPERTY_JAVAC2_HOME, propertyRelativePath(PROPERTY_IDEA_HOME, "lib")));
+        Path javac2 = new Path(PROPERTY_JAVAC2_CLASSPATH_ID);
+        javac2.add(new PathElement(propertyRelativePath(PROPERTY_JAVAC2_HOME, "javac2.jar")));
+        javac2.add(new PathElement(propertyRelativePath(PROPERTY_JAVAC2_HOME, "jdom.jar")));
+        javac2.add(new PathElement(propertyRelativePath(PROPERTY_JAVAC2_HOME, "asm4-all.jar")));
+        javac2.add(new PathElement(propertyRelativePath(PROPERTY_JAVAC2_HOME, "jgoodies-forms.jar")));
+        add(javac2);
+        //noinspection HardCodedStringLiteral
+        register.add(new Tag("taskdef", Pair.create("name", "javac2"), Pair.create("classname", "com.intellij.ant.Javac2"),
+                    Pair.create("classpathref", PROPERTY_JAVAC2_CLASSPATH_ID)));
+        register.add(new Tag("taskdef", Pair.create("name", "instrumentIdeaExtensions"),
+                    Pair.create("classname", "com.intellij.ant.InstrumentIdeaExtensions"),
+                    Pair.create("classpathref", PROPERTY_JAVAC2_CLASSPATH_ID)));
+      }
+      if (customCompilers.length > 0) {
+        for (ChunkCustomCompilerExtension ext : customCompilers) {
+          ext.generateCustomCompilerTaskRegistration(project, genOptions, register);
+        }
+      }
+      add(register);
+    }
+  }
+
+  protected void createJdkGenerators(final Project project) {
+    final Sdk[] jdks = getUsedJdks(project);
+
+    if (jdks.length > 0) {
+      add(new Comment(CompilerBundle.message("generated.ant.build.jdk.definitions.comment")), 1);
+
+      for (final Sdk jdk : jdks) {
+        if (jdk.getHomeDirectory() == null) {
+          continue;
+        }
+        final SdkTypeId sdkType = jdk.getSdkType();
+        if (!(sdkType instanceof JavaSdkType) || ((JavaSdkType)sdkType).getBinPath(jdk) == null) {
+          continue;
+        }
+        final File home = VfsUtil.virtualToIoFile(jdk.getHomeDirectory());
+        File homeDir;
+        try {
+          // use canonical path in order to resolve symlinks
+          homeDir = home.getCanonicalFile();
+        }
+        catch (IOException e) {
+          homeDir = home;
+        }
+        final String jdkName = jdk.getName();
+        final String jdkHomeProperty = getJdkHomeProperty(jdkName);
+        final FileSet fileSet = new FileSet(propertyRef(jdkHomeProperty));
+        final String[] urls = jdk.getRootProvider().getUrls(OrderRootType.CLASSES);
+        for (String url : urls) {
+          final String path = GenerationUtils.trimJarSeparator(VirtualFileManager.extractPath(url));
+          final File pathElement = new File(path);
+          final String relativePath = FileUtil.getRelativePath(homeDir, pathElement);
+          if (relativePath != null) {
+            fileSet.add(new Include(relativePath.replace(File.separatorChar, '/')));
+          }
+        }
+
+        final File binPath = toCanonicalFile(new File(((JavaSdkType)sdkType).getBinPath(jdk)));
+        final String relativePath = FileUtil.getRelativePath(homeDir, binPath);
+        if (relativePath != null) {
+          add(new Property(BuildProperties.getJdkBinProperty(jdkName),
+                           propertyRef(jdkHomeProperty) + "/" + FileUtil.toSystemIndependentName(relativePath)), 1);
+        }
+        else {
+          add(new Property(BuildProperties.getJdkBinProperty(jdkName), FileUtil.toSystemIndependentName(binPath.getPath())), 1);
+        }
+
+        final Path jdkPath = new Path(getJdkPathId(jdkName));
+        jdkPath.add(fileSet);
+        add(jdkPath);
+      }
+    }
+
+    final Sdk projectJdk = ProjectRootManager.getInstance(project).getProjectSdk();
+    add(new Property(PROPERTY_PROJECT_JDK_HOME, projectJdk != null ? propertyRef(getJdkHomeProperty(projectJdk.getName())) : ""), 1);
+    add(new Property(PROPERTY_PROJECT_JDK_BIN, projectJdk != null ? propertyRef(getJdkBinProperty(projectJdk.getName())) : ""));
+    add(new Property(PROPERTY_PROJECT_JDK_CLASSPATH, projectJdk != null ? getJdkPathId(projectJdk.getName()) : ""));
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/BuildTargetsFactoryImpl.java b/java/compiler/impl/src/com/intellij/compiler/ant/BuildTargetsFactoryImpl.java
new file mode 100644
index 0000000..0db7086
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/BuildTargetsFactoryImpl.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.
+ */
+
+/*
+ * User: anna
+ * Date: 19-Dec-2006
+ */
+package com.intellij.compiler.ant;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.util.ArrayUtil;
+import org.jetbrains.annotations.TestOnly;
+
+public class BuildTargetsFactoryImpl extends BuildTargetsFactory {
+
+
+  public Generator createComment(final String comment) {
+    return new Comment(comment);
+  }
+
+  @TestOnly
+  public GenerationOptions getDefaultOptions(Project project) {
+    return new GenerationOptionsImpl(project, true, false, false, true, ArrayUtil.EMPTY_STRING_ARRAY);
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/ChunkBuild.java b/java/compiler/impl/src/com/intellij/compiler/ant/ChunkBuild.java
new file mode 100644
index 0000000..cd20b1c
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/ChunkBuild.java
@@ -0,0 +1,84 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.compiler.ant.taskdefs.Path;
+import com.intellij.compiler.ant.taskdefs.Property;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.vfs.VirtualFileManager;
+
+import java.io.File;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 22, 2004
+ */
+public class ChunkBuild extends CompositeGenerator{
+
+  public ChunkBuild(Project project, ModuleChunk chunk, GenerationOptions genOptions) {
+    final File chunkBaseDir = chunk.getBaseDir();
+    if (genOptions.forceTargetJdk) {
+      if (chunk.isJdkInherited()) {
+        add(new Property(BuildProperties.getModuleChunkJdkHomeProperty(chunk.getName()), BuildProperties.propertyRef(BuildProperties.PROPERTY_PROJECT_JDK_HOME)));
+        add(new Property(BuildProperties.getModuleChunkJdkBinProperty(chunk.getName()), BuildProperties.propertyRef(BuildProperties.PROPERTY_PROJECT_JDK_BIN)));
+        add(new Property(BuildProperties.getModuleChunkJdkClasspathProperty(chunk.getName()), BuildProperties.propertyRef(BuildProperties.PROPERTY_PROJECT_JDK_CLASSPATH)));
+      }
+      else {
+        final Sdk jdk = chunk.getJdk();
+        add(new Property(BuildProperties.getModuleChunkJdkHomeProperty(chunk.getName()), jdk != null? BuildProperties.propertyRef(BuildProperties.getJdkHomeProperty(jdk.getName())): ""));
+        add(new Property(BuildProperties.getModuleChunkJdkBinProperty(chunk.getName()), jdk != null? BuildProperties.propertyRef(BuildProperties.getJdkBinProperty(jdk.getName())): ""));
+        add(new Property(BuildProperties.getModuleChunkJdkClasspathProperty(chunk.getName()), jdk != null? BuildProperties.getJdkPathId(jdk.getName()) : ""));
+      }
+    }
+
+    add(new Property(BuildProperties.getModuleChunkCompilerArgsProperty(chunk.getName()), BuildProperties.propertyRef(BuildProperties.PROPERTY_COMPILER_ADDITIONAL_ARGS)), 1);
+
+    final String outputPathUrl = chunk.getOutputDirUrl();
+    String location = outputPathUrl != null?
+                      GenerationUtils.toRelativePath(VirtualFileManager.extractPath(outputPathUrl), chunkBaseDir, BuildProperties.getModuleChunkBasedirProperty(chunk), genOptions) :
+                      CompilerBundle.message("value.undefined");
+    add(new Property(BuildProperties.getOutputPathProperty(chunk.getName()), location), 1);
+
+    final String testOutputPathUrl = chunk.getTestsOutputDirUrl();
+    if (testOutputPathUrl != null) {
+      location = GenerationUtils.toRelativePath(VirtualFileManager.extractPath(testOutputPathUrl), chunkBaseDir, BuildProperties.getModuleChunkBasedirProperty(chunk), genOptions);
+    }
+    add(new Property(BuildProperties.getOutputPathForTestsProperty(chunk.getName()), location));
+
+    add(createBootclasspath(chunk), 1);
+    add(new ModuleChunkClasspath(chunk, genOptions, false, false), 1);
+    add(new ModuleChunkClasspath(chunk, genOptions, true, false), 1);
+    add(new ModuleChunkClasspath(chunk, genOptions, false, true), 1);
+    add(new ModuleChunkClasspath(chunk, genOptions, true, true), 1);
+
+    final ModuleChunkSourcePath moduleSources = new ModuleChunkSourcePath(project, chunk, genOptions);
+    add(moduleSources, 1);
+    add(new CompileModuleChunkTarget(project, chunk, moduleSources.getSourceRoots(), moduleSources.getTestSourceRoots(), chunkBaseDir, genOptions), 1);
+    add(new CleanModule(chunk), 1);
+
+    ChunkBuildExtension.process(this, chunk, genOptions);
+  }
+
+  private static Generator createBootclasspath(ModuleChunk chunk) {
+    final Path bootclasspath = new Path(BuildProperties.getBootClasspathProperty(chunk.getName()));
+    bootclasspath.add(new Comment(CompilerBundle.message("generated.ant.build.bootclasspath.comment")));
+    return bootclasspath;
+  }
+
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/CleanModule.java b/java/compiler/impl/src/com/intellij/compiler/ant/CleanModule.java
new file mode 100644
index 0000000..0b27fac
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/CleanModule.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.
+ */
+package com.intellij.compiler.ant;
+
+import com.intellij.compiler.ant.taskdefs.Delete;
+import com.intellij.compiler.ant.taskdefs.Target;
+import com.intellij.openapi.compiler.CompilerBundle;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 24, 2004
+ */
+public class CleanModule extends Target {
+  public CleanModule(ModuleChunk chunk) {
+    super(BuildProperties.getModuleCleanTargetName(chunk.getName()), null,
+          CompilerBundle.message("generated.ant.build.cleanup.module.task.comment"), null);
+    final String chunkName = chunk.getName();
+    add(new Delete(BuildProperties.propertyRef(BuildProperties.getOutputPathProperty(chunkName))));
+    add(new Delete(BuildProperties.propertyRef(BuildProperties.getOutputPathForTestsProperty(chunkName))));
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/CleanProject.java b/java/compiler/impl/src/com/intellij/compiler/ant/CleanProject.java
new file mode 100644
index 0000000..5613da9
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/CleanProject.java
@@ -0,0 +1,54 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.compiler.ant.artifacts.ArtifactsGenerator;
+import com.intellij.compiler.ant.taskdefs.Target;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 24, 2004
+ */
+public class CleanProject extends Generator {
+  private final Target myTarget;
+
+  public CleanProject(Project project, @NotNull GenerationOptions genOptions, @NotNull ArtifactsGenerator artifactsGenerator) {
+    List<String> dependencies = new ArrayList<String>();
+    final ModuleChunk[] chunks = genOptions.getModuleChunks();
+    for (ModuleChunk chunk : chunks) {
+      dependencies.add(BuildProperties.getModuleCleanTargetName(chunk.getName()));
+    }
+    dependencies.addAll(artifactsGenerator.getCleanTargetNames());
+    for (ChunkBuildExtension extension : ChunkBuildExtension.EP_NAME.getExtensions()) {
+      dependencies.addAll(extension.getCleanTargetNames(project, genOptions));
+    }
+    myTarget = new Target(BuildProperties.TARGET_CLEAN, StringUtil.join(dependencies, ", "),
+                          CompilerBundle.message("generated.ant.build.clean.all.task.comment"), null);
+  }
+
+  public void generate(PrintWriter out) throws IOException {
+    myTarget.generate(out);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/Comment.java b/java/compiler/impl/src/com/intellij/compiler/ant/Comment.java
new file mode 100644
index 0000000..146d256
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/Comment.java
@@ -0,0 +1,59 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class Comment extends Generator{
+  private final String myComment;
+  private final Generator myCommentedData;
+
+  public Comment(String comment) {
+    this(comment, null);
+  }
+
+  public Comment(Generator commentedData) {
+    this(null, commentedData);
+  }
+
+  public Comment(String comment, Generator commentedData) {
+    myComment = comment;
+    myCommentedData = commentedData;
+  }
+
+  public void generate(PrintWriter out) throws IOException {
+    if (myComment != null) {
+      out.print("<!-- ");
+      out.print(myComment);
+      out.print(" -->");
+      if (myCommentedData != null) {
+        crlf(out);
+      }
+    }
+    if (myCommentedData != null) {
+      out.print("<!-- ");
+      crlf(out);
+      myCommentedData.generate(out);
+      crlf(out);
+      out.print(" -->");
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/CompileModuleChunkTarget.java b/java/compiler/impl/src/com/intellij/compiler/ant/CompileModuleChunkTarget.java
new file mode 100644
index 0000000..e08040b
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/CompileModuleChunkTarget.java
@@ -0,0 +1,213 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.compiler.ant.taskdefs.*;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NonNls;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class CompileModuleChunkTarget extends CompositeGenerator {
+
+  public CompileModuleChunkTarget(final Project project,
+                                  ModuleChunk moduleChunk,
+                                  VirtualFile[] sourceRoots,
+                                  VirtualFile[] testSourceRoots,
+                                  File baseDir,
+                                  GenerationOptions genOptions) {
+    final String moduleChunkName = moduleChunk.getName();
+    //noinspection HardCodedStringLiteral
+    final Tag compilerArgs = new Tag("compilerarg", Pair.create("line", BuildProperties.propertyRef(
+      BuildProperties.getModuleChunkCompilerArgsProperty(moduleChunkName))));
+    //noinspection HardCodedStringLiteral
+    final Pair<String, String> classpathRef = Pair.create("refid", BuildProperties.getClasspathProperty(moduleChunkName));
+    final Tag classpathTag = new Tag("classpath", classpathRef);
+    //noinspection HardCodedStringLiteral
+    final Tag bootclasspathTag =
+      new Tag("bootclasspath", Pair.create("refid", BuildProperties.getBootClasspathProperty(moduleChunkName)));
+    final PatternSetRef compilerExcludes = new PatternSetRef(BuildProperties.getExcludedFromCompilationProperty(moduleChunkName));
+
+    final String mainTargetName = BuildProperties.getCompileTargetName(moduleChunkName);
+    final @NonNls String productionTargetName = mainTargetName + ".production";
+    final @NonNls String testsTargetName = mainTargetName + ".tests";
+
+    final ChunkCustomCompilerExtension[] customCompilers = moduleChunk.getCustomCompilers();
+    final String customCompilersDependency = customCompilers.length != 0 || genOptions.enableFormCompiler ?
+                                             BuildProperties.TARGET_REGISTER_CUSTOM_COMPILERS : "";
+    final int modulesCount = moduleChunk.getModules().length;
+    Target mainTarget = new Target(mainTargetName, productionTargetName + "," + testsTargetName,
+                                   CompilerBundle.message("generated.ant.build.compile.modules.main.target.comment", modulesCount,
+                                                          moduleChunkName), null);
+    String dependenciesProduction = getChunkDependenciesString(moduleChunk);
+    if (customCompilersDependency.length() > 0) {
+      if (dependenciesProduction != null && dependenciesProduction.length() > 0) {
+        dependenciesProduction = customCompilersDependency + "," + dependenciesProduction;
+      }
+      else {
+        dependenciesProduction = customCompilersDependency;
+      }
+    }
+    Target productionTarget = new Target(productionTargetName, dependenciesProduction,
+                                         CompilerBundle.message("generated.ant.build.compile.modules.production.classes.target.comment",
+                                                                modulesCount, moduleChunkName), null);
+    String dependenciesTests = (customCompilersDependency.length() != 0 ? customCompilersDependency + "," : "") + productionTargetName;
+    Target testsTarget = new Target(testsTargetName, dependenciesTests,
+                                    CompilerBundle.message("generated.ant.build.compile.modules.tests.target.comment", modulesCount,
+                                                           moduleChunkName), BuildProperties.PROPERTY_SKIP_TESTS);
+
+    if (sourceRoots.length > 0) {
+      final String outputPathRef = BuildProperties.propertyRef(BuildProperties.getOutputPathProperty(moduleChunkName));
+      final Tag srcTag = new Tag("src", Pair.create("refid", BuildProperties.getSourcepathProperty(moduleChunkName)));
+      productionTarget.add(new Mkdir(outputPathRef));
+      createCustomCompilerTasks(project, moduleChunk, genOptions, false, customCompilers, compilerArgs, bootclasspathTag,
+                                classpathTag, compilerExcludes, srcTag, outputPathRef, productionTarget);
+      if (customCompilers.length == 0 || genOptions.enableFormCompiler) {
+        final Javac javac = new Javac(genOptions, moduleChunk, outputPathRef);
+        javac.add(compilerArgs);
+        javac.add(bootclasspathTag);
+        javac.add(classpathTag);
+        javac.add(srcTag);
+        javac.add(compilerExcludes);
+        productionTarget.add(javac);
+      }
+      productionTarget.add(createCopyTask(project, moduleChunk, sourceRoots, outputPathRef, baseDir, genOptions));
+    }
+
+    if (testSourceRoots.length > 0) {
+
+      final String testOutputPathRef = BuildProperties.propertyRef(BuildProperties.getOutputPathForTestsProperty(moduleChunkName));
+      final Tag srcTag = new Tag("src", Pair.create("refid", BuildProperties.getTestSourcepathProperty(moduleChunkName)));
+      final Pair<String, String> testClasspathRef = Pair.create("refid", BuildProperties.getTestClasspathProperty(moduleChunkName));
+      final Tag testClassPath = new Tag("classpath", testClasspathRef);
+      testsTarget.add(new Mkdir(testOutputPathRef));
+      createCustomCompilerTasks(project, moduleChunk, genOptions, true, customCompilers, compilerArgs, bootclasspathTag,
+                                testClassPath, compilerExcludes, srcTag, testOutputPathRef, testsTarget);
+      if (customCompilers.length == 0 || genOptions.enableFormCompiler) {
+        final Javac javac = new Javac(genOptions, moduleChunk, testOutputPathRef);
+        javac.add(compilerArgs);
+        javac.add(bootclasspathTag);
+        javac.add(testClassPath);
+        javac.add(srcTag);
+        javac.add(compilerExcludes);
+        testsTarget.add(javac);
+      }
+      testsTarget.add(createCopyTask(project, moduleChunk, testSourceRoots, testOutputPathRef, baseDir, genOptions));
+    }
+
+    add(mainTarget);
+    add(productionTarget, 1);
+    add(testsTarget, 1);
+  }
+
+  /**
+   * Create custom compiler tasks
+   *
+   * @param project          the project
+   * @param moduleChunk      the module chunk
+   * @param genOptions       generation options
+   * @param compileTests     if true tests are being compiled
+   * @param customCompilers  an array of custom compilers for this chunk
+   * @param compilerArgs     the javac compiler arguments
+   * @param bootclasspathTag the boot classpath element for the javac compiler
+   * @param classpathTag     the classpath tag for the javac compiler
+   * @param compilerExcludes the compiler excluded tag
+   * @param srcTag           the source tag
+   * @param outputPathRef    the output path references
+   * @param target           the target where to add custom compiler
+   */
+  private static void createCustomCompilerTasks(Project project,
+                                                ModuleChunk moduleChunk,
+                                                GenerationOptions genOptions,
+                                                boolean compileTests,
+                                                ChunkCustomCompilerExtension[] customCompilers,
+                                                Tag compilerArgs,
+                                                Tag bootclasspathTag,
+                                                Tag classpathTag,
+                                                PatternSetRef compilerExcludes,
+                                                Tag srcTag,
+                                                String outputPathRef,
+                                                Target target) {
+    if (customCompilers.length > 1) {
+      target.add(new Tag("fail", Pair.create("message", CompilerBundle.message(
+        "generated.ant.build.compile.modules.fail.custom.compilers"))));
+    }
+    for (ChunkCustomCompilerExtension ext : customCompilers) {
+      ext.generateCustomCompile(project, moduleChunk, genOptions, compileTests, target, compilerArgs, bootclasspathTag,
+                                classpathTag, compilerExcludes, srcTag, outputPathRef);
+    }
+  }
+
+  private static String getChunkDependenciesString(ModuleChunk moduleChunk) {
+    final StringBuffer moduleDependencies = new StringBuffer();
+    final ModuleChunk[] dependencies = moduleChunk.getDependentChunks();
+    for (int idx = 0; idx < dependencies.length; idx++) {
+      final ModuleChunk dependency = dependencies[idx];
+      if (idx > 0) {
+        moduleDependencies.append(",");
+      }
+      moduleDependencies.append(BuildProperties.getCompileTargetName(dependency.getName()));
+    }
+    return moduleDependencies.toString();
+  }
+
+  private static Generator createCopyTask(final Project project,
+                                          ModuleChunk chunk,
+                                          VirtualFile[] sourceRoots,
+                                          String toDir,
+                                          File baseDir,
+                                          final GenerationOptions genOptions) {
+    //noinspection HardCodedStringLiteral
+    final Tag filesSelector = new Tag("type", Pair.create("type", "file"));
+    final PatternSetRef excludes = CompilerExcludes.isAvailable(project) ? new PatternSetRef(
+      BuildProperties.getExcludedFromCompilationProperty(chunk.getName())) : null;
+    final PatternSetRef resourcePatternsPatternSet = new PatternSetRef(BuildProperties.PROPERTY_COMPILER_RESOURCE_PATTERNS);
+    final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
+    final CompositeGenerator composite = new CompositeGenerator();
+    final Map<String, Copy> outputDirToTaskMap = new HashMap<String, Copy>();
+    for (final VirtualFile root : sourceRoots) {
+      final String packagePrefix = fileIndex.getPackageNameByDirectory(root);
+      final String targetDir =
+        packagePrefix != null && packagePrefix.length() > 0 ? toDir + "/" + packagePrefix.replace('.', '/') : toDir;
+      Copy copy = outputDirToTaskMap.get(targetDir);
+      if (copy == null) {
+        copy = new Copy(targetDir);
+        outputDirToTaskMap.put(targetDir, copy);
+        composite.add(copy);
+      }
+      final FileSet fileSet = new FileSet(
+        GenerationUtils.toRelativePath(root, baseDir, BuildProperties.getModuleChunkBasedirProperty(chunk), genOptions));
+      fileSet.add(resourcePatternsPatternSet);
+      fileSet.add(filesSelector);
+      if (excludes != null) {
+        fileSet.add(excludes);
+      }
+      copy.add(fileSet);
+    }
+    return composite;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/CompilerExcludes.java b/java/compiler/impl/src/com/intellij/compiler/ant/CompilerExcludes.java
new file mode 100644
index 0000000..538fee9
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/CompilerExcludes.java
@@ -0,0 +1,74 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.CompilerConfigurationImpl;
+import com.intellij.compiler.ant.taskdefs.Exclude;
+import com.intellij.compiler.ant.taskdefs.PatternSet;
+import com.intellij.openapi.compiler.options.ExcludeEntryDescription;
+import com.intellij.openapi.project.Project;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class CompilerExcludes extends Generator {
+  private final PatternSet myPatternSet;
+
+  public CompilerExcludes(Project project, GenerationOptions genOptions) {
+    final CompilerConfigurationImpl compilerConfiguration = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(project);
+    final ExcludeEntryDescription[] excludeEntryDescriptions =
+      compilerConfiguration.getExcludedEntriesConfiguration().getExcludeEntryDescriptions();
+    myPatternSet = new PatternSet(BuildProperties.PROPERTY_COMPILER_EXCLUDES);
+    for (final ExcludeEntryDescription entry : excludeEntryDescriptions) {
+      final String path = GenerationUtils
+        .toRelativePath(entry.getVirtualFile(), BuildProperties.getProjectBaseDir(project), BuildProperties.getProjectBaseDirProperty(),
+                        genOptions);
+      if (path == null) {
+        // entry is invalid, skip it
+        continue;
+      }
+      if (entry.isFile()) {
+        myPatternSet.add(new Exclude(path));
+      }
+      else {
+        if (entry.isIncludeSubdirectories()) {
+          myPatternSet.add(new Exclude(path + "/**"));
+        }
+        else {
+          myPatternSet.add(new Exclude(path + "/*"));
+        }
+      }
+    }
+  }
+
+
+  public void generate(PrintWriter out) throws IOException {
+    myPatternSet.generate(out);
+  }
+
+  public static boolean isAvailable(Project project) {
+    final CompilerConfigurationImpl compilerConfiguration = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(project);
+    final ExcludeEntryDescription[] excludeEntryDescriptions =
+      compilerConfiguration.getExcludedEntriesConfiguration().getExcludeEntryDescriptions();
+    return excludeEntryDescriptions.length > 0;
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/CompilerResourcePatterns.java b/java/compiler/impl/src/com/intellij/compiler/ant/CompilerResourcePatterns.java
new file mode 100644
index 0000000..d63966b
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/CompilerResourcePatterns.java
@@ -0,0 +1,55 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.CompilerConfigurationImpl;
+import com.intellij.compiler.ant.taskdefs.Exclude;
+import com.intellij.compiler.ant.taskdefs.Include;
+import com.intellij.compiler.ant.taskdefs.PatternSet;
+import com.intellij.openapi.project.Project;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class CompilerResourcePatterns extends Generator {
+  private final PatternSet myPatternSet;
+
+  public CompilerResourcePatterns(Project project) {
+    final CompilerConfigurationImpl compilerConfiguration = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(project);
+    final String[] patterns = compilerConfiguration.getResourceFilePatterns();
+    myPatternSet = new PatternSet(BuildProperties.PROPERTY_COMPILER_RESOURCE_PATTERNS);
+    for (String pattern : patterns) {
+      if (CompilerConfigurationImpl.isPatternNegated(pattern)) {
+        myPatternSet.add(new Exclude("**/" + pattern.substring(1)));
+      }
+      else {
+        myPatternSet.add(new Include("**/" + pattern));
+      }
+    }
+  }
+
+
+  public void generate(PrintWriter out) throws IOException {
+    myPatternSet.generate(out);
+  }
+
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/GenerateAntApplication.java b/java/compiler/impl/src/com/intellij/compiler/ant/GenerateAntApplication.java
new file mode 100644
index 0000000..5a2fa70
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/GenerateAntApplication.java
@@ -0,0 +1,125 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.codeInspection.InspectionsBundle;
+import com.intellij.compiler.actions.GenerateAntBuildAction;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.application.ex.ApplicationEx;
+import com.intellij.openapi.application.ex.ApplicationManagerEx;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ex.ProjectManagerEx;
+import com.intellij.openapi.roots.impl.DirectoryIndex;
+import com.intellij.openapi.roots.impl.DirectoryIndexImpl;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+
+import javax.swing.*;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * @author max
+ */
+public class GenerateAntApplication {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ex.InspectionApplication");
+
+  public String myProjectPath = null;
+  public String myOutPath = null;
+  private Project myProject;
+  private int myVerboseLevel = 0;
+
+  public void startup() {
+    if (myProjectPath == null || myOutPath == null) {
+      GenerateAntMain.printHelp();
+    }
+
+    SwingUtilities.invokeLater(new Runnable() {
+      public void run() {
+        ApplicationEx application = ApplicationManagerEx.getApplicationEx();
+        try {
+          logMessage(0, "Starting app... ");
+          application.doNotSave();
+          application.load(PathManager.getOptionsPath());
+          logMessageLn(0, "done");
+
+          GenerateAntApplication.this.run();
+        }
+        catch (Exception e) {
+          GenerateAntApplication.LOG.error(e);
+        }
+        finally {
+          application.exit(true);
+        }
+      }
+    });
+  }
+
+  public void run() {
+    try {
+      myProjectPath = myProjectPath.replace(File.separatorChar, '/');
+      VirtualFile vfsProject = LocalFileSystem.getInstance().findFileByPath(myProjectPath);
+      if (vfsProject == null) {
+        logError(InspectionsBundle.message("inspection.application.file.cannot.be.found", myProjectPath));
+        GenerateAntMain.printHelp();
+      }
+
+      logMessage(0, "Loading project...");
+      myProject = ProjectManagerEx.getInstanceEx().loadProject(myProjectPath);
+
+      DirectoryIndexImpl dirIndex = (DirectoryIndexImpl)DirectoryIndex.getInstance(myProject);
+      dirIndex.initialize();
+
+      logMessageLn(0, " done");
+
+      GenerateAntBuildAction.generateSingleFileBuild(myProject,
+                                                     new GenerationOptionsImpl(myProject, true, true, false, false, new String[] {}),
+                                                     new File("/Users/max/build/build.xml"),
+                                                     new File("/Users/max/build/build.properties"));
+
+      logMessage(0, "Hello!");
+    }
+    catch (IOException e) {
+      GenerateAntApplication.LOG.error(e);
+      GenerateAntMain.printHelp();
+    }
+    catch (Exception e) {
+      GenerateAntApplication.LOG.error(e);
+      System.exit(1);
+    }
+  }
+
+  public void setVerboseLevel(int verboseLevel) {
+    myVerboseLevel = verboseLevel;
+  }
+
+  private void logMessage(int minVerboseLevel, String message) {
+    if (myVerboseLevel >= minVerboseLevel) {
+      System.out.print(message);
+    }
+  }
+
+  private void logError(String message) {
+    System.err.println(message);
+  }
+
+  private void logMessageLn(int minVerboseLevel, String message) {
+    if (myVerboseLevel >= minVerboseLevel) {
+      System.out.println(message);
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/GenerateAntMain.java b/java/compiler/impl/src/com/intellij/compiler/ant/GenerateAntMain.java
new file mode 100644
index 0000000..b705a97
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/GenerateAntMain.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.
+ */
+package com.intellij.compiler.ant;
+
+import com.intellij.openapi.application.ApplicationStarter;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author max
+ */
+public class GenerateAntMain implements ApplicationStarter {
+  private GenerateAntApplication myApplication;
+
+  @NonNls
+  public String getCommandName() {
+    return "ant";
+  }
+
+  public void premain(String[] args) {
+    System.setProperty("idea.load.plugins", "false");
+    myApplication = new GenerateAntApplication();
+
+    myApplication.myProjectPath = args[1];
+    myApplication.myOutPath = args[2];    
+  }
+
+  public void main(String[] args) {
+    myApplication.startup();
+  }
+
+  public static void printHelp() {
+    System.out.println("Wrong params");
+    System.exit(1);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/GenerationOptionsImpl.java b/java/compiler/impl/src/com/intellij/compiler/ant/GenerationOptionsImpl.java
new file mode 100644
index 0000000..61158d5
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/GenerationOptionsImpl.java
@@ -0,0 +1,326 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.application.options.ReplacePathToMacroMap;
+import com.intellij.compiler.ModuleCompilerUtil;
+import com.intellij.openapi.application.PathMacros;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.ProjectJdkTable;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.CompilerModuleExtension;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.util.Chunk;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.graph.CachingSemiGraph;
+import com.intellij.util.graph.Graph;
+import com.intellij.util.graph.GraphGenerator;
+
+import javax.naming.OperationNotSupportedException;
+import java.io.File;
+import java.util.*;
+
+/**
+ * Implementation class for Ant generation options
+ *
+ * @author Eugene Zhuravlev
+ *         Date: Mar 25, 2004
+ */
+public class GenerationOptionsImpl extends GenerationOptions {
+
+  /**
+   * from absolute path to macro substitutions
+   */
+  private final ReplacePathToMacroMap myMacroReplacementMap;
+  /**
+   * from absolute path to macro substitutions
+   */
+  private final Map<String, String> myOutputUrlToPropertyRefMap;
+  /**
+   * module chunks
+   */
+  private final ModuleChunk[] myModuleChunks;
+  /**
+   * the project to be converted
+   */
+  private final Project myProject;
+  private final boolean myGenerateIdeaHomeProperty;
+  private final String myOutputFileName;
+  private Set<String> myJdkUrls;
+  /**
+   * Custom compilers used in the ant build.
+   */
+  private final Set<ChunkCustomCompilerExtension> myCustomCompilers = new HashSet<ChunkCustomCompilerExtension>();
+  /**
+   * map from modules to chunks
+   */
+  private final Map<Module, ModuleChunk> myModuleToChunkMap = new HashMap<Module, ModuleChunk>();
+
+  /**
+   * A constructor
+   *
+   * @param project                        a project to generate
+   * @param generateSingleFile             a value of corresponding option
+   * @param enableFormCompiler             a value of corresponding option
+   * @param backupPreviouslyGeneratedFiles a value of corresponding option
+   * @param forceTargetJdk                 a value of corresponding option
+   * @param inlineRuntimeClasspath         if true a runtiem classpaths are inlined
+   * @param representativeModuleNames      a module name that represents module chunks.
+   * @param outputFileName                 a name for the output file
+   */
+  public GenerationOptionsImpl(Project project,
+                               boolean generateSingleFile,
+                               boolean enableFormCompiler,
+                               boolean backupPreviouslyGeneratedFiles,
+                               boolean forceTargetJdk,
+                               boolean inlineRuntimeClasspath,
+                               boolean generateIdeaHomeProperty,
+                               String[] representativeModuleNames, String outputFileName) {
+    super(forceTargetJdk, generateSingleFile, enableFormCompiler, backupPreviouslyGeneratedFiles, inlineRuntimeClasspath);
+    myProject = project;
+    myGenerateIdeaHomeProperty = generateIdeaHomeProperty;
+    myOutputFileName = outputFileName;
+    myMacroReplacementMap = createReplacementMap();
+    myModuleChunks = createModuleChunks(representativeModuleNames);
+    myOutputUrlToPropertyRefMap = createOutputUrlToPropertyRefMap(myModuleChunks);
+  }
+
+  /**
+   * A constructor
+   *
+   * @param project                        a project to generate
+   * @param forceTargetJdk                 a value of corresponding option
+   * @param generateSingleFile             a value of corresponding option
+   * @param enableFormCompiler             a value of corresponding option
+   * @param backupPreviouslyGeneratedFiles a value of corresponding option
+   * @param representativeModuleNames      a module name that represents module chunks.
+   */
+  @Deprecated
+  public GenerationOptionsImpl(Project project,
+                               boolean generateSingleFile,
+                               boolean enableFormCompiler,
+                               boolean backupPreviouslyGeneratedFiles,
+                               boolean forceTargetJdk,
+                               String[] representativeModuleNames) {
+    this(project, forceTargetJdk, generateSingleFile, enableFormCompiler, backupPreviouslyGeneratedFiles, false, false,
+         representativeModuleNames, null);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean isIdeaHomeGenerated() {
+    return myGenerateIdeaHomeProperty;
+  }
+
+  public String getBuildFileName() {
+    return getOutputFileName() + ".xml";
+  }
+
+  public String getPropertiesFileName() {
+    return getOutputFileName() + ".properties";
+  }
+
+  private String getOutputFileName() {
+    if (myOutputFileName == null || myOutputFileName.length() == 0) {
+      return BuildProperties.getProjectBuildFileName(myProject);
+    }
+    return myOutputFileName;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public ModuleChunk getChunkByModule(final Module module) {
+    if (myModuleToChunkMap.isEmpty()) {
+      for (ModuleChunk c : myModuleChunks) {
+        for (Module m : c.getModules()) {
+          myModuleToChunkMap.put(m, c);
+        }
+      }
+    }
+    return myModuleToChunkMap.get(module);
+  }
+
+  @Override
+  public String subsitutePathWithMacros(String path) {
+    return myMacroReplacementMap.substitute(path, SystemInfo.isFileSystemCaseSensitive);
+  }
+
+  public String getPropertyRefForUrl(String url) {
+    return myOutputUrlToPropertyRefMap.get(url);
+  }
+
+  private static ReplacePathToMacroMap createReplacementMap() {
+    final PathMacros pathMacros = PathMacros.getInstance();
+    final Set<String> macroNames = pathMacros.getUserMacroNames();
+    final ReplacePathToMacroMap map = new ReplacePathToMacroMap();
+    for (final String macroName : macroNames) {
+      map.put(GenerationUtils.normalizePath(pathMacros.getValue(macroName)),
+              BuildProperties.propertyRef(BuildProperties.getPathMacroProperty(macroName)));
+    }
+    map.put(GenerationUtils.normalizePath(PathManager.getHomePath()), BuildProperties.propertyRef(BuildProperties.PROPERTY_IDEA_HOME));
+    return map;
+  }
+
+  private static Map<String, String> createOutputUrlToPropertyRefMap(ModuleChunk[] chunks) {
+    final Map<String, String> map = new HashMap<String, String>();
+
+    for (final ModuleChunk chunk : chunks) {
+      final String outputPathRef = BuildProperties.propertyRef(BuildProperties.getOutputPathProperty(chunk.getName()));
+      final String testsOutputPathRef = BuildProperties.propertyRef(BuildProperties.getOutputPathForTestsProperty(chunk.getName()));
+
+      final Module[] modules = chunk.getModules();
+      for (final Module module : modules) {
+        final String outputPathUrl = CompilerModuleExtension.getInstance(module).getCompilerOutputUrl();
+        if (outputPathUrl != null) {
+          map.put(outputPathUrl, outputPathRef);
+        }
+        final String outputPathForTestsUrl = CompilerModuleExtension.getInstance(module).getCompilerOutputUrlForTests();
+        if (outputPathForTestsUrl != null) {
+          if (outputPathUrl == null || !outputPathForTestsUrl.equals(outputPathUrl)) {
+            map.put(outputPathForTestsUrl, testsOutputPathRef);
+          }
+        }
+      }
+    }
+    return map;
+  }
+
+  @Override
+  public ModuleChunk[] getModuleChunks() {
+    return myModuleChunks;
+  }
+
+  private ModuleChunk[] createModuleChunks(String[] representativeModuleNames) {
+    final Set<String> mainModuleNames = new HashSet<String>(Arrays.asList(representativeModuleNames));
+    final Graph<Chunk<Module>> chunkGraph = ModuleCompilerUtil.toChunkGraph(ModuleManager.getInstance(myProject).moduleGraph());
+    final Map<Chunk<Module>, ModuleChunk> map = new HashMap<Chunk<Module>, ModuleChunk>();
+    final Map<ModuleChunk, Chunk<Module>> reverseMap = new HashMap<ModuleChunk, Chunk<Module>>();
+    for (final Chunk<Module> chunk : chunkGraph.getNodes()) {
+      final Set<Module> modules = chunk.getNodes();
+      final ModuleChunk moduleChunk = new ModuleChunk(modules.toArray(new Module[modules.size()]));
+      for (final Module module : modules) {
+        if (mainModuleNames.contains(module.getName())) {
+          moduleChunk.setMainModule(module);
+          break;
+        }
+      }
+      map.put(chunk, moduleChunk);
+      reverseMap.put(moduleChunk, chunk);
+    }
+
+    final Graph<ModuleChunk> moduleChunkGraph =
+      GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph<ModuleChunk>() {
+        public Collection<ModuleChunk> getNodes() {
+          return map.values();
+        }
+
+        public Iterator<ModuleChunk> getIn(ModuleChunk n) {
+          final Chunk<Module> chunk = reverseMap.get(n);
+          final Iterator<Chunk<Module>> in = chunkGraph.getIn(chunk);
+          return new Iterator<ModuleChunk>() {
+            public boolean hasNext() {
+              return in.hasNext();
+            }
+
+            public ModuleChunk next() {
+              return map.get(in.next());
+            }
+
+            public void remove() {
+              new OperationNotSupportedException();
+            }
+          };
+        }
+      }));
+    final Collection<ModuleChunk> nodes = moduleChunkGraph.getNodes();
+    final ModuleChunk[] moduleChunks = nodes.toArray(new ModuleChunk[nodes.size()]);
+    for (ModuleChunk moduleChunk : moduleChunks) {
+      final Iterator<ModuleChunk> depsIterator = moduleChunkGraph.getIn(moduleChunk);
+      List<ModuleChunk> deps = new ArrayList<ModuleChunk>();
+      while (depsIterator.hasNext()) {
+        deps.add(depsIterator.next());
+      }
+      moduleChunk.setDependentChunks(deps.toArray(new ModuleChunk[deps.size()]));
+      ContainerUtil.addAll(myCustomCompilers, moduleChunk.getCustomCompilers());
+    }
+    Arrays.sort(moduleChunks, new ChunksComparator());
+    if (generateSingleFile) {
+      final File baseDir = BuildProperties.getProjectBaseDir(myProject);
+      for (ModuleChunk chunk : moduleChunks) {
+        chunk.setBaseDir(baseDir);
+      }
+    }
+    return moduleChunks;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public ChunkCustomCompilerExtension[] getCustomCompilers() {
+    ChunkCustomCompilerExtension[] sorted = myCustomCompilers.toArray(new ChunkCustomCompilerExtension[myCustomCompilers.size()]);
+    Arrays.sort(sorted, ChunkCustomCompilerExtension.COMPARATOR);
+    return sorted;
+  }
+
+  Set<String> getAllJdkUrls() {
+    if (myJdkUrls != null) {
+      return myJdkUrls;
+    }
+    final Sdk[] projectJdks = ProjectJdkTable.getInstance().getAllJdks();
+    myJdkUrls = new HashSet<String>();
+    for (Sdk jdk : projectJdks) {
+      ContainerUtil.addAll(myJdkUrls, jdk.getRootProvider().getUrls(OrderRootType.CLASSES));
+    }
+    return myJdkUrls;
+  }
+
+  private static class ChunksComparator implements Comparator<ModuleChunk> {
+    final Map<ModuleChunk, Integer> myCachedLevels = new HashMap<ModuleChunk, Integer>();
+
+    public int compare(final ModuleChunk o1, final ModuleChunk o2) {
+      final int level1 = getChunkLevel(o1);
+      final int level2 = getChunkLevel(o2);
+      return (level1 == level2) ? o1.getName().compareToIgnoreCase(o2.getName()) : (level1 - level2);
+    }
+
+    private int getChunkLevel(ModuleChunk chunk) {
+      Integer level = myCachedLevels.get(chunk);
+      if (level == null) {
+        final ModuleChunk[] chunks = chunk.getDependentChunks();
+        if (chunks.length > 0) {
+          int maxLevel = 0;
+          for (ModuleChunk dependent : chunks) {
+            maxLevel = Math.max(maxLevel, getChunkLevel(dependent));
+          }
+          level = 1 + maxLevel;
+        }
+        else {
+          level = 0;
+        }
+        myCachedLevels.put(chunk, level);
+      }
+      return level.intValue();
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/IgnoredFiles.java b/java/compiler/impl/src/com/intellij/compiler/ant/IgnoredFiles.java
new file mode 100644
index 0000000..069ee94
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/IgnoredFiles.java
@@ -0,0 +1,47 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.compiler.ant.taskdefs.Exclude;
+import com.intellij.compiler.ant.taskdefs.PatternSet;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.StringTokenizer;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class IgnoredFiles extends Generator{
+  private final PatternSet myPatternSet;
+
+  public IgnoredFiles() {
+    myPatternSet = new PatternSet(BuildProperties.PROPERTY_IGNORED_FILES);
+    final StringTokenizer tokenizer = new StringTokenizer(FileTypeManager.getInstance().getIgnoredFilesList(), ";", false);
+    while(tokenizer.hasMoreTokens()) {
+      final String filemask = tokenizer.nextToken();
+      myPatternSet.add(new Exclude("**/" + filemask + "/**"));
+    }
+  }
+
+
+
+  public void generate(PrintWriter out) throws IOException {
+    myPatternSet.generate(out);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/LibraryDefinitionsGeneratorFactory.java b/java/compiler/impl/src/com/intellij/compiler/ant/LibraryDefinitionsGeneratorFactory.java
new file mode 100644
index 0000000..5565f21
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/LibraryDefinitionsGeneratorFactory.java
@@ -0,0 +1,181 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.compiler.ant.taskdefs.FileSet;
+import com.intellij.compiler.ant.taskdefs.Path;
+import com.intellij.compiler.ant.taskdefs.PathElement;
+import com.intellij.compiler.ant.taskdefs.PatternSetRef;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.ex.ProjectEx;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.Processor;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Nov 25, 2004
+ */
+public class LibraryDefinitionsGeneratorFactory {
+  private final ProjectEx myProject;
+  private final GenerationOptions myGenOptions;
+  private final Set<String> myUsedLibraries = new HashSet<String>();
+
+  public LibraryDefinitionsGeneratorFactory(ProjectEx project, GenerationOptions genOptions) {
+    myProject = project;
+    myGenOptions = genOptions;
+    final ModuleManager moduleManager = ModuleManager.getInstance(project);
+    final Module[] modules = moduleManager.getModules();
+    for (Module module : modules) {
+      ModuleRootManager.getInstance(module).orderEntries().forEachLibrary(new Processor<Library>() {
+        @Override
+        public boolean process(Library library) {
+          final String name = library.getName();
+          if (name != null) {
+            myUsedLibraries.add(name);
+          }
+          return true;
+        }
+      });
+    }
+  }
+
+  /**
+   * Create a generator for the specified libary type. It generates a list of library definitions.
+   *
+   * @param libraryTable a library table to examine
+   * @param baseDir      base directory for ant build script
+   * @param comment      a comment to use for the library
+   * @return the created generator or null if there is a nothing to generate
+   */
+  @Nullable
+  public Generator create(LibraryTable libraryTable, File baseDir, final String comment) {
+    final Library[] libraries = libraryTable.getLibraries();
+    if (libraries.length == 0) {
+      return null;
+    }
+
+    final CompositeGenerator gen = new CompositeGenerator();
+
+    gen.add(new Comment(comment), 1);
+    // sort libraries to ensure stable order of them.
+    TreeMap<String, Library> sortedLibs = new TreeMap<String, Library>();
+    for (final Library library : libraries) {
+      final String libraryName = library.getName();
+      if (!myUsedLibraries.contains(libraryName)) {
+        continue;
+      }
+      sortedLibs.put(BuildProperties.getLibraryPathId(libraryName), library);
+    }
+    for (final Library library : sortedLibs.values()) {
+      final String libraryName = library.getName();
+      final Path libraryPath = new Path(BuildProperties.getLibraryPathId(libraryName));
+      genLibraryContent(myProject, myGenOptions, library, baseDir, libraryPath);
+      gen.add(libraryPath, 1);
+    }
+    return gen.getGeneratorCount() > 0 ? gen : null;
+  }
+
+  /**
+   * Generate library content
+   *
+   * @param project     the context project
+   * @param genOptions  the generation options
+   * @param library     the library which content is generated
+   * @param baseDir     the base directory
+   * @param libraryPath the composite generator to update
+   */
+  public static void genLibraryContent(final ProjectEx project,
+                                       final GenerationOptions genOptions,
+                                       final Library library,
+                                       final File baseDir,
+                                       final CompositeGenerator libraryPath) {
+    genLibraryContent(genOptions, library, OrderRootType.CLASSES, baseDir, libraryPath);
+  }
+
+  public static void genLibraryContent(final GenerationOptions genOptions,
+                                       final Library library,
+                                       final OrderRootType rootType, final File baseDir,
+                                       final CompositeGenerator libraryPath) {
+    if (genOptions.expandJarDirectories) {
+      final VirtualFile[] files = library.getFiles(rootType);
+      // note that it is assumed that directory entries inside library path are unordered
+      TreeSet<String> visitedPaths = new TreeSet<String>();
+      for (final VirtualFile file : files) {
+        final String path = GenerationUtils
+          .toRelativePath(file, baseDir, BuildProperties.getProjectBaseDirProperty(), genOptions);
+        visitedPaths.add(path);
+      }
+      for (final String path : visitedPaths) {
+        libraryPath.add(new PathElement(path));
+      }
+    }
+    else {
+      TreeSet<String> urls = new TreeSet<String>(Arrays.asList(library.getUrls(rootType)));
+      for (String url : urls) {
+        File file = fileFromUrl(url);
+        final String path = GenerationUtils
+          .toRelativePath(file.getPath(), baseDir, BuildProperties.getProjectBaseDirProperty(), genOptions);
+        if (url.startsWith(JarFileSystem.PROTOCOL_PREFIX)) {
+          libraryPath.add(new PathElement(path));
+        }
+        else if (url.startsWith(LocalFileSystem.PROTOCOL_PREFIX)) {
+          if (library.isJarDirectory(url, rootType)) {
+            final FileSet fileSet = new FileSet(path);
+            fileSet.add(new PatternSetRef(BuildProperties.PROPERTY_LIBRARIES_PATTERNS));
+            libraryPath.add(fileSet);
+          }
+          else {
+            libraryPath.add(new PathElement(path));
+          }
+        }
+        else {
+          throw new IllegalStateException("Unknown url type: " + url);
+        }
+      }
+    }
+  }
+
+  /**
+   * Gets file from jar of file URL
+   *
+   * @param url an url to parse
+   * @return
+   */
+  private static File fileFromUrl(String url) {
+    final String filePart;
+    if (url.startsWith(JarFileSystem.PROTOCOL_PREFIX) && url.endsWith(JarFileSystem.JAR_SEPARATOR)) {
+      filePart = url.substring(JarFileSystem.PROTOCOL_PREFIX.length(), url.length() - JarFileSystem.JAR_SEPARATOR.length());
+    }
+    else if (url.startsWith(LocalFileSystem.PROTOCOL_PREFIX)) {
+      filePart = url.substring(JarFileSystem.PROTOCOL_PREFIX.length());
+    }
+    else {
+      throw new IllegalArgumentException("Unknown url type: " + url);
+    }
+    return new File(filePart.replace('/', File.separatorChar));
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/LibraryPatterns.java b/java/compiler/impl/src/com/intellij/compiler/ant/LibraryPatterns.java
new file mode 100644
index 0000000..ee6ada4
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/LibraryPatterns.java
@@ -0,0 +1,65 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.compiler.ant.taskdefs.Include;
+import com.intellij.compiler.ant.taskdefs.PatternSet;
+import com.intellij.openapi.fileTypes.*;
+import com.intellij.openapi.project.Project;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Library pattern generation (based on {@link FileTypes#ARCHIVE} file type).
+ */
+public class LibraryPatterns extends Generator {
+  /**
+   * A pattern set to use
+   */
+  private final PatternSet myPatternSet;
+
+  /**
+   * A constructor
+   *
+   * @param project    a context project
+   * @param genOptions a generation options
+   */
+  public LibraryPatterns(Project project, GenerationOptions genOptions) {
+    myPatternSet = new PatternSet(BuildProperties.PROPERTY_LIBRARIES_PATTERNS);
+    final FileType type = FileTypes.ARCHIVE;
+    final List<FileNameMatcher> matchers = FileTypeManager.getInstance().getAssociations(type);
+    for (FileNameMatcher m : matchers) {
+      if (m instanceof ExactFileNameMatcher) {
+        final String path = GenerationUtils
+          .toRelativePath(m.getPresentableString(), BuildProperties.getProjectBaseDir(project), BuildProperties.getProjectBaseDirProperty(),
+                          genOptions);
+        myPatternSet.add(new Include(path));
+      }
+      else {
+        myPatternSet.add(new Include(m.getPresentableString()));
+      }
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void generate(final PrintWriter out) throws IOException {
+    myPatternSet.generate(out);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/ModuleChunkAntProject.java b/java/compiler/impl/src/com/intellij/compiler/ant/ModuleChunkAntProject.java
new file mode 100644
index 0000000..8408931
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/ModuleChunkAntProject.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.
+ */
+package com.intellij.compiler.ant;
+
+import com.intellij.compiler.ant.taskdefs.AntProject;
+import com.intellij.compiler.ant.taskdefs.Dirname;
+import com.intellij.openapi.project.Project;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 16, 2004
+ */
+public class ModuleChunkAntProject extends Generator{
+  private final AntProject myAntProject;
+
+  public ModuleChunkAntProject(Project project, ModuleChunk moduleChunk, GenerationOptions genOptions) {
+    myAntProject = new AntProject(BuildProperties.getModuleChunkBuildFileName(moduleChunk), BuildProperties.getCompileTargetName(moduleChunk.getName()));
+    myAntProject.add(new Dirname(BuildProperties.getModuleChunkBasedirProperty(moduleChunk), BuildProperties.propertyRef("ant.file." + BuildProperties.getModuleChunkBuildFileName(moduleChunk))));
+    myAntProject.add(new ChunkBuild(project, moduleChunk, genOptions));
+
+  }
+
+  public void generate(PrintWriter out) throws IOException {
+    writeXmlHeader(out);
+    myAntProject.generate(out);
+  }
+
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/ModuleChunkClasspath.java b/java/compiler/impl/src/com/intellij/compiler/ant/ModuleChunkClasspath.java
new file mode 100644
index 0000000..0883913
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/ModuleChunkClasspath.java
@@ -0,0 +1,364 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.compiler.ant.taskdefs.Path;
+import com.intellij.compiler.ant.taskdefs.PathElement;
+import com.intellij.compiler.ant.taskdefs.PathRef;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.ex.ProjectEx;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.Processor;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.OrderedSet;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Generator of module chunk classspath. The class used to generate both runtime and compile time classpaths.
+ *
+ * @author Eugene Zhuravlev
+ */
+public class ModuleChunkClasspath extends Path {
+
+  /**
+   * A constructor
+   *
+   * @param chunk                    a chunk to process
+   * @param genOptions               a generation options
+   * @param generateRuntimeClasspath if true, runtime classpath is being generated. Otherwise a compile time classpath is constructed
+   * @param generateTestClasspath    if true, a test classpath is generated.
+   */
+  @SuppressWarnings({"unchecked"})
+  public ModuleChunkClasspath(final ModuleChunk chunk,
+                              final GenerationOptions genOptions,
+                              final boolean generateRuntimeClasspath,
+                              final boolean generateTestClasspath) {
+    super(generateClasspathName(chunk, generateRuntimeClasspath, generateTestClasspath));
+
+    final OrderedSet<ClasspathItem> pathItems =
+      new OrderedSet<ClasspathItem>();
+    final String moduleChunkBasedirProperty = BuildProperties.getModuleChunkBasedirProperty(chunk);
+    final Module[] modules = chunk.getModules();
+    // processed chunks (used only in runtime classpath), every chunk is referenced exactly once
+    final Set<ModuleChunk> processedChunks = new HashSet<ModuleChunk>();
+    // pocessed modules
+    final Set<Module> processedModules = new HashSet<Module>();
+    for (final Module module : modules) {
+      new Object() {
+        /**
+         * Process the module. The logic is different for compile-time case and runtime case.
+         * In the case of runtime, only directly referenced objects are included in classpath.
+         * Indirectly referenced are
+         *
+         * @param module a module to process.
+         * @param dependencyLevel is increased with every of recursion.
+         * @param isModuleExported if true the module is exported from the previous level
+         */
+        public void processModule(final Module module, final int dependencyLevel, final boolean isModuleExported) {
+          if (processedModules.contains(module)) {
+            // the module is already processed, nothing should be done
+            return;
+          }
+          if (dependencyLevel > 1 && !isModuleExported && !(genOptions.inlineRuntimeClasspath && generateRuntimeClasspath)) {
+            // the module is not in exports and it is not directly included skip it in the case of library pathgeneration
+            return;
+          }
+          processedModules.add(module);
+          final ProjectEx project = (ProjectEx)chunk.getProject();
+          final File baseDir = BuildProperties.getProjectBaseDir(project);
+          OrderEnumerator enumerator = ModuleRootManager.getInstance(module).orderEntries();
+          if (generateRuntimeClasspath) {
+            enumerator = enumerator.runtimeOnly();
+          }
+          else {
+            enumerator = enumerator.compileOnly();
+            if (!generateTestClasspath && (dependencyLevel == 0 || chunk.contains(module))) {
+              // this is the entry for outpath of the currently processed module
+              // the root module is never included
+              enumerator = enumerator.withoutModuleSourceEntries();
+            }
+          }
+          if (!generateTestClasspath) {
+            enumerator = enumerator.productionOnly();
+          }
+          enumerator.forEach(new Processor<OrderEntry>() {
+            @Override
+            public boolean process(OrderEntry orderEntry) {
+              if (!orderEntry.isValid()) {
+                return true;
+              }
+
+              if (!generateRuntimeClasspath &&
+                  !(orderEntry instanceof ModuleOrderEntry) &&
+                  !(orderEntry instanceof ModuleSourceOrderEntry)) {
+                // needed for compilation classpath only
+                final boolean isExported = (orderEntry instanceof ExportableOrderEntry) && ((ExportableOrderEntry)orderEntry).isExported();
+                if (dependencyLevel > 0 && !isExported) {
+                  // non-exported dependencies are excluded and not processed
+                  return true;
+                }
+              }
+
+              if (orderEntry instanceof JdkOrderEntry) {
+                if (genOptions.forceTargetJdk && !generateRuntimeClasspath) {
+                  pathItems
+                    .add(new PathRefItem(BuildProperties.propertyRef(BuildProperties.getModuleChunkJdkClasspathProperty(chunk.getName()))));
+                }
+              }
+              else if (orderEntry instanceof ModuleOrderEntry) {
+                final ModuleOrderEntry moduleOrderEntry = (ModuleOrderEntry)orderEntry;
+                final Module dependentModule = moduleOrderEntry.getModule();
+                if (!chunk.contains(dependentModule)) {
+                  if (generateRuntimeClasspath && !genOptions.inlineRuntimeClasspath) {
+                    // in case of runtime classpath, just an referenced to corresponding classpath is created
+                    final ModuleChunk depChunk = genOptions.getChunkByModule(dependentModule);
+                    if (!processedChunks.contains(depChunk)) {
+                      // chunk references are included in the runtime classpath only once
+                      processedChunks.add(depChunk);
+                      String property = generateTestClasspath ? BuildProperties.getTestRuntimeClasspathProperty(depChunk.getName())
+                                                              : BuildProperties.getRuntimeClasspathProperty(depChunk.getName());
+                      pathItems.add(new PathRefItem(property));
+                    }
+                  }
+                  else {
+                    // in case of compile classpath or inlined runtime classpath,
+                    // the referenced module is processed recursively
+                    processModule(dependentModule, dependencyLevel + 1, moduleOrderEntry.isExported());
+                  }
+                }
+              }
+              else if (orderEntry instanceof LibraryOrderEntry) {
+                final LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)orderEntry;
+                final String libraryName = libraryOrderEntry.getLibraryName();
+                if (((LibraryOrderEntry)orderEntry).isModuleLevel()) {
+                  CompositeGenerator gen = new CompositeGenerator();
+                  gen.setHasLeadingNewline(false);
+                  LibraryDefinitionsGeneratorFactory.genLibraryContent(project, genOptions, libraryOrderEntry.getLibrary(), baseDir, gen);
+                  pathItems.add(new GeneratorItem(libraryName, gen));
+                }
+                else {
+                  pathItems.add(new PathRefItem(BuildProperties.getLibraryPathId(libraryName)));
+                }
+              }
+              else if (orderEntry instanceof ModuleSourceOrderEntry) {
+                // Module source entry?
+                for (String url : getCompilationClasses(module, ((GenerationOptionsImpl)genOptions), generateRuntimeClasspath,
+                                                        generateTestClasspath, dependencyLevel == 0)) {
+                  if (url.endsWith(JarFileSystem.JAR_SEPARATOR)) {
+                    url = url.substring(0, url.length() - JarFileSystem.JAR_SEPARATOR.length());
+                  }
+                  final String propertyRef = genOptions.getPropertyRefForUrl(url);
+                  if (propertyRef != null) {
+                    pathItems.add(new PathElementItem(propertyRef));
+                  }
+                  else {
+                    final String path = VirtualFileManager.extractPath(url);
+                    pathItems.add(new PathElementItem(
+                      GenerationUtils.toRelativePath(path, chunk.getBaseDir(), moduleChunkBasedirProperty, genOptions)));
+                  }
+                }
+              }
+              else {
+                // Unknown order entry type. If it is actually encountered, extension point should be implemented
+                pathItems.add(new GeneratorItem(orderEntry.getClass().getName(),
+                                                new Comment("Unknown OrderEntryType: " + orderEntry.getClass().getName())));
+              }
+              return true;
+            }
+          });
+        }
+      }.processModule(module, 0, false);
+    }
+    // convert path items to generators
+    for (final ClasspathItem pathItem : pathItems) {
+      add(pathItem.toGenerator());
+    }
+  }
+
+  /**
+   * Generate classpath name
+   *
+   * @param chunk                    a chunk
+   * @param generateRuntimeClasspath
+   * @param generateTestClasspath
+   * @return a name for the classpath
+   */
+  private static String generateClasspathName(ModuleChunk chunk, boolean generateRuntimeClasspath, boolean generateTestClasspath) {
+    if (generateTestClasspath) {
+      return generateRuntimeClasspath
+             ? BuildProperties.getTestRuntimeClasspathProperty(chunk.getName())
+             : BuildProperties.getTestClasspathProperty(chunk.getName());
+    }
+    else {
+      return generateRuntimeClasspath
+             ? BuildProperties.getRuntimeClasspathProperty(chunk.getName())
+             : BuildProperties.getClasspathProperty(chunk.getName());
+    }
+  }
+
+  private static String[] getCompilationClasses(final Module module,
+                                                final GenerationOptionsImpl options,
+                                                final boolean forRuntime,
+                                                final boolean forTest,
+                                                final boolean firstLevel) {
+    final CompilerModuleExtension extension = CompilerModuleExtension.getInstance(module);
+    if (extension == null) return ArrayUtil.EMPTY_STRING_ARRAY;
+
+    if (!forRuntime) {
+      if (forTest) {
+        return extension.getOutputRootUrls(!firstLevel);
+      }
+      else {
+        return firstLevel ? ArrayUtil.EMPTY_STRING_ARRAY : extension.getOutputRootUrls(false);
+      }
+    }
+    final Set<String> jdkUrls = options.getAllJdkUrls();
+
+    final OrderedSet<String> urls = new OrderedSet<String>();
+    ContainerUtil.addAll(urls, extension.getOutputRootUrls(forTest));
+    urls.removeAll(jdkUrls);
+    return ArrayUtil.toStringArray(urls);
+  }
+
+  /**
+   * The base class for an item in the class path. Instances of the subclasses are used instead
+   * of generators when building the class path content. The subclasses implement {@link Object#equals(Object)}
+   * and {@link Object#hashCode()} methods in order to eliminate duplicates when building classpath.
+   */
+  private abstract static class ClasspathItem {
+    /**
+     * primary reference or path of the element
+     */
+    protected final String myValue;
+
+    /**
+     * A constructor
+     *
+     * @param value primary value of the element
+     */
+    public ClasspathItem(String value) {
+      myValue = value;
+    }
+
+    /**
+     * @return a generator for path elements.
+     */
+    public abstract Generator toGenerator();
+  }
+
+  /**
+   * Class path item that directly embeds generator
+   */
+  private static class GeneratorItem extends ClasspathItem {
+    /**
+     * An embedded generator
+     */
+    final Generator myGenerator;
+
+    /**
+     * A constructor
+     *
+     * @param value     primary value of the element
+     * @param generator a generator to use
+     */
+    public GeneratorItem(String value, final Generator generator) {
+      super(value);
+      myGenerator = generator;
+    }
+
+    public Generator toGenerator() {
+      return myGenerator;
+    }
+  }
+
+  /**
+   * This path element directly references some location.
+   */
+  private static class PathElementItem extends ClasspathItem {
+    /**
+     * A constructor
+     *
+     * @param value a referenced location
+     */
+    public PathElementItem(String value) {
+      super(value);
+    }
+
+    @Override
+    public Generator toGenerator() {
+      return new PathElement(myValue);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (!(o instanceof PathElementItem)) return false;
+
+      final PathElementItem pathElementItem = (PathElementItem)o;
+
+      if (!myValue.equals(pathElementItem.myValue)) return false;
+
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      return myValue.hashCode();
+    }
+  }
+
+  /**
+   * This path element references a path
+   */
+  private static class PathRefItem extends ClasspathItem {
+    /**
+     * A constructor
+     *
+     * @param value an indentifier of referenced classpath
+     */
+    public PathRefItem(String value) {
+      super(value);
+    }
+
+    @Override
+    public Generator toGenerator() {
+      return new PathRef(myValue);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (!(o instanceof PathRefItem)) return false;
+
+      final PathRefItem pathRefItem = (PathRefItem)o;
+
+      if (!myValue.equals(pathRefItem.myValue)) return false;
+
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      return myValue.hashCode();
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/ModuleChunkSourcePath.java b/java/compiler/impl/src/com/intellij/compiler/ant/ModuleChunkSourcePath.java
new file mode 100644
index 0000000..ebe3f5d
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/ModuleChunkSourcePath.java
@@ -0,0 +1,183 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.compiler.ant.taskdefs.*;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ContentEntry;
+import com.intellij.openapi.roots.ModuleFileIndex;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileVisitor;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ * @since Nov 22, 2004
+ */
+public class ModuleChunkSourcePath extends CompositeGenerator{
+  private final VirtualFile[] mySourceRoots;
+  private final VirtualFile[] myTestSourceRoots;
+
+  public ModuleChunkSourcePath(final Project project, ModuleChunk chunk, final GenerationOptions genOptions) {
+    final Path sourcePath = new Path(BuildProperties.getSourcepathProperty(chunk.getName()));
+    final Path testSourcePath = new Path(BuildProperties.getTestSourcepathProperty(chunk.getName()));
+    final PatternSet excludedFromCompilation = new PatternSet(BuildProperties.getExcludedFromCompilationProperty(chunk.getName()));
+    final String moduleChunkBasedirProperty = BuildProperties.getModuleChunkBasedirProperty(chunk);
+    final Module[] modules = chunk.getModules();
+
+    if (CompilerExcludes.isAvailable(project)) {
+      excludedFromCompilation.add(new PatternSetRef(BuildProperties.PROPERTY_COMPILER_EXCLUDES));
+    }
+
+    final List<VirtualFile> sourceRootFiles = new ArrayList<VirtualFile>();
+    final List<VirtualFile> testSourceRootFiles = new ArrayList<VirtualFile>();
+
+    for (final Module module : modules) {
+      final String moduleName = module.getName();
+      final ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
+      final ModuleFileIndex moduleFileIndex = rootManager.getFileIndex();
+
+
+      final PatternSet excludedFromModule = new PatternSet(BuildProperties.getExcludedFromModuleProperty(moduleName));
+      excludedFromModule.add(new PatternSetRef(BuildProperties.PROPERTY_IGNORED_FILES));
+
+      final ContentEntry[] contentEntries = rootManager.getContentEntries();
+      for (final ContentEntry contentEntry : contentEntries) {
+        final VirtualFile file = contentEntry.getFile();
+        if (file == null) {
+          continue; // filter invalid entries
+        }
+        if (!file.isInLocalFileSystem()) {
+          continue; // skip content roots inside jar and zip archives
+        }
+        final VirtualFile dirSetRoot = getDirSetRoot(contentEntry);
+
+        final String dirSetRootRelativeToBasedir = GenerationUtils
+          .toRelativePath(dirSetRoot, chunk.getBaseDir(), moduleChunkBasedirProperty, genOptions);
+        final DirSet sourcesDirSet = new DirSet(dirSetRootRelativeToBasedir);
+        final DirSet testSourcesDirSet = new DirSet(dirSetRootRelativeToBasedir);
+
+        final VirtualFile[] sourceRoots = contentEntry.getSourceFolderFiles();
+        for (final VirtualFile root : sourceRoots) {
+          if (!moduleFileIndex.isInContent(root)) {
+            continue; // skip library sources
+          }
+
+          addExcludePatterns(module, root, root, excludedFromModule, true);
+
+          final Include include = new Include(VfsUtilCore.getRelativePath(root, dirSetRoot, '/'));
+          if (moduleFileIndex.isInTestSourceContent(root)) {
+            testSourcesDirSet.add(include);
+            testSourceRootFiles.add(root);
+          }
+          else {
+            sourcesDirSet.add(include);
+            sourceRootFiles.add(root);
+          }
+        }
+        if (sourcesDirSet.getGeneratorCount() > 0) {
+          sourcePath.add(sourcesDirSet);
+        }
+        if (testSourcesDirSet.getGeneratorCount() > 0) {
+          testSourcePath.add(testSourcesDirSet);
+        }
+      }
+
+      if (excludedFromModule.getGeneratorCount() > 0) {
+        add(excludedFromModule);
+        excludedFromCompilation.add(new PatternSetRef(BuildProperties.getExcludedFromModuleProperty(moduleName)));
+      }
+    }
+
+    mySourceRoots = VfsUtilCore.toVirtualFileArray(sourceRootFiles);
+    myTestSourceRoots = VfsUtilCore.toVirtualFileArray(testSourceRootFiles);
+
+    if (excludedFromCompilation.getGeneratorCount() > 0) {
+      add(excludedFromCompilation, 1);
+    }
+    if (sourcePath.getGeneratorCount() > 0) {
+      add(sourcePath, 1);
+    }
+    if (testSourcePath.getGeneratorCount() != 0) {
+      add(testSourcePath, 1);
+    }
+  }
+
+  public VirtualFile[] getSourceRoots() {
+    return mySourceRoots;
+  }
+
+  public VirtualFile[] getTestSourceRoots() {
+    return myTestSourceRoots;
+  }
+
+  private VirtualFile getDirSetRoot(final ContentEntry contentEntry) {
+    final VirtualFile contentRoot = contentEntry.getFile();
+    final VirtualFile[] sourceFolderFiles = contentEntry.getSourceFolderFiles();
+    for (VirtualFile sourceFolderFile : sourceFolderFiles) {
+      if (contentRoot.equals(sourceFolderFile)) {
+        return contentRoot.getParent();
+      }
+    }
+    return contentRoot;
+  }
+
+  private static void addExcludePatterns(Module module,
+                                         final VirtualFile root,
+                                         VirtualFile dir,
+                                         final CompositeGenerator generator,
+                                         final boolean parentIncluded) {
+    final FileTypeManager fileTypeManager = FileTypeManager.getInstance();
+    final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
+
+    VfsUtilCore.visitChildrenRecursively(dir, new VirtualFileVisitor() {
+      @Override
+      public boolean visitFile(@NotNull VirtualFile dir) {
+        if (!dir.isDirectory() || fileTypeManager.isFileIgnored(dir)) {
+          // ignored files are handled by global 'ignored' pattern set
+          return false;
+        }
+
+        final boolean isIncluded = moduleRootManager.getFileIndex().isInContent(dir);
+        if (isIncluded != parentIncluded) {
+          final String relativePath = VfsUtilCore.getRelativePath(dir, root, '/');
+          if (isIncluded) {
+            generator.add(new Include(relativePath + "/**"));
+          }
+          else {
+            if (!isExcludedByDefault(dir.getName())) {
+              generator.add(new Exclude(relativePath + "/**"));
+            }
+          }
+        }
+
+        return true;
+      }
+    });
+  }
+
+  private static boolean isExcludedByDefault(String name) {
+    //noinspection HardCodedStringLiteral
+    return "CVS".equals(name) || "SCCS".equals(name) || ".DS_Store".equals(name);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/ModuleSources.java b/java/compiler/impl/src/com/intellij/compiler/ant/ModuleSources.java
new file mode 100644
index 0000000..d9d6e07
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/ModuleSources.java
@@ -0,0 +1,127 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.ContentEntry;
+import com.intellij.openapi.vfs.VirtualFile;
+
+import java.io.File;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class ModuleSources extends CompositeGenerator{
+  private final VirtualFile[] mySourceRoots = VirtualFile.EMPTY_ARRAY;
+  private final VirtualFile[] myTestSourceRoots = VirtualFile.EMPTY_ARRAY;
+
+  public ModuleSources(Module module, File baseDir, final GenerationOptions genOptions) {
+    /*
+    final ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
+    final ModuleFileIndex moduleFileIndex = rootManager.getFileIndex();
+
+    final List<VirtualFile> sourceRootFiles = new ArrayList<VirtualFile>();
+    final List<VirtualFile> testSourceRootFiles = new ArrayList<VirtualFile>();
+
+    final Path sourcepath = new Path(BuildProperties.getSourcepathProperty(module.getName()));
+    final Path testSourcepath = new Path(BuildProperties.getTestSourcepathProperty(module.getName()));
+    final PatternSet excludedFromModule = new PatternSet(BuildProperties.getExcludedFromModuleProperty(module.getName()));
+
+    final ContentEntry[] contentEntries = rootManager.getContentEntries();
+    for (int idx = 0; idx < contentEntries.length; idx++) {
+      final ContentEntry contentEntry = contentEntries[idx];
+      final VirtualFile file = contentEntry.getFile();
+      if (file == null) {
+        continue; // filter invalid entries
+      }
+      if (!(file.getFileSystem() instanceof LocalFileSystem)) {
+        continue; // skip content roots inside jar and zip archives
+      }
+      final VirtualFile dirSetRoot = getDirSetRoot(contentEntry);
+
+      final String dirSetRootRelativeToBasedir = GenerationUtils.toRelativePath(dirSetRoot, baseDir, BuildProperties.getModuleChunkBasedirProperty(module), genOptions, !module.isSavePathsRelative());
+      final DirSet sourcesDirSet = new DirSet(dirSetRootRelativeToBasedir);
+      final DirSet testSourcesDirSet = new DirSet(dirSetRootRelativeToBasedir);
+
+      final VirtualFile[] sourceRoots = contentEntry.getSourceFolderFiles();
+      for (int i = 0; i < sourceRoots.length; i++) {
+        final VirtualFile root = sourceRoots[i];
+        if (!moduleFileIndex.isInContent(root)) {
+          continue; // skip library sources
+        }
+
+        addExcludePatterns(module, root, root, excludedFromModule, true);
+
+        final Include include = new Include(VfsUtil.getRelativePath(root, dirSetRoot, '/'));
+        if (moduleFileIndex.isInTestSourceContent(root)) {
+          testSourcesDirSet.add(include);
+          testSourceRootFiles.add(root);
+        }
+        else {
+          sourcesDirSet.add(include);
+          sourceRootFiles.add(root);
+        }
+      }
+      if (sourcesDirSet.getGeneratorCount() > 0) {
+        sourcepath.add(sourcesDirSet);
+      }
+      if (testSourcesDirSet.getGeneratorCount() > 0) {
+        testSourcepath.add(testSourcesDirSet);
+      }
+    }
+
+    mySourceRoots = sourceRootFiles.toArray(new VirtualFile[sourceRootFiles.size()]);
+    myTestSourceRoots = testSourceRootFiles.toArray(new VirtualFile[testSourceRootFiles.size()]);
+
+    final String moduleName = module.getName();
+
+    add(excludedFromModule);
+
+    final PatternSet excludedFromCompilation = new PatternSet(BuildProperties.getExcludedFromCompilationProperty(moduleName));
+    excludedFromCompilation.add(new PatternSetRef(BuildProperties.getExcludedFromModuleProperty(moduleName)));
+    excludedFromCompilation.add(new PatternSetRef(BuildProperties.PROPERTY_COMPILER_EXCLUDES));
+    add(excludedFromCompilation, 1);
+
+    if (sourcepath.getGeneratorCount() > 0) {
+      add(sourcepath, 1);
+    }
+    if (testSourcepath.getGeneratorCount() != 0) {
+      add(testSourcepath, 1);
+    }
+    */
+  }
+
+  private VirtualFile getDirSetRoot(final ContentEntry contentEntry) {
+    final VirtualFile contentRoot = contentEntry.getFile();
+    final VirtualFile[] sourceFolderFiles = contentEntry.getSourceFolderFiles();
+    for (VirtualFile sourceFolderFile : sourceFolderFiles) {
+      if (contentRoot.equals(sourceFolderFile)) {
+        return contentRoot.getParent();
+      }
+    }
+    return contentRoot;
+  }
+
+  public VirtualFile[] getSourceRoots() {
+    return mySourceRoots;
+  }
+
+  public VirtualFile[] getTestSourceRoots() {
+    return myTestSourceRoots;
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/MultipleFileProjectBuild.java b/java/compiler/impl/src/com/intellij/compiler/ant/MultipleFileProjectBuild.java
new file mode 100644
index 0000000..9916ee0
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/MultipleFileProjectBuild.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 com.intellij.compiler.ant;
+
+import com.intellij.compiler.ant.taskdefs.Import;
+import com.intellij.openapi.project.Project;
+
+import java.io.File;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 16, 2004
+ */
+public class MultipleFileProjectBuild extends ProjectBuild{
+  public MultipleFileProjectBuild(Project project, GenerationOptions genOptions) {
+    super(project, genOptions);
+  }
+
+  protected Generator createModuleBuildGenerator(ModuleChunk chunk, GenerationOptions genOptions) {
+    //noinspection HardCodedStringLiteral
+    final String chunkBuildFile = BuildProperties.getModuleChunkBaseDir(chunk).getPath() + File.separator + BuildProperties.getModuleChunkBuildFileName(chunk) + ".xml";
+    final File projectBaseDir = BuildProperties.getProjectBaseDir(myProject);
+    final String pathToFile = GenerationUtils.toRelativePath(
+      chunkBuildFile, projectBaseDir, BuildProperties.getProjectBaseDirProperty(), genOptions);
+    return new Import(pathToFile);
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/ProjectBuild.java b/java/compiler/impl/src/com/intellij/compiler/ant/ProjectBuild.java
new file mode 100644
index 0000000..7d4c3d9
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/ProjectBuild.java
@@ -0,0 +1,103 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.compiler.ant.artifacts.ArtifactsGenerator;
+import com.intellij.compiler.ant.taskdefs.AntProject;
+import com.intellij.compiler.ant.taskdefs.Target;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.project.Project;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 24, 2004
+ */
+public abstract class ProjectBuild extends Generator {
+  protected final Project myProject;
+  private final AntProject myAntProject;
+
+  public ProjectBuild(Project project, GenerationOptions genOptions) {
+    myProject = project;
+    myAntProject = new AntProject(BuildProperties.getProjectBuildFileName(myProject), BuildProperties.DEFAULT_TARGET);
+
+    myAntProject.add(new BuildPropertiesImpl(myProject, genOptions), 1);
+
+    // the sequence in which modules are imported is important cause output path properties for dependent modules should be defined first
+
+    final StringBuilder buildModulesTargetNames = new StringBuilder();
+    buildModulesTargetNames.append(BuildProperties.TARGET_INIT);
+    buildModulesTargetNames.append(", ");
+    buildModulesTargetNames.append(BuildProperties.TARGET_CLEAN);
+    final ModuleChunk[] chunks = genOptions.getModuleChunks();
+
+    if (chunks.length > 0) {
+      myAntProject.add(new Comment(CompilerBundle.message("generated.ant.build.modules.section.title")), 1);
+
+      for (final ModuleChunk chunk : chunks) {
+        myAntProject.add(createModuleBuildGenerator(chunk, genOptions), 1);
+        final String[] targets = ChunkBuildExtension.getAllTargets(chunk);
+        for (String target : targets) {
+          if (buildModulesTargetNames.length() > 0) {
+            buildModulesTargetNames.append(", ");
+          }
+          buildModulesTargetNames.append(target);
+        }
+      }
+    }
+    for (ChunkBuildExtension extension : ChunkBuildExtension.EP_NAME.getExtensions()) {
+      extension.generateProjectTargets(project, genOptions, myAntProject);
+    }
+
+    final Target initTarget = new Target(BuildProperties.TARGET_INIT, null,
+                                         CompilerBundle.message("generated.ant.build.initialization.section.title"), null);
+    initTarget.add(new Comment(CompilerBundle.message("generated.ant.build.initialization.section.comment")));
+    myAntProject.add(initTarget, 1);
+
+    ArtifactsGenerator artifactsGenerator = new ArtifactsGenerator(project, genOptions);
+
+    myAntProject.add(new CleanProject(project, genOptions, artifactsGenerator), 1);
+
+    myAntProject.add(new Target(BuildProperties.TARGET_BUILD_MODULES, buildModulesTargetNames.toString(),
+                                CompilerBundle.message("generated.ant.build.build.all.modules.target.name"), null), 1);
+    
+    StringBuilder buildAllTargetNames = new StringBuilder();
+    buildAllTargetNames.append(BuildProperties.TARGET_BUILD_MODULES);
+    if (artifactsGenerator.hasArtifacts()) {
+      List<Generator> generators = artifactsGenerator.generate();
+      for (Generator generator : generators) {
+        myAntProject.add(generator, 1);
+      }
+
+      buildAllTargetNames.append(", ").append(ArtifactsGenerator.BUILD_ALL_ARTIFACTS_TARGET);
+    }
+
+    myAntProject.add(new Target(BuildProperties.TARGET_ALL, buildAllTargetNames.toString(),
+                                CompilerBundle.message("generated.ant.build.build.all.target.name"), null), 1);
+  }
+
+  public void generate(PrintWriter out) throws IOException {
+    //noinspection HardCodedStringLiteral
+    writeXmlHeader(out);
+    myAntProject.generate(out);
+  }
+
+  protected abstract Generator createModuleBuildGenerator(final ModuleChunk chunk, GenerationOptions genOptions);
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/PropertyFileGeneratorImpl.java b/java/compiler/impl/src/com/intellij/compiler/ant/PropertyFileGeneratorImpl.java
new file mode 100644
index 0000000..ab93b32
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/PropertyFileGeneratorImpl.java
@@ -0,0 +1,101 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.openapi.application.PathMacros;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.util.ArrayUtil;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Generator for property files.
+ *
+ * @author Eugene Zhuravlev
+ *         Date: Nov 27, 2004
+ */
+public class PropertyFileGeneratorImpl extends PropertyFileGenerator {
+    /**
+     * List of the properties
+     */
+    private final List<Pair<String, String>> myProperties = new ArrayList<Pair<String, String>>();
+
+    /**
+     * A constctor that extracts all neeed properties for ant build from the project.
+     *
+     * @param project    a project to examine
+     * @param genOptions generation options
+     */
+    public PropertyFileGeneratorImpl(Project project, GenerationOptions genOptions) {
+        // path variables
+        final PathMacros pathMacros = PathMacros.getInstance();
+        final Set<String> macroNamesSet = pathMacros.getUserMacroNames();
+        if (macroNamesSet.size() > 0) {
+          final String[] macroNames = ArrayUtil.toStringArray(macroNamesSet);
+            Arrays.sort(macroNames);
+            for (final String macroName : macroNames) {
+                addProperty(BuildProperties.getPathMacroProperty(macroName), pathMacros.getValue(macroName));
+            }
+        }
+        // jdk homes
+        if (genOptions.forceTargetJdk) {
+            final Sdk[] usedJdks = BuildProperties.getUsedJdks(project);
+            for (Sdk jdk : usedJdks) {
+                if (jdk.getHomeDirectory() == null) {
+                    continue;
+                }
+                final File homeDir = BuildProperties.toCanonicalFile(VfsUtil.virtualToIoFile(jdk.getHomeDirectory()));
+                addProperty(BuildProperties.getJdkHomeProperty(jdk.getName()), homeDir.getPath().replace(File.separatorChar, '/'));
+            }
+        }
+        // generate idea.home property
+        if (genOptions.isIdeaHomeGenerated()) {
+            addProperty(BuildProperties.PROPERTY_IDEA_HOME, PathManager.getHomePath());
+        }
+        ChunkBuildExtension.generateAllProperties(this, project, genOptions);
+    }
+
+    public void addProperty(String name, String value) {
+        myProperties.add(new Pair<String, String>(name, value));
+    }
+
+    @Override
+    public void generate(PrintWriter out) throws IOException {
+        boolean isFirst = true;
+        for (final Pair<String, String> pair : myProperties) {
+            if (!isFirst) {
+                crlf(out);
+            }
+            else {
+                isFirst = false;
+            }
+            out.print(StringUtil.escapeProperty(pair.getFirst(), true));
+            out.print("=");
+            out.print(StringUtil.escapeProperty(pair.getSecond(), false));
+        }
+    }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/SingleFileProjectBuild.java b/java/compiler/impl/src/com/intellij/compiler/ant/SingleFileProjectBuild.java
new file mode 100644
index 0000000..d084879
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/SingleFileProjectBuild.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 com.intellij.compiler.ant;
+
+import com.intellij.compiler.ant.taskdefs.Dirname;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.compiler.CompilerBundle;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 16, 2004
+ */
+public class SingleFileProjectBuild extends ProjectBuild {
+  public SingleFileProjectBuild(Project project, GenerationOptions genOptions) {
+    super(project, genOptions);
+  }
+
+  protected Generator createModuleBuildGenerator(ModuleChunk chunk, GenerationOptions genOptions) {
+    final CompositeGenerator gen = new CompositeGenerator();
+    gen.add(new Comment(CompilerBundle.message("generated.ant.build.building.concrete.module.section.title", chunk.getName())));
+    gen.add(new Dirname(BuildProperties.getModuleChunkBasedirProperty(chunk), BuildProperties.propertyRef("ant.file")), 1);
+    gen.add(new ChunkBuild(myProject, chunk, genOptions), 1);
+    return gen;
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/ArchiveAntCopyInstructionCreator.java b/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/ArchiveAntCopyInstructionCreator.java
new file mode 100644
index 0000000..4ede60c
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/ArchiveAntCopyInstructionCreator.java
@@ -0,0 +1,59 @@
+/*
+ * 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 com.intellij.compiler.ant.artifacts;
+
+import com.intellij.compiler.ant.Generator;
+import com.intellij.compiler.ant.Tag;
+import com.intellij.compiler.ant.taskdefs.ZipFileSet;
+import com.intellij.packaging.elements.AntCopyInstructionCreator;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class ArchiveAntCopyInstructionCreator implements AntCopyInstructionCreator {
+  private final String myPrefix;
+
+  public ArchiveAntCopyInstructionCreator(String prefix) {
+    myPrefix = prefix;
+  }
+
+  @NotNull
+  public Tag createDirectoryContentCopyInstruction(@NotNull String dirPath) {
+    return new ZipFileSet(dirPath, myPrefix, true);
+  }
+
+  @NotNull
+  public Tag createFileCopyInstruction(@NotNull String filePath, String outputFileName) {
+    final String relativePath = myPrefix + "/" + outputFileName;
+    return new ZipFileSet(filePath, relativePath, false);
+  }
+
+  @NotNull
+  public AntCopyInstructionCreator subFolder(@NotNull String directoryName) {
+    return new ArchiveAntCopyInstructionCreator(myPrefix + "/" + directoryName);
+  }
+
+  public Generator createSubFolderCommand(@NotNull String directoryName) {
+    return null;
+  }
+
+  @NotNull
+  @Override
+  public Generator createExtractedDirectoryInstruction(@NotNull String jarPath) {
+    return ZipFileSet.createUnpackedSet(jarPath, myPrefix, true);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/ArtifactAntGenerationContextImpl.java b/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/ArtifactAntGenerationContextImpl.java
new file mode 100644
index 0000000..b422760
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/ArtifactAntGenerationContextImpl.java
@@ -0,0 +1,166 @@
+/*
+ * 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 com.intellij.compiler.ant.artifacts;
+
+import com.intellij.compiler.ant.BuildProperties;
+import com.intellij.compiler.ant.GenerationOptions;
+import com.intellij.compiler.ant.GenerationUtils;
+import com.intellij.compiler.ant.Generator;
+import com.intellij.compiler.ant.taskdefs.Mkdir;
+import com.intellij.compiler.ant.taskdefs.Property;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.ArtifactAntGenerationContext;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import gnu.trove.THashMap;
+import gnu.trove.THashSet;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class ArtifactAntGenerationContextImpl implements ArtifactAntGenerationContext {
+  @NonNls public static final String ARTIFACTS_TEMP_DIR_PROPERTY = "artifacts.temp.dir";
+  private final Map<Artifact, String> myArtifact2Target = new THashMap<Artifact, String>();
+  private final List<Generator> myBeforeBuildGenerators = new ArrayList<Generator>();
+  private final List<Generator> myAfterBuildGenerators = new ArrayList<Generator>();
+  private final Set<String> myTempFileNames = new THashSet<String>();
+  private final Set<String> myCreatedTempSubdirs = new THashSet<String>();
+  private final Set<String> myProperties = new LinkedHashSet<String>();
+  private final Project myProject;
+  private final GenerationOptions myGenerationOptions;
+  private final List<Generator> myBeforeCurrentArtifact = new ArrayList<Generator>();
+  private final Set<Artifact> myArtifactsToClean = new THashSet<Artifact>();
+
+  public ArtifactAntGenerationContextImpl(Project project, GenerationOptions generationOptions, List<Artifact> allArtifacts) {
+    myProject = project;
+    myGenerationOptions = generationOptions;
+    for (Artifact artifact : allArtifacts) {
+      if (ArtifactUtil.shouldClearArtifactOutputBeforeRebuild(artifact)) {
+        myArtifactsToClean.add(artifact);
+      }
+    }
+  }
+
+  @Override
+  public Project getProject() {
+    return myProject;
+  }
+
+  @Override
+  public GenerationOptions getGenerationOptions() {
+    return myGenerationOptions;
+  }
+
+  public String getConfiguredArtifactOutputProperty(@NotNull Artifact artifact) {
+    return "artifact.output." + BuildProperties.convertName(artifact.getName());
+  }
+
+  public String getArtifactOutputProperty(@NotNull Artifact artifact) {
+    if (shouldBuildIntoTempDirectory(artifact)) {
+      return "artifact.temp.output." + BuildProperties.convertName(artifact.getName());
+    }
+    return getConfiguredArtifactOutputProperty(artifact);
+  }
+
+  public boolean shouldBuildIntoTempDirectory(@NotNull Artifact artifact) {
+    return !myArtifactsToClean.contains(artifact);
+  }
+
+  public String getCleanTargetName(@NotNull Artifact artifact) {
+    return "clean.artifact." + BuildProperties.convertName(artifact.getName());
+  }
+
+  public String getTargetName(@NotNull Artifact artifact) {
+    String target = myArtifact2Target.get(artifact);
+    if (target == null) {
+      target = generateTargetName(artifact.getName());
+      myArtifact2Target.put(artifact, target);
+    }
+    return target;
+  }
+
+  private static String generateTargetName(String artifactName) {
+    return "artifact." + BuildProperties.convertName(artifactName);
+  }
+
+  public String getSubstitutedPath(String path) {
+    return GenerationUtils.toRelativePath(path, VfsUtil.virtualToIoFile(myProject.getBaseDir()), BuildProperties.getProjectBaseDirProperty(), myGenerationOptions);
+  }
+
+  public void runBeforeCurrentArtifact(Generator generator) {
+    myBeforeCurrentArtifact.add(generator);
+  }
+
+  public void runBeforeBuild(Generator generator) {
+    myBeforeBuildGenerators.add(generator);
+  }
+
+  public void runAfterBuild(Generator generator) {
+    myAfterBuildGenerators.add(generator);
+  }
+
+  public String createNewTempFileProperty(String basePropertyName, String fileName) {
+    String tempFileName = fileName;
+    int i = 1;
+    String tempSubdir = null;
+    while (myTempFileNames.contains(tempFileName)) {
+      tempSubdir = String.valueOf(i++);
+      tempFileName = tempSubdir + "/" + fileName;
+    }
+
+    String propertyName = basePropertyName;
+    i = 2;
+    while (myProperties.contains(propertyName)) {
+      propertyName = basePropertyName + i++;
+    }
+
+    runBeforeBuild(new Property(propertyName, BuildProperties.propertyRelativePath(ARTIFACTS_TEMP_DIR_PROPERTY, tempFileName)));
+    if (tempSubdir != null && myCreatedTempSubdirs.add(tempSubdir)) {
+      runBeforeBuild(new Mkdir(BuildProperties.propertyRelativePath(ARTIFACTS_TEMP_DIR_PROPERTY, tempSubdir)));
+    }
+    myTempFileNames.add(tempFileName);
+    myProperties.add(propertyName);
+    return propertyName;
+  }
+
+  public Generator[] getAndClearBeforeCurrentArtifact() {
+    final Generator[] generators = myBeforeCurrentArtifact.toArray(new Generator[myBeforeCurrentArtifact.size()]);
+    myBeforeCurrentArtifact.clear();
+    return generators;
+  }
+
+  public String getModuleOutputPath(String moduleName) {
+    return BuildProperties.getOutputPathProperty(moduleName);
+  }
+
+  @Override
+  public String getModuleTestOutputPath(@NonNls String moduleName) {
+    return BuildProperties.getOutputPathForTestsProperty(moduleName);
+  }
+
+  public List<Generator> getBeforeBuildGenerators() {
+    return myBeforeBuildGenerators;
+  }
+
+  public List<Generator> getAfterBuildGenerators() {
+    return myAfterBuildGenerators;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/ArtifactsGenerator.java b/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/ArtifactsGenerator.java
new file mode 100644
index 0000000..51ce1f4
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/ArtifactsGenerator.java
@@ -0,0 +1,190 @@
+/*
+ * 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 com.intellij.compiler.ant.artifacts;
+
+import com.intellij.compiler.ant.*;
+import com.intellij.compiler.ant.taskdefs.*;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactManager;
+import com.intellij.packaging.elements.ComplexPackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.impl.artifacts.PackagingElementPath;
+import com.intellij.packaging.impl.artifacts.PackagingElementProcessor;
+import com.intellij.packaging.impl.elements.ArtifactPackagingElement;
+import com.intellij.packaging.impl.elements.ModuleOutputPackagingElement;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ArtifactsGenerator {
+  @NonNls public static final String BUILD_ALL_ARTIFACTS_TARGET = "build.all.artifacts";
+  @NonNls private static final String INIT_ARTIFACTS_TARGET = "init.artifacts";
+  private final PackagingElementResolvingContext myResolvingContext;
+  private final ArtifactAntGenerationContextImpl myContext;
+  private final List<Artifact> myAllArtifacts;
+
+  public ArtifactsGenerator(Project project, GenerationOptions genOptions) {
+    myResolvingContext = ArtifactManager.getInstance(project).getResolvingContext();
+
+    myAllArtifacts = new ArrayList<Artifact>(Arrays.asList(ArtifactManager.getInstance(project).getSortedArtifacts()));
+
+    myContext = new ArtifactAntGenerationContextImpl(project, genOptions, myAllArtifacts);
+  }
+
+  public boolean hasArtifacts() {
+    return !myAllArtifacts.isEmpty();
+  }
+
+  public List<Generator> generate() {
+    final List<Generator> generators = new ArrayList<Generator>();
+
+    final Target initTarget = new Target(INIT_ARTIFACTS_TARGET, null, null, null);
+    generators.add(initTarget);
+    initTarget.add(new Property(ArtifactAntGenerationContextImpl.ARTIFACTS_TEMP_DIR_PROPERTY, BuildProperties.propertyRelativePath(BuildProperties.getProjectBaseDirProperty(), "__artifacts_temp")));
+
+    for (Artifact artifact : myAllArtifacts) {
+      if (!myContext.shouldBuildIntoTempDirectory(artifact)) {
+        generators.add(new CleanArtifactTarget(artifact, myContext));
+      }
+      final String outputPath = artifact.getOutputPath();
+      if (!StringUtil.isEmpty(outputPath)) {
+        initTarget.add(new Property(myContext.getConfiguredArtifactOutputProperty(artifact), myContext.getSubstitutedPath(outputPath)));
+      }
+    }
+    initTarget.add(new Mkdir(BuildProperties.propertyRef(ArtifactAntGenerationContextImpl.ARTIFACTS_TEMP_DIR_PROPERTY)));
+
+    StringBuilder depends = new StringBuilder();
+    for (Artifact artifact : myAllArtifacts) {
+      Target target = createArtifactTarget(artifact);
+      generators.add(target);
+
+      if (!StringUtil.isEmpty(artifact.getOutputPath())) {
+        if (depends.length() > 0) depends.append(", ");
+        depends.append(myContext.getTargetName(artifact));
+      }
+    }
+
+    for (Generator generator : myContext.getBeforeBuildGenerators()) {
+      initTarget.add(generator);
+    }
+
+    Target buildAllArtifacts = new Target(BUILD_ALL_ARTIFACTS_TARGET, depends.toString(), "Build all artifacts", null);
+    for (Artifact artifact : myAllArtifacts) {
+      final String artifactOutputPath = artifact.getOutputPath();
+      if (!StringUtil.isEmpty(artifactOutputPath) && myContext.shouldBuildIntoTempDirectory(artifact)) {
+        final String outputPath = BuildProperties.propertyRef(myContext.getConfiguredArtifactOutputProperty(artifact));
+        buildAllArtifacts.add(new Mkdir(outputPath));
+        final Copy copy = new Copy(outputPath);
+        copy.add(new FileSet(BuildProperties.propertyRef(myContext.getArtifactOutputProperty(artifact))));
+        buildAllArtifacts.add(copy);
+      }
+    }
+
+    buildAllArtifacts.add(new Comment("Delete temporary files"), 1);
+    for (Generator generator : myContext.getAfterBuildGenerators()) {
+      buildAllArtifacts.add(generator);
+    }
+    buildAllArtifacts.add(new Delete(BuildProperties.propertyRef(ArtifactAntGenerationContextImpl.ARTIFACTS_TEMP_DIR_PROPERTY)));
+
+    generators.add(buildAllArtifacts);
+    return generators;
+  }
+
+  private Target createArtifactTarget(Artifact artifact) {
+    final StringBuilder depends = new StringBuilder(INIT_ARTIFACTS_TARGET);
+
+    ArtifactUtil.processPackagingElements(artifact, null, new PackagingElementProcessor<PackagingElement<?>>() {
+      @Override
+      public boolean shouldProcessSubstitution(ComplexPackagingElement<?> element) {
+        return !(element instanceof ArtifactPackagingElement);
+      }
+
+      @Override
+      public boolean process(@NotNull PackagingElement<?> packagingElement, @NotNull PackagingElementPath path) {
+        if (packagingElement instanceof ArtifactPackagingElement) {
+          final Artifact included = ((ArtifactPackagingElement)packagingElement).findArtifact(myResolvingContext);
+          if (included != null) {
+            if (depends.length() > 0) depends.append(", ");
+            depends.append(myContext.getTargetName(included));
+          }
+        }
+        else if (packagingElement instanceof ModuleOutputPackagingElement) {
+          final Module module = ((ModuleOutputPackagingElement)packagingElement).findModule(myResolvingContext);
+          if (module != null) {
+            if (depends.length() > 0) depends.append(", ");
+            depends.append(BuildProperties.getCompileTargetName(module.getName()));
+          }
+        }
+        return true;
+      }
+    }, myResolvingContext, true);
+
+    final Target artifactTarget =
+        new Target(myContext.getTargetName(artifact), depends.toString(), "Build '" + artifact.getName() + "' artifact", null);
+
+    if (myContext.shouldBuildIntoTempDirectory(artifact)) {
+      final String outputDirectory = BuildProperties.propertyRelativePath(ArtifactAntGenerationContextImpl.ARTIFACTS_TEMP_DIR_PROPERTY,
+                                                                          FileUtil.sanitizeFileName(artifact.getName()));
+      artifactTarget.add(new Property(myContext.getArtifactOutputProperty(artifact), outputDirectory));
+    }
+
+    final String outputPath = BuildProperties.propertyRef(myContext.getArtifactOutputProperty(artifact));
+    artifactTarget.add(new Mkdir(outputPath));
+    generateTasksForArtifacts(artifact, artifactTarget, true);
+
+    final DirectoryAntCopyInstructionCreator creator = new DirectoryAntCopyInstructionCreator(outputPath);
+
+    List<Generator> copyInstructions = new ArrayList<Generator>();
+    copyInstructions.addAll(artifact.getRootElement().computeAntInstructions(myResolvingContext, creator, myContext, artifact.getArtifactType()));
+
+    for (Generator generator : myContext.getAndClearBeforeCurrentArtifact()) {
+      artifactTarget.add(generator);
+    }
+    for (Generator tag : copyInstructions) {
+      artifactTarget.add(tag);
+    }
+    generateTasksForArtifacts(artifact, artifactTarget, false);
+    return artifactTarget;
+  }
+
+  private void generateTasksForArtifacts(Artifact artifact, Target artifactTarget, final boolean preprocessing) {
+    for (ChunkBuildExtension extension : ChunkBuildExtension.EP_NAME.getExtensions()) {
+      extension.generateTasksForArtifact(artifact, preprocessing, myContext, artifactTarget);
+    }
+  }
+
+  public List<String> getCleanTargetNames() {
+    final List<String> targets = new ArrayList<String>();
+    for (Artifact artifact : myAllArtifacts) {
+      if (!myContext.shouldBuildIntoTempDirectory(artifact)) {
+        targets.add(myContext.getCleanTargetName(artifact));
+      }
+    }
+    return targets;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/CleanArtifactTarget.java b/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/CleanArtifactTarget.java
new file mode 100644
index 0000000..8a205af
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/CleanArtifactTarget.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 com.intellij.compiler.ant.artifacts;
+
+import com.intellij.compiler.ant.taskdefs.Target;
+import com.intellij.compiler.ant.taskdefs.Delete;
+import com.intellij.compiler.ant.BuildProperties;
+import com.intellij.packaging.artifacts.Artifact;
+
+/**
+ * @author nik
+ */
+public class CleanArtifactTarget extends Target {
+  public CleanArtifactTarget(Artifact artifact, ArtifactAntGenerationContextImpl context) {
+    super(context.getCleanTargetName(artifact), null, "clean " + artifact.getName() + " artifact output", null);
+    add(new Delete(BuildProperties.propertyRef(context.getConfiguredArtifactOutputProperty(artifact))));
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/DirectoryAntCopyInstructionCreator.java b/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/DirectoryAntCopyInstructionCreator.java
new file mode 100644
index 0000000..900dff4
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/ant/artifacts/DirectoryAntCopyInstructionCreator.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 com.intellij.compiler.ant.artifacts;
+
+import com.intellij.compiler.ant.Generator;
+import com.intellij.compiler.ant.Tag;
+import com.intellij.compiler.ant.taskdefs.*;
+import com.intellij.packaging.elements.AntCopyInstructionCreator;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class DirectoryAntCopyInstructionCreator implements AntCopyInstructionCreator {
+  private final String myOutputDirectory;
+
+  public DirectoryAntCopyInstructionCreator(String outputDirectory) {
+    myOutputDirectory = outputDirectory;
+  }
+
+  public String getOutputDirectory() {
+    return myOutputDirectory;
+  }
+
+  @NotNull
+  public Tag createDirectoryContentCopyInstruction(@NotNull String dirPath) {
+    final Copy copy = new Copy(myOutputDirectory);
+    copy.add(new FileSet(dirPath));
+    return copy;
+  }
+
+  @NotNull
+  public Tag createFileCopyInstruction(@NotNull String filePath, String outputFileName) {
+    return new Copy(filePath, myOutputDirectory + "/" + outputFileName);
+  }
+
+  @NotNull
+  public AntCopyInstructionCreator subFolder(@NotNull String directoryName) {
+    return new DirectoryAntCopyInstructionCreator(myOutputDirectory + "/" + directoryName);
+  }
+
+  public Generator createSubFolderCommand(@NotNull String directoryName) {
+    return new Mkdir(myOutputDirectory + "/" + directoryName);
+  }
+
+  @NotNull
+  @Override
+  public Generator createExtractedDirectoryInstruction(@NotNull String jarPath) {
+    return new Unzip(jarPath, myOutputDirectory);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/AnnotationConstantValue.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/AnnotationConstantValue.java
new file mode 100644
index 0000000..4d4bfc4
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/AnnotationConstantValue.java
@@ -0,0 +1,85 @@
+/*
+ * 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 com.intellij.compiler.classParsing;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Apr 2, 2004
+ */
+public class AnnotationConstantValue extends ConstantValue {
+  public static final AnnotationConstantValue[] EMPTY_ARRAY = new AnnotationConstantValue[0];
+  public static final AnnotationConstantValue[][] EMPTY_ARRAY_ARRAY = new AnnotationConstantValue[0][];
+  public final int myQName;
+  public final AnnotationNameValuePair[] myMemberValues;
+
+  public AnnotationConstantValue(int qName, AnnotationNameValuePair[] memberValues) {
+    myQName = qName;
+    myMemberValues = memberValues;
+  }
+
+  public int getAnnotationQName() {
+    return myQName;
+  }
+
+  /**
+   * @return an array of Integer -> ConstantValue pairs
+   */
+  public AnnotationNameValuePair[] getMemberValues() {
+    return myMemberValues;
+  }
+
+  public AnnotationConstantValue(DataInput in) throws IOException {
+    myQName = in.readInt();
+    final int size = in.readInt();
+    myMemberValues = new AnnotationNameValuePair[size];
+    for (int idx = 0; idx < myMemberValues.length; idx++) {
+      final int name = in.readInt();
+      final ConstantValue constantValue = MemberInfoExternalizer.loadConstantValue(in);
+      myMemberValues[idx] = new AnnotationNameValuePair(name, constantValue);
+    }
+  }
+
+  public void save(DataOutput out) throws IOException {
+    out.writeInt(myQName);
+    out.writeInt(myMemberValues.length);
+    for (int idx = 0; idx < myMemberValues.length; idx++) {
+      final AnnotationNameValuePair nameValuePair = myMemberValues[idx];
+      out.writeInt(nameValuePair.getName());
+      MemberInfoExternalizer.saveConstantValue(out, nameValuePair.getValue());
+    }
+  }
+
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (!(o instanceof AnnotationConstantValue)) return false;
+
+    final AnnotationConstantValue annotationConstantValue = (AnnotationConstantValue)o;
+
+    if (myQName != annotationConstantValue.myQName) return false;
+    if (!Arrays.equals(myMemberValues, annotationConstantValue.myMemberValues)) return false;
+
+    return true;
+  }
+
+  public int hashCode() {
+    return myQName;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/AnnotationNameValuePair.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/AnnotationNameValuePair.java
new file mode 100644
index 0000000..e559875
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/AnnotationNameValuePair.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 com.intellij.compiler.classParsing;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Apr 8, 2004
+ */
+public class AnnotationNameValuePair {
+  private final int myName;
+  private final ConstantValue myValue;
+
+  public AnnotationNameValuePair(int name, ConstantValue value) {
+    myName = name;
+    myValue = value;
+  }
+
+  public int getName() {
+    return myName;
+  }
+
+  public ConstantValue getValue() {
+    return myValue;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/AnnotationPrimitiveConstantValue.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/AnnotationPrimitiveConstantValue.java
new file mode 100644
index 0000000..24ade71
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/AnnotationPrimitiveConstantValue.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 com.intellij.compiler.classParsing;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Apr 2, 2004
+ */
+public class AnnotationPrimitiveConstantValue extends ConstantValue{
+  private final char myValueTag;
+  private final ConstantValue myValue;
+
+  public AnnotationPrimitiveConstantValue(char valueTag, ConstantValue value) {
+    myValueTag = valueTag;
+    myValue = value;
+  }
+
+  public AnnotationPrimitiveConstantValue(DataInput in) throws IOException {
+    myValueTag = in.readChar();
+    myValue = MemberInfoExternalizer.loadConstantValue(in);
+  }
+
+  public char getValueTag() {
+    return myValueTag;
+  }
+
+  public ConstantValue getValue() {
+    return myValue;
+  }
+
+  public void save(DataOutput out) throws IOException {
+    out.writeChar(myValueTag);
+    MemberInfoExternalizer.saveConstantValue(out, myValue);
+  }
+
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (!(o instanceof AnnotationPrimitiveConstantValue)) return false;
+
+    final AnnotationPrimitiveConstantValue memberValue = (AnnotationPrimitiveConstantValue)o;
+
+    if (myValueTag != memberValue.myValueTag) return false;
+    if (!myValue.equals(memberValue.myValue)) return false;
+
+    return true;
+  }
+
+  public int hashCode() {
+    int result;
+    result = (int)myValueTag;
+    result = 29 * result + myValue.hashCode();
+    return result;
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/ClassFileReader.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/ClassFileReader.java
new file mode 100644
index 0000000..bd80570
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/ClassFileReader.java
@@ -0,0 +1,770 @@
+/*
+ * 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.
+ */
+
+/**
+ * created at Jan 2, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.classParsing;
+
+import com.intellij.compiler.SymbolTable;
+import com.intellij.compiler.make.CacheCorruptedException;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.psi.CommonClassNames;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.cls.BytePointer;
+import com.intellij.util.cls.ClsFormatException;
+import com.intellij.util.cls.ClsUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class ClassFileReader {
+  private final File myFile;
+  private byte[] myData;
+  private int[] myConstantPoolOffsets = null; // the last offset points to the constant pool end
+
+  private String myQualifiedName;
+  private String myGenericSignature;
+  private List<ReferenceInfo> myReferences;
+  private List<FieldInfo> myFields;
+  private List<MethodInfo> myMethods;
+  private String mySourceFileName;
+  private String mySuperClassName;
+  private String[] mySuperInterfaces;
+  private final SymbolTable mySymbolTable;
+  private AnnotationConstantValue[] myRuntimeVisibleAnnotations;
+  private AnnotationConstantValue[] myRuntimeInvisibleAnnotations;
+  private static final String CONSTRUCTOR_NAME = "<init>";
+  private boolean myParsingDone;
+
+  public ClassFileReader(@NotNull File file, SymbolTable symbolTable, @Nullable final byte[] fileContent) {
+    mySymbolTable = symbolTable;
+    myFile = file;
+    myData = fileContent;
+  }
+
+  public String getPath() {
+    return myFile.getAbsolutePath();
+  }
+
+  public Collection<ReferenceInfo> getReferences() throws ClsFormatException {
+    parseConstantPool();
+    return myReferences;
+  }
+
+  public MethodInfo[] getMethods() throws ClsFormatException{
+    parseMembers();
+    return myMethods.toArray(new MethodInfo[myMethods.size()]);
+  }
+
+  public FieldInfo[] getFields() throws ClsFormatException{
+    parseMembers();
+    return myFields.toArray(new FieldInfo[myFields.size()]);
+  }
+
+  private void parseMembers() throws ClsFormatException {
+    if (myParsingDone) {
+      return;
+    }
+    initConstantPool();
+    myMethods = new ArrayList<MethodInfo>();
+    myFields = new ArrayList<FieldInfo>();
+    BytePointer ptr = new BytePointer(getData(), getConstantPoolEnd());
+    ptr.offset += 2; // access flags
+    ptr.offset += 2; // this class
+    ptr.offset += 2; // super class
+    int count = ClsUtil.readU2(ptr); // interface count
+    ptr.offset += 2 * count; // skip interface infos
+    count = ClsUtil.readU2(ptr); // field count
+    while (count-- > 0) {
+      FieldInfo field = (FieldInfo)readMemberStructure(ptr, true);
+      String name = getSymbol(field.getName());
+      if (name.indexOf('$') < 0 && name.indexOf('<') < 0){ // skip synthetic fields
+        myFields.add(field);
+      }
+    }
+    count = ClsUtil.readU2(ptr); // method count
+    while (count-- > 0) {
+      MethodInfo method = (MethodInfo)readMemberStructure(ptr, false);
+      String name = getSymbol(method.getName());
+      if (name.indexOf('$') < 0 && name.indexOf('<') < 0) { // skip synthetic methods
+        myMethods.add(method);
+      }
+      else
+      if (CONSTRUCTOR_NAME.equals(name)) { // store constructors
+        myMethods.add(method);
+      }
+    }
+
+    final ClsAttributeTable attributeTable = readAttributes(ptr);
+    mySourceFileName = attributeTable.sourceFile;
+    myGenericSignature = attributeTable.genericSignature;
+    myRuntimeVisibleAnnotations = attributeTable.runtimeVisibleAnnotations;
+    myRuntimeInvisibleAnnotations = attributeTable.runtimeInvisibleAnnotations;
+    myParsingDone = true;
+  }
+
+  private String getSymbol(final int id) throws ClsFormatException {
+    try {
+      return mySymbolTable.getSymbol(id);
+    }
+    catch (CacheCorruptedException e) {
+      throw new ClsFormatException(e.getLocalizedMessage());
+    }
+  }
+
+  private MemberInfo readMemberStructure(BytePointer ptr, boolean isField) throws ClsFormatException {
+    int flags = ClsUtil.readU2(ptr);
+    int nameIndex = ClsUtil.readU2(ptr);
+    int descriptorIndex = ClsUtil.readU2(ptr);
+
+    BytePointer p = new BytePointer(getData(), getOffsetInConstantPool(nameIndex));
+    String name = ClsUtil.readUtf8Info(p);
+    p.offset = getOffsetInConstantPool(descriptorIndex);
+    String descriptor = ClsUtil.readUtf8Info(p);
+
+    if (isField) {
+      final ClsAttributeTable attributeTable = readAttributes(ptr);
+      return new FieldInfo(
+        getSymbolId(name),
+        getSymbolId(descriptor),
+        attributeTable.genericSignature != null? getSymbolId(attributeTable.genericSignature) : -1,
+        flags,
+        attributeTable.constantValue,
+        attributeTable.runtimeVisibleAnnotations,
+        attributeTable.runtimeInvisibleAnnotations
+      );
+    }
+    else {
+      final ClsAttributeTable attributeTable = readAttributes(ptr);
+      int[] intExceptions = null;
+      if (attributeTable.exceptions != null) {
+        intExceptions = ArrayUtil.newIntArray(attributeTable.exceptions.length);
+        for (int idx = 0; idx < intExceptions.length; idx++) {
+          intExceptions[idx]  = getSymbolId(attributeTable.exceptions[idx]);
+        }
+      }
+      return new MethodInfo(
+        getSymbolId(name),
+        getSymbolId(descriptor),
+        attributeTable.genericSignature != null? getSymbolId(attributeTable.genericSignature) : -1,
+        flags,
+        intExceptions,
+        CONSTRUCTOR_NAME.equals(name),
+        attributeTable.runtimeVisibleAnnotations,
+        attributeTable.runtimeInvisibleAnnotations,
+        attributeTable.runtimeVisibleParameterAnnotations,
+        attributeTable.runtimeInvisibleParameterAnnotations,
+        attributeTable.annotationDefault
+      );
+    }
+  }
+
+  private int getSymbolId(final String symbol)  throws ClsFormatException{
+    try {
+      return mySymbolTable.getId(symbol);
+    }
+    catch (CacheCorruptedException e) {
+      throw new ClsFormatException(e.getLocalizedMessage());
+    }
+  }
+
+  public String getQualifiedName() throws ClsFormatException {
+    if (myQualifiedName == null) {
+      BytePointer ptr = new BytePointer(getData(), getConstantPoolEnd() + 2);
+      ptr.offset = getOffsetInConstantPool(ClsUtil.readU2(ptr));
+      int tag = ClsUtil.readU1(ptr);
+      if (tag != ClsUtil.CONSTANT_Class){
+        throw new ClsFormatException();
+      }
+      ptr.offset = getOffsetInConstantPool(ClsUtil.readU2(ptr));
+      myQualifiedName = ClsUtil.readUtf8Info(ptr, '/', '.'); // keep '$' in the names
+    }
+    return myQualifiedName;
+  }
+
+  /**
+   * @return fully qualified name of the class' superclass. In case there is no super return ""
+   */
+  public String getSuperClass() throws ClsFormatException {
+    if (mySuperClassName == null) {
+      BytePointer ptr = new BytePointer(getData(), getConstantPoolEnd() + 4);
+      int index = ClsUtil.readU2(ptr);
+      if (index == 0) {
+        if (CommonClassNames.JAVA_LANG_OBJECT.equals(getQualifiedName())) {
+          mySuperClassName = "";
+        }
+        else {
+          throw new ClsFormatException();
+        }
+      }
+      else {
+        ptr.offset = getOffsetInConstantPool(index);
+        mySuperClassName = readClassInfo(ptr); // keep '$' in the name for anonymous classes
+        if (isInterface()) {
+          if (!CommonClassNames.JAVA_LANG_OBJECT.equals(mySuperClassName)) {
+            throw new ClsFormatException();
+          }
+        }
+        /*
+        else {
+          if (!MakeUtil.isAnonymous(mySuperClassName)) {
+            mySuperClassName = mySuperClassName.replace('$', '.');
+          }
+        }
+        */
+      }
+    }
+    return mySuperClassName;
+  }
+
+  public String[] getSuperInterfaces() throws ClsFormatException {
+    if (mySuperInterfaces == null) {
+      BytePointer ptr = new BytePointer(getData(), getConstantPoolEnd() + 6);
+      int count = ClsUtil.readU2(ptr);
+      mySuperInterfaces = ArrayUtil.newStringArray(count);
+      BytePointer auxPtr = new BytePointer(ptr.bytes, 0);
+      for (int idx = 0; idx < mySuperInterfaces.length; idx++) {
+        auxPtr.offset = getOffsetInConstantPool(ClsUtil.readU2(ptr));
+        mySuperInterfaces[idx] = readClassInfo(auxPtr);
+      }
+    }
+    return mySuperInterfaces;
+  }
+
+
+  public String getSourceFileName() throws ClsFormatException {
+    parseMembers();
+    final String fName = mySourceFileName;
+    return fName != null? fName : "";
+  }
+
+  public String getGenericSignature() throws ClsFormatException {
+    parseMembers();
+    final String genericSignature = myGenericSignature;
+    return genericSignature != null && !genericSignature.isEmpty() ? genericSignature : null;
+  }
+
+  public AnnotationConstantValue[] getRuntimeVisibleAnnotations() throws ClsFormatException {
+    parseMembers();
+    final AnnotationConstantValue[] annotations = myRuntimeVisibleAnnotations;
+    return annotations != null? annotations : AnnotationConstantValue.EMPTY_ARRAY;
+  }
+
+  public AnnotationConstantValue[] getRuntimeInvisibleAnnotations() throws ClsFormatException {
+    parseMembers();
+    final AnnotationConstantValue[] annotations = myRuntimeInvisibleAnnotations;
+    return annotations != null? annotations : AnnotationConstantValue.EMPTY_ARRAY;
+  }
+
+  private boolean isInterface(){
+    return (getAccessFlags() & ClsUtil.ACC_INTERFACE) != 0;
+  }
+
+// helper methods
+  private void parseConstantPool() throws ClsFormatException {
+    if (myReferences != null) {
+      return;
+    }
+    myReferences = new ArrayList<ReferenceInfo>();
+    initConstantPool();
+    final BytePointer ptr = new BytePointer(getData(), 0);
+    ConstantPoolIterator iterator = new ConstantPoolIterator(ptr);
+    while (iterator.hasMoreEntries()) {
+      final int tag = ClsUtil.readU1(ptr);
+      if (tag == ClsUtil.CONSTANT_Fieldref || tag == ClsUtil.CONSTANT_Methodref || tag == ClsUtil.CONSTANT_InterfaceMethodref) {
+        //ptr.offset -= 1; // position to the beginning of the structure
+        MemberReferenceInfo refInfo = readRefStructure(tag, ptr);
+        if (refInfo != null) {
+          myReferences.add(refInfo);
+        }
+        /*
+        String name = mySymbolTable.getSymbol(refInfo.getMemberInfo().getName());
+        if (name.indexOf('$') < 0 && name.indexOf('<') < 0) { // skip refs to synthetic members
+          myReferences.add(refInfo);
+        }
+        else if ("<init>".equals(name)) { // add instance initializers (constructors)
+          myReferences.add(refInfo);
+        }
+        else {
+          System.out.println("ReferenceInfo thrown out: " + mySymbolTable.getSymbol(refInfo.getClassName()) + "." + mySymbolTable.getSymbol(refInfo.getMemberInfo().getName()));
+          ourWasteReferenceObjectsCounter += 1;
+        }
+        */
+      }
+      else if (tag == ClsUtil.CONSTANT_Class) {
+        ptr.offset -= 1; // position to the beginning of the structure
+        String className = readClassInfo(ptr);
+        myReferences.add(new ReferenceInfo(getSymbolId(className)));
+      }
+      iterator.next();
+    }
+    //System.out.println("ourWasteReferenceObjectsCounter = " + ourWasteReferenceObjectsCounter);
+  }
+
+  private MemberReferenceInfo readRefStructure(int tag, BytePointer ptr) throws ClsFormatException {
+    /*
+    if (tag != ClsUtil.CONSTANT_Fieldref && tag != ClsUtil.CONSTANT_Methodref && tag != ClsUtil.CONSTANT_InterfaceMethodref) {
+      throw new ClsFormatException();
+    }
+    */
+    int classInfoIndex = ClsUtil.readU2(ptr);
+    int nameTypeInfoIndex = ClsUtil.readU2(ptr);
+
+    ptr.offset = getOffsetInConstantPool(classInfoIndex);
+    if (ClsUtil.CONSTANT_Class != ClsUtil.readU1(ptr)) {
+      throw new ClsFormatException();
+    }
+    ptr.offset = getOffsetInConstantPool(ClsUtil.readU2(ptr));
+    String className = ClsUtil.readUtf8Info(ptr, '/', '.'); // keep '$' in names
+
+    ptr.offset = getOffsetInConstantPool(nameTypeInfoIndex);
+    if (ClsUtil.CONSTANT_NameAndType != ClsUtil.readU1(ptr)) {
+      throw new ClsFormatException();
+    }
+    int memberNameIndex = ClsUtil.readU2(ptr);
+    int descriptorIndex = ClsUtil.readU2(ptr);
+
+    ptr.offset = getOffsetInConstantPool(memberNameIndex);
+    String memberName = ClsUtil.readUtf8Info(ptr);
+
+    if ((memberName.indexOf('$') >= 0 || memberName.indexOf('<') >= 0) && !CONSTRUCTOR_NAME.equals(memberName)) { // skip refs to synthetic members
+      return null;
+    }
+
+    ptr.offset = getOffsetInConstantPool(descriptorIndex);
+    String descriptor = ClsUtil.readUtf8Info(ptr);
+
+    MemberInfo info = ClsUtil.CONSTANT_Fieldref == tag? new FieldInfo(getSymbolId(memberName), getSymbolId(descriptor)) : new MethodInfo(getSymbolId(memberName), getSymbolId(descriptor), CONSTRUCTOR_NAME.equals(memberName));
+    return new MemberReferenceInfo(getSymbolId(className), info);
+  }
+
+  public int getAccessFlags(){
+    try{
+      int offset = getConstantPoolEnd();
+      byte[] data = getData();
+      if (offset + 2 > data.length){
+        throw new ClsFormatException();
+      }
+      int b1 = data[offset++] & 0xFF;
+      int b2 = data[offset++] & 0xFF;
+      return (b1 << 8) + b2;
+    }
+    catch(ClsFormatException e){
+      return 0;
+    }
+  }
+
+  private byte[] getData(){
+    if (myData == null) {
+      try{
+        myData = FileUtil.loadFileBytes(myFile);
+      }
+      catch(IOException e){
+        myData = ArrayUtil.EMPTY_BYTE_ARRAY;
+      }
+    }
+    return myData;
+  }
+
+  private int getOffsetInConstantPool(int index) throws ClsFormatException {
+    initConstantPool();
+    if (index < 1 || index >= myConstantPoolOffsets.length){
+      throw new ClsFormatException();
+    }
+    return myConstantPoolOffsets[index - 1];
+  }
+
+  private int getConstantPoolEnd() throws ClsFormatException {
+    initConstantPool();
+    return myConstantPoolOffsets[myConstantPoolOffsets.length - 1];
+  }
+
+  private void initConstantPool() throws ClsFormatException {
+    if (myConstantPoolOffsets == null){
+      BytePointer ptr = new BytePointer(getData(), 0);
+      ConstantPoolIterator iterator = new ConstantPoolIterator(ptr);
+      myConstantPoolOffsets = new int[iterator.getEntryCount()];
+      myConstantPoolOffsets[0] = iterator.getCurrentOffset();
+      int index = 1;
+      while (iterator.hasMoreEntries()) {
+        int tag = ClsUtil.readU1(ptr);
+        if (tag == ClsUtil.CONSTANT_Long || tag == ClsUtil.CONSTANT_Double) {
+          myConstantPoolOffsets[index++] = ptr.offset + 8; // takes 2 entries!
+        }
+        iterator.next();
+        myConstantPoolOffsets[index++] = iterator.getCurrentOffset();
+      }
+    }
+  }
+
+  private String readClassInfo(BytePointer ptr) throws ClsFormatException{
+    final int tag = ClsUtil.readU1(ptr);
+    if (tag != ClsUtil.CONSTANT_Class){
+      throw new ClsFormatException(CompilerBundle.message("class.parsing.error.wrong.record.tag.expected.another", tag, ClsUtil.CONSTANT_Class));
+    }
+    int index = ClsUtil.readU2(ptr);
+    return ClsUtil.readUtf8Info(new BytePointer(ptr.bytes, getOffsetInConstantPool(index)), '/', '.');
+  }
+
+  @SuppressWarnings({"HardCodedStringLiteral"})
+  private ClsAttributeTable readAttributes(BytePointer ptr) throws ClsFormatException {
+    int count = ClsUtil.readU2(ptr); // attributeCount
+    final ClsAttributeTable attributes = new ClsAttributeTable();
+    while (count-- > 0) {
+      final String attrName = readAttributeName(ptr);
+      if ("Exceptions".equals(attrName)) {
+        attributes.exceptions = readExceptions(ptr);
+      }
+      else if ("Signature".equals(attrName)) {
+        attributes.genericSignature = readSignatureAttribute(ptr);
+      }
+      else if ("SourceFile".equals(attrName)) {
+        attributes.sourceFile = readSourceFileAttribute(ptr);
+      }
+      else if ("ConstantValue".equals(attrName)){
+        attributes.constantValue = readFieldConstantValue(ptr);
+      }
+      else if ("RuntimeVisibleAnnotations".equals(attrName)) {
+        attributes.runtimeVisibleAnnotations = readAnnotations(ptr);
+      }
+      else if ("RuntimeInvisibleAnnotations".equals(attrName)) {
+        attributes.runtimeInvisibleAnnotations = readAnnotations(ptr);
+      }
+      else if ("RuntimeVisibleParameterAnnotations".equals(attrName)) {
+        attributes.runtimeVisibleParameterAnnotations = readParameterAnnotations(ptr);
+      }
+      else if ("RuntimeInvisibleParameterAnnotations".equals(attrName)) {
+        attributes.runtimeInvisibleParameterAnnotations = readParameterAnnotations(ptr);
+      }
+      else if ("AnnotationDefault".equals(attrName)) {
+        attributes.annotationDefault = readAnnotationMemberValue(new BytePointer(ptr.bytes, ptr.offset + 6));
+      }
+      gotoNextAttribute(ptr);
+    }
+    return attributes;
+  }
+
+  private String readAttributeName(BytePointer p) throws ClsFormatException {
+    final BytePointer ptr = new BytePointer(p.bytes, p.offset);
+    final int nameIndex = ClsUtil.readU2(ptr);
+    ptr.offset = getOffsetInConstantPool(nameIndex);
+    return ClsUtil.readUtf8Info(ptr);
+  }
+
+  private static void gotoNextAttribute(BytePointer ptr) throws ClsFormatException {
+    ptr.offset += 2; // skip name index
+    final int length = ClsUtil.readU4(ptr);    // important! Do not inline since ptr.offset is also changed inside ClsUtil.readU4() method
+    ptr.offset += length;
+  }
+
+  /**
+      Signature_attribute {
+        u2 attribute_name_index;    (must be equal to "Signature")
+        u4 attribute_length;        (must be equal to 2)
+        u2 signature_index;
+      }
+   */
+  private String readSignatureAttribute(BytePointer p) throws ClsFormatException {
+    final BytePointer ptr = new BytePointer(p.bytes, p.offset + 2); // position to the length
+    if (ClsUtil.readU4(ptr) != 2) {
+      return null;
+    }
+    ptr.offset = getOffsetInConstantPool(ClsUtil.readU2(ptr));
+    return ClsUtil.readUtf8Info(ptr);
+  }
+
+  private String[] readExceptions(BytePointer p) throws ClsFormatException{
+    final BytePointer ptr = new BytePointer(p.bytes, p.offset + 6); // position to the count of exceptions
+    int count = ClsUtil.readU2(ptr);
+    final ArrayList<String> array = new ArrayList<String>(count);
+    while (count-- > 0) {
+      int idx = ClsUtil.readU2(ptr);
+      if (idx != 0) {
+        final String exceptionClass = readClassInfo(new BytePointer(ptr.bytes, getOffsetInConstantPool(idx)));
+        array.add(exceptionClass);
+      }
+    }
+    return ArrayUtil.toStringArray(array);
+  }
+
+  private String readSourceFileAttribute(BytePointer p) throws ClsFormatException {
+    BytePointer ptr = new BytePointer(p.bytes, p.offset + 2); // position to the length
+    if (ClsUtil.readU4(ptr) != 2) {
+      return null;
+    }
+    ptr.offset = getOffsetInConstantPool(ClsUtil.readU2(ptr));
+    String path = ClsUtil.readUtf8Info(ptr);
+    // jdk version 1.3.0 puts full path to the source, but later versions store only short name
+    final int slashIndex = path.lastIndexOf('/');
+    if (slashIndex >= 0) {
+      path = path.substring(slashIndex + 1, path.length());
+    }
+    return path;
+  }
+
+  private ConstantValue readFieldConstantValue(BytePointer p) throws ClsFormatException{
+    final BytePointer ptr = new BytePointer(p.bytes, p.offset + 2);
+    if (ClsUtil.readU4(ptr) != 2) {
+      throw new ClsFormatException(); // attribute length must be 2
+    }
+    int valueIndex = ClsUtil.readU2(ptr);
+    ptr.offset = getOffsetInConstantPool(valueIndex);
+    return readConstant(ptr);
+  }
+
+  private ConstantValue readConstant(final BytePointer ptr) throws ClsFormatException {
+    final int tag = ClsUtil.readU1(ptr);
+    switch (tag) {
+      case ClsUtil.CONSTANT_Integer :
+        int value = ClsUtil.readU4(ptr);
+        return new IntegerConstantValue(value);
+      case ClsUtil.CONSTANT_Float:
+        float floatValue = ClsUtil.readFloat(ptr);
+        return new FloatConstantValue(floatValue);
+      case ClsUtil.CONSTANT_Long :
+        int high = ClsUtil.readU4(ptr);
+        int low = ClsUtil.readU4(ptr);
+        long v = ((long)high << 32) | (low & 0xFFFFFFFFL);
+        return new LongConstantValue(v);
+      case ClsUtil.CONSTANT_Double :
+        double doubleValue = ClsUtil.readDouble(ptr);
+        return new DoubleConstantValue(doubleValue);
+      case ClsUtil.CONSTANT_String :
+        int stringIndex = ClsUtil.readU2(ptr);
+        ptr.offset = getOffsetInConstantPool(stringIndex);
+        return new StringConstantValue(ClsUtil.readUtf8Info(ptr));
+      default : throw new ClsFormatException();
+    }
+  }
+
+  private AnnotationConstantValue[] readAnnotations(BytePointer p) throws ClsFormatException {
+    final BytePointer ptr = new BytePointer(p.bytes, p.offset + 6);
+    return readAnnotationsArray(ptr);
+  }
+
+  private AnnotationConstantValue[][] readParameterAnnotations(BytePointer p) throws ClsFormatException {
+    final BytePointer ptr = new BytePointer(p.bytes, p.offset + 6); // position to the number of parameters
+    final int numberOfParams = ClsUtil.readU1(ptr);
+    if (numberOfParams == 0) {
+      return null;
+    }
+    final AnnotationConstantValue[][] annotations = new AnnotationConstantValue[numberOfParams][];
+    for (int parameterIndex = 0; parameterIndex < numberOfParams; parameterIndex++) {
+      annotations[parameterIndex] = readAnnotationsArray(ptr);
+    }
+    return annotations;
+  }
+
+  private AnnotationConstantValue[] readAnnotationsArray(BytePointer ptr) throws ClsFormatException {
+    final int numberOfAnnotations = ClsUtil.readU2(ptr);
+    if (numberOfAnnotations == 0) {
+      return AnnotationConstantValue.EMPTY_ARRAY;
+    }
+    AnnotationConstantValue[] annotations = new AnnotationConstantValue[numberOfAnnotations];
+    for (int attributeIndex = 0; attributeIndex < numberOfAnnotations; attributeIndex++) {
+      annotations[attributeIndex] = readAnnotation(ptr);
+    }
+    return annotations;
+  }
+
+  private AnnotationConstantValue readAnnotation(BytePointer ptr) throws ClsFormatException {
+    final int classInfoIndex = ClsUtil.readU2(ptr);
+    final String qName = readAnnotationClassName(new BytePointer(ptr.bytes, getOffsetInConstantPool(classInfoIndex)));
+    final List<AnnotationNameValuePair> memberValues = new ArrayList<AnnotationNameValuePair>();
+    final int numberOfPairs = ClsUtil.readU2(ptr);
+    for (int idx = 0; idx < numberOfPairs; idx++) {
+      final int memberNameIndex = ClsUtil.readU2(ptr);
+      final String memberName = ClsUtil.readUtf8Info(ptr.bytes, getOffsetInConstantPool(memberNameIndex));
+      final ConstantValue memberValue = readAnnotationMemberValue(ptr);
+      memberValues.add(new AnnotationNameValuePair(getSymbolId(memberName), memberValue));
+    }
+    return new AnnotationConstantValue(getSymbolId(qName), memberValues.toArray(new AnnotationNameValuePair[memberValues.size()]));
+  }
+
+  private String readAnnotationClassName(BytePointer ptr) throws ClsFormatException {
+    // TODO: need this method because because of incomplete class format spec at the moment of writing
+    // it is not clear what structure is expected: CONSTANT_Utf8 or CONSTANT_Class
+    final int tag = ClsUtil.readU1(ptr);
+    if (tag == ClsUtil.CONSTANT_Utf8) {
+      return ClsUtil.getTypeText(ptr.bytes, ptr.offset + 2); //skip length
+    }
+    if (tag == ClsUtil.CONSTANT_Class){
+      ptr.offset -= 1; // rollback
+      return readClassInfo(ptr);
+    }
+    //noinspection HardCodedStringLiteral
+    throw new ClsFormatException(CompilerBundle.message("class.parsing.error.wrong.record.tag.expected.another", tag, "CONSTANT_Utf8(" + ClsUtil.CONSTANT_Utf8 + ") / CONSTANT_Class(" + ClsUtil.CONSTANT_Class + ")"));
+  }
+
+  private ConstantValue readAnnotationMemberValue(BytePointer ptr) throws ClsFormatException {
+    final char tag = (char)ClsUtil.readU1(ptr);
+    switch (tag) {
+      case 'B':
+      case 'C':
+      case 'D':
+      case 'F':
+      case 'I':
+      case 'J':
+      case 'S':
+      case 'Z': {
+        final int valueIndex = ClsUtil.readU2(ptr);
+        return new AnnotationPrimitiveConstantValue(tag, readConstant(new BytePointer(ptr.bytes, getOffsetInConstantPool(valueIndex))));
+      }
+      case 's': {
+        final int valueIndex = ClsUtil.readU2(ptr);
+        return new StringConstantValue(ClsUtil.readUtf8Info(ptr.bytes, getOffsetInConstantPool(valueIndex)));
+      }
+      case 'e': {
+        final int typeNameIndex = ClsUtil.readU2(ptr);
+        final int constantNameIndex = ClsUtil.readU2(ptr);
+        final String typeName = ClsUtil.readUtf8Info(ptr.bytes, getOffsetInConstantPool(typeNameIndex));
+        final String constantName = ClsUtil.readUtf8Info(ptr.bytes, getOffsetInConstantPool(constantNameIndex));
+        return new EnumConstantValue(getSymbolId(typeName), getSymbolId(constantName));
+      }
+      case 'c' : {
+        final int classInfoIndex = ClsUtil.readU2(ptr);
+        BytePointer p = new BytePointer(ptr.bytes, getOffsetInConstantPool(classInfoIndex));
+        final int recordTag = ClsUtil.readU1(p);
+        if (recordTag != ClsUtil.CONSTANT_Utf8) {
+          throw new ClsFormatException(CompilerBundle.message("class.parsing.error.wrong.record.tag.expected.another", recordTag, ClsUtil.CONSTANT_Utf8));
+        }
+        p.offset += 2; //Skip length
+        final String className = ClsUtil.getTypeText(p.bytes, p.offset);
+        return new ClassInfoConstantValue(getSymbolId(className));
+      }
+      case '@' : {
+        return readAnnotation(ptr);
+      }
+      case '[' : {
+        final int numberOfValues = ClsUtil.readU2(ptr);
+        final ConstantValue[] values = new ConstantValue[numberOfValues];
+        for (int idx = 0; idx < numberOfValues; idx++) {
+          values[idx] = readAnnotationMemberValue(ptr);
+        }
+        return new ConstantValueArray(values);
+      }
+      default : throw new ClsFormatException(CompilerBundle.message("class.parsing.error.wrong.tag.annotation.member.value", tag));
+    }
+  }
+
+  private static class ClsAttributeTable {
+    public String[] exceptions;
+    public String genericSignature;
+    public String sourceFile;
+    public ConstantValue constantValue;
+    public AnnotationConstantValue[] runtimeVisibleAnnotations;
+    public AnnotationConstantValue[] runtimeInvisibleAnnotations;
+    public AnnotationConstantValue[][] runtimeVisibleParameterAnnotations;
+    public AnnotationConstantValue[][] runtimeInvisibleParameterAnnotations;
+    public ConstantValue annotationDefault;
+  }
+
+  private static class ConstantPoolIterator {
+    private final BytePointer myPtr;
+    private final int myEntryCount;
+    private int myCurrentEntryIndex;
+    private int myCurrentOffset;
+
+    public ConstantPoolIterator(BytePointer ptr) throws ClsFormatException {
+      myPtr = ptr;
+      myPtr.offset = 0;
+      int magic = ClsUtil.readU4(myPtr);
+      if (magic != ClsUtil.MAGIC){
+        throw new ClsFormatException();
+      }
+      myPtr.offset += 2; // minor version
+      myPtr.offset += 2; // major version
+      myEntryCount = ClsUtil.readU2(myPtr);
+      if (myEntryCount < 1){
+        throw new ClsFormatException();
+      }
+      myCurrentEntryIndex = 1; // Entry at index 0 is included in the count but is not present in the constant pool
+      myCurrentOffset = myPtr.offset;
+    }
+
+    public int getEntryCount() {
+      return myEntryCount;
+    }
+
+    public int getCurrentOffset() {
+      return myCurrentOffset;
+    }
+
+    /**
+     *  tests if there are unread entries
+     */
+    public boolean hasMoreEntries() {
+      return myCurrentEntryIndex < myEntryCount;
+    }
+
+    /**
+     * Positions the pointer to the next entry
+     */
+    public void next() throws ClsFormatException {
+      myPtr.offset = myCurrentOffset;
+      int tag = ClsUtil.readU1(myPtr);
+      switch(tag){
+        default:
+          throw new ClsFormatException();
+
+        case ClsUtil.CONSTANT_Class:
+        case ClsUtil.CONSTANT_String:
+          myPtr.offset += 2;
+        break;
+
+        case ClsUtil.CONSTANT_Fieldref:
+        case ClsUtil.CONSTANT_Methodref:
+        case ClsUtil.CONSTANT_InterfaceMethodref:
+        case ClsUtil.CONSTANT_Integer:
+        case ClsUtil.CONSTANT_Float:
+        case ClsUtil.CONSTANT_NameAndType:
+          myPtr.offset += 4;
+        break;
+
+        case ClsUtil.CONSTANT_Long:
+        case ClsUtil.CONSTANT_Double:
+          myPtr.offset += 8;
+          myCurrentEntryIndex++; // takes 2 entries
+        break;
+
+        case ClsUtil.CONSTANT_Utf8:
+          int length = ClsUtil.readU2(myPtr);
+          myPtr.offset += length;
+        break;
+
+        case ClsUtil.CONSTANT_MethodHandle:
+          myPtr.offset += 3;
+          break;
+        case ClsUtil.CONSTANT_MethodType:
+          myPtr.offset += 2;
+          break;
+        case ClsUtil.CONSTANT_InvokeDynamic:
+          myPtr.offset += 4;
+          break;
+      }
+      myCurrentEntryIndex++;
+      myCurrentOffset = myPtr.offset;
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/ClassInfo.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/ClassInfo.java
new file mode 100644
index 0000000..2d816ef
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/ClassInfo.java
@@ -0,0 +1,229 @@
+/*
+ * 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 com.intellij.compiler.classParsing;
+
+import com.intellij.compiler.SymbolTable;
+import com.intellij.compiler.make.CacheCorruptedException;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.cls.ClsFormatException;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Collection;
+
+/**
+  @author Eugene Zhuravlev
+*/
+public final class ClassInfo implements Cloneable {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.classParsing.ClassInfo");
+
+  private static final ReferenceInfo[] EMPTY_REF_ARRAY = new ReferenceInfo[0];
+
+  private final int myQualifiedName;
+  private final int myGenericSignature;
+  private final int mySuperQualifiedName;
+  private final int myFlags;
+  private String myPath;
+  private final String mySourceFileName;
+  private final int[] mySuperInterfaces;
+  private final FieldInfo[] myFields;
+  private final MethodInfo[] myMethods;
+  private ReferenceInfo[] myReferences;
+  private final AnnotationConstantValue[] myRuntimeVisibleAnnotations;
+  private final AnnotationConstantValue[] myRuntimeInvisibleAnnotations;
+  private boolean myIsDirty = false;
+
+  public ClassInfo(ClassFileReader reader, SymbolTable symbolTable) throws CacheCorruptedException {
+    try {
+      final int qName = symbolTable.getId(reader.getQualifiedName());
+      myQualifiedName = qName;
+
+      final String genericSignature = reader.getGenericSignature();
+      myGenericSignature = genericSignature != null? symbolTable.getId(genericSignature) : -1;
+
+      myPath = reader.getPath();
+
+      final String superClass = reader.getSuperClass();
+      final int superQName = "".equals(superClass)? -1 : symbolTable.getId(superClass);
+      mySuperQualifiedName = superQName;
+
+      LOG.assertTrue(superQName != qName);
+
+      final String[] superInterfaces = reader.getSuperInterfaces();
+      mySuperInterfaces = ArrayUtil.newIntArray(superInterfaces.length);
+      for (int idx = 0; idx < superInterfaces.length; idx++) {
+        mySuperInterfaces[idx] = symbolTable.getId(superInterfaces[idx]);
+      }
+
+      final String sourceFileName = reader.getSourceFileName();
+      mySourceFileName = sourceFileName != null? sourceFileName : "";
+
+      myFlags = reader.getAccessFlags();
+
+      myRuntimeVisibleAnnotations = reader.getRuntimeVisibleAnnotations();
+      myRuntimeInvisibleAnnotations = reader.getRuntimeInvisibleAnnotations();
+
+      final Collection<ReferenceInfo> refs = reader.getReferences();
+      myReferences = refs.toArray(new ReferenceInfo[refs.size()]);
+
+      myFields = reader.getFields();
+      myMethods = reader.getMethods();
+    }
+    catch (ClsFormatException e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public ClassInfo(DataInput in) throws IOException {
+    myQualifiedName = in.readInt();
+    mySuperQualifiedName = in.readInt();
+    myGenericSignature = in.readInt();
+    myFlags = in.readInt();
+    myPath = in.readUTF();
+    mySourceFileName = in.readUTF();
+
+    final int ifaceCount = in.readInt();
+    mySuperInterfaces = new int[ifaceCount];
+    for (int idx = 0; idx < ifaceCount; idx++) {
+      mySuperInterfaces[idx] = in.readInt();
+    }
+
+    final int fieldCount = in.readInt();
+    myFields = new FieldInfo[fieldCount];
+    for (int idx = 0; idx < fieldCount; idx++) {
+      myFields[idx] = new FieldInfo(in);
+    }
+
+    final int methodCount = in.readInt();
+    myMethods = new MethodInfo[methodCount];
+    for (int idx = 0; idx < methodCount; idx++) {
+      myMethods[idx] = new MethodInfo(in);
+    }
+
+    final int refCount = in.readInt();
+    myReferences = refCount > 0? new ReferenceInfo[refCount] : EMPTY_REF_ARRAY;
+    for (int idx = 0; idx < refCount; idx++) {
+      myReferences[idx] = MemberInfoExternalizer.loadReferenceInfo(in);
+    }
+
+    myRuntimeVisibleAnnotations = MemberInfoExternalizer.readAnnotationConstantValueArray1(in);
+    myRuntimeInvisibleAnnotations = MemberInfoExternalizer.readAnnotationConstantValueArray1(in);
+  }
+
+  public ClassInfo clone()  {
+    try {
+      return (ClassInfo)super.clone();
+    }
+    catch (CloneNotSupportedException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public void save(DataOutput out) throws IOException {
+    out.writeInt(myQualifiedName);
+    out.writeInt(mySuperQualifiedName);
+    out.writeInt(myGenericSignature);
+    out.writeInt(myFlags);
+    out.writeUTF(myPath);
+    out.writeUTF(mySourceFileName);
+    out.writeInt(mySuperInterfaces.length);
+    for (int ifaceQName : mySuperInterfaces) {
+      out.writeInt(ifaceQName);
+    }
+
+    out.writeInt(myFields.length);
+    for (FieldInfo field : myFields) {
+      field.save(out);
+    }
+
+    out.writeInt(myMethods.length);
+    for (MethodInfo method : myMethods) {
+      method.save(out);
+    }
+
+    out.writeInt(myReferences.length);
+    for (ReferenceInfo info : myReferences) {
+      MemberInfoExternalizer.saveReferenceInfo(out, info);
+    }
+
+    MemberInfoExternalizer.writeConstantValueArray1(out, myRuntimeVisibleAnnotations);
+    MemberInfoExternalizer.writeConstantValueArray1(out, myRuntimeInvisibleAnnotations);
+  }
+
+  public boolean isDirty() {
+    return myIsDirty;
+  }
+
+  public int getQualifiedName() throws IOException {
+    return myQualifiedName;
+  }
+
+  public int getGenericSignature() throws IOException {
+    return myGenericSignature;
+  }
+
+  public int getSuperQualifiedName() throws IOException {
+    return mySuperQualifiedName;
+  }
+
+  public int[] getSuperInterfaces() throws IOException {
+    return mySuperInterfaces;
+  }
+
+  public int getFlags() throws IOException {
+    return myFlags;
+  }
+
+  public String getPath() throws IOException {
+    return myPath;
+  }
+
+  public void setPath(String path) {
+    myIsDirty |= !Comparing.equal(myPath, path);
+    myPath = path;
+  }
+
+  public String getSourceFileName() throws IOException {
+    return mySourceFileName;
+  }
+
+  public AnnotationConstantValue[] getRuntimeVisibleAnnotations() throws IOException {
+    return myRuntimeVisibleAnnotations;
+  }
+
+  public AnnotationConstantValue[] getRuntimeInvisibleAnnotations() throws IOException {
+    return myRuntimeInvisibleAnnotations;
+  }
+
+  public ReferenceInfo[] getReferences() {
+    return myReferences;
+  }
+
+  public void clearReferences() {
+    myReferences = EMPTY_REF_ARRAY;
+  }
+
+  public FieldInfo[] getFields() {
+    return myFields;
+  }
+
+  public MethodInfo[] getMethods() {
+    return myMethods;
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/ClassInfoConstantValue.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/ClassInfoConstantValue.java
new file mode 100644
index 0000000..a444d32
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/ClassInfoConstantValue.java
@@ -0,0 +1,56 @@
+/*
+ * 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 com.intellij.compiler.classParsing;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+public class ClassInfoConstantValue extends ConstantValue{
+  private final int myValue;
+
+  public ClassInfoConstantValue(int value) {
+    myValue = value;
+  }
+
+  public ClassInfoConstantValue(DataInput in) throws IOException{
+    myValue = in.readInt();
+  }
+
+  public int getValue() {
+    return myValue;
+  }
+
+  public void save(DataOutput out) throws IOException {
+    super.save(out);
+    out.writeInt(myValue);
+  }
+
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (!(o instanceof ClassInfoConstantValue)) return false;
+
+    final ClassInfoConstantValue classInfoConstantValue = (ClassInfoConstantValue)o;
+
+    if (myValue != classInfoConstantValue.myValue) return false;
+
+    return true;
+  }
+
+  public int hashCode() {
+    return myValue;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/ConstantValue.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/ConstantValue.java
new file mode 100644
index 0000000..31615b2
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/ConstantValue.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.
+ */
+
+/**
+ * created at Feb 24, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.classParsing;
+
+import java.io.DataOutput;
+import java.io.IOException;
+
+public class ConstantValue {
+  public static final ConstantValue EMPTY_CONSTANT_VALUE = new ConstantValue();
+
+  protected ConstantValue() {
+  }
+
+  public void save(DataOutput out) throws IOException {
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/ConstantValueArray.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/ConstantValueArray.java
new file mode 100644
index 0000000..850564a
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/ConstantValueArray.java
@@ -0,0 +1,68 @@
+/*
+ * 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 com.intellij.compiler.classParsing;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Apr 2, 2004
+ */
+public class ConstantValueArray extends ConstantValue{
+  private final ConstantValue[] myValue;
+
+  public ConstantValueArray(ConstantValue[] value) {
+    myValue = value;
+  }
+
+  public ConstantValueArray(DataInput in) throws IOException {
+    final int size = in.readInt();
+    myValue = new ConstantValue[size];
+    for (int idx = 0; idx < size; idx++) {
+      myValue[idx] = MemberInfoExternalizer.loadConstantValue(in);
+    }
+  }
+
+  public ConstantValue[] getValue() {
+    return myValue;
+  }
+
+  public void save(DataOutput out) throws IOException {
+    out.writeInt(myValue.length);
+    for (int idx = 0; idx < myValue.length; idx++) {
+      MemberInfoExternalizer.saveConstantValue(out, myValue[idx]);
+    }
+  }
+
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (!(o instanceof ConstantValueArray)) return false;
+
+    final ConstantValueArray constantValueArray = (ConstantValueArray)o;
+
+    if (!Arrays.equals(myValue, constantValueArray.myValue)) return false;
+
+    return true;
+  }
+
+  public int hashCode() {
+    return 0;
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/DoubleConstantValue.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/DoubleConstantValue.java
new file mode 100644
index 0000000..7e7b8e6
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/DoubleConstantValue.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.
+ */
+
+/**
+ * created at Feb 24, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.classParsing;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+public class DoubleConstantValue extends ConstantValue{
+  private final double myValue;
+
+  public DoubleConstantValue(double value) {
+    myValue = value;
+  }
+
+  public DoubleConstantValue(DataInput in) throws IOException{
+    myValue = in.readDouble();
+  }
+
+  public double getValue() {
+    return myValue;
+  }
+
+  public void save(DataOutput out) throws IOException {
+    super.save(out);
+    out.writeDouble(myValue);
+  }
+
+  public boolean equals(Object obj) {
+    return (obj instanceof DoubleConstantValue) && (((DoubleConstantValue)obj).myValue == myValue);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/EnumConstantValue.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/EnumConstantValue.java
new file mode 100644
index 0000000..548bda1
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/EnumConstantValue.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+/**
+ * created at Feb 24, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.classParsing;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+public class EnumConstantValue extends ConstantValue{
+  private final int myTypeName;
+  private final int myConstantName;
+
+  public EnumConstantValue(int typeName, int constantName) {
+    myTypeName = typeName;
+    myConstantName = constantName;
+  }
+
+  public EnumConstantValue(DataInput in) throws IOException{
+    myTypeName = in.readInt();
+    myConstantName = in.readInt();
+  }
+
+  public int getTypeName() {
+    return myTypeName;
+  }
+
+  public int getConstantName() {
+    return myConstantName;
+  }
+
+  public void save(DataOutput out) throws IOException {
+    super.save(out);
+    out.writeInt(myTypeName);
+    out.writeInt(myConstantName);
+  }
+
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (!(o instanceof EnumConstantValue)) return false;
+
+    final EnumConstantValue enumConstantValue = (EnumConstantValue)o;
+
+    if (myConstantName != enumConstantValue.myConstantName) return false;
+    if (myTypeName != enumConstantValue.myTypeName) return false;
+
+    return true;
+  }
+
+  public int hashCode() {
+    int result;
+    result = myTypeName;
+    result = 29 * result + myConstantName;
+    return result;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/FieldInfo.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/FieldInfo.java
new file mode 100644
index 0000000..4611f08
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/FieldInfo.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+/**
+ * created at Jan 10, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.classParsing;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+public class FieldInfo extends MemberInfo {
+  public static final FieldInfo[] EMPTY_ARRAY = new FieldInfo[0];
+  private final ConstantValue myConstantValue;
+
+  public FieldInfo(int name, int descriptor) {
+    super(name, descriptor);
+    myConstantValue = ConstantValue.EMPTY_CONSTANT_VALUE;
+  }
+
+  public FieldInfo(int name, int descriptor, final int genericSignature, int flags, ConstantValue value, final AnnotationConstantValue[] runtimeVisibleAnnotations, final AnnotationConstantValue[] runtimeInvisibleAnnotations) {
+    super(name, descriptor, genericSignature, flags, runtimeVisibleAnnotations, runtimeInvisibleAnnotations);
+    myConstantValue = value == null ? ConstantValue.EMPTY_CONSTANT_VALUE : value;
+  }
+
+  public FieldInfo(DataInput in) throws IOException {
+    super(in);
+    myConstantValue = MemberInfoExternalizer.loadConstantValue(in);
+  }
+
+  public ConstantValue getConstantValue() {
+    return myConstantValue;
+  }
+
+  public void save(DataOutput out) throws IOException {
+    super.save(out);
+    MemberInfoExternalizer.saveConstantValue(out, myConstantValue);
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/FloatConstantValue.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/FloatConstantValue.java
new file mode 100644
index 0000000..f818cbf
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/FloatConstantValue.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.
+ */
+
+/**
+ * created at Feb 24, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.classParsing;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+public class FloatConstantValue extends ConstantValue{
+  private final float myValue;
+
+  public FloatConstantValue(float value) {
+    myValue = value;
+  }
+
+  public FloatConstantValue(DataInput in) throws IOException{
+    myValue = in.readFloat();
+  }
+
+  public float getValue() {
+    return myValue;
+  }
+
+  public void save(DataOutput out) throws IOException {
+    super.save(out);
+    out.writeFloat(myValue);
+  }
+
+  public boolean equals(Object obj) {
+    return (obj instanceof FloatConstantValue) && (((FloatConstantValue)obj).myValue == myValue);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/GenericMethodSignature.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/GenericMethodSignature.java
new file mode 100644
index 0000000..10c01b4
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/GenericMethodSignature.java
@@ -0,0 +1,98 @@
+/*
+ * 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 com.intellij.compiler.classParsing;
+
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.util.ArrayUtil;
+
+import java.text.CharacterIterator;
+import java.text.StringCharacterIterator;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 4, 2004
+ */
+public class GenericMethodSignature {
+  private final String myFormalTypeParams;
+  private final String[] myParamSignatures;
+  private final String myReturnTypeSignature;
+  private final String myThrowsSignature;
+
+  private GenericMethodSignature(String formalTypeParams, String[] paramSignatures, String returnTypeSignature, String throwsSignature) {
+    myFormalTypeParams = formalTypeParams;
+    myParamSignatures = paramSignatures;
+    myReturnTypeSignature = returnTypeSignature;
+    myThrowsSignature = throwsSignature;
+  }
+
+  public String getFormalTypeParams() {
+    return myFormalTypeParams;
+  }
+
+  public String[] getParamSignatures() {
+    return myParamSignatures;
+  }
+
+  public String getReturnTypeSignature() {
+    return myReturnTypeSignature;
+  }
+
+  public String getThrowsSignature() {
+    return myThrowsSignature;
+  }
+
+  public static GenericMethodSignature parse(String methodSignature) throws SignatureParsingException {
+    final StringCharacterIterator it = new StringCharacterIterator(methodSignature);
+
+    final StringBuilder formals = new StringBuilder();
+    if (it.current() == '<') {
+      SignatureParser.INSTANCE.parseFormalTypeParameters(it, formals);
+    }
+
+    if (it.current() != '(') {
+      throw new SignatureParsingException(CompilerBundle.message("error.signature.parsing.expected.other.symbol", "(", formals.toString()));
+    }
+
+    it.next(); // skip '('
+
+    final String[] paramSignatures;
+    if (it.current() != ')') {
+      final List<String> params = new ArrayList<String>();
+      while (it.current() != ')') {
+        final StringBuilder typeSignature = new StringBuilder();
+        SignatureParser.INSTANCE.parseTypeSignature(it, typeSignature);
+        params.add(typeSignature.toString());
+      }
+      paramSignatures = ArrayUtil.toStringArray(params);
+    }
+    else {
+      paramSignatures = ArrayUtil.EMPTY_STRING_ARRAY;
+    }
+    it.next(); // skip ')'
+
+    final StringBuilder returnTypeSignature = new StringBuilder();
+    SignatureParser.INSTANCE.parseReturnType(it, returnTypeSignature);
+
+    final StringBuilder throwsSignature = new StringBuilder();
+    if (it.current() != CharacterIterator.DONE) {
+      SignatureParser.INSTANCE.parseThrowsSignature(it, throwsSignature);
+    }
+    
+    return new GenericMethodSignature(formals.toString(), paramSignatures, returnTypeSignature.toString(), throwsSignature.toString());
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/IntegerConstantValue.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/IntegerConstantValue.java
new file mode 100644
index 0000000..d59a727
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/IntegerConstantValue.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.
+ */
+
+/**
+ * created at Feb 24, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.classParsing;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+public class IntegerConstantValue extends ConstantValue{
+  private final int myValue;
+
+  public IntegerConstantValue(int value) {
+    myValue = value;
+  }
+
+  public IntegerConstantValue(DataInput in) throws IOException{
+    myValue = in.readInt();
+  }
+
+  public int getValue() {
+    return myValue;
+  }
+
+  public void save(DataOutput out) throws IOException {
+    super.save(out);
+    out.writeInt(myValue);
+  }
+
+  public boolean equals(Object obj) {
+    return (obj instanceof IntegerConstantValue) && (((IntegerConstantValue)obj).myValue == myValue);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/LongConstantValue.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/LongConstantValue.java
new file mode 100644
index 0000000..2cbe2d7
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/LongConstantValue.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+/**
+ * created at Feb 24, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.classParsing;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+public class LongConstantValue extends ConstantValue{
+  private final long myValue;
+
+  public LongConstantValue(long value) {
+    myValue = value;
+  }
+  public LongConstantValue(DataInput in) throws IOException{
+    myValue = in.readLong();
+  }
+
+  public long getValue() {
+    return myValue;
+  }
+
+  public void save(DataOutput out) throws IOException {
+    super.save(out);
+    out.writeLong(myValue);
+  }
+
+  public boolean equals(Object obj) {
+    return (obj instanceof LongConstantValue) && (((LongConstantValue)obj).myValue == myValue);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/MemberInfo.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/MemberInfo.java
new file mode 100644
index 0000000..3394a58
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/MemberInfo.java
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+/**
+ * created at Jan 8, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.classParsing;
+
+import com.intellij.util.cls.ClsUtil;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+public abstract class MemberInfo {
+  public static final MemberInfo[] EMPTY_MEMBER_INFO_ARRAY = new MemberInfo[0];
+  private static final int FLAG_INFO_UNAVAILABLE = 0x8000;
+  private final int myName;
+  private final int myDescriptor;
+  private final int myGenericSignature;
+  private final int myFlags;
+  private final AnnotationConstantValue[] myRuntimeVisibleAnnotations;
+  private final AnnotationConstantValue[] myRuntimeInvisibleAnnotations;
+
+  protected MemberInfo(int name, int descriptor) {
+    this(name, descriptor, -1, FLAG_INFO_UNAVAILABLE, AnnotationConstantValue.EMPTY_ARRAY, AnnotationConstantValue.EMPTY_ARRAY);
+  }
+
+  protected MemberInfo(int name, int descriptor, int genericSignature, int flags, final AnnotationConstantValue[] runtimeVisibleAnnotations, final AnnotationConstantValue[] runtimeInvisibleAnnotations) {
+    myDescriptor = descriptor;
+    myGenericSignature = genericSignature;
+    myName = name;
+    myFlags = flags;
+    myRuntimeVisibleAnnotations = runtimeVisibleAnnotations != null? runtimeVisibleAnnotations : AnnotationConstantValue.EMPTY_ARRAY;
+    myRuntimeInvisibleAnnotations = runtimeInvisibleAnnotations != null? runtimeInvisibleAnnotations : AnnotationConstantValue.EMPTY_ARRAY;
+  }
+
+  protected MemberInfo(DataInput in) throws IOException {
+    myName = in.readInt();
+    myDescriptor = in.readInt();
+    myGenericSignature = in.readInt();
+    myFlags = in.readInt();
+    myRuntimeVisibleAnnotations = loadAnnotations(in);
+    myRuntimeInvisibleAnnotations = loadAnnotations(in);
+  }
+
+  public void save(DataOutput out) throws IOException {
+    out.writeInt(myName);
+    out.writeInt(myDescriptor);
+    out.writeInt(myGenericSignature);
+    out.writeInt(myFlags);
+    saveAnnotations(out, myRuntimeVisibleAnnotations);
+    saveAnnotations(out, myRuntimeInvisibleAnnotations);
+  }
+
+  public int getName() {
+    return myName;
+  }
+
+  public int getDescriptor() {
+    return myDescriptor;
+  }
+
+  public int getGenericSignature() {
+    return myGenericSignature;
+  }
+
+  public boolean isFlagInfoAvailable() {
+    return myFlags != FLAG_INFO_UNAVAILABLE;
+  }
+
+  public int getFlags() {
+    return myFlags;
+  }
+
+  public boolean isPublic() {
+    return ClsUtil.isPublic(myFlags);
+  }
+
+  public boolean isProtected() {
+    return ClsUtil.isProtected(myFlags);
+  }
+
+  public boolean isFinal() {
+    return ClsUtil.isFinal(myFlags);
+  }
+
+  public boolean isPrivate() {
+    return ClsUtil.isPrivate(myFlags);
+  }
+
+  public boolean isPackageLocal() {
+    return ClsUtil.isPackageLocal(myFlags);
+  }
+
+  public boolean isStatic() {
+    return ClsUtil.isStatic(myFlags);
+  }
+
+  public boolean equals(Object obj) {
+    if (!(obj instanceof MemberInfo)) return false;
+    MemberInfo info = (MemberInfo)obj;
+    return (myName == info.myName) && (myDescriptor == info.myDescriptor);
+  }
+
+  public int hashCode() {
+    return myName + myDescriptor;
+  }
+
+  public AnnotationConstantValue[] getRuntimeVisibleAnnotations() {
+    return myRuntimeVisibleAnnotations;
+  }
+
+  public AnnotationConstantValue[] getRuntimeInvisibleAnnotations() {
+    return myRuntimeInvisibleAnnotations;
+  }
+
+  protected final void saveAnnotations(DataOutput out, final AnnotationConstantValue[] annotations) throws IOException {
+    out.writeInt(annotations.length);
+    for (AnnotationConstantValue annotation : annotations) {
+      MemberInfoExternalizer.saveConstantValue(out, annotation);
+    }
+  }
+
+  protected final AnnotationConstantValue[] loadAnnotations(DataInput in) throws IOException {
+    final int size = in.readInt();
+    if (size == 0) {
+      return AnnotationConstantValue.EMPTY_ARRAY;
+    }
+    final AnnotationConstantValue[] annotations = new AnnotationConstantValue[size];
+    for (int idx = 0; idx < size; idx++) {
+      annotations[idx] = (AnnotationConstantValue)MemberInfoExternalizer.loadConstantValue(in);
+    }
+    return annotations;
+  }
+
+}
+
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/MemberInfoExternalizer.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/MemberInfoExternalizer.java
new file mode 100644
index 0000000..a807bd5
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/MemberInfoExternalizer.java
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+
+/**
+ * created at Jan 10, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.classParsing;
+
+import com.intellij.openapi.diagnostic.Logger;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+public class MemberInfoExternalizer {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.classParsing.MemberInfoExternalizer");
+
+  public static final byte FIELD_INFO_TAG = 1;
+  public static final byte METHOD_INFO_TAG = 2;
+
+  public static final byte DECLARATION_INFO_TAG = 9;
+
+  public static final byte REFERENCE_INFO_TAG = 11;
+  public static final byte MEMBER_REFERENCE_INFO_TAG = 12;
+
+  public static final byte LONG_CONSTANT_TAG = 3;
+  public static final byte FLOAT_CONSTANT_TAG = 4;
+  public static final byte DOUBLE_CONSTANT_TAG = 5;
+  public static final byte INTEGER_CONSTANT_TAG = 6;
+  public static final byte STRING_CONSTANT_TAG = 7;
+  public static final byte CONSTANT_TAG = 8;
+  public static final byte ANNOTATION_CONSTANT_TAG = 13;
+  public static final byte ANNOTATION_PRIMITIVE_CONSTANT_TAG = 14;
+  public static final byte CONSTANT_VALUE_ARRAY_TAG = 15;
+  public static final byte CLASS_CONSTANT_VALUE_TAG = 16;
+  public static final byte ENUM_CONSTANT_VALUE_TAG = 17;
+
+  public static MemberInfo loadMemberInfo(DataInput in) throws IOException {
+    byte tag = in.readByte();
+    if (tag == METHOD_INFO_TAG) {
+      return new MethodInfo(in);
+    }
+    else if (tag == FIELD_INFO_TAG) {
+      return new FieldInfo(in);
+    }
+    LOG.error("Unknown member info");
+    return null;
+  }
+
+  public static ReferenceInfo loadReferenceInfo(DataInput in) throws IOException {
+    final byte tag = in.readByte();
+    if (tag == REFERENCE_INFO_TAG) {
+      return new ReferenceInfo(in);
+    }
+    else if (tag == MEMBER_REFERENCE_INFO_TAG) {
+      return new MemberReferenceInfo(in);
+    }
+    LOG.error("Unknown declaration info tag: " + tag);
+    return null;
+  }
+
+  public static ConstantValue loadConstantValue(DataInput in) throws IOException {
+    final byte tag = in.readByte();
+    if (tag == LONG_CONSTANT_TAG) {
+      return new LongConstantValue(in);
+    }
+    else if (tag == FLOAT_CONSTANT_TAG) {
+      return new FloatConstantValue(in);
+    }
+    else if (tag == DOUBLE_CONSTANT_TAG) {
+      return new DoubleConstantValue(in);
+    }
+    else if (tag == INTEGER_CONSTANT_TAG) {
+      return new IntegerConstantValue(in);
+    }
+    else if (tag == STRING_CONSTANT_TAG) {
+      return new StringConstantValue(in);
+    }
+    else if (tag == CONSTANT_TAG) {
+      return ConstantValue.EMPTY_CONSTANT_VALUE;
+    }
+    else if (tag == ANNOTATION_CONSTANT_TAG) {
+      return new AnnotationConstantValue(in);
+    }
+    else if (tag == ANNOTATION_PRIMITIVE_CONSTANT_TAG) {
+      return new AnnotationPrimitiveConstantValue(in);
+    }
+    else if (tag == CONSTANT_VALUE_ARRAY_TAG) {
+      return new ConstantValueArray(in);
+    }
+    else if (tag == CLASS_CONSTANT_VALUE_TAG) {
+      return new ClassInfoConstantValue(in);
+    }
+    else if (tag == ENUM_CONSTANT_VALUE_TAG) {
+      return new EnumConstantValue(in);
+    }
+    LOG.error("Unknown constant value type " + tag);
+    return null;
+  }
+
+  public static void saveMemberInfo(DataOutput out, MemberInfo info) throws IOException {
+    if (info instanceof MethodInfo) {
+      out.writeByte(METHOD_INFO_TAG);
+    }
+    else if (info instanceof FieldInfo){
+      out.writeByte(FIELD_INFO_TAG);
+    }
+    else {
+      LOG.error("Unknown member info");
+    }
+    info.save(out);
+  }
+
+  public static void saveReferenceInfo(DataOutput out, ReferenceInfo info) throws IOException {
+    if (info instanceof MemberReferenceInfo) {
+      out.writeByte(MEMBER_REFERENCE_INFO_TAG);
+    }
+    else{
+      out.writeByte(REFERENCE_INFO_TAG);
+    }
+    info.save(out);
+  }
+
+  public static void saveConstantValue(DataOutput out, ConstantValue value) throws IOException {
+    if (value instanceof LongConstantValue) {
+      out.writeByte(LONG_CONSTANT_TAG);
+    }
+    else if (value instanceof FloatConstantValue){
+      out.writeByte(FLOAT_CONSTANT_TAG);
+    }
+    else if (value instanceof DoubleConstantValue){
+      out.writeByte(DOUBLE_CONSTANT_TAG);
+    }
+    else if (value instanceof IntegerConstantValue){
+      out.writeByte(INTEGER_CONSTANT_TAG);
+    }
+    else if (value instanceof StringConstantValue){
+      out.writeByte(STRING_CONSTANT_TAG);
+    }
+    else if (value instanceof AnnotationConstantValue) {
+      out.writeByte(ANNOTATION_CONSTANT_TAG);
+    }
+    else if (value instanceof AnnotationPrimitiveConstantValue) {
+      out.writeByte(ANNOTATION_PRIMITIVE_CONSTANT_TAG);
+    }
+    else if (value instanceof ConstantValueArray) {
+      out.writeByte(CONSTANT_VALUE_ARRAY_TAG);
+    }
+    else if (value instanceof ClassInfoConstantValue) {
+      out.writeByte(CLASS_CONSTANT_VALUE_TAG);
+    }
+    else if (value instanceof EnumConstantValue) {
+      out.writeByte(ENUM_CONSTANT_VALUE_TAG);
+    }
+    else {
+      out.writeByte(CONSTANT_TAG);
+    }
+    if (value != null) {
+      value.save(out);
+    }
+  }
+
+  public static AnnotationConstantValue[] readAnnotationConstantValueArray1(DataInput in) throws IOException {
+    final int size = in.readInt();
+    final AnnotationConstantValue[] array = size > 0? new AnnotationConstantValue[size] : AnnotationConstantValue.EMPTY_ARRAY;
+    for (int idx = 0; idx < size; idx++) {
+      array[idx] = (AnnotationConstantValue)loadConstantValue(in);
+    }
+    return array;
+  }
+
+  public static AnnotationConstantValue[][] readAnnotationConstantValueArray2(DataInput in) throws IOException {
+    final int size = in.readInt();
+    final AnnotationConstantValue[][] array = size > 0? new AnnotationConstantValue[size][] : AnnotationConstantValue.EMPTY_ARRAY_ARRAY;
+    for (int idx = 0; idx < size; idx++) {
+      array[idx] = readAnnotationConstantValueArray1(in);
+    }
+    return array;
+  }
+
+  public static void writeConstantValueArray1(DataOutput writer, final ConstantValue[] array) throws IOException {
+    if (array != null && array.length > 0) {
+      writer.writeInt(array.length);
+      for (ConstantValue value : array) {
+        saveConstantValue(writer, value);
+      }
+    }
+    else {
+      writer.writeInt(0);
+    }
+  }
+
+  public static void writeConstantValueArray2(DataOutput writer, final ConstantValue[][] array) throws IOException {
+    if (array != null && array.length > 0) {
+      writer.writeInt(array.length);
+      for (ConstantValue[] aArray : array) {
+        writeConstantValueArray1(writer, aArray);
+      }
+    }
+    else {
+      writer.writeInt(0);
+    }
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/MemberReferenceInfo.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/MemberReferenceInfo.java
new file mode 100644
index 0000000..63d7c54
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/MemberReferenceInfo.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+/**
+ * created at Jan 8, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.classParsing;
+
+import org.jetbrains.annotations.NonNls;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+public class MemberReferenceInfo extends ReferenceInfo {
+  private final MemberInfo myMemberInfo;
+
+  public MemberReferenceInfo(int declaringClass, MemberInfo memberInfo) {
+    super(declaringClass);
+    myMemberInfo = memberInfo;
+  }
+
+  public MemberReferenceInfo(DataInput in) throws IOException {
+    super(in);
+    myMemberInfo = MemberInfoExternalizer.loadMemberInfo(in);
+  }
+
+  public MemberInfo getMemberInfo() {
+    return myMemberInfo;
+  }
+
+  public boolean isFieldReference() {
+    return myMemberInfo instanceof FieldInfo;
+  }
+
+  public boolean isMethodReference() {
+    return myMemberInfo instanceof MethodInfo;
+  }
+
+  public void save(DataOutput out) throws IOException {
+    super.save(out);
+    MemberInfoExternalizer.saveMemberInfo(out, myMemberInfo);
+  }
+
+  public boolean equals(Object o) {
+    if (!super.equals(o)) {
+      return false;
+    }
+    return myMemberInfo.equals(((MemberReferenceInfo)o).myMemberInfo);
+  }
+
+  public int hashCode() {
+    return super.hashCode() + myMemberInfo.hashCode();
+  }
+
+  public @NonNls String toString() { // for debug purposes
+    return "Member reference: [class name=" + getClassName() + ", member name = " + myMemberInfo.getName() + ", member descriptor=" + myMemberInfo.getDescriptor() + ", member signature=" + myMemberInfo.getGenericSignature() + "]";
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/MethodInfo.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/MethodInfo.java
new file mode 100644
index 0000000..c18d9ae
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/MethodInfo.java
@@ -0,0 +1,263 @@
+/*
+ * 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.
+ */
+
+/**
+ * created at Jan 10, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.classParsing;
+
+import com.intellij.compiler.SymbolTable;
+import com.intellij.compiler.make.CacheCorruptedException;
+import com.intellij.compiler.make.CacheUtils;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.cls.ClsUtil;
+import gnu.trove.TIntHashSet;
+import org.jetbrains.annotations.NonNls;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class MethodInfo extends MemberInfo {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.classParsing.MethodInfo");
+
+  private static final int[] EXCEPTION_INFO_UNAVAILABLE = ArrayUtil.EMPTY_INT_ARRAY;
+  public static final MethodInfo[] EMPTY_ARRAY = new MethodInfo[0];
+
+  private final int[] myThrownExceptions;
+  // cached (lazy initialized) data
+  private String mySignature = null;
+  private String[] myParameterDescriptors = null;
+  private String myReturnTypeSignature = null;
+  private final boolean myIsConstructor;
+  private final AnnotationConstantValue[][] myRuntimeVisibleParameterAnnotations;
+  private final AnnotationConstantValue[][] myRuntimeInvisibleParameterAnnotations;
+  private final ConstantValue myAnnotationDefault;
+
+  public MethodInfo(int name, int descriptor, boolean isConstructor) {
+    super(name, descriptor);
+    myIsConstructor = isConstructor;
+    myThrownExceptions = EXCEPTION_INFO_UNAVAILABLE;
+    myRuntimeVisibleParameterAnnotations = AnnotationConstantValue.EMPTY_ARRAY_ARRAY;
+    myRuntimeInvisibleParameterAnnotations = AnnotationConstantValue.EMPTY_ARRAY_ARRAY;
+    myAnnotationDefault = ConstantValue.EMPTY_CONSTANT_VALUE;
+  }
+
+  public MethodInfo(int name,
+                    int descriptor,
+                    final int genericSignature,
+                    int flags,
+                    int[] exceptions,
+                    boolean isConstructor,
+                    final AnnotationConstantValue[] runtimeVisibleAnnotations,
+                    final AnnotationConstantValue[] runtimeInvisibleAnnotations,
+                    final AnnotationConstantValue[][] runtimeVisibleParameterAnnotations,
+                    final AnnotationConstantValue[][] runtimeInvisibleParameterAnnotations, ConstantValue annotationDefault) {
+
+    super(name, descriptor, genericSignature, flags, runtimeVisibleAnnotations, runtimeInvisibleAnnotations);
+    myThrownExceptions = exceptions != null? exceptions : ArrayUtil.EMPTY_INT_ARRAY;
+    myIsConstructor = isConstructor;
+    myRuntimeVisibleParameterAnnotations = runtimeVisibleParameterAnnotations == null? AnnotationConstantValue.EMPTY_ARRAY_ARRAY : runtimeVisibleParameterAnnotations; 
+    myRuntimeInvisibleParameterAnnotations = runtimeInvisibleParameterAnnotations == null? AnnotationConstantValue.EMPTY_ARRAY_ARRAY : runtimeInvisibleParameterAnnotations;
+    myAnnotationDefault = annotationDefault;
+  }
+
+  public MethodInfo(DataInput in) throws IOException {
+    super(in);
+    myIsConstructor = in.readBoolean();
+    int count = in.readInt();
+    if (count == -1) {
+      myThrownExceptions = EXCEPTION_INFO_UNAVAILABLE;
+    }
+    else {
+      myThrownExceptions = ArrayUtil.newIntArray(count);
+      for (int idx = 0; idx < count; idx++) {
+        myThrownExceptions[idx] = in.readInt();
+      }
+    }
+    myRuntimeVisibleParameterAnnotations = loadParameterAnnotations(in);
+    myRuntimeInvisibleParameterAnnotations = loadParameterAnnotations(in);
+    myAnnotationDefault = MemberInfoExternalizer.loadConstantValue(in);
+  }
+
+  public void save(DataOutput out) throws IOException {
+    super.save(out);
+    out.writeBoolean(myIsConstructor);
+    if (isExceptionInfoAvailable()) {
+      out.writeInt(myThrownExceptions.length);
+    }
+    else {
+      out.writeInt(-1);
+    }
+    for (int thrownException : myThrownExceptions) {
+      out.writeInt(thrownException);
+    }
+    saveParameterAnnotations(out, myRuntimeVisibleParameterAnnotations);
+    saveParameterAnnotations(out, myRuntimeInvisibleParameterAnnotations);
+    MemberInfoExternalizer.saveConstantValue(out, myAnnotationDefault);
+  }
+
+  private boolean isExceptionInfoAvailable() {
+    return myThrownExceptions != EXCEPTION_INFO_UNAVAILABLE;
+  }
+
+  public boolean areExceptionsEqual(MethodInfo info) {
+    if (myThrownExceptions.length != info.myThrownExceptions.length) {
+      return false;
+    }
+    if (myThrownExceptions.length != 0) { // optimization
+      TIntHashSet exceptionsSet = new TIntHashSet();
+      for (int thrownException : myThrownExceptions) {
+        exceptionsSet.add(thrownException);
+      }
+      for (int exception : info.myThrownExceptions) {
+        if (!exceptionsSet.contains(exception)) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  public int[] getThrownExceptions() {
+    return myThrownExceptions;
+  }
+
+  public String getDescriptor(SymbolTable symbolTable) throws CacheCorruptedException {
+    if (mySignature == null) {
+      final String descriptor = symbolTable.getSymbol(getDescriptor());
+      final String name = symbolTable.getSymbol(getName());
+      mySignature = CacheUtils.getMethodSignature(name, descriptor);
+    }
+    return mySignature;
+  }
+
+  public String getReturnTypeDescriptor(SymbolTable symbolTable) throws CacheCorruptedException {
+    if (myReturnTypeSignature == null) {
+      String descriptor = symbolTable.getSymbol(getDescriptor());
+      myReturnTypeSignature = descriptor.substring(descriptor.indexOf(')') + 1, descriptor.length());
+    }
+    return myReturnTypeSignature;
+  }
+
+  public String[] getParameterDescriptors(SymbolTable symbolTable) throws CacheCorruptedException {
+    if (myParameterDescriptors == null) {
+      String descriptor = symbolTable.getSymbol(getDescriptor());
+      int endIndex = descriptor.indexOf(')');
+      if (endIndex <= 0) {
+        LOG.error("Corrupted method descriptor: " + descriptor);
+      }
+      myParameterDescriptors = parseParameterDescriptors(descriptor.substring(1, endIndex));
+    }
+    return myParameterDescriptors;
+  }
+
+  public boolean isAbstract() {
+    return ClsUtil.isAbstract(getFlags());
+  }
+
+  public boolean isConstructor() {
+    return myIsConstructor;
+  }
+
+  private String[] parseParameterDescriptors(String signature) {
+    ArrayList<String> list = new ArrayList<String>();
+    String paramSignature = parseFieldType(signature);
+    while (paramSignature != null && !"".equals(paramSignature)) {
+      list.add(paramSignature);
+      signature = signature.substring(paramSignature.length());
+      paramSignature = parseFieldType(signature);
+    }
+    return ArrayUtil.toStringArray(list);
+  }
+
+  private @NonNls String parseFieldType(@NonNls String signature) {
+    if (signature.length() == 0) {
+      return null;
+    }
+    if (signature.charAt(0) == 'B') {
+      return "B";
+    }
+    if (signature.charAt(0) == 'C') {
+      return "C";
+    }
+    if (signature.charAt(0) == 'D') {
+      return "D";
+    }
+    if (signature.charAt(0) == 'F') {
+      return "F";
+    }
+    if (signature.charAt(0) == 'I') {
+      return "I";
+    }
+    if (signature.charAt(0) == 'J') {
+      return "J";
+    }
+    if (signature.charAt(0) == 'S') {
+      return "S";
+    }
+    if (signature.charAt(0) == 'Z') {
+      return "Z";
+    }
+    if (signature.charAt(0) == 'L') {
+      return signature.substring(0, signature.indexOf(";") + 1);
+    }
+    if (signature.charAt(0) == '[') {
+      String s = parseFieldType(signature.substring(1));
+      return (s != null)? ("[" + s) : null;
+    }
+    return null;
+  }
+
+  public AnnotationConstantValue[][] getRuntimeVisibleParameterAnnotations() {
+    return myRuntimeVisibleParameterAnnotations;
+  }
+
+  public AnnotationConstantValue[][] getRuntimeInvisibleParameterAnnotations() {
+    return myRuntimeInvisibleParameterAnnotations;
+  }
+
+  public String toString() {
+    return mySignature;
+  }
+
+  private AnnotationConstantValue[][] loadParameterAnnotations(DataInput in) throws IOException {
+    final int size = in.readInt();
+    if (size == 0) {
+      return AnnotationConstantValue.EMPTY_ARRAY_ARRAY;
+    }
+    final AnnotationConstantValue[][] paramAnnotations = new AnnotationConstantValue[size][];
+    for (int idx = 0; idx < size; idx++) {
+      paramAnnotations[idx] = loadAnnotations(in);
+    }
+    return paramAnnotations;
+  }
+
+  private void saveParameterAnnotations(DataOutput out, AnnotationConstantValue[][] parameterAnnotations) throws IOException {
+    out.writeInt(parameterAnnotations.length);
+    for (AnnotationConstantValue[] parameterAnnotation : parameterAnnotations) {
+      saveAnnotations(out, parameterAnnotation);
+    }
+  }
+
+  public ConstantValue getAnnotationDefault() {
+    return myAnnotationDefault;
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/ReferenceInfo.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/ReferenceInfo.java
new file mode 100644
index 0000000..3cf9a19
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/ReferenceInfo.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author Eugene Zhuravlev
+ */
+package com.intellij.compiler.classParsing;
+
+import org.jetbrains.annotations.NonNls;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+public class ReferenceInfo {
+  private final int myClassName;
+
+  public ReferenceInfo(int declaringClassName) {
+    myClassName = declaringClassName;
+  }
+
+  public ReferenceInfo(DataInput in) throws IOException {
+    this(in.readInt());
+  }
+
+  public @NonNls String toString() { // for debug purposes
+    return "Class reference[class name=" + String.valueOf(getClassName()) + "]";
+  }
+
+  public void save(DataOutput out) throws IOException {
+    out.writeInt(myClassName);
+  }
+
+  public int getClassName() {
+    return myClassName;
+  }
+
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    ReferenceInfo that = (ReferenceInfo)o;
+
+    if (myClassName != that.myClassName) return false;
+
+    return true;
+  }
+
+  public int hashCode() {
+    return myClassName;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/SignatureParser.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/SignatureParser.java
new file mode 100644
index 0000000..d195cf3
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/SignatureParser.java
@@ -0,0 +1,259 @@
+/*
+ * 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 com.intellij.compiler.classParsing;
+
+import com.intellij.openapi.compiler.CompilerBundle;
+
+import java.text.CharacterIterator;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 4, 2004
+ */
+
+public class SignatureParser {
+  public static final SignatureParser INSTANCE = new SignatureParser();
+
+  public void parseIdentifier(CharacterIterator it, final StringBuilder buf) {
+    while (Character.isJavaIdentifierPart(it.current())) {
+      buf.append(it.current());
+      it.next();
+    }
+  }
+
+  public void parseFormalTypeParameters(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    if (it.current() != '<') {
+      throw new SignatureParsingException(CompilerBundle.message("error.signature.parsing.expected.other.symbol", "<", buf.toString()));
+    }
+
+    buf.append(it.current()); // skip '<'
+    it.next();
+
+    while (it.current() != '>') {
+      parseFormalTypeParameter(it, buf);
+    }
+
+    buf.append(it.current());
+    it.next();
+  }
+
+  public void parseFormalTypeParameter(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    parseIdentifier(it, buf);
+    parseClassBound(it, buf);
+    while (it.current() == ':') {
+      parseInterfaceBound(it, buf);
+    }
+  }
+
+  public void parseClassBound(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    if (it.current() != ':') {
+      throw new SignatureParsingException(CompilerBundle.message("error.signature.parsing.expected.other.symbol", ":", buf.toString()));
+    }
+    buf.append(it.current());
+    it.next();
+
+    final char current = it.current();
+    if (current != CharacterIterator.DONE && current != ':') {
+      parseFieldTypeSignature(it, buf);
+    }
+  }
+
+  public void parseInterfaceBound(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    if (it.current() != ':') {
+      throw new SignatureParsingException(CompilerBundle.message("error.signature.parsing.expected.other.symbol", ":", buf.toString()));
+    }
+    buf.append(it.current());
+    it.next();
+    parseFieldTypeSignature(it, buf);
+  }
+
+  public void parseSuperclassSignature(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    parseClassTypeSignature(it, buf);
+  }
+
+  public void parseSuperinterfaceSignature(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    parseClassTypeSignature(it, buf);
+  }
+
+  public void parseFieldTypeSignature(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    if (it.current() == 'L') {
+      parseClassTypeSignature(it, buf);
+    }
+    else if (it.current() == '[') {
+      parseArrayTypeSignature(it, buf);
+    }
+    else if (it.current() == 'T') {
+      parseTypeVariableSignature(it, buf);
+    }
+    else {
+      //noinspection HardCodedStringLiteral
+      throw new SignatureParsingException(CompilerBundle.message("error.signature.parsing.expected.other.symbol", "'L' / '[' / 'T'", buf.toString()));
+    }
+  }
+
+  public void parseClassTypeSignature(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    buf.append(it.current());
+    it.next();     // consume 'L'
+    parseSimpleClassTypeSignature(it, buf);
+    while (it.current() == '/' || it.current() == '.') { // Eclipse compiler generates '.' for inner classes 
+      parseClassTypeSignatureSuffix(it, buf);
+    }
+    if (it.current() != ';') {
+      throw new SignatureParsingException(CompilerBundle.message("error.signature.parsing.expected.other.symbol", ";", buf.toString()));
+    }
+    buf.append(it.current());
+    it.next(); // consume ';'
+  }
+
+  public void parseSimpleClassTypeSignature(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    parseIdentifier(it, buf);
+    if (it.current() == '<') {
+      parseTypeArguments(it, buf);
+    }
+  }
+
+  public void parseClassTypeSignatureSuffix(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    buf.append(it.current());
+    it.next();
+    parseSimpleClassTypeSignature(it, buf);
+  }
+
+  public void parseTypeVariableSignature(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    buf.append(it.current());
+    it.next(); // consume 'T'
+    parseIdentifier(it, buf);
+    if (it.current() != ';') {
+      throw new SignatureParsingException(CompilerBundle.message("error.signature.parsing.expected.other.symbol", ";", buf.toString()));
+    }
+    buf.append(it.current());
+    it.next(); // consume ';'
+  }
+
+  public void parseTypeArguments(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    buf.append(it.current());
+    it.next(); // consume '<'
+    while (it.current() != '>') {
+      parseTypeArgument(it, buf);
+    }
+    buf.append(it.current());
+    it.next(); // consume '>'
+  }
+
+  public void parseTypeArgument(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    if (it.current() == '*') {
+      parseWildcardIndicator(it, buf);
+    }
+    else {
+      if (it.current() == '+' || it.current() == '-') {
+        parseWildcardIndicator(it, buf);
+      }
+      parseFieldTypeSignature(it, buf);
+    }
+  }
+
+  public void parseWildcardIndicator(CharacterIterator it, final StringBuilder buf) {
+    buf.append(it.current());
+    it.next();
+  }
+
+  public void parseArrayTypeSignature(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    buf.append(it.current());
+    it.next(); // consume '['
+    parseTypeSignature(it, buf);
+  }
+
+  public void parseTypeSignature(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    char current = it.current();
+    if (current == 'B' || current == 'C' || current == 'D' || current == 'F' || current == 'I' || current == 'J' || current == 'S' || current == 'Z') {
+      buf.append(it.current());
+      it.next(); // base type
+    }
+    else if (current == 'L' || current == '[' || current == 'T') {
+      parseFieldTypeSignature(it, buf);
+    }
+    else {
+      throw new SignatureParsingException(CompilerBundle.message("error.signature.parsing.unknown.type.signature"));
+    }
+  }
+
+  public void parseReturnType(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    if (it.current() == 'V') {
+      buf.append(it.current());
+      it.next();
+    }
+    else {
+      parseTypeSignature(it, buf);
+    }
+  }
+
+  public void parseThrowsSignature(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    if (it.current() != '^') {
+      throw new SignatureParsingException(CompilerBundle.message("error.signature.parsing.expected.other.symbol", "^", buf.toString()));
+    }
+    buf.append(it.current());
+    it.next();
+    if (it.current() == 'T') {
+      parseTypeVariableSignature(it, buf);
+    }
+    else {
+      parseClassTypeSignature(it, buf);
+    }
+  }
+
+  public void parseMethodSignature(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    if (it.current() == '<') {
+      parseFormalTypeParameters(it, buf);
+    }
+
+    if (it.current() != '(') {
+      throw new SignatureParsingException(CompilerBundle.message("error.signature.parsing.expected.other.symbol", "(", buf.toString()));
+    }
+
+    buf.append(it.current());
+    it.next(); // skip '('
+
+    while (it.current() != ')') {
+      parseTypeSignature(it, buf);
+    }
+
+    buf.append(it.current());
+    it.next(); // skip ')'
+
+    parseReturnType(it, buf);
+
+    if (it.current() != CharacterIterator.DONE) {
+      parseThrowsSignature(it, buf);
+    }
+  }
+
+  public void parseClassSignature(CharacterIterator it, final StringBuilder buf) throws SignatureParsingException {
+    if (it.current() == '<') {
+      buf.append(it.current());
+      it.next();
+      while (it.current() != '>') {
+        parseFormalTypeParameter(it, buf);
+      }
+      buf.append(it.current());
+      it.next();
+    }
+
+    parseClassTypeSignature(it, buf);
+
+    while (it.current() != CharacterIterator.DONE) {
+      parseClassTypeSignature(it, buf);
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/SignatureParsingException.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/SignatureParsingException.java
new file mode 100644
index 0000000..7246b19
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/SignatureParsingException.java
@@ -0,0 +1,26 @@
+/*
+ * 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 com.intellij.compiler.classParsing;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 4, 2004
+ */
+public class SignatureParsingException extends Exception{
+  public SignatureParsingException(String message) {
+    super(message);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/classParsing/StringConstantValue.java b/java/compiler/impl/src/com/intellij/compiler/classParsing/StringConstantValue.java
new file mode 100644
index 0000000..faf560f
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/classParsing/StringConstantValue.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.
+ */
+
+/**
+ * created at Feb 24, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.classParsing;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+public class StringConstantValue extends ConstantValue{
+  private final String myValue;
+
+  public StringConstantValue(String value) {
+    myValue = value;
+  }
+
+  public StringConstantValue(DataInput in) throws IOException{
+    myValue = in.readUTF();
+  }
+
+  public String getValue() {
+    return myValue;
+  }
+
+  public void save(DataOutput out) throws IOException {
+    super.save(out);
+    out.writeUTF(myValue);
+  }
+
+  public boolean equals(Object obj) {
+    return
+      (obj instanceof StringConstantValue) &&
+      ((myValue == null)? ((StringConstantValue)obj).myValue == null : myValue.equals(((StringConstantValue)obj).myValue));
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/AdditionalCompileScopeProvider.java b/java/compiler/impl/src/com/intellij/compiler/impl/AdditionalCompileScopeProvider.java
new file mode 100644
index 0000000..0b9ed31
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/AdditionalCompileScopeProvider.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.
+ */
+
+package com.intellij.compiler.impl;
+
+import com.intellij.openapi.compiler.CompilerFilter;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public abstract class AdditionalCompileScopeProvider {
+  public static final ExtensionPointName<AdditionalCompileScopeProvider> EXTENSION_POINT_NAME = ExtensionPointName.create("com.intellij.compiler.additionalCompileScopeProvider");
+
+  @Nullable
+  public abstract CompileScope getAdditionalScope(@NotNull CompileScope baseScope, @NotNull CompilerFilter filter, @NotNull Project project);
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/BuildTargetScopeProvider.java b/java/compiler/impl/src/com/intellij/compiler/impl/BuildTargetScopeProvider.java
new file mode 100644
index 0000000..2f55990
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/BuildTargetScopeProvider.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 com.intellij.compiler.impl;
+
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.CompilerFilter;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+import static org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage.TargetTypeBuildScope;
+
+/**
+ * @author nik
+ */
+public abstract class BuildTargetScopeProvider {
+  public static final ExtensionPointName<BuildTargetScopeProvider> EP_NAME = ExtensionPointName.create("com.intellij.compiler.buildTargetScopeProvider");
+
+  @NotNull
+  public abstract List<TargetTypeBuildScope> getBuildTargetScopes(@NotNull CompileScope baseScope, @NotNull CompilerFilter filter,
+                                                                  @NotNull Project project);
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileContextExProxy.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileContextExProxy.java
new file mode 100644
index 0000000..7341089
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileContextExProxy.java
@@ -0,0 +1,174 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import com.intellij.compiler.make.DependencyCache;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.CompilerMessage;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.compiler.ex.CompileContextEx;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.pom.Navigatable;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Dec 4, 2007
+ */
+public class CompileContextExProxy implements CompileContextEx {
+  private final CompileContextEx myDelegate;
+
+  public CompileContextExProxy(CompileContextEx delegate) {
+    myDelegate = delegate;
+  }
+
+  public Project getProject() {
+    return myDelegate.getProject();
+  }
+
+  public DependencyCache getDependencyCache() {
+    return myDelegate.getDependencyCache();
+  }
+
+  public VirtualFile getSourceFileByOutputFile(final VirtualFile outputFile) {
+    return myDelegate.getSourceFileByOutputFile(outputFile);
+  }
+
+  public void addMessage(final CompilerMessage message) {
+    myDelegate.addMessage(message);
+  }
+
+  @NotNull
+  public Set<VirtualFile> getTestOutputDirectories() {
+    return myDelegate.getTestOutputDirectories();
+  }
+
+  public boolean isInTestSourceContent(@NotNull final VirtualFile fileOrDir) {
+    return myDelegate.isInTestSourceContent(fileOrDir);
+  }
+
+  public boolean isInSourceContent(@NotNull final VirtualFile fileOrDir) {
+    return myDelegate.isInSourceContent(fileOrDir);
+  }
+
+  public void addScope(final CompileScope additionalScope) {
+    myDelegate.addScope(additionalScope);
+  }
+
+  public void addMessage(final CompilerMessageCategory category,
+                         final String message, @Nullable final String url, final int lineNum, final int columnNum) {
+    myDelegate.addMessage(category, message, url, lineNum, columnNum);
+  }
+
+  public void addMessage(final CompilerMessageCategory category, final String message, @Nullable final String url,
+                         final int lineNum,
+                         final int columnNum,
+                         final Navigatable navigatable) {
+    myDelegate.addMessage(category, message, url, lineNum, columnNum, navigatable);
+  }
+
+  public CompilerMessage[] getMessages(final CompilerMessageCategory category) {
+    return myDelegate.getMessages(category);
+  }
+
+  public int getMessageCount(final CompilerMessageCategory category) {
+    return myDelegate.getMessageCount(category);
+  }
+
+  public ProgressIndicator getProgressIndicator() {
+    return myDelegate.getProgressIndicator();
+  }
+
+  public CompileScope getCompileScope() {
+    return myDelegate.getCompileScope();
+  }
+
+  public CompileScope getProjectCompileScope() {
+    return myDelegate.getProjectCompileScope();
+  }
+
+  public void requestRebuildNextTime(final String message) {
+    myDelegate.requestRebuildNextTime(message);
+  }
+
+  public Module getModuleByFile(final VirtualFile file) {
+    return myDelegate.getModuleByFile(file);
+  }
+
+  public VirtualFile[] getSourceRoots(final Module module) {
+    return myDelegate.getSourceRoots(module);
+  }
+
+  public VirtualFile[] getAllOutputDirectories() {
+    return myDelegate.getAllOutputDirectories();
+  }
+
+  public VirtualFile getModuleOutputDirectory(final Module module) {
+    return myDelegate.getModuleOutputDirectory(module);
+  }
+
+  public VirtualFile getModuleOutputDirectoryForTests(final Module module) {
+    return myDelegate.getModuleOutputDirectoryForTests(module);
+  }
+
+  public boolean isMake() {
+    return myDelegate.isMake();
+  }
+
+  public boolean isAnnotationProcessorsEnabled() {
+    return myDelegate.isAnnotationProcessorsEnabled();
+  }
+
+  public boolean isRebuild() {
+    return myDelegate.isRebuild();
+  }
+
+  public <T> T getUserData(@NotNull final Key<T> key) {
+    return myDelegate.getUserData(key);
+  }
+
+  public <T> void putUserData(@NotNull final Key<T> key, final T value) {
+    myDelegate.putUserData(key, value);
+  }
+
+  public void recalculateOutputDirs() {
+    myDelegate.recalculateOutputDirs();
+  }
+
+  public void markGenerated(Collection<VirtualFile> files) {
+    myDelegate.markGenerated(files);
+  }
+
+  public boolean isGenerated(VirtualFile file) {
+    return myDelegate.isGenerated(file);
+  }
+
+  public long getStartCompilationStamp() {
+    return myDelegate.getStartCompilationStamp();
+  }
+
+  public void assignModule(@NotNull VirtualFile root, @NotNull Module module, boolean isTestSource, com.intellij.openapi.compiler.Compiler compiler) {
+    myDelegate.assignModule(root, module, isTestSource, compiler);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileContextImpl.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileContextImpl.java
new file mode 100644
index 0000000..298ce1a
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileContextImpl.java
@@ -0,0 +1,480 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: Jan 21, 2003
+ * Time: 4:19:03 PM
+ */
+package com.intellij.compiler.impl;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.CompilerMessageImpl;
+import com.intellij.compiler.CompilerWorkspaceConfiguration;
+import com.intellij.compiler.make.DependencyCache;
+import com.intellij.compiler.progress.CompilerTask;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.*;
+import com.intellij.openapi.compiler.Compiler;
+import com.intellij.openapi.compiler.ex.CompileContextEx;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.CompilerModuleExtension;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.UserDataHolderBase;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.pom.Navigatable;
+import com.intellij.util.containers.HashMap;
+import com.intellij.util.containers.HashSet;
+import com.intellij.util.containers.OrderedSet;
+import com.intellij.util.indexing.FileBasedIndex;
+import gnu.trove.TIntHashSet;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.*;
+
+public class CompileContextImpl extends UserDataHolderBase implements CompileContextEx {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.CompileContextImpl");
+  private final Project myProject;
+  private final CompilerTask myTask;
+  private final Map<CompilerMessageCategory, Collection<CompilerMessage>> myMessages = new EnumMap<CompilerMessageCategory, Collection<CompilerMessage>>(CompilerMessageCategory.class);
+  private final boolean myShouldUpdateProblemsView;
+  private CompileScope myCompileScope;
+  private final DependencyCache myDependencyCache;
+  private final boolean myMake;
+  private final boolean myIsRebuild;
+  private final boolean myIsAnnotationProcessorsEnabled;
+  private boolean myRebuildRequested = false;
+  private String myRebuildReason;
+  private final Map<VirtualFile, Module> myRootToModuleMap = new HashMap<VirtualFile, Module>();
+  private final Map<Module, Set<VirtualFile>> myModuleToRootsMap = new HashMap<Module, Set<VirtualFile>>();
+  private final Map<VirtualFile, Pair<SourceGeneratingCompiler, Module>> myOutputRootToSourceGeneratorMap = new HashMap<VirtualFile, Pair<SourceGeneratingCompiler, Module>>();
+  private final Set<VirtualFile> myGeneratedTestRoots = new java.util.HashSet<VirtualFile>();
+  private VirtualFile[] myOutputDirectories;
+  private Set<VirtualFile> myTestOutputDirectories;
+  private final TIntHashSet myGeneratedSources = new TIntHashSet();
+  private final ProjectFileIndex myProjectFileIndex; // cached for performance reasons
+  private final ProjectCompileScope myProjectCompileScope;
+  private final long myStartCompilationStamp;
+  private final UUID mySessionId = UUID.randomUUID();
+
+  public CompileContextImpl(final Project project,
+                            final CompilerTask compilerSession,
+                            CompileScope compileScope,
+                            DependencyCache dependencyCache, boolean isMake, boolean isRebuild) {
+    myProject = project;
+    myTask = compilerSession;
+    myCompileScope = compileScope;
+    myDependencyCache = dependencyCache;
+    myMake = isMake;
+    myIsRebuild = isRebuild;
+    myStartCompilationStamp = System.currentTimeMillis();
+    myProjectFileIndex = ProjectRootManager.getInstance(myProject).getFileIndex();
+    myProjectCompileScope = new ProjectCompileScope(myProject);
+    myIsAnnotationProcessorsEnabled = CompilerConfiguration.getInstance(project).isAnnotationProcessorsEnabled();
+
+    if (compilerSession != null) {
+      compilerSession.setContentIdKey(compileScope.getUserData(CompilerManager.CONTENT_ID_KEY));
+    }
+    recalculateOutputDirs();
+    final CompilerWorkspaceConfiguration workspaceConfig = CompilerWorkspaceConfiguration.getInstance(myProject);
+    myShouldUpdateProblemsView = workspaceConfig.useOutOfProcessBuild() && workspaceConfig.MAKE_PROJECT_ON_SAVE;
+  }
+
+  public boolean shouldUpdateProblemsView() {
+    return myShouldUpdateProblemsView;
+  }
+
+  public void recalculateOutputDirs() {
+    final Module[] allModules = ModuleManager.getInstance(myProject).getModules();
+
+    final Set<VirtualFile> allDirs = new OrderedSet<VirtualFile>();
+    final Set<VirtualFile> testOutputDirs = new java.util.HashSet<VirtualFile>();
+    final Set<VirtualFile> productionOutputDirs = new java.util.HashSet<VirtualFile>();
+
+    for (Module module : allModules) {
+      final CompilerModuleExtension manager = CompilerModuleExtension.getInstance(module);
+      final VirtualFile output = manager.getCompilerOutputPath();
+      if (output != null && output.isValid()) {
+        allDirs.add(output);
+        productionOutputDirs.add(output);
+      }
+      final VirtualFile testsOutput = manager.getCompilerOutputPathForTests();
+      if (testsOutput != null && testsOutput.isValid()) {
+        allDirs.add(testsOutput);
+        testOutputDirs.add(testsOutput);
+      }
+    }
+    myOutputDirectories = VfsUtil.toVirtualFileArray(allDirs);
+    // need this to ensure that the sent contains only _dedicated_ test output dirs
+    // Directories that are configured for both test and production classes must not be added in the resulting set
+    testOutputDirs.removeAll(productionOutputDirs);
+    myTestOutputDirectories = Collections.unmodifiableSet(testOutputDirs);
+  }
+
+  public void markGenerated(Collection<VirtualFile> files) {
+    for (final VirtualFile file : files) {
+      myGeneratedSources.add(FileBasedIndex.getFileId(file));
+    }
+  }
+
+  public long getStartCompilationStamp() {
+    return myStartCompilationStamp;
+  }
+
+  public boolean isGenerated(VirtualFile file) {
+    if (myGeneratedSources.contains(FileBasedIndex.getFileId(file))) {
+      return true;
+    }
+    if (isUnderRoots(myRootToModuleMap.keySet(), file)) {
+      return true;
+    }
+    final Module module = getModuleByFile(file);
+    if (module != null) {
+      final String procGenRoot = CompilerPaths.getAnnotationProcessorsGenerationPath(module);
+      if (procGenRoot != null && VfsUtil.isAncestor(new File(procGenRoot), new File(file.getPath()), true)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /*
+  private JBZipFile lookupZip(String outputDir) {
+    synchronized (myOpenZipFiles) {
+      JBZipFile zip = myOpenZipFiles.get(outputDir);
+      if (zip == null) {
+        final File zipFile = CompilerPathsEx.getZippedOutputPath(myProject, outputDir);
+        try {
+          try {
+            zip = new JBZipFile(zipFile);
+          }
+          catch (FileNotFoundException e) {
+            try {
+              zipFile.createNewFile();
+              zip = new JBZipFile(zipFile);
+            }
+            catch (IOException e1) {
+              zipFile.getParentFile().mkdirs();
+              zipFile.createNewFile();
+              zip = new JBZipFile(zipFile);
+            }
+          }
+          myOpenZipFiles.put(outputDir, zip);
+        }
+        catch (IOException e) {
+          LOG.info(e);
+          addMessage(CompilerMessageCategory.ERROR, "Cannot create zip file " + zipFile.getPath() + ": " + e.getMessage(), null, -1, -1);
+        }
+      }
+      return zip;
+    }
+  }
+  */
+
+  public Project getProject() {
+    return myProject;
+  }
+
+  public DependencyCache getDependencyCache() {
+    return myDependencyCache;
+  }
+
+  public CompilerMessage[] getMessages(CompilerMessageCategory category) {
+    Collection<CompilerMessage> collection = myMessages.get(category);
+    if (collection == null) {
+      return CompilerMessage.EMPTY_ARRAY;
+    }
+    return collection.toArray(new CompilerMessage[collection.size()]);
+  }
+
+  public void addMessage(CompilerMessageCategory category, String message, String url, int lineNum, int columnNum) {
+    CompilerMessageImpl msg = new CompilerMessageImpl(myProject, category, message, findPresentableFileForMessage(url), lineNum, columnNum, null);
+    addMessage(msg);
+  }
+
+  public void addMessage(CompilerMessageCategory category, String message, String url, int lineNum, int columnNum,
+                         Navigatable navigatable) {
+    CompilerMessageImpl msg = new CompilerMessageImpl(myProject, category, message, findPresentableFileForMessage(url), lineNum, columnNum, navigatable);
+    addMessage(msg);
+  }
+
+  @Nullable
+  private VirtualFile findPresentableFileForMessage(@Nullable final String url) {
+    final VirtualFile file = findFileByUrl(url);
+    if (file == null) {
+      return null;
+    }
+    return ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile>() {
+      @Override
+      public VirtualFile compute() {
+        if (file.isValid()) {
+          for (final Map.Entry<VirtualFile, Pair<SourceGeneratingCompiler, Module>> entry : myOutputRootToSourceGeneratorMap.entrySet()) {
+            final VirtualFile root = entry.getKey();
+            if (VfsUtilCore.isAncestor(root, file, false)) {
+              final Pair<SourceGeneratingCompiler, Module> pair = entry.getValue();
+              final VirtualFile presentableFile = pair.getFirst().getPresentableFile(CompileContextImpl.this, pair.getSecond(), root, file);
+              return presentableFile != null ? presentableFile : file;
+            }
+          }
+        }
+        return file;
+      }
+    });
+  }
+
+  @Nullable 
+  private static VirtualFile findFileByUrl(@Nullable String url) {
+    if (url == null) {
+      return null;
+    }
+    VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(url);
+    if (file == null) {
+      // groovy stubs may be placed in completely random directories which aren't refreshed automatically 
+      return VirtualFileManager.getInstance().refreshAndFindFileByUrl(url);
+    }
+    return file;
+  }
+
+  public void addMessage(CompilerMessage msg) {
+    if (ApplicationManager.getApplication().isUnitTestMode()) {
+      LOG.info("addMessage: " + msg + " this=" + this);
+    }
+
+    Collection<CompilerMessage> messages = myMessages.get(msg.getCategory());
+    if (messages == null) {
+      messages = new LinkedHashSet<CompilerMessage>();
+      myMessages.put(msg.getCategory(), messages);
+    }
+    if (messages.add(msg)) {
+      myTask.addMessage(msg);
+    }
+    if (myShouldUpdateProblemsView && msg.getCategory() == CompilerMessageCategory.ERROR) {
+      ProblemsViewImpl.SERVICE.getInstance(myProject).addMessage(msg, mySessionId);
+    }
+  }
+
+  public int getMessageCount(CompilerMessageCategory category) {
+    if (category != null) {
+      Collection<CompilerMessage> collection = myMessages.get(category);
+      return collection != null ? collection.size() : 0;
+    }
+    int count = 0;
+    for (Collection<CompilerMessage> collection : myMessages.values()) {
+      if (collection != null) {
+        count += collection.size();
+      }
+    }
+    return count;
+  }
+
+  public CompileScope getCompileScope() {
+    return myCompileScope;
+  }
+
+  public CompileScope getProjectCompileScope() {
+    return myProjectCompileScope;
+  }
+
+  public void requestRebuildNextTime(String message) {
+    if (!myRebuildRequested) {
+      myRebuildRequested = true;
+      myRebuildReason = message;
+      addMessage(CompilerMessageCategory.ERROR, message, null, -1, -1);
+    }
+  }
+
+  public boolean isRebuildRequested() {
+    return myRebuildRequested;
+  }
+
+  public String getRebuildReason() {
+    return myRebuildReason;
+  }
+
+  public ProgressIndicator getProgressIndicator() {
+    //if (myProgressIndicatorProxy != null) {
+    //  return myProgressIndicatorProxy;
+    //}
+    return myTask.getIndicator();
+  }
+
+  public void assignModule(@NotNull VirtualFile root, @NotNull Module module, final boolean isTestSource, @Nullable Compiler compiler) {
+    try {
+      myRootToModuleMap.put(root, module);
+      Set<VirtualFile> set = myModuleToRootsMap.get(module);
+      if (set == null) {
+        set = new HashSet<VirtualFile>();
+        myModuleToRootsMap.put(module, set);
+      }
+      set.add(root);
+      if (isTestSource) {
+        myGeneratedTestRoots.add(root);
+      }
+      if (compiler instanceof SourceGeneratingCompiler) {
+        myOutputRootToSourceGeneratorMap.put(root, new Pair<SourceGeneratingCompiler, Module>((SourceGeneratingCompiler)compiler, module));
+      }
+    }
+    finally {
+      myModuleToRootsCache.remove(module);
+    }
+  }
+
+  @Nullable
+  public VirtualFile getSourceFileByOutputFile(VirtualFile outputFile) {
+    return TranslatingCompilerFilesMonitor.getSourceFileByOutput(outputFile);
+  }
+
+  public Module getModuleByFile(VirtualFile file) {
+    final Module module = myProjectFileIndex.getModuleForFile(file);
+    if (module != null) {
+      LOG.assertTrue(!module.isDisposed());
+      return module;
+    }
+    for (final VirtualFile root : myRootToModuleMap.keySet()) {
+      if (VfsUtil.isAncestor(root, file, false)) {
+        final Module mod = myRootToModuleMap.get(root);
+        if (mod != null) {
+          LOG.assertTrue(!mod.isDisposed());
+        }
+        return mod;
+      }
+    }
+    return null;
+  }
+
+
+  private final Map<Module, VirtualFile[]> myModuleToRootsCache = new HashMap<Module, VirtualFile[]>();
+
+  public VirtualFile[] getSourceRoots(Module module) {
+    VirtualFile[] cachedRoots = myModuleToRootsCache.get(module);
+    if (cachedRoots != null) {
+      if (areFilesValid(cachedRoots)) {
+        return cachedRoots;
+      }
+      else {
+        myModuleToRootsCache.remove(module); // clear cache for this module and rebuild list of roots
+      }
+    }
+
+    Set<VirtualFile> additionalRoots = myModuleToRootsMap.get(module);
+    VirtualFile[] moduleRoots = ModuleRootManager.getInstance(module).getSourceRoots();
+    if (additionalRoots == null || additionalRoots.isEmpty()) {
+      myModuleToRootsCache.put(module, moduleRoots);
+      return moduleRoots;
+    }
+
+    final VirtualFile[] allRoots = new VirtualFile[additionalRoots.size() + moduleRoots.length];
+    System.arraycopy(moduleRoots, 0, allRoots, 0, moduleRoots.length);
+    int index = moduleRoots.length;
+    for (final VirtualFile additionalRoot : additionalRoots) {
+      allRoots[index++] = additionalRoot;
+    }
+    myModuleToRootsCache.put(module, allRoots);
+    return allRoots;
+  }
+
+  private static boolean areFilesValid(VirtualFile[] files) {
+    for (VirtualFile file : files) {
+      if (!file.isValid()) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  public VirtualFile[] getAllOutputDirectories() {
+    return myOutputDirectories;
+  }
+
+  @NotNull
+  public Set<VirtualFile> getTestOutputDirectories() {
+    return myTestOutputDirectories;
+  }
+
+  public VirtualFile getModuleOutputDirectory(Module module) {
+    return CompilerPaths.getModuleOutputDirectory(module, false);
+  }
+
+  public VirtualFile getModuleOutputDirectoryForTests(Module module) {
+    return CompilerPaths.getModuleOutputDirectory(module, true);
+  }
+
+  public boolean isMake() {
+    return myMake;
+  }
+
+  public boolean isRebuild() {
+    return myIsRebuild;
+  }
+
+  public boolean isAnnotationProcessorsEnabled() {
+    return myIsAnnotationProcessorsEnabled;
+  }
+
+  public void addScope(final CompileScope additionalScope) {
+    myCompileScope = new CompositeScope(myCompileScope, additionalScope);
+  }
+
+  public boolean isInTestSourceContent(@NotNull final VirtualFile fileOrDir) {
+    if (myProjectFileIndex.isInTestSourceContent(fileOrDir)) {
+      return true;
+    }
+    if (isUnderRoots(myGeneratedTestRoots, fileOrDir)) {
+      return true;
+    }
+    return false;
+  }
+
+  public boolean isInSourceContent(@NotNull final VirtualFile fileOrDir) {
+    if (myProjectFileIndex.isInSourceContent(fileOrDir)) {
+      return true;
+    }
+    if (isUnderRoots(myRootToModuleMap.keySet(), fileOrDir)) {
+      return true;
+    }
+    return false;
+  }
+
+  public static boolean isUnderRoots(@NotNull Set<VirtualFile> roots, @NotNull VirtualFile file) {
+    VirtualFile parent = file;
+    while (true) {
+      if (parent == null) {
+        return false;
+      }
+      if (roots.contains(parent)) {
+        return true;
+      }
+      parent = parent.getParent();
+    }
+  }
+
+  public UUID getSessionId() {
+    return mySessionId;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java
new file mode 100644
index 0000000..568bb42
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java
@@ -0,0 +1,2851 @@
+/*
+ * 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.
+ */
+
+/**
+ * @author: Eugene Zhuravlev
+ * Date: Jan 17, 2003
+ * Time: 1:42:26 PM
+ */
+package com.intellij.compiler.impl;
+
+import com.intellij.CommonBundle;
+import com.intellij.compiler.*;
+import com.intellij.compiler.make.CacheCorruptedException;
+import com.intellij.compiler.make.CacheUtils;
+import com.intellij.compiler.make.ChangedConstantsDependencyProcessor;
+import com.intellij.compiler.make.DependencyCache;
+import com.intellij.compiler.progress.CompilerTask;
+import com.intellij.compiler.server.BuildManager;
+import com.intellij.compiler.server.CustomBuilderMessageHandler;
+import com.intellij.compiler.server.DefaultMessageHandler;
+import com.intellij.diagnostic.IdeErrorsDialog;
+import com.intellij.diagnostic.PluginException;
+import com.intellij.openapi.application.*;
+import com.intellij.openapi.compiler.*;
+import com.intellij.openapi.compiler.Compiler;
+import com.intellij.openapi.compiler.ex.CompileContextEx;
+import com.intellij.openapi.compiler.ex.CompilerPathsEx;
+import com.intellij.openapi.compiler.generic.GenericCompiler;
+import com.intellij.openapi.deployment.DeploymentUtil;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.extensions.PluginId;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.module.LanguageLevelUtil;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.project.DumbService;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
+import com.intellij.openapi.roots.ui.configuration.CommonContentEntriesEditor;
+import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService;
+import com.intellij.openapi.ui.MessageType;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.*;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.vfs.*;
+import com.intellij.openapi.vfs.newvfs.ManagingFS;
+import com.intellij.openapi.vfs.newvfs.RefreshQueue;
+import com.intellij.openapi.wm.StatusBar;
+import com.intellij.openapi.wm.ToolWindowId;
+import com.intellij.openapi.wm.ToolWindowManager;
+import com.intellij.openapi.wm.WindowManager;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactManager;
+import com.intellij.packaging.impl.artifacts.ArtifactImpl;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.impl.compiler.ArtifactCompileScope;
+import com.intellij.packaging.impl.compiler.ArtifactCompilerUtil;
+import com.intellij.packaging.impl.compiler.ArtifactsCompiler;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.util.Chunk;
+import com.intellij.util.Function;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.util.ThrowableRunnable;
+import com.intellij.util.concurrency.Semaphore;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.HashMap;
+import com.intellij.util.containers.MultiMap;
+import com.intellij.util.containers.OrderedSet;
+import com.intellij.util.messages.MessageBus;
+import gnu.trove.THashSet;
+import gnu.trove.TIntHashSet;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.TestOnly;
+import org.jetbrains.jps.api.CmdlineProtoUtil;
+import org.jetbrains.jps.api.CmdlineRemoteProto;
+import org.jetbrains.jps.api.RequestFuture;
+import org.jetbrains.jps.incremental.Utils;
+
+import javax.swing.*;
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+import static org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage.TargetTypeBuildScope;
+
+public class CompileDriver {
+
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.CompileDriver");
+  // to be used in tests only for debug output
+  public static volatile boolean ourDebugMode = false;
+
+  private final Project myProject;
+  private final Map<Pair<IntermediateOutputCompiler, Module>, Pair<VirtualFile, VirtualFile>> myGenerationCompilerModuleToOutputDirMap; // [IntermediateOutputCompiler, Module] -> [ProductionSources, TestSources]
+  private final String myCachesDirectoryPath;
+  private boolean myShouldClearOutputDirectory;
+
+  private final Map<Module, String> myModuleOutputPaths = new HashMap<Module, String>();
+  private final Map<Module, String> myModuleTestOutputPaths = new HashMap<Module, String>();
+
+  @NonNls private static final String VERSION_FILE_NAME = "version.dat";
+  @NonNls private static final String LOCK_FILE_NAME = "in_progress.dat";
+
+  private static final boolean GENERATE_CLASSPATH_INDEX = "true".equals(System.getProperty("generate.classpath.index"));
+  private static final String PROP_PERFORM_INITIAL_REFRESH = "compiler.perform.outputs.refresh.on.start";
+  private static final Key<Boolean> REFRESH_DONE_KEY = Key.create("_compiler.initial.refresh.done_");
+
+  private static final FileProcessingCompilerAdapterFactory FILE_PROCESSING_COMPILER_ADAPTER_FACTORY = new FileProcessingCompilerAdapterFactory() {
+    public FileProcessingCompilerAdapter create(CompileContext context, FileProcessingCompiler compiler) {
+      return new FileProcessingCompilerAdapter(context, compiler);
+    }
+  };
+  private static final FileProcessingCompilerAdapterFactory FILE_PACKAGING_COMPILER_ADAPTER_FACTORY = new FileProcessingCompilerAdapterFactory() {
+    public FileProcessingCompilerAdapter create(CompileContext context, FileProcessingCompiler compiler) {
+      return new PackagingCompilerAdapter(context, (PackagingCompiler)compiler);
+    }
+  };
+  private CompilerFilter myCompilerFilter = CompilerFilter.ALL;
+  private static final CompilerFilter SOURCE_PROCESSING_ONLY = new CompilerFilter() {
+    public boolean acceptCompiler(Compiler compiler) {
+      return compiler instanceof SourceProcessingCompiler;
+    }
+  };
+  private static final CompilerFilter ALL_EXCEPT_SOURCE_PROCESSING = new CompilerFilter() {
+    public boolean acceptCompiler(Compiler compiler) {
+      return !SOURCE_PROCESSING_ONLY.acceptCompiler(compiler);
+    }
+  };
+
+  private Set<File> myAllOutputDirectories;
+  private static final long ONE_MINUTE_MS = 60L /*sec*/ * 1000L /*millisec*/;
+
+  public CompileDriver(Project project) {
+    myProject = project;
+    myCachesDirectoryPath = CompilerPaths.getCacheStoreDirectory(myProject).getPath().replace('/', File.separatorChar);
+    myShouldClearOutputDirectory = CompilerWorkspaceConfiguration.getInstance(myProject).CLEAR_OUTPUT_DIRECTORY;
+
+    myGenerationCompilerModuleToOutputDirMap = new HashMap<Pair<IntermediateOutputCompiler, Module>, Pair<VirtualFile, VirtualFile>>();
+
+    if (!useOutOfProcessBuild()) {
+      final LocalFileSystem lfs = LocalFileSystem.getInstance();
+      final IntermediateOutputCompiler[] generatingCompilers = CompilerManager.getInstance(myProject).getCompilers(IntermediateOutputCompiler.class, myCompilerFilter);
+      final Module[] allModules = ModuleManager.getInstance(myProject).getModules();
+      final CompilerConfiguration config = CompilerConfiguration.getInstance(project);
+      for (Module module : allModules) {
+        for (IntermediateOutputCompiler compiler : generatingCompilers) {
+          final VirtualFile productionOutput = lookupVFile(lfs, CompilerPaths.getGenerationOutputPath(compiler, module, false));
+          final VirtualFile testOutput = lookupVFile(lfs, CompilerPaths.getGenerationOutputPath(compiler, module, true));
+          final Pair<IntermediateOutputCompiler, Module> pair = new Pair<IntermediateOutputCompiler, Module>(compiler, module);
+          final Pair<VirtualFile, VirtualFile> outputs = new Pair<VirtualFile, VirtualFile>(productionOutput, testOutput);
+          myGenerationCompilerModuleToOutputDirMap.put(pair, outputs);
+        }
+        if (config.getAnnotationProcessingConfiguration(module).isEnabled()) {
+          final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module);
+          if (path != null) {
+            lookupVFile(lfs, path);  // ensure the file is created and added to VFS
+          }
+        }
+      }
+    }
+  }
+
+  public void setCompilerFilter(CompilerFilter compilerFilter) {
+    myCompilerFilter = compilerFilter == null? CompilerFilter.ALL : compilerFilter;
+  }
+
+  public void rebuild(CompileStatusNotification callback) {
+    final CompileScope compileScope;
+    ProjectCompileScope projectScope = new ProjectCompileScope(myProject);
+    if (useOutOfProcessBuild()) {
+      compileScope = projectScope;
+    }
+    else {
+      CompileScope scopeWithArtifacts = ArtifactCompileScope.createScopeWithArtifacts(projectScope, ArtifactUtil.getArtifactWithOutputPaths(myProject), false);
+      compileScope = addAdditionalRoots(scopeWithArtifacts, ALL_EXCEPT_SOURCE_PROCESSING);
+    }
+    doRebuild(callback, null, true, compileScope);
+  }
+
+  public void make(CompileScope scope, CompileStatusNotification callback) {
+    if (!useOutOfProcessBuild()) {
+      scope = addAdditionalRoots(scope, ALL_EXCEPT_SOURCE_PROCESSING);
+    }
+    if (validateCompilerConfiguration(scope, false)) {
+      startup(scope, false, false, callback, null, true);
+    }
+    else {
+      callback.finished(true, 0, 0, DummyCompileContext.getInstance());
+    }
+  }
+
+  public boolean isUpToDate(CompileScope scope) {
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("isUpToDate operation started");
+    }
+    if (!useOutOfProcessBuild()) {
+      scope = addAdditionalRoots(scope, ALL_EXCEPT_SOURCE_PROCESSING);
+    }
+
+    final CompilerTask task = new CompilerTask(myProject, "Classes up-to-date check", true, false);
+    final DependencyCache cache = useOutOfProcessBuild()? null : createDependencyCache();
+    final CompileContextImpl compileContext = new CompileContextImpl(myProject, task, scope, cache, true, false);
+
+    if (!useOutOfProcessBuild()) {
+      checkCachesVersion(compileContext, ManagingFS.getInstance().getCreationTimestamp());
+      if (compileContext.isRebuildRequested()) {
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Rebuild requested, up-to-date=false");
+        }
+        return false;
+      }
+
+      for (Map.Entry<Pair<IntermediateOutputCompiler, Module>, Pair<VirtualFile, VirtualFile>> entry : myGenerationCompilerModuleToOutputDirMap.entrySet()) {
+        final Pair<VirtualFile, VirtualFile> outputs = entry.getValue();
+        final Pair<IntermediateOutputCompiler, Module> key = entry.getKey();
+        final Module module = key.getSecond();
+        compileContext.assignModule(outputs.getFirst(), module, false, key.getFirst());
+        compileContext.assignModule(outputs.getSecond(), module, true, key.getFirst());
+      }
+    }
+
+    final Ref<ExitStatus> result = new Ref<ExitStatus>();
+
+    final Runnable compileWork;
+    if (useOutOfProcessBuild()) {
+      compileWork = new Runnable() {
+        public void run() {
+          final ProgressIndicator indicator = compileContext.getProgressIndicator();
+          if (indicator.isCanceled() || myProject.isDisposed()) {
+            return;
+          }
+          try {
+            final Collection<String> paths = CompileScopeUtil.fetchFiles(compileContext);
+            List<TargetTypeBuildScope> scopes = new ArrayList<TargetTypeBuildScope>();
+            if (paths.isEmpty()) {
+              if (!compileContext.isRebuild() && !CompileScopeUtil.allProjectModulesAffected(compileContext)) {
+                CompileScopeUtil.addScopesForModules(Arrays.asList(compileContext.getCompileScope().getAffectedModules()), scopes);
+              }
+              else {
+                scopes.addAll(CmdlineProtoUtil.createAllModulesScopes());
+              }
+              for (BuildTargetScopeProvider provider : BuildTargetScopeProvider.EP_NAME.getExtensions()) {
+                scopes = CompileScopeUtil.mergeScopes(scopes, provider.getBuildTargetScopes(compileContext.getCompileScope(), myCompilerFilter, myProject));
+              }
+            }
+            final RequestFuture future = compileInExternalProcess(compileContext, scopes, paths, true);
+            if (future != null) {
+              while (!future.waitFor(200L , TimeUnit.MILLISECONDS)) {
+                if (indicator.isCanceled()) {
+                  future.cancel(false);
+                }
+              }
+            }
+          }
+          catch (Throwable e) {
+            LOG.error(e);
+          }
+          finally {
+            result.set(COMPILE_SERVER_BUILD_STATUS.get(compileContext));
+            CompilerCacheManager.getInstance(myProject).flushCaches();
+          }
+        }
+      };
+    }
+    else {
+      compileWork = new Runnable() {
+        public void run() {
+          try {
+            myAllOutputDirectories = getAllOutputDirectories(compileContext);
+            // need this for updating zip archives experiment, uncomment if the feature is turned on
+            //myOutputFinder = new OutputPathFinder(myAllOutputDirectories);
+            result.set(doCompile(compileContext, false, false, true));
+          }
+          finally {
+            CompilerCacheManager.getInstance(myProject).flushCaches();
+          }
+        }
+      };
+    }
+    task.start(compileWork, null);
+
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("isUpToDate operation finished");
+    }
+
+    return ExitStatus.UP_TO_DATE.equals(result.get());
+  }
+
+  private DependencyCache createDependencyCache() {
+    return new DependencyCache(myCachesDirectoryPath + File.separator + ".dependency-info");
+  }
+
+  public void compile(CompileScope scope, CompileStatusNotification callback, boolean clearingOutputDirsPossible) {
+    myShouldClearOutputDirectory &= clearingOutputDirsPossible;
+    if (containsFileIndexScopes(scope)) {
+      scope = addAdditionalRoots(scope, ALL_EXCEPT_SOURCE_PROCESSING);
+    }
+    if (validateCompilerConfiguration(scope, false)) {
+      startup(scope, false, true, callback, null, true);
+    }
+    else {
+      callback.finished(true, 0, 0, DummyCompileContext.getInstance());
+    }
+  }
+
+  private static boolean containsFileIndexScopes(CompileScope scope) {
+    if (scope instanceof CompositeScope) {
+      for (CompileScope childScope : ((CompositeScope)scope).getScopes()) {
+        if (containsFileIndexScopes(childScope)) {
+          return true;
+        }
+      }
+    }
+    return scope instanceof FileIndexCompileScope;
+  }
+
+  private static class CompileStatus {
+    final int CACHE_FORMAT_VERSION;
+    final boolean COMPILATION_IN_PROGRESS;
+    final long VFS_CREATION_STAMP;
+
+    private CompileStatus(int cacheVersion, boolean isCompilationInProgress, long vfsStamp) {
+      CACHE_FORMAT_VERSION = cacheVersion;
+      COMPILATION_IN_PROGRESS = isCompilationInProgress;
+      VFS_CREATION_STAMP = vfsStamp;
+    }
+  }
+
+  private CompileStatus readStatus() {
+    final boolean isInProgress = getLockFile().exists();
+    int version = -1;
+    long vfsStamp = -1L;
+    try {
+      final File versionFile = new File(myCachesDirectoryPath, VERSION_FILE_NAME);
+      DataInputStream in = new DataInputStream(new FileInputStream(versionFile));
+      try {
+        version = in.readInt();
+        try {
+          vfsStamp = in.readLong();
+        }
+        catch (IOException ignored) {
+        }
+      }
+      finally {
+        in.close();
+      }
+    }
+    catch (FileNotFoundException e) {
+      // ignore
+    }
+    catch (IOException e) {
+      LOG.info(e);  // may happen in case of IDEA crashed and the file is not written properly
+      return null;
+    }
+    return new CompileStatus(version, isInProgress, vfsStamp);
+  }
+
+  private void writeStatus(CompileStatus status, CompileContext context) {
+    final File statusFile = new File(myCachesDirectoryPath, VERSION_FILE_NAME);
+
+    final File lockFile = getLockFile();
+    try {
+      FileUtil.createIfDoesntExist(statusFile);
+      DataOutputStream out = new DataOutputStream(new FileOutputStream(statusFile));
+      try {
+        out.writeInt(status.CACHE_FORMAT_VERSION);
+        out.writeLong(status.VFS_CREATION_STAMP);
+      }
+      finally {
+        out.close();
+      }
+      if (status.COMPILATION_IN_PROGRESS) {
+        FileUtil.createIfDoesntExist(lockFile);
+      }
+      else {
+        deleteFile(lockFile);
+      }
+    }
+    catch (IOException e) {
+      context.addMessage(CompilerMessageCategory.ERROR, CompilerBundle.message("compiler.error.exception", e.getMessage()), null, -1, -1);
+    }
+  }
+
+  private File getLockFile() {
+    return new File(CompilerPaths.getCompilerSystemDirectory(myProject), LOCK_FILE_NAME);
+  }
+
+  private void doRebuild(CompileStatusNotification callback,
+                         CompilerMessage message,
+                         final boolean checkCachesVersion,
+                         final CompileScope compileScope) {
+    if (validateCompilerConfiguration(compileScope, !useOutOfProcessBuild())) {
+      startup(compileScope, true, false, callback, message, checkCachesVersion);
+    }
+    else {
+      callback.finished(true, 0, 0, DummyCompileContext.getInstance());
+    }
+  }
+
+  private CompileScope addAdditionalRoots(CompileScope originalScope, final CompilerFilter filter) {
+    CompileScope scope = attachIntermediateOutputDirectories(originalScope, filter);
+
+    final AdditionalCompileScopeProvider[] scopeProviders = Extensions.getExtensions(AdditionalCompileScopeProvider.EXTENSION_POINT_NAME);
+    CompileScope baseScope = scope;
+    for (AdditionalCompileScopeProvider scopeProvider : scopeProviders) {
+      final CompileScope additionalScope = scopeProvider.getAdditionalScope(baseScope, filter, myProject);
+      if (additionalScope != null) {
+        scope = new CompositeScope(scope, additionalScope);
+      }
+    }
+    return scope;
+  }
+
+  private CompileScope attachIntermediateOutputDirectories(CompileScope originalScope, CompilerFilter filter) {
+    CompileScope scope = originalScope;
+    final Set<Module> affected = new HashSet<Module>(Arrays.asList(originalScope.getAffectedModules()));
+    for (Map.Entry<Pair<IntermediateOutputCompiler, Module>, Pair<VirtualFile, VirtualFile>> entry : myGenerationCompilerModuleToOutputDirMap.entrySet()) {
+      final Module module = entry.getKey().getSecond();
+      if (affected.contains(module) && filter.acceptCompiler(entry.getKey().getFirst())) {
+        final Pair<VirtualFile, VirtualFile> outputs = entry.getValue();
+        scope = new CompositeScope(scope, new FileSetCompileScope(Arrays.asList(outputs.getFirst(), outputs.getSecond()), new Module[]{module}));
+      }
+    }
+    return scope;
+  }
+
+  private void attachAnnotationProcessorsOutputDirectories(CompileContextEx context) {
+    final LocalFileSystem lfs = LocalFileSystem.getInstance();
+    final CompilerConfiguration config = CompilerConfiguration.getInstance(myProject);
+    final Set<Module> affected = new HashSet<Module>(Arrays.asList(context.getCompileScope().getAffectedModules()));
+    for (Module module : affected) {
+      if (!config.getAnnotationProcessingConfiguration(module).isEnabled()) {
+        continue;
+      }
+      final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module);
+      if (path == null) {
+        continue;
+      }
+      final VirtualFile vFile = lfs.findFileByPath(path);
+      if (vFile == null) {
+        continue;
+      }
+      if (ModuleRootManager.getInstance(module).getFileIndex().isInSourceContent(vFile)) {
+        // no need to add, is already marked as source
+        continue;
+      }
+      context.addScope(new FileSetCompileScope(Collections.singletonList(vFile), new Module[]{module}));
+      context.assignModule(vFile, module, false, null);
+    }
+  }
+
+  @Nullable
+  private RequestFuture compileInExternalProcess(final @NotNull CompileContextImpl compileContext,
+                                                 @NotNull List<TargetTypeBuildScope> scopes,
+                                                 final @NotNull Collection<String> paths,
+                                                 final boolean onlyCheckUpToDate)
+    throws Exception {
+    final CompileScope scope = compileContext.getCompileScope();
+    // need to pass scope's user data to server
+    final Map<String, String> builderParams;
+    if (onlyCheckUpToDate) {
+      builderParams = Collections.emptyMap();
+    }
+    else {
+      final Map<Key, Object> exported = scope.exportUserData();
+      if (!exported.isEmpty()) {
+        builderParams = new HashMap<String, String>();
+        for (Map.Entry<Key, Object> entry : exported.entrySet()) {
+          final String _key = entry.getKey().toString();
+          final String _value = entry.getValue().toString();
+          builderParams.put(_key, _value);
+        }
+      }
+      else {
+        builderParams = Collections.emptyMap();
+      }
+    }
+
+    final MessageBus messageBus = myProject.getMessageBus();
+    final MultiMap<String, Artifact> outputToArtifact = ArtifactCompilerUtil.containsArtifacts(scopes) ? ArtifactCompilerUtil.createOutputToArtifactMap(myProject) : null;
+    final BuildManager buildManager = BuildManager.getInstance();
+    buildManager.cancelAutoMakeTasks(myProject);
+    return buildManager.scheduleBuild(myProject, compileContext.isRebuild(), compileContext.isMake(), onlyCheckUpToDate, scopes, paths, builderParams, new DefaultMessageHandler(myProject) {
+
+      @Override
+      public void buildStarted(UUID sessionId) {
+      }
+
+      @Override
+      public void sessionTerminated(final UUID sessionId) {
+        if (compileContext.shouldUpdateProblemsView()) {
+          final ProblemsView view = ProblemsViewImpl.SERVICE.getInstance(myProject);
+          view.clearProgress();
+          view.clearOldMessages(compileContext.getCompileScope(), compileContext.getSessionId());
+        }
+      }
+
+      @Override
+      public void handleFailure(UUID sessionId, CmdlineRemoteProto.Message.Failure failure) {
+        compileContext.addMessage(CompilerMessageCategory.ERROR, failure.getDescription(), null, -1, -1);
+        final String trace = failure.getStacktrace();
+        if (trace != null) {
+          LOG.info(trace);
+          System.out.println(trace);
+        }
+        compileContext.putUserData(COMPILE_SERVER_BUILD_STATUS, ExitStatus.ERRORS);
+      }
+
+      @Override
+      protected void handleCompileMessage(UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.CompileMessage message) {
+        final CmdlineRemoteProto.Message.BuilderMessage.CompileMessage.Kind kind = message.getKind();
+        //System.out.println(compilerMessage.getText());
+        final String messageText = message.getText();
+        if (kind == CmdlineRemoteProto.Message.BuilderMessage.CompileMessage.Kind.PROGRESS) {
+          final ProgressIndicator indicator = compileContext.getProgressIndicator();
+          indicator.setText(messageText);
+          if (message.hasDone()) {
+            indicator.setFraction(message.getDone());
+          }
+        }
+        else {
+          final CompilerMessageCategory category = kind == CmdlineRemoteProto.Message.BuilderMessage.CompileMessage.Kind.ERROR ? CompilerMessageCategory.ERROR
+            : kind == CmdlineRemoteProto.Message.BuilderMessage.CompileMessage.Kind.WARNING ? CompilerMessageCategory.WARNING : CompilerMessageCategory.INFORMATION;
+
+          String sourceFilePath = message.hasSourceFilePath() ? message.getSourceFilePath() : null;
+          if (sourceFilePath != null) {
+            sourceFilePath = FileUtil.toSystemIndependentName(sourceFilePath);
+          }
+          final long line = message.hasLine() ? message.getLine() : -1;
+          final long column = message.hasColumn() ? message.getColumn() : -1;
+          final String srcUrl = sourceFilePath != null ? VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, sourceFilePath) : null;
+          compileContext.addMessage(category, messageText, srcUrl, (int)line, (int)column);
+        }
+      }
+
+      @Override
+      protected void handleBuildEvent(UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.BuildEvent event) {
+        final CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.Type eventType = event.getEventType();
+        switch (eventType) {
+          case FILES_GENERATED:
+            final List<CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.GeneratedFile> generated = event.getGeneratedFilesList();
+            final CompilationStatusListener publisher = messageBus.syncPublisher(CompilerTopics.COMPILATION_STATUS);
+            Set<String> writtenArtifactOutputPaths = outputToArtifact != null ? new THashSet<String>(FileUtil.PATH_HASHING_STRATEGY) : null;
+            for (CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.GeneratedFile generatedFile : generated) {
+              final String root = FileUtil.toSystemIndependentName(generatedFile.getOutputRoot());
+              final String relativePath = FileUtil.toSystemIndependentName(generatedFile.getRelativePath());
+              publisher.fileGenerated(root, relativePath);
+              if (outputToArtifact != null) {
+                Collection<Artifact> artifacts = outputToArtifact.get(root);
+                if (!artifacts.isEmpty()) {
+                  for (Artifact artifact : artifacts) {
+                    ArtifactsCompiler.addChangedArtifact(compileContext, artifact);
+                  }
+                  writtenArtifactOutputPaths.add(FileUtil.toSystemDependentName(DeploymentUtil.appendToPath(root, relativePath)));
+                }
+              }
+            }
+            if (writtenArtifactOutputPaths != null && !writtenArtifactOutputPaths.isEmpty()) {
+              ArtifactsCompiler.addWrittenPaths(compileContext, writtenArtifactOutputPaths);
+            }
+            break;
+          case BUILD_COMPLETED:
+            ExitStatus status = ExitStatus.SUCCESS;
+            if (event.hasCompletionStatus()) {
+              final CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.Status completionStatus = event.getCompletionStatus();
+              switch (completionStatus) {
+                case CANCELED:
+                  status = ExitStatus.CANCELLED;
+                  break;
+                case ERRORS:
+                  status = ExitStatus.ERRORS;
+                  break;
+                case SUCCESS:
+                  status = ExitStatus.SUCCESS;
+                  break;
+                case UP_TO_DATE:
+                  status = ExitStatus.UP_TO_DATE;
+                  break;
+              }
+            }
+            compileContext.putUserDataIfAbsent(COMPILE_SERVER_BUILD_STATUS, status);
+            break;
+          case CUSTOM_BUILDER_MESSAGE:
+            if (event.hasCustomBuilderMessage()) {
+              CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.CustomBuilderMessage message = event.getCustomBuilderMessage();
+              messageBus.syncPublisher(CustomBuilderMessageHandler.TOPIC).messageReceived(message.getBuilderId(), message.getMessageType(),
+                                                                                          message.getMessageText());
+            }
+            break;
+        }
+      }
+    });
+  }
+
+
+  private static final Key<ExitStatus> COMPILE_SERVER_BUILD_STATUS = Key.create("COMPILE_SERVER_BUILD_STATUS");
+
+  private void startup(final CompileScope scope,
+                       final boolean isRebuild,
+                       final boolean forceCompile,
+                       final CompileStatusNotification callback,
+                       final CompilerMessage message,
+                       final boolean checkCachesVersion) {
+    ApplicationManager.getApplication().assertIsDispatchThread();
+
+    final boolean useExtProcessBuild = useOutOfProcessBuild();
+
+    final String contentName =
+      forceCompile ? CompilerBundle.message("compiler.content.name.compile") : CompilerBundle.message("compiler.content.name.make");
+    final CompilerTask compileTask = new CompilerTask(myProject, contentName, ApplicationManager.getApplication().isUnitTestMode(), true);
+
+    StatusBar.Info.set("", myProject, "Compiler");
+    if (useExtProcessBuild) {
+      // ensure the project model seen by build process is up-to-date
+      myProject.save();
+    }
+    PsiDocumentManager.getInstance(myProject).commitAllDocuments();
+    FileDocumentManager.getInstance().saveAllDocuments();
+
+    final DependencyCache dependencyCache = useExtProcessBuild ? null: createDependencyCache();
+    final CompileContextImpl compileContext =
+      new CompileContextImpl(myProject, compileTask, scope, dependencyCache, !isRebuild && !forceCompile, isRebuild);
+
+    if (!useExtProcessBuild) {
+      for (Map.Entry<Pair<IntermediateOutputCompiler, Module>, Pair<VirtualFile, VirtualFile>> entry : myGenerationCompilerModuleToOutputDirMap.entrySet()) {
+        final Pair<VirtualFile, VirtualFile> outputs = entry.getValue();
+        final Pair<IntermediateOutputCompiler, Module> key = entry.getKey();
+        final Module module = key.getSecond();
+        compileContext.assignModule(outputs.getFirst(), module, false, key.getFirst());
+        compileContext.assignModule(outputs.getSecond(), module, true, key.getFirst());
+      }
+      attachAnnotationProcessorsOutputDirectories(compileContext);
+    }
+
+    final Runnable compileWork;
+    if (useExtProcessBuild) {
+      compileWork = new Runnable() {
+        public void run() {
+          final ProgressIndicator indicator = compileContext.getProgressIndicator();
+          if (indicator.isCanceled() || myProject.isDisposed()) {
+            if (callback != null) {
+              callback.finished(true, 0, 0, compileContext);
+            }
+            return;
+          }
+          try {
+            LOG.info("COMPILATION STARTED (BUILD PROCESS)");
+            if (message != null) {
+              compileContext.addMessage(message);
+            }
+            if (!executeCompileTasks(compileContext, true)) {
+              COMPILE_SERVER_BUILD_STATUS.set(compileContext, ExitStatus.CANCELLED);
+              return;
+            }
+
+            final Collection<String> paths = CompileScopeUtil.fetchFiles(compileContext);
+            List<TargetTypeBuildScope> scopes = new ArrayList<TargetTypeBuildScope>();
+            if (paths.isEmpty()) {
+              if (!isRebuild && !CompileScopeUtil.allProjectModulesAffected(compileContext)) {
+                CompileScopeUtil.addScopesForModules(Arrays.asList(compileContext.getCompileScope().getAffectedModules()), scopes);
+              }
+              else {
+                scopes.addAll(CmdlineProtoUtil.createAllModulesScopes());
+              }
+              for (BuildTargetScopeProvider provider : BuildTargetScopeProvider.EP_NAME.getExtensions()) {
+                scopes = CompileScopeUtil.mergeScopes(scopes, provider.getBuildTargetScopes(scope, myCompilerFilter, myProject));
+              }
+            }
+            final RequestFuture future = compileInExternalProcess(compileContext, scopes, paths, false);
+            if (future != null) {
+              while (!future.waitFor(200L , TimeUnit.MILLISECONDS)) {
+                if (indicator.isCanceled()) {
+                  future.cancel(false);
+                }
+              }
+              if (!executeCompileTasks(compileContext, false)) {
+                COMPILE_SERVER_BUILD_STATUS.set(compileContext, ExitStatus.CANCELLED);
+                return;
+              }
+            }
+          }
+          catch (Throwable e) {
+            LOG.error(e); // todo
+          }
+          finally {
+            CompilerCacheManager.getInstance(myProject).flushCaches();
+
+            final long duration = notifyCompilationCompleted(compileContext, callback, COMPILE_SERVER_BUILD_STATUS.get(compileContext));
+            CompilerUtil.logDuration(
+              "\tCOMPILATION FINISHED (BUILD PROCESS); Errors: " +
+              compileContext.getMessageCount(CompilerMessageCategory.ERROR) +
+              "; warnings: " +
+              compileContext.getMessageCount(CompilerMessageCategory.WARNING),
+              duration
+            );
+
+            // refresh on output roots is required in order for the order enumerator to see all roots via VFS
+            final Set<File> outputs = new HashSet<File>();
+            for (final String path : CompilerPathsEx.getOutputPaths(ModuleManager.getInstance(myProject).getModules())) {
+              outputs.add(new File(path));
+            }
+            if (!outputs.isEmpty()) {
+              LocalFileSystem.getInstance().refreshIoFiles(outputs, true, false, null);
+            }
+          }
+        }
+      };
+    }
+    else {
+      compileWork = new Runnable() {
+        public void run() {
+          if (compileContext.getProgressIndicator().isCanceled()) {
+            if (callback != null) {
+              callback.finished(true, 0, 0, compileContext);
+            }
+            return;
+          }
+          try {
+            if (myProject.isDisposed()) {
+              return;
+            }
+            LOG.info("COMPILATION STARTED");
+            if (message != null) {
+              compileContext.addMessage(message);
+            }
+            TranslatingCompilerFilesMonitor.getInstance().ensureInitializationCompleted(myProject, compileContext.getProgressIndicator());
+            doCompile(compileContext, isRebuild, forceCompile, callback, checkCachesVersion);
+          }
+          finally {
+            FileUtil.delete(CompilerPaths.getRebuildMarkerFile(myProject));
+          }
+        }
+      };
+    }
+
+    compileTask.start(compileWork, new Runnable() {
+      public void run() {
+        if (isRebuild) {
+          final int rv = Messages.showOkCancelDialog(
+              myProject, "You are about to rebuild the whole project.\nRun 'Make Project' instead?", "Confirm Project Rebuild",
+              "Make", "Rebuild", Messages.getQuestionIcon()
+          );
+          if (rv == 0 /*yes, please, do run make*/) {
+            startup(scope, false, false, callback, null, checkCachesVersion);
+            return;
+          }
+        }
+        startup(scope, isRebuild, forceCompile, callback, message, checkCachesVersion);
+      }
+    });
+  }
+
+  @Nullable @TestOnly
+  public static ExitStatus getExternalBuildExitStatus(CompileContext context) {
+    return context.getUserData(COMPILE_SERVER_BUILD_STATUS);
+  }
+
+  private void doCompile(final CompileContextImpl compileContext,
+                         final boolean isRebuild,
+                         final boolean forceCompile,
+                         final CompileStatusNotification callback,
+                         final boolean checkCachesVersion) {
+    ExitStatus status = ExitStatus.ERRORS;
+    boolean wereExceptions = false;
+    final long vfsTimestamp = (ManagingFS.getInstance()).getCreationTimestamp();
+    try {
+      if (checkCachesVersion) {
+        checkCachesVersion(compileContext, vfsTimestamp);
+        if (compileContext.isRebuildRequested()) {
+          return;
+        }
+      }
+      writeStatus(new CompileStatus(CompilerConfigurationImpl.DEPENDENCY_FORMAT_VERSION, true, vfsTimestamp), compileContext);
+      if (compileContext.getMessageCount(CompilerMessageCategory.ERROR) > 0) {
+        return;
+      }
+
+      myAllOutputDirectories = getAllOutputDirectories(compileContext);
+      status = doCompile(compileContext, isRebuild, forceCompile, false);
+    }
+    catch (Throwable ex) {
+      if (ApplicationManager.getApplication().isUnitTestMode()) {
+        throw new RuntimeException(ex);
+      }
+      wereExceptions = true;
+      final PluginId pluginId = IdeErrorsDialog.findPluginId(ex);
+
+      final StringBuilder message = new StringBuilder();
+      message.append("Internal error");
+      if (pluginId != null) {
+        message.append(" (Plugin: ").append(pluginId).append(")");
+      }
+      message.append(": ").append(ex.getMessage());
+      compileContext.addMessage(CompilerMessageCategory.ERROR, message.toString(), null, -1, -1);
+      
+      if (pluginId != null) {
+        throw new PluginException(ex, pluginId);
+      }
+      throw new RuntimeException(ex);
+    }
+    finally {
+      dropDependencyCache(compileContext);
+      CompilerCacheManager.getInstance(myProject).flushCaches();
+      if (compileContext.isRebuildRequested()) {
+        ApplicationManager.getApplication().invokeLater(new Runnable() {
+          public void run() {
+            final CompilerMessageImpl msg = new CompilerMessageImpl(myProject, CompilerMessageCategory.INFORMATION, compileContext.getRebuildReason());
+            doRebuild(callback, msg, false, compileContext.getCompileScope());
+          }
+        }, ModalityState.NON_MODAL);
+      }
+      else {
+        if (!myProject.isDisposed()) {
+          writeStatus(new CompileStatus(CompilerConfigurationImpl.DEPENDENCY_FORMAT_VERSION, wereExceptions, vfsTimestamp), compileContext);
+        }
+        final long duration = notifyCompilationCompleted(compileContext, callback, status);
+        CompilerUtil.logDuration(
+          "\tCOMPILATION FINISHED; Errors: " +
+          compileContext.getMessageCount(CompilerMessageCategory.ERROR) +
+          "; warnings: " +
+          compileContext.getMessageCount(CompilerMessageCategory.WARNING),
+          duration
+        );
+      }
+    }
+  }
+
+  /** @noinspection SSBasedInspection*/
+  private long notifyCompilationCompleted(final CompileContextImpl compileContext, final CompileStatusNotification callback, final ExitStatus _status) {
+    final long duration = System.currentTimeMillis() - compileContext.getStartCompilationStamp();
+    SwingUtilities.invokeLater(new Runnable() {
+      public void run() {
+        int errorCount = 0;
+        int warningCount = 0;
+        try {
+          errorCount = compileContext.getMessageCount(CompilerMessageCategory.ERROR);
+          warningCount = compileContext.getMessageCount(CompilerMessageCategory.WARNING);
+          if (!myProject.isDisposed()) {
+            final String statusMessage = createStatusMessage(_status, warningCount, errorCount, duration);
+            final MessageType messageType = errorCount > 0 ? MessageType.ERROR : warningCount > 0 ? MessageType.WARNING : MessageType.INFO;
+            if (duration > ONE_MINUTE_MS) {
+              ToolWindowManager.getInstance(myProject).notifyByBalloon(ToolWindowId.MESSAGES_WINDOW, messageType, statusMessage);
+            }
+            CompilerManager.NOTIFICATION_GROUP.createNotification(statusMessage, messageType).notify(myProject);
+            if (_status != ExitStatus.UP_TO_DATE && compileContext.getMessageCount(null) > 0) {
+              compileContext.addMessage(CompilerMessageCategory.INFORMATION, statusMessage, null, -1, -1);
+            }
+          }
+        }
+        finally {
+          if (callback != null) {
+            callback.finished(_status == ExitStatus.CANCELLED, errorCount, warningCount, compileContext);
+          }
+        }
+      }
+    });
+    return duration;
+  }
+
+  private void checkCachesVersion(final CompileContextImpl compileContext, final long currentVFSTimestamp) {
+    if (CompilerPaths.getRebuildMarkerFile(compileContext.getProject()).exists()) {
+      compileContext.requestRebuildNextTime("Compiler caches are out of date, project rebuild is required");
+      return;
+    }
+    final CompileStatus compileStatus = readStatus();
+    if (compileStatus == null) {
+      compileContext.requestRebuildNextTime(CompilerBundle.message("error.compiler.caches.corrupted"));
+    }
+    else if (compileStatus.CACHE_FORMAT_VERSION != -1 &&
+             compileStatus.CACHE_FORMAT_VERSION != CompilerConfigurationImpl.DEPENDENCY_FORMAT_VERSION) {
+      compileContext.requestRebuildNextTime(CompilerBundle.message("error.caches.old.format"));
+    }
+    else if (compileStatus.COMPILATION_IN_PROGRESS) {
+      compileContext.requestRebuildNextTime(CompilerBundle.message("error.previous.compilation.failed"));
+    }
+    else if (compileStatus.VFS_CREATION_STAMP >= 0L){
+      if (currentVFSTimestamp != compileStatus.VFS_CREATION_STAMP) {
+        compileContext.requestRebuildNextTime(CompilerBundle.message("error.vfs.was.rebuilt"));
+      }
+    }
+  }
+
+  private static String createStatusMessage(final ExitStatus status, final int warningCount, final int errorCount, long duration) {
+    String message;
+    if (status == ExitStatus.CANCELLED) {
+      message = CompilerBundle.message("status.compilation.aborted");
+    }
+    else if (status == ExitStatus.UP_TO_DATE) {
+      message = CompilerBundle.message("status.all.up.to.date");
+    }
+    else  {
+      if (status == ExitStatus.SUCCESS) {
+        message = warningCount > 0
+               ? CompilerBundle.message("status.compilation.completed.successfully.with.warnings", warningCount)
+               : CompilerBundle.message("status.compilation.completed.successfully");
+      }
+      else {
+        message = CompilerBundle.message("status.compilation.completed.successfully.with.warnings.and.errors", errorCount, warningCount);
+      }
+      message = message + " in " + Utils.formatDuration(duration);
+    }
+    return message;
+  }
+
+  private ExitStatus doCompile(final CompileContextEx context, boolean isRebuild, final boolean forceCompile, final boolean onlyCheckStatus) {
+    try {
+      if (isRebuild) {
+        deleteAll(context);
+      }
+      else if (forceCompile) {
+        if (myShouldClearOutputDirectory) {
+          clearAffectedOutputPathsIfPossible(context);
+        }
+      }
+      if (context.getMessageCount(CompilerMessageCategory.ERROR) > 0) {
+        if (LOG.isDebugEnabled()) {
+          logErrorMessages(context);
+        }
+        return ExitStatus.ERRORS;
+      }
+
+      if (!onlyCheckStatus) {
+          if (!executeCompileTasks(context, true)) {
+            if (LOG.isDebugEnabled()) {
+              LOG.debug("Compilation cancelled");
+            }
+            return ExitStatus.CANCELLED;
+          }
+        }
+
+      if (context.getMessageCount(CompilerMessageCategory.ERROR) > 0) {
+        if (LOG.isDebugEnabled()) {
+          logErrorMessages(context);
+        }
+        return ExitStatus.ERRORS;
+      }
+
+      boolean needRecalcOutputDirs = false;
+      if (Registry.is(PROP_PERFORM_INITIAL_REFRESH) || !Boolean.valueOf(REFRESH_DONE_KEY.get(myProject, Boolean.FALSE))) {
+        REFRESH_DONE_KEY.set(myProject, Boolean.TRUE);
+        final long refreshStart = System.currentTimeMillis();
+
+        //need this to make sure the VFS is built
+        final List<VirtualFile> outputsToRefresh = new ArrayList<VirtualFile>();
+
+        final VirtualFile[] all = context.getAllOutputDirectories();
+
+        final ProgressIndicator progressIndicator = context.getProgressIndicator();
+
+        //final int totalCount = all.length + myGenerationCompilerModuleToOutputDirMap.size() * 2;
+        progressIndicator.pushState();
+        progressIndicator.setText("Inspecting output directories...");
+        try {
+          for (VirtualFile output : all) {
+            if (output.isValid()) {
+              walkChildren(output, context);
+            }
+            else {
+              needRecalcOutputDirs = true;
+              final File file = new File(output.getPath());
+              if (!file.exists()) {
+                final boolean created = file.mkdirs();
+                if (!created) {
+                  context.addMessage(CompilerMessageCategory.ERROR, "Failed to create output directory " + file.getPath(), null, 0, 0);
+                  return ExitStatus.ERRORS;
+                }
+              }
+              output = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file);
+              if (output == null) {
+                context.addMessage(CompilerMessageCategory.ERROR, "Failed to locate output directory " + file.getPath(), null, 0, 0);
+                return ExitStatus.ERRORS;
+              }
+            }
+            outputsToRefresh.add(output);
+          }
+          for (Pair<IntermediateOutputCompiler, Module> pair : myGenerationCompilerModuleToOutputDirMap.keySet()) {
+            final Pair<VirtualFile, VirtualFile> generated = myGenerationCompilerModuleToOutputDirMap.get(pair);
+            walkChildren(generated.getFirst(), context);
+            outputsToRefresh.add(generated.getFirst());
+            walkChildren(generated.getSecond(), context);
+            outputsToRefresh.add(generated.getSecond());
+          }
+
+          RefreshQueue.getInstance().refresh(false, true, null, outputsToRefresh);
+          if (progressIndicator.isCanceled()) {
+            return ExitStatus.CANCELLED;
+          }
+        }
+        finally {
+          progressIndicator.popState();
+        }
+
+        final long initialRefreshTime = System.currentTimeMillis() - refreshStart;
+        CompilerUtil.logDuration("Initial VFS refresh", initialRefreshTime);
+      }
+
+      //DumbService.getInstance(myProject).waitForSmartMode();
+      final Semaphore semaphore = new Semaphore();
+      semaphore.down();
+      DumbService.getInstance(myProject).runWhenSmart(new Runnable() {
+        public void run() {
+          semaphore.up();
+        }
+      });
+      while (!semaphore.waitFor(500)) {
+        if (context.getProgressIndicator().isCanceled()) {
+          return ExitStatus.CANCELLED;
+        }
+      }
+
+      if (needRecalcOutputDirs) {
+        context.recalculateOutputDirs();
+      }
+
+      boolean didSomething = false;
+
+      final CompilerManager compilerManager = CompilerManager.getInstance(myProject);
+      GenericCompilerRunner runner = new GenericCompilerRunner(context, isRebuild, onlyCheckStatus,
+                                                               compilerManager.getCompilers(GenericCompiler.class, myCompilerFilter));
+      try {
+        didSomething |= generateSources(compilerManager, context, forceCompile, onlyCheckStatus);
+
+        didSomething |= invokeFileProcessingCompilers(compilerManager, context, SourceInstrumentingCompiler.class,
+                                                      FILE_PROCESSING_COMPILER_ADAPTER_FACTORY, forceCompile, true, onlyCheckStatus);
+
+        didSomething |= invokeFileProcessingCompilers(compilerManager, context, SourceProcessingCompiler.class,
+                                                      FILE_PROCESSING_COMPILER_ADAPTER_FACTORY, forceCompile, true, onlyCheckStatus);
+
+        final CompileScope intermediateSources = attachIntermediateOutputDirectories(new CompositeScope(CompileScope.EMPTY_ARRAY) {
+          @NotNull
+          public Module[] getAffectedModules() {
+            return context.getCompileScope().getAffectedModules();
+          }
+        }, SOURCE_PROCESSING_ONLY);
+        context.addScope(intermediateSources);
+
+        didSomething |= translate(context, compilerManager, forceCompile, isRebuild, onlyCheckStatus);
+
+        didSomething |= invokeFileProcessingCompilers(compilerManager, context, ClassInstrumentingCompiler.class,
+                                                      FILE_PROCESSING_COMPILER_ADAPTER_FACTORY, isRebuild, false, onlyCheckStatus);
+        didSomething |= runner.invokeCompilers(GenericCompiler.CompileOrderPlace.CLASS_INSTRUMENTING);
+
+        // explicitly passing forceCompile = false because in scopes that is narrower than ProjectScope it is impossible
+        // to understand whether the class to be processed is in scope or not. Otherwise compiler may process its items even if
+        // there were changes in completely independent files.
+        didSomething |= invokeFileProcessingCompilers(compilerManager, context, ClassPostProcessingCompiler.class,
+                                                      FILE_PROCESSING_COMPILER_ADAPTER_FACTORY, isRebuild, false, onlyCheckStatus);
+        didSomething |= runner.invokeCompilers(GenericCompiler.CompileOrderPlace.CLASS_POST_PROCESSING);
+
+        didSomething |= invokeFileProcessingCompilers(compilerManager, context, PackagingCompiler.class,
+                                                      FILE_PACKAGING_COMPILER_ADAPTER_FACTORY,
+                                                      isRebuild, false, onlyCheckStatus);
+        didSomething |= runner.invokeCompilers(GenericCompiler.CompileOrderPlace.PACKAGING);
+
+        didSomething |= invokeFileProcessingCompilers(compilerManager, context, Validator.class, FILE_PROCESSING_COMPILER_ADAPTER_FACTORY,
+                                                      forceCompile, true, onlyCheckStatus);
+        didSomething |= runner.invokeCompilers(GenericCompiler.CompileOrderPlace.VALIDATING);
+      }
+      catch (ExitException e) {
+        if (LOG.isDebugEnabled()) {
+          LOG.debug(e);
+          logErrorMessages(context);
+        }
+        return e.getExitStatus();
+      }
+      finally {
+        // drop in case it has not been dropped yet.
+        dropDependencyCache(context);
+        final VirtualFile[] allOutputDirs = context.getAllOutputDirectories();
+
+        if (didSomething && GENERATE_CLASSPATH_INDEX) {
+          CompilerUtil.runInContext(context, "Generating classpath index...", new ThrowableRunnable<RuntimeException>(){
+            public void run() {
+              int count = 0;
+              for (VirtualFile file : allOutputDirs) {
+                context.getProgressIndicator().setFraction((double)++count / allOutputDirs.length);
+                createClasspathIndex(file);
+              }
+            }
+          });
+        }
+
+      }
+
+      if (!onlyCheckStatus) {
+          if (!executeCompileTasks(context, false)) {
+            return ExitStatus.CANCELLED;
+          }
+          final int constantSearchesCount = ChangedConstantsDependencyProcessor.getConstantSearchesCount(context);
+          LOG.debug("Constants searches: " + constantSearchesCount);
+        }
+
+      if (context.getMessageCount(CompilerMessageCategory.ERROR) > 0) {
+        if (LOG.isDebugEnabled()) {
+          logErrorMessages(context);
+        }
+        return ExitStatus.ERRORS;
+      }
+      if (!didSomething) {
+        return ExitStatus.UP_TO_DATE;
+      }
+      return ExitStatus.SUCCESS;
+    }
+    catch (ProcessCanceledException e) {
+      return ExitStatus.CANCELLED;
+    }
+  }
+
+  private void clearAffectedOutputPathsIfPossible(final CompileContextEx context) {
+    final List<File> scopeOutputs = new ReadAction<List<File>>() {
+      protected void run(final Result<List<File>> result) {
+        final MultiMap<File, Module> outputToModulesMap = new MultiMap<File, Module>();
+        for (Module module : ModuleManager.getInstance(myProject).getModules()) {
+          final CompilerModuleExtension compilerModuleExtension = CompilerModuleExtension.getInstance(module);
+          if (compilerModuleExtension == null) {
+            continue;
+          }
+          final String outputPathUrl = compilerModuleExtension.getCompilerOutputUrl();
+          if (outputPathUrl != null) {
+            final String path = VirtualFileManager.extractPath(outputPathUrl);
+            outputToModulesMap.putValue(new File(path), module);
+          }
+
+          final String outputPathForTestsUrl = compilerModuleExtension.getCompilerOutputUrlForTests();
+          if (outputPathForTestsUrl != null) {
+            final String path = VirtualFileManager.extractPath(outputPathForTestsUrl);
+            outputToModulesMap.putValue(new File(path), module);
+          }
+        }
+        final Set<Module> affectedModules = new HashSet<Module>(Arrays.asList(context.getCompileScope().getAffectedModules()));
+        List<File> scopeOutputs = new ArrayList<File>(affectedModules.size() * 2);
+        for (File output : outputToModulesMap.keySet()) {
+          if (affectedModules.containsAll(outputToModulesMap.get(output))) {
+            scopeOutputs.add(output);
+          }
+        }
+
+        final Set<Artifact> artifactsToBuild = ArtifactCompileScope.getArtifactsToBuild(myProject, context.getCompileScope(), true);
+        for (Artifact artifact : artifactsToBuild) {
+          final String outputFilePath = ((ArtifactImpl)artifact).getOutputDirectoryPathToCleanOnRebuild();
+          if (outputFilePath != null) {
+            scopeOutputs.add(new File(FileUtil.toSystemDependentName(outputFilePath)));
+          }
+        }
+        result.setResult(scopeOutputs);
+      }
+    }.execute().getResultObject();
+    if (scopeOutputs.size() > 0) {
+      CompilerUtil.runInContext(context, CompilerBundle.message("progress.clearing.output"), new ThrowableRunnable<RuntimeException>() {
+        public void run() {
+          CompilerUtil.clearOutputDirectories(scopeOutputs);
+        }
+      });
+    }
+  }
+
+  private static void logErrorMessages(final CompileContext context) {
+    final CompilerMessage[] errors = context.getMessages(CompilerMessageCategory.ERROR);
+    if (errors.length > 0) {
+      LOG.debug("Errors reported: ");
+      for (CompilerMessage error : errors) {
+        LOG.debug("\t" + error.getMessage());
+      }
+    }
+  }
+
+  private static void walkChildren(VirtualFile from, final CompileContext context) {
+    VfsUtilCore.visitChildrenRecursively(from, new VirtualFileVisitor() {
+      @Override
+      public boolean visitFile(@NotNull VirtualFile file) {
+        if (file.isDirectory()) {
+          context.getProgressIndicator().checkCanceled();
+          context.getProgressIndicator().setText2(file.getPresentableUrl());
+        }
+        return true;
+      }
+    });
+  }
+
+  private static void createClasspathIndex(final VirtualFile file) {
+    try {
+      BufferedWriter writer = new BufferedWriter(new FileWriter(new File(VfsUtilCore.virtualToIoFile(file), "classpath.index")));
+      try {
+        writeIndex(writer, file, file);
+      }
+      finally {
+        writer.close();
+      }
+    }
+    catch (IOException e) {
+      // Ignore. Failed to create optional classpath index
+    }
+  }
+
+  private static void writeIndex(final BufferedWriter writer, final VirtualFile root, final VirtualFile file) throws IOException {
+    VfsUtilCore.visitChildrenRecursively(file, new VirtualFileVisitor() {
+      @Override
+      public boolean visitFile(@NotNull VirtualFile file) {
+        try {
+          writer.write(VfsUtilCore.getRelativePath(file, root, '/'));
+          writer.write('\n');
+          return true;
+        }
+        catch (IOException e) {
+          throw new VisitorException(e);
+        }
+      }
+    }, IOException.class);
+  }
+
+  private static void dropDependencyCache(final CompileContextEx context) {
+    CompilerUtil.runInContext(context, CompilerBundle.message("progress.saving.caches"), new ThrowableRunnable<RuntimeException>(){
+      public void run() {
+        context.getDependencyCache().resetState();
+      }
+    });
+  }
+
+  private boolean generateSources(final CompilerManager compilerManager,
+                                  CompileContextEx context,
+                                  final boolean forceCompile,
+                                  final boolean onlyCheckStatus) throws ExitException {
+    boolean didSomething = false;
+
+    final SourceGeneratingCompiler[] sourceGenerators = compilerManager.getCompilers(SourceGeneratingCompiler.class, myCompilerFilter);
+    for (final SourceGeneratingCompiler sourceGenerator : sourceGenerators) {
+      if (context.getProgressIndicator().isCanceled()) {
+        throw new ExitException(ExitStatus.CANCELLED);
+      }
+
+      final boolean generatedSomething = generateOutput(context, sourceGenerator, forceCompile, onlyCheckStatus);
+
+      if (context.getMessageCount(CompilerMessageCategory.ERROR) > 0) {
+        throw new ExitException(ExitStatus.ERRORS);
+      }
+      didSomething |= generatedSomething;
+    }
+    return didSomething;
+  }
+
+  private boolean translate(final CompileContextEx context,
+                            final CompilerManager compilerManager,
+                            final boolean forceCompile,
+                            boolean isRebuild,
+                            final boolean onlyCheckStatus) throws ExitException {
+
+    boolean didSomething = false;
+
+    final TranslatingCompiler[] translators = compilerManager.getCompilers(TranslatingCompiler.class, myCompilerFilter);
+
+
+    final List<Chunk<Module>> sortedChunks = Collections.unmodifiableList(ApplicationManager.getApplication().runReadAction(new Computable<List<Chunk<Module>>>() {
+      public List<Chunk<Module>> compute() {
+        final ModuleManager moduleManager = ModuleManager.getInstance(myProject);
+        return ModuleCompilerUtil.getSortedModuleChunks(myProject, Arrays.asList(moduleManager.getModules()));
+      }
+    }));
+
+    final DumbService dumbService = DumbService.getInstance(myProject);
+    try {
+      final Set<Module> processedModules = new HashSet<Module>();
+      VirtualFile[] snapshot = null;
+      final Map<Chunk<Module>, Collection<VirtualFile>> chunkMap = new HashMap<Chunk<Module>, Collection<VirtualFile>>();
+      int total = 0;
+      int processed = 0;
+      for (final Chunk<Module> currentChunk : sortedChunks) {
+        final TranslatorsOutputSink sink = new TranslatorsOutputSink(context, translators);
+        final Set<FileType> generatedTypes = new HashSet<FileType>();
+        Collection<VirtualFile> chunkFiles = chunkMap.get(currentChunk);
+        final Set<VirtualFile> filesToRecompile = new HashSet<VirtualFile>();
+        final Set<VirtualFile> allDependent = new HashSet<VirtualFile>();
+        try {
+          int round = 0;
+          boolean compiledSomethingForThisChunk = false;
+          Collection<VirtualFile> dependentFiles = Collections.emptyList();
+          final Function<Pair<int[], Set<VirtualFile>>, Pair<int[], Set<VirtualFile>>> dependencyFilter = new DependentClassesCumulativeFilter();
+          
+          do {
+            for (int currentCompiler = 0, translatorsLength = translators.length; currentCompiler < translatorsLength; currentCompiler++) {
+              sink.setCurrentCompilerIndex(currentCompiler);
+              final TranslatingCompiler compiler = translators[currentCompiler];
+              if (context.getProgressIndicator().isCanceled()) {
+                throw new ExitException(ExitStatus.CANCELLED);
+              }
+
+              dumbService.waitForSmartMode();
+
+              if (snapshot == null || ContainerUtil.intersects(generatedTypes, compilerManager.getRegisteredInputTypes(compiler))) {
+                // rescan snapshot if previously generated files may influence the input of this compiler
+                final Set<VirtualFile> prevSnapshot = round > 0 && snapshot != null? new HashSet<VirtualFile>(Arrays.asList(snapshot)) : Collections.<VirtualFile>emptySet();
+                snapshot = ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile[]>() {
+                  public VirtualFile[] compute() {
+                    return context.getCompileScope().getFiles(null, true);
+                  }
+                });
+                recalculateChunkToFilesMap(context, sortedChunks, snapshot, chunkMap);
+                if (round == 0) {
+                  chunkFiles = chunkMap.get(currentChunk);
+                }
+                else {
+                  final Set<VirtualFile> newFiles = new HashSet<VirtualFile>(chunkMap.get(currentChunk));
+                  newFiles.removeAll(prevSnapshot);
+                  newFiles.removeAll(chunkFiles);
+                  if (!newFiles.isEmpty()) {
+                    final ArrayList<VirtualFile> merged = new ArrayList<VirtualFile>(chunkFiles.size() + newFiles.size());
+                    merged.addAll(chunkFiles);
+                    merged.addAll(newFiles);
+                    chunkFiles = merged;
+                  }
+                }
+                total = snapshot.length * translatorsLength;
+              }
+
+              final CompileContextEx _context;
+              if (compiler instanceof IntermediateOutputCompiler) {
+                // wrap compile context so that output goes into intermediate directories
+                final IntermediateOutputCompiler _compiler = (IntermediateOutputCompiler)compiler;
+                _context = new CompileContextExProxy(context) {
+                  public VirtualFile getModuleOutputDirectory(final Module module) {
+                    return getGenerationOutputDir(_compiler, module, false);
+                  }
+  
+                  public VirtualFile getModuleOutputDirectoryForTests(final Module module) {
+                    return getGenerationOutputDir(_compiler, module, true);
+                  }
+                };
+              }
+              else {
+                _context = context;
+              }
+              final boolean compiledSomething =
+                compileSources(_context, currentChunk, compiler, chunkFiles, round == 0? forceCompile : true, isRebuild, onlyCheckStatus, sink);
+  
+              processed += chunkFiles.size();
+              _context.getProgressIndicator().setFraction(((double)processed) / total);
+  
+              if (compiledSomething) {
+                generatedTypes.addAll(compilerManager.getRegisteredOutputTypes(compiler));
+              }
+  
+              didSomething |= compiledSomething;
+              compiledSomethingForThisChunk |= didSomething;
+
+              if (_context.getMessageCount(CompilerMessageCategory.ERROR) > 0) {
+                break; // break the loop over compilers
+              }
+            }
+
+            final boolean hasUnprocessedTraverseRoots = context.getDependencyCache().hasUnprocessedTraverseRoots();
+            if (!isRebuild && (compiledSomethingForThisChunk || hasUnprocessedTraverseRoots)) {
+              final Set<VirtualFile> compiledWithErrors = CacheUtils.getFilesCompiledWithErrors(context);
+              filesToRecompile.removeAll(sink.getCompiledSources());
+              filesToRecompile.addAll(compiledWithErrors);
+
+              dependentFiles = CacheUtils.findDependentFiles(context, compiledWithErrors, dependencyFilter);
+              if (!processedModules.isEmpty()) {
+                for (Iterator<VirtualFile> it = dependentFiles.iterator(); it.hasNext();) {
+                  final VirtualFile next = it.next();
+                  final Module module = context.getModuleByFile(next);
+                  if (module != null && processedModules.contains(module)) {
+                    it.remove();
+                  }
+                }
+              }
+              
+              if (ourDebugMode) {
+                if (!dependentFiles.isEmpty()) {
+                  for (VirtualFile dependentFile : dependentFiles) {
+                    System.out.println("FOUND TO RECOMPILE: " + dependentFile.getPresentableUrl());
+                  }
+                }
+                else {
+                  System.out.println("NO FILES TO RECOMPILE");
+                }
+              }
+
+              if (!dependentFiles.isEmpty()) {
+                filesToRecompile.addAll(dependentFiles);
+                allDependent.addAll(dependentFiles);
+                if (context.getProgressIndicator().isCanceled() || context.getMessageCount(CompilerMessageCategory.ERROR) > 0) {
+                  break;
+                }
+                final List<VirtualFile> filesInScope = getFilesInScope(context, currentChunk, dependentFiles);
+                if (filesInScope.isEmpty()) {
+                  break;
+                }
+                context.getDependencyCache().clearTraverseRoots();
+                chunkFiles = filesInScope;
+                total += chunkFiles.size() * translators.length;
+              }
+              
+              didSomething |= (hasUnprocessedTraverseRoots != context.getDependencyCache().hasUnprocessedTraverseRoots());
+            }
+
+            round++;
+          }
+          while (!dependentFiles.isEmpty() && context.getMessageCount(CompilerMessageCategory.ERROR) == 0);
+
+          if (CompilerConfiguration.MAKE_ENABLED) {
+            if (!context.getProgressIndicator().isCanceled()) {
+              // when cancelled pretend nothing was compiled and next compile will compile everything from the scratch
+              final ProgressIndicator indicator = context.getProgressIndicator();
+              final DependencyCache cache = context.getDependencyCache();
+
+              indicator.pushState();
+              indicator.setText(CompilerBundle.message("progress.updating.caches"));
+              indicator.setText2("");
+
+              cache.update();
+
+              indicator.setText(CompilerBundle.message("progress.saving.caches"));
+              cache.resetState();
+              processedModules.addAll(currentChunk.getNodes());
+              indicator.popState();
+            }
+          }
+
+          if (context.getMessageCount(CompilerMessageCategory.ERROR) > 0) {
+            throw new ExitException(ExitStatus.ERRORS);
+          }
+
+        }
+        catch (CacheCorruptedException e) {
+          LOG.info(e);
+          context.requestRebuildNextTime(e.getMessage());
+        }
+        finally {
+          final int errorCount = context.getMessageCount(CompilerMessageCategory.ERROR);
+          if (errorCount != 0) {
+            filesToRecompile.addAll(allDependent);
+          }
+          if (filesToRecompile.size() > 0) {
+            sink.add(null, Collections.<TranslatingCompiler.OutputItem>emptyList(), VfsUtilCore.toVirtualFileArray(filesToRecompile));
+          }
+          if (errorCount == 0) {
+            // perform update only if there were no errors, so it is guaranteed that the file was processd by all neccesary compilers
+            sink.flushPostponedItems();
+          }
+        }
+      }
+    }
+    catch (ProcessCanceledException e) {
+      ProgressManager.getInstance().executeNonCancelableSection(new Runnable() {
+        public void run() {
+          try {
+            final Collection<VirtualFile> deps = CacheUtils.findDependentFiles(context, Collections.<VirtualFile>emptySet(), null);
+            if (deps.size() > 0) {
+              TranslatingCompilerFilesMonitor.getInstance().update(context, null, Collections.<TranslatingCompiler.OutputItem>emptyList(),
+                                                                   VfsUtilCore.toVirtualFileArray(deps));
+            }
+          }
+          catch (IOException ignored) {
+            LOG.info(ignored);
+          }
+          catch (CacheCorruptedException ignored) {
+            LOG.info(ignored);
+          }
+          catch (ExitException e1) {
+            LOG.info(e1);
+          }
+        }
+      });
+      throw e;
+    }
+    finally {
+      dropDependencyCache(context);
+      if (didSomething) {
+        TranslatingCompilerFilesMonitor.getInstance().updateOutputRootsLayout(myProject);
+      }
+    }
+    return didSomething;
+  }
+
+  private static List<VirtualFile> getFilesInScope(final CompileContextEx context, final Chunk<Module> chunk, final Collection<VirtualFile> files) {
+    final List<VirtualFile> filesInScope = new ArrayList<VirtualFile>(files.size());
+    ApplicationManager.getApplication().runReadAction(new Runnable() {
+      public void run() {
+        for (VirtualFile file : files) {
+          if (context.getCompileScope().belongs(file.getUrl())) {
+            final Module module = context.getModuleByFile(file);
+            if (chunk.getNodes().contains(module)) {
+              filesInScope.add(file);
+            }
+          }
+        }
+      }
+    });
+    return filesInScope;
+  }
+
+  private static void recalculateChunkToFilesMap(CompileContextEx context, List<Chunk<Module>> allChunks, VirtualFile[] snapshot, Map<Chunk<Module>, Collection<VirtualFile>> chunkMap) {
+    final Map<Module, List<VirtualFile>> moduleToFilesMap = CompilerUtil.buildModuleToFilesMap(context, snapshot);
+    for (Chunk<Module> moduleChunk : allChunks) {
+      List<VirtualFile> files = Collections.emptyList();
+      for (Module module : moduleChunk.getNodes()) {
+        final List<VirtualFile> moduleFiles = moduleToFilesMap.get(module);
+        if (moduleFiles != null) {
+          files = ContainerUtil.concat(files, moduleFiles);
+        }
+      }
+      chunkMap.put(moduleChunk, files);
+    }
+  }
+
+  private interface FileProcessingCompilerAdapterFactory {
+    FileProcessingCompilerAdapter create(CompileContext context, FileProcessingCompiler compiler);
+  }
+
+  private boolean invokeFileProcessingCompilers(final CompilerManager compilerManager,
+                                                CompileContextEx context,
+                                                Class<? extends FileProcessingCompiler> fileProcessingCompilerClass,
+                                                FileProcessingCompilerAdapterFactory factory,
+                                                boolean forceCompile,
+                                                final boolean checkScope,
+                                                final boolean onlyCheckStatus) throws ExitException {
+    boolean didSomething = false;
+    final FileProcessingCompiler[] compilers = compilerManager.getCompilers(fileProcessingCompilerClass, myCompilerFilter);
+    if (compilers.length > 0) {
+      try {
+        CacheDeferredUpdater cacheUpdater = new CacheDeferredUpdater();
+        try {
+          for (final FileProcessingCompiler compiler : compilers) {
+            if (context.getProgressIndicator().isCanceled()) {
+              throw new ExitException(ExitStatus.CANCELLED);
+            }
+
+            CompileContextEx _context = context;
+            if (compiler instanceof IntermediateOutputCompiler) {
+              final IntermediateOutputCompiler _compiler = (IntermediateOutputCompiler)compiler;
+              _context = new CompileContextExProxy(context) {
+                public VirtualFile getModuleOutputDirectory(final Module module) {
+                  return getGenerationOutputDir(_compiler, module, false);
+                }
+
+                public VirtualFile getModuleOutputDirectoryForTests(final Module module) {
+                  return getGenerationOutputDir(_compiler, module, true);
+                }
+              };
+            }
+
+            final boolean processedSomething = processFiles(factory.create(_context, compiler), forceCompile, checkScope, onlyCheckStatus, cacheUpdater);
+
+            if (context.getMessageCount(CompilerMessageCategory.ERROR) > 0) {
+              throw new ExitException(ExitStatus.ERRORS);
+            }
+
+            didSomething |= processedSomething;
+          }
+        }
+        finally {
+          cacheUpdater.doUpdate();
+        }
+      }
+      catch (IOException e) {
+        LOG.info(e);
+        context.requestRebuildNextTime(e.getMessage());
+        throw new ExitException(ExitStatus.ERRORS);
+      }
+      catch (ProcessCanceledException e) {
+        throw e;
+      }
+      catch (ExitException e) {
+        throw e;
+      }
+      catch (Exception e) {
+        context.addMessage(CompilerMessageCategory.ERROR, CompilerBundle.message("compiler.error.exception", e.getMessage()), null, -1, -1);
+        LOG.error(e);
+      }
+    }
+
+    return didSomething;
+  }
+
+  private static Map<Module, Set<GeneratingCompiler.GenerationItem>> buildModuleToGenerationItemMap(GeneratingCompiler.GenerationItem[] items) {
+    final Map<Module, Set<GeneratingCompiler.GenerationItem>> map = new HashMap<Module, Set<GeneratingCompiler.GenerationItem>>();
+    for (GeneratingCompiler.GenerationItem item : items) {
+      Module module = item.getModule();
+      LOG.assertTrue(module != null);
+      Set<GeneratingCompiler.GenerationItem> itemSet = map.get(module);
+      if (itemSet == null) {
+        itemSet = new HashSet<GeneratingCompiler.GenerationItem>();
+        map.put(module, itemSet);
+      }
+      itemSet.add(item);
+    }
+    return map;
+  }
+
+  private void deleteAll(final CompileContextEx context) {
+    CompilerUtil.runInContext(context, CompilerBundle.message("progress.clearing.output"), new ThrowableRunnable<RuntimeException>() {
+      public void run() {
+        final boolean isTestMode = ApplicationManager.getApplication().isUnitTestMode();
+        final VirtualFile[] allSources = context.getProjectCompileScope().getFiles(null, true);
+        if (myShouldClearOutputDirectory) {
+          CompilerUtil.clearOutputDirectories(myAllOutputDirectories);
+        }
+        else { // refresh is still required
+          try {
+            for (final Compiler compiler : CompilerManager.getInstance(myProject).getCompilers(Compiler.class)) {
+              try {
+                if (compiler instanceof GeneratingCompiler) {
+                  final StateCache<ValidityState> cache = getGeneratingCompilerCache((GeneratingCompiler)compiler);
+                  final Iterator<String> urlIterator = cache.getUrlsIterator();
+                  while (urlIterator.hasNext()) {
+                    context.getProgressIndicator().checkCanceled();
+                    deleteFile(new File(VirtualFileManager.extractPath(urlIterator.next())));
+                  }
+                }
+                else if (compiler instanceof TranslatingCompiler) {
+                  final ArrayList<Trinity<File, String, Boolean>> toDelete = new ArrayList<Trinity<File, String, Boolean>>();
+                  ApplicationManager.getApplication().runReadAction(new Runnable() {
+                    public void run() {
+                      TranslatingCompilerFilesMonitor.getInstance().collectFiles(
+                        context, 
+                        (TranslatingCompiler)compiler, Arrays.<VirtualFile>asList(allSources).iterator(), 
+                        true /*pass true to make sure that every source in scope file is processed*/, 
+                        false /*important! should pass false to enable collection of files to delete*/,
+                        new ArrayList<VirtualFile>(), 
+                        toDelete
+                      );
+                    }
+                  });
+                  for (Trinity<File, String, Boolean> trinity : toDelete) {
+                    context.getProgressIndicator().checkCanceled();
+                    final File file = trinity.getFirst();
+                    deleteFile(file);
+                    if (isTestMode) {
+                      CompilerManagerImpl.addDeletedPath(file.getPath());
+                    }
+                  }
+                }
+              }
+              catch (IOException e) {
+                LOG.info(e);
+              }
+            }
+            pruneEmptyDirectories(context.getProgressIndicator(), myAllOutputDirectories); // to avoid too much files deleted events
+          }
+          finally {
+            CompilerUtil.refreshIODirectories(myAllOutputDirectories);
+          }
+        }
+        dropScopesCaches();
+
+        clearCompilerSystemDirectory(context);
+      }
+    });
+  }
+
+  private void dropScopesCaches() {
+    // hack to be sure the classpath will include the output directories
+    ApplicationManager.getApplication().runReadAction(new Runnable() {
+      public void run() {
+        ((ProjectRootManagerEx)ProjectRootManager.getInstance(myProject)).clearScopesCachesForModules();
+      }
+    });
+  }
+
+  private static void pruneEmptyDirectories(ProgressIndicator progress, final Set<File> directories) {
+    for (File directory : directories) {
+      doPrune(progress, directory, directories);
+    }
+  }
+
+  private static boolean doPrune(ProgressIndicator progress, final File directory, final Set<File> outPutDirectories) {
+    progress.checkCanceled();
+    final File[] files = directory.listFiles();
+    boolean isEmpty = true;
+    if (files != null) {
+      for (File file : files) {
+        if (!outPutDirectories.contains(file)) {
+          if (doPrune(progress, file, outPutDirectories)) {
+            deleteFile(file);
+          }
+          else {
+            isEmpty = false;
+          }
+        }
+        else {
+          isEmpty = false;
+        }
+      }
+    }
+    else {
+      isEmpty = false;
+    }
+
+    return isEmpty;
+  }
+
+  private Set<File> getAllOutputDirectories(CompileContext context) {
+    final Set<File> outputDirs = new OrderedSet<File>();
+    final Module[] modules = ModuleManager.getInstance(myProject).getModules();
+    for (final String path : CompilerPathsEx.getOutputPaths(modules)) {
+      outputDirs.add(new File(path));
+    }
+    for (Pair<IntermediateOutputCompiler, Module> pair : myGenerationCompilerModuleToOutputDirMap.keySet()) {
+      outputDirs.add(new File(CompilerPaths.getGenerationOutputPath(pair.getFirst(), pair.getSecond(), false)));
+      outputDirs.add(new File(CompilerPaths.getGenerationOutputPath(pair.getFirst(), pair.getSecond(), true)));
+    }
+    final CompilerConfiguration config = CompilerConfiguration.getInstance(myProject);
+    if (context.isAnnotationProcessorsEnabled()) {
+      for (Module module : modules) {
+        if (config.getAnnotationProcessingConfiguration(module).isEnabled()) {
+          final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module);
+          if (path != null) {
+            outputDirs.add(new File(path));
+          }
+        }
+      }
+    }
+    for (Artifact artifact : ArtifactManager.getInstance(myProject).getArtifacts()) {
+      final String path = ((ArtifactImpl)artifact).getOutputDirectoryPathToCleanOnRebuild();
+      if (path != null) {
+        outputDirs.add(new File(FileUtil.toSystemDependentName(path)));
+      }
+    }
+    return outputDirs;
+  }
+
+  private void clearCompilerSystemDirectory(final CompileContextEx context) {
+    CompilerCacheManager.getInstance(myProject).clearCaches(context);
+    FileUtil.delete(CompilerPathsEx.getZipStoreDirectory(myProject));
+    dropDependencyCache(context);
+
+    for (Pair<IntermediateOutputCompiler, Module> pair : myGenerationCompilerModuleToOutputDirMap.keySet()) {
+      final File[] outputs = {
+        new File(CompilerPaths.getGenerationOutputPath(pair.getFirst(), pair.getSecond(), false)),
+        new File(CompilerPaths.getGenerationOutputPath(pair.getFirst(), pair.getSecond(), true))
+      };
+      for (File output : outputs) {
+        final File[] files = output.listFiles();
+        if (files != null) {
+          for (final File file : files) {
+            final boolean deleteOk = deleteFile(file);
+            if (!deleteOk) {
+              context.addMessage(CompilerMessageCategory.ERROR, CompilerBundle.message("compiler.error.failed.to.delete", file.getPath()),
+                                 null, -1, -1);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * @param file a file to delete
+   * @return true if and only if the file existed and was successfully deleted
+   * Note: the behaviour is different from FileUtil.delete() which returns true if the file absent on the disk
+   */
+  private static boolean deleteFile(final File file) {
+    File[] files = file.listFiles();
+    if (files != null) {
+      for (File file1 : files) {
+        deleteFile(file1);
+      }
+    }
+
+    for (int i = 0; i < 10; i++){
+      if (file.delete()) {
+        return true;
+      }
+      if (!file.exists()) {
+        return false;
+      }
+      try {
+        Thread.sleep(50);
+      }
+      catch (InterruptedException ignored) {
+      }
+    }
+    return false;
+  }
+
+  private VirtualFile getGenerationOutputDir(final IntermediateOutputCompiler compiler, final Module module, final boolean forTestSources) {
+    final Pair<VirtualFile, VirtualFile> outputs =
+      myGenerationCompilerModuleToOutputDirMap.get(new Pair<IntermediateOutputCompiler, Module>(compiler, module));
+    return forTestSources? outputs.getSecond() : outputs.getFirst();
+  }
+
+  private boolean generateOutput(final CompileContextEx context,
+                                 final GeneratingCompiler compiler,
+                                 final boolean forceGenerate,
+                                 final boolean onlyCheckStatus) throws ExitException {
+    final GeneratingCompiler.GenerationItem[] allItems = compiler.getGenerationItems(context);
+    final List<GeneratingCompiler.GenerationItem> toGenerate = new ArrayList<GeneratingCompiler.GenerationItem>();
+    final List<File> filesToRefresh = new ArrayList<File>();
+    final List<File> generatedFiles = new ArrayList<File>();
+    final List<Module> affectedModules = new ArrayList<Module>();
+    try {
+      final StateCache<ValidityState> cache = getGeneratingCompilerCache(compiler);
+      final Set<String> pathsToRemove = new HashSet<String>(cache.getUrls());
+
+      final Map<GeneratingCompiler.GenerationItem, String> itemToOutputPathMap = new HashMap<GeneratingCompiler.GenerationItem, String>();
+      final IOException[] ex = {null};
+      ApplicationManager.getApplication().runReadAction(new Runnable() {
+        public void run() {
+          for (final GeneratingCompiler.GenerationItem item : allItems) {
+            final Module itemModule = item.getModule();
+            final String outputDirPath = CompilerPaths.getGenerationOutputPath(compiler, itemModule, item.isTestSource());
+            final String outputPath = outputDirPath + "/" + item.getPath();
+            itemToOutputPathMap.put(item, outputPath);
+
+            try {
+              final ValidityState savedState = cache.getState(outputPath);
+
+              if (forceGenerate || savedState == null || !savedState.equalsTo(item.getValidityState())) {
+                final String outputPathUrl = VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, outputPath);
+                if (context.getCompileScope().belongs(outputPathUrl)) {
+                  toGenerate.add(item);
+                }
+                else {
+                  pathsToRemove.remove(outputPath);
+                }
+              }
+              else {
+                pathsToRemove.remove(outputPath);
+              }
+            }
+            catch (IOException e) {
+              ex[0] = e;
+            }
+          }
+        }
+      });
+      if (ex[0] != null) {
+        throw ex[0];
+      }                   
+
+      if (onlyCheckStatus) {
+        if (toGenerate.isEmpty() && pathsToRemove.isEmpty()) {
+          return false;
+        }
+        if (LOG.isDebugEnabled()) {
+          if (!toGenerate.isEmpty()) {
+            LOG.debug("Found items to generate, compiler " + compiler.getDescription());
+          }
+          if (!pathsToRemove.isEmpty()) {
+            LOG.debug("Found paths to remove, compiler " + compiler.getDescription());
+          }
+        }
+        throw new ExitException(ExitStatus.CANCELLED);
+      }
+
+      if (!pathsToRemove.isEmpty()) {
+        CompilerUtil.runInContext(context, CompilerBundle.message("progress.synchronizing.output.directory"), new ThrowableRunnable<IOException>(){
+          public void run() throws IOException {
+            for (final String path : pathsToRemove) {
+              final File file = new File(path);
+              final boolean deleted = deleteFile(file);
+              if (deleted) {
+                cache.remove(path);
+                filesToRefresh.add(file);
+              }
+            }
+          }
+        });
+      }
+
+      final Map<Module, Set<GeneratingCompiler.GenerationItem>> moduleToItemMap =
+          buildModuleToGenerationItemMap(toGenerate.toArray(new GeneratingCompiler.GenerationItem[toGenerate.size()]));
+      List<Module> modules = new ArrayList<Module>(moduleToItemMap.size());
+      for (final Module module : moduleToItemMap.keySet()) {
+        modules.add(module);
+      }
+      ModuleCompilerUtil.sortModules(myProject, modules);
+
+      for (final Module module : modules) {
+        CompilerUtil.runInContext(context, "Generating output from "+compiler.getDescription(),new ThrowableRunnable<IOException>(){
+          public void run() throws IOException {
+            final Set<GeneratingCompiler.GenerationItem> items = moduleToItemMap.get(module);
+            if (items != null && !items.isEmpty()) {
+              final GeneratingCompiler.GenerationItem[][] productionAndTestItems = splitGenerationItems(items);
+              for (GeneratingCompiler.GenerationItem[] _items : productionAndTestItems) {
+                if (_items.length == 0) continue;
+                final VirtualFile outputDir = getGenerationOutputDir(compiler, module, _items[0].isTestSource());
+                final GeneratingCompiler.GenerationItem[] successfullyGenerated = compiler.generate(context, _items, outputDir);
+
+                CompilerUtil.runInContext(context, CompilerBundle.message("progress.updating.caches"), new ThrowableRunnable<IOException>() {
+                  public void run() throws IOException {
+                    if (successfullyGenerated.length > 0) {
+                      affectedModules.add(module);
+                    }
+                    for (final GeneratingCompiler.GenerationItem item : successfullyGenerated) {
+                      final String fullOutputPath = itemToOutputPathMap.get(item);
+                      cache.update(fullOutputPath, item.getValidityState());
+                      final File file = new File(fullOutputPath);
+                      filesToRefresh.add(file);
+                      generatedFiles.add(file);
+                      context.getProgressIndicator().setText2(file.getPath());
+                    }
+                  }
+                });
+              }
+            }
+          }
+        });
+      }
+    }
+    catch (IOException e) {
+      LOG.info(e);
+      context.requestRebuildNextTime(e.getMessage());
+      throw new ExitException(ExitStatus.ERRORS);
+    }
+    finally {
+      CompilerUtil.refreshIOFiles(filesToRefresh);
+      if (!generatedFiles.isEmpty()) {
+        DumbService.getInstance(myProject).waitForSmartMode();
+        List<VirtualFile> vFiles = ApplicationManager.getApplication().runReadAction(new Computable<List<VirtualFile>>() {
+          public List<VirtualFile> compute() {
+            final ArrayList<VirtualFile> vFiles = new ArrayList<VirtualFile>(generatedFiles.size());
+            for (File generatedFile : generatedFiles) {
+              final VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(generatedFile);
+              if (vFile != null) {
+                vFiles.add(vFile);
+              }
+            }
+            return vFiles;
+          }
+        });
+        if (forceGenerate) {
+          context.addScope(new FileSetCompileScope(vFiles, affectedModules.toArray(new Module[affectedModules.size()])));
+        }
+        context.markGenerated(vFiles);
+      }
+    }
+    return !toGenerate.isEmpty() || !filesToRefresh.isEmpty();
+  }
+
+  private static GeneratingCompiler.GenerationItem[][] splitGenerationItems(final Set<GeneratingCompiler.GenerationItem> items) {
+    final List<GeneratingCompiler.GenerationItem> production = new ArrayList<GeneratingCompiler.GenerationItem>();
+    final List<GeneratingCompiler.GenerationItem> tests = new ArrayList<GeneratingCompiler.GenerationItem>();
+    for (GeneratingCompiler.GenerationItem item : items) {
+      if (item.isTestSource()) {
+        tests.add(item);
+      }
+      else {
+        production.add(item);
+      }
+    }
+    return new GeneratingCompiler.GenerationItem[][]{
+      production.toArray(new GeneratingCompiler.GenerationItem[production.size()]),
+      tests.toArray(new GeneratingCompiler.GenerationItem[tests.size()])
+    };
+  }
+
+  private boolean compileSources(final CompileContextEx context,
+                                 final Chunk<Module> moduleChunk,
+                                 final TranslatingCompiler compiler,
+                                 final Collection<VirtualFile> srcSnapshot,
+                                 final boolean forceCompile,
+                                 final boolean isRebuild,
+                                 final boolean onlyCheckStatus,
+                                 TranslatingCompiler.OutputSink sink) throws ExitException {
+
+    final Set<VirtualFile> toCompile = new HashSet<VirtualFile>();
+    final List<Trinity<File, String, Boolean>> toDelete = new ArrayList<Trinity<File, String, Boolean>>();
+    context.getProgressIndicator().pushState();
+
+    final boolean[] wereFilesDeleted = {false};
+    try {
+      ApplicationManager.getApplication().runReadAction(new Runnable() {
+        public void run() {
+          TranslatingCompilerFilesMonitor.getInstance().collectFiles(
+            context, compiler, srcSnapshot.iterator(), forceCompile, isRebuild, toCompile, toDelete
+          );
+        }
+      });
+
+      if (onlyCheckStatus) {
+        if (toDelete.isEmpty() && toCompile.isEmpty()) {
+          return false;
+        }
+        if (LOG.isDebugEnabled() || ourDebugMode) {
+          if (!toDelete.isEmpty()) {
+            final StringBuilder message = new StringBuilder();
+            message.append("Found items to delete, compiler ").append(compiler.getDescription());
+            for (Trinity<File, String, Boolean> trinity : toDelete) {
+              message.append("\n").append(trinity.getFirst());
+            }
+            LOG.debug(message.toString());
+            if (ourDebugMode) {
+              System.out.println(message);
+            }
+          }
+          if (!toCompile.isEmpty()) {
+            final String message = "Found items to compile, compiler " + compiler.getDescription();
+            LOG.debug(message);
+            if (ourDebugMode) {
+              System.out.println(message);
+            }
+          }
+        }
+        throw new ExitException(ExitStatus.CANCELLED);
+      }
+
+      if (!toDelete.isEmpty()) {
+        try {
+          wereFilesDeleted[0] = syncOutputDir(context, toDelete);
+        }
+        catch (CacheCorruptedException e) {
+          LOG.info(e);
+          context.requestRebuildNextTime(e.getMessage());
+        }
+      }
+
+      if ((wereFilesDeleted[0] || !toCompile.isEmpty()) && context.getMessageCount(CompilerMessageCategory.ERROR) == 0) {
+        compiler.compile(context, moduleChunk, VfsUtilCore.toVirtualFileArray(toCompile), sink);
+      }
+    }
+    finally {
+      context.getProgressIndicator().popState();
+    }
+    return !toCompile.isEmpty() || wereFilesDeleted[0];
+  }
+
+  private static boolean syncOutputDir(final CompileContextEx context, final Collection<Trinity<File, String, Boolean>> toDelete) throws CacheCorruptedException {
+    final DependencyCache dependencyCache = context.getDependencyCache();
+    final boolean isTestMode = ApplicationManager.getApplication().isUnitTestMode();
+
+    final List<File> filesToRefresh = new ArrayList<File>();
+    final boolean[] wereFilesDeleted = {false};
+    CompilerUtil.runInContext(context, CompilerBundle.message("progress.synchronizing.output.directory"), new ThrowableRunnable<CacheCorruptedException>(){
+      public void run() throws CacheCorruptedException {
+        final long start = System.currentTimeMillis();
+        try {
+          for (final Trinity<File, String, Boolean> trinity : toDelete) {
+            final File outputPath = trinity.getFirst();
+            context.getProgressIndicator().checkCanceled();
+            context.getProgressIndicator().setText2(outputPath.getPath());
+            filesToRefresh.add(outputPath);
+            if (isTestMode) {
+              LOG.assertTrue(outputPath.exists());
+            }
+            if (!deleteFile(outputPath)) {
+              if (isTestMode) {
+                if (outputPath.exists()) {
+                  LOG.error("Was not able to delete output file: " + outputPath.getPath());
+                }
+                else {
+                  CompilerManagerImpl.addDeletedPath(outputPath.getPath());
+                }
+              }
+              continue;
+            }
+            wereFilesDeleted[0] = true;
+
+            // update zip here
+            //final String outputDir = myOutputFinder.lookupOutputPath(outputPath);
+            //if (outputDir != null) {
+            //  try {
+            //    context.updateZippedOuput(outputDir, FileUtil.toSystemIndependentName(outputPath.getPath()).substring(outputDir.length() + 1));
+            //  }
+            //  catch (IOException e) {
+            //    LOG.info(e);
+            //  }
+            //}
+
+            final String className = trinity.getSecond();
+            if (className != null) {
+              final int id = dependencyCache.getSymbolTable().getId(className);
+              dependencyCache.addTraverseRoot(id);
+              final boolean sourcePresent = trinity.getThird().booleanValue();
+              if (!sourcePresent) {
+                dependencyCache.markSourceRemoved(id);
+              }
+            }
+            if (isTestMode) {
+              CompilerManagerImpl.addDeletedPath(outputPath.getPath());
+            }
+          }
+        }
+        finally {
+          CompilerUtil.logDuration("Sync output directory", System.currentTimeMillis() - start);
+          CompilerUtil.refreshIOFiles(filesToRefresh);
+        }
+      }
+    });
+    return wereFilesDeleted[0];
+  }
+
+  // [mike] performance optimization - this method is accessed > 15,000 times in Aurora
+  private String getModuleOutputPath(final Module module, boolean inTestSourceContent) {
+    final Map<Module, String> map = inTestSourceContent ? myModuleTestOutputPaths : myModuleOutputPaths;
+    String path = map.get(module);
+    if (path == null) {
+      path = CompilerPaths.getModuleOutputPath(module, inTestSourceContent);
+      map.put(module, path);
+    }
+
+    return path;
+  }
+
+  private boolean processFiles(final FileProcessingCompilerAdapter adapter,
+                               final boolean forceCompile,
+                               final boolean checkScope,
+                               final boolean onlyCheckStatus, final CacheDeferredUpdater cacheUpdater) throws ExitException, IOException {
+    final CompileContextEx context = (CompileContextEx)adapter.getCompileContext();
+    final FileProcessingCompilerStateCache cache = getFileProcessingCompilerCache(adapter.getCompiler());
+    final FileProcessingCompiler.ProcessingItem[] items = adapter.getProcessingItems();
+    if (context.getMessageCount(CompilerMessageCategory.ERROR) > 0) {
+      return false;
+    }
+    if (LOG.isDebugEnabled() && items.length > 0) {
+      LOG.debug("Start processing files by " + adapter.getCompiler().getDescription());
+    }
+    final CompileScope scope = context.getCompileScope();
+    final List<FileProcessingCompiler.ProcessingItem> toProcess = new ArrayList<FileProcessingCompiler.ProcessingItem>();
+    final Set<String> allUrls = new HashSet<String>();
+    final IOException[] ex = {null};
+    DumbService.getInstance(myProject).waitForSmartMode();
+    ApplicationManager.getApplication().runReadAction(new Runnable() {
+      public void run() {
+        try {
+          for (FileProcessingCompiler.ProcessingItem item : items) {
+            final VirtualFile file = item.getFile();
+            final String url = file.getUrl();
+            allUrls.add(url);
+            if (!forceCompile && cache.getTimestamp(url) == file.getTimeStamp()) {
+              final ValidityState state = cache.getExtState(url);
+              final ValidityState itemState = item.getValidityState();
+              if (state != null ? state.equalsTo(itemState) : itemState == null) {
+                continue;
+              }
+            }
+            if (LOG.isDebugEnabled()) {
+              LOG.debug("Adding item to process: " + url + "; saved ts= " + cache.getTimestamp(url) + "; VFS ts=" + file.getTimeStamp());
+            }
+            toProcess.add(item);
+          }
+        }
+        catch (IOException e) {
+          ex[0] = e;
+        }
+      }
+    });
+
+    if (ex[0] != null) {
+      throw ex[0];
+    }
+
+    final Collection<String> urls = cache.getUrls();
+    final List<String> urlsToRemove = new ArrayList<String>();
+    if (!urls.isEmpty()) {
+      CompilerUtil.runInContext(context, CompilerBundle.message("progress.processing.outdated.files"), new ThrowableRunnable<IOException>(){
+        public void run() throws IOException {
+          ApplicationManager.getApplication().runReadAction(new Runnable() {
+            public void run() {
+              for (final String url : urls) {
+                if (!allUrls.contains(url)) {
+                  if (!checkScope || scope.belongs(url)) {
+                    urlsToRemove.add(url);
+                  }
+                }
+              }
+            }
+          });
+          if (!onlyCheckStatus && !urlsToRemove.isEmpty()) {
+            for (final String url : urlsToRemove) {
+              adapter.processOutdatedItem(context, url, cache.getExtState(url));
+              cache.remove(url);
+            }
+          }
+        }
+      });
+    }
+
+    if (onlyCheckStatus) {
+      if (urlsToRemove.isEmpty() && toProcess.isEmpty()) {
+        return false;
+      }
+      if (LOG.isDebugEnabled()) {
+        if (!urlsToRemove.isEmpty()) {
+          LOG.debug("Found urls to remove, compiler " + adapter.getCompiler().getDescription());
+          for (String url : urlsToRemove) {
+            LOG.debug("\t" + url);
+          }
+        }
+        if (!toProcess.isEmpty()) {
+          LOG.debug("Found items to compile, compiler " + adapter.getCompiler().getDescription());
+          for (FileProcessingCompiler.ProcessingItem item : toProcess) {
+            LOG.debug("\t" + item.getFile().getPresentableUrl());
+          }
+        }
+      }
+      throw new ExitException(ExitStatus.CANCELLED);
+    }
+
+    if (toProcess.isEmpty()) {
+      return false;
+    }
+
+    final FileProcessingCompiler.ProcessingItem[] processed =
+      adapter.process(toProcess.toArray(new FileProcessingCompiler.ProcessingItem[toProcess.size()]));
+
+    if (processed.length == 0) {
+      return true;
+    }
+    CompilerUtil.runInContext(context, CompilerBundle.message("progress.updating.caches"), new ThrowableRunnable<IOException>() {
+      public void run() {
+        final List<VirtualFile> vFiles = new ArrayList<VirtualFile>(processed.length);
+        for (FileProcessingCompiler.ProcessingItem aProcessed : processed) {
+          final VirtualFile file = aProcessed.getFile();
+          vFiles.add(file);
+          if (LOG.isDebugEnabled()) {
+            LOG.debug("\tFile processed " + file.getPresentableUrl() + "; ts=" + file.getTimeStamp());
+          }
+
+          //final String path = file.getPath();
+          //final String outputDir = myOutputFinder.lookupOutputPath(path);
+          //if (outputDir != null) {
+          //  context.updateZippedOuput(outputDir, path.substring(outputDir.length() + 1));
+          //}
+        }
+        LocalFileSystem.getInstance().refreshFiles(vFiles);
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Files after VFS refresh:");
+          for (VirtualFile file : vFiles) {
+            LOG.debug("\t" + file.getPresentableUrl() + "; ts=" + file.getTimeStamp());
+          }
+        }
+        for (FileProcessingCompiler.ProcessingItem item : processed) {
+          cacheUpdater.addFileForUpdate(item, cache);
+        }
+      }
+    });
+    return true;
+  }
+
+  private FileProcessingCompilerStateCache getFileProcessingCompilerCache(FileProcessingCompiler compiler) throws IOException {
+    return CompilerCacheManager.getInstance(myProject).getFileProcessingCompilerCache(compiler);
+  }
+
+  private StateCache<ValidityState> getGeneratingCompilerCache(final GeneratingCompiler compiler) throws IOException {
+    return CompilerCacheManager.getInstance(myProject).getGeneratingCompilerCache(compiler);
+  }
+
+  public void executeCompileTask(final CompileTask task, final CompileScope scope, final String contentName, final Runnable onTaskFinished) {
+    final CompilerTask progressManagerTask =
+      new CompilerTask(myProject, contentName, false, false);
+    final CompileContextImpl compileContext = new CompileContextImpl(myProject, progressManagerTask, scope, null, false, false);
+
+    FileDocumentManager.getInstance().saveAllDocuments();
+
+    progressManagerTask.start(new Runnable() {
+      public void run() {
+        try {
+          task.execute(compileContext);
+        }
+        catch (ProcessCanceledException ex) {
+          // suppressed
+        }
+        finally {
+          if (onTaskFinished != null) {
+            onTaskFinished.run();
+          }
+        }
+      }
+    }, null);
+  }
+
+  private boolean executeCompileTasks(final CompileContext context, final boolean beforeTasks) {
+    final CompilerManager manager = CompilerManager.getInstance(myProject);
+    final ProgressIndicator progressIndicator = context.getProgressIndicator();
+    progressIndicator.pushState();
+    try {
+      CompileTask[] tasks = beforeTasks ? manager.getBeforeTasks() : manager.getAfterTasks();
+      if (tasks.length > 0) {
+        progressIndicator.setText(beforeTasks
+                                  ? CompilerBundle.message("progress.executing.precompile.tasks")
+                                  : CompilerBundle.message("progress.executing.postcompile.tasks"));
+        for (CompileTask task : tasks) {
+          if (!task.execute(context)) {
+            return false;
+          }
+        }
+      }
+    }
+    finally {
+      progressIndicator.popState();
+      WindowManager.getInstance().getStatusBar(myProject).setInfo("");
+      if (progressIndicator instanceof CompilerTask) {
+        ApplicationManager.getApplication().invokeLater(new Runnable() {
+          public void run() {
+            ((CompilerTask)progressIndicator).showCompilerContent();
+          }
+        });
+      }
+    }
+    return true;
+  }
+
+  private boolean validateCompilerConfiguration(final CompileScope scope, boolean checkOutputAndSourceIntersection) {
+    try {
+      final Module[] scopeModules = scope.getAffectedModules()/*ModuleManager.getInstance(myProject).getModules()*/;
+      final List<String> modulesWithoutOutputPathSpecified = new ArrayList<String>();
+      boolean isProjectCompilePathSpecified = true;
+      final List<String> modulesWithoutJdkAssigned = new ArrayList<String>();
+      final Set<File> nonExistingOutputPaths = new HashSet<File>();
+      final CompilerConfiguration config = CompilerConfiguration.getInstance(myProject);
+      final CompilerManager compilerManager = CompilerManager.getInstance(myProject);
+      final boolean useOutOfProcessBuild = useOutOfProcessBuild();
+      for (final Module module : scopeModules) {
+        if (!compilerManager.isValidationEnabled(module)) {
+          continue;
+        }
+        final boolean hasSources = hasSources(module, false);
+        final boolean hasTestSources = hasSources(module, true);
+        if (!hasSources && !hasTestSources) {
+          // If module contains no sources, shouldn't have to select JDK or output directory (SCR #19333)
+          // todo still there may be problems with this approach if some generated files are attributed by this module
+          continue;
+        }
+        final Sdk jdk = ModuleRootManager.getInstance(module).getSdk();
+        if (jdk == null) {
+          modulesWithoutJdkAssigned.add(module.getName());
+        }
+        final String outputPath = getModuleOutputPath(module, false);
+        final String testsOutputPath = getModuleOutputPath(module, true);
+        if (outputPath == null && testsOutputPath == null) {
+          modulesWithoutOutputPathSpecified.add(module.getName());
+        }
+        else {
+          if (outputPath != null) {
+            if (!useOutOfProcessBuild) {
+              final File file = new File(outputPath.replace('/', File.separatorChar));
+              if (!file.exists()) {
+                nonExistingOutputPaths.add(file);
+              }
+            }
+          }
+          else {
+            if (hasSources) {
+              modulesWithoutOutputPathSpecified.add(module.getName());
+            }
+          }
+          if (testsOutputPath != null) {
+            if (!useOutOfProcessBuild) {
+              final File f = new File(testsOutputPath.replace('/', File.separatorChar));
+              if (!f.exists()) {
+                nonExistingOutputPaths.add(f);
+              }
+            }
+          }
+          else {
+            if (hasTestSources) {
+              modulesWithoutOutputPathSpecified.add(module.getName());
+            }
+          }
+          if (!useOutOfProcessBuild) {
+            if (config.getAnnotationProcessingConfiguration(module).isEnabled()) {
+              final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module);
+              if (path == null) {
+                final CompilerProjectExtension extension = CompilerProjectExtension.getInstance(module.getProject());
+                if (extension == null || extension.getCompilerOutputUrl() == null) {
+                  isProjectCompilePathSpecified = false;
+                }
+                else {
+                  modulesWithoutOutputPathSpecified.add(module.getName());
+                }
+              }
+              else {
+                final File file = new File(path);
+                if (!file.exists()) {
+                  nonExistingOutputPaths.add(file);
+                }
+              }
+            }
+          }
+        }
+      }
+      if (!modulesWithoutJdkAssigned.isEmpty()) {
+        showNotSpecifiedError("error.jdk.not.specified", modulesWithoutJdkAssigned, ProjectBundle.message("modules.classpath.title"));
+        return false;
+      }
+
+      if (!isProjectCompilePathSpecified) {
+        final String message = CompilerBundle.message("error.project.output.not.specified");
+        if (ApplicationManager.getApplication().isUnitTestMode()) {
+          LOG.error(message);
+        }
+  
+        Messages.showMessageDialog(myProject, message, CommonBundle.getErrorTitle(), Messages.getErrorIcon());
+        ProjectSettingsService.getInstance(myProject).openProjectSettings();
+        return false;
+      }
+
+      if (!modulesWithoutOutputPathSpecified.isEmpty()) {
+        showNotSpecifiedError("error.output.not.specified", modulesWithoutOutputPathSpecified, CommonContentEntriesEditor.NAME);
+        return false;
+      }
+
+      if (!nonExistingOutputPaths.isEmpty()) {
+        for (File file : nonExistingOutputPaths) {
+          final boolean succeeded = file.mkdirs();
+          if (!succeeded) {
+            if (file.exists()) {
+              // for overlapping paths, this one might have been created as an intermediate path on a previous iteration
+              continue;
+            }
+            Messages.showMessageDialog(myProject, CompilerBundle.message("error.failed.to.create.directory", file.getPath()),
+                                       CommonBundle.getErrorTitle(), Messages.getErrorIcon());
+            return false;
+          }
+        }
+        final Boolean refreshSuccess =
+          new WriteAction<Boolean>() {
+            @Override
+            protected void run(Result<Boolean> result) throws Throwable {
+              LocalFileSystem.getInstance().refreshIoFiles(nonExistingOutputPaths);
+              Boolean res = Boolean.TRUE;
+              for (File file : nonExistingOutputPaths) {
+                if (LocalFileSystem.getInstance().findFileByIoFile(file) == null) {
+                  res = Boolean.FALSE;
+                  break;
+                }
+              }
+              result.setResult(res);
+            }
+          }.execute().getResultObject();
+  
+        if (!refreshSuccess.booleanValue()) {
+          return false;
+        }
+        dropScopesCaches();
+      }
+
+      if (checkOutputAndSourceIntersection && myShouldClearOutputDirectory) {
+        if (!validateOutputAndSourcePathsIntersection()) {
+          return false;
+        }
+        // myShouldClearOutputDirectory may change in validateOutputAndSourcePathsIntersection()
+        CompilerPathsEx.CLEAR_ALL_OUTPUTS_KEY.set(scope, myShouldClearOutputDirectory);
+      }
+      else {
+        CompilerPathsEx.CLEAR_ALL_OUTPUTS_KEY.set(scope, false);
+      }
+      final List<Chunk<Module>> chunks = ModuleCompilerUtil.getSortedModuleChunks(myProject, Arrays.asList(scopeModules));
+      for (final Chunk<Module> chunk : chunks) {
+        final Set<Module> chunkModules = chunk.getNodes();
+        if (chunkModules.size() <= 1) {
+          continue; // no need to check one-module chunks
+        }
+        for (Module chunkModule : chunkModules) {
+          if (config.getAnnotationProcessingConfiguration(chunkModule).isEnabled()) {
+            showCyclesNotSupportedForAnnotationProcessors(chunkModules.toArray(new Module[chunkModules.size()]));
+            return false;
+          }
+        }
+        Sdk jdk = null;
+        LanguageLevel languageLevel = null;
+        for (final Module module : chunkModules) {
+          final Sdk moduleJdk = ModuleRootManager.getInstance(module).getSdk();
+          if (jdk == null) {
+            jdk = moduleJdk;
+          }
+          else {
+            if (!jdk.equals(moduleJdk)) {
+              showCyclicModulesHaveDifferentJdksError(chunkModules.toArray(new Module[chunkModules.size()]));
+              return false;
+            }
+          }
+  
+          LanguageLevel moduleLanguageLevel = LanguageLevelUtil.getEffectiveLanguageLevel(module);
+          if (languageLevel == null) {
+            languageLevel = moduleLanguageLevel;
+          }
+          else {
+            if (!languageLevel.equals(moduleLanguageLevel)) {
+              showCyclicModulesHaveDifferentLanguageLevel(chunkModules.toArray(new Module[chunkModules.size()]));
+              return false;
+            }
+          }
+        }
+      }
+      if (!useOutOfProcessBuild) {
+        final Compiler[] allCompilers = compilerManager.getCompilers(Compiler.class);
+        for (Compiler compiler : allCompilers) {
+          if (!compiler.validateConfiguration(scope)) {
+            return false;
+          }
+        }
+      }
+      return true;
+    }
+    catch (Throwable e) {
+      LOG.info(e);
+      return false;
+    }
+  }
+
+  private boolean useOutOfProcessBuild() {
+    return CompilerWorkspaceConfiguration.getInstance(myProject).useOutOfProcessBuild();
+  }
+
+  private void showCyclicModulesHaveDifferentLanguageLevel(Module[] modulesInChunk) {
+    LOG.assertTrue(modulesInChunk.length > 0);
+    String moduleNameToSelect = modulesInChunk[0].getName();
+    final String moduleNames = getModulesString(modulesInChunk);
+    Messages.showMessageDialog(myProject, CompilerBundle.message("error.chunk.modules.must.have.same.language.level", moduleNames),
+                               CommonBundle.getErrorTitle(), Messages.getErrorIcon());
+    showConfigurationDialog(moduleNameToSelect, null);
+  }
+
+  private void showCyclicModulesHaveDifferentJdksError(Module[] modulesInChunk) {
+    LOG.assertTrue(modulesInChunk.length > 0);
+    String moduleNameToSelect = modulesInChunk[0].getName();
+    final String moduleNames = getModulesString(modulesInChunk);
+    Messages.showMessageDialog(myProject, CompilerBundle.message("error.chunk.modules.must.have.same.jdk", moduleNames),
+                               CommonBundle.getErrorTitle(), Messages.getErrorIcon());
+    showConfigurationDialog(moduleNameToSelect, null);
+  }
+
+  private void showCyclesNotSupportedForAnnotationProcessors(Module[] modulesInChunk) {
+    LOG.assertTrue(modulesInChunk.length > 0);
+    String moduleNameToSelect = modulesInChunk[0].getName();
+    final String moduleNames = getModulesString(modulesInChunk);
+    Messages.showMessageDialog(myProject, CompilerBundle.message("error.annotation.processing.not.supported.for.module.cycles", moduleNames),
+                               CommonBundle.getErrorTitle(), Messages.getErrorIcon());
+    showConfigurationDialog(moduleNameToSelect, null);
+  }
+
+  private static String getModulesString(Module[] modulesInChunk) {
+    final StringBuilder moduleNames = StringBuilderSpinAllocator.alloc();
+    try {
+      for (Module module : modulesInChunk) {
+        if (moduleNames.length() > 0) {
+          moduleNames.append("\n");
+        }
+        moduleNames.append("\"").append(module.getName()).append("\"");
+      }
+      return moduleNames.toString();
+    }
+    finally {
+      StringBuilderSpinAllocator.dispose(moduleNames);
+    }
+  }
+
+  private static boolean hasSources(Module module, boolean checkTestSources) {
+    final ContentEntry[] contentEntries = ModuleRootManager.getInstance(module).getContentEntries();
+    for (final ContentEntry contentEntry : contentEntries) {
+      final SourceFolder[] sourceFolders = contentEntry.getSourceFolders();
+      for (final SourceFolder sourceFolder : sourceFolders) {
+        if (sourceFolder.getFile() == null) {
+          continue; // skip invalid source folders
+        }
+        if (checkTestSources) {
+          if (sourceFolder.isTestSource()) {
+            return true;
+          }
+        }
+        else {
+          if (!sourceFolder.isTestSource()) {
+            return true;
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  private void showNotSpecifiedError(@NonNls final String resourceId, List<String> modules, String editorNameToSelect) {
+    String nameToSelect = null;
+    final StringBuilder names = StringBuilderSpinAllocator.alloc();
+    final String message;
+    try {
+      final int maxModulesToShow = 10;
+      for (String name : modules.size() > maxModulesToShow ? modules.subList(0, maxModulesToShow) : modules) {
+        if (nameToSelect == null) {
+          nameToSelect = name;
+        }
+        if (names.length() > 0) {
+          names.append(",\n");
+        }
+        names.append("\"");
+        names.append(name);
+        names.append("\"");
+      }
+      if (modules.size() > maxModulesToShow) {
+        names.append(",\n...");
+      }
+      message = CompilerBundle.message(resourceId, modules.size(), names.toString());
+    }
+    finally {
+      StringBuilderSpinAllocator.dispose(names);
+    }
+
+    if (ApplicationManager.getApplication().isUnitTestMode()) {
+      LOG.error(message);
+    }
+
+    Messages.showMessageDialog(myProject, message, CommonBundle.getErrorTitle(), Messages.getErrorIcon());
+    showConfigurationDialog(nameToSelect, editorNameToSelect);
+  }
+
+  private boolean validateOutputAndSourcePathsIntersection() {
+    final Module[] allModules = ModuleManager.getInstance(myProject).getModules();
+    List<VirtualFile> allOutputs = new ArrayList<VirtualFile>();
+    ContainerUtil.addAll(allOutputs, CompilerPathsEx.getOutputDirectories(allModules));
+    for (Artifact artifact : ArtifactManager.getInstance(myProject).getArtifacts()) {
+      ContainerUtil.addIfNotNull(artifact.getOutputFile(), allOutputs);
+    }
+    final Set<VirtualFile> affectedOutputPaths = new HashSet<VirtualFile>();
+    CompilerUtil.computeIntersectingPaths(myProject, allOutputs, affectedOutputPaths);
+    affectedOutputPaths.addAll(ArtifactCompilerUtil.getArtifactOutputsContainingSourceFiles(myProject));
+
+    if (!affectedOutputPaths.isEmpty()) {
+      if (CompilerUtil.askUserToContinueWithNoClearing(myProject, affectedOutputPaths)) {
+        myShouldClearOutputDirectory = false;
+        return true;
+      }
+      else {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  private void showConfigurationDialog(String moduleNameToSelect, String tabNameToSelect) {
+    ProjectSettingsService.getInstance(myProject).showModuleConfigurationDialog(moduleNameToSelect, tabNameToSelect);
+  }
+
+  private static VirtualFile lookupVFile(final LocalFileSystem lfs, final String path) {
+    final File file = new File(path);
+
+    VirtualFile vFile = lfs.findFileByIoFile(file);
+    if (vFile != null) {
+      return vFile;
+    }
+
+    final boolean justCreated = file.mkdirs();
+    vFile = lfs.refreshAndFindFileByIoFile(file);
+
+    if (vFile == null) {
+      assert false: "Virtual file not found for " + file.getPath() + "; mkdirs() exit code is " + justCreated + "; file exists()? " + file.exists();
+    }
+
+    return vFile;
+  }
+
+  private static class CacheDeferredUpdater {
+    private final Map<VirtualFile, List<Pair<FileProcessingCompilerStateCache, FileProcessingCompiler.ProcessingItem>>> myData = new java.util.HashMap<VirtualFile, List<Pair<FileProcessingCompilerStateCache, FileProcessingCompiler.ProcessingItem>>>();
+
+    public void addFileForUpdate(final FileProcessingCompiler.ProcessingItem item, FileProcessingCompilerStateCache cache) {
+      final VirtualFile file = item.getFile();
+      List<Pair<FileProcessingCompilerStateCache, FileProcessingCompiler.ProcessingItem>> list = myData.get(file);
+      if (list == null) {
+        list = new ArrayList<Pair<FileProcessingCompilerStateCache, FileProcessingCompiler.ProcessingItem>>();
+        myData.put(file, list);
+      }
+      list.add(new Pair<FileProcessingCompilerStateCache, FileProcessingCompiler.ProcessingItem>(cache, item));
+    }
+
+    public void doUpdate() throws IOException{
+      final IOException[] ex = {null};
+      ApplicationManager.getApplication().runReadAction(new Runnable() {
+        public void run() {
+          try {
+            for (Map.Entry<VirtualFile, List<Pair<FileProcessingCompilerStateCache, FileProcessingCompiler.ProcessingItem>>> entry : myData.entrySet()) {
+              for (Pair<FileProcessingCompilerStateCache, FileProcessingCompiler.ProcessingItem> pair : entry.getValue()) {
+                final FileProcessingCompiler.ProcessingItem item = pair.getSecond();
+                pair.getFirst().update(entry.getKey(), item.getValidityState());
+              }
+            }
+          }
+          catch (IOException e) {
+            ex[0] = e;
+          }
+        }
+      });
+      if (ex[0] != null) {
+        throw ex[0];
+      }
+    }
+  }
+
+  private static class TranslatorsOutputSink implements TranslatingCompiler.OutputSink {
+    final Map<String, Collection<TranslatingCompiler.OutputItem>> myPostponedItems = new HashMap<String, Collection<TranslatingCompiler.OutputItem>>();
+    private final CompileContextEx myContext;
+    private final TranslatingCompiler[] myCompilers;
+    private int myCurrentCompilerIdx;
+    private final Set<VirtualFile> myCompiledSources = new HashSet<VirtualFile>();
+    //private LinkedBlockingQueue<Future> myFutures = new LinkedBlockingQueue<Future>();
+
+    private TranslatorsOutputSink(CompileContextEx context, TranslatingCompiler[] compilers) {
+      myContext = context;
+      myCompilers = compilers;
+    }
+
+    public void setCurrentCompilerIndex(int index) {
+      myCurrentCompilerIdx = index;
+    }
+
+    public Set<VirtualFile> getCompiledSources() {
+      return Collections.unmodifiableSet(myCompiledSources);
+    }
+
+    public void add(final String outputRoot, final Collection<TranslatingCompiler.OutputItem> items, final VirtualFile[] filesToRecompile) {
+      for (TranslatingCompiler.OutputItem item : items) {
+        final VirtualFile file = item.getSourceFile();
+        if (file != null) {
+          myCompiledSources.add(file);
+        }
+      }
+      final TranslatingCompiler compiler = myCompilers[myCurrentCompilerIdx];
+      if (compiler instanceof IntermediateOutputCompiler) {
+        final LocalFileSystem lfs = LocalFileSystem.getInstance();
+        final List<VirtualFile> outputs = new ArrayList<VirtualFile>();
+        for (TranslatingCompiler.OutputItem item : items) {
+          final VirtualFile vFile = lfs.findFileByPath(item.getOutputPath());
+          if (vFile != null) {
+            outputs.add(vFile);
+          }
+        }
+        myContext.markGenerated(outputs);
+      }
+      final int nextCompilerIdx = myCurrentCompilerIdx + 1;
+      try {
+        if (nextCompilerIdx < myCompilers.length ) {
+          final Map<String, Collection<TranslatingCompiler.OutputItem>> updateNow = new java.util.HashMap<String, Collection<TranslatingCompiler.OutputItem>>();
+          // process postponed
+          for (Map.Entry<String, Collection<TranslatingCompiler.OutputItem>> entry : myPostponedItems.entrySet()) {
+            final String outputDir = entry.getKey();
+            final Collection<TranslatingCompiler.OutputItem> postponed = entry.getValue();
+            for (Iterator<TranslatingCompiler.OutputItem> it = postponed.iterator(); it.hasNext();) {
+              TranslatingCompiler.OutputItem item = it.next();
+              boolean shouldPostpone = false;
+              for (int idx = nextCompilerIdx; idx < myCompilers.length; idx++) {
+                shouldPostpone = myCompilers[idx].isCompilableFile(item.getSourceFile(), myContext);
+                if (shouldPostpone) {
+                  break;
+                }
+              }
+              if (!shouldPostpone) {
+                // the file is not compilable by the rest of compilers, so it is safe to update it now
+                it.remove();
+                addItemToMap(updateNow, outputDir, item);
+              }
+            }
+          }
+          // process items from current compilation
+          for (TranslatingCompiler.OutputItem item : items) {
+            boolean shouldPostpone = false;
+            for (int idx = nextCompilerIdx; idx < myCompilers.length; idx++) {
+              shouldPostpone = myCompilers[idx].isCompilableFile(item.getSourceFile(), myContext);
+              if (shouldPostpone) {
+                break;
+              }
+            }
+            if (shouldPostpone) {
+              // the file is compilable by the next compiler in row, update should be postponed
+              addItemToMap(myPostponedItems, outputRoot, item);
+            }
+            else {
+              addItemToMap(updateNow, outputRoot, item);
+            }
+          }
+
+          if (updateNow.size() == 1) {
+            final Map.Entry<String, Collection<TranslatingCompiler.OutputItem>> entry = updateNow.entrySet().iterator().next();
+            final String outputDir = entry.getKey();
+            final Collection<TranslatingCompiler.OutputItem> itemsToUpdate = entry.getValue();
+            TranslatingCompilerFilesMonitor.getInstance().update(myContext, outputDir, itemsToUpdate, filesToRecompile);
+          }
+          else {
+            for (Map.Entry<String, Collection<TranslatingCompiler.OutputItem>> entry : updateNow.entrySet()) {
+              final String outputDir = entry.getKey();
+              final Collection<TranslatingCompiler.OutputItem> itemsToUpdate = entry.getValue();
+              TranslatingCompilerFilesMonitor.getInstance().update(myContext, outputDir, itemsToUpdate, VirtualFile.EMPTY_ARRAY);
+            }
+            if (filesToRecompile.length > 0) {
+              TranslatingCompilerFilesMonitor.getInstance().update(myContext, null, Collections.<TranslatingCompiler.OutputItem>emptyList(), filesToRecompile);
+            }
+          }
+        }
+        else {
+          TranslatingCompilerFilesMonitor.getInstance().update(myContext, outputRoot, items, filesToRecompile);
+        }
+      }
+      catch (IOException e) {
+        LOG.info(e);
+        myContext.requestRebuildNextTime(e.getMessage());
+      }
+    }
+
+    private static void addItemToMap(Map<String, Collection<TranslatingCompiler.OutputItem>> map, String outputDir, TranslatingCompiler.OutputItem item) {
+      Collection<TranslatingCompiler.OutputItem> collection = map.get(outputDir);
+      if (collection == null) {
+        collection = new ArrayList<TranslatingCompiler.OutputItem>();
+        map.put(outputDir, collection);
+      }
+      collection.add(item);
+    }
+
+    public void flushPostponedItems() {
+      final TranslatingCompilerFilesMonitor filesMonitor = TranslatingCompilerFilesMonitor.getInstance();
+      try {
+        for (Map.Entry<String, Collection<TranslatingCompiler.OutputItem>> entry : myPostponedItems.entrySet()) {
+          final String outputDir = entry.getKey();
+          final Collection<TranslatingCompiler.OutputItem> items = entry.getValue();
+          filesMonitor.update(myContext, outputDir, items, VirtualFile.EMPTY_ARRAY);
+        }
+      }
+      catch (IOException e) {
+        LOG.info(e);
+        myContext.requestRebuildNextTime(e.getMessage());
+      }
+    }
+  }
+
+  private static class DependentClassesCumulativeFilter implements Function<Pair<int[], Set<VirtualFile>>, Pair<int[], Set<VirtualFile>>> {
+
+    private final TIntHashSet myProcessedNames = new TIntHashSet();
+    private final Set<VirtualFile> myProcessedFiles = new HashSet<VirtualFile>();
+
+    public Pair<int[], Set<VirtualFile>> fun(Pair<int[], Set<VirtualFile>> deps) {
+      final TIntHashSet currentDeps = new TIntHashSet(deps.getFirst());
+      currentDeps.removeAll(myProcessedNames.toArray());
+      myProcessedNames.addAll(deps.getFirst());
+
+      final Set<VirtualFile> depFiles = new HashSet<VirtualFile>(deps.getSecond());
+      depFiles.removeAll(myProcessedFiles);
+      myProcessedFiles.addAll(deps.getSecond());
+      return new Pair<int[], Set<VirtualFile>>(currentDeps.toArray(), depFiles);
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileScopeUtil.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileScopeUtil.java
new file mode 100644
index 0000000..102db7a
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileScopeUtil.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 com.intellij.compiler.impl;
+
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage.TargetTypeBuildScope;
+import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType;
+
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class CompileScopeUtil {
+  public static void addScopesForModules(Collection<Module> modules, List<TargetTypeBuildScope> scopes) {
+    if (!modules.isEmpty()) {
+      for (JavaModuleBuildTargetType type : JavaModuleBuildTargetType.ALL_TYPES) {
+        TargetTypeBuildScope.Builder builder = TargetTypeBuildScope.newBuilder().setTypeId(type.getTypeId());
+        for (Module module : modules) {
+          builder.addTargetId(module.getName());
+        }
+        scopes.add(builder.build());
+      }
+    }
+  }
+
+  public static List<TargetTypeBuildScope> mergeScopes(List<TargetTypeBuildScope> scopes1, List<TargetTypeBuildScope> scopes2) {
+    if (scopes2.isEmpty()) return scopes1;
+    if (scopes1.isEmpty()) return scopes2;
+
+    Map<String, TargetTypeBuildScope> scopeById = new HashMap<String, TargetTypeBuildScope>();
+    mergeScopes(scopeById, scopes1);
+    mergeScopes(scopeById, scopes2);
+    return new ArrayList<TargetTypeBuildScope>(scopeById.values());
+  }
+
+  private static void mergeScopes(Map<String, TargetTypeBuildScope> scopeById, List<TargetTypeBuildScope> scopes) {
+    for (TargetTypeBuildScope scope : scopes) {
+      String id = scope.getTypeId();
+      TargetTypeBuildScope old = scopeById.get(id);
+      if (old == null) {
+        scopeById.put(id, scope);
+      }
+      else {
+        scopeById.put(id, mergeScope(old, scope));
+      }
+    }
+  }
+
+  private static TargetTypeBuildScope mergeScope(TargetTypeBuildScope scope1, TargetTypeBuildScope scope2) {
+    if (scope1.getAllTargets()) return scope1;
+    if (scope2.getAllTargets()) return scope2;
+    return TargetTypeBuildScope.newBuilder()
+      .setTypeId(scope1.getTypeId())
+      .addAllTargetId(scope1.getTargetIdList())
+      .addAllTargetId(scope2.getTargetIdList())
+      .build();
+  }
+
+  public static boolean allProjectModulesAffected(CompileContextImpl compileContext) {
+    final Set<Module> allModules = new HashSet<Module>(Arrays.asList(compileContext.getProjectCompileScope().getAffectedModules()));
+    allModules.removeAll(Arrays.asList(compileContext.getCompileScope().getAffectedModules()));
+    return allModules.isEmpty();
+  }
+
+  public static List<String> fetchFiles(CompileContextImpl context) {
+    if (context.isRebuild()) {
+      return Collections.emptyList();
+    }
+    final CompileScope scope = context.getCompileScope();
+    if (shouldFetchFiles(scope)) {
+      final List<String> paths = new ArrayList<String>();
+      for (VirtualFile file : scope.getFiles(null, true)) {
+        paths.add(file.getPath());
+      }
+      return paths;
+    }
+    return Collections.emptyList();
+  }
+
+  private static boolean shouldFetchFiles(CompileScope scope) {
+    if (scope instanceof CompositeScope) {
+      for (CompileScope compileScope : ((CompositeScope)scope).getScopes()) {
+        if (shouldFetchFiles(compileScope)) {
+          return true;
+        }
+      }
+    }
+    return scope instanceof OneProjectItemCompileScope || scope instanceof FileSetCompileScope;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompilerCacheManager.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerCacheManager.java
new file mode 100644
index 0000000..5c147ee
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerCacheManager.java
@@ -0,0 +1,187 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import com.intellij.compiler.impl.generic.GenericCompilerCache;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.compiler.*;
+import com.intellij.openapi.compiler.Compiler;
+import com.intellij.openapi.compiler.generic.GenericCompiler;
+import com.intellij.openapi.components.ProjectComponent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.io.FileUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: May 4, 2008
+ */
+public class CompilerCacheManager implements ProjectComponent {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.CompilerCacheManager");
+  private final Map<Compiler, Object> myCompilerToCacheMap = new HashMap<Compiler, Object>();
+  private final Map<GenericCompiler<?,?,?>, GenericCompilerCache<?,?,?>> myGenericCachesMap = new HashMap<GenericCompiler<?,?,?>, GenericCompilerCache<?,?,?>>();
+  private final List<Disposable> myCacheDisposables = new ArrayList<Disposable>();
+  private final File myCachesRoot;
+  private final Project myProject;
+
+  public CompilerCacheManager(Project project) {
+    myProject = project;
+    myCachesRoot = CompilerPaths.getCacheStoreDirectory(project);
+  }
+
+  public static CompilerCacheManager getInstance(Project project) {
+    return project.getComponent(CompilerCacheManager.class);
+  }
+  
+  public void projectOpened() {
+  }
+
+  public void projectClosed() {
+  }
+
+  @NotNull
+  public String getComponentName() {
+    return "CompilerCacheManager";
+  }
+
+  public void initComponent() {
+  }
+
+  public void disposeComponent() {
+    flushCaches();
+  }
+  
+  private File getCompilerRootDir(final Compiler compiler) {
+    final File dir = new File(myCachesRoot, getCompilerIdString(compiler));
+    dir.mkdirs();
+    return dir;
+  }
+
+  public synchronized <Key, SourceState, OutputState> GenericCompilerCache<Key, SourceState, OutputState>
+                                 getGenericCompilerCache(final GenericCompiler<Key, SourceState, OutputState> compiler) throws IOException {
+    GenericCompilerCache<?,?,?> cache = myGenericCachesMap.get(compiler);
+    if (cache == null) {
+      final GenericCompilerCache<?,?,?> genericCache = new GenericCompilerCache<Key, SourceState, OutputState>(compiler, GenericCompilerRunner
+        .getGenericCompilerCacheDir(myProject, compiler));
+      myGenericCachesMap.put(compiler, genericCache);
+      myCacheDisposables.add(new Disposable() {
+        public void dispose() {
+          if (LOG.isDebugEnabled()) {
+            LOG.debug("Closing cache for feneric compiler " + compiler.getId());
+          }
+          genericCache.close();
+        }
+      });
+      cache = genericCache;
+    }
+    //noinspection unchecked
+    return (GenericCompilerCache<Key, SourceState, OutputState>)cache;
+  }
+
+  public synchronized FileProcessingCompilerStateCache getFileProcessingCompilerCache(final FileProcessingCompiler compiler) throws IOException {
+    Object cache = myCompilerToCacheMap.get(compiler);
+    if (cache == null) {
+      final File compilerRootDir = getCompilerRootDir(compiler);
+      final FileProcessingCompilerStateCache stateCache = new FileProcessingCompilerStateCache(compilerRootDir, compiler);
+      myCompilerToCacheMap.put(compiler, stateCache);
+      myCacheDisposables.add(new Disposable() {
+        public void dispose() {
+          if (LOG.isDebugEnabled()) {
+            LOG.debug("Closing cache for compiler " + compiler.getDescription() + "; cache root dir: " + compilerRootDir);
+          }
+          stateCache.close();
+        }
+      });
+      cache = stateCache;
+    }
+    else {
+      LOG.assertTrue(cache instanceof FileProcessingCompilerStateCache);
+    }
+    return (FileProcessingCompilerStateCache)cache;
+  }
+
+  public synchronized StateCache<ValidityState> getGeneratingCompilerCache(final GeneratingCompiler compiler) throws IOException {
+    Object cache = myCompilerToCacheMap.get(compiler);
+    if (cache == null) {
+      final File cacheDir = getCompilerRootDir(compiler);
+      final StateCache<ValidityState> stateCache = new StateCache<ValidityState>(new File(cacheDir, "timestamps")) {
+        public ValidityState read(DataInput stream) throws IOException {
+          return compiler.createValidityState(stream);
+        }
+  
+        public void write(ValidityState validityState, DataOutput out) throws IOException {
+          validityState.save(out);
+        }
+      };
+      myCompilerToCacheMap.put(compiler, stateCache);
+      myCacheDisposables.add(new Disposable() {
+        public void dispose() {
+          try {
+            stateCache.close();
+          }
+          catch (IOException e) {
+            LOG.info(e);
+          }
+        }
+      });
+      cache = stateCache;
+    }
+    return (StateCache<ValidityState>)cache;
+  }
+
+  public static String getCompilerIdString(Compiler compiler) {
+    @NonNls String description = compiler.getDescription();
+    return description.replaceAll("\\s+", "_").replaceAll("[\\.\\?]", "_").toLowerCase();
+  }
+  
+  public synchronized void flushCaches() {
+    for (Disposable disposable : myCacheDisposables) {
+      try {
+        disposable.dispose();
+      }
+      catch (Throwable e) {
+        LOG.info(e);
+      }
+    }
+    myCacheDisposables.clear();
+    myGenericCachesMap.clear();
+    myCompilerToCacheMap.clear();
+  }
+
+  public void clearCaches(final CompileContext context) {
+    flushCaches();
+    final File[] children = myCachesRoot.listFiles();
+    if (children != null) {
+      for (final File child : children) {
+        final boolean deleteOk = FileUtil.delete(child);
+        if (!deleteOk) {
+          context.addMessage(CompilerMessageCategory.ERROR, CompilerBundle.message("compiler.error.failed.to.delete", child.getPath()), null, -1, -1);
+        }
+      }
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompilerContentIterator.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerContentIterator.java
new file mode 100644
index 0000000..c763480
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerContentIterator.java
@@ -0,0 +1,49 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.roots.ContentIterator;
+import com.intellij.openapi.roots.FileIndex;
+import com.intellij.openapi.vfs.VirtualFile;
+
+import java.util.Collection;
+
+public class CompilerContentIterator implements ContentIterator {
+  final FileTypeManager fileTypeManager = FileTypeManager.getInstance();
+  private final FileType myFileType;
+  private final FileIndex myFileIndex;
+  private final boolean myInSourceOnly;
+  private final Collection<VirtualFile> myFiles;
+
+  public CompilerContentIterator(FileType fileType, FileIndex fileIndex, boolean inSourceOnly, Collection<VirtualFile> files) {
+    myFileType = fileType;
+    myFileIndex = fileIndex;
+    myInSourceOnly = inSourceOnly;
+    myFiles = files;
+  }
+
+  public boolean processFile(VirtualFile fileOrDir) {
+    if (fileOrDir.isDirectory()) return true;
+    if (!fileOrDir.isInLocalFileSystem()) return true;
+    if (myInSourceOnly && !myFileIndex.isInSourceContent(fileOrDir)) return true;
+    if (myFileType == null || myFileType == fileOrDir.getFileType()) {
+      myFiles.add(fileOrDir);
+    }
+    return true;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompilerEncodingServiceImpl.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerEncodingServiceImpl.java
new file mode 100644
index 0000000..d50063d
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerEncodingServiceImpl.java
@@ -0,0 +1,134 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import com.intellij.compiler.CompilerEncodingService;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.encoding.EncodingProjectManager;
+import com.intellij.openapi.vfs.encoding.EncodingProjectManagerImpl;
+import com.intellij.psi.util.CachedValue;
+import com.intellij.psi.util.CachedValueProvider;
+import com.intellij.psi.util.CachedValuesManager;
+import com.intellij.util.containers.ContainerUtil;
+import gnu.trove.THashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class CompilerEncodingServiceImpl extends CompilerEncodingService {
+  @NotNull private final Project myProject;
+  private final CachedValue<Map<Module, Set<Charset>>> myModuleFileEncodings;
+
+  public CompilerEncodingServiceImpl(@NotNull Project project) {
+    myProject = project;
+    myModuleFileEncodings = CachedValuesManager.getManager(project).createCachedValue(new CachedValueProvider<Map<Module, Set<Charset>>>() {
+      @Override
+      public Result<Map<Module, Set<Charset>>> compute() {
+        Map<Module, Set<Charset>> result = computeModuleCharsetMap();
+        return Result.create(result, ProjectRootManager.getInstance(myProject),
+                             ((EncodingProjectManagerImpl)EncodingProjectManager.getInstance(myProject)).getModificationTracker());
+      }
+    }, false);
+  }
+
+  private Map<Module, Set<Charset>> computeModuleCharsetMap() {
+    final Map<Module, Set<Charset>> map = new THashMap<Module, Set<Charset>>();
+    final Map<VirtualFile, Charset> mappings = EncodingProjectManager.getInstance(myProject).getAllMappings();
+    ProjectFileIndex index = ProjectRootManager.getInstance(myProject).getFileIndex();
+    final CompilerManager compilerManager = CompilerManager.getInstance(myProject);
+    for (Map.Entry<VirtualFile, Charset> entry : mappings.entrySet()) {
+      final VirtualFile file = entry.getKey();
+      final Charset charset = entry.getValue();
+      if (file == null || charset == null || (!file.isDirectory() && !compilerManager.isCompilableFileType(file.getFileType()))
+          || !index.isInSourceContent(file)) continue;
+
+      final Module module = index.getModuleForFile(file);
+      if (module == null) continue;
+
+      Set<Charset> set = map.get(module);
+      if (set == null) {
+        set = new LinkedHashSet<Charset>();
+        map.put(module, set);
+
+        final VirtualFile sourceRoot = index.getSourceRootForFile(file);
+        VirtualFile current = file.getParent();
+        Charset parentCharset = null;
+        while (current != null) {
+          final Charset currentCharset = mappings.get(current);
+          if (currentCharset != null) {
+            parentCharset = currentCharset;
+          }
+          if (current.equals(sourceRoot)) {
+            break;
+          }
+          current = current.getParent();
+        }
+        if (parentCharset != null) {
+          set.add(parentCharset);
+        }
+      }
+      set.add(charset);
+    }
+    //todo[nik,jeka] perhaps we should take into account encodings of source roots only not individual files
+    for (Module module : ModuleManager.getInstance(myProject).getModules()) {
+      for (VirtualFile file : ModuleRootManager.getInstance(module).getSourceRoots(true)) {
+        Charset encoding = EncodingProjectManager.getInstance(myProject).getEncoding(file, true);
+        if (encoding != null) {
+          Set<Charset> charsets = map.get(module);
+          if (charsets == null) {
+            charsets = new LinkedHashSet<Charset>();
+            map.put(module, charsets);
+          }
+          charsets.add(encoding);
+        }
+      }
+    }
+    
+    return map;
+  }
+
+  @Override
+  @Nullable
+  public Charset getPreferredModuleEncoding(@NotNull Module module) {
+    final Set<Charset> encodings = myModuleFileEncodings.getValue().get(module);
+    return ContainerUtil.getFirstItem(encodings, EncodingProjectManager.getInstance(myProject).getDefaultCharset());
+  }
+
+  @NotNull
+  @Override
+  public Collection<Charset> getAllModuleEncodings(@NotNull Module module) {
+    final Set<Charset> encodings = myModuleFileEncodings.getValue().get(module);
+    if (encodings != null) {
+      return encodings;
+    }
+    return ContainerUtil.createMaybeSingletonList(EncodingProjectManager.getInstance(myProject).getDefaultCharset());
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompilerErrorTreeView.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerErrorTreeView.java
new file mode 100644
index 0000000..d9fea53
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerErrorTreeView.java
@@ -0,0 +1,182 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import com.intellij.codeInsight.daemon.impl.actions.SuppressFix;
+import com.intellij.codeInsight.daemon.impl.actions.SuppressForClassFix;
+import com.intellij.compiler.CompilerWorkspaceConfiguration;
+import com.intellij.compiler.HelpID;
+import com.intellij.ide.errorTreeView.ErrorTreeElement;
+import com.intellij.ide.errorTreeView.NavigatableMessageElement;
+import com.intellij.ide.errorTreeView.NewErrorTreeViewPanel;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.module.LanguageLevelUtil;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtilCore;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.JavaSdk;
+import com.intellij.openapi.projectRoots.JavaSdkVersion;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.pom.Navigatable;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.psi.*;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+
+public class CompilerErrorTreeView extends NewErrorTreeViewPanel {
+  public CompilerErrorTreeView(Project project, Runnable rerunAction) {
+    super(project, HelpID.COMPILER, true, true, rerunAction);
+  }
+
+  protected void fillRightToolbarGroup(DefaultActionGroup group) {
+    super.fillRightToolbarGroup(group);
+    group.add(new CompilerPropertiesAction());
+  }
+
+  protected void addExtraPopupMenuActions(DefaultActionGroup group) {
+    group.add(new ExcludeFromCompileAction(myProject, this));
+    group.addSeparator();
+    group.add(new SuppressJavacWarningsAction());
+    group.add(new SuppressJavacWarningForClassAction());
+    group.addSeparator();
+    ActionGroup popupGroup = (ActionGroup)ActionManager.getInstance().getAction(IdeActions.GROUP_COMPILER_ERROR_VIEW_POPUP);
+    if (popupGroup != null) {
+      for (AnAction action : popupGroup.getChildren(null)) {
+        group.add(action);
+      }
+    }
+  }
+
+  protected boolean shouldShowFirstErrorInEditor() {
+    return CompilerWorkspaceConfiguration.getInstance(myProject).AUTO_SHOW_ERRORS_IN_EDITOR;
+  }
+
+  private class SuppressJavacWarningsAction extends AnAction {
+    public void actionPerformed(final AnActionEvent e) {
+      final NavigatableMessageElement messageElement = (NavigatableMessageElement)getSelectedErrorTreeElement();
+      final String[] text = messageElement.getText();
+      final String id = text[0].substring(1, text[0].indexOf("]"));
+      final SuppressFix suppressInspectionFix = getSuppressAction(id);
+      final Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
+      assert project != null;
+      final OpenFileDescriptor navigatable = (OpenFileDescriptor)messageElement.getNavigatable();
+      final PsiFile file = PsiManager.getInstance(project).findFile(navigatable.getFile());
+      assert file != null;
+      CommandProcessor.getInstance().executeCommand(project, new Runnable() {
+        public void run() {
+          ApplicationManager.getApplication().runWriteAction(new Runnable() {
+            public void run() {
+              try {
+                suppressInspectionFix.invoke(project, null, file.findElementAt(navigatable.getOffset()));
+              }
+              catch (IncorrectOperationException e1) {
+                LOG.error(e1);
+              }
+            }
+          });
+        }
+      }, suppressInspectionFix.getText(), null);
+    }
+
+    @Override
+    public void update(final AnActionEvent e) {
+      final Presentation presentation = e.getPresentation();
+      presentation.setVisible(false);
+      presentation.setEnabled(false);
+      final Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
+      if (project == null) {
+        return;
+      }
+      final ErrorTreeElement errorTreeElement = getSelectedErrorTreeElement();
+      if (errorTreeElement instanceof NavigatableMessageElement) {
+        final NavigatableMessageElement messageElement = (NavigatableMessageElement)errorTreeElement;
+        final String[] text = messageElement.getText();
+        if (text.length > 0) {
+          if (text[0].startsWith("[") && text[0].indexOf("]") != -1) {
+            final Navigatable navigatable = messageElement.getNavigatable();
+            if (navigatable instanceof OpenFileDescriptor) {
+              final OpenFileDescriptor fileDescriptor = (OpenFileDescriptor)navigatable;
+              final VirtualFile virtualFile = fileDescriptor.getFile();
+              final Module module = ModuleUtilCore.findModuleForFile(virtualFile, project);
+              if (module == null) {
+                return;
+              }
+              final Sdk jdk = ModuleRootManager.getInstance(module).getSdk();
+              if (jdk == null) {
+                return;
+              }
+              final boolean is_1_5 = JavaSdk.getInstance().isOfVersionOrHigher(jdk, JavaSdkVersion.JDK_1_5);
+              if (!is_1_5) {
+                return;
+              }
+              final PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
+              if (psiFile == null) {
+                return;
+              }
+              if (LanguageLevelUtil.getEffectiveLanguageLevel(module).compareTo(LanguageLevel.JDK_1_5) < 0) return;
+              final PsiElement context = psiFile.findElementAt(fileDescriptor.getOffset());
+              if (context == null) {
+                return;
+              }
+              final String id = text[0].substring(1, text[0].indexOf("]"));
+              final SuppressFix suppressInspectionFix = getSuppressAction(id);
+              final boolean available = suppressInspectionFix.isAvailable(project, null, context);
+              presentation.setEnabled(available);
+              presentation.setVisible(available);
+              if (available) {
+                presentation.setText(suppressInspectionFix.getText());
+              }
+            }
+          }
+        }
+      }
+    }
+
+    protected SuppressFix getSuppressAction(final String id) {
+      return new SuppressFix(id) {
+        @Override
+        @SuppressWarnings({"SimplifiableIfStatement"})
+        public boolean isAvailable(@NotNull final Project project, final Editor editor, @NotNull final PsiElement context) {
+          if (getContainer(context) instanceof PsiClass) return false;
+          return super.isAvailable(project, editor, context);
+        }
+
+        @Override
+        protected boolean use15Suppressions(final PsiDocCommentOwner container) {
+          return true;
+        }
+      };
+    }
+  }
+
+  private class SuppressJavacWarningForClassAction extends SuppressJavacWarningsAction {
+    @Override
+    protected SuppressFix getSuppressAction(final String id) {
+      return new SuppressForClassFix(id){
+        @Override
+        protected boolean use15Suppressions(final PsiDocCommentOwner container) {
+          return true;
+        }
+      };
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompilerPropertiesAction.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerPropertiesAction.java
new file mode 100644
index 0000000..5f1bf29
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerPropertiesAction.java
@@ -0,0 +1,42 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import com.intellij.compiler.options.CompilerConfigurable;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.options.ShowSettingsUtil;
+import com.intellij.openapi.project.Project;
+
+/**
+* @author Eugene Zhuravlev
+*         Date: 9/12/12
+*/
+class CompilerPropertiesAction extends AnAction {
+  public CompilerPropertiesAction() {
+    super(CompilerBundle.message("action.compiler.properties.text"), null, AllIcons.General.Settings);
+  }
+
+  public void actionPerformed(AnActionEvent e) {
+    Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
+    if (project != null) {
+      ShowSettingsUtil.getInstance().editConfigurable(project, new CompilerConfigurable(project));
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompilerUtil.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerUtil.java
new file mode 100644
index 0000000..1417fa6
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerUtil.java
@@ -0,0 +1,346 @@
+/*
+ * 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.
+ */
+
+/**
+ * created at Jan 3, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.impl;
+
+import com.intellij.CommonBundle;
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.impl.javaCompiler.ModuleChunk;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.*;
+import com.intellij.openapi.vfs.newvfs.RefreshQueue;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.util.ThrowableRunnable;
+import com.intellij.util.containers.ContainerUtil;
+import gnu.trove.THashMap;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.*;
+
+public class CompilerUtil {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.CompilerUtil");
+
+  public static String quotePath(String path) {
+    if(path != null && path.indexOf(' ') != -1) {
+      path = path.replaceAll("\\\\", "\\\\\\\\");
+      path = '"' + path + '"';
+    }
+    return path;
+  }
+
+  public static void collectFiles(Collection<File> container, File rootDir, FileFilter fileFilter) {
+    final File[] files = rootDir.listFiles(fileFilter);
+    if (files == null) {
+      return;
+    }
+    for (File file : files) {
+      if (file.isDirectory()) {
+        collectFiles(container, file, fileFilter);
+      }
+      else {
+        container.add(file);
+      }
+    }
+  }
+
+  public static Map<Module, List<VirtualFile>> buildModuleToFilesMap(CompileContext context, VirtualFile[] files) {
+    return buildModuleToFilesMap(context, Arrays.asList(files));
+  }
+
+
+  public static Map<Module, List<VirtualFile>> buildModuleToFilesMap(final CompileContext context, final List<VirtualFile> files) {
+    //assertion: all files are different
+    final Map<Module, List<VirtualFile>> map = new THashMap<Module, List<VirtualFile>>();
+    ApplicationManager.getApplication().runReadAction(new Runnable() {
+      public void run() {
+        for (VirtualFile file : files) {
+          final Module module = context.getModuleByFile(file);
+
+          if (module == null) {
+            continue; // looks like file invalidated
+          }
+
+          List<VirtualFile> moduleFiles = map.get(module);
+          if (moduleFiles == null) {
+            moduleFiles = new ArrayList<VirtualFile>();
+            map.put(module, moduleFiles);
+          }
+          moduleFiles.add(file);
+        }
+      }
+    });
+    return map;
+  }
+
+
+  /**
+   * must not be called inside ReadAction
+   * @param files
+   */
+  public static void refreshIOFiles(@NotNull final Collection<File> files) {
+    if (!files.isEmpty()) {
+      LocalFileSystem.getInstance().refreshIoFiles(files);
+    }
+  }
+
+  public static void refreshIODirectories(@NotNull final Collection<File> files) {
+    final LocalFileSystem lfs = LocalFileSystem.getInstance();
+    final List<VirtualFile> filesToRefresh = new ArrayList<VirtualFile>();
+    for (File file : files) {
+      final VirtualFile virtualFile = lfs.refreshAndFindFileByIoFile(file);
+      if (virtualFile != null) {
+        filesToRefresh.add(virtualFile);
+      }
+    }
+    if (!filesToRefresh.isEmpty()) {
+      RefreshQueue.getInstance().refresh(false, true, null, filesToRefresh);
+    }
+  }
+
+  public static void refreshIOFile(final File file) {
+    final VirtualFile vFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file);
+    if (vFile != null) {
+      vFile.refresh(false, false);
+    }
+  }
+
+  public static void addLocaleOptions(final List<String> commandLine, final boolean launcherUsed) {
+    // need to specify default encoding so that javac outputs messages in 'correct' language
+    //noinspection HardCodedStringLiteral
+    commandLine.add((launcherUsed? "-J" : "") + "-D" + CharsetToolkit.FILE_ENCODING_PROPERTY + "=" + CharsetToolkit.getDefaultSystemCharset().name());
+    // javac's VM should use the same default locale that IDEA uses in order for javac to print messages in 'correct' language
+    //noinspection HardCodedStringLiteral
+    final String lang = System.getProperty("user.language");
+    if (lang != null) {
+      //noinspection HardCodedStringLiteral
+      commandLine.add((launcherUsed? "-J" : "") + "-Duser.language=" + lang);
+    }
+    //noinspection HardCodedStringLiteral
+    final String country = System.getProperty("user.country");
+    if (country != null) {
+      //noinspection HardCodedStringLiteral
+      commandLine.add((launcherUsed? "-J" : "") + "-Duser.country=" + country);
+    }
+    //noinspection HardCodedStringLiteral
+    final String region = System.getProperty("user.region");
+    if (region != null) {
+      //noinspection HardCodedStringLiteral
+      commandLine.add((launcherUsed? "-J" : "") + "-Duser.region=" + region);
+    }
+  }
+
+  public static void addTargetCommandLineSwitch(final ModuleChunk chunk, final List<String> commandLine) {
+    String optionValue = null;
+    CompilerConfiguration config = null;
+    final Module[] modules = chunk.getModules();
+    for (Module module : modules) {
+      if (config == null) {
+        config = CompilerConfiguration.getInstance(module.getProject());
+      }
+      final String moduleTarget = config.getBytecodeTargetLevel(module);
+      if (moduleTarget == null) {
+        continue;
+      }
+      if (optionValue == null) {
+        optionValue = moduleTarget;
+      }
+      else {
+        if (moduleTarget.compareTo(optionValue) < 0) {
+          optionValue = moduleTarget; // use the lower possible target among modules that form the chunk
+        }
+      }
+    }
+    if (optionValue != null) {
+      commandLine.add("-target");
+      commandLine.add(optionValue);
+    }
+  }
+
+  public static void addSourceCommandLineSwitch(final Sdk jdk, LanguageLevel chunkLanguageLevel, @NonNls final List<String> commandLine) {
+    final String versionString = jdk.getVersionString();
+    if (StringUtil.isEmpty(versionString)) {
+      throw new IllegalArgumentException(CompilerBundle.message("javac.error.unknown.jdk.version", jdk.getName()));
+    }
+
+    final LanguageLevel applicableLanguageLevel = getApplicableLanguageLevel(versionString, chunkLanguageLevel);
+    if (applicableLanguageLevel.equals(LanguageLevel.JDK_1_8)) {
+      commandLine.add("-source");
+      commandLine.add("8");
+    }
+    else if (applicableLanguageLevel.equals(LanguageLevel.JDK_1_7)) {
+      commandLine.add("-source");
+      commandLine.add("1.7");
+    }
+    else if (applicableLanguageLevel.equals(LanguageLevel.JDK_1_6)) {
+      commandLine.add("-source");
+      commandLine.add("1.6");
+    }
+    else if (applicableLanguageLevel.equals(LanguageLevel.JDK_1_5)) {
+      commandLine.add("-source");
+      commandLine.add("1.5");
+    }
+    else if (applicableLanguageLevel.equals(LanguageLevel.JDK_1_4)) {
+      commandLine.add("-source");
+      commandLine.add("1.4");
+    }
+    else if (applicableLanguageLevel.equals(LanguageLevel.JDK_1_3)) {
+      if (!(isOfVersion(versionString, "1.3") || isOfVersion(versionString, "1.2") || isOfVersion(versionString, "1.1"))) {
+        //noinspection HardCodedStringLiteral
+        commandLine.add("-source");
+        commandLine.add("1.3");
+      }
+    }
+  }
+
+  //todo[nik] rewrite using JavaSdkVersion#getMaxLanguageLevel
+  @NotNull
+  public static LanguageLevel getApplicableLanguageLevel(String versionString, @NotNull LanguageLevel languageLevel) {
+    final boolean is8OrNewer = isOfVersion(versionString, "1.8") || isOfVersion(versionString, "8.0");
+    final boolean is7OrNewer = is8OrNewer || isOfVersion(versionString, "1.7") || isOfVersion(versionString, "7.0");
+    final boolean is6OrNewer = is7OrNewer || isOfVersion(versionString, "1.6") || isOfVersion(versionString, "6.0");
+    final boolean is5OrNewer = is6OrNewer || isOfVersion(versionString, "1.5") || isOfVersion(versionString, "5.0");
+    final boolean is4OrNewer = is5OrNewer || isOfVersion(versionString, "1.4");
+    final boolean is3OrNewer = is4OrNewer || isOfVersion(versionString, "1.3");
+    final boolean is2OrNewer = is3OrNewer || isOfVersion(versionString, "1.2");
+    final boolean is1OrNewer = is2OrNewer || isOfVersion(versionString, "1.0") || isOfVersion(versionString, "1.1");
+    
+    if (!is1OrNewer) {
+      // unknown jdk version, cannot say anything about the corresponding language level, so leave it unchanged
+      return languageLevel;
+    }
+    // now correct the language level to be not higher than jdk used to compile
+    if (LanguageLevel.JDK_1_8.equals(languageLevel) && !is8OrNewer) {
+      languageLevel = LanguageLevel.JDK_1_7;
+    }
+    if (LanguageLevel.JDK_1_7.equals(languageLevel) && !is7OrNewer) {
+      languageLevel = LanguageLevel.JDK_1_6;
+    }
+    if (LanguageLevel.JDK_1_6.equals(languageLevel) && !is6OrNewer) {
+      languageLevel = LanguageLevel.JDK_1_5;
+    }
+    if (LanguageLevel.JDK_1_5.equals(languageLevel) && !is5OrNewer) {
+      languageLevel = LanguageLevel.JDK_1_4;
+    }
+    if (LanguageLevel.JDK_1_4.equals(languageLevel) && !is4OrNewer) {
+      languageLevel = LanguageLevel.JDK_1_3;
+    }
+    return languageLevel;
+  }
+
+  public static boolean isOfVersion(String versionString, String checkedVersion) {
+    return versionString.contains(checkedVersion);
+  }
+
+  public static <T extends Throwable> void runInContext(CompileContext context, String title, ThrowableRunnable<T> action) throws T {
+    if (title != null) {
+      context.getProgressIndicator().pushState();
+      context.getProgressIndicator().setText(title);
+    }
+    try {
+      action.run();
+    }
+    finally {
+      if (title != null) {
+        context.getProgressIndicator().popState();
+      }
+    }
+  }
+
+  public static void logDuration(final String activityName, long duration) {
+    LOG.info(activityName + " took " + duration + " ms: " + duration /60000 + " min " +(duration %60000)/1000 + "sec");
+  }
+
+  public static void clearOutputDirectories(final Collection<File> outputDirectories) {
+    final long start = System.currentTimeMillis();
+    // do not delete directories themselves, or we'll get rootsChanged() otherwise
+    final Collection<File> filesToDelete = new ArrayList<File>(outputDirectories.size() * 2);
+    for (File outputDirectory : outputDirectories) {
+      File[] files = outputDirectory.listFiles();
+      if (files != null) {
+        ContainerUtil.addAll(filesToDelete, files);
+      }
+    }
+    if (filesToDelete.size() > 0) {
+      FileUtil.asyncDelete(filesToDelete);
+
+      // ensure output directories exist
+      for (final File file : outputDirectories) {
+        file.mkdirs();
+      }
+      final long clearStop = System.currentTimeMillis();
+
+      refreshIODirectories(outputDirectories);
+
+      final long refreshStop = System.currentTimeMillis();
+
+      logDuration("Clearing output dirs", clearStop - start);
+      logDuration("Refreshing output directories", refreshStop - clearStop);
+    }
+  }
+
+  public static void computeIntersectingPaths(final Project project,
+                                                          final Collection<VirtualFile> outputPaths,
+                                                          final Collection<VirtualFile> result) {
+    for (Module module : ModuleManager.getInstance(project).getModules()) {
+      final ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
+      final VirtualFile[] sourceRoots = rootManager.getSourceRoots();
+      for (final VirtualFile outputPath : outputPaths) {
+        for (VirtualFile sourceRoot : sourceRoots) {
+          if (VfsUtilCore.isAncestor(outputPath, sourceRoot, true) || VfsUtilCore.isAncestor(sourceRoot, outputPath, false)) {
+            result.add(outputPath);
+          }
+        }
+      }
+    }
+  }
+
+  public static boolean askUserToContinueWithNoClearing(Project project, Collection<VirtualFile> affectedOutputPaths) {
+    final StringBuilder paths = new StringBuilder();
+    for (final VirtualFile affectedOutputPath : affectedOutputPaths) {
+      if (paths.length() > 0) {
+        paths.append(",\n");
+      }
+      paths.append(affectedOutputPath.getPath().replace('/', File.separatorChar));
+    }
+    final int answer = Messages.showOkCancelDialog(project,
+                                                   CompilerBundle.message("warning.sources.under.output.paths", paths.toString()),
+                                                   CommonBundle.getErrorTitle(), Messages.getWarningIcon());
+    if (answer == Messages.OK) { // ok
+      return true;
+    }
+    else {
+      return false;
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompositeScope.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompositeScope.java
new file mode 100644
index 0000000..2d18e44
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompositeScope.java
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: Feb 5, 2003
+ * Time: 4:17:58 PM
+ */
+package com.intellij.compiler.impl;
+
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.ExportableUserDataHolderBase;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.containers.ContainerUtil;
+import gnu.trove.THashSet;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+
+public class CompositeScope extends ExportableUserDataHolderBase implements CompileScope{
+  private final List<CompileScope> myScopes = new ArrayList<CompileScope>();
+
+  public CompositeScope(CompileScope scope1, CompileScope scope2) {
+    addScope(scope1);
+    addScope(scope2);
+  }
+
+  public CompositeScope(CompileScope[] scopes) {
+    for (CompileScope scope : scopes) {
+      addScope(scope);
+    }
+  }
+
+  private void addScope(CompileScope scope) {
+    if (scope instanceof CompositeScope) {
+      final CompositeScope compositeScope = (CompositeScope)scope;
+      for (CompileScope childScope : compositeScope.myScopes) {
+        addScope(childScope);
+      }
+    }
+    else {
+      myScopes.add(scope);
+    }
+  }
+
+  @NotNull
+  public VirtualFile[] getFiles(FileType fileType, boolean inSourceOnly) {
+    Set<VirtualFile> allFiles = new THashSet<VirtualFile>();
+    for (CompileScope scope : myScopes) {
+      final VirtualFile[] files = scope.getFiles(fileType, inSourceOnly);
+      if (files.length > 0) {
+        ContainerUtil.addAll(allFiles, files);
+      }
+    }
+    return VfsUtil.toVirtualFileArray(allFiles);
+  }
+
+  public boolean belongs(String url) {
+    for (CompileScope scope : myScopes) {
+      if (scope.belongs(url)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @NotNull
+  public Module[] getAffectedModules() {
+    Set<Module> modules = new HashSet<Module>();
+    for (final CompileScope compileScope : myScopes) {
+      ContainerUtil.addAll(modules, compileScope.getAffectedModules());
+    }
+    return modules.toArray(new Module[modules.size()]);
+  }
+
+  public <T> T getUserData(@NotNull Key<T> key) {
+    for (CompileScope compileScope : myScopes) {
+      T userData = compileScope.getUserData(key);
+      if (userData != null) {
+        return userData;
+      }
+    }
+    return super.getUserData(key);
+  }
+  
+  public Collection<CompileScope> getScopes() {
+    return Collections.unmodifiableList(myScopes);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/ExcludeFromCompileAction.java b/java/compiler/impl/src/com/intellij/compiler/impl/ExcludeFromCompileAction.java
new file mode 100644
index 0000000..91055db
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/ExcludeFromCompileAction.java
@@ -0,0 +1,78 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.CompilerConfigurationImpl;
+import com.intellij.ide.errorTreeView.ErrorTreeElement;
+import com.intellij.ide.errorTreeView.ErrorTreeNodeDescriptor;
+import com.intellij.ide.errorTreeView.GroupingElement;
+import com.intellij.ide.errorTreeView.NewErrorTreeViewPanel;
+import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.compiler.options.ExcludeEntryDescription;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vcs.FileStatusManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.Nullable;
+
+/**
+* @author Eugene Zhuravlev
+*         Date: 9/12/12
+*/
+class ExcludeFromCompileAction extends AnAction {
+  private final Project myProject;
+  private final NewErrorTreeViewPanel myErrorTreeView;
+
+  public ExcludeFromCompileAction(Project project, NewErrorTreeViewPanel errorTreeView) {
+    super(CompilerBundle.message("actions.exclude.from.compile.text"));
+    myProject = project;
+    myErrorTreeView = errorTreeView;
+  }
+
+  public void actionPerformed(AnActionEvent e) {
+    VirtualFile file = getSelectedFile();
+
+    if (file != null && file.isValid()) {
+      ExcludeEntryDescription description = new ExcludeEntryDescription(file, false, true, myProject);
+      ((CompilerConfigurationImpl) CompilerConfiguration.getInstance(myProject)).getExcludedEntriesConfiguration().addExcludeEntryDescription(description);
+      FileStatusManager.getInstance(myProject).fileStatusesChanged();
+    }
+  }
+
+  @Nullable
+  private VirtualFile getSelectedFile() {
+    final ErrorTreeNodeDescriptor descriptor = myErrorTreeView.getSelectedNodeDescriptor();
+    ErrorTreeElement element = descriptor != null? descriptor.getElement() : null;
+    if (element != null && !(element instanceof GroupingElement)) {
+      NodeDescriptor parent = descriptor.getParentDescriptor();
+      if (parent instanceof ErrorTreeNodeDescriptor) {
+        element = ((ErrorTreeNodeDescriptor)parent).getElement();
+      }
+    }
+    return element instanceof GroupingElement? ((GroupingElement)element).getFile() : null;
+  }
+
+  public void update(AnActionEvent e) {
+    final Presentation presentation = e.getPresentation();
+    final boolean isApplicable = getSelectedFile() != null;
+    presentation.setEnabled(isApplicable);
+    presentation.setVisible(isApplicable);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/ExitException.java b/java/compiler/impl/src/com/intellij/compiler/impl/ExitException.java
new file mode 100644
index 0000000..b40d6f4
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/ExitException.java
@@ -0,0 +1,32 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+/**
+* @author Eugene Zhuravlev
+*         Date: 11/24/11
+*/
+public class ExitException extends Exception {
+  private final ExitStatus myStatus;
+
+  public ExitException(ExitStatus status) {
+    myStatus = status;
+  }
+
+  public ExitStatus getExitStatus() {
+    return myStatus;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/ExitStatus.java b/java/compiler/impl/src/com/intellij/compiler/impl/ExitStatus.java
new file mode 100644
index 0000000..d44156b
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/ExitStatus.java
@@ -0,0 +1,39 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import org.jetbrains.annotations.NonNls;
+
+/**
+* @author Eugene Zhuravlev
+*         Date: 11/24/11
+*/
+public class ExitStatus {
+  private final String myName;
+
+  ExitStatus(@NonNls String name) {
+    myName = name;
+  }
+
+  public String toString() {
+    return myName;
+  }
+
+  public static final ExitStatus CANCELLED = new ExitStatus("CANCELLED");
+  public static final ExitStatus ERRORS = new ExitStatus("ERRORS");
+  public static final ExitStatus SUCCESS = new ExitStatus("SUCCESS");
+  public static final ExitStatus UP_TO_DATE = new ExitStatus("UP_TO_DATE");
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/FileIndexCompileScope.java b/java/compiler/impl/src/com/intellij/compiler/impl/FileIndexCompileScope.java
new file mode 100644
index 0000000..8f1f2dd
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/FileIndexCompileScope.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 com.intellij.compiler.impl;
+
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.ExportableUserDataHolderBase;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.roots.FileIndex;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Dec 18, 2003
+ */
+public abstract class FileIndexCompileScope extends ExportableUserDataHolderBase implements CompileScope {
+
+  protected abstract FileIndex[] getFileIndices();
+
+  @NotNull
+  public VirtualFile[] getFiles(final FileType fileType, final boolean inSourceOnly) {
+    final List<VirtualFile> files = new ArrayList<VirtualFile>();
+    final FileIndex[] fileIndices = getFileIndices();
+    for (final FileIndex fileIndex : fileIndices) {
+      fileIndex.iterateContent(new CompilerContentIterator(fileType, fileIndex, inSourceOnly, files));
+    }
+    return VfsUtil.toVirtualFileArray(files);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/FileProcessingCompilerAdapter.java b/java/compiler/impl/src/com/intellij/compiler/impl/FileProcessingCompilerAdapter.java
new file mode 100644
index 0000000..8087b42
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/FileProcessingCompilerAdapter.java
@@ -0,0 +1,54 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.FileProcessingCompiler;
+import com.intellij.openapi.compiler.ValidityState;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Oct 16
+ * @author 2003
+ */
+public class FileProcessingCompilerAdapter {
+  private final CompileContext myCompileContext;
+  private final FileProcessingCompiler myCompiler;
+
+  protected FileProcessingCompilerAdapter(CompileContext compileContext, FileProcessingCompiler compiler) {
+    myCompileContext = compileContext;
+    myCompiler = compiler;
+  }
+
+  public CompileContext getCompileContext() {
+    return myCompileContext;
+  }
+
+  public FileProcessingCompiler.ProcessingItem[] getProcessingItems() {
+    return myCompiler.getProcessingItems(getCompileContext());
+  }
+
+  public FileProcessingCompiler.ProcessingItem[] process(FileProcessingCompiler.ProcessingItem[] items) {
+    return myCompiler.process(getCompileContext(), items);
+  }
+
+  public final FileProcessingCompiler getCompiler() {
+    return myCompiler;
+  }
+
+  public void processOutdatedItem(CompileContext context, String url, @Nullable ValidityState state){}
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/FileProcessingCompilerAdapterTask.java b/java/compiler/impl/src/com/intellij/compiler/impl/FileProcessingCompilerAdapterTask.java
new file mode 100644
index 0000000..f53e4df
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/FileProcessingCompilerAdapterTask.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 com.intellij.compiler.impl;
+
+import com.intellij.compiler.CompilerWorkspaceConfiguration;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.*;
+import com.intellij.openapi.project.DumbService;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.ThrowableRunnable;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This is an adapter for running any FileProcessingCompiler as a compiler task
+ *
+ *
+ * @author Eugene Zhuravlev
+ *         Date: 9/5/12
+ */
+public class FileProcessingCompilerAdapterTask implements CompileTask{
+  private final FileProcessingCompiler myCompiler;
+
+  public FileProcessingCompilerAdapterTask(FileProcessingCompiler compiler) {
+    myCompiler = compiler;
+  }
+
+  public FileProcessingCompiler getCompiler() {
+    return myCompiler;
+  }
+
+  @Override
+  public boolean execute(CompileContext context) {
+    final Project project = context.getProject();
+    if (!CompilerWorkspaceConfiguration.getInstance(project).useOutOfProcessBuild()) {
+      return true;
+    }
+    try {
+      final FileProcessingCompiler.ProcessingItem[] items = myCompiler.getProcessingItems(context);
+      if (items.length == 0) {
+        return true;
+      }
+      final List<FileProcessingCompiler.ProcessingItem> toProcess = new ArrayList<FileProcessingCompiler.ProcessingItem>();
+      final Ref<IOException> ex = new Ref<IOException>(null);
+
+      DumbService.getInstance(project).waitForSmartMode();
+
+      final FileProcessingCompilerStateCache cache = CompilerCacheManager.getInstance(project).getFileProcessingCompilerCache(myCompiler);
+      final boolean isMake = context.isMake();
+      ApplicationManager.getApplication().runReadAction(new Runnable() {
+        public void run() {
+          try {
+            for (FileProcessingCompiler.ProcessingItem item : items) {
+              final VirtualFile file = item.getFile();
+              final String url = file.getUrl();
+              if (isMake && cache.getTimestamp(url) == file.getTimeStamp()) {
+                final ValidityState state = cache.getExtState(url);
+                final ValidityState itemState = item.getValidityState();
+                if (state != null ? state.equalsTo(itemState) : itemState == null) {
+                  continue;
+                }
+              }
+              toProcess.add(item);
+            }
+          }
+          catch (IOException e) {
+            ex.set(e);
+          }
+        }
+      });
+
+      if (ex.get() != null) {
+        throw ex.get();
+      }
+
+      if (toProcess.isEmpty()) {
+        return true;
+      }
+
+      final FileProcessingCompiler.ProcessingItem[] processed = myCompiler.process(context, toProcess.toArray(new FileProcessingCompiler.ProcessingItem[toProcess.size()]));
+
+      if (processed.length == 0) {
+        return true;
+      }
+
+      CompilerUtil.runInContext(context, CompilerBundle.message("progress.updating.caches"), new ThrowableRunnable<IOException>() {
+        public void run() throws IOException{
+          final List<VirtualFile> vFiles = new ArrayList<VirtualFile>(processed.length);
+          for (FileProcessingCompiler.ProcessingItem item : processed) {
+            vFiles.add(item.getFile());
+          }
+          LocalFileSystem.getInstance().refreshFiles(vFiles);
+          for (FileProcessingCompiler.ProcessingItem item : processed) {
+            cache.update(item.getFile(), item.getValidityState());
+          }
+        }
+      });
+    }
+    catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+    return true;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/FileProcessingCompilerStateCache.java b/java/compiler/impl/src/com/intellij/compiler/impl/FileProcessingCompilerStateCache.java
new file mode 100644
index 0000000..050002d
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/FileProcessingCompilerStateCache.java
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: Apr 1, 2003
+ * Time: 1:17:50 PM
+ */
+package com.intellij.compiler.impl;
+
+import com.intellij.openapi.compiler.ValidityState;
+import com.intellij.openapi.compiler.ValidityStateFactory;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.*;
+import java.util.Collection;
+
+public class FileProcessingCompilerStateCache {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.FileProcessingCompilerStateCache");
+  private final StateCache<MyState> myCache;
+
+  public FileProcessingCompilerStateCache(File storeDirectory, final ValidityStateFactory stateFactory) throws IOException {
+    myCache = new StateCache<MyState>(new File(storeDirectory, "timestamps")) {
+      public MyState read(DataInput stream) throws IOException {
+        return new MyState(stream.readLong(), stateFactory.createValidityState(stream));
+      }
+
+      public void write(MyState state, DataOutput out) throws IOException {
+        out.writeLong(state.getTimestamp());
+        final ValidityState extState = state.getExtState();
+        if (extState != null) {
+          extState.save(out);
+        }
+      }
+    };
+  }
+
+  public void update(VirtualFile sourceFile, ValidityState extState) throws IOException {
+    if (sourceFile.isValid()) {
+      // only mark as up-to-date if the file did not become invalid during make
+      myCache.update(sourceFile.getUrl(), new MyState(sourceFile.getTimeStamp(), extState));
+    }
+  }
+
+  public void remove(String url) throws IOException {
+    myCache.remove(url);
+  }
+
+  public long getTimestamp(String url) throws IOException {
+    final Serializable savedState = myCache.getState(url);
+    if (savedState != null) {
+      LOG.assertTrue(savedState instanceof MyState);
+    }
+    MyState state = (MyState)savedState;
+    return (state != null)? state.getTimestamp() : -1L;
+  }
+
+  public ValidityState getExtState(String url) throws IOException {
+    MyState state = myCache.getState(url);
+    return (state != null)? state.getExtState() : null;
+  }
+
+  public void force() {
+    myCache.force();
+  }
+
+  public Collection<String> getUrls() throws IOException {
+    return myCache.getUrls();
+  }
+
+  public boolean wipe() {
+    return myCache.wipe();
+  }
+
+  public void close() {
+    try {
+      myCache.close();
+    }
+    catch (IOException ignored) {
+      LOG.info(ignored);
+    }
+  }
+
+  private static class MyState implements Serializable {
+    private final long myTimestamp;
+    private final ValidityState myExtState;
+
+    public MyState(long timestamp, @Nullable ValidityState extState) {
+      myTimestamp = timestamp;
+      myExtState = extState;
+    }
+
+    public long getTimestamp() {
+      return myTimestamp;
+    }
+
+    public @Nullable ValidityState getExtState() {
+      return myExtState;
+    }
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/FileSetCompileScope.java b/java/compiler/impl/src/com/intellij/compiler/impl/FileSetCompileScope.java
new file mode 100644
index 0000000..4a10ad3
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/FileSetCompileScope.java
@@ -0,0 +1,128 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.ExportableUserDataHolderBase;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileVisitor;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+
+/**
+ * @author Eugene Zhuravlev
+ * @since Jan 20, 2003
+ */
+public class FileSetCompileScope extends ExportableUserDataHolderBase implements CompileScope {
+  private final Set<VirtualFile> myRootFiles = new HashSet<VirtualFile>();
+  private final Set<String> myDirectoryUrls = new HashSet<String>();
+  private Set<String> myUrls = null; // urls caching
+  private final Module[] myAffectedModules;
+
+  public FileSetCompileScope(final Collection<VirtualFile> files, Module[] modules) {
+    myAffectedModules = modules;
+    ApplicationManager.getApplication().runReadAction(
+      new Runnable() {
+        public void run() {
+          for (VirtualFile file : files) {
+            assert file != null;
+            addFile(file);
+          }
+        }
+      }
+    );
+  }
+
+  @NotNull
+  public Module[] getAffectedModules() {
+    return myAffectedModules;
+  }
+
+  public Collection<VirtualFile> getRootFiles() {
+    return Collections.unmodifiableCollection(myRootFiles);
+  }
+
+  @NotNull
+  public VirtualFile[] getFiles(final FileType fileType, boolean inSourceOnly) {
+    final List<VirtualFile> files = new ArrayList<VirtualFile>();
+    for (Iterator<VirtualFile> it = myRootFiles.iterator(); it.hasNext();) {
+      VirtualFile file = it.next();
+      if (!file.isValid()) {
+        it.remove();
+        continue;
+      }
+      if (file.isDirectory()) {
+        addRecursively(files, file, fileType);
+      }
+      else {
+        if (fileType == null || fileType.equals(file.getFileType())) {
+          files.add(file);
+        }
+      }
+    }
+    return VfsUtilCore.toVirtualFileArray(files);
+  }
+
+  public boolean belongs(String url) {
+    //url = CompilerUtil.normalizePath(url, '/');
+    if (getUrls().contains(url)) {
+      return true;
+    }
+    for (String directoryUrl : myDirectoryUrls) {
+      if (FileUtil.startsWith(url, directoryUrl)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private Set<String> getUrls() {
+    if (myUrls == null) {
+      myUrls = new HashSet<String>();
+      for (VirtualFile file : myRootFiles) {
+        String url = file.getUrl();
+        myUrls.add(url);
+      }
+    }
+    return myUrls;
+  }
+
+  private void addFile(VirtualFile file) {
+    if (file.isDirectory()) {
+      myDirectoryUrls.add(file.getUrl() + "/");
+    }
+    myRootFiles.add(file);
+    myUrls = null;
+  }
+
+  private static void addRecursively(final Collection<VirtualFile> container, VirtualFile fromDirectory, final FileType fileType) {
+    VfsUtilCore.visitChildrenRecursively(fromDirectory, new VirtualFileVisitor(VirtualFileVisitor.SKIP_ROOT) {
+      @Override
+      public boolean visitFile(@NotNull VirtualFile child) {
+        if (!child.isDirectory() && (fileType == null || fileType.equals(child.getFileType()))) {
+          container.add(child);
+        }
+        return true;
+      }
+    });
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/GenericCompilerRunner.java b/java/compiler/impl/src/com/intellij/compiler/impl/GenericCompilerRunner.java
new file mode 100644
index 0000000..3cba065
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/GenericCompilerRunner.java
@@ -0,0 +1,338 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import com.intellij.compiler.impl.generic.GenericCompilerCache;
+import com.intellij.compiler.impl.generic.GenericCompilerPersistentData;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ReadAction;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.RunResult;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.compiler.CompilerPaths;
+import com.intellij.openapi.compiler.generic.*;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.project.DumbService;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Ref;
+import com.intellij.util.CommonProcessors;
+import com.intellij.util.Processor;
+import com.intellij.util.ThrowableRunnable;
+import com.intellij.util.io.KeyDescriptor;
+import gnu.trove.THashSet;
+import gnu.trove.TObjectHashingStrategy;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class GenericCompilerRunner {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.GenericCompilerRunner");
+  private static final Logger FULL_LOG = Logger.getInstance("#com.intellij.full-generic-compiler-log");
+  private CompileContext myContext;
+  private final boolean myForceCompile;
+  private final boolean myOnlyCheckStatus;
+  private final GenericCompiler<?,?,?>[] myCompilers;
+  private final Project myProject;
+
+  public GenericCompilerRunner(CompileContext context,
+                               boolean forceCompile,
+                               boolean onlyCheckStatus, final GenericCompiler[] compilers) {
+    myContext = context;
+    myForceCompile = forceCompile;
+    myOnlyCheckStatus = onlyCheckStatus;
+    myCompilers = compilers;
+    myProject = myContext.getProject();
+  }
+
+  public boolean invokeCompilers(GenericCompiler.CompileOrderPlace place) throws ExitException {
+    boolean didSomething = false;
+    try {
+      for (GenericCompiler<?,?,?> compiler : myCompilers) {
+        if (compiler.getOrderPlace().equals(place)) {
+          didSomething |= invokeCompiler(compiler);
+        }
+      }
+    }
+    catch (IOException e) {
+      LOG.info(e);
+      myContext.requestRebuildNextTime(e.getMessage());
+      throw new ExitException(ExitStatus.ERRORS);
+    }
+    catch (ExitException e) {
+      throw e;
+    }
+    catch (ProcessCanceledException e) {
+      throw e;
+    }
+    catch (Exception e) {
+      LOG.info(e);
+      myContext.addMessage(CompilerMessageCategory.ERROR, CompilerBundle.message("compiler.error.exception", e.getMessage()), null, -1, -1);
+    }
+    return didSomething;
+  }
+
+  private <Key, SourceState, OutputState> boolean invokeCompiler(GenericCompiler<Key, SourceState, OutputState> compiler) throws IOException,
+                                                                                                                                 ExitException {
+    return invokeCompiler(compiler, compiler.createInstance(myContext));
+  }
+
+  private <T extends BuildTarget, Item extends CompileItem<Key, SourceState, OutputState>, Key, SourceState, OutputState>
+  boolean invokeCompiler(GenericCompiler<Key, SourceState, OutputState> compiler, final GenericCompilerInstance<T, Item, Key, SourceState, OutputState> instance) throws IOException,
+                                                                                                                                                                         ExitException {
+    final GenericCompilerCache<Key, SourceState, OutputState> cache = CompilerCacheManager.getInstance(myProject).getGenericCompilerCache(compiler);
+    GenericCompilerPersistentData
+      data = new GenericCompilerPersistentData(getGenericCompilerCacheDir(myProject, compiler), compiler.getVersion());
+    if (data.isVersionChanged()) {
+      LOG.info("Clearing cache for " + compiler.getDescription());
+      cache.wipe();
+      data.save();
+    }
+
+    final Set<String> targetsToRemove = new HashSet<String>(data.getAllTargets());
+    new ReadAction() {
+      protected void run(final Result result) {
+        for (T target : instance.getAllTargets()) {
+          targetsToRemove.remove(target.getId());
+        }
+      }
+    }.execute();
+
+    if (!myOnlyCheckStatus) {
+      for (final String target : targetsToRemove) {
+        final int id = data.removeId(target);
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Removing obsolete target '" + target + "' (id=" + id + ")");
+        }
+
+        final List<Key> keys = new ArrayList<Key>();
+        CompilerUtil.runInContext(myContext, "Processing obsolete targets...", new ThrowableRunnable<IOException>() {
+          @Override
+          public void run() throws IOException {
+            cache.processSources(id, new CommonProcessors.CollectProcessor<Key>(keys));
+            List<GenericCompilerCacheState<Key, SourceState, OutputState>> obsoleteSources = new ArrayList<GenericCompilerCacheState<Key,SourceState,OutputState>>();
+            for (Key key : keys) {
+              final GenericCompilerCache.PersistentStateData<SourceState, OutputState> state = cache.getState(id, key);
+              obsoleteSources.add(new GenericCompilerCacheState<Key,SourceState,OutputState>(key, state.mySourceState, state.myOutputState));
+            }
+            instance.processObsoleteTarget(target, obsoleteSources);
+          }
+        });
+        checkForErrorsOrCanceled();
+        for (Key key : keys) {
+          cache.remove(id, key);
+        }
+      }
+    }
+
+    final List<T> selectedTargets = new ReadAction<List<T>>() {
+      protected void run(final Result<List<T>> result) {
+        result.setResult(instance.getSelectedTargets());
+      }
+    }.execute().getResultObject();
+
+    boolean didSomething = false;
+    for (T target : selectedTargets) {
+      int id = data.getId(target.getId());
+      didSomething |= processTarget(target, id, compiler, instance, cache);
+    }
+
+    data.save();
+    return didSomething;
+  }
+
+  private void checkForErrorsOrCanceled() throws ExitException {
+    if (myContext.getMessageCount(CompilerMessageCategory.ERROR) > 0) {
+      throw new ExitException(ExitStatus.ERRORS);
+    }
+    if (myContext.getProgressIndicator().isCanceled()) {
+      throw new ExitException(ExitStatus.CANCELLED);
+    }
+  }
+
+  public static File getGenericCompilerCacheDir(Project project, GenericCompiler<?,?,?> compiler) {
+    return new File(CompilerPaths.getCacheStoreDirectory(project), compiler.getId());
+  }
+
+  private <T extends BuildTarget, Item extends CompileItem<Key, SourceState, OutputState>, Key, SourceState, OutputState>
+  boolean processTarget(T target, final int targetId, final GenericCompiler<Key, SourceState, OutputState> compiler, final GenericCompilerInstance<T, Item, Key, SourceState, OutputState> instance,
+                        final GenericCompilerCache<Key, SourceState, OutputState> cache) throws IOException, ExitException {
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Processing target '" + target + "' (id=" + targetId + ") by " + compiler);
+    }
+    final List<Item> items = instance.getItems(target);
+    checkForErrorsOrCanceled();
+
+    final List<GenericCompilerProcessingItem<Item, SourceState, OutputState>> toProcess = new ArrayList<GenericCompilerProcessingItem<Item,SourceState,OutputState>>();
+    final THashSet<Key> keySet = new THashSet<Key>(new SourceItemHashingStrategy<Key>(compiler));
+    final Ref<IOException> exception = Ref.create(null);
+    DumbService.getInstance(myProject).waitForSmartMode();
+    final Map<Item, SourceState> sourceStates = new HashMap<Item,SourceState>();
+    ApplicationManager.getApplication().runReadAction(new Runnable() {
+      @Override
+      public void run() {
+        try {
+          for (Item item : items) {
+            final Key key = item.getKey();
+            keySet.add(key);
+            if (item.isExcluded()) continue;
+
+            final GenericCompilerCache.PersistentStateData<SourceState, OutputState> data = cache.getState(targetId, key);
+            SourceState sourceState = data != null ? data.mySourceState : null;
+            final OutputState outputState = data != null ? data.myOutputState : null;
+            if (myForceCompile || sourceState == null || !item.isSourceUpToDate(sourceState)
+                               || outputState == null || !item.isOutputUpToDate(outputState)) {
+              sourceStates.put(item, item.computeSourceState());
+              toProcess.add(new GenericCompilerProcessingItem<Item,SourceState,OutputState>(item, sourceState, outputState));
+            }
+          }
+        }
+        catch (IOException e) {
+          exception.set(e);
+        }
+      }
+    });
+    if (!exception.isNull()) {
+      throw exception.get();
+    }
+
+    final List<Key> toRemove = new ArrayList<Key>();
+    cache.processSources(targetId, new Processor<Key>() {
+      @Override
+      public boolean process(Key key) {
+        if (!keySet.contains(key)) {
+          toRemove.add(key);
+        }
+        return true;
+      }
+    });
+
+    if (LOG.isDebugEnabled()) {
+      LOG.debug(toProcess.size() + " items will be processed, " + toRemove.size() + " items will be removed");
+      for (int i = 0; i < getItemsCountToShowInLog(toProcess.size()); i++) {
+        LOG.debug("to process:" + toProcess.get(i).getItem().getKey());
+      }
+      for (int i = 0; i < getItemsCountToShowInLog(toRemove.size()); i++) {
+        LOG.debug("to delete:" + toRemove.get(i));
+      }
+    }
+
+    if (toProcess.isEmpty() && toRemove.isEmpty()) {
+      return false;
+    }
+
+    if (myOnlyCheckStatus) {
+      throw new ExitException(ExitStatus.CANCELLED);
+    }
+
+    List<GenericCompilerCacheState<Key, SourceState, OutputState>> obsoleteItems = new ArrayList<GenericCompilerCacheState<Key,SourceState,OutputState>>();
+    for (Key key : toRemove) {
+      final GenericCompilerCache.PersistentStateData<SourceState, OutputState> data = cache.getState(targetId, key);
+      obsoleteItems.add(new GenericCompilerCacheState<Key,SourceState,OutputState>(key, data.mySourceState, data.myOutputState));
+    }
+
+    final List<Item> processedItems = new ArrayList<Item>();
+    final List<File> filesToRefresh = new ArrayList<File>();
+    final List<File> dirsToRefresh = new ArrayList<File>();
+    instance.processItems(target, toProcess, obsoleteItems, new GenericCompilerInstance.OutputConsumer<Item>() {
+      @Override
+      public void addFileToRefresh(@NotNull File file) {
+        filesToRefresh.add(file);
+      }
+
+      @Override
+      public void addDirectoryToRefresh(@NotNull File dir) {
+        dirsToRefresh.add(dir);
+      }
+
+      @Override
+      public void addProcessedItem(@NotNull Item sourceItem) {
+        processedItems.add(sourceItem);
+      }
+    });
+    checkForErrorsOrCanceled();
+
+    CompilerUtil.runInContext(myContext, CompilerBundle.message("progress.updating.caches"), new ThrowableRunnable<IOException>() {
+      @Override
+      public void run() throws IOException {
+        for (Key key : toRemove) {
+          cache.remove(targetId, key);
+        }
+        CompilerUtil.refreshIOFiles(filesToRefresh);
+        CompilerUtil.refreshIODirectories(dirsToRefresh);
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("refreshed " + filesToRefresh.size() + " files and " + dirsToRefresh.size() + " dirs");
+          for (int i = 0; i < getItemsCountToShowInLog(filesToRefresh.size()); i++) {
+            LOG.debug("file: " + filesToRefresh.get(i));
+          }
+          for (int i = 0; i < getItemsCountToShowInLog(dirsToRefresh.size()); i++) {
+            LOG.debug("dir: " + dirsToRefresh.get(i));
+          }
+        }
+
+        final RunResult runResult = new ReadAction() {
+          protected void run(final Result result) throws Throwable {
+            for (Item item : processedItems) {
+              SourceState sourceState = sourceStates.get(item);
+              if (sourceState == null) {
+                sourceState = item.computeSourceState();
+              }
+              cache.putState(targetId, item.getKey(), sourceState, item.computeOutputState());
+            }
+          }
+        }.executeSilently();
+        Throwable throwable = runResult.getThrowable();
+        if (throwable instanceof IOException) throw (IOException) throwable;
+        else if (throwable != null) throw new RuntimeException(throwable);
+      }
+    });
+
+    return true;
+
+  }
+
+  private static int getItemsCountToShowInLog(final int size) {
+    if (size > 100 && !FULL_LOG.isDebugEnabled()) {
+      return 100;
+    }
+    return size;
+  }
+
+  private class SourceItemHashingStrategy<S> implements TObjectHashingStrategy<S> {
+    private KeyDescriptor<S> myKeyDescriptor;
+
+    public SourceItemHashingStrategy(GenericCompiler<S, ?, ?> compiler) {
+      myKeyDescriptor = compiler.getItemKeyDescriptor();
+    }
+
+    @Override
+    public int computeHashCode(S object) {
+      return myKeyDescriptor.getHashCode(object);
+    }
+
+    @Override
+    public boolean equals(S o1, S o2) {
+      return myKeyDescriptor.isEqual(o1, o2);
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/InternedPath.java b/java/compiler/impl/src/com/intellij/compiler/impl/InternedPath.java
new file mode 100644
index 0000000..7914dc2
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/InternedPath.java
@@ -0,0 +1,79 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.util.containers.StringInterner;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Jun 21, 2006
+ */
+public class InternedPath {
+  private final @NotNull List<String> myValue;
+
+  public InternedPath(StringInterner interner, String url, final char separator) {
+    myValue = convert(interner, url, separator);
+  }
+
+  public String toString() {
+    return join(myValue, '/');
+  }
+
+  public static List<String> convert(StringInterner interner, String value, char delim) {
+    final List<String> result = new ArrayList<String>();
+    int start = 0;
+    final int len = value.length();
+    for (int idx = 0; idx < len; idx++) {
+      if (value.charAt(idx) == delim) {
+        result.add(interner.intern(value.substring(start, idx)));
+        start = idx + 1;
+      }
+    }
+    if (start < value.length()) {
+      result.add(interner.intern(value.substring(start)));
+    }
+    if (len > 0 && value.charAt(len-1) == delim) { // ends with delimiter
+      result.add("");
+    }
+    return result;
+  }
+
+  public static String join(List<String> value, char separator) {
+    final int size = value.size();
+    if (size > 1) {
+      final StringBuilder builder = StringBuilderSpinAllocator.alloc();
+      try {
+        builder.append(value.get(0));
+        for (int idx = 1; idx < size; idx++) {
+          builder.append(separator).append(value.get(idx));
+        }
+        return builder.toString();
+      }
+      finally {
+        StringBuilderSpinAllocator.dispose(builder);
+      }
+    }
+    else if (size == 1){
+      return value.get(0);
+    }
+    return "";
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java b/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java
new file mode 100644
index 0000000..3be5efc
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java
@@ -0,0 +1,177 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: Jan 20, 2003
+ * Time: 5:34:19 PM
+ */
+package com.intellij.compiler.impl;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.FileIndex;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class ModuleCompileScope extends FileIndexCompileScope {
+  private final Project myProject;
+  private final Set<Module> myScopeModules;
+  private final Module[] myModules;
+
+  public ModuleCompileScope(final Module module, boolean includeDependentModules) {
+    myProject = module.getProject();
+    myScopeModules = new HashSet<Module>();
+    if (includeDependentModules) {
+      buildScopeModulesSet(module);
+    }
+    else {
+      myScopeModules.add(module);
+    }
+    myModules = ModuleManager.getInstance(myProject).getModules();
+  }
+
+  public ModuleCompileScope(Project project, final Module[] modules, boolean includeDependentModules) {
+    myProject = project;
+    myScopeModules = new HashSet<Module>();
+    for (Module module : modules) {
+      if (module == null) {
+        continue; // prevent NPE
+      }
+      if (includeDependentModules) {
+        buildScopeModulesSet(module);
+      }
+      else {
+        myScopeModules.add(module);
+      }
+    }
+    myModules = ModuleManager.getInstance(myProject).getModules();
+  }
+
+  private void buildScopeModulesSet(Module module) {
+    myScopeModules.add(module);
+    final Module[] dependencies = ModuleRootManager.getInstance(module).getDependencies();
+    for (Module dependency : dependencies) {
+      if (!myScopeModules.contains(dependency)) { // may be in case of module circular dependencies
+        buildScopeModulesSet(dependency);
+      }
+    }
+  }
+
+  @NotNull
+  public Module[] getAffectedModules() {
+    return myScopeModules.toArray(new Module[myScopeModules.size()]);
+  }
+
+  protected FileIndex[] getFileIndices() {
+    final FileIndex[] indices = new FileIndex[myScopeModules.size()];
+    int idx = 0;
+    for (final Module module : myScopeModules) {
+      indices[idx++] = ModuleRootManager.getInstance(module).getFileIndex();
+    }
+    return indices;
+  }
+
+  public boolean belongs(final String url) {
+    if (myScopeModules.isEmpty()) {
+      return false; // optimization
+    }
+    Module candidateModule = null;
+    int maxUrlLength = 0;
+    final ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(myProject).getFileIndex();
+    for (final Module module : myModules) {
+      final String[] contentRootUrls = getModuleContentUrls(module);
+      for (final String contentRootUrl : contentRootUrls) {
+        if (contentRootUrl.length() < maxUrlLength) {
+          continue;
+        }
+        if (!isUrlUnderRoot(url, contentRootUrl)) {
+          continue;
+        }
+        if (contentRootUrl.length() == maxUrlLength) {
+          if (candidateModule == null) {
+            candidateModule = module;
+          }
+          else {
+            // the same content root exists in several modules
+            if (!candidateModule.equals(module)) {
+              candidateModule = ApplicationManager.getApplication().runReadAction(new Computable<Module>() {
+                public Module compute() {
+                  final VirtualFile contentRootFile = VirtualFileManager.getInstance().findFileByUrl(contentRootUrl);
+                  if (contentRootFile != null) {
+                    return projectFileIndex.getModuleForFile(contentRootFile);
+                  }
+                  return null;
+                }
+              });
+            }
+          }
+        }
+        else {
+          maxUrlLength = contentRootUrl.length();
+          candidateModule = module;
+        }
+      }
+    }
+
+    if (candidateModule != null && myScopeModules.contains(candidateModule)) {
+      final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(candidateModule);
+      final String[] excludeRootUrls = moduleRootManager.getExcludeRootUrls();
+      for (String excludeRootUrl : excludeRootUrls) {
+        if (isUrlUnderRoot(url, excludeRootUrl)) {
+          return false;
+        }
+      }
+      final String[] sourceRootUrls = moduleRootManager.getSourceRootUrls();
+      for (String sourceRootUrl : sourceRootUrls) {
+        if (isUrlUnderRoot(url, sourceRootUrl)) {
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  private static boolean isUrlUnderRoot(final String url, final String root) {
+    return (url.length() > root.length()) && url.charAt(root.length()) == '/' && FileUtil.startsWith(url, root);
+  }
+
+  private final Map<Module, String[]> myContentUrlsCache = new HashMap<Module, String[]>();
+
+  private String[] getModuleContentUrls(final Module module) {
+    String[] contentRootUrls = myContentUrlsCache.get(module);
+    if (contentRootUrls == null) {
+      contentRootUrls = ModuleRootManager.getInstance(module).getContentRootUrls();
+      myContentUrlsCache.put(module, contentRootUrls);
+    }
+    return contentRootUrls;
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/OneProjectItemCompileScope.java b/java/compiler/impl/src/com/intellij/compiler/impl/OneProjectItemCompileScope.java
new file mode 100644
index 0000000..4b0b653
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/OneProjectItemCompileScope.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 com.intellij.compiler.impl;
+
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.ExportableUserDataHolderBase;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ContentIterator;
+import com.intellij.openapi.roots.FileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class OneProjectItemCompileScope extends ExportableUserDataHolderBase implements CompileScope{
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.OneProjectItemCompileScope");
+  private final Project myProject;
+  private final VirtualFile myFile;
+  private final String myUrl;
+
+  public OneProjectItemCompileScope(Project project, VirtualFile file) {
+    myProject = project;
+    myFile = file;
+    final String url = file.getUrl();
+    myUrl = file.isDirectory()? url + "/" : url;
+  }
+
+  @NotNull
+  public VirtualFile[] getFiles(final FileType fileType, final boolean inSourceOnly) {
+    final List<VirtualFile> files = new ArrayList<VirtualFile>(1);
+    final FileIndex projectFileIndex = ProjectRootManager.getInstance(myProject).getFileIndex();
+    final ContentIterator iterator = new CompilerContentIterator(fileType, projectFileIndex, inSourceOnly, files);
+    if (myFile.isDirectory()){
+      projectFileIndex.iterateContentUnderDirectory(myFile, iterator);
+    }
+    else{
+      iterator.processFile(myFile);
+    }
+    return VfsUtil.toVirtualFileArray(files);
+  }
+
+  public boolean belongs(String url) {
+    if (myFile.isDirectory()){
+      return FileUtil.startsWith(url, myUrl);
+    }
+    return FileUtil.pathsEqual(url, myUrl);
+  }
+
+  @NotNull
+  public Module[] getAffectedModules() {
+    final Module module = ModuleUtil.findModuleForFile(myFile, myProject);
+    if (module == null) {
+      LOG.error("Module is null for file " + myFile.getPresentableUrl());
+      return Module.EMPTY_ARRAY;
+    }
+    return new Module[] {module};
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/OutputPathFinder.java b/java/compiler/impl/src/com/intellij/compiler/impl/OutputPathFinder.java
new file mode 100644
index 0000000..6a712d1
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/OutputPathFinder.java
@@ -0,0 +1,219 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.io.FileUtil;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Apr 6, 2009
+ */
+public class OutputPathFinder {
+  private final Node myRoot = new Node("");
+
+  public OutputPathFinder(Set<File> outputDirs) {
+    for (File dir : outputDirs) {
+      final String path = FileUtil.toSystemIndependentName(dir.getPath());
+      Node node = myRoot;
+      int idx = path.startsWith("/")? 1 : 0;
+      int slashIndex = path.indexOf('/', idx);
+
+      for (; slashIndex >= 0; slashIndex = path.indexOf('/', idx)) {
+        final String name = path.substring(idx, slashIndex);
+        node = node.addChild(name);
+        idx = slashIndex + 1;
+      }
+
+      if (idx < path.length()) {
+        node = node.addChild(path.substring(idx));
+      }
+
+      node.setData(path);
+    }
+  }
+
+  @Nullable
+  public String lookupOutputPath(File outputFile) {
+    return lookupOutputPath(outputFile.getPath());
+  }
+
+  @Nullable
+  public String lookupOutputPath(String filePath) {
+    final String path = FileUtil.toSystemIndependentName(filePath);
+    Node node = myRoot;
+    int idx = path.startsWith("/")? 1 : 0;
+
+    return findOutputPath(path, idx, node);
+  }
+
+  private static @Nullable String findOutputPath(final String path, int idx, Node node) {
+    while (true) {
+      final int slashIndex = path.indexOf('/', idx);
+      final String name = slashIndex < idx? path.substring(idx) : path.substring(idx, slashIndex);
+      node = node.getChild(name);
+      if (node == null) {
+        return null;
+      }
+      if (node.isOutputRoot()) {
+        if (node.hasChildren() && slashIndex > idx) {
+          final String candidate = findOutputPath(path, slashIndex + 1, node);
+          if (candidate != null) {
+            return candidate;
+          }
+        }
+        return node.getData();
+      }
+      if (slashIndex < 0) {
+        return null;  // end of path reached
+      }
+      idx = slashIndex + 1;
+    }
+  }
+
+  private static class Node {
+    private final String myName;
+    @Nullable
+    private Object myChildren; // either a Node or a Map<String, Node> or a String  or a Pair<String, Node or NodeMap>
+
+    private Node(String name) {
+      myName = name;
+    }
+
+    public String getName() {
+      return myName;
+    }
+
+    public boolean isOutputRoot() {
+      return myChildren instanceof String || myChildren instanceof Pair;
+    }
+
+    public boolean hasChildren() {
+      return myChildren instanceof Map || myChildren instanceof Pair;
+    }
+
+    @Nullable
+    public String getData() {
+      if (myChildren instanceof String) {
+        return (String)myChildren;
+      }
+      if (myChildren instanceof Pair) {
+        //noinspection unchecked
+        return (String)((Pair)myChildren).first;
+      }
+      return null;
+    }
+
+    public void setData(String path) {
+      if (myChildren != null) {
+        if (myChildren instanceof String) {
+          myChildren = path;
+        }
+        else {
+          myChildren = new Pair(path, myChildren instanceof Pair? ((Pair)myChildren).second : myChildren);
+        }
+      }
+      else {
+        myChildren = path;
+      }
+    }
+
+    public Node addChild(String childName) {
+      if (myChildren == null) {
+        final Node node = new Node(childName);
+        myChildren = node;
+        return node;
+      }
+      if (myChildren instanceof String) {
+        final Node node = new Node(childName);
+        myChildren = new Pair(myChildren, node);
+        return node;
+      }
+
+      final Map<String, Node> map;
+      if (myChildren instanceof Map) {
+        map = (Map<String, Node>)myChildren;
+      }
+      else if (myChildren instanceof Node)  {
+        final Node existingChild = (Node)myChildren;
+        myChildren = map = new HashMap<String, Node>();
+        map.put(existingChild.getName(), existingChild);
+      }
+      else { // myChildren is a Pair
+        Object children = ((Pair)myChildren).second;
+        if (children instanceof Map) {
+          map = (Map<String, Node>)children;
+        }
+        else {
+          final Node existingChild = (Node)children;
+          myChildren = new Pair(((Pair)myChildren).first, map = new HashMap<String, Node>());
+          map.put(existingChild.getName(), existingChild);
+        }
+      }
+
+      Node node = map.get(childName);
+      if (node == null) {
+        map.put(childName, node = new Node(childName));
+      }
+      return node;
+    }
+
+    @Nullable
+    public Node getChild(String childName) {
+      final Object children = myChildren instanceof Pair? ((Pair)myChildren).second : myChildren;
+      if (children instanceof Node) {
+        final Node childNode = (Node)children;
+        return childName.equals(childNode.getName())? childNode : null;
+      }
+      if (children instanceof Map) {
+        return ((Map<String, Node>)myChildren).get(childName);
+      }
+      return null;
+    }
+  }
+
+
+  public static void main(String[] args) {
+    final Set<File> set = new HashSet<File>();
+    set.add(new File("/a/b/c"));
+    set.add(new File("a/b/d"));
+    set.add(new File("a/b/e"));
+    set.add(new File("/a/b/f/g"));
+    set.add(new File("/a/b/f/g/zzz"));
+
+    final OutputPathFinder finder = new OutputPathFinder(set);
+
+    System.out.println(finder.lookupOutputPath(new File("a/b")));
+    System.out.println(finder.lookupOutputPath(new File("a/b/c/dir1/dir2/File.class")));
+    System.out.println(finder.lookupOutputPath(new File("a/b/d/dir1/dir2/File.class")));
+    System.out.println(finder.lookupOutputPath(new File("a/b/jjjjj/dir1/dir2/File.class")));
+    System.out.println(finder.lookupOutputPath(new File("a/b/e/File.class")));
+    System.out.println(finder.lookupOutputPath(new File("a/b/File.class")));
+
+    System.out.println(finder.lookupOutputPath(new File("/a/b/f/g/File.class")));
+    System.out.println(finder.lookupOutputPath(new File("/a/b/f/g/ttt/yy/File.class")));
+    System.out.println(finder.lookupOutputPath(new File("/a/b/f/g/zzz/File.class")));
+    System.out.println(finder.lookupOutputPath(new File("/a/b/f/g/zzz/mmm/ttt/File.class")));
+
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/PackagingCompilerAdapter.java b/java/compiler/impl/src/com/intellij/compiler/impl/PackagingCompilerAdapter.java
new file mode 100644
index 0000000..d2f7826
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/PackagingCompilerAdapter.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.
+ */
+package com.intellij.compiler.impl;
+
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.PackagingCompiler;
+import com.intellij.openapi.compiler.ValidityState;
+import org.jetbrains.annotations.Nullable;
+
+public class PackagingCompilerAdapter extends FileProcessingCompilerAdapter{
+  private final PackagingCompiler myCompiler;
+
+  public PackagingCompilerAdapter(CompileContext compileContext, PackagingCompiler compiler) {
+    super(compileContext, compiler);
+    myCompiler = compiler;
+  }
+
+  public void processOutdatedItem(CompileContext context, String url, @Nullable ValidityState state) {
+    myCompiler.processOutdatedItem(context, url, state);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/ProblemsViewImpl.java b/java/compiler/impl/src/com/intellij/compiler/impl/ProblemsViewImpl.java
new file mode 100644
index 0000000..320898a
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/ProblemsViewImpl.java
@@ -0,0 +1,163 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import com.intellij.compiler.ProblemsView;
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.errorTreeView.ErrorTreeElement;
+import com.intellij.ide.errorTreeView.ErrorViewStructure;
+import com.intellij.ide.errorTreeView.GroupingElement;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.openapi.wm.ToolWindowAnchor;
+import com.intellij.openapi.wm.ToolWindowManager;
+import com.intellij.pom.Navigatable;
+import com.intellij.ui.content.Content;
+import com.intellij.ui.content.ContentFactory;
+import com.intellij.util.concurrency.SequentialTaskExecutor;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.UUID;
+import java.util.concurrent.Executor;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: 9/18/12
+ */
+public class ProblemsViewImpl extends ProblemsView{
+  private static final String PROBLEMS_TOOLWINDOW_ID = "Problems";
+  
+  private final ProblemsViewPanel myPanel;
+  private final SequentialTaskExecutor myViewUpdater = new SequentialTaskExecutor(new Executor() {
+    @Override
+    public void execute(Runnable command) {
+      ApplicationManager.getApplication().executeOnPooledThread(command);
+    }
+  });
+
+  public ProblemsViewImpl(final Project project, final ToolWindowManager wm) {
+    super(project);
+    myPanel = new ProblemsViewPanel(project);
+    Disposer.register(project, new Disposable() {
+      @Override
+      public void dispose() {
+        Disposer.dispose(myPanel);
+      }
+    });
+    UIUtil.invokeLaterIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+        if (project.isDisposed()) {
+          return;
+        }
+        final ToolWindow tw = wm.registerToolWindow(PROBLEMS_TOOLWINDOW_ID, false, ToolWindowAnchor.BOTTOM, project);
+        tw.setIcon(AllIcons.Toolwindows.Problems);
+        final Content content = ContentFactory.SERVICE.getInstance().createContent(myPanel, "", false);
+        // todo: setup content?
+        tw.getContentManager().addContent(content);
+        Disposer.register(project, new Disposable() {
+          @Override
+          public void dispose() {
+            tw.getContentManager().removeAllContents(true);
+          }
+        });
+      }
+    });
+  }
+
+  @Override
+  public void clearOldMessages(@Nullable final CompileScope scope, @NotNull final UUID currentSessionId) {
+    myViewUpdater.execute(new Runnable() {
+      @Override
+      public void run() {
+        cleanupChildrenRecursively(myPanel.getErrorViewStructure().getRootElement(), scope, currentSessionId);
+        myPanel.reload();
+      }
+    });
+  }
+
+  private void cleanupChildrenRecursively(@NotNull final Object fromElement, final @Nullable CompileScope scope, @NotNull UUID currentSessionId) {
+    final ErrorViewStructure structure = myPanel.getErrorViewStructure();
+    for (ErrorTreeElement element : structure.getChildElements(fromElement)) {
+      if (element instanceof GroupingElement) {
+        if (scope != null) {
+          final VirtualFile file = ((GroupingElement)element).getFile();
+          if (file != null && !scope.belongs(file.getUrl())) {
+            continue; 
+          }
+        }
+        if (!currentSessionId.equals(element.getData())) {
+          structure.removeElement(element);
+        }
+        else {
+          cleanupChildrenRecursively(element, scope, currentSessionId);
+        }
+      }
+      else {
+        if (!currentSessionId.equals(element.getData())) {
+          structure.removeElement(element);
+        }
+      }
+    }
+  }
+
+  @Override
+  public void addMessage(final int type,
+                         @NotNull final String[] text,
+                         @Nullable final String groupName,
+                         @Nullable final Navigatable navigatable,
+                         @Nullable final String exportTextPrefix, @Nullable final String rendererTextPrefix, @Nullable final UUID sessionId) {
+
+    myViewUpdater.execute(new Runnable() {
+      @Override
+      public void run() {
+        final ErrorViewStructure structure = myPanel.getErrorViewStructure();
+        final GroupingElement group = structure.lookupGroupingElement(groupName);
+        if (group != null && !sessionId.equals(group.getData())) {
+          structure.removeElement(group);
+        }
+        if (navigatable != null) {
+          myPanel.addMessage(type, text, groupName, navigatable, exportTextPrefix, rendererTextPrefix, sessionId);
+        }
+        else {
+          myPanel.addMessage(type, text, null, -1, -1, sessionId);
+        }
+      }
+    });
+  }
+
+  @Override
+  public void setProgress(String text, float fraction) {
+    myPanel.setProgress(text, fraction);
+  }
+
+  @Override
+  public void setProgress(String text) {
+    myPanel.setProgressText(text);
+  }
+
+  @Override
+  public void clearProgress() {
+    myPanel.clearProgressData();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/ProblemsViewPanel.java b/java/compiler/impl/src/com/intellij/compiler/impl/ProblemsViewPanel.java
new file mode 100644
index 0000000..d65451e
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/ProblemsViewPanel.java
@@ -0,0 +1,55 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import com.intellij.ide.errorTreeView.NewErrorTreeViewPanel;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.project.Project;
+
+public class ProblemsViewPanel extends NewErrorTreeViewPanel {
+  public ProblemsViewPanel(Project project) {
+    super(project, null, false, true, null);
+    myTree.getEmptyText().setText("No compilation problems found");
+  }
+
+
+  protected void fillRightToolbarGroup(DefaultActionGroup group) {
+    super.fillRightToolbarGroup(group);
+    group.add(new CompilerPropertiesAction());
+  }
+
+  protected void addExtraPopupMenuActions(DefaultActionGroup group) {
+    group.add(new ExcludeFromCompileAction(myProject, this));
+    // todo: do we need compiler's popup actions here?
+    //ActionGroup popupGroup = (ActionGroup)ActionManager.getInstance().getAction(IdeActions.GROUP_COMPILER_ERROR_VIEW_POPUP);
+    //if (popupGroup != null) {
+    //  for (AnAction action : popupGroup.getChildren(null)) {
+    //    group.add(action);
+    //  }
+    //}
+  }
+
+  @Override
+  protected boolean shouldShowFirstErrorInEditor() {
+    return false;
+  }
+
+  @Override
+  protected boolean canHideWarnings() {
+    return false;
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/ProjectCompileScope.java b/java/compiler/impl/src/com/intellij/compiler/impl/ProjectCompileScope.java
new file mode 100644
index 0000000..ed41606
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/ProjectCompileScope.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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: Jan 20, 2003
+ * Time: 5:34:19 PM
+ */
+package com.intellij.compiler.impl;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.FileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import org.jetbrains.annotations.NotNull;
+
+public class ProjectCompileScope extends FileIndexCompileScope {
+  private final Project myProject;
+
+  public ProjectCompileScope(final Project project) {
+    myProject = project;
+  }
+
+  protected FileIndex[] getFileIndices() {
+    return new FileIndex[] {ProjectRootManager.getInstance(myProject).getFileIndex()};
+  }
+
+  public boolean belongs(String url) {
+    final VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(url);
+    if (file != null) {
+      for (FileIndex index : getFileIndices()) {
+        if (index.isInSourceContent(file)) {
+          return true;
+        }
+      }
+    }
+    else {
+      // the file might be deleted
+      for (VirtualFile root : ProjectRootManager.getInstance(myProject).getContentSourceRoots()) {
+        final String rootUrl = root.getUrl();
+        if (FileUtil.startsWith(url, rootUrl.endsWith("/")? rootUrl : rootUrl + "/")) {
+          return true;
+        }
+      }
+    }
+    return false;
+    //return !FileUtil.startsWith(url, myTempDirUrl);
+  }
+
+  @NotNull
+  public Module[] getAffectedModules() {
+    return ModuleManager.getInstance(myProject).getModules();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/SourceUrlClassNamePair.java b/java/compiler/impl/src/com/intellij/compiler/impl/SourceUrlClassNamePair.java
new file mode 100644
index 0000000..6d9a60d
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/SourceUrlClassNamePair.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 com.intellij.compiler.impl;
+
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Jun 21, 2006
+ */
+public class SourceUrlClassNamePair {
+  private final String mySourceUrl;
+  private final @Nullable String myClassName;
+
+  public SourceUrlClassNamePair(String url, @Nullable String className) {
+    mySourceUrl = url;
+    myClassName = className;
+  }
+
+  public String getSourceUrl() {
+    return mySourceUrl;
+  }
+
+  public @Nullable String getClassName() {
+    return myClassName;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/StateCache.java b/java/compiler/impl/src/com/intellij/compiler/impl/StateCache.java
new file mode 100644
index 0000000..bd56b66
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/StateCache.java
@@ -0,0 +1,105 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import com.intellij.util.io.DataExternalizer;
+import com.intellij.util.io.EnumeratorStringDescriptor;
+import com.intellij.util.io.PersistentHashMap;
+import org.jetbrains.annotations.NonNls;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Iterator;
+
+public abstract class StateCache<T> {
+  private PersistentHashMap<String, T> myMap;
+  private final File myBaseFile;
+
+  public StateCache(@NonNls File storePath) throws IOException {
+    myBaseFile = storePath;
+    myMap = createMap(storePath);
+  }
+
+  protected abstract T read(DataInput stream) throws IOException;
+
+  protected abstract void write(T t, DataOutput out) throws IOException;
+
+  public void force() {
+    myMap.force();
+  }
+  
+  public void close() throws IOException {
+    myMap.close();
+  }
+  
+  public boolean wipe() {
+    try {
+      myMap.close();
+    }
+    catch (IOException ignored) {
+    }
+    PersistentHashMap.deleteFilesStartingWith(myBaseFile);
+    try {
+      myMap = createMap(myBaseFile);
+    }
+    catch (IOException ignored) {
+      return false;
+    }
+    return true;
+  }
+
+  public void update(@NonNls String url, T state) throws IOException {
+    if (state != null) {
+      myMap.put(url, state);
+    }
+    else {
+      remove(url);
+    }
+  }
+
+  public void remove(String url) throws IOException {
+    myMap.remove(url);
+  }
+
+  public T getState(String url) throws IOException {
+    return myMap.get(url);
+  }
+
+  public Collection<String> getUrls() throws IOException {
+    return myMap.getAllKeysWithExistingMapping();
+  }
+
+  public Iterator<String> getUrlsIterator() throws IOException {
+    return myMap.getAllKeysWithExistingMapping().iterator();
+  }
+
+
+  private PersistentHashMap<String, T> createMap(final File file) throws IOException {
+    return new PersistentHashMap<String,T>(file, new EnumeratorStringDescriptor(), new DataExternalizer<T>() {
+      public void save(final DataOutput out, final T value) throws IOException {
+        StateCache.this.write(value, out);
+      }
+
+      public T read(final DataInput in) throws IOException {
+        return StateCache.this.read(in);
+      }
+    });
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java b/java/compiler/impl/src/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java
new file mode 100644
index 0000000..8ba9de9
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java
@@ -0,0 +1,1920 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import com.intellij.ProjectTopics;
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.CompilerIOUtil;
+import com.intellij.compiler.CompilerWorkspaceConfiguration;
+import com.intellij.compiler.make.MakeUtil;
+import com.intellij.compiler.server.BuildManager;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.compiler.*;
+import com.intellij.openapi.compiler.ex.CompileContextEx;
+import com.intellij.openapi.components.ApplicationComponent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.project.ProjectManagerAdapter;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.startup.StartupManager;
+import com.intellij.openapi.util.*;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.*;
+import com.intellij.openapi.vfs.newvfs.FileAttribute;
+import com.intellij.openapi.vfs.newvfs.ManagingFS;
+import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
+import com.intellij.openapi.vfs.newvfs.persistent.FSRecords;
+import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
+import com.intellij.util.Alarm;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.SLRUCache;
+import com.intellij.util.indexing.FileBasedIndex;
+import com.intellij.util.indexing.IndexInfrastructure;
+import com.intellij.util.io.*;
+import com.intellij.util.io.DataOutputStream;
+import com.intellij.util.messages.MessageBusConnection;
+import gnu.trove.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @author Eugene Zhuravlev
+ * @since Jun 3, 2008
+ *
+ * A source file is scheduled for recompilation if
+ * 1. its timestamp has changed
+ * 2. one of its corresponding output files was deleted
+ * 3. output root of containing module has changed
+ *
+ * An output file is scheduled for deletion if:
+ * 1. corresponding source file has been scheduled for recompilation (see above)
+ * 2. corresponding source file has been deleted
+ */
+public class TranslatingCompilerFilesMonitor implements ApplicationComponent {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.TranslatingCompilerFilesMonitor");
+  private static final boolean ourDebugMode = false;
+
+  private static final FileAttribute ourSourceFileAttribute = new FileAttribute("_make_source_file_info_", 3);
+  private static final FileAttribute ourOutputFileAttribute = new FileAttribute("_make_output_file_info_", 3);
+  private static final Key<Map<String, VirtualFile>> SOURCE_FILES_CACHE = Key.create("_source_url_to_vfile_cache_");
+
+  private final Object myDataLock = new Object();
+
+  private final TIntHashSet mySuspendedProjects = new TIntHashSet(); // projectId for all projects that should not be monitored
+
+  private final TIntObjectHashMap<TIntHashSet> mySourcesToRecompile = new TIntObjectHashMap<TIntHashSet>(); // ProjectId->set of source file paths
+  private PersistentHashMap<Integer, TIntObjectHashMap<Pair<Integer, Integer>>> myOutputRootsStorage; // ProjectId->map[moduleId->Pair(outputDirId, testOutputDirId)]
+  
+  // Map: projectId -> Map{output path -> [sourceUrl; className]}
+  private final SLRUCache<Integer, Outputs> myOutputsToDelete = new SLRUCache<Integer, Outputs>(3, 3) {
+    @Override
+    public Outputs getIfCached(Integer key) {
+      final Outputs value = super.getIfCached(key);
+      if (value != null) {
+        value.allocate();
+      }
+      return value;
+    }
+
+    @NotNull
+    @Override
+    public Outputs get(Integer key) {
+      final Outputs value = super.get(key);
+      value.allocate();
+      return value;
+    }
+
+    @NotNull
+    @Override
+    public Outputs createValue(Integer key) {
+      try {
+        final String dirName = FSRecords.getNames().valueOf(key);
+        final File storeFile;
+        if (StringUtil.isEmpty(dirName)) {
+          storeFile = null;
+        }
+        else {
+          final File compilerCacheDir = CompilerPaths.getCacheStoreDirectory(dirName);
+          storeFile = compilerCacheDir.exists()? new File(compilerCacheDir, "paths_to_delete.dat") : null;
+        }
+        return new Outputs(storeFile, loadPathsToDelete(storeFile));
+      }
+      catch (IOException e) {
+        LOG.info(e);
+        return new Outputs(null, new HashMap<String, SourceUrlClassNamePair>());
+      }
+    }
+
+    @Override
+    protected void onDropFromCache(Integer key, Outputs value) {
+      value.release();
+    }
+  };
+  private final SLRUCache<Project, File> myGeneratedDataPaths = new SLRUCache<Project, File>(8, 8) {
+    @NotNull
+    public File createValue(final Project project) {
+      Disposer.register(project, new Disposable() {
+        public void dispose() {
+          myGeneratedDataPaths.remove(project);
+        }
+      });
+      return CompilerPaths.getGeneratedDataDirectory(project);
+    }
+  };
+  private final SLRUCache<Integer, TIntObjectHashMap<Pair<Integer, Integer>>> myProjectOutputRoots = new SLRUCache<Integer, TIntObjectHashMap<Pair<Integer, Integer>>>(2, 2) {
+    protected void onDropFromCache(Integer key, TIntObjectHashMap<Pair<Integer, Integer>> value) {
+      try {
+        myOutputRootsStorage.put(key, value);
+      }
+      catch (IOException e) {
+        LOG.info(e);
+      }
+    }
+
+    @NotNull
+    public TIntObjectHashMap<Pair<Integer, Integer>> createValue(Integer key) {
+      TIntObjectHashMap<Pair<Integer, Integer>> map = null;
+      try {
+        ensureOutputStorageInitialized();
+        map = myOutputRootsStorage.get(key);
+      }
+      catch (IOException e) {
+        LOG.info(e);
+      }
+      return map != null? map : new TIntObjectHashMap<Pair<Integer, Integer>>();
+    }
+  };
+  private final ProjectManager myProjectManager;
+  private final TIntIntHashMap myInitInProgress = new TIntIntHashMap(); // projectId for successfully initialized projects
+  private final Object myAsyncScanLock = new Object();
+
+  public TranslatingCompilerFilesMonitor(VirtualFileManager vfsManager, ProjectManager projectManager, Application application) {
+    myProjectManager = projectManager;
+
+    projectManager.addProjectManagerListener(new MyProjectManagerListener());
+    vfsManager.addVirtualFileListener(new MyVfsListener(), application);
+  }
+
+  public static TranslatingCompilerFilesMonitor getInstance() {
+    return ApplicationManager.getApplication().getComponent(TranslatingCompilerFilesMonitor.class);
+  }
+
+  public void suspendProject(Project project) {
+    final int projectId = getProjectId(project);
+
+    synchronized (myDataLock) {
+      if (!mySuspendedProjects.add(projectId)) {
+        return;
+      }
+      FileUtil.createIfDoesntExist(CompilerPaths.getRebuildMarkerFile(project));
+      // cleanup internal structures to free memory
+      mySourcesToRecompile.remove(projectId);
+      myOutputsToDelete.remove(projectId);
+      myGeneratedDataPaths.remove(project);
+    }
+
+    synchronized (myProjectOutputRoots) {
+      ensureOutputStorageInitialized();
+      myProjectOutputRoots.remove(projectId);
+      try {
+        myOutputRootsStorage.remove(projectId);
+      }
+      catch (IOException e) {
+        LOG.info(e);
+      }
+    }
+  }
+
+  public void watchProject(Project project) {
+    synchronized (myDataLock) {
+      mySuspendedProjects.remove(getProjectId(project));
+    }
+  }
+
+  public boolean isSuspended(Project project) {
+    return isSuspended(getProjectId(project));
+  }
+
+  public boolean isSuspended(int projectId) {
+    synchronized (myDataLock) {
+      return mySuspendedProjects.contains(projectId);
+    }
+  }
+
+  @Nullable
+  public static VirtualFile getSourceFileByOutput(VirtualFile outputFile) {
+    final OutputFileInfo outputFileInfo = loadOutputInfo(outputFile);
+    if (outputFileInfo != null) {
+      final String path = outputFileInfo.getSourceFilePath();
+      if (path != null) {
+        return LocalFileSystem.getInstance().findFileByPath(path);
+      }
+    }
+    return null;
+  }
+
+  public void collectFiles(CompileContext context, final TranslatingCompiler compiler, Iterator<VirtualFile> scopeSrcIterator, boolean forceCompile,
+                           final boolean isRebuild,
+                           Collection<VirtualFile> toCompile,
+                           Collection<Trinity<File, String, Boolean>> toDelete) {
+    final Project project = context.getProject();
+    final int projectId = getProjectId(project);
+    final CompilerConfiguration configuration = CompilerConfiguration.getInstance(project);
+    final boolean _forceCompile = forceCompile || isRebuild;
+    final Set<VirtualFile> selectedForRecompilation = new HashSet<VirtualFile>();
+    synchronized (myDataLock) {
+      final TIntHashSet pathsToRecompile = mySourcesToRecompile.get(projectId);
+      if (_forceCompile || pathsToRecompile != null && !pathsToRecompile.isEmpty()) {
+        if (ourDebugMode) {
+          System.out.println("Analysing potentially recompilable files for " + compiler.getDescription());
+        }
+        while (scopeSrcIterator.hasNext()) {
+          final VirtualFile file = scopeSrcIterator.next();
+          if (!file.isValid()) {
+            if (LOG.isDebugEnabled() || ourDebugMode) {
+              LOG.debug("Skipping invalid file " + file.getPresentableUrl());
+              if (ourDebugMode) {
+                System.out.println("\t SKIPPED(INVALID) " + file.getPresentableUrl());
+              }
+            }
+            continue;
+          }
+          final int fileId = getFileId(file);
+          if (_forceCompile) {
+            if (compiler.isCompilableFile(file, context) && !configuration.isExcludedFromCompilation(file)) {
+              toCompile.add(file);
+              if (ourDebugMode) {
+                System.out.println("\t INCLUDED " + file.getPresentableUrl());
+              }
+              selectedForRecompilation.add(file);
+              if (pathsToRecompile == null || !pathsToRecompile.contains(fileId)) {
+                loadInfoAndAddSourceForRecompilation(projectId, file);
+              }
+            }
+            else {
+              if (ourDebugMode) {
+                System.out.println("\t NOT COMPILABLE OR EXCLUDED " + file.getPresentableUrl());
+              }
+            }
+          }
+          else if (pathsToRecompile.contains(fileId)) {
+            if (compiler.isCompilableFile(file, context) && !configuration.isExcludedFromCompilation(file)) {
+              toCompile.add(file);
+              if (ourDebugMode) {
+                System.out.println("\t INCLUDED " + file.getPresentableUrl());
+              }
+              selectedForRecompilation.add(file);
+            }
+            else {
+              if (ourDebugMode) {
+                System.out.println("\t NOT COMPILABLE OR EXCLUDED " + file.getPresentableUrl());
+              }
+            }
+          }
+          else {
+            if (ourDebugMode) {
+              System.out.println("\t NOT INCLUDED " + file.getPresentableUrl());
+            }
+          }
+        }
+      }
+      // it is important that files to delete are collected after the files to compile (see what happens if forceCompile == true)
+      if (!isRebuild) {
+        final Outputs outputs = myOutputsToDelete.get(projectId);
+        try {
+          final VirtualFileManager vfm = VirtualFileManager.getInstance();
+          final LocalFileSystem lfs = LocalFileSystem.getInstance();
+          final List<String> zombieEntries = new ArrayList<String>();
+          final Map<String, VirtualFile> srcFileCache = getFileCache(context);
+          for (Map.Entry<String, SourceUrlClassNamePair> entry : outputs.getEntries()) {
+            final String outputPath = entry.getKey();
+            final SourceUrlClassNamePair classNamePair = entry.getValue();
+            final String sourceUrl = classNamePair.getSourceUrl();
+
+            final VirtualFile srcFile;
+            if (srcFileCache.containsKey(sourceUrl)) {
+              srcFile = srcFileCache.get(sourceUrl);
+            }
+            else {
+              srcFile = vfm.findFileByUrl(sourceUrl);
+              srcFileCache.put(sourceUrl, srcFile);
+            }
+
+            final boolean sourcePresent = srcFile != null;
+            if (sourcePresent) {
+              if (!compiler.isCompilableFile(srcFile, context)) {
+                continue; // do not collect files that were compiled by another compiler
+              }
+              if (!selectedForRecompilation.contains(srcFile)) {
+                if (!isMarkedForRecompilation(projectId, getFileId(srcFile))) {
+                  if (LOG.isDebugEnabled() || ourDebugMode) {
+                    final String message = "Found zombie entry (output is marked, but source is present and up-to-date): " + outputPath;
+                    LOG.debug(message);
+                    if (ourDebugMode) {
+                      System.out.println(message);
+                    }
+                  }
+                  zombieEntries.add(outputPath);
+                }
+                continue;
+              }
+            }
+            if (lfs.findFileByPath(outputPath) != null) {
+              //noinspection UnnecessaryBoxing
+              final File file = new File(outputPath);
+              toDelete.add(new Trinity<File, String, Boolean>(file, classNamePair.getClassName(), Boolean.valueOf(sourcePresent)));
+              if (LOG.isDebugEnabled() || ourDebugMode) {
+                final String message = "Found file to delete: " + file;
+                LOG.debug(message);
+                if (ourDebugMode) {
+                  System.out.println(message);
+                }
+              }
+            }
+            else {
+              if (LOG.isDebugEnabled() || ourDebugMode) {
+                final String message = "Found zombie entry marked for deletion: " + outputPath;
+                LOG.debug(message);
+                if (ourDebugMode) {
+                  System.out.println(message);
+                }
+              }
+              // must be gagbage entry, should cleanup
+              zombieEntries.add(outputPath);
+            }
+          }
+          for (String path : zombieEntries) {
+            unmarkOutputPathForDeletion(projectId, path);
+          }
+        }
+        finally {
+          outputs.release();
+        }
+      }
+    }
+  }
+
+  private static Map<String, VirtualFile> getFileCache(CompileContext context) {
+    Map<String, VirtualFile> cache = context.getUserData(SOURCE_FILES_CACHE);
+    if (cache == null) {
+      context.putUserData(SOURCE_FILES_CACHE, cache = new HashMap<String, VirtualFile>());
+    }
+    return cache;
+  }
+
+  private static int getFileId(final VirtualFile file) {
+    return FileBasedIndex.getFileId(file);
+  }
+
+  private static VirtualFile findFileById(int id) {
+    return IndexInfrastructure.findFileById((PersistentFS)ManagingFS.getInstance(), id);
+  }
+
+  public void update(final CompileContext context, @Nullable final String outputRoot, final Collection<TranslatingCompiler.OutputItem> successfullyCompiled, final VirtualFile[] filesToRecompile)
+      throws IOException {
+    final Project project = context.getProject();
+    final int projectId = getProjectId(project);
+    if (!successfullyCompiled.isEmpty()) {
+      final LocalFileSystem lfs = LocalFileSystem.getInstance();
+      final IOException[] exceptions = {null};
+      // need read action here to ensure that no modifications were made to VFS while updating file attributes
+      ApplicationManager.getApplication().runReadAction(new Runnable() {
+        public void run() {
+          try {
+            final Map<VirtualFile, SourceFileInfo> compiledSources = new HashMap<VirtualFile, SourceFileInfo>();
+            final Set<VirtualFile> forceRecompile = new HashSet<VirtualFile>();
+
+            for (TranslatingCompiler.OutputItem item : successfullyCompiled) {
+              final VirtualFile sourceFile = item.getSourceFile();
+              final boolean isSourceValid = sourceFile.isValid();
+              SourceFileInfo srcInfo = compiledSources.get(sourceFile);
+              if (isSourceValid && srcInfo == null) {
+                srcInfo = loadSourceInfo(sourceFile);
+                if (srcInfo != null) {
+                  srcInfo.clearPaths(projectId);
+                }
+                else {
+                  srcInfo = new SourceFileInfo();
+                }
+                compiledSources.put(sourceFile, srcInfo);
+              }
+
+              final String outputPath = item.getOutputPath();
+              if (outputPath != null) { // can be null for packageinfo
+                final VirtualFile outputFile = lfs.findFileByPath(outputPath);
+
+                //assert outputFile != null : "Virtual file was not found for \"" + outputPath + "\"";
+
+                if (outputFile != null) {
+                  if (!sourceFile.equals(outputFile)) {
+                    final String className = outputRoot == null? null : MakeUtil.relativeClassPathToQName(outputPath.substring(outputRoot.length()), '/');
+                    if (isSourceValid) {
+                      srcInfo.addOutputPath(projectId, outputPath);
+                      saveOutputInfo(outputFile, new OutputFileInfo(sourceFile.getPath(), className));
+                    }
+                    else {
+                      markOutputPathForDeletion(projectId, outputPath, className, sourceFile.getUrl());
+                    }
+                  }
+                }
+                else {  // output file was not found
+                  LOG.warn("TranslatingCompilerFilesMonitor.update():  Virtual file was not found for \"" + outputPath + "\"");
+                  if (isSourceValid) {
+                    forceRecompile.add(sourceFile);
+                  }
+                }
+              }
+            }
+            final long compilationStartStamp = ((CompileContextEx)context).getStartCompilationStamp();
+            for (Map.Entry<VirtualFile, SourceFileInfo> entry : compiledSources.entrySet()) {
+              final SourceFileInfo info = entry.getValue();
+              final VirtualFile file = entry.getKey();
+
+              final long fileStamp = file.getTimeStamp();
+              info.updateTimestamp(projectId, fileStamp);
+              saveSourceInfo(file, info);
+              if (LOG.isDebugEnabled() || ourDebugMode) {
+                final String message = "Unschedule recompilation (successfully compiled) " + file.getPresentableUrl();
+                LOG.debug(message);
+                if (ourDebugMode) {
+                  System.out.println(message);
+                }
+              }
+              removeSourceForRecompilation(projectId, Math.abs(getFileId(file)));
+              if (fileStamp > compilationStartStamp && !((CompileContextEx)context).isGenerated(file) || forceRecompile.contains(file)) {
+                // changes were made during compilation, need to re-schedule compilation
+                // it is important to invoke removeSourceForRecompilation() before this call to make sure
+                // the corresponding output paths will be scheduled for deletion
+                addSourceForRecompilation(projectId, file, info);
+              }
+            }
+          }
+          catch (IOException e) {
+            exceptions[0] = e;
+          }
+        }
+      });
+      if (exceptions[0] != null) {
+        throw exceptions[0];
+      }
+    }
+    
+    if (filesToRecompile.length > 0) {
+      ApplicationManager.getApplication().runReadAction(new Runnable() {
+        public void run() {
+          for (VirtualFile file : filesToRecompile) {
+            if (file.isValid()) {
+              loadInfoAndAddSourceForRecompilation(projectId, file);
+            }
+          }
+        }
+      });
+    }
+  }
+
+  public void updateOutputRootsLayout(Project project) {
+    final TIntObjectHashMap<Pair<Integer, Integer>> map = buildOutputRootsLayout(new ProjectRef(project));
+    final int projectId = getProjectId(project);
+    synchronized (myProjectOutputRoots) {
+      myProjectOutputRoots.put(projectId, map);
+    }
+  }
+
+  @NotNull
+  public String getComponentName() {
+    return "TranslatingCompilerFilesMonitor";
+  }
+
+  public void initComponent() {
+    ensureOutputStorageInitialized();
+  }
+
+  private static File getOutputRootsFile() {
+    return new File(CompilerPaths.getCompilerSystemDirectory(), "output_roots.dat");
+  }
+
+  private static void deleteStorageFiles(File tableFile) {
+    final File[] files = tableFile.getParentFile().listFiles();
+    if (files != null) {
+      final String name = tableFile.getName();
+      for (File file : files) {
+        if (file.getName().startsWith(name)) {
+          FileUtil.delete(file);
+        }
+      }
+    }
+  }
+
+  private static Map<String, SourceUrlClassNamePair> loadPathsToDelete(@Nullable final File file) {
+    final Map<String, SourceUrlClassNamePair> map = new HashMap<String, SourceUrlClassNamePair>();
+    try {
+      if (file != null && file.length() > 0) {
+        final DataInputStream is = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
+        try {
+          final int size = is.readInt();
+          for (int i = 0; i < size; i++) {
+            final String _outputPath = CompilerIOUtil.readString(is);
+            final String srcUrl = CompilerIOUtil.readString(is);
+            final String className = CompilerIOUtil.readString(is);
+            map.put(FileUtil.toSystemIndependentName(_outputPath), new SourceUrlClassNamePair(srcUrl, className));
+          }
+        }
+        finally {
+          is.close();
+        }
+      }
+    }
+    catch (FileNotFoundException ignored) {
+    }
+    catch (IOException e) {
+      LOG.info(e);
+    }
+    return map;
+  }
+
+  private void ensureOutputStorageInitialized() {
+    if (myOutputRootsStorage != null) {
+      return;
+    }
+    final File rootsFile = getOutputRootsFile();
+    try {
+      initOutputRootsFile(rootsFile);
+    }
+    catch (IOException e) {
+      LOG.info(e);
+      deleteStorageFiles(rootsFile);
+      try {
+        initOutputRootsFile(rootsFile);
+      }
+      catch (IOException e1) {
+        LOG.error(e1);
+      }
+    }
+  }
+
+  private TIntObjectHashMap<Pair<Integer, Integer>> buildOutputRootsLayout(ProjectRef projRef) {
+    final TIntObjectHashMap<Pair<Integer, Integer>> map = new TIntObjectHashMap<Pair<Integer, Integer>>();
+    for (Module module : ModuleManager.getInstance(projRef.get()).getModules()) {
+      final CompilerModuleExtension manager = CompilerModuleExtension.getInstance(module);
+      if (manager != null) {
+        final VirtualFile output = manager.getCompilerOutputPath();
+        final int first = output != null? Math.abs(getFileId(output)) : -1;
+        final VirtualFile testsOutput = manager.getCompilerOutputPathForTests();
+        final int second = testsOutput != null? Math.abs(getFileId(testsOutput)) : -1;
+        map.put(getModuleId(module), new Pair<Integer, Integer>(first, second));
+      }
+    }
+    return map;
+  }
+
+  private void initOutputRootsFile(File rootsFile) throws IOException {
+    myOutputRootsStorage = new PersistentHashMap<Integer, TIntObjectHashMap<Pair<Integer, Integer>>>(rootsFile, EnumeratorIntegerDescriptor.INSTANCE, new DataExternalizer<TIntObjectHashMap<Pair<Integer, Integer>>>() {
+      public void save(DataOutput out, TIntObjectHashMap<Pair<Integer, Integer>> value) throws IOException {
+        for (final TIntObjectIterator<Pair<Integer, Integer>> it = value.iterator(); it.hasNext();) {
+          it.advance();
+          DataInputOutputUtil.writeINT(out, it.key());
+          final Pair<Integer, Integer> pair = it.value();
+          DataInputOutputUtil.writeINT(out, pair.first);
+          DataInputOutputUtil.writeINT(out, pair.second);
+        }
+      }
+
+      public TIntObjectHashMap<Pair<Integer, Integer>> read(DataInput in) throws IOException {
+        final DataInputStream _in = (DataInputStream)in;
+        final TIntObjectHashMap<Pair<Integer, Integer>> map = new TIntObjectHashMap<Pair<Integer, Integer>>();
+        while (_in.available() > 0) {
+          final int key = DataInputOutputUtil.readINT(_in);
+          final int first = DataInputOutputUtil.readINT(_in);
+          final int second = DataInputOutputUtil.readINT(_in);
+          map.put(key, new Pair<Integer, Integer>(first, second));
+        }
+        return map;
+      }
+    });
+  }
+
+  public void disposeComponent() {
+    try {
+      synchronized (myProjectOutputRoots) {
+        myProjectOutputRoots.clear();
+      }
+    }
+    finally {
+      synchronized (myDataLock) {
+        myOutputsToDelete.clear();
+      }
+    }
+    
+    try {
+      final PersistentHashMap<Integer, TIntObjectHashMap<Pair<Integer, Integer>>> storage = myOutputRootsStorage;
+      if (storage != null) {
+        storage.close();
+      }
+    }
+    catch (IOException e) {
+      LOG.info(e);
+      deleteStorageFiles(getOutputRootsFile());
+    }
+  }
+
+  private static void savePathsToDelete(final File file, final Map<String, SourceUrlClassNamePair> outputs) {
+    try {
+      FileUtil.createParentDirs(file);
+      final DataOutputStream os = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
+      try {
+        if (outputs != null) {
+          os.writeInt(outputs.size());
+          for (Map.Entry<String, SourceUrlClassNamePair> entry : outputs.entrySet()) {
+            CompilerIOUtil.writeString(entry.getKey(), os);
+            final SourceUrlClassNamePair pair = entry.getValue();
+            CompilerIOUtil.writeString(pair.getSourceUrl(), os);
+            CompilerIOUtil.writeString(pair.getClassName(), os);
+          }
+        }
+        else {
+          os.writeInt(0);
+        }
+      }
+      finally {
+        os.close();
+      }
+    }
+    catch (IOException e) {
+      LOG.error(e);
+    }
+  }
+
+  @Nullable
+  private static SourceFileInfo loadSourceInfo(final VirtualFile file) {
+    try {
+      final DataInputStream is = ourSourceFileAttribute.readAttribute(file);
+      if (is != null) {
+        try {
+          return new SourceFileInfo(is);
+        }
+        finally {
+          is.close();
+        }
+      }
+    }
+    catch (RuntimeException e) {
+      final Throwable cause = e.getCause();
+      if (cause instanceof IOException) {
+        LOG.info(e); // ignore IOExceptions
+      }
+      else {
+        throw e;
+      }
+    }
+    catch (IOException ignored) {
+      LOG.info(ignored);
+    }
+    return null;
+  }
+
+  public static void removeSourceInfo(VirtualFile file) {
+    saveSourceInfo(file, new SourceFileInfo());
+  }
+
+  private static void saveSourceInfo(VirtualFile file, SourceFileInfo descriptor) {
+    final java.io.DataOutputStream out = ourSourceFileAttribute.writeAttribute(file);
+    try {
+      try {
+        descriptor.save(out);
+      }
+      finally {
+        out.close();
+      }
+    }
+    catch (IOException ignored) {
+      LOG.info(ignored);
+    }
+  }
+
+  @Nullable
+  private static OutputFileInfo loadOutputInfo(final VirtualFile file) {
+    try {
+      final DataInputStream is = ourOutputFileAttribute.readAttribute(file);
+      if (is != null) {
+        try {
+          return new OutputFileInfo(is);
+        }
+        finally {
+          is.close();
+        }
+      }
+    }
+    catch (RuntimeException e) {
+      final Throwable cause = e.getCause();
+      if (cause instanceof IOException) {
+        LOG.info(e); // ignore IO exceptions
+      }
+      else {
+        throw e;
+      }
+    }
+    catch (IOException ignored) {
+      LOG.info(ignored);
+    }
+    return null;
+  }
+
+  private static void saveOutputInfo(VirtualFile file, OutputFileInfo descriptor) {
+    final java.io.DataOutputStream out = ourOutputFileAttribute.writeAttribute(file);
+    try {
+      try {
+        descriptor.save(out);
+      }
+      finally {
+        out.close();
+      }
+    }
+    catch (IOException ignored) {
+      LOG.info(ignored);
+    }
+  }
+
+  private int getProjectId(Project project) {
+    try {
+      return FSRecords.getNames().enumerate(CompilerPaths.getCompilerSystemDirectoryName(project));
+    }
+    catch (IOException e) {
+      LOG.info(e);
+    }
+    return -1;
+  }
+
+  private int getModuleId(Module module) {
+    try {
+      return FSRecords.getNames().enumerate(module.getName().toLowerCase(Locale.US));
+    }
+    catch (IOException e) {
+      LOG.info(e);
+    }
+    return -1;
+  }
+
+  private static class OutputFileInfo {
+    private final int mySourcePath;
+
+    private final int myClassName;
+
+    OutputFileInfo(final String sourcePath, @Nullable String className) throws IOException {
+      final PersistentStringEnumerator symtable = FSRecords.getNames();
+      mySourcePath = symtable.enumerate(sourcePath);
+      myClassName = className != null? symtable.enumerate(className) : -1;
+    }
+
+    OutputFileInfo(final DataInput in) throws IOException {
+      mySourcePath = in.readInt();
+      myClassName = in.readInt();
+    }
+
+    String getSourceFilePath() {
+      try {
+        return FSRecords.getNames().valueOf(mySourcePath);
+      }
+      catch (IOException e) {
+        LOG.info(e);
+      }
+      return null;
+    }
+
+    @Nullable
+    public String getClassName() {
+      try {
+        return myClassName < 0? null : FSRecords.getNames().valueOf(myClassName);
+      }
+      catch (IOException e) {
+        LOG.info(e);
+      }
+      return null;
+    }
+
+    public void save(final DataOutput out) throws IOException {
+      out.writeInt(mySourcePath);
+      out.writeInt(myClassName);
+    }
+  }
+
+  private static class SourceFileInfo {
+    private TIntLongHashMap myTimestamps; // ProjectId -> last compiled stamp
+    private TIntObjectHashMap<Serializable> myProjectToOutputPathMap; // ProjectId -> either a single output path or a set of output paths
+
+    private SourceFileInfo() {
+    }
+
+    private SourceFileInfo(@NotNull DataInput in) throws IOException {
+      final int projCount = DataInputOutputUtil.readINT(in);
+      for (int idx = 0; idx < projCount; idx++) {
+        final int projectId = DataInputOutputUtil.readINT(in);
+        final long stamp = DataInputOutputUtil.readTIME(in);
+        updateTimestamp(projectId, stamp);
+
+        final int pathsCount = DataInputOutputUtil.readINT(in);
+        for (int i = 0; i < pathsCount; i++) {
+          final int path = in.readInt();
+          addOutputPath(projectId, path);
+        }
+      }
+    }
+
+    public void save(@NotNull final DataOutput out) throws IOException {
+      final int[] projects = getProjectIds().toArray();
+      DataInputOutputUtil.writeINT(out, projects.length);
+      for (int projectId : projects) {
+        DataInputOutputUtil.writeINT(out, projectId);
+        DataInputOutputUtil.writeTIME(out, getTimestamp(projectId));
+        final Object value = myProjectToOutputPathMap != null? myProjectToOutputPathMap.get(projectId) : null;
+        if (value instanceof Integer) {
+          DataInputOutputUtil.writeINT(out, 1);
+          out.writeInt(((Integer)value).intValue());
+        }
+        else if (value instanceof TIntHashSet) {
+          final TIntHashSet set = (TIntHashSet)value;
+          DataInputOutputUtil.writeINT(out, set.size());
+          final IOException[] ex = new IOException[] {null};
+          set.forEach(new TIntProcedure() {
+            public boolean execute(final int value) {
+              try {
+                out.writeInt(value);
+                return true;
+              }
+              catch (IOException e) {
+                ex[0] = e;
+                return false;
+              }
+            }
+          });
+          if (ex[0] != null) {
+            throw ex[0];
+          }
+        }
+        else {
+          DataInputOutputUtil.writeINT(out, 0);
+        }
+      }
+    }
+
+    private void updateTimestamp(final int projectId, final long stamp) {
+      if (stamp > 0L) {
+        if (myTimestamps == null) {
+          myTimestamps = new TIntLongHashMap(1, 0.98f);
+        }
+        myTimestamps.put(projectId, stamp);
+      }
+      else {
+        if (myTimestamps != null) {
+          myTimestamps.remove(projectId);
+        }
+      }
+    }
+
+    TIntHashSet getProjectIds() {
+      final TIntHashSet result = new TIntHashSet();
+      if (myTimestamps != null) {
+        result.addAll(myTimestamps.keys());
+      }
+      if (myProjectToOutputPathMap != null) {
+        result.addAll(myProjectToOutputPathMap.keys());
+      }
+      return result;
+    }
+
+    private void addOutputPath(final int projectId, String outputPath) {
+      try {
+        addOutputPath(projectId, FSRecords.getNames().enumerate(outputPath));
+      }
+      catch (IOException e) {
+        LOG.info(e);
+      }
+    }
+
+    private void addOutputPath(final int projectId, final int outputPath) {
+      if (myProjectToOutputPathMap == null) {
+        myProjectToOutputPathMap = new TIntObjectHashMap<Serializable>(1, 0.98f);
+        myProjectToOutputPathMap.put(projectId, outputPath);
+      }
+      else {
+        final Object val = myProjectToOutputPathMap.get(projectId);
+        if (val == null)  {
+          myProjectToOutputPathMap.put(projectId, outputPath);
+        }
+        else {
+          TIntHashSet set;
+          if (val instanceof Integer)  {
+            set = new TIntHashSet();
+            set.add(((Integer)val).intValue());
+            myProjectToOutputPathMap.put(projectId, set);
+          }
+          else {
+            assert val instanceof TIntHashSet;
+            set = (TIntHashSet)val;
+          }
+          set.add(outputPath);
+        }
+      }
+    }
+
+    public boolean clearPaths(final int projectId){
+      if (myProjectToOutputPathMap != null) {
+        final Serializable removed = myProjectToOutputPathMap.remove(projectId);
+        return removed != null;
+      }
+      return false;
+    }
+
+    long getTimestamp(final int projectId) {
+      return myTimestamps == null? -1L : myTimestamps.get(projectId);
+    }
+
+    void processOutputPaths(final int projectId, final Proc proc){
+      if (myProjectToOutputPathMap != null) {
+        try {
+          final PersistentStringEnumerator symtable = FSRecords.getNames();
+          final Object val = myProjectToOutputPathMap.get(projectId);
+          if (val instanceof Integer)  {
+            proc.execute(projectId, symtable.valueOf(((Integer)val).intValue()));
+          }
+          else if (val instanceof TIntHashSet) {
+            ((TIntHashSet)val).forEach(new TIntProcedure() {
+              public boolean execute(final int value) {
+                try {
+                  proc.execute(projectId, symtable.valueOf(value));
+                  return true;
+                }
+                catch (IOException e) {
+                  LOG.info(e);
+                  return false;
+                }
+              }
+            });
+          }
+        }
+        catch (IOException e) {
+          LOG.info(e);
+        }
+      }
+    }
+
+    boolean isAssociated(int projectId, String outputPath) {
+      if (myProjectToOutputPathMap != null) {
+        try {
+          final Object val = myProjectToOutputPathMap.get(projectId);
+          if (val instanceof Integer)  {
+            return FileUtil.pathsEqual(outputPath, FSRecords.getNames().valueOf(((Integer)val).intValue()));
+          }
+          if (val instanceof TIntHashSet) {
+            final int _outputPath = FSRecords.getNames().enumerate(outputPath);
+            return ((TIntHashSet)val).contains(_outputPath);
+          }
+        }
+        catch (IOException e) {
+          LOG.info(e);
+        }
+      }
+      return false;
+    }
+  }
+
+  public List<String> getCompiledClassNames(VirtualFile srcFile, Project project) {
+    final SourceFileInfo info = loadSourceInfo(srcFile);
+    if (info == null) {
+      return Collections.emptyList();
+    }
+
+    final ArrayList<String> result = new ArrayList<String>();
+
+    info.processOutputPaths(getProjectId(project), new Proc() {
+      @Override
+      public boolean execute(int projectId, String outputPath) {
+        VirtualFile clsFile = LocalFileSystem.getInstance().findFileByPath(outputPath);
+        if (clsFile != null) {
+          OutputFileInfo outputInfo = loadOutputInfo(clsFile);
+          if (outputInfo != null) {
+            ContainerUtil.addIfNotNull(result, outputInfo.getClassName());
+          }
+        }
+        return true;
+      }
+    });
+    return result;
+  }
+
+
+  private interface FileProcessor {
+    void execute(VirtualFile file);
+  }
+
+  private static void processRecursively(VirtualFile file, final boolean dbOnly, final FileProcessor processor) {
+    if (!(file.getFileSystem() instanceof LocalFileSystem)) {
+      return;
+    }
+
+    final FileTypeManager fileTypeManager = FileTypeManager.getInstance();
+    VfsUtilCore.visitChildrenRecursively(file, new VirtualFileVisitor() {
+      @NotNull @Override
+      public Result visitFileEx(@NotNull VirtualFile file) {
+        if (fileTypeManager.isFileIgnored(file)) {
+          return SKIP_CHILDREN;
+        }
+
+        if (!file.isDirectory()) {
+          processor.execute(file);
+        }
+        return CONTINUE;
+      }
+
+      @Nullable
+      @Override
+      public Iterable<VirtualFile> getChildrenIterable(@NotNull VirtualFile file) {
+        return file.isDirectory() && dbOnly ? ((NewVirtualFile)file).iterInDbChildren() : null;
+      }
+    });
+  }
+
+  // made public for tests
+  public void scanSourceContent(final ProjectRef projRef, final Collection<VirtualFile> roots, final int totalRootCount, final boolean isNewRoots) {
+    if (roots.isEmpty()) {
+      return;
+    }
+    final int projectId = getProjectId(projRef.get());
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Scanning source content for project projectId=" + projectId + "; url=" + projRef.get().getPresentableUrl());
+    }
+
+    final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(projRef.get()).getFileIndex();
+    final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
+    int processed = 0;
+    for (VirtualFile srcRoot : roots) {
+      if (indicator != null) {
+        projRef.get();
+        indicator.setText2(srcRoot.getPresentableUrl());
+        indicator.setFraction(++processed / (double)totalRootCount);
+      }
+      if (isNewRoots) {
+        fileIndex.iterateContentUnderDirectory(srcRoot, new ContentIterator() {
+          public boolean processFile(final VirtualFile file) {
+            if (!file.isDirectory()) {
+              if (!isMarkedForRecompilation(projectId, Math.abs(getFileId(file)))) {
+                final SourceFileInfo srcInfo = loadSourceInfo(file);
+                if (srcInfo == null || srcInfo.getTimestamp(projectId) != file.getTimeStamp()) {
+                  addSourceForRecompilation(projectId, file, srcInfo);
+                }
+              }
+            }
+            else {
+              projRef.get();
+            }
+            return true;
+          }
+        });
+      }
+      else {
+        final FileTypeManager fileTypeManager = FileTypeManager.getInstance();
+        VfsUtilCore.visitChildrenRecursively(srcRoot, new VirtualFileVisitor() {
+          @Override
+          public boolean visitFile(@NotNull VirtualFile file) {
+            if (fileTypeManager.isFileIgnored(file)) {
+              return false;
+            }
+            final int fileId = getFileId(file);
+            if (fileId > 0 /*file is valid*/) {
+              if (file.isDirectory()) {
+                projRef.get();
+              }
+              else if (!isMarkedForRecompilation(projectId, fileId)) {
+                final SourceFileInfo srcInfo = loadSourceInfo(file);
+                if (srcInfo != null) {
+                  addSourceForRecompilation(projectId, file, srcInfo);
+                }
+              }
+            }
+            return true;
+          }
+        });
+      }
+    }
+  }
+
+  public void ensureInitializationCompleted(Project project, ProgressIndicator indicator) {
+    final int id = getProjectId(project);
+    synchronized (myAsyncScanLock) {
+      while (myInitInProgress.containsKey(id)) {
+        if (!project.isOpen() || project.isDisposed() || (indicator != null && indicator.isCanceled())) {
+          // makes no sense to continue waiting
+          break;
+        }
+        try {
+          myAsyncScanLock.wait(500);
+        }
+        catch (InterruptedException ignored) {
+          break;
+        }
+      }
+    }
+  }
+
+  private void markOldOutputRoots(final ProjectRef projRef, final TIntObjectHashMap<Pair<Integer, Integer>> currentLayout) {
+    final int projectId = getProjectId(projRef.get());
+
+    final TIntHashSet rootsToMark = new TIntHashSet();
+    synchronized (myProjectOutputRoots) {
+      final TIntObjectHashMap<Pair<Integer, Integer>> oldLayout = myProjectOutputRoots.get(projectId);
+      for (final TIntObjectIterator<Pair<Integer, Integer>> it = oldLayout.iterator(); it.hasNext();) {
+        it.advance();
+        final Pair<Integer, Integer> currentRoots = currentLayout.get(it.key());
+        final Pair<Integer, Integer> oldRoots = it.value();
+        if (shouldMark(oldRoots.first, currentRoots != null? currentRoots.first : -1)) {
+          rootsToMark.add(oldRoots.first);
+        }
+        if (shouldMark(oldRoots.second, currentRoots != null? currentRoots.second : -1)) {
+          rootsToMark.add(oldRoots.second);
+        }
+      }
+    }
+
+    for (TIntIterator it = rootsToMark.iterator(); it.hasNext();) {
+      final int id = it.next();
+      final VirtualFile outputRoot = findFileById(id);
+      if (outputRoot != null) {
+        processOldOutputRoot(projectId, outputRoot);
+      }
+    }
+  }
+
+  private static boolean shouldMark(Integer oldOutputRoot, Integer currentOutputRoot) {
+    return oldOutputRoot != null && oldOutputRoot.intValue() > 0 && !Comparing.equal(oldOutputRoot, currentOutputRoot);
+  }
+
+  private void processOldOutputRoot(final int projectId, VirtualFile outputRoot) {
+    // recursively mark all corresponding sources for recompilation
+    VfsUtilCore.visitChildrenRecursively(outputRoot, new VirtualFileVisitor() {
+      @Override
+      public boolean visitFile(@NotNull VirtualFile file) {
+        if (!file.isDirectory()) {
+          // todo: possible optimization - process only those outputs that are not marked for deletion yet
+          final OutputFileInfo outputInfo = loadOutputInfo(file);
+          if (outputInfo != null) {
+            final String srcPath = outputInfo.getSourceFilePath();
+            final VirtualFile srcFile = srcPath != null? LocalFileSystem.getInstance().findFileByPath(srcPath) : null;
+            if (srcFile != null) {
+              loadInfoAndAddSourceForRecompilation(projectId, srcFile);
+            }
+          }
+        }
+        return true;
+      }
+    });
+  }
+
+  public void scanSourcesForCompilableFiles(final Project project) {
+    final int projectId = getProjectId(project);
+    if (isSuspended(projectId)) {
+      return;
+    }
+    startAsyncScan(projectId);
+    StartupManager.getInstance(project).runWhenProjectIsInitialized(new Runnable() {
+      public void run() {
+        new Task.Backgroundable(project, CompilerBundle.message("compiler.initial.scanning.progress.text"), false) {
+          public void run(@NotNull final ProgressIndicator indicator) {
+            final ProjectRef projRef = new ProjectRef(project);
+            if (LOG.isDebugEnabled()) {
+              LOG.debug("Initial sources scan for project hash=" + projectId + "; url="+ projRef.get().getPresentableUrl());
+            }
+            try {
+              final IntermediateOutputCompiler[] compilers =
+                  CompilerManager.getInstance(projRef.get()).getCompilers(IntermediateOutputCompiler.class);
+
+              final Set<VirtualFile> intermediateRoots = new HashSet<VirtualFile>();
+              if (compilers.length > 0) {
+                final Module[] modules = ModuleManager.getInstance(projRef.get()).getModules();
+                for (IntermediateOutputCompiler compiler : compilers) {
+                  for (Module module : modules) {
+                    if (module.isDisposed()) {
+                      continue;
+                    }
+                    final VirtualFile outputRoot = LocalFileSystem.getInstance().refreshAndFindFileByPath(CompilerPaths.getGenerationOutputPath(compiler, module, false));
+                    if (outputRoot != null) {
+                      intermediateRoots.add(outputRoot);
+                    }
+                    final VirtualFile testsOutputRoot = LocalFileSystem.getInstance().refreshAndFindFileByPath(CompilerPaths.getGenerationOutputPath(compiler, module, true));
+                    if (testsOutputRoot != null) {
+                      intermediateRoots.add(testsOutputRoot);
+                    }
+                  }
+                }
+              }
+
+              final List<VirtualFile> projectRoots = Arrays.asList(ProjectRootManager.getInstance(projRef.get()).getContentSourceRoots());
+              final int totalRootsCount = projectRoots.size() + intermediateRoots.size();
+              scanSourceContent(projRef, projectRoots, totalRootsCount, true);
+
+              if (!intermediateRoots.isEmpty()) {
+                final FileProcessor processor = new FileProcessor() {
+                  public void execute(final VirtualFile file) {
+                    if (!isMarkedForRecompilation(projectId, Math.abs(getFileId(file)))) {
+                      final SourceFileInfo srcInfo = loadSourceInfo(file);
+                      if (srcInfo == null || srcInfo.getTimestamp(projectId) != file.getTimeStamp()) {
+                        addSourceForRecompilation(projectId, file, srcInfo);
+                      }
+                    }
+                  }
+                };
+                int processed = projectRoots.size();
+                for (VirtualFile root : intermediateRoots) {
+                  projRef.get();
+                  indicator.setText2(root.getPresentableUrl());
+                  indicator.setFraction(++processed / (double)totalRootsCount);
+                  processRecursively(root, false, processor);
+                }
+              }
+              
+              markOldOutputRoots(projRef, buildOutputRootsLayout(projRef));
+            }
+            catch (ProjectRef.ProjectClosedException swallowed) {
+            }
+            finally {
+              terminateAsyncScan(projectId, false);
+            }
+          }
+        }.queue();
+      }
+    });
+  }
+
+  private void terminateAsyncScan(int projectId, final boolean clearCounter) {
+    synchronized (myAsyncScanLock) {
+      int counter = myInitInProgress.remove(projectId);
+      if (clearCounter) {
+        myAsyncScanLock.notifyAll();
+      }
+      else {
+        if (--counter > 0) {
+          myInitInProgress.put(projectId, counter);
+        }
+        else {
+          myAsyncScanLock.notifyAll();
+        }
+      }
+    }
+  }
+
+  private void startAsyncScan(final int projectId) {
+    synchronized (myAsyncScanLock) {
+      int counter = myInitInProgress.get(projectId);
+      counter = (counter > 0)? counter + 1 : 1;
+      myInitInProgress.put(projectId, counter);
+      myAsyncScanLock.notifyAll();
+    }
+  }
+
+  private class MyProjectManagerListener extends ProjectManagerAdapter {
+
+    final Map<Project, MessageBusConnection> myConnections = new HashMap<Project, MessageBusConnection>();
+
+    public void projectOpened(final Project project) {
+      final MessageBusConnection conn = project.getMessageBus().connect();
+      myConnections.put(project, conn);
+      final ProjectRef projRef = new ProjectRef(project);
+      final int projectId = getProjectId(project);
+
+      if (CompilerWorkspaceConfiguration.getInstance(project).useOutOfProcessBuild()) {
+        suspendProject(project);
+      }
+      else {
+        watchProject(project);
+      }
+
+      conn.subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootListener() {
+        private VirtualFile[] myRootsBefore;
+        private Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, project);
+
+        public void beforeRootsChange(final ModuleRootEvent event) {
+          if (isSuspended(projectId)) {
+            return;
+          }
+          try {
+            myRootsBefore = ProjectRootManager.getInstance(projRef.get()).getContentSourceRoots();
+          }
+          catch (ProjectRef.ProjectClosedException e) {
+            myRootsBefore = null;
+          }
+        }
+
+        public void rootsChanged(final ModuleRootEvent event) {
+          if (isSuspended(projectId)) {
+            return;
+          }
+          if (LOG.isDebugEnabled()) {
+            LOG.debug("Before roots changed for projectId=" + projectId + "; url="+ project.getPresentableUrl());
+          }
+          try {
+            final VirtualFile[] rootsBefore = myRootsBefore;
+            myRootsBefore = null;
+            final VirtualFile[] rootsAfter = ProjectRootManager.getInstance(projRef.get()).getContentSourceRoots();
+            final Set<VirtualFile> newRoots = new HashSet<VirtualFile>();
+            final Set<VirtualFile> oldRoots = new HashSet<VirtualFile>();
+            {
+              if (rootsAfter.length > 0) {
+                ContainerUtil.addAll(newRoots, rootsAfter);
+              }
+              if (rootsBefore != null) {
+                newRoots.removeAll(Arrays.asList(rootsBefore));
+              }
+            }
+            {
+              if (rootsBefore != null) {
+                ContainerUtil.addAll(oldRoots, rootsBefore);
+              }
+              if (!oldRoots.isEmpty() && rootsAfter.length > 0) {
+                oldRoots.removeAll(Arrays.asList(rootsAfter));
+              }
+            }
+
+            myAlarm.cancelAllRequests(); // need alarm to deal with multiple rootsChanged events
+            myAlarm.addRequest(new Runnable() {
+              public void run() {
+                startAsyncScan(projectId);
+                new Task.Backgroundable(project, CompilerBundle.message("compiler.initial.scanning.progress.text"), false) {
+                  public void run(@NotNull final ProgressIndicator indicator) {
+                    try {
+                      if (newRoots.size() > 0) {
+                        scanSourceContent(projRef, newRoots, newRoots.size(), true);
+                      }
+                      if (oldRoots.size() > 0) {
+                        scanSourceContent(projRef, oldRoots, oldRoots.size(), false);
+                      }
+                      markOldOutputRoots(projRef, buildOutputRootsLayout(projRef));
+                    }
+                    catch (ProjectRef.ProjectClosedException swallowed) {
+                      // ignored
+                    }
+                    finally {
+                      terminateAsyncScan(projectId, false);
+                    }
+                  }
+                }.queue();
+              }
+            }, 500, ModalityState.NON_MODAL);
+          }
+          catch (ProjectRef.ProjectClosedException e) {
+            LOG.info(e);
+          }
+        }
+      });
+
+      scanSourcesForCompilableFiles(project);
+    }
+
+    public void projectClosed(final Project project) {
+      final int projectId = getProjectId(project);
+      terminateAsyncScan(projectId, true);
+      myConnections.remove(project).disconnect();
+      synchronized (myDataLock) {
+        mySourcesToRecompile.remove(projectId);
+        myOutputsToDelete.remove(projectId);  // drop cache to save memory
+      }
+    }
+  }
+
+  private class MyVfsListener extends VirtualFileAdapter {
+    public void propertyChanged(final VirtualFilePropertyEvent event) {
+      if (VirtualFile.PROP_NAME.equals(event.getPropertyName())) {
+        final VirtualFile eventFile = event.getFile();
+        final VirtualFile parent = event.getParent();
+        if (parent != null) {
+          final String oldName = (String)event.getOldValue();
+          final String root = parent.getPath() + "/" + oldName;
+          final Set<File> toMark = new THashSet<File>(FileUtil.FILE_HASHING_STRATEGY);
+          if (eventFile.isDirectory()) {
+            VfsUtilCore.visitChildrenRecursively(eventFile, new VirtualFileVisitor() {
+              private StringBuilder filePath = new StringBuilder(root);
+
+              @Override
+              public boolean visitFile(@NotNull VirtualFile child) {
+                if (child.isDirectory()) {
+                  if (!Comparing.equal(child, eventFile)) {
+                    filePath.append("/").append(child.getName());
+                  }
+                }
+                else {
+                  String childPath = filePath.toString();
+                  if (!Comparing.equal(child, eventFile)) {
+                    childPath += "/" + child.getName();
+                  }
+                  toMark.add(new File(childPath));
+                }
+                return true;
+              }
+
+              @Override
+              public void afterChildrenVisited(@NotNull VirtualFile file) {
+                if (file.isDirectory() && !Comparing.equal(file, eventFile)) {
+                  filePath.delete(filePath.length() - file.getName().length() - 1, filePath.length());
+                }
+              }
+            });
+          }
+          else {
+            toMark.add(new File(root));
+          }
+          notifyFilesDeleted(toMark);
+        }
+        markDirtyIfSource(eventFile, false);
+      }
+    }
+
+    public void contentsChanged(final VirtualFileEvent event) {
+      markDirtyIfSource(event.getFile(), false);
+    }
+
+    public void fileCreated(final VirtualFileEvent event) {
+      processNewFile(event.getFile(), true);
+    }
+
+    public void fileCopied(final VirtualFileCopyEvent event) {
+      processNewFile(event.getFile(), true);
+    }
+
+    public void fileMoved(VirtualFileMoveEvent event) {
+      processNewFile(event.getFile(), true);
+    }
+
+    public void beforeFileDeletion(final VirtualFileEvent event) {
+      final VirtualFile eventFile = event.getFile();
+      if ((LOG.isDebugEnabled() && eventFile.isDirectory()) || ourDebugMode) {
+        final String message = "Processing file deletion: " + eventFile.getPresentableUrl();
+        LOG.debug(message);
+        if (ourDebugMode) {
+          System.out.println(message);
+        }
+      }
+
+      final Set<File> pathsToMark = new THashSet<File>(FileUtil.FILE_HASHING_STRATEGY);
+
+      processRecursively(eventFile, true, new FileProcessor() {
+        private final TIntArrayList myAssociatedProjectIds = new TIntArrayList();
+        
+        public void execute(final VirtualFile file) {
+          final String filePath = file.getPath();
+          pathsToMark.add(new File(filePath));
+          myAssociatedProjectIds.clear();
+          try {
+            final OutputFileInfo outputInfo = loadOutputInfo(file);
+            if (outputInfo != null) {
+              final String srcPath = outputInfo.getSourceFilePath();
+              final VirtualFile srcFile = srcPath != null? LocalFileSystem.getInstance().findFileByPath(srcPath) : null;
+              if (srcFile != null) {
+                final SourceFileInfo srcInfo = loadSourceInfo(srcFile);
+                if (srcInfo != null) {
+                  final boolean srcWillBeDeleted = VfsUtil.isAncestor(eventFile, srcFile, false);
+                  for (int projectId : srcInfo.getProjectIds().toArray()) {
+                    if (isSuspended(projectId)) {
+                      continue;
+                    }
+                    if (srcInfo.isAssociated(projectId, filePath)) {
+                      myAssociatedProjectIds.add(projectId);
+                      if (srcWillBeDeleted) {
+                        if (LOG.isDebugEnabled() || ourDebugMode) {
+                          final String message = "Unschedule recompilation because of deletion " + srcFile.getPresentableUrl();
+                          LOG.debug(message);
+                          if (ourDebugMode) {
+                            System.out.println(message);
+                          }
+                        }
+                        removeSourceForRecompilation(projectId, Math.abs(getFileId(srcFile)));
+                      }
+                      else {
+                        addSourceForRecompilation(projectId, srcFile, srcInfo);
+                      }
+                    }
+                  }
+                }
+              }
+            }
+
+            final SourceFileInfo srcInfo = loadSourceInfo(file);
+            if (srcInfo != null) {
+              final TIntHashSet projects = srcInfo.getProjectIds();
+              if (!projects.isEmpty()) {
+                final ScheduleOutputsForDeletionProc deletionProc = new ScheduleOutputsForDeletionProc(file.getUrl());
+                deletionProc.setRootBeingDeleted(eventFile);
+                final int sourceFileId = Math.abs(getFileId(file));
+                for (int projectId : projects.toArray()) {
+                  if (isSuspended(projectId)) {
+                    continue;
+                  }
+                  if (srcInfo.isAssociated(projectId, filePath)) {
+                    myAssociatedProjectIds.add(projectId);
+                  }
+                  // mark associated outputs for deletion
+                  srcInfo.processOutputPaths(projectId, deletionProc);
+                  if (LOG.isDebugEnabled() || ourDebugMode) {
+                    final String message = "Unschedule recompilation because of deletion " + file.getPresentableUrl();
+                    LOG.debug(message);
+                    if (ourDebugMode) {
+                      System.out.println(message);
+                    }
+                  }
+                  removeSourceForRecompilation(projectId, sourceFileId);
+                }
+              }
+            }
+          }
+          finally {
+            // it is important that update of myOutputsToDelete is done at the end
+            // otherwise the filePath of the file that is about to be deleted may be re-scheduled for deletion in addSourceForRecompilation()
+            myAssociatedProjectIds.forEach(new TIntProcedure() {
+              public boolean execute(int projectId) {
+                unmarkOutputPathForDeletion(projectId, filePath);
+                return true;
+              }
+            });
+          }
+        }
+      });
+
+      notifyFilesDeleted(pathsToMark);
+    }
+
+    public void beforeFileMovement(final VirtualFileMoveEvent event) {
+      markDirtyIfSource(event.getFile(), true);
+    }
+
+    private void markDirtyIfSource(final VirtualFile file, final boolean fromMove) {
+      final Set<File> pathsToMark = new THashSet<File>(FileUtil.FILE_HASHING_STRATEGY);
+      processRecursively(file, false, new FileProcessor() {
+        public void execute(final VirtualFile file) {
+          pathsToMark.add(new File(file.getPath()));
+          final SourceFileInfo srcInfo = file.isValid()? loadSourceInfo(file) : null;
+          if (srcInfo != null) {
+            for (int projectId : srcInfo.getProjectIds().toArray()) {
+              if (isSuspended(projectId)) {
+                if (srcInfo.clearPaths(projectId)) {
+                  srcInfo.updateTimestamp(projectId, -1L);
+                  saveSourceInfo(file, srcInfo);
+                }
+              }
+              else {
+                addSourceForRecompilation(projectId, file, srcInfo);
+                // when the file is moved to a new location, we should 'forget' previous associations
+                if (fromMove) {
+                  if (srcInfo.clearPaths(projectId)) {
+                    saveSourceInfo(file, srcInfo);
+                  }
+                }
+              }
+            }
+          }
+          else {
+            processNewFile(file, false);
+          }
+        }
+      });
+      if (fromMove) {
+        notifyFilesDeleted(pathsToMark);
+      }
+      else if (!isIgnoredOrUnderIgnoredDirectory(file)) {
+        notifyFilesChanged(pathsToMark);
+      }
+    }
+
+    private void processNewFile(final VirtualFile file, final boolean notifyServer) {
+      final Ref<Boolean> isInContent = Ref.create(false);
+      ApplicationManager.getApplication().runReadAction(new Runnable() {
+        // need read action to ensure that the project was not disposed during the iteration over the project list
+        public void run() {
+          for (final Project project : myProjectManager.getOpenProjects()) {
+            if (!project.isInitialized()) {
+              continue; // the content of this project will be scanned during its post-startup activities
+            }
+            final int projectId = getProjectId(project);
+            final boolean projectSuspended = isSuspended(projectId);
+            final ProjectRootManager rootManager = ProjectRootManager.getInstance(project);
+            ProjectFileIndex fileIndex = rootManager.getFileIndex();
+            if (fileIndex.isInContent(file)) {
+              isInContent.set(true);
+            }
+
+            if (fileIndex.isInSourceContent(file)) {
+              final TranslatingCompiler[] translators = CompilerManager.getInstance(project).getCompilers(TranslatingCompiler.class);
+              processRecursively(file, false, new FileProcessor() {
+                public void execute(final VirtualFile file) {
+                  if (!projectSuspended && isCompilable(file)) {
+                    loadInfoAndAddSourceForRecompilation(projectId, file);
+                  }
+                }
+
+                boolean isCompilable(VirtualFile file) {
+                  for (TranslatingCompiler translator : translators) {
+                    if (translator.isCompilableFile(file, DummyCompileContext.getInstance())) {
+                      return true;
+                    }
+                  }
+                  return false;
+                }
+              });
+            }
+            else {
+              if (!projectSuspended && belongsToIntermediateSources(file, project)) {
+                processRecursively(file, false, new FileProcessor() {
+                  public void execute(final VirtualFile file) {
+                    loadInfoAndAddSourceForRecompilation(projectId, file);
+                  }
+                });
+              }
+            }
+          }
+        }
+      });
+      if (notifyServer && !isIgnoredOrUnderIgnoredDirectory(file)) {
+        final Set<File> pathsToMark = new THashSet<File>(FileUtil.FILE_HASHING_STRATEGY);
+        boolean dbOnly = !isInContent.get();
+        processRecursively(file, dbOnly, new FileProcessor() {
+          @Override
+          public void execute(VirtualFile file) {
+            pathsToMark.add(new File(file.getPath()));
+          }
+        });
+        notifyFilesChanged(pathsToMark);
+      }
+    }
+  }
+
+  private boolean isIgnoredOrUnderIgnoredDirectory(final VirtualFile file) {
+    FileTypeManager fileTypeManager = FileTypeManager.getInstance();
+    if (fileTypeManager.isFileIgnored(file)) {
+      return true;
+    }
+
+    //optimization: if file is in content of some project it's definitely not ignored
+    boolean isInContent = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
+      @Override
+      public Boolean compute() {
+        for (Project project : myProjectManager.getOpenProjects()) {
+          if (project.isInitialized() && ProjectRootManager.getInstance(project).getFileIndex().isInContent(file)) {
+            return true;
+          }
+        }
+        return false;
+      }
+    });
+    if (isInContent) {
+      return false;
+    }
+
+    VirtualFile current = file.getParent();
+    while (current != null) {
+      if (fileTypeManager.isFileIgnored(current)) {
+        return true;
+      }
+      current = current.getParent();
+    }
+    return false;
+  }
+
+  private static void notifyFilesChanged(Collection<File> paths) {
+    if (!paths.isEmpty()) {
+      BuildManager.getInstance().notifyFilesChanged(paths);
+    }
+  }
+
+  private static void notifyFilesDeleted(Collection<File> paths) {
+    if (!paths.isEmpty()) {
+      BuildManager.getInstance().notifyFilesDeleted(paths);
+    }
+  }
+
+  private boolean belongsToIntermediateSources(VirtualFile file, Project project) {
+    return FileUtil.isAncestor(myGeneratedDataPaths.get(project), new File(file.getPath()), true);
+  }
+
+  private void loadInfoAndAddSourceForRecompilation(final int projectId, final VirtualFile srcFile) {
+    addSourceForRecompilation(projectId, srcFile, loadSourceInfo(srcFile));
+  }
+  private void addSourceForRecompilation(final int projectId, final VirtualFile srcFile, @Nullable final SourceFileInfo srcInfo) {
+    final boolean alreadyMarked;
+    synchronized (myDataLock) {
+      TIntHashSet set = mySourcesToRecompile.get(projectId);
+      if (set == null) {
+        set = new TIntHashSet();
+        mySourcesToRecompile.put(projectId, set);
+      }
+      alreadyMarked = !set.add(Math.abs(getFileId(srcFile)));
+      if (!alreadyMarked && (LOG.isDebugEnabled() || ourDebugMode)) {
+        final String message = "Scheduled recompilation " + srcFile.getPresentableUrl();
+        LOG.debug(message);
+        if (ourDebugMode) {
+          System.out.println(message);
+        }
+      }
+    }
+
+    if (!alreadyMarked && srcInfo != null) {
+      srcInfo.updateTimestamp(projectId, -1L);
+      srcInfo.processOutputPaths(projectId, new ScheduleOutputsForDeletionProc(srcFile.getUrl()));
+      saveSourceInfo(srcFile, srcInfo);
+    }
+  }
+
+  private void removeSourceForRecompilation(final int projectId, final int srcId) {
+    synchronized (myDataLock) {
+      TIntHashSet set = mySourcesToRecompile.get(projectId);
+      if (set != null) {
+        set.remove(srcId);
+        if (set.isEmpty()) {
+          mySourcesToRecompile.remove(projectId);
+        }
+      }
+    }
+  }
+  
+  public boolean isMarkedForCompilation(Project project, VirtualFile file) {
+    if (CompilerWorkspaceConfiguration.getInstance(project).USE_COMPILE_SERVER) {
+      final CompilerManager compilerManager = CompilerManager.getInstance(project);
+      return !compilerManager.isUpToDate(compilerManager.createFilesCompileScope(new VirtualFile[]{file}));
+    }
+    return isMarkedForRecompilation(getProjectId(project), getFileId(file));
+  }
+  
+  private boolean isMarkedForRecompilation(int projectId, final int srcId) {
+    synchronized (myDataLock) {
+      final TIntHashSet set = mySourcesToRecompile.get(projectId);
+      return set != null && set.contains(srcId);
+    }
+  }
+  
+  private interface Proc {
+    boolean execute(final int projectId, String outputPath);
+  }
+  
+  private class ScheduleOutputsForDeletionProc implements Proc {
+    private final String mySrcUrl;
+    private final LocalFileSystem myFileSystem;
+    @Nullable
+    private VirtualFile myRootBeingDeleted;
+
+    private ScheduleOutputsForDeletionProc(final String srcUrl) {
+      mySrcUrl = srcUrl;
+      myFileSystem = LocalFileSystem.getInstance();
+    }
+
+    public void setRootBeingDeleted(@Nullable VirtualFile rootBeingDeleted) {
+      myRootBeingDeleted = rootBeingDeleted;
+    }
+
+    public boolean execute(final int projectId, String outputPath) {
+      final VirtualFile outFile = myFileSystem.findFileByPath(outputPath);
+      if (outFile != null) { // not deleted yet
+        if (myRootBeingDeleted != null && VfsUtil.isAncestor(myRootBeingDeleted, outFile, false)) {
+          unmarkOutputPathForDeletion(projectId, outputPath);
+        }
+        else {
+          final OutputFileInfo outputInfo = loadOutputInfo(outFile);
+          final String classname = outputInfo != null? outputInfo.getClassName() : null;
+          markOutputPathForDeletion(projectId, outputPath, classname, mySrcUrl);
+        }
+      }
+      return true;
+    }
+  }
+
+  private void markOutputPathForDeletion(final int projectId, final String outputPath, final String classname, final String srcUrl) {
+    final SourceUrlClassNamePair pair = new SourceUrlClassNamePair(srcUrl, classname);
+    synchronized (myDataLock) {
+      final Outputs outputs = myOutputsToDelete.get(projectId);
+      try {
+        outputs.put(outputPath, pair);
+        if (LOG.isDebugEnabled() || ourDebugMode) {
+          final String message = "ADD path to delete: " + outputPath + "; source: " + srcUrl;
+          LOG.debug(message);
+          if (ourDebugMode) {
+            System.out.println(message);
+          }
+        }
+      }
+      finally {
+        outputs.release();
+      }
+    }
+  }
+
+  private void unmarkOutputPathForDeletion(final int projectId, String outputPath) {
+    synchronized (myDataLock) {
+      final Outputs outputs = myOutputsToDelete.get(projectId);
+      try {
+        final SourceUrlClassNamePair val = outputs.remove(outputPath);
+        if (val != null) {
+          if (LOG.isDebugEnabled() || ourDebugMode) {
+            final String message = "REMOVE path to delete: " + outputPath;
+            LOG.debug(message);
+            if (ourDebugMode) {
+              System.out.println(message);
+            }
+          }
+        }
+      }
+      finally {
+        outputs.release();
+      }
+    }
+  }
+
+  public static final class ProjectRef extends Ref<Project> {
+    static class ProjectClosedException extends RuntimeException {
+    }
+
+    public ProjectRef(Project project) {
+      super(project);
+    }
+
+    public Project get() {
+      final Project project = super.get();
+      if (project != null && project.isDisposed()) {
+        throw new ProjectClosedException();
+      }
+      return project;
+    }
+  }
+  
+  private static class Outputs {
+    private boolean myIsDirty = false;
+    @Nullable
+    private final File myStoreFile;
+    private final Map<String, SourceUrlClassNamePair> myMap;
+    private final AtomicInteger myRefCount = new AtomicInteger(1);
+    
+    Outputs(@Nullable File storeFile, Map<String, SourceUrlClassNamePair> map) {
+      myStoreFile = storeFile;
+      myMap = map;
+    }
+
+    public Set<Map.Entry<String, SourceUrlClassNamePair>> getEntries() {
+      return Collections.unmodifiableSet(myMap.entrySet());
+    }
+    
+    public void put(String outputPath, SourceUrlClassNamePair pair) {
+      if (myStoreFile == null) {
+        return;
+      }
+      if (pair == null) {
+        remove(outputPath);
+      }
+      else {
+        myMap.put(outputPath, pair);
+        myIsDirty = true;
+      }
+    }
+    
+    public SourceUrlClassNamePair remove(String outputPath) {
+      if (myStoreFile == null) {
+        return null;
+      }
+      final SourceUrlClassNamePair removed = myMap.remove(outputPath);
+      myIsDirty |= removed != null;
+      return removed;
+    }
+    
+    void allocate() {
+      myRefCount.incrementAndGet();
+    }
+    
+    public void release() {
+      if (myRefCount.decrementAndGet() == 0) {
+        if (myIsDirty && myStoreFile != null) {
+          savePathsToDelete(myStoreFile, myMap);
+        }
+      }
+    }
+  }
+  
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/TreeBasedMap.java b/java/compiler/impl/src/com/intellij/compiler/impl/TreeBasedMap.java
new file mode 100644
index 0000000..0031966
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/TreeBasedMap.java
@@ -0,0 +1,239 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import com.intellij.util.containers.StringInterner;
+import com.intellij.util.containers.EmptyIterator;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Stack;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Jun 18, 2006
+ */
+public class TreeBasedMap<T> {
+  private Node<T> myRoot = new Node<T>();
+  private final StringInterner myInterner;
+  private final char mySeparator;
+  private int mySize = 0;
+
+  public TreeBasedMap(StringInterner table, final char separator) {
+    myInterner = table;
+    mySeparator = separator;
+  }
+
+  private class Node<T> {
+    private boolean myMappingExists = false;
+    private T myValue = null;
+    private @Nullable HashMap<String, Node<T>> myChildren = null;
+
+    public void setValue(T value) {
+      myValue = value;
+      myMappingExists = true;
+    }
+
+    public T getValue() {
+      return myValue;
+    }
+
+    public void removeValue() {
+      myValue = null;
+      myMappingExists = false;
+    }
+
+    public boolean mappingExists() {
+      return myMappingExists;
+    }
+
+    @Nullable
+    public Node<T> findRelative(String text, boolean create, final StringInterner table) {
+      return findRelative(text, 0, create, table);
+    }
+
+    @Nullable
+    private Node<T> findRelative(final String text, final int nameStartIndex, final boolean create, final StringInterner table) {
+      if (myChildren == null && !create) {
+        return null;
+      }
+
+      final int textLen = text.length();
+      final int separatorIdx = text.indexOf(mySeparator, nameStartIndex);
+      final int childNameEnd = separatorIdx >= 0 ? separatorIdx : textLen;
+
+      if (myChildren != null) {
+        final Node<T> child = myChildren.get(text.substring(nameStartIndex, childNameEnd));
+        if (child != null) {
+          if (separatorIdx < 0) {
+            return child;
+          }
+          return child.findRelative(text, childNameEnd + 1, create, table);
+        }
+      }
+
+      if (create) {
+        return addChild(table, text, nameStartIndex, childNameEnd);
+      }
+
+      return null;
+    }
+
+    @NotNull
+    private Node<T> addChild(final StringInterner table, final String text, final int nameStartIndex, final int nameEndIndex) {
+      if (myChildren == null) {
+        myChildren = new HashMap<String, Node<T>>(3, 0.95f);
+      }
+
+      Node<T> newChild = new Node<T>();
+      final String key = table.intern(text.substring(nameStartIndex, nameEndIndex));
+      myChildren.put(key, newChild);
+
+      if (nameEndIndex == text.length()) {
+        return newChild;
+      }
+
+      Node<T> restNodes = newChild.findRelative(text, nameEndIndex + 1, true, table);
+      assert restNodes != null;
+      return restNodes;
+    }
+  }
+
+  public void put(String key, T value) {
+    final Node<T> node = myRoot.findRelative(key, true, myInterner);
+    assert node != null;
+    final boolean mappingExisted = node.mappingExists();
+    node.setValue(value);
+    if (!mappingExisted) {
+      mySize++;
+    }
+  }
+
+  public void remove(String key) {
+    final Node node = myRoot.findRelative(key, false, myInterner);
+    if (node != null && node.mappingExists()) {
+      node.removeValue();
+      mySize--;
+    }
+  }
+
+  public int size() {
+    return mySize;
+  }
+
+  public T get(String key) {
+    final Node<T> node = myRoot.findRelative(key, false, myInterner);
+    return (node != null && node.mappingExists()) ? node.getValue() : null;
+  }
+
+  public boolean containsKey(String key) {
+    final Node<T> node = myRoot.findRelative(key, false, myInterner);
+    return node != null && node.mappingExists();
+  }
+
+  public void removeAll() {
+    myRoot = new Node<T>();
+  }
+
+  public Iterator<String> getKeysIterator() {
+    return new KeysIterator();
+  }
+
+
+  private class KeysIterator implements Iterator<String> {
+    private final Stack<PathElement<T>> myCurrentNodePath = new Stack<PathElement<T>>();
+    private final StringBuilder myCurrentName = new StringBuilder();
+
+    public KeysIterator() {
+      pushNode("", myRoot);
+      findNextNode();
+    }
+
+    public boolean hasNext() {
+      return myCurrentNodePath.size() > 0;
+    }
+
+    public String next() {
+      final String key = myCurrentName.toString();
+      popNode();
+      findNextNode();
+      return key;
+    }
+
+    public void remove() {
+      throw new UnsupportedOperationException("Remove not supported");
+    }
+
+    private boolean pushNode(final @NotNull String name, @NotNull Node<T> node) {
+      final HashMap<String, Node<T>> childrenMap = node.myChildren;
+      final boolean hasChildren = childrenMap != null && childrenMap.size() > 0;
+      if (hasChildren || node.mappingExists()) {
+        myCurrentNodePath.push(new PathElement<T>(node, hasChildren? childrenMap.keySet().iterator() : EmptyIterator.<String>getInstance()));
+        if (myCurrentNodePath.size() > 2) {
+          // do not add separator before the Root and its direct child nodes 
+          myCurrentName.append(mySeparator);
+        }
+        myCurrentName.append(name);
+        return true;
+      }
+      return false;
+    }
+
+    private void popNode() {
+      myCurrentNodePath.pop();
+      final int separatorIndex = myCurrentName.lastIndexOf(String.valueOf(mySeparator));
+      if (separatorIndex >= 0) {
+        myCurrentName.replace(separatorIndex, myCurrentName.length(), "");
+      }
+      else {
+        myCurrentName.setLength(0);
+      }
+    }
+
+    private void findNextNode() {
+      MAIN_LOOP: while (!myCurrentNodePath.isEmpty()) {
+        final PathElement<T> element = myCurrentNodePath.peek();
+        final Iterator<String> childrenIterator = element.iterator;
+        final Node<T> currentNode = element.node;
+        while (childrenIterator.hasNext()) {
+          final String name = childrenIterator.next();
+          final Node<T> childNode = currentNode.myChildren.get(name);
+          if (pushNode(name, childNode)) {
+            continue MAIN_LOOP;
+          }
+        }
+        if (!currentNode.mappingExists()) {
+          popNode();
+        }
+        else {
+          break;
+        }
+      }
+    }
+  }
+
+  private class PathElement<T> {
+    final @NotNull Iterator<String> iterator;
+    final @NotNull Node<T> node;
+    public PathElement(@NotNull final Node<T> node, Iterator<String> iterator) {
+      this.node = node;
+      this.iterator = iterator;
+    }
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/TreeBasedPathsSet.java b/java/compiler/impl/src/com/intellij/compiler/impl/TreeBasedPathsSet.java
new file mode 100644
index 0000000..4fd3ee8
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/TreeBasedPathsSet.java
@@ -0,0 +1,42 @@
+/*
+ * 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 com.intellij.compiler.impl;
+
+import com.intellij.util.containers.StringInterner;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Jun 19, 2006
+ */
+public class TreeBasedPathsSet {
+  private final TreeBasedMap<Object> myMap;
+
+  public TreeBasedPathsSet(StringInterner interner, char separator) {
+    myMap = new TreeBasedMap<Object>(interner, separator);
+  }
+
+  public void add(String path) {
+    myMap.put(path, null);
+  }
+
+  public void remove(String path) {
+    myMap.remove(path);
+  }
+
+  public boolean contains(String path) {
+    return myMap.containsKey(path);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/generic/GenericCompilerCache.java b/java/compiler/impl/src/com/intellij/compiler/impl/generic/GenericCompilerCache.java
new file mode 100644
index 0000000..5741b9c
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/generic/GenericCompilerCache.java
@@ -0,0 +1,178 @@
+/*
+ * 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 com.intellij.compiler.impl.generic;
+
+import com.intellij.openapi.compiler.generic.GenericCompiler;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.util.Processor;
+import com.intellij.util.io.DataExternalizer;
+import com.intellij.util.io.KeyDescriptor;
+import com.intellij.util.io.PersistentEnumerator;
+import com.intellij.util.io.PersistentHashMap;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * @author nik
+ */
+public class GenericCompilerCache<Key, SourceState, OutputState> {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.generic.GenericCompilerCache");
+  private PersistentHashMap<KeyAndTargetData<Key>, PersistentStateData<SourceState, OutputState>> myPersistentMap;
+  private File myCacheFile;
+  private final GenericCompiler<Key, SourceState, OutputState> myCompiler;
+
+  public GenericCompilerCache(GenericCompiler<Key, SourceState, OutputState> compiler, final File compilerCacheDir) throws IOException {
+    myCompiler = compiler;
+    myCacheFile = new File(compilerCacheDir, "timestamps");
+    createMap();
+  }
+
+  private void createMap() throws IOException {
+    try {
+      myPersistentMap = new PersistentHashMap<KeyAndTargetData<Key>, PersistentStateData<SourceState,OutputState>>(myCacheFile, new SourceItemDataDescriptor(myCompiler.getItemKeyDescriptor()),
+                                                                    new PersistentStateDataExternalizer(myCompiler));
+    }
+    catch (PersistentEnumerator.CorruptedException e) {
+      FileUtil.delete(myCacheFile);
+      throw e;
+    }
+  }
+
+  private KeyAndTargetData<Key> getKeyAndTargetData(Key key, int target) {
+    return new KeyAndTargetData<Key>(target, key);
+  }
+
+  public void wipe() throws IOException {
+    try {
+      myPersistentMap.close();
+    }
+    catch (IOException ignored) {
+    }
+    PersistentHashMap.deleteFilesStartingWith(myCacheFile);
+    createMap();
+  }
+
+  public void close() {
+    try {
+      myPersistentMap.close();
+    }
+    catch (IOException e) {
+      LOG.info(e);
+    }
+  }
+
+  public void remove(int targetId, Key key) throws IOException {
+    myPersistentMap.remove(getKeyAndTargetData(key, targetId));
+  }
+
+  public PersistentStateData<SourceState, OutputState> getState(int targetId, Key key) throws IOException {
+    return myPersistentMap.get(getKeyAndTargetData(key, targetId));
+  }
+
+  public void processSources(final int targetId, final Processor<Key> processor) throws IOException {
+    myPersistentMap.processKeysWithExistingMapping(new Processor<KeyAndTargetData<Key>>() {
+      @Override
+      public boolean process(KeyAndTargetData<Key> data) {
+        return targetId == data.myTarget ? processor.process(data.myKey) : true;
+      }
+    });
+  }
+
+  public void putState(int targetId, @NotNull Key key, @NotNull SourceState sourceState, @NotNull OutputState outputState) throws IOException {
+    myPersistentMap.put(getKeyAndTargetData(key, targetId), new PersistentStateData<SourceState,OutputState>(sourceState, outputState));
+  }
+
+
+  private static class KeyAndTargetData<Key> {
+    public final int myTarget;
+    public final Key myKey;
+
+    private KeyAndTargetData(int target, Key key) {
+      myTarget = target;
+      myKey = key;
+    }
+  }
+
+  public static class PersistentStateData<SourceState, OutputState> {
+    public final SourceState mySourceState;
+    public final OutputState myOutputState;
+
+    private PersistentStateData(@NotNull SourceState sourceState, @NotNull OutputState outputState) {
+      mySourceState = sourceState;
+      myOutputState = outputState;
+    }
+  }
+  
+  private class SourceItemDataDescriptor implements KeyDescriptor<KeyAndTargetData<Key>> {
+    private final KeyDescriptor<Key> myKeyDescriptor;
+
+    public SourceItemDataDescriptor(KeyDescriptor<Key> keyDescriptor) {
+      myKeyDescriptor = keyDescriptor;
+    }
+
+    @Override
+    public boolean isEqual(KeyAndTargetData<Key> val1, KeyAndTargetData<Key> val2) {
+      return val1.myTarget == val2.myTarget;
+    }
+
+    @Override
+    public int getHashCode(KeyAndTargetData<Key> value) {
+      return value.myTarget + 239 * myKeyDescriptor.getHashCode(value.myKey);
+    }
+
+    @Override
+    public void save(DataOutput out, KeyAndTargetData<Key> value) throws IOException {
+      out.writeInt(value.myTarget);
+      myKeyDescriptor.save(out, value.myKey);
+    }
+
+
+    @Override
+    public KeyAndTargetData<Key> read(DataInput in) throws IOException {
+      int target = in.readInt();
+      final Key item = myKeyDescriptor.read(in);
+      return getKeyAndTargetData(item, target);
+    }
+  }
+
+  private class PersistentStateDataExternalizer implements DataExternalizer<PersistentStateData<SourceState, OutputState>> {
+    private DataExternalizer<SourceState> mySourceStateExternalizer;
+    private DataExternalizer<OutputState> myOutputStateExternalizer;
+
+    public PersistentStateDataExternalizer(GenericCompiler<Key,SourceState,OutputState> compiler) {
+      mySourceStateExternalizer = compiler.getSourceStateExternalizer();
+      myOutputStateExternalizer = compiler.getOutputStateExternalizer();
+    }
+
+    @Override
+    public void save(DataOutput out, PersistentStateData<SourceState, OutputState> value) throws IOException {
+      mySourceStateExternalizer.save(out, value.mySourceState);
+      myOutputStateExternalizer.save(out, value.myOutputState);
+    }
+
+    @Override
+    public PersistentStateData<SourceState, OutputState> read(DataInput in) throws IOException {
+      SourceState sourceState = mySourceStateExternalizer.read(in);
+      OutputState outputState = myOutputStateExternalizer.read(in);
+      return new PersistentStateData<SourceState,OutputState>(sourceState, outputState);
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/generic/GenericCompilerPersistentData.java b/java/compiler/impl/src/com/intellij/compiler/impl/generic/GenericCompilerPersistentData.java
new file mode 100644
index 0000000..513be6c
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/generic/GenericCompilerPersistentData.java
@@ -0,0 +1,119 @@
+/*
+ * 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 com.intellij.compiler.impl.generic;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.io.IOUtil;
+import gnu.trove.TIntHashSet;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class GenericCompilerPersistentData {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.generic.GenericCompilerPersistentData");
+  private static final int VERSION = 1;
+  private File myFile;
+  private Map<String, Integer> myTarget2Id = new HashMap<String, Integer>();
+  private TIntHashSet myUsedIds = new TIntHashSet();
+  private boolean myVersionChanged;
+  private final int myCompilerVersion;
+
+  public GenericCompilerPersistentData(File cacheStoreDirectory, int compilerVersion) throws IOException {
+    myCompilerVersion = compilerVersion;
+    myFile = new File(cacheStoreDirectory, "info");
+    if (!myFile.exists()) {
+      LOG.info("Compiler info file doesn't exists: " + myFile.getAbsolutePath());
+      myVersionChanged = true;
+      return;
+    }
+
+    DataInputStream input = new DataInputStream(new BufferedInputStream(new FileInputStream(myFile)));
+    try {
+      final int dataVersion = input.readInt();
+      if (dataVersion != VERSION) {
+        LOG.info("Version of compiler info file (" + myFile.getAbsolutePath() + ") changed: " + dataVersion + " -> " + VERSION);
+        myVersionChanged = true;
+        return;
+      }
+
+      final int savedCompilerVersion = input.readInt();
+      if (savedCompilerVersion != compilerVersion) {
+        LOG.info("Compiler caches version changed (" + myFile.getAbsolutePath() + "): " + savedCompilerVersion + " -> " + compilerVersion);
+        myVersionChanged = true;
+        return;
+      }
+
+      int size = input.readInt();
+      while (size-- > 0) {
+        final String target = IOUtil.readString(input);
+        final int id = input.readInt();
+        myTarget2Id.put(target, id);
+        myUsedIds.add(id);
+      }
+    }
+    finally {
+      input.close();
+    }
+  }
+
+  public boolean isVersionChanged() {
+    return myVersionChanged;
+  }
+
+  public void save() throws IOException {
+    final DataOutputStream output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(myFile)));
+    try {
+      output.writeInt(VERSION);
+      output.writeInt(myCompilerVersion);
+      output.writeInt(myTarget2Id.size());
+
+      for (Map.Entry<String, Integer> entry : myTarget2Id.entrySet()) {
+        IOUtil.writeString(entry.getKey(), output);
+        output.writeInt(entry.getValue());
+      }
+    }
+    finally {
+      output.close();
+    }
+  }
+
+  public int getId(@NotNull String target) {
+    if (myTarget2Id.containsKey(target)) {
+      return myTarget2Id.get(target);
+    }
+    int id = 0;
+    while (myUsedIds.contains(id)) {
+      id++;
+    }
+    myTarget2Id.put(target, id);
+    myUsedIds.add(id);
+    return id;
+  }
+
+  public Set<String> getAllTargets() {
+    return myTarget2Id.keySet();
+  }
+
+  public int removeId(String target) {
+    return myTarget2Id.remove(target);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/AnnotationProcessingCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/AnnotationProcessingCompiler.java
new file mode 100644
index 0000000..c7269a4
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/AnnotationProcessingCompiler.java
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: Jan 17, 2003
+ * Time: 3:22:59 PM
+ */
+package com.intellij.compiler.impl.javaCompiler;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.CompilerConfigurationImpl;
+import com.intellij.compiler.CompilerException;
+import com.intellij.compiler.impl.CompileContextExProxy;
+import com.intellij.compiler.impl.javaCompiler.javac.JavacCompiler;
+import com.intellij.compiler.make.CacheCorruptedException;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.*;
+import com.intellij.openapi.compiler.ex.CompileContextEx;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.Chunk;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public class AnnotationProcessingCompiler implements TranslatingCompiler{
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.javaCompiler.JavaCompiler");
+  private final Project myProject;
+  private final CompilerConfiguration myConfig;
+
+  public AnnotationProcessingCompiler(Project project) {
+    myProject = project;
+    myConfig = CompilerConfiguration.getInstance(project);
+  }
+
+  @NotNull
+  public String getDescription() {
+    return CompilerBundle.message("annotation.processing.compiler.description");
+  }
+
+  public boolean isCompilableFile(VirtualFile file, CompileContext context) {
+    if (!context.isAnnotationProcessorsEnabled()) {
+      return false;
+    } 
+    return file.getFileType() == StdFileTypes.JAVA && !isExcludedFromAnnotationProcessing(file, context);
+  }
+
+  public void compile(final CompileContext context, final Chunk<Module> moduleChunk, final VirtualFile[] files, OutputSink sink) {
+    if (!context.isAnnotationProcessorsEnabled()) {
+      return;
+    }
+    final LocalFileSystem lfs = LocalFileSystem.getInstance();
+    final CompileContextEx _context = new CompileContextExProxy((CompileContextEx)context) {
+      public VirtualFile getModuleOutputDirectory(Module module) {
+        final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module);
+        return path != null? lfs.findFileByPath(path) : null;
+      }
+
+      public VirtualFile getModuleOutputDirectoryForTests(Module module) {
+        return getModuleOutputDirectory(module);
+      }
+    };
+    final JavacCompiler javacCompiler = getBackEndCompiler();
+    final boolean processorMode = javacCompiler.setAnnotationProcessorMode(true);
+    final BackendCompilerWrapper wrapper = new BackendCompilerWrapper(moduleChunk, myProject, Arrays.asList(files), _context, javacCompiler, sink);
+    wrapper.setForceCompileTestsSeparately(true);
+    try {
+      wrapper.compile();
+    }
+    catch (CompilerException e) {
+      _context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
+    }
+    catch (CacheCorruptedException e) {
+      LOG.info(e);
+      _context.requestRebuildNextTime(e.getMessage());
+    }
+    finally {
+      javacCompiler.setAnnotationProcessorMode(processorMode);
+      final Set<VirtualFile> dirsToRefresh = new HashSet<VirtualFile>();
+      ApplicationManager.getApplication().runReadAction(new Runnable() {
+        public void run() {
+          for (Module module : moduleChunk.getNodes()) {
+            final VirtualFile out = _context.getModuleOutputDirectory(module);
+            if (out != null) {
+              dirsToRefresh.add(out);
+            }
+          }
+        }
+      });
+      for (VirtualFile root : dirsToRefresh) {
+        root.refresh(false, true);
+      }
+    }
+  }
+
+  private boolean isExcludedFromAnnotationProcessing(VirtualFile file, CompileContext context) {
+    if (!context.isAnnotationProcessorsEnabled()) {
+      return true;
+    }
+    final Module module = context.getModuleByFile(file);
+    if (module != null) {
+      if (!myConfig.getAnnotationProcessingConfiguration(module).isEnabled()) {
+        return true;
+      }
+      final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module);
+      final VirtualFile generationDir = path != null? LocalFileSystem.getInstance().findFileByPath(path) : null;
+      if (generationDir != null && VfsUtil.isAncestor(generationDir, file, false)) {
+        return true;
+      }
+    }
+    return myConfig.isExcludedFromCompilation(file);
+  }
+
+  public boolean validateConfiguration(CompileScope scope) {
+    final JavacCompiler compiler = getBackEndCompiler();
+    final boolean previousValue = compiler.setAnnotationProcessorMode(true);
+    try {
+      return compiler.checkCompiler(scope);
+    }
+    finally {
+      compiler.setAnnotationProcessorMode(previousValue);
+    }
+  }
+
+  private JavacCompiler getBackEndCompiler() {
+    CompilerConfigurationImpl configuration = (CompilerConfigurationImpl)myConfig;
+    return configuration.getJavacCompiler();
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/BackendCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/BackendCompiler.java
new file mode 100644
index 0000000..9da4a39
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/BackendCompiler.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 com.intellij.compiler.impl.javaCompiler;
+
+import com.intellij.compiler.OutputParser;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.options.Configurable;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.util.Set;
+
+public interface BackendCompiler {
+  ExtensionPointName<BackendCompiler> EP_NAME = ExtensionPointName.create("com.intellij.java.compiler");
+
+  @NotNull @NonNls String getId(); // used for externalization
+  @NotNull String getPresentableName();
+  @NotNull Configurable createConfigurable();
+  @NotNull Set<FileType> getCompilableFileTypes();
+  @Nullable OutputParser createErrorParser(@NotNull String outputDir, Process process);
+  @Nullable OutputParser createOutputParser(@NotNull String outputDir);
+
+  boolean checkCompiler(final CompileScope scope);
+
+  @NotNull Process launchProcess(
+    @NotNull ModuleChunk chunk,
+    @NotNull String outputDir,
+    @NotNull CompileContext compileContext) throws IOException;
+
+  void compileFinished();
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/BackendCompilerWrapper.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/BackendCompilerWrapper.java
new file mode 100644
index 0000000..a6567a5
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/BackendCompilerWrapper.java
@@ -0,0 +1,1014 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler;
+
+import com.intellij.codeInsight.NullableNotNullManager;
+import com.intellij.compiler.*;
+import com.intellij.compiler.classParsing.AnnotationConstantValue;
+import com.intellij.compiler.classParsing.MethodInfo;
+import com.intellij.compiler.impl.CompileDriver;
+import com.intellij.compiler.impl.CompilerUtil;
+import com.intellij.compiler.make.Cache;
+import com.intellij.compiler.make.CacheCorruptedException;
+import com.intellij.compiler.make.DependencyCache;
+import com.intellij.compiler.make.MakeUtil;
+import com.intellij.compiler.notNullVerification.NotNullVerifyingInstrumenter;
+import com.intellij.ide.util.projectWizard.JavaModuleBuilder;
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.*;
+import com.intellij.openapi.compiler.ex.CompileContextEx;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.module.JavaModuleType;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.JavaSdk;
+import com.intellij.openapi.projectRoots.JavaSdkType;
+import com.intellij.openapi.projectRoots.JavaSdkVersion;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.util.*;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileVisitor;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.search.GlobalSearchScopes;
+import com.intellij.util.Chunk;
+import com.intellij.util.cls.ClsFormatException;
+import gnu.trove.THashMap;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.asm4.ClassReader;
+import org.jetbrains.asm4.ClassWriter;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.*;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+/**
+ * @author Eugene Zhuravlev
+ * @since Jan 24, 2003
+ */
+public class BackendCompilerWrapper {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.javaCompiler.BackendCompilerWrapper");
+
+  private final BackendCompiler myCompiler;
+
+  private final CompileContextEx myCompileContext;
+  private final List<VirtualFile> myFilesToCompile;
+  private final TranslatingCompiler.OutputSink mySink;
+  private final Chunk<Module> myChunk;
+  private final Project myProject;
+  private final Map<Module, VirtualFile> myModuleToTempDirMap = new THashMap<Module, VirtualFile>();
+  private final ProjectFileIndex myProjectFileIndex;
+  @NonNls private static final String PACKAGE_ANNOTATION_FILE_NAME = "package-info.java";
+  private static final FileObject myStopThreadToken = new FileObject(new File(""), new byte[0]);
+  public final Map<String, Set<CompiledClass>> myFileNameToSourceMap=  new THashMap<String, Set<CompiledClass>>();
+  private final Set<VirtualFile> myProcessedPackageInfos = new HashSet<VirtualFile>();
+  private final CompileStatistics myStatistics;
+  private volatile String myModuleName = null;
+  private boolean myForceCompileTestsSeparately = false;
+
+  public BackendCompilerWrapper(Chunk<Module> chunk, @NotNull final Project project,
+                                @NotNull List<VirtualFile> filesToCompile,
+                                @NotNull CompileContextEx compileContext,
+                                @NotNull BackendCompiler compiler, TranslatingCompiler.OutputSink sink) {
+    myChunk = chunk;
+    myProject = project;
+    myCompiler = compiler;
+    myCompileContext = compileContext;
+    myFilesToCompile = filesToCompile;
+    mySink = sink;
+    myProjectFileIndex = ProjectRootManager.getInstance(myProject).getFileIndex();
+    CompileStatistics stat = compileContext.getUserData(CompileStatistics.KEY);
+    if (stat == null) {
+      stat = new CompileStatistics();
+      compileContext.putUserData(CompileStatistics.KEY, stat);
+    }
+    myStatistics = stat;
+  }
+
+  public void compile() throws CompilerException, CacheCorruptedException {
+    Application application = ApplicationManager.getApplication();
+    try {
+      if (!myFilesToCompile.isEmpty()) {
+        if (application.isUnitTestMode()) {
+          saveTestData();
+        }
+        compileModules(buildModuleToFilesMap(myFilesToCompile));
+      }
+    }
+    catch (SecurityException e) {
+      throw new CompilerException(CompilerBundle.message("error.compiler.process.not.started", e.getMessage()), e);
+    }
+    catch (IllegalArgumentException e) {
+      throw new CompilerException(e.getMessage(), e);
+    }
+    finally {
+      for (final VirtualFile file : myModuleToTempDirMap.values()) {
+        if (file != null) {
+          final File ioFile = new File(file.getPath());
+          FileUtil.asyncDelete(ioFile);
+        }
+      }
+      myModuleToTempDirMap.clear();
+    }
+
+    if (!myFilesToCompile.isEmpty() && myCompileContext.getMessageCount(CompilerMessageCategory.ERROR) == 0) {
+      // package-info.java hack
+      final List<TranslatingCompiler.OutputItem> outputs = new ArrayList<TranslatingCompiler.OutputItem>();
+      ApplicationManager.getApplication().runReadAction(new Runnable() {
+        public void run() {
+          for (final VirtualFile file : myFilesToCompile) {
+            if (PACKAGE_ANNOTATION_FILE_NAME.equals(file.getName()) && !myProcessedPackageInfos.contains(file)) {
+              outputs.add(new OutputItemImpl(file));
+            }
+          }
+        }
+      });
+      if (!outputs.isEmpty()) {
+        mySink.add(null, outputs, VirtualFile.EMPTY_ARRAY);
+      }
+    }
+  }
+
+  public boolean isForceCompileTestsSeparately() {
+    return myForceCompileTestsSeparately;
+  }
+
+  public void setForceCompileTestsSeparately(boolean forceCompileTestsSeparately) {
+    myForceCompileTestsSeparately = forceCompileTestsSeparately;
+  }
+
+  private Map<Module, List<VirtualFile>> buildModuleToFilesMap(final List<VirtualFile> filesToCompile) {
+    if (myChunk.getNodes().size() == 1) {
+      return Collections.singletonMap(myChunk.getNodes().iterator().next(), Collections.unmodifiableList(filesToCompile));
+    }
+    return CompilerUtil.buildModuleToFilesMap(myCompileContext, filesToCompile);
+  }
+
+  private void compileModules(final Map<Module, List<VirtualFile>> moduleToFilesMap) throws CompilerException {
+    try {
+      compileChunk(new ModuleChunk(myCompileContext, myChunk, moduleToFilesMap));
+    }
+    catch (IOException e) {
+      throw new CompilerException(e.getMessage(), e);
+    }
+  }
+
+  private void compileChunk(ModuleChunk chunk) throws IOException {
+    final String chunkPresentableName = getPresentableNameFor(chunk);
+    myModuleName = chunkPresentableName;
+
+    // validate encodings
+    if (chunk.getModuleCount() > 1) {
+      validateEncoding(chunk, chunkPresentableName);
+      // todo: validation for bytecode target?
+    }
+
+    runTransformingCompilers(chunk);
+
+
+    final List<OutputDir> outs = new ArrayList<OutputDir>();
+    File fileToDelete = getOutputDirsToCompileTo(chunk, outs);
+
+    try {
+      for (final OutputDir outputDir : outs) {
+        chunk.setSourcesFilter(outputDir.getKind());
+        doCompile(chunk, outputDir.getPath());
+      }
+    }
+    finally {
+      if (fileToDelete != null) {
+        FileUtil.asyncDelete(fileToDelete);
+      }
+    }
+  }
+
+  private void validateEncoding(ModuleChunk chunk, String chunkPresentableName) {
+    final CompilerEncodingService es = CompilerEncodingService.getInstance(myProject);
+    Charset charset = null;
+    for (Module module : chunk.getModules()) {
+      final Charset moduleCharset = es.getPreferredModuleEncoding(module);
+      if (charset == null) {
+        charset = moduleCharset;
+      }
+      else {
+        if (!Comparing.equal(charset, moduleCharset)) {
+          // warn user
+          final Charset chunkEncoding = CompilerEncodingService.getPreferredModuleEncoding(chunk);
+          final StringBuilder message = new StringBuilder();
+          message.append("Modules in chunk [");
+          message.append(chunkPresentableName);
+          message.append("] configured to use different encodings.\n");
+          if (chunkEncoding != null) {
+            message.append("\"").append(chunkEncoding.name()).append("\" encoding will be used to compile the chunk");
+          }
+          else {
+            message.append("Default compiler encoding will be used to compile the chunk");
+          }
+          myCompileContext.addMessage(CompilerMessageCategory.INFORMATION, message.toString(), null, -1, -1);
+          break;
+        }
+      }
+    }
+  }
+
+
+  private static String getPresentableNameFor(final ModuleChunk chunk) {
+    return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
+      public String compute() {
+        final Module[] modules = chunk.getModules();
+        StringBuilder moduleName = new StringBuilder(Math.min(128, modules.length * 8));
+        for (int idx = 0; idx < modules.length; idx++) {
+          final Module module = modules[idx];
+          if (idx > 0) {
+            moduleName.append(", ");
+          }
+          moduleName.append(module.getName());
+          if (moduleName.length() > 128 && idx + 1 < modules.length /*name is already too long and seems to grow longer*/) {
+            moduleName.append("...");
+            break;
+          }
+        }
+        return moduleName.toString();
+      }
+    });
+  }
+
+  @Nullable
+  private File getOutputDirsToCompileTo(ModuleChunk chunk, final List<OutputDir> dirs) throws IOException {
+    File fileToDelete = null;
+    if (chunk.getModuleCount() == 1) { // optimization
+      final Module module = chunk.getModules()[0];
+      ApplicationManager.getApplication().runReadAction(new Runnable() {
+        public void run() {
+          final String sourcesOutputDir = getOutputDir(module);
+          if (shouldCompileTestsSeparately(module)) {
+            if (sourcesOutputDir != null) {
+              dirs.add(new OutputDir(sourcesOutputDir, ModuleChunk.SOURCES));
+            }
+            final String testsOutputDir = getTestsOutputDir(module);
+            if (testsOutputDir == null) {
+              LOG.error("Tests output dir is null for module \"" + module.getName() + "\"");
+            }
+            else {
+              dirs.add(new OutputDir(testsOutputDir, ModuleChunk.TEST_SOURCES));
+            }
+          }
+          else { // both sources and test sources go into the same output
+            if (sourcesOutputDir == null) {
+              LOG.error("Sources output dir is null for module \"" + module.getName() + "\"");
+            }
+            else {
+              dirs.add(new OutputDir(sourcesOutputDir, ModuleChunk.ALL_SOURCES));
+            }
+          }
+        }
+      });
+    }
+    else { // chunk has several modules
+      final File outputDir = FileUtil.createTempDirectory("compile", "output");
+      fileToDelete = outputDir;
+      dirs.add(new OutputDir(outputDir.getPath(), ModuleChunk.ALL_SOURCES));
+    }
+    return fileToDelete;
+  }
+
+
+  private boolean shouldCompileTestsSeparately(Module module) {
+    if (myForceCompileTestsSeparately) {
+      return true;
+    }
+    final String moduleTestOutputDirectory = getTestsOutputDir(module);
+    if (moduleTestOutputDirectory == null) {
+      return false;
+    }
+    // here we have test output specified
+    final String moduleOutputDirectory = getOutputDir(module);
+    if (moduleOutputDirectory == null) {
+      // only test output is specified, so should return true
+      return true;
+    }
+    return !FileUtil.pathsEqual(moduleTestOutputDirectory, moduleOutputDirectory);
+  }
+
+  private void saveTestData() {
+    ApplicationManager.getApplication().runReadAction(new Runnable() {
+      public void run() {
+        for (VirtualFile file : myFilesToCompile) {
+          CompilerManagerImpl.addCompiledPath(file.getPath());
+        }
+      }
+    });
+  }
+
+  private final Object lock = new Object();
+
+  private class SynchedCompilerParsing extends CompilerParsingThread {
+    private final ClassParsingThread myClassParsingThread;
+
+    private SynchedCompilerParsing(Process process,
+                                  final CompileContext context,
+                                  OutputParser outputParser,
+                                  ClassParsingThread classParsingThread,
+                                  boolean readErrorStream,
+                                  boolean trimLines) {
+      super(process, outputParser, readErrorStream, trimLines,context);
+      myClassParsingThread = classParsingThread;
+    }
+
+    public void setProgressText(String text) {
+      synchronized (lock) {
+        super.setProgressText(text);
+      }
+    }
+
+    public void message(CompilerMessageCategory category, String message, String url, int lineNum, int columnNum) {
+      synchronized (lock) {
+        super.message(category, message, url, lineNum, columnNum);
+      }
+    }
+
+    public void fileProcessed(String path) {
+      synchronized (lock) {
+        sourceFileProcessed();
+      }
+    }
+
+    protected void processCompiledClass(final FileObject classFileToProcess) throws CacheCorruptedException {
+      synchronized (lock) {
+        myClassParsingThread.addPath(classFileToProcess);
+      }
+    }
+  }
+
+  private void doCompile(@NotNull final ModuleChunk chunk, @NotNull String outputDir) throws IOException {
+    myCompileContext.getProgressIndicator().checkCanceled();
+
+    if (ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
+      public Boolean compute() {
+        return chunk.getFilesToCompile().isEmpty() ? Boolean.TRUE : Boolean.FALSE;
+      }
+    }).booleanValue()) {
+      return; // should not invoke javac with empty sources list
+    }
+
+    ModuleType moduleType = ModuleType.get(chunk.getModules()[0]);
+    if ((chunk.getJdk() == null || !(chunk.getJdk().getSdkType() instanceof JavaSdkType)) &&
+        !(moduleType instanceof JavaModuleType || moduleType.createModuleBuilder() instanceof JavaModuleBuilder)) {
+      // TODO
+      // don't try to compile non-java type module
+      return;
+    }
+
+    int exitValue = 0;
+    try {
+      final Process process = myCompiler.launchProcess(chunk, outputDir, myCompileContext);
+      final long compilationStart = System.currentTimeMillis();
+      final ClassParsingThread classParsingThread = new ClassParsingThread(isJdk6(chunk.getJdk()), outputDir);
+      final Future<?> classParsingThreadFuture = ApplicationManager.getApplication().executeOnPooledThread(classParsingThread);
+
+      OutputParser errorParser = myCompiler.createErrorParser(outputDir, process);
+      CompilerParsingThread errorParsingThread = errorParser == null
+                                                 ? null
+                                                 : new SynchedCompilerParsing(process, myCompileContext, errorParser, classParsingThread,
+                                                                              true, errorParser.isTrimLines());
+      Future<?> errorParsingThreadFuture = null;
+      if (errorParsingThread != null) {
+        errorParsingThreadFuture = ApplicationManager.getApplication().executeOnPooledThread(errorParsingThread);
+      }
+
+      OutputParser outputParser = myCompiler.createOutputParser(outputDir);
+      CompilerParsingThread outputParsingThread = outputParser == null
+                                                  ? null
+                                                  : new SynchedCompilerParsing(process, myCompileContext, outputParser, classParsingThread,
+                                                                               false, outputParser.isTrimLines());
+      Future<?> outputParsingThreadFuture = null;
+      if (outputParsingThread != null) {
+        outputParsingThreadFuture = ApplicationManager.getApplication().executeOnPooledThread(outputParsingThread);
+      }
+
+      try {
+        exitValue = process.waitFor();
+      }
+      catch (InterruptedException e) {
+        process.destroy();
+        exitValue = process.exitValue();
+      }
+      catch (Error e) {
+        process.destroy();
+        exitValue = process.exitValue();
+        throw e;
+      }
+      finally {
+        if (CompileDriver.ourDebugMode) {
+          System.out.println("Compiler exit code is " + exitValue);
+        }
+        if (errorParsingThread != null) {
+          errorParsingThread.setProcessTerminated(true);
+        }
+        if (outputParsingThread != null) {
+          outputParsingThread.setProcessTerminated(true);
+        }
+        joinThread(errorParsingThreadFuture);
+        joinThread(outputParsingThreadFuture);
+        classParsingThread.stopParsing();
+        joinThread(classParsingThreadFuture);
+
+        registerParsingException(outputParsingThread);
+        registerParsingException(errorParsingThread);
+        assert outputParsingThread == null || !outputParsingThread.processing;
+        assert errorParsingThread == null || !errorParsingThread.processing;
+        assert classParsingThread == null || !classParsingThread.processing;
+      }
+    }
+    finally {
+      compileFinished(exitValue, chunk, outputDir);
+      myModuleName = null;
+    }
+  }
+
+  private static void joinThread(final Future<?> threadFuture) {
+    if (threadFuture != null) {
+      try {
+        threadFuture.get();
+      }
+      catch (InterruptedException ignored) {
+        LOG.info("Thread interrupted", ignored);
+      }
+      catch (ExecutionException ignored) {
+        LOG.info("Thread interrupted", ignored);
+      }
+    }
+  }
+
+  private void registerParsingException(final CompilerParsingThread outputParsingThread) {
+    Throwable error = outputParsingThread == null ? null : outputParsingThread.getError();
+    if (error != null) {
+      String message = error.getMessage();
+      if (error instanceof CacheCorruptedException) {
+        myCompileContext.requestRebuildNextTime(message);
+      }
+      else {
+        myCompileContext.addMessage(CompilerMessageCategory.ERROR, message, null, -1, -1);
+      }
+    }
+  }
+
+  private void runTransformingCompilers(final ModuleChunk chunk) {
+    final JavaSourceTransformingCompiler[] transformers =
+      CompilerManager.getInstance(myProject).getCompilers(JavaSourceTransformingCompiler.class);
+    if (transformers.length == 0) {
+      return;
+    }
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Running transforming compilers...");
+    }
+    final Module[] modules = chunk.getModules();
+    for (final JavaSourceTransformingCompiler transformer : transformers) {
+      final Map<VirtualFile, VirtualFile> originalToCopyFileMap = new HashMap<VirtualFile, VirtualFile>();
+      final Application application = ApplicationManager.getApplication();
+      application.invokeAndWait(new Runnable() {
+        public void run() {
+          for (final Module module : modules) {
+            for (final VirtualFile file : chunk.getFilesToCompile(module)) {
+              final VirtualFile untransformed = chunk.getOriginalFile(file);
+              if (transformer.isTransformable(untransformed)) {
+                application.runWriteAction(new Runnable() {
+                  public void run() {
+                    try {
+                      // if untransformed != file, the file is already a (possibly transformed) copy of the original 'untransformed' file.
+                      // If this is the case, just use already created copy and do not copy file content once again
+                      final VirtualFile fileCopy = untransformed.equals(file)? createFileCopy(getTempDir(module), file) : file;
+                      originalToCopyFileMap.put(file, fileCopy);
+                    }
+                    catch (IOException e) {
+                      // skip it
+                    }
+                  }
+                });
+              }
+            }
+          }
+        }
+      }, myCompileContext.getProgressIndicator().getModalityState());
+
+      // do actual transform
+      for (final Module module : modules) {
+        final List<VirtualFile> filesToCompile = chunk.getFilesToCompile(module);
+        for (int j = 0; j < filesToCompile.size(); j++) {
+          final VirtualFile file = filesToCompile.get(j);
+          final VirtualFile fileCopy = originalToCopyFileMap.get(file);
+          if (fileCopy != null) {
+            final boolean ok = transformer.transform(myCompileContext, fileCopy, chunk.getOriginalFile(file));
+            if (ok) {
+              chunk.substituteWithTransformedVersion(module, j, fileCopy);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  private VirtualFile createFileCopy(VirtualFile tempDir, final VirtualFile file) throws IOException {
+    final String fileName = file.getName();
+    if (tempDir.findChild(fileName) != null) {
+      int idx = 0;
+      while (true) {
+        //noinspection HardCodedStringLiteral
+        final String dirName = "dir" + idx++;
+        final VirtualFile dir = tempDir.findChild(dirName);
+        if (dir == null) {
+          tempDir = tempDir.createChildDirectory(this, dirName);
+          break;
+        }
+        if (dir.findChild(fileName) == null) {
+          tempDir = dir;
+          break;
+        }
+      }
+    }
+    return VfsUtilCore.copyFile(this, file, tempDir);
+  }
+
+  private VirtualFile getTempDir(Module module) throws IOException {
+    VirtualFile tempDir = myModuleToTempDirMap.get(module);
+    if (tempDir == null) {
+      final String projectName = myProject.getName();
+      final String moduleName = module.getName();
+      File tempDirectory = FileUtil.createTempDirectory(projectName, moduleName);
+      tempDir = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(tempDirectory);
+      if (tempDir == null) {
+        LOG.error("Cannot locate temp directory " + tempDirectory.getPath());
+      }
+      myModuleToTempDirMap.put(module, tempDir);
+    }
+    return tempDir;
+  }
+
+  private void compileFinished(int exitValue, final ModuleChunk chunk, final String outputDir) {
+    if (exitValue != 0 && !myCompileContext.getProgressIndicator().isCanceled() && myCompileContext.getMessageCount(CompilerMessageCategory.ERROR) == 0) {
+      myCompileContext.addMessage(CompilerMessageCategory.ERROR, CompilerBundle.message("error.compiler.internal.error", exitValue), null, -1, -1);
+    }
+
+    myCompiler.compileFinished();
+    final List<File> toRefresh = new ArrayList<File>();
+    final Map<String, Collection<TranslatingCompiler.OutputItem>> results = new HashMap<String, Collection<TranslatingCompiler.OutputItem>>();
+    try {
+      final FileTypeManager typeManager = FileTypeManager.getInstance();
+      final String outputDirPath = outputDir.replace(File.separatorChar, '/');
+      try {
+        for (final Module module : chunk.getModules()) {
+          for (final VirtualFile root : chunk.getSourceRoots(module)) {
+            final String packagePrefix = myProjectFileIndex.getPackageNameByDirectory(root);
+            if (LOG.isDebugEnabled()) {
+              LOG.debug("Building output items for " + root.getPresentableUrl() + "; output dir = " + outputDirPath + "; packagePrefix = \"" + packagePrefix + "\"");
+            }
+            buildOutputItemsList(outputDirPath, module, root, typeManager, root, packagePrefix, toRefresh, results);
+          }
+        }
+      }
+      catch (CacheCorruptedException e) {
+        myCompileContext.requestRebuildNextTime(CompilerBundle.message("error.compiler.caches.corrupted"));
+        if (LOG.isDebugEnabled()) {
+          LOG.debug(e);
+        }
+      }
+    }
+    finally {
+      CompilerUtil.refreshIOFiles(toRefresh);
+      for (Iterator<Map.Entry<String, Collection<TranslatingCompiler.OutputItem>>> it = results.entrySet().iterator(); it.hasNext();) {
+        Map.Entry<String, Collection<TranslatingCompiler.OutputItem>> entry = it.next();
+        mySink.add(entry.getKey(), entry.getValue(), VirtualFile.EMPTY_ARRAY);
+        it.remove(); // to free memory
+      }
+    }
+    myFileNameToSourceMap.clear(); // clear the map before the next use
+  }
+
+  private void buildOutputItemsList(final String outputDir, final Module module, VirtualFile from,
+                                    final FileTypeManager typeManager,
+                                    final VirtualFile sourceRoot,
+                                    final String packagePrefix, final List<File> filesToRefresh, final Map<String, Collection<TranslatingCompiler.OutputItem>> results) throws CacheCorruptedException {
+    final Ref<CacheCorruptedException> exRef = new Ref<CacheCorruptedException>(null);
+    final ModuleFileIndex fileIndex = ModuleRootManager.getInstance(module).getFileIndex();
+    final GlobalSearchScope srcRootScope = GlobalSearchScope.moduleScope(module).intersectWith(
+        GlobalSearchScopes.directoryScope(myProject, sourceRoot, true));
+    
+    final ContentIterator contentIterator = new ContentIterator() {
+      public boolean processFile(final VirtualFile child) {
+        try {
+          if (child.isValid()) {
+            if (!child.isDirectory() && myCompiler.getCompilableFileTypes().contains(child.getFileType())) {
+              updateOutputItemsList(outputDir, child, sourceRoot, packagePrefix, filesToRefresh, results, srcRootScope);
+            }
+          }
+          return true;
+        }
+        catch (CacheCorruptedException e) {
+          exRef.set(e);
+          return false;
+        }
+      }
+    };
+    if (fileIndex.isInContent(from)) {
+      // use file index for iteration to handle 'inner modules' and excludes properly
+      fileIndex.iterateContentUnderDirectory(from, contentIterator);
+    }
+    else {
+      // seems to be a root for generated sources
+      VfsUtilCore.visitChildrenRecursively(from, new VirtualFileVisitor() {
+        @Override
+        public boolean visitFile(@NotNull VirtualFile file) {
+          if (!file.isDirectory()) {
+            contentIterator.processFile(file);
+          }
+          return true;
+        }
+      });
+    }
+    final CacheCorruptedException exc = exRef.get();
+    if (exc != null) {
+      throw exc;
+    }
+  }
+
+  private void putName(String sourceFileName, int classQName, String relativePathToSource, String pathToClass) {
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Registering [sourceFileName, relativePathToSource, pathToClass] = [" + sourceFileName + "; " + relativePathToSource +
+                "; " + pathToClass + "]");
+    }
+    Set<CompiledClass> paths = myFileNameToSourceMap.get(sourceFileName);
+
+    if (paths == null) {
+      paths = new HashSet<CompiledClass>();
+      myFileNameToSourceMap.put(sourceFileName, paths);
+    }
+    paths.add(new CompiledClass(classQName, relativePathToSource, pathToClass));
+  }
+
+  private void updateOutputItemsList(final String outputDir, final VirtualFile srcFile,
+                                     VirtualFile sourceRoot,
+                                     final String packagePrefix, final List<File> filesToRefresh,
+                                     Map<String, Collection<TranslatingCompiler.OutputItem>> results,
+                                     final GlobalSearchScope srcRootScope) throws CacheCorruptedException {
+    final Cache newCache = myCompileContext.getDependencyCache().getNewClassesCache();
+    final Set<CompiledClass> paths = myFileNameToSourceMap.get(srcFile.getName());
+    if (paths == null || paths.isEmpty()) {
+      return;
+    }
+    final String filePath = "/" + calcPackagePath(srcFile, sourceRoot, packagePrefix);
+    for (final CompiledClass cc : paths) {
+      myCompileContext.getProgressIndicator().checkCanceled();
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("Checking [pathToClass; relPathToSource] = " + cc);
+      }
+      
+      boolean pathsEquals = FileUtil.pathsEqual(filePath, cc.relativePathToSource);
+      if (!pathsEquals) {
+        final String qName = myCompileContext.getDependencyCache().resolve(cc.qName);
+        if (qName != null) {
+          pathsEquals = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
+            public Boolean compute() {
+              final JavaPsiFacade facade = JavaPsiFacade.getInstance(myProject);
+              PsiClass psiClass = facade.findClass(qName, srcRootScope);
+              if (psiClass == null) {
+                final int dollarIndex = qName.indexOf("$");
+                if (dollarIndex >= 0) {
+                  final String topLevelClassName = qName.substring(0, dollarIndex);
+                  psiClass = facade.findClass(topLevelClassName, srcRootScope);
+                }
+              }
+              if (psiClass != null) {
+                final VirtualFile vFile = psiClass.getContainingFile().getVirtualFile();
+                return vFile != null && vFile.equals(srcFile);
+              }
+              return false;
+            }
+          });
+        }
+      }
+      
+      if (pathsEquals) {
+        final String outputPath = cc.pathToClass.replace(File.separatorChar, '/');
+        final Pair<String, String> realLocation = moveToRealLocation(outputDir, outputPath, srcFile, filesToRefresh);
+        if (realLocation != null) {
+          Collection<TranslatingCompiler.OutputItem> outputs = results.get(realLocation.getFirst());
+          if (outputs == null) {
+            outputs = new ArrayList<TranslatingCompiler.OutputItem>();
+            results.put(realLocation.getFirst(), outputs);
+          }
+          outputs.add(new OutputItemImpl(realLocation.getSecond(), srcFile));
+          if (PACKAGE_ANNOTATION_FILE_NAME.equals(srcFile.getName())) {
+            myProcessedPackageInfos.add(srcFile);
+          }
+          if (CompilerConfiguration.MAKE_ENABLED) {
+            newCache.setPath(cc.qName, realLocation.getSecond());
+          }
+          if (LOG.isDebugEnabled()) {
+            LOG.debug("Added output item: [outputDir; outputPath; sourceFile]  = [" + realLocation.getFirst() + "; " +
+                      realLocation.getSecond() + "; " + srcFile.getPresentableUrl() + "]");
+          }
+        }
+        else {
+          myCompileContext.addMessage(CompilerMessageCategory.ERROR, "Failed to copy from temporary location to output directory: " + outputPath + " (see idea.log for details)", null, -1, -1);
+          if (LOG.isDebugEnabled()) {
+            LOG.debug("Failed to move to real location: " + outputPath + "; from " + outputDir);
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   *
+   * @param srcFile
+   * @param sourceRoot
+   * @param packagePrefix
+   * @return A 'package'-path to a given src file relative to a specified root. "/" slashes must be used
+   */
+  protected static String calcPackagePath(VirtualFile srcFile, VirtualFile sourceRoot, String packagePrefix) {
+    final String prefix = packagePrefix != null && packagePrefix.length() > 0 ? packagePrefix.replace('.', '/') + "/" : "";
+    return prefix + VfsUtilCore.getRelativePath(srcFile, sourceRoot, '/');
+  }
+
+  @Nullable
+  private Pair<String, String> moveToRealLocation(String tempOutputDir, String pathToClass, VirtualFile sourceFile, final List<File> filesToRefresh) {
+    final Module module = myCompileContext.getModuleByFile(sourceFile);
+    if (module == null) {
+      final String message =
+        "Cannot determine module for source file: " + sourceFile.getPresentableUrl() + ";\nCorresponding output file: " + pathToClass;
+      LOG.info(message);
+      myCompileContext.addMessage(CompilerMessageCategory.WARNING, message, sourceFile.getUrl(), -1, -1);
+      // do not move: looks like source file has been invalidated, need recompilation
+      return new Pair<String, String>(tempOutputDir, pathToClass);
+    }
+    final String realOutputDir;
+    if (myCompileContext.isInTestSourceContent(sourceFile)) {
+      realOutputDir = getTestsOutputDir(module);
+      LOG.assertTrue(realOutputDir != null);
+    }
+    else {
+      realOutputDir = getOutputDir(module);
+      LOG.assertTrue(realOutputDir != null);
+    }
+
+    if (FileUtil.pathsEqual(tempOutputDir, realOutputDir)) { // no need to move
+      filesToRefresh.add(new File(pathToClass));
+      return new Pair<String, String>(realOutputDir, pathToClass);
+    }
+
+    final String realPathToClass = realOutputDir + pathToClass.substring(tempOutputDir.length());
+    final File fromFile = new File(pathToClass);
+    final File toFile = new File(realPathToClass);
+
+    boolean success = fromFile.renameTo(toFile);
+    if (!success) {
+      // assuming cause of the fail: intermediate dirs do not exist
+      FileUtil.createParentDirs(toFile);
+      // retry after making non-existent dirs
+      success = fromFile.renameTo(toFile);
+    }
+    if (!success) { // failed to move the file: e.g. because source and destination reside on different mountpoints.
+      try {
+        FileUtil.copy(fromFile, toFile);
+        FileUtil.delete(fromFile);
+        success = true;
+      }
+      catch (IOException e) {
+        LOG.info(e);
+        success = false;
+      }
+    }
+    if (success) {
+      filesToRefresh.add(toFile);
+      return new Pair<String, String>(realOutputDir, realPathToClass);
+    }
+    return null;
+  }
+
+  private final Map<Module, String> myModuleToTestsOutput = new HashMap<Module, String>();
+
+  private String getTestsOutputDir(final Module module) {
+    if (myModuleToTestsOutput.containsKey(module)) {
+      return myModuleToTestsOutput.get(module);
+    }
+    final VirtualFile outputDirectory = myCompileContext.getModuleOutputDirectoryForTests(module);
+    final String out = outputDirectory != null? outputDirectory.getPath() : null;
+    myModuleToTestsOutput.put(module, out);
+    return out;
+  }
+
+  private final Map<Module, String> myModuleToOutput = new HashMap<Module, String>();
+
+  private String getOutputDir(final Module module) {
+    if (myModuleToOutput.containsKey(module)) {
+      return myModuleToOutput.get(module);
+    }
+    final VirtualFile outputDirectory = myCompileContext.getModuleOutputDirectory(module);
+    final String out = outputDirectory != null? outputDirectory.getPath() : null;
+    myModuleToOutput.put(module, out);
+    return out;
+  }
+
+  private void sourceFileProcessed() {
+    myStatistics.incFilesCount();
+    updateStatistics();
+  }
+
+  private void updateStatistics() {
+    final String msg;
+    String moduleName = myModuleName;
+    if (moduleName != null) {
+      msg = CompilerBundle.message("statistics.files.classes.module", myStatistics.getFilesCount(), myStatistics.getClassesCount(), moduleName);
+    }
+    else {
+      msg = CompilerBundle.message("statistics.files.classes", myStatistics.getFilesCount(), myStatistics.getClassesCount());
+    }
+    myCompileContext.getProgressIndicator().setText2(msg);
+    //myCompileContext.getProgressIndicator().setFraction(1.0* myProcessedFilesCount /myTotalFilesToCompile);
+  }
+
+  private class ClassParsingThread implements Runnable {
+    private final BlockingQueue<FileObject> myPaths = new ArrayBlockingQueue<FileObject>(50000);
+    private CacheCorruptedException myError = null;
+    private final boolean myAddNotNullAssertions;
+    private final boolean myIsJdk16;
+    private final String myOutputDir;
+
+    private ClassParsingThread(final boolean isJdk16, String outputDir) {
+      myIsJdk16 = isJdk16;
+      myOutputDir = FileUtil.toSystemIndependentName(outputDir);
+      myAddNotNullAssertions = CompilerConfiguration.getInstance(myProject).isAddNotNullAssertions();
+    }
+
+    private volatile boolean processing;
+    public void run() {
+      processing = true;
+      try {
+        while (true) {
+          FileObject path = myPaths.take();
+
+          if (path == myStopThreadToken) {
+            break;
+          }
+          processPath(path, myProject);
+        }
+      }
+      catch (InterruptedException e) {
+        LOG.error(e);
+      }
+      catch (CacheCorruptedException e) {
+        myError = e;
+      }
+      finally {
+        processing = false;
+      }
+    }
+
+    public void addPath(FileObject path) throws CacheCorruptedException {
+      if (myError != null) {
+        throw myError;
+      }
+      myPaths.offer(path);
+    }
+
+    public void stopParsing() {
+      myPaths.offer(myStopThreadToken);
+    }
+
+    private void processPath(FileObject fileObject, Project project) throws CacheCorruptedException {
+      File file = fileObject.getFile();
+      final String path = file.getPath();
+      try {
+        if (CompilerConfiguration.MAKE_ENABLED) {
+          byte[] fileContent = fileObject.getContent();
+          // the file is assumed to exist!
+          final DependencyCache dependencyCache = myCompileContext.getDependencyCache();
+          final int newClassQName = dependencyCache.reparseClassFile(file, fileContent);
+          final Cache newClassesCache = dependencyCache.getNewClassesCache();
+          final String sourceFileName = newClassesCache.getSourceFileName(newClassQName);
+          final String qName = dependencyCache.resolve(newClassQName);
+          String relativePathToSource = "/" + MakeUtil.createRelativePathToSource(qName, sourceFileName);
+          putName(sourceFileName, newClassQName, relativePathToSource, path);
+          boolean haveToInstrument = myAddNotNullAssertions && hasNotNullAnnotations(newClassesCache, dependencyCache.getSymbolTable(), newClassQName, project);
+
+          if (haveToInstrument) {
+            try {
+              ClassReader reader = new ClassReader(fileContent, 0, fileContent.length);
+              ClassWriter writer = new PsiClassWriter(myProject, myIsJdk16);
+
+              final NotNullVerifyingInstrumenter instrumenter = new NotNullVerifyingInstrumenter(writer);
+              reader.accept(instrumenter, 0);
+              if (instrumenter.isModification()) {
+                fileObject = new FileObject(file, writer.toByteArray());
+              }
+            }
+            catch (Exception ignored) {
+              LOG.info(ignored);
+            }
+          }
+
+          fileObject.save();
+        }
+        else {
+          final String _path = FileUtil.toSystemIndependentName(path);
+          final int dollarIndex = _path.indexOf('$');
+          final int tailIndex = dollarIndex >=0 ? dollarIndex : _path.length() - ".class".length();
+          final int slashIndex = _path.lastIndexOf('/');
+          final String sourceFileName = _path.substring(slashIndex + 1, tailIndex) + ".java";
+          String relativePathToSource = _path.substring(myOutputDir.length(), tailIndex) + ".java";
+          putName(sourceFileName, 0 /*doesn't matter here*/ , relativePathToSource.startsWith("/")? relativePathToSource : "/" + relativePathToSource, path);
+        }
+      }
+      catch (ClsFormatException e) {
+        final String m = e.getMessage();
+        String message = CompilerBundle.message("error.bad.class.file.format", StringUtil.isEmpty(m) ? path : m + "\n" + path);
+        myCompileContext.addMessage(CompilerMessageCategory.ERROR, message, null, -1, -1);
+        LOG.info(e);
+      }
+      catch (IOException e) {
+        myCompileContext.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
+        LOG.info(e);
+      }
+      finally {
+        myStatistics.incClassesCount();
+        updateStatistics();
+      }
+    }
+  }
+
+  private static boolean hasNotNullAnnotations(final Cache cache, final SymbolTable symbolTable, final int className, Project project) throws CacheCorruptedException {
+    final NullableNotNullManager manager = NullableNotNullManager.getInstance(project);
+    final List<String> notNulls = manager.getNotNulls();
+    for (MethodInfo methodId : cache.getMethods(className)) {
+      for (AnnotationConstantValue annotation : methodId.getRuntimeInvisibleAnnotations()) {
+        if (notNulls.contains(symbolTable.getSymbol(annotation.getAnnotationQName()))) {
+          return true;
+        }
+      }
+      final AnnotationConstantValue[][] paramAnnotations = methodId.getRuntimeInvisibleParameterAnnotations();
+      for (AnnotationConstantValue[] _singleParamAnnotations : paramAnnotations) {
+        for (AnnotationConstantValue annotation : _singleParamAnnotations) {
+          if (notNulls.contains(symbolTable.getSymbol(annotation.getAnnotationQName()))) {
+            return true;
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  private static boolean isJdk6(final Sdk jdk) {
+    return jdk != null && JavaSdk.getInstance().isOfVersionOrHigher(jdk, JavaSdkVersion.JDK_1_6);
+  }
+  
+  private static final class CompileStatistics {
+    private static final Key<CompileStatistics> KEY = Key.create("_Compile_Statistics_");
+    private int myClassesCount;
+    private int myFilesCount;
+
+    public int getClassesCount() {
+      return myClassesCount;
+    }
+
+    public int incClassesCount() {
+      return ++myClassesCount;
+    }
+
+    public int getFilesCount() {
+      return myFilesCount;
+    }
+
+    public int incFilesCount() {
+      return ++myFilesCount;
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/CompiledClass.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/CompiledClass.java
new file mode 100644
index 0000000..4bfa519
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/CompiledClass.java
@@ -0,0 +1,56 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler;
+
+/**
+* @author cdr
+*/
+final class CompiledClass {
+  public final int qName;
+  public final String relativePathToSource;
+  public final String pathToClass;
+
+  CompiledClass(final int qName, final String relativePathToSource, final String pathToClass) {
+    this.qName = qName;
+    this.relativePathToSource = relativePathToSource;
+    this.pathToClass = pathToClass;
+  }
+
+  public boolean equals(final Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    final CompiledClass that = (CompiledClass)o;
+
+    if (qName != that.qName) return false;
+    if (!pathToClass.equals(that.pathToClass)) return false;
+    if (!relativePathToSource.equals(that.relativePathToSource)) return false;
+
+    return true;
+  }
+
+  public int hashCode() {
+    int result;
+    result = qName;
+    result = 31 * result + relativePathToSource.hashCode();
+    result = 31 * result + pathToClass.hashCode();
+    return result;
+  }
+
+  public String toString() {
+    return "[" + pathToClass + ";" + relativePathToSource + "]";
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/CompilerParsingThread.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/CompilerParsingThread.java
new file mode 100644
index 0000000..38b6b2f
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/CompilerParsingThread.java
@@ -0,0 +1,263 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler;
+
+import com.intellij.compiler.OutputParser;
+import com.intellij.compiler.impl.CompileDriver;
+import com.intellij.compiler.make.CacheCorruptedException;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.SpinAllocator;
+import com.intellij.util.StringBuilderSpinAllocator;
+import org.jetbrains.annotations.NonNls;
+
+import java.io.*;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 30, 2004
+ */
+public class CompilerParsingThread implements Runnable, OutputParser.Callback {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.javaCompiler.JavaCompilerParsingThread");
+  @NonNls public static final String TERMINATION_STRING = "__terminate_read__";
+  private final Reader myCompilerOutStreamReader;
+  private Process myProcess;
+  private final OutputParser myOutputParser;
+  private final boolean myTrimLines;
+  private boolean mySkipLF = false;
+  private Throwable myError = null;
+  private final boolean myIsUnitTestMode;
+  private FileObject myClassFileToProcess = null;
+  private String myLastReadLine = null;
+  private String myPushBackLine = null;
+  private volatile boolean myProcessExited = false;
+  private final CompileContext myContext;
+  
+  public CompilerParsingThread(Process process, OutputParser outputParser, final boolean readErrorStream, boolean trimLines, CompileContext context) {
+    myProcess = process;
+    myOutputParser = outputParser;
+    myTrimLines = trimLines;
+    InputStream stream = readErrorStream ? process.getErrorStream() : process.getInputStream();
+    myCompilerOutStreamReader = stream == null ? null : new BufferedReader(new InputStreamReader(stream), 16384);
+    myIsUnitTestMode = ApplicationManager.getApplication().isUnitTestMode();
+    myContext = context;
+  }
+
+  volatile boolean processing;
+  public void run() {
+    processing = true;
+    try {
+      while (true) {
+        if (!myIsUnitTestMode && myProcess == null) {
+          break;
+        }
+        if (isCanceled()) {
+          break;
+        }
+        if (!myOutputParser.processMessageLine(this)) {
+          break;
+        }
+      }
+      if (myClassFileToProcess != null) {
+        processCompiledClass(myClassFileToProcess);
+        myClassFileToProcess = null;
+      }
+    }
+    catch (Throwable e) {
+      e.printStackTrace();
+      myError = e;
+      LOG.info(e);
+    }
+    finally {
+      killProcess();
+      processing = false;
+    }
+  }
+
+  private void killProcess() {
+    if (myProcess != null) {
+      myProcess.destroy();
+      myProcess = null;
+    }
+  }
+
+  public Throwable getError() {
+    return myError;
+  }
+
+  public String getCurrentLine() {
+    return myLastReadLine;
+  }
+
+  public final String getNextLine() {
+    final String pushBack = myPushBackLine;
+    if (pushBack != null) {
+      myPushBackLine = null;
+      myLastReadLine = pushBack;
+      return pushBack;
+    }
+    final String line = readLine(myCompilerOutStreamReader);
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("LIne read: #" + line + "#");
+    }
+    if (CompileDriver.ourDebugMode) {
+      System.out.println("LIne read: #" + line + "#");
+    }
+    if (TERMINATION_STRING.equals(line)) {
+      myLastReadLine = null;
+    }
+    else {
+      myLastReadLine = line == null ? null : myTrimLines ? line.trim() : line;
+    }
+    return myLastReadLine;
+  }
+
+  @Override
+  public void pushBack(String line) {
+    myLastReadLine = null;
+    myPushBackLine = line;
+  }
+
+  public final void fileGenerated(FileObject path) {
+    // javac first logs file generated, then starts to write the file to disk,
+    // so this thread sometimes can stumble on not yet existing file,
+    // hence this complex logic
+    FileObject previousPath = myClassFileToProcess;
+    myClassFileToProcess = path;
+    if (previousPath != null) {
+      try {
+        processCompiledClass(previousPath);
+      }
+      catch (CacheCorruptedException e) {
+        if (CompileDriver.ourDebugMode) {
+          e.printStackTrace();
+        }
+        myError = e;
+        LOG.info(e);
+        killProcess();
+      }
+    }
+  }
+
+  protected boolean isCanceled() {
+    return myContext.getProgressIndicator().isCanceled();
+  }
+
+  public void setProgressText(String text) {
+    myContext.getProgressIndicator().setText(text);
+  }
+
+  public void fileProcessed(String path) {
+  }
+
+  public void message(CompilerMessageCategory category, String message, String url, int lineNum, int columnNum) {
+    myContext.addMessage(category, message, url, lineNum, columnNum);
+  }
+
+  protected void processCompiledClass(final FileObject classFileToProcess) throws CacheCorruptedException {
+  }
+
+
+  private String readLine(final Reader reader) {
+    StringBuilder buffer;
+    boolean releaseBuffer = true;
+    try {
+      buffer = StringBuilderSpinAllocator.alloc();
+    }
+    catch (SpinAllocator.AllocatorExhaustedException e) {
+      if (CompileDriver.ourDebugMode) {
+        e.printStackTrace();
+      }
+      LOG.info(e);
+      buffer = new StringBuilder();
+      releaseBuffer = false;
+    }
+
+    try {
+      boolean first = true;
+      while (true) {
+        int c = readNextByte(reader);
+        if (c == -1) break;
+        first = false;
+        if (c == '\n') {
+          if (mySkipLF) {
+            mySkipLF = false;
+            continue;
+          }
+          break;
+        }
+        else if (c == '\r') {
+          mySkipLF = true;
+          break;
+        }
+        else {
+          mySkipLF = false;
+          buffer.append((char)c);
+        }
+      }
+      if (first) {
+        return null;
+      }
+      return buffer.toString();
+    }
+    finally {
+      if (releaseBuffer) {
+        StringBuilderSpinAllocator.dispose(buffer);
+      }
+    }
+  }
+
+  private int readNextByte(final Reader reader) {
+    try {
+      while(!reader.ready()) {
+        if (isProcessTerminated()) {
+          if (reader.ready()) {
+            break;
+          }
+          return -1;
+        }
+        try {
+          Thread.sleep(1L);
+        }
+        catch (InterruptedException ignore) {
+        }
+      }
+      return reader.read();
+    }
+    catch (IOException e) {
+      if (CompileDriver.ourDebugMode) {
+        e.printStackTrace();
+      }
+      return -1; // When process terminated Process.getInputStream()'s underlying stream becomes closed on Linux.
+    }
+    catch (Throwable t) {
+      if (CompileDriver.ourDebugMode) {
+        t.printStackTrace();
+      }
+      return -1;
+    }
+  }
+
+  private boolean isProcessTerminated() {
+    return myProcessExited;
+  }
+
+  public void setProcessTerminated(final boolean procesExited) {
+    myProcessExited = procesExited;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/DummySourceGeneratingCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/DummySourceGeneratingCompiler.java
new file mode 100644
index 0000000..5fd91b0
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/DummySourceGeneratingCompiler.java
@@ -0,0 +1,137 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.SourceGeneratingCompiler;
+import com.intellij.openapi.compiler.ValidityState;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.DataInput;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Oct 9, 2004
+ */
+@SuppressWarnings({"HardCodedStringLiteral"})
+public class DummySourceGeneratingCompiler implements SourceGeneratingCompiler{
+  public static final String MODULE_NAME = "generated";
+  private final Project myProject;
+
+  public DummySourceGeneratingCompiler(Project project) {
+    myProject = project;
+  }
+
+  @Override
+  public VirtualFile getPresentableFile(CompileContext context, Module module, VirtualFile outputRoot, VirtualFile generatedFile) {
+    return null;
+  }
+
+  public GenerationItem[] getGenerationItems(CompileContext context) {
+    final Module module = findMyModule();
+    return new GenerationItem[] {
+      new MyGenerationItem("aaa/p1.properties", module, false),
+      new MyGenerationItem("bbb/p2.properties", module, false),
+      new MyGenerationItem("bbb/ccc/p3.properties", module, false),
+      new MyGenerationItem("aaa/p1.properties", module, true),
+      new MyGenerationItem("bbb/p2-t.properties", module, true),
+      new MyGenerationItem("bbb/ccc/p3.properties", module, true)
+    };
+  }
+
+  private Module findMyModule() {
+    return ApplicationManager.getApplication().runReadAction(new Computable<Module>() {
+      public Module compute() {
+        Module[] modules = ModuleManager.getInstance(myProject).getModules();
+        for (Module module : modules) {
+          if (MODULE_NAME.equals(module.getName())) {
+            return module;
+          }
+        }
+        return null;
+      }
+    });
+  }
+
+  public GenerationItem[] generate(CompileContext context, GenerationItem[] items, final VirtualFile outputRootDirectory) {
+    final String rootPath = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
+      public String compute() {
+        return outputRootDirectory.getPath();
+      }
+    });
+    final List<GenerationItem> success = new ArrayList<GenerationItem>();
+    for (GenerationItem item1 : items) {
+      GenerationItem item = item1;
+      File file = new File(rootPath + File.separator + item.getPath());
+      FileUtil.createIfDoesntExist(file);
+      success.add(item);
+    }
+    return success.toArray(new GenerationItem[success.size()]);
+  }
+
+  @NotNull
+  public String getDescription() {
+    return "Dummy Source Generator";
+  }
+
+  public boolean validateConfiguration(CompileScope scope) {
+    return findMyModule() != null;
+  }
+
+  public ValidityState createValidityState(DataInput in) throws IOException {
+    return null;
+  }
+
+  private static class MyGenerationItem implements GenerationItem {
+    private final String myRelPath;
+    private final Module myModule;
+    private final boolean myIsTestSource;
+
+    public MyGenerationItem(String relPath, Module module, final boolean isTestSource) {
+      myRelPath = relPath;
+      myModule = module;
+      myIsTestSource = isTestSource;
+    }
+
+    public String getPath() {
+      return myRelPath;
+    }
+
+    public ValidityState getValidityState() {
+      return null;
+    }
+
+    public Module getModule() {
+      return myModule;
+    }
+
+    public boolean isTestSource() {
+      return myIsTestSource;
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/DummyTransformingCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/DummyTransformingCompiler.java
new file mode 100644
index 0000000..2931190
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/DummyTransformingCompiler.java
@@ -0,0 +1,78 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.compiler.JavaSourceTransformingCompiler;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.*;
+
+/**          
+ * @author Eugene Zhuravlev
+ *         Date: Jul 10, 2004
+ */
+@SuppressWarnings({"HardCodedStringLiteral"})
+public class DummyTransformingCompiler implements JavaSourceTransformingCompiler{
+  public boolean isTransformable(VirtualFile file) {
+    return "A.java".equals(file.getName());
+  }
+
+  public boolean transform(CompileContext context, final VirtualFile file, VirtualFile originalFile) {
+    System.out.println("DummyTransformingCompiler.transform");
+    final String url = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
+      public String compute() {
+        return file.getPresentableUrl();
+      }
+    });
+    context.getProgressIndicator().setText("Transforming file: " + url);
+    try {
+      OutputStream os = new BufferedOutputStream(new FileOutputStream(new File(url)));
+      DataOutput out = new DataOutputStream(os);
+      out.writeBytes("package a; ");
+      out.writeBytes("public class A { public static void main(String[] args) { System.out.println(\"Hello from modified class\");} }");
+      os.close();
+      UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+        public void run() {
+          file.refresh(false, false);
+        }
+      });
+      return true;
+    }
+    catch (FileNotFoundException e) {
+      context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
+    }
+    catch (IOException e) {
+      context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
+    }
+    return false;
+  }
+
+  @NotNull
+  public String getDescription() {
+    return "a dummy compiler for testing";
+  }
+
+  public boolean validateConfiguration(CompileScope scope) {
+    return true;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/DummyTranslatingCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/DummyTranslatingCompiler.java
new file mode 100644
index 0000000..573b7e0
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/DummyTranslatingCompiler.java
@@ -0,0 +1,93 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler;
+
+import com.intellij.compiler.impl.CompilerUtil;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.*;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.Chunk;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Dec 4, 2007
+ */
+public class DummyTranslatingCompiler implements TranslatingCompiler, IntermediateOutputCompiler{
+  @NonNls private static final String DESCRIPTION = "DUMMY TRANSLATOR";
+  @NonNls private static final String FILETYPE_EXTENSION = ".dummy";
+
+  public boolean isCompilableFile(final VirtualFile file, final CompileContext context) {
+    return file.getName().endsWith(FILETYPE_EXTENSION);
+  }
+
+  public void compile(final CompileContext context, Chunk<Module> moduleChunk, final VirtualFile[] files, OutputSink sink) {
+    final List<File> filesToRefresh = new ArrayList<File>();
+    final Map<String, Collection<OutputItem>> outputs = new HashMap<String, Collection<OutputItem>>();
+    ApplicationManager.getApplication().runReadAction(new Runnable() {
+      public void run() {
+        for (final VirtualFile file : files) {
+          final Module module = context.getModuleByFile(file);
+          try {
+            final VirtualFile outputDir = context.getModuleOutputDirectory(module);
+            if (outputDir != null) {
+              final String outputDirPath = outputDir.getPath();
+              final File compiledFile = doCompile(outputDir, file);
+              filesToRefresh.add(compiledFile);
+              Collection<OutputItem> collection = outputs.get(outputDirPath);
+              if (collection == null) {
+                collection = new ArrayList<OutputItem>();
+                outputs.put(outputDirPath, collection);
+              }
+              collection.add(new OutputItemImpl(FileUtil.toSystemIndependentName(compiledFile.getPath()), file));
+            }
+          }
+          catch (IOException e) {
+            context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, 0, 0);
+          }
+        }
+      }
+    });
+    CompilerUtil.refreshIOFiles(filesToRefresh);
+    for (Map.Entry<String, Collection<OutputItem>> entry : outputs.entrySet()) {
+      sink.add(entry.getKey(), entry.getValue(), VirtualFile.EMPTY_ARRAY);
+    }
+  }
+
+  private static File doCompile(VirtualFile out, VirtualFile src) throws IOException {
+    final String originalName = src.getName();
+    String compiledName = originalName.substring(0, originalName.length() - FILETYPE_EXTENSION.length());
+    final File destFile = new File(out.getPath(), compiledName + ".java");
+    FileUtil.copy(new File(src.getPath()), destFile);
+    return destFile;
+  }
+  
+  @NotNull
+  public String getDescription() {
+    return DESCRIPTION;
+  }
+
+  public boolean validateConfiguration(final CompileScope scope) {
+    return true;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/ExternalCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/ExternalCompiler.java
new file mode 100644
index 0000000..9223cbd
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/ExternalCompiler.java
@@ -0,0 +1,83 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler;
+
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.EnvironmentUtil;
+import com.intellij.util.StringBuilderSpinAllocator;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+
+public abstract class ExternalCompiler implements BackendCompiler {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.javaCompiler.ExternalCompiler");
+  private static final Set<FileType> COMPILABLE_TYPES = Collections.<FileType>singleton(StdFileTypes.JAVA);
+
+  @NotNull
+  public abstract String[] createStartupCommand(ModuleChunk chunk, CompileContext context, String outputPath)
+    throws IOException, IllegalArgumentException;
+
+  @NotNull
+  public Set<FileType> getCompilableFileTypes() {
+    return COMPILABLE_TYPES;
+  }
+
+  /**
+   * Checks if the compiler can compile the specified file.
+   *
+   * @param file    the file to check.
+   * @param context the context for the current compile operation.
+   * @return true if can compile the file, false otherwise. If the method returns false, <code>file</code>
+   *         will not be included in the list of files passed to {@link #compile(CompileContext, com.intellij.util.Chunk < com.intellij.openapi.module.Module >,com.intellij.openapi.vfs.VirtualFile[], com.intellij.openapi.compiler.TranslatingCompiler.OutputSink)}.
+   */
+  public boolean isCompilableFile(VirtualFile file, CompileContext context) {
+    return getCompilableFileTypes().contains(file.getFileType());
+  }
+
+  @NotNull
+  public Process launchProcess(@NotNull final ModuleChunk chunk, @NotNull final String outputDir, @NotNull final CompileContext compileContext) throws IOException {
+    final String[] commands = createStartupCommand(chunk, compileContext, outputDir);
+
+    if (LOG.isDebugEnabled()) {
+      @NonNls final StringBuilder buf = StringBuilderSpinAllocator.alloc();
+      try {
+        buf.append("\n===================================Environment:===========================\n");
+        for (String pair : EnvironmentUtil.getEnvironment()) {
+          buf.append("\t").append(pair).append("\n");
+        }
+        buf.append("=============================================================================\n");
+        buf.append("Running compiler: ");
+        for (final String command : commands) {
+          buf.append(" ").append(command);
+        }
+
+        LOG.debug(buf.toString());
+      }
+      finally {
+        StringBuilderSpinAllocator.dispose(buf);
+      }
+    }
+
+    return Runtime.getRuntime().exec(commands);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/FileObject.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/FileObject.java
new file mode 100644
index 0000000..4ce53a5
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/FileObject.java
@@ -0,0 +1,73 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler;
+
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.util.ArrayUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ * @author cdr
+ */
+public class FileObject {
+  private static final byte[] NOT_LOADED = ArrayUtil.EMPTY_BYTE_ARRAY;
+
+  private final File myFile;
+  private final byte[] myContent;
+
+  public FileObject(@NotNull File file, @NotNull byte[] content) {
+    myFile = file;
+    myContent = content;
+  }
+
+  public FileObject(File file) {
+    myFile = file;
+    myContent = NOT_LOADED;
+  }
+
+  public File getFile() {
+    return myFile;
+  }
+
+  public byte[] getContent() throws IOException {
+    if (myContent == NOT_LOADED) {
+      return FileUtil.loadFileBytes(myFile);
+    }
+    return myContent;
+  }
+
+  public void save() throws IOException {
+    if (myContent == NOT_LOADED) {
+      return; // already on disk
+    }
+    try {
+      FileUtil.writeToFile(myFile, myContent);
+    }
+    catch (FileNotFoundException e) {
+      FileUtil.createParentDirs(myFile);
+      FileUtil.writeToFile(myFile, myContent);
+    }
+  }
+
+  @Override
+  public String toString() {
+    return getFile().toString();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/JavaCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/JavaCompiler.java
new file mode 100644
index 0000000..827c813
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/JavaCompiler.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: Jan 17, 2003
+ * Time: 3:22:59 PM
+ */
+package com.intellij.compiler.impl.javaCompiler;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.CompilerConfigurationImpl;
+import com.intellij.compiler.CompilerException;
+import com.intellij.compiler.impl.CompileDriver;
+import com.intellij.compiler.make.CacheCorruptedException;
+import com.intellij.openapi.compiler.*;
+import com.intellij.openapi.compiler.ex.CompileContextEx;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.Chunk;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Arrays;
+
+public class JavaCompiler implements TranslatingCompiler {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.javaCompiler.JavaCompiler");
+  private final Project myProject;
+
+  public JavaCompiler(Project project) {
+    myProject = project;
+  }
+
+  @NotNull
+  public String getDescription() {
+    return CompilerBundle.message("java.compiler.description");
+  }
+
+  public boolean isCompilableFile(VirtualFile file, CompileContext context) {
+    final BackendCompiler backEndCompiler = getBackEndCompiler();
+    if (backEndCompiler instanceof ExternalCompiler) {
+      return ((ExternalCompiler)backEndCompiler).isCompilableFile(file, context);
+    }
+    return backEndCompiler.getCompilableFileTypes().contains(file.getFileType());
+  }
+
+  public void compile(CompileContext context, Chunk<Module> moduleChunk, VirtualFile[] files, OutputSink sink) {
+    final BackendCompiler backEndCompiler = getBackEndCompiler();
+    final BackendCompilerWrapper wrapper = new BackendCompilerWrapper(moduleChunk, myProject, Arrays.asList(files), (CompileContextEx)context, backEndCompiler, sink);
+    try {
+      if (CompileDriver.ourDebugMode) {
+        System.out.println("Starting java compiler; with backend compiler: " + backEndCompiler.getClass().getName());
+      }
+      wrapper.compile();
+    }
+    catch (CompilerException e) {
+      if (CompileDriver.ourDebugMode) {
+        e.printStackTrace();
+      }
+      context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
+      LOG.info(e);
+    }
+    catch (CacheCorruptedException e) {
+      if (CompileDriver.ourDebugMode) {
+        e.printStackTrace();
+      }
+      LOG.info(e);
+      context.requestRebuildNextTime(e.getMessage());
+    }
+  }
+
+  public boolean validateConfiguration(CompileScope scope) {
+    return getBackEndCompiler().checkCompiler(scope);
+  }
+
+  private BackendCompiler getBackEndCompiler() {
+    CompilerConfigurationImpl configuration = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(myProject);
+    return configuration.getDefaultCompiler();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/ModuleChunk.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/ModuleChunk.java
new file mode 100644
index 0000000..864e4f8
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/ModuleChunk.java
@@ -0,0 +1,343 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.CompilerConfigurationImpl;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.ex.CompileContextEx;
+import com.intellij.openapi.module.LanguageLevelUtil;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.JdkOrderEntry;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.OrderEnumerator;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.util.Chunk;
+import com.intellij.util.PathsList;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.OrderedSet;
+import gnu.trove.THashMap;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Sep 29, 2004
+ */
+public class ModuleChunk extends Chunk<Module> {
+  private final CompileContextEx myContext;
+  private final Map<Module, List<VirtualFile>> myModuleToFilesMap = new THashMap<Module, List<VirtualFile>>();
+  private final Map<VirtualFile, VirtualFile> myTransformedToOriginalMap = new THashMap<VirtualFile, VirtualFile>();
+  private int mySourcesFilter = ALL_SOURCES;
+
+  public ModuleChunk(CompileContextEx context, Chunk<Module> chunk, Map<Module, List<VirtualFile>> moduleToFilesMap) {
+    super(chunk.getNodes());
+    myContext = context;
+    for (final Module module : chunk.getNodes()) {
+      final List<VirtualFile> files = moduleToFilesMap.get(module);
+      // Important!!! Collections in the myModuleToFilesMap must be modifiable copies of the corresponding collections
+      // from the moduleToFilesMap. This is needed to support SourceTransforming compilers
+      myModuleToFilesMap.put(module, files == null ? Collections.<VirtualFile>emptyList() : new ArrayList<VirtualFile>(files));
+    }
+  }
+
+  public static final int SOURCES = 0x1;
+  public static final int TEST_SOURCES = 0x2;
+  public static final int ALL_SOURCES = SOURCES | TEST_SOURCES;
+
+  public void setSourcesFilter(int filter) {
+    mySourcesFilter = filter;
+  }
+
+  public int getSourcesFilter() {
+    return mySourcesFilter;
+  }
+
+  public void substituteWithTransformedVersion(Module module, int fileIndex, VirtualFile transformedFile) {
+    final List<VirtualFile> moduleFiles = getFilesToCompile(module);
+    final VirtualFile currentFile = moduleFiles.get(fileIndex);
+    moduleFiles.set(fileIndex, transformedFile);
+    VirtualFile originalFile = myTransformedToOriginalMap.remove(currentFile);
+    if (originalFile == null) {
+      originalFile = currentFile;
+    }
+    myTransformedToOriginalMap.put(transformedFile, originalFile);
+  }
+
+  public VirtualFile getOriginalFile(VirtualFile file) {
+    final VirtualFile original = myTransformedToOriginalMap.get(file);
+    return original != null? original : file;
+  }
+
+  @NotNull
+  public List<VirtualFile> getFilesToCompile(Module forModule) {
+    return myModuleToFilesMap.get(forModule);
+  }
+
+  @NotNull
+  public List<VirtualFile> getFilesToCompile() {
+    if (getModuleCount() == 0) {
+      return Collections.emptyList();
+    }
+    final Set<Module> modules = getNodes();
+
+    final List<VirtualFile> filesToCompile = new ArrayList<VirtualFile>();
+    for (final Module module : modules) {
+      final List<VirtualFile> moduleCompilableFiles = getFilesToCompile(module);
+      if (mySourcesFilter == ALL_SOURCES) {
+        filesToCompile.addAll(moduleCompilableFiles);
+      }
+      else {
+        for (final VirtualFile file : moduleCompilableFiles) {
+          VirtualFile originalFile = myTransformedToOriginalMap.get(file);
+          if (originalFile == null) {
+            originalFile = file;
+          }
+          if (mySourcesFilter == TEST_SOURCES) {
+            if (myContext.isInTestSourceContent(originalFile)) {
+              filesToCompile.add(file);
+            }
+          }
+          else {
+            if (!myContext.isInTestSourceContent(originalFile)) {
+              filesToCompile.add(file);
+            }
+          }
+        }
+      }
+    }
+    return filesToCompile;
+  }
+
+  /**
+   * @return the jdk. Assumes that the jdk is the same for all modules
+   */
+  public Sdk getJdk() {
+    final Module module = getNodes().iterator().next();
+    return ModuleRootManager.getInstance(module).getSdk();
+  }
+
+  public VirtualFile[] getSourceRoots() {
+    return getSourceRoots(mySourcesFilter);
+  }
+
+  private VirtualFile[] getSourceRoots(final int sourcesFilter) {
+    return ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile[]>() {
+      public VirtualFile[] compute() {
+        return filterRoots(getAllSourceRoots(), getNodes().iterator().next().getProject(), sourcesFilter);
+      }
+    });
+  }
+
+  public VirtualFile[] getSourceRoots(final Module module) {
+    if (!getNodes().contains(module)) {
+      return VirtualFile.EMPTY_ARRAY;
+    }
+    return ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile[]>() {
+      public VirtualFile[] compute() {
+        return filterRoots(myContext.getSourceRoots(module), module.getProject(), mySourcesFilter);
+      }
+    });
+  }
+
+  private VirtualFile[] filterRoots(VirtualFile[] roots, Project project, final int sourcesFilter) {
+    final List<VirtualFile> filteredRoots = new ArrayList<VirtualFile>(roots.length);
+    final CompilerConfigurationImpl compilerConfiguration = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(project);
+    for (final VirtualFile root : roots) {
+      if (sourcesFilter != ALL_SOURCES) {
+        if (myContext.isInTestSourceContent(root)) {
+          if ((sourcesFilter & TEST_SOURCES) == 0) {
+            continue;
+          }
+        }
+        else {
+          if ((sourcesFilter & SOURCES) == 0) {
+            continue;
+          }
+        }
+      }
+      if (compilerConfiguration.isExcludedFromCompilation(root)) {
+        continue;
+      }
+      filteredRoots.add(root);
+    }
+    return VfsUtil.toVirtualFileArray(filteredRoots);
+  }
+
+  private VirtualFile[] getAllSourceRoots() {
+    final Set<Module> modules = getNodes();
+    Set<VirtualFile> roots = new HashSet<VirtualFile>();
+    for (final Module module : modules) {
+      ContainerUtil.addAll(roots, myContext.getSourceRoots(module));
+    }
+    return VfsUtil.toVirtualFileArray(roots);
+  }
+
+  public String getCompilationClasspath() {
+    final OrderedSet<VirtualFile> cpFiles = getCompilationClasspathFiles();
+    return convertToStringPath(cpFiles);
+
+  }
+
+  public OrderedSet<VirtualFile> getCompilationClasspathFiles() {
+    return getCompilationClasspathFiles(true);
+  }
+
+  public OrderedSet<VirtualFile> getCompilationClasspathFiles(final boolean exportedOnly) {
+    final Set<Module> modules = getNodes();
+
+    OrderedSet<VirtualFile> cpFiles = new OrderedSet<VirtualFile>();
+    for (final Module module : modules) {
+      Collections.addAll(cpFiles, orderEnumerator(module, exportedOnly, new AfterJdkOrderEntryCondition()).getClassesRoots());
+    }
+    return cpFiles;
+  }
+
+  private OrderEnumerator orderEnumerator(Module module, boolean exportedOnly, Condition<OrderEntry> condition) {
+    OrderEnumerator enumerator = OrderEnumerator.orderEntries(module).compileOnly().satisfying(condition);
+    if ((mySourcesFilter & TEST_SOURCES) == 0) {
+      enumerator = enumerator.productionOnly();
+    }
+    enumerator = enumerator.recursively();
+    return exportedOnly ? enumerator.exportedOnly() : enumerator;
+  }
+
+  public String getCompilationBootClasspath() {
+    return convertToStringPath(getCompilationBootClasspathFiles());
+  }
+
+  public OrderedSet<VirtualFile> getCompilationBootClasspathFiles() {
+    return getCompilationBootClasspathFiles(true);
+  }
+
+  public OrderedSet<VirtualFile> getCompilationBootClasspathFiles(final boolean exportedOnly) {
+    final Set<Module> modules = getNodes();
+    final OrderedSet<VirtualFile> cpFiles = new OrderedSet<VirtualFile>();
+    final OrderedSet<VirtualFile> jdkFiles = new OrderedSet<VirtualFile>();
+    for (final Module module : modules) {
+      Collections.addAll(cpFiles, orderEnumerator(module, exportedOnly, new BeforeJdkOrderEntryCondition(module)).getClassesRoots());
+      Collections.addAll(jdkFiles, OrderEnumerator.orderEntries(module).sdkOnly().getClassesRoots());
+    }
+    cpFiles.addAll(jdkFiles);
+    return cpFiles;
+  }
+
+  private static String convertToStringPath(final OrderedSet<VirtualFile> cpFiles) {
+    PathsList classpath = new PathsList();
+    classpath.addVirtualFiles(cpFiles);
+    return classpath.getPathsString();
+  }
+
+  //private String tryZipFor(String outputDir) {
+  //  final File zip = CompilerPathsEx.getZippedOutputPath(myContext.getProject(), outputDir);
+  //  if (zip.exists()) {
+  //    try {
+  //      myContext.commitZip(outputDir); // flush unsaved data if any
+  //    }
+  //    catch (IOException e) {
+  //      LOG.info(e);
+  //    }
+  //    return zip.getPath();
+  //  }
+  //  return outputDir;
+  //}
+
+  public int getModuleCount() {
+    return getNodes().size();
+  }
+
+  public Module[] getModules() {
+    final Set<Module> nodes = getNodes();
+    return nodes.toArray(new Module[nodes.size()]);
+  }
+
+  public String getSourcePath() {
+    return getSourcePath(mySourcesFilter);
+  }
+
+  public String getSourcePath(final int sourcesFilter) {
+    if (getModuleCount() == 0) {
+      return "";
+    }
+    final VirtualFile[] filteredRoots = getSourceRoots(sourcesFilter);
+    final StringBuilder buffer = StringBuilderSpinAllocator.alloc();
+    try {
+      ApplicationManager.getApplication().runReadAction(new Runnable() {
+        public void run() {
+          for (VirtualFile root : filteredRoots) {
+            if (buffer.length() > 0) {
+              buffer.append(File.pathSeparatorChar);
+            }
+            buffer.append(root.getPath().replace('/', File.separatorChar));
+          }
+        }
+      });
+      return buffer.toString();
+    }
+    finally {
+      StringBuilderSpinAllocator.dispose(buffer);
+    }
+  }
+
+  //the check for equal language levels is done elsewhere
+  public LanguageLevel getLanguageLevel() {
+    return LanguageLevelUtil.getEffectiveLanguageLevel(getNodes().iterator().next());
+  }
+
+  public Project getProject() {
+    return myContext.getProject();
+  }
+
+  private static class BeforeJdkOrderEntryCondition implements Condition<OrderEntry> {
+    private boolean myJdkFound;
+    private final Module myOwnerModule;
+
+    private BeforeJdkOrderEntryCondition(Module ownerModule) {
+      myOwnerModule = ownerModule;
+    }
+
+    @Override
+    public boolean value(OrderEntry orderEntry) {
+      if (orderEntry instanceof JdkOrderEntry && myOwnerModule.equals(orderEntry.getOwnerModule())) {
+        myJdkFound = true;
+      }
+      return !myJdkFound;
+    }
+  }
+
+  private static class AfterJdkOrderEntryCondition implements Condition<OrderEntry> {
+    private boolean myJdkFound;
+
+    @Override
+    public boolean value(OrderEntry orderEntry) {
+      if (orderEntry instanceof JdkOrderEntry) {
+        myJdkFound = true;
+        return false;
+      }
+      return myJdkFound;
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/OutputDir.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/OutputDir.java
new file mode 100644
index 0000000..7d7b12d
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/OutputDir.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 com.intellij.compiler.impl.javaCompiler;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+* @author cdr
+*/
+class OutputDir {
+  private final String myPath;
+  private final int myKind;
+
+  OutputDir(@NotNull String path, int kind) {
+    myPath = path;
+    myKind = kind;
+  }
+
+  @NotNull
+  public String getPath() {
+    return myPath;
+  }
+
+  public int getKind() {
+    return myKind;
+  }
+
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof OutputDir)) {
+      return false;
+    }
+
+    final OutputDir outputDir = (OutputDir)o;
+
+    return myKind == outputDir.myKind && myPath.equals(outputDir.myPath);
+
+  }
+
+  public int hashCode() {
+    int result = myPath.hashCode();
+    result = 29 * result + myKind;
+    return result;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/OutputItemImpl.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/OutputItemImpl.java
new file mode 100644
index 0000000..eb5720c
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/OutputItemImpl.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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: Apr 1, 2003
+ * Time: 5:58:41 PM
+ */
+package com.intellij.compiler.impl.javaCompiler;
+
+import com.intellij.openapi.compiler.TranslatingCompiler;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.Nullable;
+
+public class OutputItemImpl implements TranslatingCompiler.OutputItem{
+
+  private final String myOutputPath;
+  private final VirtualFile mySourceFile;
+
+  public OutputItemImpl(VirtualFile packageInfoFile) {
+    this(null, packageInfoFile);
+  }
+
+  /**
+   * @param outputPath absolute path of the output file ('/' slashes used)
+   * @param sourceFile corresponding source file
+   */
+  public OutputItemImpl(@Nullable String outputPath, VirtualFile sourceFile) {
+    myOutputPath = outputPath;
+    mySourceFile = sourceFile;
+  }
+
+  public String getOutputPath() {
+    return myOutputPath;
+  }
+
+  public VirtualFile getSourceFile() {
+    return mySourceFile;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/CompAPIDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/CompAPIDriver.java
new file mode 100644
index 0000000..d75fe63
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/CompAPIDriver.java
@@ -0,0 +1,180 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.api;
+
+import com.intellij.compiler.OutputParser;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.TaskEvent;
+import com.sun.source.util.TaskListener;
+import com.sun.tools.javac.api.JavacTool;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.tools.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.reflect.Field;
+import java.net.URI;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * @author cdr
+ */
+@SuppressWarnings({"Since15"})
+class CompAPIDriver {
+  private final BlockingQueue<CompilationEvent> myCompilationResults = new LinkedBlockingQueue<CompilationEvent>();
+  private static final CompilationEvent GUARD = new CompilationEvent() {
+    @Override
+    protected void process(OutputParser.Callback callback) {
+    }
+
+    @Override
+    public String toString() {
+      return "FINISH";
+    }
+  };
+  private String myOutputDir;
+
+  private volatile boolean compiling;
+  private static final PrintWriter COMPILER_ERRORS = new PrintWriter(System.err);
+  @Nullable
+  private final String mySourcesEncoding;
+
+  CompAPIDriver(@Nullable String sourcesEncoding) {
+    mySourcesEncoding = sourcesEncoding;
+  }
+
+  public void compile(List<String> commandLine, List<File> paths, final String outputDir) {
+    myOutputDir = outputDir;
+    compiling = true;
+
+    assert myCompilationResults.isEmpty();
+    JavaCompiler compiler = JavacTool.create(); //use current classloader
+    StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(null, null, null);
+    MyFileManager manager = new MyFileManager(this, outputDir, standardFileManager, mySourcesEncoding);
+
+    Iterable<? extends JavaFileObject> input = manager.getJavaFileObjectsFromFiles(paths);
+
+    DiagnosticListener<JavaFileObject> listener = new DiagnosticListener<JavaFileObject>() {
+      public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+        CompilationEvent event = CompilationEvent.diagnostic(diagnostic);
+        myCompilationResults.offer(event);
+      }
+    };
+    try {
+      JavaCompiler.CompilationTask task = compiler.getTask(COMPILER_ERRORS, manager, listener, commandLine, null, input);
+      ((JavacTask)task).setTaskListener(new TaskListener() {
+        public void started(TaskEvent taskEvent) {
+          JavaFileObject sourceFile = taskEvent.getSourceFile();
+          CompilationEvent event;
+          switch (taskEvent.getKind()) {
+            case ANALYZE:
+              event = CompilationEvent.progress("Analyzing ",sourceFile);
+              break;
+            case PARSE:
+              event = CompilationEvent.progress("Parsing ", sourceFile);
+              break;
+            default:
+              event = null;
+          }
+          if (event != null) {
+            myCompilationResults.offer(event);
+          }
+        }
+        public void finished(TaskEvent taskEvent) {
+          CompilationEvent event;
+          switch (taskEvent.getKind()) {
+            case ENTER:
+              event = CompilationEvent.fileProcessed();
+              break;
+            default:
+              event = null;
+          }
+          if (event != null) {
+            myCompilationResults.offer(event);
+          }
+        }
+      });
+      task.call();
+    }
+    catch (IllegalArgumentException e) {
+      final String message = "Javac in-process compiler error: " + e.getMessage();
+      myCompilationResults.offer(new CompilationEvent() {
+        @Override
+        protected void process(OutputParser.Callback callback) {
+          callback.message(CompilerMessageCategory.ERROR, message, null, -1, -1);
+        }
+      });
+    }
+    finally {
+      compiling = false;
+      myCompilationResults.offer(GUARD);
+      try {
+        manager.close();
+      }
+      catch (IOException ignored) {
+      }
+    }
+  }
+
+  private volatile boolean processing;
+  public void processAll(@NotNull OutputParser.Callback callback) {
+    try {
+      processing =  true;
+      while (true) {
+        CompilationEvent event = myCompilationResults.take();
+        if (event == GUARD) break;
+        event.process(callback);
+      }
+    }
+    catch (InterruptedException ignored) {
+    }
+    finally {
+      processing = false;
+    }
+  }
+
+  public void finish() {
+    assert !compiling : "still compiling to "+myOutputDir;
+    assert !processing;
+    //assert myCompilationResults.isEmpty() : myCompilationResults;
+    myCompilationResults.clear();
+    cleanupInternalFields();
+  }
+
+  private static void cleanupInternalFields() {
+    try {
+      Field freelist = Class.forName("com.sun.tools.javac.util.SharedNameTable").getDeclaredField("freelist");
+      freelist.setAccessible(true);
+      freelist.set(null, com.sun.tools.javac.util.List.nil());
+    }
+    catch (Exception ignored) {
+
+    }
+  }
+
+  public void offerClassFile(URI uri, byte[] bytes) {
+    CompilationEvent event = CompilationEvent.generateClass(uri, bytes);
+    myCompilationResults.offer(event);
+  }
+
+}
+
+
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/CompilationEvent.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/CompilationEvent.java
new file mode 100644
index 0000000..0ca1e2b
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/CompilationEvent.java
@@ -0,0 +1,127 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.api;
+
+import com.intellij.compiler.OutputParser;
+import com.intellij.compiler.impl.javaCompiler.FileObject;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+import org.jetbrains.annotations.NonNls;
+
+import javax.tools.*;
+import java.io.File;
+import java.net.URI;
+
+/**
+ * @author cdr
+ */
+abstract class CompilationEvent {
+  protected abstract void process(OutputParser.Callback callback);
+  static CompilationEvent progress(final String title, final JavaFileObject fileObject) {
+    return new CompilationEvent() {
+      @Override
+      protected void process(OutputParser.Callback callback) {
+        showProgressFor(title, fileObject.toUri(), callback);
+      }
+
+      @NonNls
+      @Override
+      public String toString() {
+        return "Progress: "+title+" "+fileObject.toUri();
+      }
+    };
+  }
+
+  private static void showProgressFor(String title, URI uri, OutputParser.Callback callback) {
+    String normalizedPath;
+    try {
+      normalizedPath = new File(uri).getPath();
+    }
+    catch (IllegalArgumentException e) {
+      normalizedPath = uri.toString();
+    }
+    callback.setProgressText(title + StringUtil.last(normalizedPath, 100, true));
+  }
+
+  static CompilationEvent generateClass(final URI uri, final byte[] bytes) {
+    return new CompilationEvent() {
+      @Override
+      protected void process(OutputParser.Callback callback) {
+        showProgressFor("Writing ", uri, callback);
+        File file = new File(uri);
+        callback.fileGenerated(new FileObject(file,bytes));
+      }
+      @NonNls
+      @Override
+      public String toString() {
+        return "Write: "+uri;
+      }
+    };
+  }
+  static CompilationEvent diagnostic(final Diagnostic<? extends JavaFileObject> diagnostic) {
+    return new CompilationEvent() {
+      @Override
+      protected void process(OutputParser.Callback callback) {
+        JavaFileObject fileObject = diagnostic.getSource();
+        String message = diagnostic.getMessage(null);
+        String url;
+        if (fileObject == null) {
+          url = null;
+        }
+        else {
+          URI uri = fileObject.toUri();
+          if (uri.getScheme().equals("file")) {
+            url = VfsUtil.pathToUrl(FileUtil.toSystemIndependentName(uri.getPath()));
+          }
+          else {
+            url = fileObject.toString();
+          }
+        }
+
+        CompilerMessageCategory category = diagnostic.getKind() == Diagnostic.Kind.ERROR
+                                           ? CompilerMessageCategory.ERROR
+                                           : diagnostic.getKind() == Diagnostic.Kind.WARNING ||
+                                             diagnostic.getKind() == Diagnostic.Kind.MANDATORY_WARNING
+                                             ? CompilerMessageCategory.WARNING
+                                             : CompilerMessageCategory.INFORMATION;
+        callback.message(category, message, url, (int)diagnostic.getLineNumber(), (int)diagnostic.getColumnNumber());
+      }
+      @NonNls
+      @Override
+      public String toString() {
+        return "Diagnostic: "+diagnostic;
+      }
+    };
+  }
+
+  public static CompilationEvent fileProcessed() {
+    return FILE_PROCESSED;
+  }
+  private static final CompilationEvent FILE_PROCESSED = new CompilationEvent() {
+    @Override
+    protected void process(OutputParser.Callback callback) {
+      callback.fileProcessed(null);
+    }
+
+    @NonNls
+    @Override
+    public String toString() {
+      return "Processed";
+    }
+  };
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/CompilerAPICompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/CompilerAPICompiler.java
new file mode 100644
index 0000000..c29b5c5
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/CompilerAPICompiler.java
@@ -0,0 +1,206 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.api;
+
+import com.intellij.compiler.OutputParser;
+import com.intellij.compiler.impl.javaCompiler.BackendCompiler;
+import com.intellij.compiler.impl.javaCompiler.ModuleChunk;
+import com.intellij.compiler.impl.javaCompiler.javac.JavacCompiler;
+import com.intellij.compiler.impl.javaCompiler.javac.JavacConfigurable;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.JavaSdkVersion;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.model.java.compiler.JavaCompilers;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+
+public class CompilerAPICompiler implements BackendCompiler {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.javaCompiler.api.CompilerAPICompiler");
+  private final Project myProject;
+  private static final Set<FileType> COMPILABLE_TYPES = Collections.<FileType>singleton(StdFileTypes.JAVA);
+
+  public CompilerAPICompiler(Project project) {
+    myProject = project;
+  }
+
+  public boolean checkCompiler(final CompileScope scope) {
+    return true;
+  }
+
+  @NotNull
+  @NonNls
+  public String getId() { // used for externalization
+    return JavaCompilers.JAVAC_API_ID;
+  }
+
+  @NotNull
+  public String getPresentableName() {
+    return "Javac in-process (Java6+ only)";
+  }
+
+  @NotNull
+  public Configurable createConfigurable() {
+    return new JavacConfigurable(CompilerAPIConfiguration.getOptions(myProject, CompilerAPIConfiguration.class));
+  }
+
+  @NotNull
+  public Set<FileType> getCompilableFileTypes() {
+    return COMPILABLE_TYPES;
+  }
+
+  @Nullable
+  public OutputParser createErrorParser(@NotNull final String outputDir, final Process process) {
+    return new OutputParser() {
+      public boolean processMessageLine(Callback callback) {
+        ((MyProcess)process).myCompAPIDriver.processAll(callback);
+        return false;
+      }
+    };
+  }
+
+  @Nullable
+  public OutputParser createOutputParser(@NotNull final String outputDir) {
+    return null;
+  }
+
+  public void compileFinished() {
+  }
+
+  @NotNull
+  public Process launchProcess(@NotNull final ModuleChunk chunk, @NotNull final String outputDir, @NotNull final CompileContext compileContext) throws IOException {
+    final IOException[] ex = {null};
+    @NonNls final List<String> commandLine = ApplicationManager.getApplication().runReadAction(new Computable<List<String>>() {
+      public List<String> compute() {
+        try {
+          List<String> commandLine = new ArrayList<String>();
+          final List<String> additionalOptions =
+            JavacCompiler.addAdditionalSettings(commandLine, CompilerAPIConfiguration.getOptions(myProject, CompilerAPIConfiguration.class), false, JavaSdkVersion.JDK_1_6, chunk, compileContext.isAnnotationProcessorsEnabled());
+
+          JavacCompiler.addCommandLineOptions(chunk, commandLine, outputDir, chunk.getJdk(), false,false, null, false, false, false);
+          commandLine.addAll(additionalOptions);
+          return commandLine;
+        }
+        catch (IOException e) {
+          ex[0] = e;
+        }
+        return null;
+      }
+    });
+    if (ex[0] != null) {
+      throw ex[0];
+    }
+    return new MyProcess(commandLine, chunk, outputDir, compileContext);
+  }
+
+  private static void compile(List<String> commandLine, ModuleChunk chunk, String outputDir, CompAPIDriver myCompAPIDriver) {
+    List<VirtualFile> filesToCompile = chunk.getFilesToCompile();
+    List<File> paths = new ArrayList<File>(filesToCompile.size());
+    for (VirtualFile file : filesToCompile) {
+      paths.add(new File(file.getPresentableUrl()));
+    }
+    myCompAPIDriver.compile(commandLine, paths, outputDir);
+  }
+
+  private static class MyProcess extends Process {
+    private final List<String> myCommandLine;
+    private final ModuleChunk myChunk;
+    private final String myOutputDir;
+    private final CompileContext myCompileContext;
+    private final CompAPIDriver myCompAPIDriver;
+
+    private MyProcess(List<String> commandLine, ModuleChunk chunk, String outputDir, CompileContext compileContext) {
+      myCommandLine = commandLine;
+      myChunk = chunk;
+      myOutputDir = outputDir;
+      myCompileContext = compileContext;
+      myCompAPIDriver = new CompAPIDriver(findEncodingValue(commandLine));
+    }
+
+    private static String findEncodingValue(List<String> commandLine) {
+      boolean found = false;
+      for (String param : commandLine) {
+        if (found) {
+          return param;
+        }
+        if ("-encoding".equalsIgnoreCase(param)) {
+          found = true;
+        }
+      }
+      return null;
+    }
+
+    public OutputStream getOutputStream() {
+      throw new UnsupportedOperationException();
+    }
+
+    public InputStream getInputStream() {
+      return null;
+    }
+
+    public InputStream getErrorStream() {
+      return null;
+    }
+
+    public void destroy() {
+      myCompAPIDriver.finish();
+    }
+
+    private int myExitCode;
+    public int waitFor() {
+      try {
+        myCommandLine.remove("-verbose");
+        compile(myCommandLine, myChunk, myOutputDir, myCompAPIDriver);
+        myExitCode = 0;
+        return myExitCode;
+      }
+      catch (Exception e) {
+        myCompileContext.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
+        LOG.info(e);
+        myExitCode = -1;
+        return -1;
+      }
+    }
+
+    public int exitValue() {
+      return myExitCode;
+    }
+
+    @Override
+    public String toString() {
+      return myChunk.toString();
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/CompilerAPIConfiguration.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/CompilerAPIConfiguration.java
new file mode 100755
index 0000000..0784a67
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/CompilerAPIConfiguration.java
@@ -0,0 +1,36 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.api;
+
+import com.intellij.compiler.impl.javaCompiler.javac.JavacConfiguration;
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
+import com.intellij.openapi.components.StoragePathMacros;
+import com.intellij.openapi.components.StorageScheme;
+import com.intellij.openapi.project.Project;
+
+@State(
+  name = "CompilerAPISettings",
+  storages = {
+    @Storage( file = StoragePathMacros.PROJECT_FILE)
+   ,@Storage( file = StoragePathMacros.PROJECT_CONFIG_DIR + "/compiler.xml", scheme = StorageScheme.DIRECTORY_BASED)
+    }
+)
+public class CompilerAPIConfiguration extends JavacConfiguration {
+  public CompilerAPIConfiguration(Project project) {
+    super(project);
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/CompilerPerfTestAction.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/CompilerPerfTestAction.java
new file mode 100644
index 0000000..1b6b355
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/CompilerPerfTestAction.java
@@ -0,0 +1,98 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.api;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.CompilerConfigurationImpl;
+import com.intellij.compiler.impl.javaCompiler.BackendCompiler;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+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.project.Project;
+import com.intellij.util.containers.ContainerUtil;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author cdr
+ */
+public class CompilerPerfTestAction extends AnAction {
+  @Override
+  public void update(AnActionEvent e) {
+    Project project = e.getData(PlatformDataKeys.PROJECT);
+    e.getPresentation().setEnabled(project != null);
+  }
+
+  public void actionPerformed(AnActionEvent e) {
+    final Project project = e.getData(PlatformDataKeys.PROJECT);
+
+    final CompilerManager compilerManager = CompilerManager.getInstance(project);
+
+    final CompilerConfigurationImpl configuration = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(project);
+    List<BackendCompiler> compilers = (List<BackendCompiler>)configuration.getRegisteredJavaCompilers();
+    final List<BackendCompiler> allCompilers = ContainerUtil.concat(compilers, compilers, compilers, compilers, compilers, compilers, compilers);
+    final int[] i = new int[1];
+
+    CompileStatusNotification callback = new CompileStatusNotification() {
+      volatile long start;
+      BackendCompiler compiler;
+
+      public void finished(boolean aborted, int errors, int warnings, CompileContext compileContext) {
+        if (compiler == null) {
+          next();
+          return;
+        }
+        final long finish = System.currentTimeMillis();
+        System.out.println("Compiled with '" +
+                           compiler.getPresentableName() + "' " +
+                           " in " +
+                           TimeUnit.MILLISECONDS.toMinutes(finish - start) + "m" +
+                           TimeUnit.MILLISECONDS.toSeconds((finish - start)%60000) + "s" +
+                           " with " +
+                           errors +
+                           " errors, " +
+                           warnings +
+                           " warnings, aborted=" +
+                           aborted+"; free memory="+Runtime.getRuntime().freeMemory()+" bytes");
+        //ProfilingUtil.forceCaptureMemorySnapshot();
+        next();
+      }
+
+      void next() {
+        if (i[0] >= allCompilers.size()) return;
+        compiler = allCompilers.get(i[0]++);
+        if (compiler.getId().equals("Jikes")|| compiler.getId().contains("Eclipse")) {
+          next();
+          return;
+        }
+        boolean success = compiler.checkCompiler(compilerManager.createProjectCompileScope(project));
+        if (!success) {
+          next();
+          return;
+        }
+        configuration.setDefaultCompiler(compiler);
+        start = System.currentTimeMillis();
+        compilerManager.rebuild(this);
+      }
+    };
+
+    callback.finished(false,0,0,null);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/FileVirtualObject.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/FileVirtualObject.java
new file mode 100644
index 0000000..b6866f0
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/FileVirtualObject.java
@@ -0,0 +1,70 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.api;
+
+import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+
+import javax.tools.*;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+
+/**
+ * @author cdr
+ */
+@SuppressWarnings({"Since15"})
+public abstract class FileVirtualObject extends SimpleJavaFileObject {
+  public FileVirtualObject(URI uri, Kind kind) {
+    super(uri, kind);
+  }
+
+  protected abstract VirtualFile getVirtualFile();
+  @Override
+  public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+    VirtualFile virtualFile = getVirtualFile();
+    if (virtualFile == null) return null;
+    return LoadTextUtil.loadText(virtualFile);
+  }
+
+  @Override
+  public InputStream openInputStream() throws IOException {
+    // in-process compiler does not work well with zipped stream
+    byte[] bytes = getVirtualFile().contentsToByteArray();
+    return new ByteArrayInputStream(bytes);
+  }
+
+  @Override
+  public OutputStream openOutputStream() throws IOException {
+    return getVirtualFile().getOutputStream(this);
+  }
+
+  @Override
+  public String toString() {
+    return toUri().toString();
+  }
+  @Override
+  public int hashCode() {
+    return toUri().hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    return obj instanceof JavaFileObject && toUri().equals(((JavaFileObject)obj).toUri());
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/JavaIoFile.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/JavaIoFile.java
new file mode 100644
index 0000000..801fc75
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/JavaIoFile.java
@@ -0,0 +1,94 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.api;
+
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import java.io.*;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+* User: cdr
+*/
+@SuppressWarnings({"Since15"})
+class JavaIoFile extends SimpleJavaFileObject {
+  private final File myFile;
+  @Nullable
+  private final String myEncoding;
+
+  JavaIoFile(@NotNull File file, @NotNull Kind kind, @Nullable String encoding) {
+    super(convertToURI(file.getPath()), kind);
+    myFile = file;
+    myEncoding = encoding;
+  }
+
+  // File.toURI() asks for File.isDirectory which is too expensive
+  @NotNull
+  private static URI convertToURI(@NotNull String path) {
+    if (File.separatorChar != '/') {
+      path = path.replace(File.separatorChar, '/');
+    }
+    if (!StringUtil.startsWithChar(path, '/')) {
+      path = "/" + path;
+    }
+
+    if (path.startsWith("//")) {
+      path = "//" + path;
+    }
+    try {
+      return new URI("file", null, path, null);
+    }
+    catch (URISyntaxException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @Override
+  public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+    return FileUtil.loadFile(myFile, myEncoding);
+  }
+
+  @Override
+  public InputStream openInputStream() throws IOException {
+    return new BufferedInputStream(new FileInputStream(myFile));
+  }
+
+  @Override
+  public OutputStream openOutputStream() throws IOException {
+    return new BufferedOutputStream(new FileOutputStream(myFile));
+  }
+
+  @Override
+  public String toString() {
+    return toUri().toString();
+  }
+
+  @Override
+  public int hashCode() {
+    return toUri().hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    return obj instanceof JavaFileObject && toUri().equals(((JavaFileObject)obj).toUri());
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/JavaVirtualByIoFile.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/JavaVirtualByIoFile.java
new file mode 100644
index 0000000..6fd1065
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/JavaVirtualByIoFile.java
@@ -0,0 +1,38 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.api;
+
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+
+import java.io.File;
+
+/**
+ * User: cdr
+ */
+@SuppressWarnings({"Since15"})
+class JavaVirtualByIoFile extends FileVirtualObject {
+  private final File myFile;
+
+  protected JavaVirtualByIoFile(File file, Kind kind) {
+    super(file.toURI(), kind);
+    myFile = file;
+  }
+
+  protected VirtualFile getVirtualFile() {
+    return LocalFileSystem.getInstance().findFileByIoFile(myFile);
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/JavaVirtualFile.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/JavaVirtualFile.java
new file mode 100644
index 0000000..abe8f24
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/JavaVirtualFile.java
@@ -0,0 +1,35 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.api;
+
+import com.intellij.openapi.vfs.VirtualFile;
+
+/**
+* User: cdr
+*/
+@SuppressWarnings({"Since15"})
+class JavaVirtualFile extends FileVirtualObject {
+  private final VirtualFile myFile;
+
+  JavaVirtualFile(VirtualFile file, Kind kind) {
+    super(MyFileManager.createUri(file.getUrl()), kind);
+    myFile = file;
+  }
+
+  protected VirtualFile getVirtualFile() {
+    return myFile;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/MyFileManager.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/MyFileManager.java
new file mode 100644
index 0000000..d46d504
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/MyFileManager.java
@@ -0,0 +1,260 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.api;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.SmartList;
+import com.sun.tools.javac.util.ListBuffer;
+import gnu.trove.THashSet;
+import gnu.trove.TObjectHashingStrategy;
+
+import javax.tools.FileObject;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.util.*;
+
+/**
+* @author cdr
+*/
+class MyFileManager implements StandardJavaFileManager {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.javaCompiler.api.MyFileManager");
+
+  private final String myOutputDir;
+  private final StandardJavaFileManager myStandardFileManager;
+  private final String myEncoding;
+  private final CompAPIDriver myCompAPIDriver;
+
+  MyFileManager(CompAPIDriver compAPIDriver, String outputDir, StandardJavaFileManager standardFileManager, String encoding) {
+    myCompAPIDriver = compAPIDriver;
+    myOutputDir = outputDir;
+    myStandardFileManager = standardFileManager;
+    myEncoding = encoding;
+  }
+
+  @Override
+  public Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(Iterable<? extends File> files) {
+    int size = ((Collection)files).size();
+    List<JavaFileObject> result = new ArrayList<JavaFileObject>(size);
+
+    for (File file : files) {
+      JavaFileObject fileObject = new JavaIoFile(file, JavaFileObject.Kind.SOURCE, myEncoding);
+      result.add(fileObject);
+    }
+
+    return result;
+  }
+
+  @Override
+  public JavaFileObject getJavaFileForOutput(Location location, String name, JavaFileObject.Kind kind, FileObject fileObject) {
+    URI uri = toURI(myOutputDir, name, kind);
+    return new Output(uri, myCompAPIDriver, kind);
+  }
+
+  static URI createUri(String url) {
+    return URI.create(url.replaceAll(" ","%20"));
+  }
+
+  private static URI toURI(String outputDir, String name, JavaFileObject.Kind kind) {
+    return createUri("file:///" + outputDir.replace('\\','/') + "/" + name.replace('.', '/') + kind.extension);
+  }
+
+
+  @Override
+  public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {
+    if (recurse) {
+      throw new IllegalArgumentException();
+    }
+    return findInside(location, packageName, kinds, false);
+  }
+
+  private List<JavaFileObject> findInside(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean lookForFile)
+    throws IOException {
+    Iterable<? extends File> path = getLocation(location);
+    if (path == null) return Collections.emptyList();
+
+    String subdirectory = packageName.replace('.', '/');
+    List<JavaFileObject> results = null;
+
+    for (File directory : path) {
+      VirtualFile dir = LocalFileSystem.getInstance().findFileByIoFile(directory);
+      if (dir == null) continue;
+      if (!dir.isDirectory()) {
+        dir = JarFileSystem.getInstance().getJarRootForLocalFile(dir);
+        if (dir == null) continue;
+      }
+      VirtualFile virtualFile = StringUtil.isEmptyOrSpaces(subdirectory) ? dir : dir.findFileByRelativePath(subdirectory);
+      if (virtualFile == null) continue;
+
+      VirtualFile[] children;
+      if (lookForFile) {
+        if (!virtualFile.isDirectory()) {
+          children = new VirtualFile[]{virtualFile};
+        }
+        else continue;
+      }
+      else {
+        children = virtualFile.getChildren();
+      }
+      for (VirtualFile child : children) {
+        JavaFileObject.Kind kind = getKind("." + child.getExtension());
+        if (kinds == null || kinds.contains(kind)) {
+          if (results == null) results = new SmartList<JavaFileObject>();
+          if (kind == JavaFileObject.Kind.SOURCE && child.getFileSystem() instanceof JarFileSystem) continue;  //for some reasdon javac looks for java files inside jar
+
+          // use VFS to read content inside .jar
+          String childPath = child.getPath();
+          JavaFileObject fileObject = !childPath.contains("!/") ? new JavaIoFile(new File(childPath), kind, myEncoding) : new JavaVirtualFile(child, kind);
+          results.add(fileObject);
+        }
+      }
+    }
+
+    List<JavaFileObject> ret = results == null ? Collections.<JavaFileObject>emptyList() : results;
+
+    if (LOG.isDebugEnabled()) {
+      // for testing consistency
+      Collection c = (Collection)myStandardFileManager.list(location, packageName, kinds, false);
+      Collection<JavaFileObject> sup = new HashSet(c);
+      assert sup.size() == c.size();
+      assert new HashSet(c).equals(sup);
+
+      THashSet<JavaFileObject> s = new THashSet<JavaFileObject>(new TObjectHashingStrategy<JavaFileObject>() {
+        public int computeHashCode(JavaFileObject object) {
+          return object.getName().hashCode();
+        }
+
+        public boolean equals(JavaFileObject o1, JavaFileObject o2) {
+          return o1.getKind() == o2.getKind() && o1.toUri().equals(o2.toUri());
+        }
+      });
+      s.addAll(ret);
+
+      s.removeAll(sup);
+      if (ret.size() != sup.size()) {
+        assert false : "our implementation differs from javac'";
+      }
+    }
+
+    return ret;
+  }
+
+  private static JavaFileObject.Kind getKind(String name) {
+      if (name.endsWith(JavaFileObject.Kind.CLASS.extension))
+          return JavaFileObject.Kind.CLASS;
+      else if (name.endsWith(JavaFileObject.Kind.SOURCE.extension))
+          return JavaFileObject.Kind.SOURCE;
+      else if (name.endsWith(JavaFileObject.Kind.HTML.extension))
+          return JavaFileObject.Kind.HTML;
+      else
+          return JavaFileObject.Kind.OTHER;
+  }
+
+
+  @Override
+  public String inferBinaryName(Location location, JavaFileObject file) {
+    return FileUtil.getNameWithoutExtension(new File(file.getName()).getName());
+  }
+
+  ////////// delegates
+  @Override
+  public int isSupportedOption(String option) {
+    return myStandardFileManager.isSupportedOption(option);
+  }
+
+  @Override
+  public void close() throws IOException {
+    myStandardFileManager.close();
+  }
+
+  @Override
+  public void flush() throws IOException {
+    myStandardFileManager.flush();
+  }
+
+  @Override
+  public boolean handleOption(String current, Iterator<String> remaining) {
+    return myStandardFileManager.handleOption(current, remaining);
+  }
+
+  @Override
+  public ClassLoader getClassLoader(Location location) {
+    return myStandardFileManager.getClassLoader(location);
+  }
+
+  @Override
+  public boolean isSameFile(FileObject a, FileObject b) {
+    if (a instanceof FileVirtualObject && b instanceof FileVirtualObject || a instanceof Output && b instanceof Output) {
+      return a.equals(b);
+    }
+    return myStandardFileManager.isSameFile(a, b);
+  }
+
+  public Iterable<? extends JavaFileObject> getJavaFileObjects(File... files) {
+    return getJavaFileObjectsFromFiles(Arrays.asList(files));
+  }
+
+  public Iterable<? extends JavaFileObject> getJavaFileObjects(String... names) {
+    return getJavaFileObjectsFromStrings(Arrays.asList(names));
+  }
+  public Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(Iterable<String> names) {
+    ListBuffer<File> files = new ListBuffer<File>();
+    for (String name : names) {
+      files.append(new File(name));
+    }
+    return getJavaFileObjectsFromFiles(files.toList());
+  }
+
+  public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException {
+    List<JavaFileObject> result = findInside(location, className, kind == null ? null : Collections.singleton(kind), true);
+    if (!result.isEmpty()) {
+      return result.get(0);
+    }
+    return null;
+  }
+
+  @Override
+  public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
+    return getJavaFileForInput(location, packageName + "/" + relativeName, getKind(relativeName));
+  }
+
+  @Override
+  public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
+    return getJavaFileForOutput(location, packageName + "/" + relativeName, getKind(relativeName), null);
+  }
+
+  @Override
+  public Iterable<? extends File> getLocation(Location location) {
+    return myStandardFileManager.getLocation(location);
+  }
+
+  @Override
+  public void setLocation(Location location, Iterable<? extends File> path) throws IOException {
+    myStandardFileManager.setLocation(location, path);
+  }
+
+  @Override
+  public boolean hasLocation(Location location) {
+    return myStandardFileManager.hasLocation(location);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/Output.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/Output.java
new file mode 100644
index 0000000..8cdf09c
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/api/Output.java
@@ -0,0 +1,80 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.api;
+
+import org.jetbrains.annotations.Nullable;
+
+import javax.tools.*;
+import java.io.*;
+import java.net.URI;
+
+/**
+* User: cdr
+*/
+@SuppressWarnings({"ALL"})
+class Output extends SimpleJavaFileObject {
+  private final CompAPIDriver myCompAPIDriver;
+  @Nullable
+  private volatile byte[] myFileBytes;
+
+  Output(URI uri, CompAPIDriver compAPIDriver, final Kind kind) {
+    super(uri, kind);
+    myCompAPIDriver = compAPIDriver;
+  }
+
+  @Override
+  public ByteArrayOutputStream openOutputStream() {
+    return new ByteArrayOutputStream() {
+      @Override
+      public void close() throws IOException {
+        super.close();
+        final byte[] bytes = toByteArray();
+        myFileBytes = bytes;
+        if (Kind.CLASS.equals(kind)) {
+          myCompAPIDriver.offerClassFile(toUri(), bytes);
+        }
+      }
+    };
+  }
+
+  @Override
+  public InputStream openInputStream() throws IOException {
+    final byte[] bytes = myFileBytes;
+    if (bytes == null) {
+      throw new FileNotFoundException(toUri().getPath());
+    }
+    return new ByteArrayInputStream(bytes);
+  }
+
+  @Override
+  public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+    final byte[] bytes = myFileBytes;
+    if (bytes == null) {
+      throw null;
+    }
+    return new String(bytes);
+  }
+
+  @Override
+  public int hashCode() {
+    return toUri().hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    return obj instanceof JavaFileObject && toUri().equals(((JavaFileObject)obj).toUri());
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompiler.java
new file mode 100644
index 0000000..615315d
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompiler.java
@@ -0,0 +1,238 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.eclipse;
+
+import com.intellij.compiler.OutputParser;
+import com.intellij.compiler.impl.CompilerUtil;
+import com.intellij.compiler.impl.javaCompiler.ExternalCompiler;
+import com.intellij.compiler.impl.javaCompiler.ModuleChunk;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.JavaSdkType;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.ArrayUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.model.java.compiler.EclipseCompilerOptions;
+import org.jetbrains.jps.model.java.compiler.JavaCompilers;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+                                        
+public class EclipseCompiler extends ExternalCompiler {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.javaCompiler.eclipse.EclipseCompiler");
+
+  private final Project myProject;
+  private final List<File> myTempFiles = new ArrayList<File>();
+  private static final String COMPILER_CLASS_NAME = "org.eclipse.jdt.core.compiler.batch.BatchCompiler";
+  @NonNls private static final String PATH_TO_COMPILER_JAR = findJarPah();
+
+  private static String findJarPah() {
+    try {
+      final Class<?> aClass = Class.forName(COMPILER_CLASS_NAME);
+      final String path = PathManager.getResourceRoot(aClass, "/" + aClass.getName().replace('.', '/') + ".class");
+      if (path != null) {
+        return path;
+      }
+    }
+    catch (ClassNotFoundException ignored) {
+    }
+
+    File dir = new File(PathManager.getLibPath());
+    File[] jars = dir.listFiles(new FilenameFilter() {
+      public boolean accept(File dir, String name) {
+        return name.startsWith("ecj-") && name.endsWith(".jar");
+      }
+    });
+    return jars.length == 0 ? dir + "/ecj-*.jar" : jars[0].getPath();
+  }
+
+  public EclipseCompiler(Project project) {
+    myProject = project;
+  }
+
+  public static boolean isInitialized() {
+    File file = new File(PATH_TO_COMPILER_JAR);
+    return file.exists();
+  }
+
+  public boolean checkCompiler(final CompileScope scope) {
+    if (!isInitialized()) {
+      Messages.showMessageDialog(
+        myProject,
+        CompilerBundle.message("eclipse.compiler.error.jar.not.found", PATH_TO_COMPILER_JAR),
+        CompilerBundle.message("compiler.eclipse.name"),
+        Messages.getErrorIcon()
+      );
+      return false;
+    }
+    return true;
+  }
+
+  @NonNls
+  public static String getCompilerClass() {
+    return "org.eclipse.jdt.internal.compiler.batch.Main";
+  }
+
+  @NotNull
+  public String getId() { // used for externalization
+    return JavaCompilers.ECLIPSE_ID;
+  }
+
+  @NotNull
+  public String getPresentableName() {
+    return CompilerBundle.message("compiler.eclipse.name");
+  }
+
+  @NotNull
+  public Configurable createConfigurable() {
+    return new EclipseCompilerConfigurable(EclipseCompilerConfiguration.getOptions(myProject, EclipseCompilerConfiguration.class));
+  }
+
+  public OutputParser createErrorParser(@NotNull final String outputDir, Process process) {
+    return new EclipseCompilerErrorParser();
+  }
+
+  @Nullable
+  public OutputParser createOutputParser(@NotNull final String outputDir) {
+    return new EclipseCompilerOutputParser(outputDir);
+  }
+
+  @NotNull
+  public String[] createStartupCommand(final ModuleChunk chunk, final CompileContext context, final String outputPath)
+    throws IOException {
+
+    final ArrayList<String> commandLine = new ArrayList<String>();
+    final IOException[] ex = {null};
+    ApplicationManager.getApplication().runReadAction(new Runnable() {
+      public void run() {
+        try {
+          createStartupCommand(chunk, commandLine, outputPath, true);
+        }
+        catch (IOException e) {
+          ex[0] = e;
+        }
+      }
+    });
+    if (ex[0] != null) {
+      throw ex[0];
+    }
+    return ArrayUtil.toStringArray(commandLine);
+  }
+
+  private void createStartupCommand(final ModuleChunk chunk,
+                                    @NonNls final ArrayList<String> commandLine,
+                                    final String outputPath,
+                                    final boolean useTempFile) throws IOException {
+    final EclipseCompilerOptions options = EclipseCompilerConfiguration.getOptions(myProject, EclipseCompilerConfiguration.class);
+
+    final Sdk projectJdk = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk();
+    final String vmExePath = ((JavaSdkType)projectJdk.getSdkType()).getVMExecutablePath(projectJdk);
+    commandLine.add(vmExePath);
+    commandLine.add("-Xmx" + options.MAXIMUM_HEAP_SIZE + "m");
+
+    CompilerUtil.addLocaleOptions(commandLine, false);
+
+    commandLine.add("-classpath");
+    commandLine.add(PATH_TO_COMPILER_JAR);
+    commandLine.add(getCompilerClass());
+
+    addCommandLineOptions(commandLine, chunk, outputPath, options, useTempFile, true);
+  }
+
+  public void addCommandLineOptions(@NotNull @NonNls final List<String> commandLine,
+                                    @NotNull final ModuleChunk chunk,
+                                    @NotNull final String outputPath,
+                                    @NotNull final EclipseCompilerOptions options,
+                                    final boolean useTempFile,
+                                    boolean quoteBootClasspath) throws IOException {
+    final Sdk jdk = chunk.getJdk();
+    CompilerUtil.addSourceCommandLineSwitch(jdk, chunk.getLanguageLevel(), commandLine);
+    CompilerUtil.addTargetCommandLineSwitch(chunk, commandLine);
+
+    final String bootCp = chunk.getCompilationBootClasspath();
+
+    final String classPath = chunk.getCompilationClasspath();
+
+    if (!StringUtil.isEmpty(bootCp)) {
+      commandLine.add("-bootclasspath");
+      // important: need to quote boot classpath if path to jdk contain spaces
+      commandLine.add(quoteBootClasspath ? CompilerUtil.quotePath(bootCp) : bootCp);
+    }
+
+    if (!StringUtil.isEmpty(classPath)) {
+      commandLine.add("-classpath");
+      commandLine.add(classPath);
+    }
+
+    commandLine.add("-d");
+    commandLine.add(outputPath.replace('/', File.separatorChar));
+
+    commandLine.add("-verbose");
+    StringTokenizer tokenizer = new StringTokenizer(new EclipseSettingsBuilder(options).getOptionsString(chunk), " ");
+    while (tokenizer.hasMoreTokens()) {
+      commandLine.add(tokenizer.nextToken());
+    }
+
+    final List<VirtualFile> files = chunk.getFilesToCompile();
+
+    if (useTempFile) {
+      File sourcesFile = FileUtil.createTempFile("javac", ".tmp");
+      sourcesFile.deleteOnExit();
+      myTempFiles.add(sourcesFile);
+      final PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(sourcesFile)));
+      try {
+        for (final VirtualFile file : files) {
+          // Important: should use "/" slashes!
+          // but not for JDK 1.5 - see SCR 36673
+          final String path = file.getPath().replace('/', File.separatorChar);
+          if (LOG.isDebugEnabled()) {
+            LOG.debug("Adding path for compilation " + path);
+          }
+          writer.println(CompilerUtil.quotePath(path));
+        }
+      }
+      finally {
+        writer.close();
+      }
+      commandLine.add("@" + sourcesFile.getAbsolutePath());
+    }
+    else {
+      for (VirtualFile file : files) {
+        commandLine.add(file.getPath());
+      }
+    }
+  }
+
+  public void compileFinished() {
+    FileUtil.asyncDelete(myTempFiles);
+    myTempFiles.clear();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerConfigurable.form b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerConfigurable.form
new file mode 100644
index 0000000..d8a50af
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerConfigurable.form
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.compiler.impl.javaCompiler.eclipse.EclipseCompilerConfigurable">
+  <grid id="280f7" binding="myPanel" layout-manager="GridLayoutManager" row-count="6" 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="79" y="160" width="584" height="236"/>
+    </constraints>
+    <properties/>
+    <clientProperties>
+      <BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithIndent"/>
+    </clientProperties>
+    <border type="etched" title-resource-bundle="messages/CompilerBundle" title-key="eclipse.options.group.title"/>
+    <children>
+      <component id="8538e" class="javax.swing.JCheckBox" binding="myCbDeprecation">
+        <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>
+          <selected value="false"/>
+          <text resource-bundle="messages/CompilerBundle" key="java.compiler.option.report.deprecated"/>
+        </properties>
+      </component>
+      <component id="3d38f" class="javax.swing.JCheckBox" binding="myCbDebuggingInfo">
+        <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>
+          <selected value="false"/>
+          <text resource-bundle="messages/CompilerBundle" key="java.compiler.option.generate.debugging.info"/>
+        </properties>
+      </component>
+      <component id="90803" class="javax.swing.JCheckBox" binding="myCbGenerateNoWarnings">
+        <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>
+          <selected value="false"/>
+          <text resource-bundle="messages/CompilerBundle" key="java.compiler.option.generate.no.warnings"/>
+        </properties>
+      </component>
+      <grid id="d7ddf" layout-manager="GridLayoutManager" row-count="2" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="10" left="8" bottom="0" right="0"/>
+        <constraints>
+          <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="f8a80" class="javax.swing.JLabel">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <text resource-bundle="messages/CompilerBundle" key="java.compiler.option.additional.command.line.parameters"/>
+            </properties>
+          </component>
+          <component id="479c2" class="com.intellij.ui.RawCommandLineEditor" binding="myAdditionalOptionsField">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="3" 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="327dc" class="javax.swing.JLabel">
+            <constraints>
+              <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <text resource-bundle="messages/CompilerBundle" key="javac.option.max.heap.size"/>
+            </properties>
+          </component>
+          <component id="15c16" class="javax.swing.JTextField" binding="myJavacMaximumHeapField">
+            <constraints>
+              <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                <preferred-size width="60" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <margin top="0" left="2" bottom="0" right="0"/>
+              <text value="128"/>
+            </properties>
+            <clientProperties>
+              <caretAspectRatio class="java.lang.Float" value="0.04"/>
+            </clientProperties>
+          </component>
+          <component id="6f8c2" class="com.intellij.ui.components.JBLabel">
+            <constraints>
+              <grid row="1" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <enabled value="false"/>
+              <text value="(ineffective when &quot;Use external build&quot; is on)"/>
+            </properties>
+          </component>
+          <hspacer id="a1325">
+            <constraints>
+              <grid row="1" column="3" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+            </constraints>
+          </hspacer>
+        </children>
+      </grid>
+      <vspacer id="af30">
+        <constraints>
+          <grid row="5" 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>
+      <component id="4eacb" class="javax.swing.JCheckBox" binding="myCbProceedOnErrors">
+        <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 value="Proceed on errors"/>
+        </properties>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerConfigurable.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerConfigurable.java
new file mode 100644
index 0000000..ad9cb1d
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerConfigurable.java
@@ -0,0 +1,103 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.eclipse;
+
+import com.intellij.compiler.options.ComparingUtils;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.ui.RawCommandLineEditor;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.model.java.compiler.EclipseCompilerOptions;
+
+import javax.swing.*;
+
+/**
+ * @author cdr
+ */
+public class EclipseCompilerConfigurable implements Configurable {
+  private JPanel myPanel;
+  private JCheckBox myCbDeprecation;
+  private JCheckBox myCbDebuggingInfo;
+  private JCheckBox myCbGenerateNoWarnings;
+  private RawCommandLineEditor myAdditionalOptionsField;
+  private JTextField myJavacMaximumHeapField;
+  private JCheckBox myCbProceedOnErrors;
+  private final EclipseCompilerOptions myCompilerSettings;
+
+  public EclipseCompilerConfigurable(EclipseCompilerOptions options) {
+    myCompilerSettings = options;
+    myAdditionalOptionsField.setDialogCaption(CompilerBundle.message("java.compiler.option.additional.command.line.parameters"));
+  }
+
+  public String getDisplayName() {
+    return null;
+  }
+
+  @Nullable
+  @NonNls
+  public String getHelpTopic() {
+    return null;
+  }
+
+  public JComponent createComponent() {
+    return myPanel;
+  }
+
+  public boolean isModified() {
+    boolean isModified = ComparingUtils.isModified(myJavacMaximumHeapField, myCompilerSettings.MAXIMUM_HEAP_SIZE);
+
+    isModified |= ComparingUtils.isModified(myCbDeprecation, myCompilerSettings.DEPRECATION);
+    isModified |= ComparingUtils.isModified(myCbDebuggingInfo, myCompilerSettings.DEBUGGING_INFO);
+    isModified |= ComparingUtils.isModified(myCbGenerateNoWarnings, myCompilerSettings.GENERATE_NO_WARNINGS);
+    isModified |= ComparingUtils.isModified(myCbProceedOnErrors, myCompilerSettings.PROCEED_ON_ERROR);
+    isModified |= ComparingUtils.isModified(myAdditionalOptionsField, myCompilerSettings.ADDITIONAL_OPTIONS_STRING);
+    return isModified;
+  }
+
+  public void apply() throws ConfigurationException {
+
+    try {
+      myCompilerSettings.MAXIMUM_HEAP_SIZE = Integer.parseInt(myJavacMaximumHeapField.getText());
+      if(myCompilerSettings.MAXIMUM_HEAP_SIZE < 1) {
+        myCompilerSettings.MAXIMUM_HEAP_SIZE = 128;
+      }
+    }
+    catch(NumberFormatException exception) {
+      myCompilerSettings.MAXIMUM_HEAP_SIZE = 128;
+    }
+
+    myCompilerSettings.DEPRECATION =  myCbDeprecation.isSelected();
+    myCompilerSettings.DEBUGGING_INFO = myCbDebuggingInfo.isSelected();
+    myCompilerSettings.GENERATE_NO_WARNINGS = myCbGenerateNoWarnings.isSelected();
+    myCompilerSettings.PROCEED_ON_ERROR = myCbProceedOnErrors.isSelected();
+    myCompilerSettings.ADDITIONAL_OPTIONS_STRING = myAdditionalOptionsField.getText();
+  }
+
+  public void reset() {
+    myJavacMaximumHeapField.setText(Integer.toString(myCompilerSettings.MAXIMUM_HEAP_SIZE));
+    myCbDeprecation.setSelected(myCompilerSettings.DEPRECATION);
+    myCbDebuggingInfo.setSelected(myCompilerSettings.DEBUGGING_INFO);
+    myCbGenerateNoWarnings.setSelected(myCompilerSettings.GENERATE_NO_WARNINGS);
+    myCbProceedOnErrors.setSelected(myCompilerSettings.PROCEED_ON_ERROR);
+    myAdditionalOptionsField.setText(myCompilerSettings.ADDITIONAL_OPTIONS_STRING);
+  }
+
+  public void disposeUIResources() {
+
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerConfiguration.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerConfiguration.java
new file mode 100755
index 0000000..4efbdcd
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerConfiguration.java
@@ -0,0 +1,45 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.eclipse;
+
+import com.intellij.openapi.components.*;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.jps.model.java.compiler.EclipseCompilerOptions;
+
+@State(
+  name = "EclipseCompilerSettings",
+  storages = {
+    @Storage( file = StoragePathMacros.PROJECT_FILE)
+   ,@Storage( file = StoragePathMacros.PROJECT_CONFIG_DIR + "/compiler.xml", scheme = StorageScheme.DIRECTORY_BASED)
+    }
+)
+public class EclipseCompilerConfiguration implements PersistentStateComponent<EclipseCompilerOptions> {
+  private final EclipseCompilerOptions mySettings = new EclipseCompilerOptions();
+
+  @NotNull
+  public EclipseCompilerOptions getState() {
+    return mySettings;
+  }
+
+  public void loadState(EclipseCompilerOptions state) {
+    XmlSerializerUtil.copyBean(state, mySettings);
+  }
+
+  public static EclipseCompilerOptions getOptions(Project project, Class<? extends EclipseCompilerConfiguration> aClass) {
+    return ServiceManager.getService(project, aClass).getState();
+  }}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerDriver.java
new file mode 100644
index 0000000..9538c16
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerDriver.java
@@ -0,0 +1,255 @@
+/*
+ * 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.
+ */
+
+/**
+ * @author Alexey
+ */
+
+package com.intellij.compiler.impl.javaCompiler.eclipse;
+
+import com.intellij.compiler.OutputParser;
+import com.intellij.compiler.impl.javaCompiler.FileObject;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.core.compiler.InvalidInputException;
+import org.eclipse.jdt.internal.compiler.*;
+import org.eclipse.jdt.internal.compiler.Compiler;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
+import org.eclipse.jdt.internal.compiler.batch.FileSystem;
+import org.eclipse.jdt.internal.compiler.batch.Main;
+import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
+import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;    
+
+public class EclipseCompilerDriver {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.javaCompiler.eclipse.EclipseCompilerDriver");
+
+  private String[] sourceFilePaths;
+  private Map compilerOptions;
+  private final BlockingQueue<CompilationResult> myCompilationResults = new LinkedBlockingQueue<CompilationResult>();
+  private FileSystem classPath;
+
+  private void parseCommandLine(String[] args) throws InvalidInputException {
+    StringWriter err = new StringWriter();
+    Main driver = new Main(null, new PrintWriter(err), false);
+    driver.configure(args);
+    StringBuffer buffer = err.getBuffer();
+    if (buffer.length() != 0) {
+      throw new InvalidInputException(buffer.toString());
+    }
+    sourceFilePaths = driver.filenames;
+    compilerOptions = driver.options;
+    classPath = driver.getLibraryAccess();
+  }
+
+  private CompilationUnit[] getCompilationUnits() {
+    int fileCount = sourceFilePaths.length;
+    CompilationUnit[] units = new CompilationUnit[fileCount];
+    final String defaultEncoding = null;
+
+    for (int i = 0; i < fileCount; i++) {
+      units[i] = new MyCompilationUnit(sourceFilePaths[i], defaultEncoding);
+    }
+    return units;
+  }
+
+  private ICompilerRequestor getBatchRequestor(final CompileContext compileContext) {
+    return new ICompilerRequestor() {
+      public void acceptResult(CompilationResult compilationResult) {
+        ProgressIndicator progress = compileContext.getProgressIndicator();
+        if (progress != null) {
+          progress.checkCanceled();
+        }
+        myCompilationResults.offer(compilationResult);
+      }
+    };
+  }
+
+  private static final CompilationResult END_OF_STREAM = new CompilationResult(new char[0], 0, 0, 0);
+
+  private INameEnvironment getEnvironment() {
+    return classPath;
+  }
+
+  private static IProblemFactory getProblemFactory() {
+    return new DefaultProblemFactory(Locale.getDefault());
+  }
+
+  private static IErrorHandlingPolicy getHandlingPolicy() {
+    return new IErrorHandlingPolicy() {
+      public boolean proceedOnErrors() {
+        return false; // stop if there are some errors
+      }
+
+      public boolean stopOnFirstError() {
+        return false;
+      }
+    };
+  }
+
+  private Map getCompilerOptions() {
+    return compilerOptions;
+  }
+
+
+  private void compile(final CompileContext compileContext) {
+    final INameEnvironment environment = getEnvironment();
+
+    Compiler compiler =
+      new Compiler(
+        environment,
+        getHandlingPolicy(),
+        getCompilerOptions(),
+        getBatchRequestor(compileContext),
+        getProblemFactory()){
+        protected void handleInternalException(Throwable internalException, CompilationUnitDeclaration unit, CompilationResult result) {
+          if (internalException instanceof ProcessCanceledException) throw (ProcessCanceledException)internalException;
+          super.handleInternalException(internalException, unit, result);
+        }
+      };
+    compiler.parseThreshold = 2500;
+    try {
+      compiler.compile(getCompilationUnits());
+    }
+    catch (ProcessCanceledException e) {
+      //compileContext.addMessage(CompilerMessageCategory.ERROR, "Canceled",null,-1,-1);
+    }
+    catch (Exception e) {
+      ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
+      if (indicator == null || !indicator.isCanceled()) {
+        compileContext.addMessage(CompilerMessageCategory.ERROR, "Internal Error: "+e.toString(),null,-1,-1);
+      }
+    }
+    finally {
+      myCompilationResults.offer(END_OF_STREAM);
+      environment.cleanup();
+    }
+  }
+
+  public boolean processMessageLine(final OutputParser.Callback callback, final String outputDir, Project project) {
+    ProgressManager.checkCanceled();
+    CompilationResult result;
+    try {
+      result = myCompilationResults.take();
+    }
+    catch (InterruptedException e) {
+      LOG.error(e);
+      return true;
+    }
+    if (result == END_OF_STREAM) {
+      return false;
+    }
+
+    String file = String.valueOf(result.getFileName());
+    callback.setProgressText(CompilerBundle.message("eclipse.compiler.parsing", file));
+    callback.fileProcessed(file);
+
+    ClassFile[] classFiles = result.getClassFiles();
+    for (ClassFile classFile : classFiles) {
+      String filePath = String.valueOf(classFile.fileName());
+      String relativePath = FileUtil.toSystemDependentName(filePath + ".class");
+      String path = FileUtil.toSystemDependentName(outputDir) + File.separatorChar + relativePath;
+
+      byte[] bytes = classFile.getBytes();
+      File out = new File(path);
+
+      callback.fileGenerated(new FileObject(out,bytes));
+    }
+    IProblem[] problems = result.getProblems();
+    if (problems != null) {
+      for (IProblem problem : problems) {
+        CompilerMessageCategory category = problem.isError() ? CompilerMessageCategory.ERROR
+                                           : problem.isWarning() ? CompilerMessageCategory.WARNING :
+                                             CompilerMessageCategory.INFORMATION;
+        String filePath = String.valueOf(problem.getOriginatingFileName());
+        String url = VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, FileUtil.toSystemIndependentName(filePath));
+        int lineNumber = problem.getSourceLineNumber();
+        int sourceStart = problem.getSourceStart();
+        int column = getColumn(url, lineNumber, sourceStart, project);
+        callback.message(category, problem.getMessage(), url, lineNumber, column);
+      }
+    }
+    return true;
+  }
+
+  private static int getColumn(final String url, final int lineNumber, final int sourceStart, final Project project) {
+    if (sourceStart == 0) return 0;
+    return ApplicationManager.getApplication().runReadAction(new Computable<Integer>() {
+      public Integer compute() {
+        VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(url);
+        if (file == null) return 0;
+        Document document = FileDocumentManager.getInstance().getDocument(file);
+        if (document == null) return 0;
+        int lineStartOffset = document.getLineStartOffset(lineNumber - 1);
+
+        String lineSeparator = FileDocumentManager.getInstance().getLineSeparator(file, project);
+        int offsetInVirtualFile = sourceStart - (lineNumber - 1) * (lineSeparator.length() - 1);
+        return offsetInVirtualFile - lineStartOffset + 1;
+      }
+    }).intValue();
+  }
+
+  public void parseCommandLineAndCompile(final String[] finalCmds, final CompileContext compileContext) throws Exception {
+    parseCommandLine(finalCmds);
+
+    compile(compileContext);
+  }
+
+  private static class MyCompilationUnit extends CompilationUnit {
+    private final String myDefaultEncoding;
+
+    private MyCompilationUnit(final String sourceFilePath, final String defaultEncoding) {
+      super(null, sourceFilePath, defaultEncoding);
+      myDefaultEncoding = defaultEncoding;
+    }
+
+    public char[] getContents() {
+      final String fileName = String.valueOf(getFileName());
+      try {
+        ProgressManager.checkCanceled();
+        return FileUtil.loadFileText(new File(fileName), myDefaultEncoding);
+      }
+      catch (IOException e) {
+        LOG.error(e);
+      }
+      return null;
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerErrorParser.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerErrorParser.java
new file mode 100644
index 0000000..aa87dcb
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerErrorParser.java
@@ -0,0 +1,131 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.eclipse;
+
+import com.intellij.compiler.OutputParser;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import org.jetbrains.annotations.NonNls;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class EclipseCompilerErrorParser extends OutputParser {
+
+  private final StringBuilder problemText = new StringBuilder();
+
+  public boolean processMessageLine(Callback callback) {
+    @NonNls String line = callback.getNextLine();
+    if (line == null) {
+      spitOutProblem(callback);
+      return false;
+    }
+    if (line.trim().length() == 0) {
+      return true;
+    }
+    if (line.equals("----------")) {
+      spitOutProblem(callback);
+      problemText.setLength(0);
+      return true;
+    }
+    problemText.append(line);
+    problemText.append("\n");
+    return true;
+  }
+
+  private void spitOutProblem(final Callback callback) {
+    final String problem = problemText.toString();
+    if (problem.trim().length() == 0) return;
+
+    @NonNls final String problemTemplate = "(\\d*)\\. (\\w*) in (.*)" +
+                                           "\\s*\\(at line (\\d*)\\)\n" +
+                                           "\\s*(.*)\n" +
+                                           "(\\s*)\\^+\n" +
+                                           "(.*)\\s*";
+    final Pattern PATTERN = Pattern.compile(problemTemplate, Pattern.DOTALL);
+    Matcher matcher = PATTERN.matcher(problem);
+    if (matcher.matches()) {
+      //String seqN = matcher.group(1);
+      @NonNls String problemType = matcher.group(2);
+      String path = matcher.group(3).trim();
+      String lineNum = matcher.group(4);
+      String codeSnippet = matcher.group(5);
+      String indentWhiteSpace = matcher.group(6);
+      String message = matcher.group(7).trim();
+
+      CompilerMessageCategory messageCategory;
+      if ("WARNING".equals(problemType)) {
+        messageCategory = CompilerMessageCategory.WARNING;
+      }
+      else if ("ERROR".equals(problemType)) {
+        messageCategory = CompilerMessageCategory.ERROR;
+      }
+      else {
+        messageCategory = CompilerMessageCategory.INFORMATION;
+      }
+      final String url = VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, FileUtil.toSystemIndependentName(path));
+      final int line = Integer.parseInt(lineNum);
+      int col = indentWhiteSpace.length();
+      final String offendingCode = codeSnippet.substring(col-1);
+
+      // try to find similar text inside source file                   
+      int colFromFile = ApplicationManager.getApplication().runReadAction(new Computable<Integer>() {
+        public Integer compute() {
+          int index = -1;
+          VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(url);
+          Document document = file == null ? null : FileDocumentManager.getInstance().getDocument(file);
+          if (document != null) {
+            // line is one-based
+            int docLine = line == 0 ? 0 : line-1;
+            int startOffset = document.getLineStartOffset(docLine);
+            int endOffset = document.getLineEndOffset(docLine);
+            String lineText = document.getText().substring(startOffset, endOffset);
+            index = lineText.indexOf(offendingCode);
+            if (index == -1) {
+              for (index = 0; index < lineText.length(); index++) {
+                if (!Character.isWhitespace(lineText.charAt(index))) break;
+              }
+              if (index == lineText.length()) index = -1;
+            }
+            // to one-based
+            if (index != -1) {
+              index++;
+            }
+          }
+          return index;
+        }
+      }).intValue();
+      if (colFromFile != -1) {
+        col = colFromFile;
+      }
+      callback.message(messageCategory, message, url, line, col);
+    }
+    else {
+      callback.message(CompilerMessageCategory.WARNING, problem, null, -1, -1);
+    }
+  }
+
+  public boolean isTrimLines() {
+    return false;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerOutputParser.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerOutputParser.java
new file mode 100644
index 0000000..12ceca3
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerOutputParser.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 com.intellij.compiler.impl.javaCompiler.eclipse;
+
+import com.intellij.compiler.OutputParser;
+import com.intellij.compiler.impl.javaCompiler.FileObject;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.util.io.FileUtil;
+import org.jetbrains.annotations.NonNls;
+
+import java.io.File;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class EclipseCompilerOutputParser extends OutputParser {
+  private final String myOutputDir;
+
+  public EclipseCompilerOutputParser(final String outputDir) {
+    myOutputDir = outputDir;
+  }
+
+  @NonNls private static final Pattern PATH_PATTERN = Pattern.compile("\\s*(.*) - #.*");
+  @NonNls private static final Pattern COMPILED_PATTERN = Pattern.compile("\\[\\d* unit(s)? compiled\\]");
+  @NonNls private static final Pattern GENERATED_PATTERN = Pattern.compile("\\[\\d* \\.class file(s)? generated\\]");
+  public boolean processMessageLine(Callback callback) {
+    @NonNls String line = callback.getNextLine();
+    if (line == null) {
+      return false;
+    }
+    if (line.trim().length() == 0) {
+      return true;
+    }
+    if (line.startsWith("[parsing ")) {
+      Matcher matcher = PATH_PATTERN.matcher(line.substring("[parsing ".length()));
+      matcher.matches();
+      String path = matcher.group(1);
+      callback.setProgressText(CompilerBundle.message("eclipse.compiler.parsing", path));
+      callback.fileProcessed(path);
+      return true;
+    }
+    if (line.startsWith("[reading ")) {
+      //StringTokenizer tokenizer = new StringTokenizer(line.substring("[reading ".length()), " ]");
+      //String fqn = tokenizer.nextToken();
+      callback.setProgressText(CompilerBundle.message("eclipse.compiler.reading"));
+      return true;
+    }
+    if (line.startsWith("[analyzing ")) {
+      Matcher matcher = PATH_PATTERN.matcher(line.substring("[analyzing ".length()));
+      matcher.matches();
+      String path = matcher.group(1);
+      callback.setProgressText(CompilerBundle.message("eclipse.compiler.analyzing", path));
+      return true;
+    }
+    if (line.startsWith("[completed ")) {
+      //Matcher matcher = PATH_PATTERN.matcher(line.substring("[completed ".length()));
+      //matcher.matches();
+      //String path = matcher.group(1);
+      //callback.setProgressText(CompilerBundle.message("eclipse.compiler.completed", path));
+      return true;
+    }
+    if (line.startsWith("[writing ")) {
+      Matcher matcher = PATH_PATTERN.matcher(line.substring("[writing ".length()));
+      matcher.matches();
+      String path = matcher.group(1);
+      String absPath = FileUtil.toSystemDependentName(myOutputDir + '/' + path);
+      //callback.setProgressText(CompilerBundle.message("eclipse.compiler.writing", absPath));
+      callback.fileGenerated(new FileObject(new File(absPath)));
+      return true;
+    }
+    if (COMPILED_PATTERN.matcher(line).matches() || GENERATED_PATTERN.matcher(line).matches()) {
+      return true;
+    }
+    callback.message(CompilerMessageCategory.INFORMATION, line, null, -1, -1);
+    return true;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseEmbeddedCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseEmbeddedCompiler.java
new file mode 100644
index 0000000..77aa2a1
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseEmbeddedCompiler.java
@@ -0,0 +1,160 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.eclipse;
+
+import com.intellij.compiler.OutputParser;
+import com.intellij.compiler.impl.javaCompiler.BackendCompiler;
+import com.intellij.compiler.impl.javaCompiler.ModuleChunk;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.ArrayUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.model.java.compiler.EclipseCompilerOptions;
+import org.jetbrains.jps.model.java.compiler.JavaCompilers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Set;
+
+
+public class EclipseEmbeddedCompiler implements BackendCompiler {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.javaCompiler.eclipse.EclipseEmbeddedCompiler");
+
+  private final Project myProject;
+  private final EclipseCompiler myEclipseExternalCompiler;
+  private int myExitCode;
+  private final EclipseCompilerDriver myEclipseCompilerDriver;
+  private static final Set<FileType> COMPILABLE_TYPES = Collections.<FileType>singleton(StdFileTypes.JAVA);
+
+  public EclipseEmbeddedCompiler(Project project) {
+    myProject = project;
+    myEclipseExternalCompiler = new EclipseCompiler(project);
+    myEclipseCompilerDriver = new EclipseCompilerDriver();
+  }
+
+  public boolean checkCompiler(final CompileScope scope) {
+    return myEclipseCompilerDriver != null && myEclipseExternalCompiler.checkCompiler(scope);
+  }
+
+  @NotNull
+  @NonNls
+  public String getId() { // used for externalization
+    return JavaCompilers.ECLIPSE_EMBEDDED_ID;
+  }
+
+  @NotNull
+  public String getPresentableName() {
+    return CompilerBundle.message("compiler.eclipse.embedded.name");
+  }
+
+  @NotNull
+  public Configurable createConfigurable() {
+    return new EclipseCompilerConfigurable(EclipseEmbeddedCompilerConfiguration.getOptions(myProject, EclipseEmbeddedCompilerConfiguration.class));
+  }
+
+  @NotNull
+  public Set<FileType> getCompilableFileTypes() {
+    return COMPILABLE_TYPES;
+  }
+
+  @Nullable
+  public OutputParser createErrorParser(@NotNull final String outputDir, Process process) {
+    return new OutputParser() {
+      public boolean processMessageLine(Callback callback) {
+        return myEclipseCompilerDriver.processMessageLine(callback, outputDir, myProject);
+      }
+    };
+  }
+
+  @Nullable
+  public OutputParser createOutputParser(@NotNull final String outputDir) {
+    return null;
+  }
+
+  public void compileFinished() {
+  }
+
+
+  @NotNull
+  public Process launchProcess(@NotNull final ModuleChunk chunk, @NotNull final String outputDir, @NotNull final CompileContext compileContext) throws IOException {
+    @NonNls final ArrayList<String> commandLine = new ArrayList<String>();
+    final IOException[] ex = {null};
+    ApplicationManager.getApplication().runReadAction(new Runnable() {
+      public void run() {
+        try {
+          final EclipseCompilerOptions options = EclipseCompilerConfiguration.getOptions(myProject, EclipseEmbeddedCompilerConfiguration.class);
+          myEclipseExternalCompiler.addCommandLineOptions(commandLine, chunk, outputDir, options, false, false);
+        }
+        catch (IOException e) {
+          ex[0] = e;
+        }
+      }
+    });
+    if (ex[0] != null) {
+      throw ex[0];
+    }
+
+    return new Process() {
+      public OutputStream getOutputStream() {
+        throw new UnsupportedOperationException();
+      }
+
+      public InputStream getInputStream() {
+        return null;
+      }
+
+      public InputStream getErrorStream() {
+        return null;
+      }
+
+      public void destroy() {
+      }
+
+      public int waitFor() {
+        try {
+          commandLine.remove("-verbose");
+          String[] finalCmds = ArrayUtil.toStringArray(commandLine);
+          myEclipseCompilerDriver.parseCommandLineAndCompile(finalCmds,compileContext);
+          myExitCode = 0;
+          return myExitCode;
+        }
+        catch (Exception e) {
+          compileContext.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
+          LOG.info(e);
+          myExitCode = -1;
+          return -1;
+        }
+      }
+
+      public int exitValue() {
+        return myExitCode;
+      }
+    };
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseEmbeddedCompilerConfiguration.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseEmbeddedCompilerConfiguration.java
new file mode 100755
index 0000000..1c36e57
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseEmbeddedCompilerConfiguration.java
@@ -0,0 +1,31 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.eclipse;
+
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
+import com.intellij.openapi.components.StoragePathMacros;
+import com.intellij.openapi.components.StorageScheme;
+
+@State(
+  name = "EclipseEmbeddedCompilerSettings",
+  storages = {
+    @Storage( file = StoragePathMacros.PROJECT_FILE)
+   ,@Storage( file = StoragePathMacros.PROJECT_CONFIG_DIR + "/compiler.xml", scheme = StorageScheme.DIRECTORY_BASED)
+    }
+)
+public class EclipseEmbeddedCompilerConfiguration extends EclipseCompilerConfiguration {
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseSettingsBuilder.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseSettingsBuilder.java
new file mode 100644
index 0000000..7748223
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseSettingsBuilder.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 com.intellij.compiler.impl.javaCompiler.eclipse;
+
+import com.intellij.compiler.impl.javaCompiler.javac.JavacSettingsBuilder;
+import com.intellij.openapi.module.Module;
+import com.intellij.util.Chunk;
+import org.jetbrains.jps.model.java.compiler.EclipseCompilerOptions;
+
+import java.util.Collection;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: 9/27/12
+ */
+public class EclipseSettingsBuilder extends JavacSettingsBuilder {
+  public EclipseSettingsBuilder(final EclipseCompilerOptions options) {
+    super(options);
+  }
+
+  @Override
+  public EclipseCompilerOptions getOptions() {
+    return (EclipseCompilerOptions)super.getOptions();
+  }
+
+  @Override
+  public Collection<String> getOptions(Chunk<Module> chunk) {
+    final Collection<String> options = super.getOptions(chunk);
+    if (getOptions().PROCEED_ON_ERROR) {
+      options.add("-proceedOnError");
+    }
+    return options;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/FilePathActionJavac.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/FilePathActionJavac.java
new file mode 100644
index 0000000..fc32b08
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/FilePathActionJavac.java
@@ -0,0 +1,65 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.javac;
+
+import com.intellij.compiler.OutputParser;
+import com.intellij.compiler.impl.javaCompiler.FileObject;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+
+import java.io.File;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author Eugene Zhuravlev              
+ *         Date: Sep 14, 2005
+ */
+public class FilePathActionJavac extends JavacParserAction {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.javaCompiler.javac.FilePathActionJavac");
+  private final Matcher myJdk7FormatMatcher;
+  
+  public FilePathActionJavac(final Matcher matcher) {
+    super(matcher);
+    myJdk7FormatMatcher = Pattern.compile("^\\w+\\[(.+)\\]$", Pattern.CASE_INSENSITIVE).matcher("");
+  }
+
+  protected void doExecute(final String line, String filePath, final OutputParser.Callback callback) {
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Process parsing message: " + filePath);
+    }
+
+    // for jdk7: cut off characters wrapping the path. e.g. "RegularFileObject[C:/tmp/bugs/src/a/Demo1.java]"
+    if (myJdk7FormatMatcher.reset(filePath).matches()) {
+      filePath = myJdk7FormatMatcher.group(1);
+    }
+
+    int index = filePath.lastIndexOf('/');
+    final String name = index >= 0 ? filePath.substring(index + 1) : filePath;
+
+    final FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(name);
+    if (StdFileTypes.JAVA.equals(fileType)) {
+      callback.fileProcessed(filePath);
+      callback.setProgressText(CompilerBundle.message("progress.parsing.file", name));
+    }
+    else if (StdFileTypes.CLASS.equals(fileType)) {
+      callback.fileGenerated(new FileObject(new File(filePath)));
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacCompiler.java
new file mode 100644
index 0000000..e962a93
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacCompiler.java
@@ -0,0 +1,450 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.javac;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.CompilerConfigurationImpl;
+import com.intellij.compiler.CompilerIOUtil;
+import com.intellij.compiler.OutputParser;
+import com.intellij.compiler.impl.CompilerUtil;
+import com.intellij.compiler.impl.javaCompiler.ExternalCompiler;
+import com.intellij.compiler.impl.javaCompiler.ModuleChunk;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.compiler.CompilerPaths;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.*;
+import com.intellij.openapi.projectRoots.ex.JavaSdkUtil;
+import com.intellij.openapi.projectRoots.impl.MockJdkWrapper;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.rt.compiler.JavacRunner;
+import com.intellij.util.ArrayUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.jps.model.java.compiler.AnnotationProcessingConfiguration;
+import org.jetbrains.jps.model.java.compiler.JavaCompilers;
+import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerOptions;
+
+import java.io.*;
+import java.util.*;
+
+public class JavacCompiler extends ExternalCompiler {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.javaCompiler.javac.JavacCompiler");
+  private final Project myProject;
+  private final List<File> myTempFiles = new ArrayList<File>();
+  @NonNls private static final String JAVAC_MAIN_CLASS_OLD = "sun.tools.javac.Main";
+  @NonNls public static final String JAVAC_MAIN_CLASS = "com.sun.tools.javac.Main";
+  private boolean myAnnotationProcessorMode = false;
+
+  public JavacCompiler(Project project) {
+    myProject = project;
+  }
+
+  public boolean isAnnotationProcessorMode() {
+    return myAnnotationProcessorMode;
+  }
+
+  /**
+   * @param annotationProcessorMode
+   * @return previous value
+   */
+  public boolean setAnnotationProcessorMode(boolean annotationProcessorMode) {
+    final boolean oldValue = myAnnotationProcessorMode;
+    myAnnotationProcessorMode = annotationProcessorMode;
+    return oldValue;
+  }
+
+  public boolean checkCompiler(final CompileScope scope) {
+    final Module[] modules = scope.getAffectedModules();
+    final Set<Sdk> checkedJdks = new HashSet<Sdk>();
+    for (final Module module : modules) {
+      final Sdk jdk  = ModuleRootManager.getInstance(module).getSdk();
+      if (jdk == null || checkedJdks.contains(jdk)) {
+        continue;
+      }
+      checkedJdks.add(jdk);
+      final SdkTypeId sdkType = jdk.getSdkType();
+      if (!(sdkType instanceof JavaSdkType)) {
+        continue;
+      }
+      final VirtualFile homeDirectory = jdk.getHomeDirectory();
+      if (homeDirectory == null) {
+        Messages.showMessageDialog(
+          myProject, CompilerBundle.jdkHomeNotFoundMessage(jdk), CompilerBundle.message("compiler.javac.name"), Messages.getErrorIcon()
+        );
+        return false;
+      }
+      final String vmExecutablePath = ((JavaSdkType)sdkType).getVMExecutablePath(jdk);
+      if (vmExecutablePath == null) {
+        Messages.showMessageDialog(
+          myProject, CompilerBundle.message("javac.error.vm.executable.missing", jdk.getName()), CompilerBundle.message("compiler.javac.name"), Messages.getErrorIcon()
+        );
+        return false;
+      }
+      final String toolsJarPath = ((JavaSdkType)sdkType).getToolsPath(jdk);
+      if (toolsJarPath == null) {
+        Messages.showMessageDialog(
+          myProject, CompilerBundle.message("javac.error.tools.jar.missing", jdk.getName()), CompilerBundle.message("compiler.javac.name"), Messages.getErrorIcon()
+        );
+        return false;
+      }
+      final String versionString = jdk.getVersionString();
+      if (versionString == null) {
+        Messages.showMessageDialog(
+          myProject, CompilerBundle.message("javac.error.unknown.jdk.version", jdk.getName()), CompilerBundle.message("compiler.javac.name"), Messages.getErrorIcon()
+        );
+        return false;
+      }
+
+      if (CompilerUtil.isOfVersion(versionString, "1.0")) {
+        Messages.showMessageDialog(
+          myProject, CompilerBundle.message("javac.error.1_0_compilation.not.supported"), CompilerBundle.message("compiler.javac.name"), Messages.getErrorIcon()
+        );
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  @NotNull
+  @NonNls
+  public String getId() { // used for externalization
+    return JavaCompilers.JAVAC_ID;
+  }
+
+  @NotNull
+  public String getPresentableName() {
+    return CompilerBundle.message("compiler.javac.name");
+  }
+
+  @NotNull
+  public Configurable createConfigurable() {
+    return new JavacConfigurable(JavacConfiguration.getOptions(myProject, JavacConfiguration.class));
+  }
+
+  public OutputParser createErrorParser(@NotNull final String outputDir, Process process) {
+    return new JavacOutputParser(myProject);
+  }
+
+  public OutputParser createOutputParser(@NotNull final String outputDir) {
+    return null;
+  }
+
+  private static class MyException extends RuntimeException {
+    private MyException(Throwable cause) {
+      super(cause);
+    }
+  }
+
+  @NotNull
+  public String[] createStartupCommand(final ModuleChunk chunk, final CompileContext context, final String outputPath)
+    throws IOException, IllegalArgumentException {
+
+    try {
+      return ApplicationManager.getApplication().runReadAction(new Computable<String[]>() {
+        public String[] compute() {
+          try {
+            final List<String> commandLine = new ArrayList<String>();
+            createStartupCommand(chunk, commandLine, outputPath, JavacConfiguration.getOptions(myProject, JavacConfiguration.class), context.isAnnotationProcessorsEnabled());
+            return ArrayUtil.toStringArray(commandLine);
+          }
+          catch (IOException e) {
+            throw new MyException(e);
+          }
+        }
+      });
+    }
+    catch (MyException e) {
+      Throwable cause = e.getCause();
+      if (cause instanceof IOException) {
+        throw (IOException)cause;
+      }
+      throw e;
+    }
+  }
+
+  private void createStartupCommand(final ModuleChunk chunk, @NonNls final List<String> commandLine, final String outputPath,
+                                    JpsJavaCompilerOptions javacOptions, final boolean annotationProcessorsEnabled) throws IOException {
+    final Sdk jdk = getJdkForStartupCommand(chunk);
+    final String versionString = jdk.getVersionString();
+    JavaSdkVersion version = JavaSdk.getInstance().getVersion(jdk);
+    if (versionString == null || version == null || !(jdk.getSdkType() instanceof JavaSdkType)) {
+      throw new IllegalArgumentException(CompilerBundle.message("javac.error.unknown.jdk.version", jdk.getName()));
+    }
+    final boolean isVersion1_0 = version == JavaSdkVersion.JDK_1_0;
+    final boolean isVersion1_1 = version == JavaSdkVersion.JDK_1_1;
+
+    JavaSdkType sdkType = (JavaSdkType)jdk.getSdkType();
+
+    final String toolsJarPath = sdkType.getToolsPath(jdk);
+    if (toolsJarPath == null) {
+      throw new IllegalArgumentException(CompilerBundle.message("javac.error.tools.jar.missing", jdk.getName()));
+    }
+
+    final String vmExePath = sdkType.getVMExecutablePath(jdk);
+
+    commandLine.add(vmExePath);
+
+    if (version.isAtLeast(JavaSdkVersion.JDK_1_2)) {
+      commandLine.add("-Xmx" + javacOptions.MAXIMUM_HEAP_SIZE + "m");
+    }
+    else {
+      commandLine.add("-mx" + javacOptions.MAXIMUM_HEAP_SIZE + "m");
+    }
+
+    final List<String> additionalOptions =
+      addAdditionalSettings(commandLine, javacOptions, myAnnotationProcessorMode, version, chunk, annotationProcessorsEnabled);
+
+    CompilerUtil.addLocaleOptions(commandLine, false);
+
+    commandLine.add("-classpath");
+
+    if (isVersion1_0) {
+      commandLine.add(sdkType.getToolsPath(jdk)); //  do not use JavacRunner for jdk 1.0
+    }
+    else {
+      commandLine.add(sdkType.getToolsPath(jdk) + File.pathSeparator + JavaSdkUtil.getIdeaRtJarPath());
+      commandLine.add(JavacRunner.class.getName());
+      commandLine.add("\"" + versionString + "\"");
+    }
+
+    if (version.isAtLeast(JavaSdkVersion.JDK_1_3)) {
+      commandLine.add(JAVAC_MAIN_CLASS);
+    }
+    else {
+      commandLine.add(JAVAC_MAIN_CLASS_OLD);
+    }
+
+    addCommandLineOptions(chunk, commandLine, outputPath, jdk, isVersion1_0, isVersion1_1, myTempFiles, true, true, myAnnotationProcessorMode);
+
+    commandLine.addAll(additionalOptions);
+
+    final List<VirtualFile> files = chunk.getFilesToCompile();
+
+    if (isVersion1_0) {
+      for (VirtualFile file : files) {
+        String path = file.getPath();
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Adding path for compilation " + path);
+        }
+        commandLine.add(CompilerUtil.quotePath(path));
+      }
+    }
+    else {
+      File sourcesFile = FileUtil.createTempFile("javac", ".tmp");
+      sourcesFile.deleteOnExit();
+      myTempFiles.add(sourcesFile);
+      final PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(sourcesFile)));
+      try {
+        for (final VirtualFile file : files) {
+          // Important: should use "/" slashes!
+          // but not for JDK 1.5 - see SCR 36673
+          final String path = version.isAtLeast(JavaSdkVersion.JDK_1_5) ? file.getPath().replace('/', File.separatorChar) : file.getPath();
+          if (LOG.isDebugEnabled()) {
+            LOG.debug("Adding path for compilation " + path);
+          }
+          writer.println(isVersion1_1 ? path : CompilerUtil.quotePath(path));
+        }
+      }
+      finally {
+        writer.close();
+      }
+      commandLine.add("@" + sourcesFile.getAbsolutePath());
+    }
+  }
+
+  public static List<String> addAdditionalSettings(List<String> commandLine, JpsJavaCompilerOptions javacOptions, boolean isAnnotationProcessing,
+                                                   JavaSdkVersion version, ModuleChunk chunk, boolean annotationProcessorsEnabled) {
+    final List<String> additionalOptions = new ArrayList<String>();
+    StringTokenizer tokenizer = new StringTokenizer(new JavacSettingsBuilder(javacOptions).getOptionsString(chunk), " ");
+    if (!version.isAtLeast(JavaSdkVersion.JDK_1_6)) {
+      isAnnotationProcessing = false; // makes no sense for these versions
+      annotationProcessorsEnabled = false;
+    }
+    if (isAnnotationProcessing) {
+      final AnnotationProcessingConfiguration config = CompilerConfiguration.getInstance(chunk.getProject()).getAnnotationProcessingConfiguration(chunk.getModules()[0]);
+      additionalOptions.add("-Xprefer:source");
+      additionalOptions.add("-implicit:none");
+      additionalOptions.add("-proc:only");
+      if (!config.isObtainProcessorsFromClasspath()) {
+        final String processorPath = config.getProcessorPath();
+        additionalOptions.add("-processorpath");
+        additionalOptions.add(FileUtil.toSystemDependentName(processorPath));
+      }
+      final Set<String> processors = config.getProcessors();
+      if (!processors.isEmpty()) {
+        additionalOptions.add("-processor");
+        additionalOptions.add(StringUtil.join(processors, ","));
+      }
+      for (Map.Entry<String, String> entry : config.getProcessorOptions().entrySet()) {
+        additionalOptions.add("-A" + entry.getKey() + "=" +entry.getValue());
+      }
+    }
+    else {
+      if (annotationProcessorsEnabled) {
+        // Unless explicitly specified by user, disable annotation processing by default for 'java compilation' mode
+        // This is needed to suppress unwanted side-effects from auto-discovered processors from compilation classpath
+        additionalOptions.add("-proc:none");
+      }
+    }
+
+    while (tokenizer.hasMoreTokens()) {
+      @NonNls String token = tokenizer.nextToken();
+      if (version == JavaSdkVersion.JDK_1_0 && "-deprecation".equals(token)) {
+        continue; // not supported for this version
+      }
+      if (!version.isAtLeast(JavaSdkVersion.JDK_1_5) && "-Xlint".equals(token)) {
+        continue; // not supported in these versions
+      }
+      if (isAnnotationProcessing) {
+        if (token.startsWith("-proc:")) {
+          continue;
+        }
+        if (token.startsWith("-implicit:")) {
+          continue;
+        }
+      }
+      else { // compiling java
+        if (annotationProcessorsEnabled) {
+          // in this mode we have -proc:none already added above, so user's settings should be ignored
+          if (token.startsWith("-proc:")) {
+            continue;
+          }
+        }
+      }
+      if (token.startsWith("-J-")) {
+        commandLine.add(token.substring("-J".length()));
+      }
+      else {
+        additionalOptions.add(token);
+      }
+    }
+
+    return additionalOptions;
+  }
+
+  public static void addCommandLineOptions(ModuleChunk chunk, @NonNls List<String> commandLine, String outputPath, Sdk jdk,
+                                           boolean version1_0,
+                                           boolean version1_1,
+                                           List<File> tempFiles, boolean addSourcePath, boolean useTempFile,
+                                           boolean isAnnotationProcessingMode) throws IOException {
+
+    LanguageLevel languageLevel = chunk.getLanguageLevel();
+    CompilerUtil.addSourceCommandLineSwitch(jdk, languageLevel, commandLine);
+    CompilerUtil.addTargetCommandLineSwitch(chunk, commandLine);
+
+    commandLine.add("-verbose");
+
+    final String cp = chunk.getCompilationClasspath();
+    final String bootCp = chunk.getCompilationBootClasspath();
+
+    final String classPath;
+    if (version1_0 || version1_1) {
+      classPath = bootCp + File.pathSeparator + cp;
+    }
+    else {
+      classPath = cp;
+      commandLine.add("-bootclasspath");
+      addClassPathValue(jdk, false, commandLine, bootCp, "javac_bootcp", tempFiles, useTempFile);
+    }
+
+    commandLine.add("-classpath");
+    addClassPathValue(jdk, version1_0, commandLine, classPath, "javac_cp", tempFiles, useTempFile);
+
+    if (!version1_1 && !version1_0 && addSourcePath) {
+      commandLine.add("-sourcepath");
+      // this way we tell the compiler that the sourcepath is "empty". However, javac thinks that sourcepath is 'new File("")'
+      // this may cause problems if we have java code in IDEA working directory
+      if (isAnnotationProcessingMode) {
+        final int currentSourcesMode = chunk.getSourcesFilter();
+        commandLine.add(chunk.getSourcePath(currentSourcesMode == ModuleChunk.TEST_SOURCES? ModuleChunk.ALL_SOURCES : currentSourcesMode));
+      }
+      else {
+        commandLine.add("\"\"");
+      }
+    }
+
+    if (isAnnotationProcessingMode) {
+      commandLine.add("-s");
+      commandLine.add(outputPath.replace('/', File.separatorChar));
+      final String moduleOutputPath = CompilerPaths.getModuleOutputPath(chunk.getModules()[0], false);
+      if (moduleOutputPath != null) {
+        commandLine.add("-d");
+        commandLine.add(moduleOutputPath.replace('/', File.separatorChar));
+      }
+    }
+    else {
+      commandLine.add("-d");
+      commandLine.add(outputPath.replace('/', File.separatorChar));
+    }
+  }
+
+  private static void addClassPathValue(final Sdk jdk, final boolean isVersion1_0, final List<String> commandLine, final String cpString, @NonNls final String tempFileName,
+                                        List<File> tempFiles,
+                                        boolean useTempFile) throws IOException {
+    if (!useTempFile) {
+      commandLine.add(cpString);
+      return;
+    }
+    // must include output path to classpath, otherwise javac will compile all dependent files no matter were they compiled before or not
+    if (isVersion1_0) {
+      commandLine.add(((JavaSdkType)jdk.getSdkType()).getToolsPath(jdk) + File.pathSeparator + cpString);
+    }
+    else {
+      File cpFile = FileUtil.createTempFile(tempFileName, ".tmp");
+      cpFile.deleteOnExit();
+      tempFiles.add(cpFile);
+      final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(cpFile)));
+      try {
+        CompilerIOUtil.writeString(cpString, out);
+      }
+      finally {
+        out.close();
+      }
+      commandLine.add("@" + cpFile.getAbsolutePath());
+    }
+  }
+
+  private Sdk getJdkForStartupCommand(final ModuleChunk chunk) {
+    final Sdk jdk = chunk.getJdk();
+    if (ApplicationManager.getApplication().isUnitTestMode() && JavacConfiguration.getOptions(myProject, JavacConfiguration.class).isTestsUseExternalCompiler()) {
+      final String jdkHomePath = CompilerConfigurationImpl.getTestsExternalCompilerHome();
+      if (jdkHomePath == null) {
+        throw new IllegalArgumentException("[TEST-MODE] Cannot determine home directory for JDK to use javac from");
+      }
+      // when running under Mock JDK use VM executable from the JDK on which the tests run
+      return new MockJdkWrapper(jdkHomePath, jdk);
+    }
+    return jdk;
+  }
+
+  public void compileFinished() {
+    FileUtil.asyncDelete(myTempFiles);
+    myTempFiles.clear();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacConfigurable.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacConfigurable.java
new file mode 100644
index 0000000..c51f5f3
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacConfigurable.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 com.intellij.compiler.impl.javaCompiler.javac;
+
+import com.intellij.compiler.options.ComparingUtils;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.ui.RawCommandLineEditor;
+import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerOptions;
+
+import javax.swing.*;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 30, 2004
+ */
+public class JavacConfigurable implements Configurable{
+  private JPanel myPanel;
+  private JCheckBox myCbDebuggingInfo;
+  private JCheckBox myCbDeprecation;
+  private JCheckBox myCbGenerateNoWarnings;
+  private RawCommandLineEditor myAdditionalOptionsField;
+  private JTextField myJavacMaximumHeapField;
+  private final JpsJavaCompilerOptions myJavacSettings;
+
+  public JavacConfigurable(final JpsJavaCompilerOptions javacSettings) {
+    myJavacSettings = javacSettings;
+    myAdditionalOptionsField.setDialogCaption(CompilerBundle.message("java.compiler.option.additional.command.line.parameters"));
+  }
+
+  public String getDisplayName() {
+    return null;
+  }
+
+  public String getHelpTopic() {
+    return null;
+  }
+
+  public JComponent createComponent() {
+    return myPanel;
+  }
+
+  public boolean isModified() {
+    boolean isModified = false;
+    isModified |= ComparingUtils.isModified(myJavacMaximumHeapField, myJavacSettings.MAXIMUM_HEAP_SIZE);
+
+    isModified |= ComparingUtils.isModified(myCbDeprecation, myJavacSettings.DEPRECATION);
+    isModified |= ComparingUtils.isModified(myCbDebuggingInfo, myJavacSettings.DEBUGGING_INFO);
+    isModified |= ComparingUtils.isModified(myCbGenerateNoWarnings, myJavacSettings.GENERATE_NO_WARNINGS);
+    isModified |= ComparingUtils.isModified(myAdditionalOptionsField, myJavacSettings.ADDITIONAL_OPTIONS_STRING);
+    return isModified;
+  }
+
+  public void apply() throws ConfigurationException {
+
+    try {
+      myJavacSettings.MAXIMUM_HEAP_SIZE = Integer.parseInt(myJavacMaximumHeapField.getText());
+      if(myJavacSettings.MAXIMUM_HEAP_SIZE < 1) {
+        myJavacSettings.MAXIMUM_HEAP_SIZE = 128;
+      }
+    }
+    catch(NumberFormatException exception) {
+      myJavacSettings.MAXIMUM_HEAP_SIZE = 128;
+    }
+
+    myJavacSettings.DEPRECATION =  myCbDeprecation.isSelected();
+    myJavacSettings.DEBUGGING_INFO = myCbDebuggingInfo.isSelected();
+    myJavacSettings.GENERATE_NO_WARNINGS = myCbGenerateNoWarnings.isSelected();
+    myJavacSettings.ADDITIONAL_OPTIONS_STRING = myAdditionalOptionsField.getText();
+  }
+
+  public void reset() {
+    myJavacMaximumHeapField.setText(Integer.toString(myJavacSettings.MAXIMUM_HEAP_SIZE));
+    myCbDeprecation.setSelected(myJavacSettings.DEPRECATION);
+    myCbDebuggingInfo.setSelected(myJavacSettings.DEBUGGING_INFO);
+    myCbGenerateNoWarnings.setSelected(myJavacSettings.GENERATE_NO_WARNINGS);
+    myAdditionalOptionsField.setText(myJavacSettings.ADDITIONAL_OPTIONS_STRING);
+  }
+
+  public void disposeUIResources() {
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacConfiguration.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacConfiguration.java
new file mode 100755
index 0000000..6e1f497
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacConfiguration.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 com.intellij.compiler.impl.javaCompiler.javac;
+
+import com.intellij.openapi.components.*;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerOptions;
+
+@State(
+  name = "JavacSettings",
+  storages = {
+    @Storage( file = StoragePathMacros.PROJECT_FILE)
+   ,@Storage( file = StoragePathMacros.PROJECT_CONFIG_DIR + "/compiler.xml", scheme = StorageScheme.DIRECTORY_BASED)
+    }
+)
+public class JavacConfiguration implements PersistentStateComponent<JpsJavaCompilerOptions> {
+  private final JpsJavaCompilerOptions mySettings = new JpsJavaCompilerOptions();
+  private final Project myProject;
+
+  public JavacConfiguration(Project project) {
+    myProject = project;
+  }
+
+  @Override
+  @NotNull
+  public JpsJavaCompilerOptions getState() {
+    JpsJavaCompilerOptions state = new JpsJavaCompilerOptions();
+    XmlSerializerUtil.copyBean(mySettings, state);
+    state.ADDITIONAL_OPTIONS_STRING = PathMacroManager.getInstance(myProject).collapsePathsRecursively(state.ADDITIONAL_OPTIONS_STRING);
+    return state;
+  }
+
+  @Override
+  public void loadState(JpsJavaCompilerOptions state) {
+    XmlSerializerUtil.copyBean(state, mySettings);
+  }
+
+  public static JpsJavaCompilerOptions getOptions(Project project, Class<? extends JavacConfiguration> aClass) {
+    JavacConfiguration configuration = ServiceManager.getService(project, aClass);
+    return configuration.mySettings;
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacOptionsPanel.form b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacOptionsPanel.form
new file mode 100644
index 0000000..3291deb
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacOptionsPanel.form
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.compiler.impl.javaCompiler.javac.JavacConfigurable">
+  <grid id="280f7" binding="myPanel" 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>
+      <xy x="79" y="160" width="574" height="210"/>
+    </constraints>
+    <properties/>
+    <clientProperties>
+      <BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithIndent"/>
+    </clientProperties>
+    <border type="etched" title-resource-bundle="messages/CompilerBundle" title-key="javac.options.group.title"/>
+    <children>
+      <component id="8538e" class="javax.swing.JCheckBox" binding="myCbDeprecation">
+        <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>
+          <selected value="true"/>
+          <text resource-bundle="messages/CompilerBundle" key="java.compiler.option.report.deprecated"/>
+        </properties>
+      </component>
+      <component id="3d38f" class="javax.swing.JCheckBox" binding="myCbDebuggingInfo">
+        <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>
+          <selected value="true"/>
+          <text resource-bundle="messages/CompilerBundle" key="java.compiler.option.generate.debugging.info"/>
+        </properties>
+      </component>
+      <component id="90803" class="javax.swing.JCheckBox" binding="myCbGenerateNoWarnings">
+        <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="messages/CompilerBundle" key="java.compiler.option.generate.no.warnings"/>
+        </properties>
+      </component>
+      <grid id="d7ddf" layout-manager="GridLayoutManager" row-count="2" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="10" left="8" bottom="0" right="0"/>
+        <constraints>
+          <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="f8a80" class="javax.swing.JLabel">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <text resource-bundle="messages/CompilerBundle" key="java.compiler.option.additional.command.line.parameters"/>
+            </properties>
+          </component>
+          <component id="479c2" class="com.intellij.ui.RawCommandLineEditor" binding="myAdditionalOptionsField">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="3" 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="327dc" class="javax.swing.JLabel">
+            <constraints>
+              <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <text resource-bundle="messages/CompilerBundle" key="javac.option.max.heap.size"/>
+            </properties>
+          </component>
+          <component id="15c16" class="javax.swing.JTextField" binding="myJavacMaximumHeapField">
+            <constraints>
+              <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
+                <preferred-size width="60" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <margin top="0" left="2" bottom="0" right="0"/>
+              <text value="128"/>
+            </properties>
+            <clientProperties>
+              <caretAspectRatio class="java.lang.Float" value="0.04"/>
+            </clientProperties>
+          </component>
+          <component id="9ae23" class="com.intellij.ui.components.JBLabel">
+            <constraints>
+              <grid row="1" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <enabled value="false"/>
+              <text value="(ineffective when &quot;Use external build&quot; is on)"/>
+            </properties>
+          </component>
+          <hspacer id="30d41">
+            <constraints>
+              <grid row="1" column="3" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+            </constraints>
+          </hspacer>
+        </children>
+      </grid>
+      <vspacer id="72a3c">
+        <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>
+</form>
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacOutputParser.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacOutputParser.java
new file mode 100644
index 0000000..ba12e69
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacOutputParser.java
@@ -0,0 +1,283 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.javac;
+
+import com.intellij.compiler.OutputParser;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.editor.ex.util.EditorUtil;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
+import com.intellij.rt.compiler.JavacResourcesReader;
+import com.intellij.util.StringBuilderSpinAllocator;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class JavacOutputParser extends OutputParser {
+  private final int myTabSize;
+  @NonNls private String WARNING_PREFIX = "warning:"; // default value
+
+  public JavacOutputParser(Project project) {
+    myTabSize = CodeStyleSettingsManager.getSettings(project).getTabSize(StdFileTypes.JAVA);
+    if (ApplicationManager.getApplication().isUnitTestMode()) {
+      // emulate patterns setup if 'embedded' javac is used (javac is started not via JavacRunner)
+      addJavacPattern(JavacResourcesReader.MSG_PARSING_STARTED + JavacResourcesReader.CATEGORY_VALUE_DIVIDER + "[parsing started {0}]");
+      addJavacPattern(JavacResourcesReader.MSG_PARSING_COMPLETED + JavacResourcesReader.CATEGORY_VALUE_DIVIDER + "[parsing completed {0}ms]");
+      addJavacPattern(JavacResourcesReader.MSG_LOADING + JavacResourcesReader.CATEGORY_VALUE_DIVIDER + "[loading {0}]");
+      addJavacPattern(JavacResourcesReader.MSG_CHECKING + JavacResourcesReader.CATEGORY_VALUE_DIVIDER + "[checking {0}]");
+      addJavacPattern(JavacResourcesReader.MSG_WROTE + JavacResourcesReader.CATEGORY_VALUE_DIVIDER + "[wrote {0}]");
+    }
+  }
+
+  public boolean processMessageLine(Callback callback) {
+    if (super.processMessageLine(callback)) {
+      return true;
+    }                                              
+    final String line = callback.getCurrentLine();
+    if (line == null) {
+      return false;
+    }
+    if (JavacResourcesReader.MSG_PATTERNS_START.equals(line)) {
+      myParserActions.clear();
+      while (true) {
+        final String patternLine = callback.getNextLine();
+        if (JavacResourcesReader.MSG_PATTERNS_END.equals(patternLine)) {
+          break;
+        }
+        addJavacPattern(patternLine);
+      }
+      return true;
+    }
+
+    int colonIndex1 = line.indexOf(':');
+    if (colonIndex1 == 1){ // drive letter
+      colonIndex1 = line.indexOf(':', colonIndex1 + 1);
+    }
+
+    if (colonIndex1 >= 0){ // looks like found something like file path
+      @NonNls String part1 = line.substring(0, colonIndex1).trim();
+      if(part1.equalsIgnoreCase("error") /*jikes*/ || part1.equalsIgnoreCase("Caused by")) {
+        addMessage(callback, CompilerMessageCategory.ERROR, line.substring(colonIndex1));
+        return true;
+      }
+      if(part1.equalsIgnoreCase("warning")) {
+        addMessage(callback, CompilerMessageCategory.WARNING, line.substring(colonIndex1));
+        return true;
+      }
+      if(part1.equals("javac")) {
+        addMessage(callback, CompilerMessageCategory.ERROR, line);
+        return true;
+      }
+
+      final int colonIndex2 = line.indexOf(':', colonIndex1 + 1);
+      if (colonIndex2 >= 0){
+        final String filePath = part1.replace(File.separatorChar, '/');
+        final Boolean fileExists = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>(){
+          public Boolean compute(){
+            return LocalFileSystem.getInstance().findFileByPath(filePath) != null? Boolean.TRUE : Boolean.FALSE;
+          }
+        });
+        if (!fileExists.booleanValue()) {
+          // the part one turned out to be something else than a file path
+          return true;
+        }
+        try {
+          final int lineNum = Integer.parseInt(line.substring(colonIndex1 + 1, colonIndex2).trim());
+          String message = line.substring(colonIndex2 + 1).trim();
+          CompilerMessageCategory category = CompilerMessageCategory.ERROR;
+          if (message.startsWith(WARNING_PREFIX)){
+            message = message.substring(WARNING_PREFIX.length()).trim();
+            category = CompilerMessageCategory.WARNING;
+          }
+
+          List<String> messages = new ArrayList<String>();
+          messages.add(message);
+          int colNum;
+          String prevLine = null;
+          do{
+            final String nextLine = callback.getNextLine();
+            if (nextLine == null) {
+              return false;
+            }
+            if (nextLine.trim().equals("^")){
+              final CharSequence chars = prevLine == null ? line : prevLine;
+              final int offset = Math.max(0, Math.min(chars.length(), nextLine.indexOf('^')));
+              colNum = EditorUtil.calcColumnNumber(null, chars,0, offset, myTabSize);
+              String messageEnd = callback.getNextLine();
+              while (isMessageEnd(messageEnd)) {
+                messages.add(messageEnd.trim());
+                messageEnd = callback.getNextLine();
+              }
+              if (messageEnd != null) {
+                callback.pushBack(messageEnd);
+              }
+              break;
+            }
+            if (prevLine != null) {
+              messages.add(prevLine);
+            }
+            prevLine = nextLine;
+          }
+          while(true);
+
+          if (colNum >= 0){
+            messages = convertMessages(messages);
+            final StringBuilder buf = StringBuilderSpinAllocator.alloc();
+            try {
+              for (final String m : messages) {
+                if (buf.length() > 0) {
+                  buf.append("\n");
+                }
+                buf.append(m);
+              }
+              addMessage(callback, category, buf.toString(), VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, filePath), lineNum, colNum + 1);
+            }
+            finally {
+              StringBuilderSpinAllocator.dispose(buf);
+            }
+            return true;
+          }
+        }
+        catch (NumberFormatException ignored) {
+        }
+      }
+    }
+
+    if(line.endsWith("java.lang.OutOfMemoryError")) {
+      addMessage(callback, CompilerMessageCategory.ERROR, CompilerBundle.message("error.javac.out.of.memory"));
+      return true;
+    }
+
+    addMessage(callback, CompilerMessageCategory.INFORMATION, line);
+    return true;
+  }
+
+  private static boolean isMessageEnd(String line) {
+    return line != null && line.length() > 0 && Character.isWhitespace(line.charAt(0));
+  }
+
+
+  private static List<String> convertMessages(List<String> messages) {
+    if(messages.size() <= 1) {
+      return messages;
+    }
+    final String line0 = messages.get(0);
+    final String line1 = messages.get(1);
+    final int colonIndex = line1.indexOf(':');
+    if (colonIndex > 0){
+      @NonNls String part1 = line1.substring(0, colonIndex).trim();
+      // jikes
+      if ("symbol".equals(part1)){
+        String symbol = line1.substring(colonIndex + 1).trim();
+        messages.remove(1);
+        if(messages.size() >= 2) {
+          messages.remove(1);
+        }
+        messages.set(0, line0 + " " + symbol);
+      }
+    }
+    return messages;
+  }
+
+  private void addJavacPattern(@NonNls final String line) {
+    final int dividerIndex = line.indexOf(JavacResourcesReader.CATEGORY_VALUE_DIVIDER);
+    if (dividerIndex < 0) {
+      // by reports it may happen for some IBM JDKs (empty string?)
+      return;
+    }
+    final String category = line.substring(0, dividerIndex);
+    final String resourceBundleValue = line.substring(dividerIndex + 1);
+    if (JavacResourcesReader.MSG_PARSING_COMPLETED.equals(category) ||
+        JavacResourcesReader.MSG_PARSING_STARTED.equals(category) ||
+        JavacResourcesReader.MSG_WROTE.equals(category)
+      ) {
+      myParserActions.add(new FilePathActionJavac(createMatcher(resourceBundleValue)));
+    }
+    else if (JavacResourcesReader.MSG_CHECKING.equals(category)) {
+      myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) {
+        protected void doExecute(final String line, String parsedData, final Callback callback) {
+          callback.setProgressText(CompilerBundle.message("progress.compiling.class", parsedData));
+        }
+      });
+    }
+    else if (JavacResourcesReader.MSG_LOADING.equals(category)) {
+      myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) {
+        protected void doExecute(final String line, @Nullable String parsedData, final Callback callback) {
+          callback.setProgressText(CompilerBundle.message("progress.loading.classes"));
+        }
+      });
+    }
+    else if (JavacResourcesReader.MSG_NOTE.equals(category)) {
+      myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) {
+        protected void doExecute(final String line, @Nullable final String filePath, final Callback callback) {
+          final boolean fileExists = filePath != null && ApplicationManager.getApplication().runReadAction(new Computable<Boolean>(){
+            public Boolean compute(){
+              return LocalFileSystem.getInstance().findFileByPath(filePath) != null? Boolean.TRUE : Boolean.FALSE;
+            }
+          });
+          if (fileExists) {
+            addMessage(callback, CompilerMessageCategory.WARNING, line, VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, filePath), -1, -1);
+          }
+          else {
+            addMessage(callback, CompilerMessageCategory.INFORMATION, line);
+          }
+        }
+      });
+    }
+    else if (JavacResourcesReader.MSG_WARNING.equals(category)) {
+      WARNING_PREFIX = resourceBundleValue;
+    }
+    else if (JavacResourcesReader.MSG_STATISTICS.equals(category)) {
+      myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) {
+        protected void doExecute(final String line, @Nullable String parsedData, final Callback callback) {
+          // empty
+        }
+      });
+    }
+    else if (JavacResourcesReader.MSG_IGNORED.equals(category)) {
+      myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) {
+        protected void doExecute(final String line, @Nullable String parsedData, final Callback callback) {
+          // ignored
+        }
+      });
+    }
+  }
+
+
+  /**
+   * made public for Tests, do not use this method directly
+   */
+  public static Matcher createMatcher(@NonNls final String resourceBundleValue) {
+    @NonNls String regexp = resourceBundleValue.replaceAll("([\\[\\]\\(\\)\\.\\*])", "\\\\$1");
+    regexp = regexp.replaceAll("\\{\\d+\\}", "(.+)");
+    return Pattern.compile(regexp, Pattern.CASE_INSENSITIVE).matcher("");
+  }
+
+  public boolean isTrimLines() {
+    return false;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacParserAction.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacParserAction.java
new file mode 100644
index 0000000..a9eefaf
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacParserAction.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.
+ */
+package com.intellij.compiler.impl.javaCompiler.javac;
+
+import com.intellij.compiler.OutputParser;
+import com.intellij.compiler.ParserAction;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.regex.Matcher;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Sep 14, 2005
+ */
+public abstract class JavacParserAction extends ParserAction {
+  private final Matcher myMatcher;
+
+  protected JavacParserAction(final Matcher matcher) {
+    myMatcher = matcher;
+  }
+
+  public final boolean execute(String line, final OutputParser.Callback callback) {
+    myMatcher.reset(line);
+    if (!myMatcher.matches()) {
+      return false;
+    }
+    final String parsed = myMatcher.groupCount() >= 1 ? myMatcher.group(1).replace(File.separatorChar, '/') : null;
+    doExecute(line, parsed, callback);
+    return true;
+  }
+
+  protected abstract void doExecute(final String line, @Nullable String parsedData, final OutputParser.Callback callback);
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacSettingsBuilder.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacSettingsBuilder.java
new file mode 100644
index 0000000..7be8e1d
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/JavacSettingsBuilder.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.
+ */
+package com.intellij.compiler.impl.javaCompiler.javac;
+
+import com.intellij.compiler.CompilerEncodingService;
+import com.intellij.compiler.impl.javaCompiler.ModuleChunk;
+import com.intellij.openapi.module.Module;
+import com.intellij.util.Chunk;
+import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerOptions;
+
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public class JavacSettingsBuilder {
+  private final JpsJavaCompilerOptions myOptions;
+
+  public JavacSettingsBuilder(JpsJavaCompilerOptions options) {
+    myOptions = options;
+  }
+
+  protected JpsJavaCompilerOptions getOptions() {
+    return myOptions;
+  }
+
+  public Collection<String> getOptions(Chunk<Module> chunk) {
+    List<String> options = new ArrayList<String>();
+    if (getOptions().DEBUGGING_INFO) {
+      options.add("-g");
+    }
+    if (getOptions().DEPRECATION) {
+      options.add("-deprecation");
+    }
+    if (getOptions().GENERATE_NO_WARNINGS) {
+      options.add("-nowarn");
+    }
+    boolean isEncodingSet = false;
+    final StringTokenizer tokenizer = new StringTokenizer(getOptions().ADDITIONAL_OPTIONS_STRING, " \t\r\n");
+    while(tokenizer.hasMoreTokens()) {
+      final String token = tokenizer.nextToken();
+      if(!acceptUserOption(token)) {
+        continue;
+      }
+      options.add(token);
+      if ("-encoding".equals(token)) {
+        isEncodingSet = true;
+      }
+    }
+    if (!isEncodingSet && acceptEncoding()) {
+      final Charset charset = CompilerEncodingService.getPreferredModuleEncoding(chunk);
+      if (charset != null) {
+        options.add("-encoding");
+        options.add(charset.name());
+      }
+    }
+    return options;
+  }
+
+  protected boolean acceptUserOption(String token) {
+    return !("-g".equals(token) || "-deprecation".equals(token) || "-nowarn".equals(token));
+  }
+
+  protected boolean acceptEncoding() {
+    return true;
+  }
+
+  public String getOptionsString(final ModuleChunk chunk) {
+    final StringBuilder options = new StringBuilder();
+    for (String option : getOptions(chunk)) {
+      if (options.length() > 0) {
+        options.append(" ");
+      }
+      options.append(option);
+    }
+   return options.toString();
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/StatisticsActionJavac.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/StatisticsActionJavac.java
new file mode 100644
index 0000000..d969e40
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/javac/StatisticsActionJavac.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 com.intellij.compiler.impl.javaCompiler.javac;
+
+import com.intellij.compiler.OutputParser;
+import com.intellij.openapi.compiler.CompilerBundle;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.regex.Matcher;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Sep 24, 2005
+ */
+public class StatisticsActionJavac extends JavacParserAction {
+  private final String myProgressMessageResourceKey;
+
+  public StatisticsActionJavac(final Matcher matcher, String progressMessageResourceKey) {
+    super(matcher);
+    myProgressMessageResourceKey = progressMessageResourceKey;
+  }
+
+  protected void doExecute(final String line, @Nullable String parsedData, final OutputParser.Callback callback) {
+    callback.setProgressText(CompilerBundle.message(myProgressMessageResourceKey));
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/JikesCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/JikesCompiler.java
new file mode 100644
index 0000000..fb4eed3
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/JikesCompiler.java
@@ -0,0 +1,237 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.jikes;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.CompilerConfigurationImpl;
+import com.intellij.compiler.OutputParser;
+import com.intellij.compiler.impl.CompilerUtil;
+import com.intellij.compiler.impl.javaCompiler.BackendCompiler;
+import com.intellij.compiler.impl.javaCompiler.ExternalCompiler;
+import com.intellij.compiler.impl.javaCompiler.ModuleChunk;
+import com.intellij.compiler.options.JavaCompilersTab;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ShowSettingsUtil;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.util.ArrayUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public class JikesCompiler extends ExternalCompiler {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.JikesHandler");
+  private final Project myProject;
+  private File myTempFile;
+
+  public JikesCompiler(Project project) {
+    myProject = project;
+  }
+
+  public boolean checkCompiler(final CompileScope scope) {
+    String compilerPath = getCompilerPath();
+    if (StringUtil.isEmpty(compilerPath)) {
+      Messages.showMessageDialog(
+        myProject,
+        CompilerBundle.message("jikes.error.path.to.compiler.unspecified"), CompilerBundle.message("compiler.jikes.name"),
+        Messages.getErrorIcon()
+      );
+      openConfigurationDialog();
+      compilerPath = getCompilerPath(); // update path
+      if (StringUtil.isEmpty(compilerPath)) {
+        return false;
+      }
+    }
+
+    File file = new File(compilerPath);
+    if (!file.exists()) {
+      Messages.showMessageDialog(
+        myProject,
+        CompilerBundle.message("jikes.error.path.to.compiler.missing", compilerPath),
+        CompilerBundle.message("compiler.jikes.name"),
+        Messages.getErrorIcon()
+      );
+      openConfigurationDialog();
+      compilerPath = getCompilerPath(); // update path
+      if (StringUtil.isEmpty(compilerPath)) {
+        return false;
+      }
+      if (!new File(compilerPath).exists()) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  private void openConfigurationDialog() {
+    final CompilerConfigurationImpl configuration = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(myProject);
+    final Collection<BackendCompiler> compilers = configuration.getRegisteredJavaCompilers();
+    final BackendCompiler defaultCompiler = configuration.getDefaultCompiler();
+    final JavaCompilersTab compilersTab = new JavaCompilersTab(myProject, compilers, defaultCompiler);
+
+    ShowSettingsUtil.getInstance().editConfigurable(myProject, compilersTab);
+  }
+
+  private String getCompilerPath() {
+    return JikesConfiguration.getOptions(myProject).JIKES_PATH.replace('/', File.separatorChar);
+  }
+
+  @NotNull
+  @NonNls
+  public String getId() // used for externalization
+  {
+    return "Jikes";
+  }
+
+  @NotNull
+  public String getPresentableName() {
+    return CompilerBundle.message("compiler.jikes.name");
+  }
+
+  @NotNull
+  public Configurable createConfigurable() {
+    return new JikesConfigurable(JikesConfiguration.getOptions(myProject));
+  }
+
+  public OutputParser createErrorParser(@NotNull final String outputDir, Process process) {
+    return new JikesOutputParser(myProject);
+  }
+
+  @Nullable
+  public OutputParser createOutputParser(@NotNull final String outputDir) {
+    return null;
+  }
+
+  @NotNull
+  public String[] createStartupCommand(final ModuleChunk chunk, final CompileContext context, final String outputPath)
+    throws IOException {
+
+    final ArrayList<String> commandLine = new ArrayList<String>();
+    final IOException[] ex = {null};
+    ApplicationManager.getApplication().runReadAction(new Runnable() {
+      public void run() {
+        try {
+          _createStartupCommand(chunk, commandLine, outputPath);
+        }
+        catch (IOException e) {
+          ex[0] = e;
+        }
+      }
+    });
+    if (ex[0] != null) {
+      throw ex[0];
+    }
+    return ArrayUtil.toStringArray(commandLine);
+  }
+
+  private void _createStartupCommand(final ModuleChunk chunk, final ArrayList<String> commandLine, @NotNull final String outputPath) throws IOException {
+
+    myTempFile = FileUtil.createTempFile("jikes", ".tmp");
+    myTempFile.deleteOnExit();
+
+    final List<VirtualFile> files = chunk.getFilesToCompile();
+    PrintWriter writer = new PrintWriter(new FileWriter(myTempFile));
+    try {
+      for (VirtualFile file : files) {
+        writer.println(file.getPath());
+      }
+    }
+    finally {
+      writer.close();
+    }
+
+    String compilerPath = getCompilerPath();
+    LOG.assertTrue(compilerPath != null, "No path to compiler configured");
+
+    commandLine.add(compilerPath);
+
+    //noinspection HardCodedStringLiteral
+    commandLine.add("-verbose");
+    //noinspection HardCodedStringLiteral
+    commandLine.add("-classpath");
+
+    // must include output path to classpath, otherwise javac will compile all dependent files no matter were they compiled before or not
+    commandLine.add(chunk.getCompilationBootClasspath() + File.pathSeparator + chunk.getCompilationClasspath());
+
+    setupSourceVersion(chunk, commandLine);
+    //noinspection HardCodedStringLiteral
+    commandLine.add("-sourcepath");
+    String sourcePath = chunk.getSourcePath();
+    if (sourcePath.length() > 0) {
+      commandLine.add(sourcePath);
+    }
+    else {
+      commandLine.add("\"\"");
+    }
+
+    //noinspection HardCodedStringLiteral
+    commandLine.add("-d");
+    commandLine.add(outputPath.replace('/', File.separatorChar));
+
+    JikesSettingsBuilder jikesSettings = new JikesSettingsBuilder(JikesConfiguration.getOptions(myProject));
+    StringTokenizer tokenizer = new StringTokenizer(jikesSettings.getOptionsString(chunk), " ");
+    while (tokenizer.hasMoreTokens()) {
+      commandLine.add(tokenizer.nextToken());
+    }
+    commandLine.add("@" + myTempFile.getAbsolutePath());
+  }
+
+  @SuppressWarnings({"HardCodedStringLiteral"})
+  private static void setupSourceVersion(final ModuleChunk chunk, final ArrayList<String> commandLine) {
+    final Sdk jdk = chunk.getJdk();
+    final String versionString = jdk.getVersionString();
+
+    final LanguageLevel applicableLanguageLevel = CompilerUtil.getApplicableLanguageLevel(versionString, chunk.getLanguageLevel());
+    if (applicableLanguageLevel.equals(LanguageLevel.JDK_1_5)) {
+      commandLine.add("-source");
+      commandLine.add("1.4"); // -source 1.5 not supported yet by jikes, so use the highest possible version
+    }
+    else if (applicableLanguageLevel.equals(LanguageLevel.JDK_1_4)) {
+      commandLine.add("-source");
+      commandLine.add("1.4");
+    }
+  }
+
+
+  public void compileFinished() {
+    if (myTempFile != null) {
+      FileUtil.delete(myTempFile);
+      myTempFile = null;
+    }
+  }
+
+}
+
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/JikesConfigurable.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/JikesConfigurable.java
new file mode 100644
index 0000000..1731625
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/JikesConfigurable.java
@@ -0,0 +1,102 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.jikes;
+
+import com.intellij.compiler.options.ComparingUtils;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.RawCommandLineEditor;
+import org.jetbrains.jps.model.java.compiler.JikesCompilerOptions;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 30, 2004
+ */
+public class JikesConfigurable implements Configurable {
+  private JPanel myPanel;
+  private JCheckBox myCbDebuggingInfo;
+  private JCheckBox myCbDeprecation;
+  private JCheckBox myCbGenerateNoWarnings;
+  private RawCommandLineEditor myAdditionalOptionsField;
+  private TextFieldWithBrowseButton myPathField;
+  private final JikesCompilerOptions myJikesSettings;
+
+  public JikesConfigurable(JikesCompilerOptions jikesSettings) {
+    myJikesSettings = jikesSettings;
+    myPathField.getButton().addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor();
+        VirtualFile file = FileChooser.chooseFile(descriptor, myPathField, null, null);
+        if (file != null) {
+          myPathField.setText(file.getPath().replace('/', File.separatorChar));
+        }
+      }
+    });
+    myAdditionalOptionsField.setDialogCaption(CompilerBundle.message("java.compiler.option.additional.command.line.parameters"));
+  }
+
+  public String getDisplayName() {
+    return null;
+  }
+
+  public String getHelpTopic() {
+    return null;
+  }
+
+  public JComponent createComponent() {
+    return myPanel;
+  }
+
+  public boolean isModified() {
+    boolean isModified = false;
+    isModified |= ComparingUtils.isModified(myPathField, myJikesSettings.JIKES_PATH.replace('/', File.separatorChar));
+    isModified |= ComparingUtils.isModified(myCbDeprecation, myJikesSettings.DEPRECATION);
+    isModified |= ComparingUtils.isModified(myCbDebuggingInfo, myJikesSettings.DEBUGGING_INFO);
+    isModified |= ComparingUtils.isModified(myCbGenerateNoWarnings, myJikesSettings.GENERATE_NO_WARNINGS);
+    isModified |= ComparingUtils.isModified(myAdditionalOptionsField, myJikesSettings.ADDITIONAL_OPTIONS_STRING);
+    return isModified;
+  }
+
+  public void apply() throws ConfigurationException {
+    myJikesSettings.JIKES_PATH = myPathField.getText().trim().replace(File.separatorChar, '/');
+    myJikesSettings.DEPRECATION = myCbDeprecation.isSelected();
+    myJikesSettings.DEBUGGING_INFO = myCbDebuggingInfo.isSelected();
+    myJikesSettings.GENERATE_NO_WARNINGS = myCbGenerateNoWarnings.isSelected();
+    myJikesSettings.ADDITIONAL_OPTIONS_STRING = myAdditionalOptionsField.getText();
+  }
+
+  public void reset() {
+    myPathField.setText(myJikesSettings.JIKES_PATH.replace('/', File.separatorChar));
+    myCbDeprecation.setSelected(myJikesSettings.DEPRECATION);
+    myCbDebuggingInfo.setSelected(myJikesSettings.DEBUGGING_INFO);
+    myCbGenerateNoWarnings.setSelected(myJikesSettings.GENERATE_NO_WARNINGS);
+    myAdditionalOptionsField.setText(myJikesSettings.ADDITIONAL_OPTIONS_STRING);
+  }
+
+  public void disposeUIResources() {
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/JikesConfiguration.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/JikesConfiguration.java
new file mode 100755
index 0000000..f82e26f
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/JikesConfiguration.java
@@ -0,0 +1,46 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.jikes;
+
+import com.intellij.openapi.components.*;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.jps.model.java.compiler.JikesCompilerOptions;
+
+@State(
+  name = "JikesSettings",
+  storages = {
+    @Storage( file = StoragePathMacros.PROJECT_FILE)
+   ,@Storage( file = StoragePathMacros.PROJECT_CONFIG_DIR + "/compiler.xml", scheme = StorageScheme.DIRECTORY_BASED)
+    }
+)
+public class JikesConfiguration implements PersistentStateComponent<JikesCompilerOptions> {
+  private final JikesCompilerOptions mySettings = new JikesCompilerOptions();
+
+  @NotNull
+  public JikesCompilerOptions getState() {
+    return mySettings;
+  }
+
+  public void loadState(JikesCompilerOptions state) {
+    XmlSerializerUtil.copyBean(state, mySettings);
+  }
+
+  public static JikesCompilerOptions getOptions(Project project) {
+    return ServiceManager.getService(project, JikesConfiguration.class).getState();
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/JikesOptionsPanel.form b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/JikesOptionsPanel.form
new file mode 100644
index 0000000..23055ee
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/JikesOptionsPanel.form
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.compiler.impl.javaCompiler.jikes.JikesConfigurable">
+  <grid id="66887" binding="myPanel" layout-manager="GridLayoutManager" row-count="6" 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="103" y="66" width="439" height="254"/>
+    </constraints>
+    <properties/>
+    <clientProperties>
+      <BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithIndent"/>
+    </clientProperties>
+    <border type="etched" title-resource-bundle="messages/CompilerBundle" title-key="jikes.options.group.title"/>
+    <children>
+      <component id="3d38f" class="javax.swing.JCheckBox" binding="myCbDebuggingInfo">
+        <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>
+          <margin top="5" left="2" bottom="2" right="2"/>
+          <selected value="true"/>
+          <text resource-bundle="messages/CompilerBundle" key="java.compiler.option.generate.debugging.info"/>
+        </properties>
+      </component>
+      <component id="90803" class="javax.swing.JCheckBox" binding="myCbGenerateNoWarnings">
+        <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>
+          <margin top="2" left="2" bottom="2" right="3"/>
+          <selected value="true"/>
+          <text resource-bundle="messages/CompilerBundle" key="java.compiler.option.generate.no.warnings"/>
+        </properties>
+      </component>
+      <component id="8538e" class="javax.swing.JCheckBox" binding="myCbDeprecation">
+        <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>
+          <margin top="2" left="2" bottom="5" right="2"/>
+          <selected value="true"/>
+          <text resource-bundle="messages/CompilerBundle" key="java.compiler.option.report.deprecated"/>
+        </properties>
+      </component>
+      <vspacer id="b6f66">
+        <constraints>
+          <grid row="5" 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>
+      <grid id="31c1" layout-manager="GridLayoutManager" row-count="1" 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="415eb" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myPathField">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+          </component>
+          <component id="327dc" 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="messages/CompilerBundle" key="jikes.option.path.to.executable"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <grid id="918d2" layout-manager="GridLayoutManager" row-count="1" 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="4" 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="f8a80" 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="messages/CompilerBundle" key="java.compiler.option.additional.command.line.parameters"/>
+            </properties>
+          </component>
+          <component id="479c2" class="com.intellij.ui.RawCommandLineEditor" binding="myAdditionalOptionsField">
+            <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>
+    </children>
+  </grid>
+</form>
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/JikesOutputParser.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/JikesOutputParser.java
new file mode 100644
index 0000000..1d5bb00
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/JikesOutputParser.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.
+ */
+package com.intellij.compiler.impl.javaCompiler.jikes;
+
+import com.intellij.compiler.OutputParser;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.jps.model.java.compiler.JikesCompilerOptions;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+public class JikesOutputParser extends OutputParser {
+  private final JikesCompilerOptions myJikesSettings;
+  @NonNls private static final String JAVA_FILE_MSG_TAIL = ".java:";
+  @NonNls private static final String CAUTION = "Caution";
+  @NonNls private static final String WARNING = "Warning";
+  @NonNls private static final String ERROR = "Error";
+  @NonNls private static final String SEMANTIC_WARNING = "Semantic Warning";
+  @NonNls private static final String SEMANTIC_ERROR = "Semantic Error";
+  @NonNls private static final String ENTER_TO_CONTINUE_REGEXP = ".*Enter\\s+to\\s+continue.*";
+
+  public JikesOutputParser(Project project) {
+    myJikesSettings = JikesConfiguration.getOptions(project);
+    myParserActions.add(new ParserActionJikes());
+  }
+
+  public boolean processMessageLine(Callback callback) {
+    if (super.processMessageLine(callback)) {
+      return true;
+    }
+    String line = callback.getCurrentLine();
+    if (line == null) {
+      return false;
+    }
+    if (line.length() == 0) {
+      return false;
+    }
+//sae
+    if (myJikesSettings.IS_EMACS_ERRORS_MODE) {
+
+      String filePath = "";
+      final int tailIndex = line.indexOf(JAVA_FILE_MSG_TAIL);
+      if (tailIndex > 5) filePath = line.substring(0, tailIndex + 5);
+      filePath = filePath.replace(File.separatorChar, '/');
+      final String url = VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, filePath);
+      if (tailIndex > 6) {
+        line = line.substring(tailIndex + 6);
+        int lineNum;
+
+//second token = start line
+        StringTokenizer tokenizer = new StringTokenizer(line, ":");
+//first token = filename
+        String token = tokenizer.nextToken();
+
+        try {
+          lineNum = Integer.parseInt(token);
+        }
+        catch (Exception e) {
+          addMessage(callback, CompilerMessageCategory.INFORMATION, line);
+          return true;
+        }
+//thrd token = start column
+        token = tokenizer.nextToken();
+        int colNum;
+        try {
+          colNum = Integer.parseInt(token);
+        }
+        catch (Exception e) {
+          addMessage(callback, CompilerMessageCategory.INFORMATION, line);
+          return true;
+        }
+//4,5 token = end line/column   tmp not used
+        tokenizer.nextToken();
+        tokenizer.nextToken();
+// 6 error type
+        CompilerMessageCategory category = CompilerMessageCategory.INFORMATION;
+        token = tokenizer.nextToken().trim();
+        if (CAUTION.equalsIgnoreCase(token)) {
+          category = CompilerMessageCategory.WARNING;
+        }
+        else if (WARNING.equalsIgnoreCase(token) || SEMANTIC_WARNING.equalsIgnoreCase(token)) { // Semantic errors/warnings were introduced in jikes 1.18
+          category = CompilerMessageCategory.WARNING;
+        }
+        else if (ERROR.equalsIgnoreCase(token) || SEMANTIC_ERROR.equalsIgnoreCase(token)) {
+          category = CompilerMessageCategory.ERROR;
+        }
+
+        String message = token;
+        message = message.concat("  ");
+        message = message.concat(tokenizer.nextToken(""));
+        ArrayList<String> messages = new ArrayList<String>();
+        messages.add(message);
+
+        if (colNum > 0 && messages.size() > 0) {
+          StringBuilder buf = new StringBuilder();
+          for (String m : messages) {
+            if (buf.length() > 0) {
+              buf.append("\n");
+            }
+            buf.append(m);
+          }
+          addMessage(callback, category, buf.toString(), url, lineNum, colNum);
+          return true;
+        }
+      }
+    }
+//--sae
+
+//Enter to continue
+    if (!line.matches(ENTER_TO_CONTINUE_REGEXP)) {
+      addMessage(callback, CompilerMessageCategory.INFORMATION, line);
+    }
+    return true;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/JikesSettingsBuilder.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/JikesSettingsBuilder.java
new file mode 100644
index 0000000..636db21
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/JikesSettingsBuilder.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 com.intellij.compiler.impl.javaCompiler.jikes;
+
+import com.intellij.compiler.impl.javaCompiler.javac.JavacSettingsBuilder;
+import com.intellij.openapi.module.Module;
+import com.intellij.util.Chunk;
+import org.jetbrains.jps.model.java.compiler.JikesCompilerOptions;
+
+import java.util.Collection;
+
+public class JikesSettingsBuilder extends JavacSettingsBuilder {
+
+  public JikesSettingsBuilder(JikesCompilerOptions options) {
+    super(options);
+  }
+
+  @Override
+  public JikesCompilerOptions getOptions() {
+    return (JikesCompilerOptions)super.getOptions();
+  }
+
+  public Collection<String> getOptions(Chunk<Module> chunk) {
+    final Collection<String> options = super.getOptions(chunk);
+    if(getOptions().IS_EMACS_ERRORS_MODE) {
+      options.add("+E");
+    }
+    return options;
+  }
+
+  protected boolean acceptUserOption(String token) {
+    if (!super.acceptUserOption(token)) {
+      return false;
+    }
+    return !("++".equals(token) || "+M".equals(token) || "+F".equals(token) || "+E".equals(token));
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/ParserActionJikes.java b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/ParserActionJikes.java
new file mode 100644
index 0000000..ba1884f
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/javaCompiler/jikes/ParserActionJikes.java
@@ -0,0 +1,88 @@
+/*
+ * 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 com.intellij.compiler.impl.javaCompiler.jikes;
+
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.compiler.ParserAction;
+import com.intellij.compiler.OutputParser;
+import com.intellij.compiler.impl.javaCompiler.FileObject;
+import org.jetbrains.annotations.NonNls;
+
+import java.io.File;
+
+/**
+ * This class provides compatibility with older javac(english locale) and jikes versions and
+ * implements parsing method from previous IDEA versions for jikes and javac compiler output.
+ * Used in order not to miss some compiler output messages when jikes is configured as a preferred compiler
+ * Also used in test mode in which javac is not run via JavacRunner
+ *
+ * @author Eugene Zhuravlev
+ *         Date: Sep 24, 2005
+ */
+public class ParserActionJikes extends ParserAction {
+  @NonNls private static final String JAVA_EXTENSION = ".java";
+
+  public boolean execute(@NonNls String line, final OutputParser.Callback callback) {
+    if (!StringUtil.startsWithChar(line, '[') || !StringUtil.endsWithChar(line, ']')) {
+      return false;
+    }
+    if (line.startsWith("[parsing started")){ // javac
+      String filePath = line.substring("[parsing started".length(), line.length() - 1).trim();
+      processParsingMessage(callback, filePath.replace(File.separatorChar, '/'));
+    }
+    else if (line.startsWith("[parsed") && line.contains(JAVA_EXTENSION)) { // javac version 1.2.2
+      int index = line.indexOf(JAVA_EXTENSION);
+      String filePath = line.substring("[parsed".length(), index + JAVA_EXTENSION.length()).trim();
+      processParsingMessage(callback, filePath.replace(File.separatorChar, '/'));
+    }
+    else if (line.startsWith("[read") && line.endsWith(".java]")){ // jikes
+      String filePath = line.substring("[read".length(), line.length() - 1).trim();
+      processParsingMessage(callback, filePath.replace(File.separatorChar, '/'));
+    }
+    else if (line.startsWith("[parsing completed")){
+    }
+    else if (line.startsWith("[loading") || line.startsWith("[loaded") || line.startsWith("[read")){
+      callback.setProgressText(CompilerBundle.message("progress.loading.classes"));
+    }
+    else if (line.startsWith("[checking")){
+      String className = line.substring("[checking".length(), line.length() - 1).trim();
+      callback.setProgressText(CompilerBundle.message("progress.compiling.class", className));
+    }
+    else if (line.startsWith("[wrote") || line.startsWith("[write")){
+      String filePath = line.substring("[wrote".length(), line.length() - 1).trim();
+      processParsingMessage(callback, filePath.replace(File.separatorChar, '/'));
+    }
+    return true;
+  }
+
+  private static void processParsingMessage(final OutputParser.Callback callback, @NonNls final String filePath) {
+    int index = filePath.lastIndexOf('/');
+    final String name = index >= 0 ? filePath.substring(index + 1) : filePath;
+
+    final FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(name);
+    if (StdFileTypes.JAVA.equals(fileType)) {
+      callback.fileProcessed(filePath);
+      callback.setProgressText(CompilerBundle.message("progress.parsing.file", name));
+    }
+    else if (StdFileTypes.CLASS.equals(fileType)) {
+      callback.fileGenerated(new FileObject(new File(filePath)));
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/BuildInstructionBase.java b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/BuildInstructionBase.java
new file mode 100644
index 0000000..446dd2e
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/BuildInstructionBase.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.
+ */
+
+/**
+ * @author cdr
+ */
+package com.intellij.compiler.impl.packagingCompiler;
+
+import com.intellij.openapi.compiler.make.BuildInstruction;
+import com.intellij.openapi.util.UserDataHolderBase;
+import com.intellij.openapi.util.io.FileUtil;
+import gnu.trove.THashSet;
+import org.jetbrains.annotations.NonNls;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+
+public abstract class BuildInstructionBase extends UserDataHolderBase implements BuildInstruction, Cloneable {
+  private final String myOutputRelativePath;
+  private Collection<File> myFilesToDelete;
+
+  protected BuildInstructionBase(String outputRelativePath) {
+    myOutputRelativePath = outputRelativePath;
+  }
+
+  public String getOutputRelativePath() {
+    return myOutputRelativePath;
+  }
+
+  public BuildInstructionBase clone() {
+    return (BuildInstructionBase)super.clone();
+  }
+
+  public void addFileToDelete(File file) {
+    if (myFilesToDelete == null) {
+      myFilesToDelete = new THashSet<File>();
+    }
+    myFilesToDelete.add(file);
+  }
+
+  public void collectFilesToDelete(Collection<File> filesToDelete) {
+    if (myFilesToDelete != null) {
+      filesToDelete.addAll(myFilesToDelete);
+    }
+    myFilesToDelete = null;
+  }
+
+  @NonNls
+  public String toString() {
+    return super.toString();
+  }
+
+  protected File createTempFile(final String prefix, final String suffix) throws IOException {
+    final File tempFile = FileUtil.createTempFile(prefix + "___", suffix);
+    addFileToDelete(tempFile);
+    return tempFile;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/BuildRecipeImpl.java b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/BuildRecipeImpl.java
new file mode 100644
index 0000000..03e62a0
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/BuildRecipeImpl.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.
+ */
+
+/**
+ * @author cdr
+ */
+package com.intellij.compiler.impl.packagingCompiler;
+
+import com.intellij.openapi.compiler.make.BuildInstruction;
+import com.intellij.openapi.compiler.make.BuildInstructionVisitor;
+import com.intellij.openapi.compiler.make.BuildRecipe;
+import com.intellij.openapi.deployment.DeploymentUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+@Deprecated
+public class BuildRecipeImpl implements BuildRecipe {
+  private final List<BuildInstruction> myInstructions = new ArrayList<BuildInstruction>();
+
+  public void addInstruction(BuildInstruction instruction) {
+    if (!contains(instruction)) {
+      myInstructions.add(instruction);
+    }
+  }
+
+  public boolean contains(final BuildInstruction instruction) {
+    return myInstructions.contains(instruction);
+  }
+
+  public boolean visitInstructions(BuildInstructionVisitor visitor, boolean reverseOrder){
+    try {
+      return visitInstructionsWithExceptions(visitor, reverseOrder);
+    }
+    catch (Exception e) {
+      if (e instanceof RuntimeException) {
+        throw (RuntimeException)e;
+      }
+      return false;
+    }
+  }
+  public boolean visitInstructionsWithExceptions(BuildInstructionVisitor visitor, boolean reverseOrder) throws Exception {
+    for (int i = reverseOrder ? myInstructions.size()-1 : 0;
+         reverseOrder ? i>=0 : i < myInstructions.size();
+         i += reverseOrder ? -1 : 1) {
+      BuildInstruction instruction = myInstructions.get(i);
+      if (!instruction.accept(visitor)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  public void addFileCopyInstruction(@NotNull File file, boolean isDirectory, String outputRelativePath) {
+    addInstruction(new FileCopyInstructionImpl(file, isDirectory, DeploymentUtil.trimForwardSlashes(outputRelativePath)));
+  }
+
+  public String toString() {
+    String s = "Build recipe:";
+    for (BuildInstruction buildInstruction : myInstructions) {
+      s += "\n" + buildInstruction + "; ";
+    }
+    return s;
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/DependentJarsEvaluator.java b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/DependentJarsEvaluator.java
new file mode 100644
index 0000000..9fa4e2c
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/DependentJarsEvaluator.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 com.intellij.compiler.impl.packagingCompiler;
+
+import com.intellij.openapi.util.Pair;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class DependentJarsEvaluator {
+  private final Set<JarInfo> myJars = new LinkedHashSet<JarInfo>();
+
+  public void addJarWithDependencies(final JarInfo jarInfo) {
+    if (myJars.add(jarInfo)) {
+      for (JarDestinationInfo destination : jarInfo.getJarDestinations()) {
+        addJarWithDependencies(destination.getJarInfo());
+      }
+      for (Pair<String, JarInfo> pair : jarInfo.getPackedJars()) {
+        addJarWithDependencies(pair.getSecond());
+      }
+    }
+  }
+
+  public Set<JarInfo> getJars() {
+    return myJars;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/DestinationInfo.java b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/DestinationInfo.java
new file mode 100644
index 0000000..abda982
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/DestinationInfo.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 com.intellij.compiler.impl.packagingCompiler;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public abstract class DestinationInfo {
+  private VirtualFile myOutputFile;
+  private final String myOutputPath;
+  private final String myOutputFilePath;
+
+  protected DestinationInfo(@NotNull final String outputPath, @Nullable final VirtualFile outputFile, @NotNull String outputFilePath) {
+    myOutputFilePath = outputFilePath;
+    myOutputFile = outputFile;
+    myOutputPath = outputPath;
+  }
+
+  @NotNull
+  public String getOutputPath() {
+    return myOutputPath;
+  }
+
+  @Nullable
+  public VirtualFile getOutputFile() {
+    return myOutputFile;
+  }
+
+  @NotNull
+  public String getOutputFilePath() {
+    return myOutputFilePath;
+  }
+
+  public void update() {
+    if (myOutputFile != null && !myOutputFile.isValid()) {
+      myOutputFile = null;
+    }
+    if (myOutputFile == null) {
+      myOutputFile = LocalFileSystem.getInstance().findFileByPath(myOutputFilePath);
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/ExplodedDestinationInfo.java b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/ExplodedDestinationInfo.java
new file mode 100644
index 0000000..33a3a8e
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/ExplodedDestinationInfo.java
@@ -0,0 +1,33 @@
+/*
+ * 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 com.intellij.compiler.impl.packagingCompiler;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public class ExplodedDestinationInfo extends DestinationInfo {
+  public ExplodedDestinationInfo(final String outputPath, @Nullable final VirtualFile outputFile) {
+    super(outputPath, outputFile, outputPath);
+  }
+
+  public String toString() {
+    return getOutputPath();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/FileCopyInstructionImpl.java b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/FileCopyInstructionImpl.java
new file mode 100644
index 0000000..64b5855
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/FileCopyInstructionImpl.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.
+ */
+package com.intellij.compiler.impl.packagingCompiler;
+
+import com.intellij.openapi.compiler.make.BuildInstructionVisitor;
+import com.intellij.openapi.compiler.make.FileCopyInstruction;
+import com.intellij.openapi.util.io.FileUtil;
+
+import java.io.File;
+
+public class FileCopyInstructionImpl extends BuildInstructionBase implements FileCopyInstruction {
+  private File myFile;
+  private boolean myIsDirectory;
+
+  public FileCopyInstructionImpl(File source, boolean isDirectory, String outputRelativePath) {
+    super(outputRelativePath);
+    setFile(source, isDirectory);
+  }
+
+  public boolean accept(BuildInstructionVisitor visitor) throws Exception {
+    return visitor.visitFileCopyInstruction(this);
+  }
+
+  public String toString() {
+    return "Copy " + getFile() + "->" + getOutputRelativePath();
+  }
+
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (!(o instanceof FileCopyInstruction)) return false;
+
+    final FileCopyInstruction item = (FileCopyInstruction) o;
+
+    if (getFile() != null ? !FileUtil.filesEqual(getFile(), item.getFile()) : item.getFile() != null) return false;
+
+    if (getOutputRelativePath() != null) {
+      if (!getOutputRelativePath().equals( item.getOutputRelativePath() )) return false;
+    } else if ( item.getOutputRelativePath() != null ) {
+      return false;
+    }
+
+    return true;
+  }
+
+  public int hashCode() {
+    return (getFile() != null ? getFile().hashCode() : 0) +
+           (getOutputRelativePath() != null ? getOutputRelativePath().hashCode():0);
+  }
+
+  public File getFile() {
+    return myFile;
+  }
+
+  public boolean isDirectory() {
+    return myIsDirectory;
+  }
+
+  private void setFile(File file, boolean isDirectory) {
+    myFile = file;
+    myIsDirectory = isDirectory;
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/IgnoredFileFilter.java b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/IgnoredFileFilter.java
new file mode 100644
index 0000000..5ad3f5a
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/IgnoredFileFilter.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.
+ */
+
+/**
+ * @author cdr
+ */
+package com.intellij.compiler.impl.packagingCompiler;
+
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.fileTypes.StdFileTypes;
+
+import java.io.File;
+import java.io.FileFilter;
+
+public class IgnoredFileFilter implements FileFilter {
+    public boolean accept(File file) {
+      final FileTypeManager fileTypeManager = FileTypeManager.getInstance();
+      final String name = file.getName();
+      return !fileTypeManager.isFileIgnored(name)
+        && fileTypeManager.getFileTypeByFileName(name) != StdFileTypes.IDEA_PROJECT
+        && fileTypeManager.getFileTypeByFileName(name) != StdFileTypes.IDEA_MODULE
+        && fileTypeManager.getFileTypeByFileName(name) != StdFileTypes.IDEA_WORKSPACE
+        ;
+    }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/JarDestinationInfo.java b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/JarDestinationInfo.java
new file mode 100644
index 0000000..5cc45c1
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/JarDestinationInfo.java
@@ -0,0 +1,55 @@
+/*
+ * 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 com.intellij.compiler.impl.packagingCompiler;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.JarFileSystem;
+
+/**
+ * @author nik
+ */
+public class JarDestinationInfo extends DestinationInfo {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.packagingCompiler.JarDestinationInfo");
+  private final String myPathInJar;
+  private final JarInfo myJarInfo;
+
+  public JarDestinationInfo(final String pathInJar, final JarInfo jarInfo, DestinationInfo jarDestination) {
+    super(appendPathInJar(jarDestination.getOutputPath(), pathInJar), jarDestination.getOutputFile(), jarDestination.getOutputFilePath());
+    LOG.assertTrue(!pathInJar.startsWith(".."), pathInJar);
+    myPathInJar = StringUtil.startsWithChar(pathInJar, '/') ? pathInJar : "/" + pathInJar;
+    myJarInfo = jarInfo;
+  }
+
+  private static String appendPathInJar(String outputPath, String pathInJar) {
+    LOG.assertTrue(outputPath.length() > 0 && outputPath.charAt(outputPath.length() - 1) != '/');
+    LOG.assertTrue(pathInJar.length() > 0 && pathInJar.charAt(0) != '/');
+    return outputPath + JarFileSystem.JAR_SEPARATOR + pathInJar;
+  }
+
+  public String getPathInJar() {
+    return myPathInJar;
+  }
+
+  public JarInfo getJarInfo() {
+    return myJarInfo;
+  }
+
+  public String toString() {
+    return myPathInJar + "(" + getOutputPath() + ")";
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/JarInfo.java b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/JarInfo.java
new file mode 100644
index 0000000..7626ca1
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/JarInfo.java
@@ -0,0 +1,77 @@
+/*
+ * 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 com.intellij.compiler.impl.packagingCompiler;
+
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.vfs.VirtualFile;
+
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class JarInfo {
+  private final List<Pair<String, VirtualFile>> myPackedFiles;
+  private final LinkedHashSet<Pair<String, JarInfo>> myPackedJars;
+  private final List<DestinationInfo> myDestinations;
+
+  public JarInfo() {
+    myDestinations = new ArrayList<DestinationInfo>();
+    myPackedFiles = new ArrayList<Pair<String, VirtualFile>>();
+    myPackedJars = new LinkedHashSet<Pair<String, JarInfo>>();
+  }
+
+  public void addDestination(DestinationInfo info) {
+    myDestinations.add(info);
+    if (info instanceof JarDestinationInfo) {
+      JarDestinationInfo destinationInfo = (JarDestinationInfo)info;
+      destinationInfo.getJarInfo().myPackedJars.add(Pair.create(destinationInfo.getPathInJar(), this));
+    }
+  }
+
+  public void addContent(String pathInJar, VirtualFile sourceFile) {
+    myPackedFiles.add(Pair.create(pathInJar, sourceFile));
+  }
+
+  public List<Pair<String, VirtualFile>> getPackedFiles() {
+    return myPackedFiles;
+  }
+
+  public LinkedHashSet<Pair<String, JarInfo>> getPackedJars() {
+    return myPackedJars;
+  }
+
+  public List<JarDestinationInfo> getJarDestinations() {
+    final ArrayList<JarDestinationInfo> list = new ArrayList<JarDestinationInfo>();
+    for (DestinationInfo destination : myDestinations) {
+      if (destination instanceof JarDestinationInfo) {
+        list.add((JarDestinationInfo)destination);
+      }
+    }
+    return list;
+  }
+
+  public List<DestinationInfo> getAllDestinations() {
+    return myDestinations;
+  }
+
+  public String getPresentableDestination() {
+    return !myDestinations.isEmpty() ? myDestinations.get(0).getOutputPath() : "";
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/JarsBuilder.java b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/JarsBuilder.java
new file mode 100644
index 0000000..a546480
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/packagingCompiler/JarsBuilder.java
@@ -0,0 +1,282 @@
+/*
+ * 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 com.intellij.compiler.impl.packagingCompiler;
+
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.deployment.DeploymentUtil;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.impl.compiler.ArtifactCompilerUtil;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.graph.CachingSemiGraph;
+import com.intellij.util.graph.DFSTBuilder;
+import com.intellij.util.graph.GraphGenerator;
+import com.intellij.util.io.ZipUtil;
+import gnu.trove.THashSet;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * @author nik
+ */
+public class JarsBuilder {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.packagingCompiler.JarsBuilder");
+  private final Set<JarInfo> myJarsToBuild;
+  private final FileFilter myFileFilter;
+  private final CompileContext myContext;
+  private Map<JarInfo, File> myBuiltJars;
+
+  public JarsBuilder(Set<JarInfo> jarsToBuild, FileFilter fileFilter, CompileContext context) {
+    DependentJarsEvaluator evaluator = new DependentJarsEvaluator();
+    for (JarInfo jarInfo : jarsToBuild) {
+      evaluator.addJarWithDependencies(jarInfo);
+    }
+    myJarsToBuild = evaluator.getJars();
+    myFileFilter = fileFilter;
+    myContext = context;
+  }
+
+  public boolean buildJars(Set<String> writtenPaths) throws IOException {
+    myContext.getProgressIndicator().setText(CompilerBundle.message("packaging.compiler.message.building.archives"));
+
+    final JarInfo[] sortedJars = sortJars();
+    if (sortedJars == null) {
+      return false;
+    }
+
+    myBuiltJars = new HashMap<JarInfo, File>();
+    try {
+      for (JarInfo jar : sortedJars) {
+        myContext.getProgressIndicator().checkCanceled();
+        buildJar(jar);
+      }
+
+      myContext.getProgressIndicator().setText(CompilerBundle.message("packaging.compiler.message.copying.archives"));
+      copyJars(writtenPaths);
+    }
+    finally {
+      deleteTemporaryJars();
+    }
+
+
+    return true;
+  }
+
+  private void deleteTemporaryJars() {
+    for (File file : myBuiltJars.values()) {
+      FileUtil.delete(file);
+    }
+  }
+
+  private void copyJars(final Set<String> writtenPaths) throws IOException {
+    for (Map.Entry<JarInfo, File> entry : myBuiltJars.entrySet()) {
+      File fromFile = entry.getValue();
+      boolean first = true;
+      for (DestinationInfo destination : entry.getKey().getAllDestinations()) {
+        if (destination instanceof ExplodedDestinationInfo) {
+          File toFile = new File(FileUtil.toSystemDependentName(destination.getOutputPath()));
+
+          if (first) {
+            first = false;
+            renameFile(fromFile, toFile, writtenPaths);
+            fromFile = toFile;
+          }
+          else {
+            DeploymentUtil.getInstance().copyFile(fromFile, toFile, myContext, writtenPaths, myFileFilter);
+          }
+
+        }
+      }
+    }
+  }
+
+  private static void renameFile(final File fromFile, final File toFile, final Set<String> writtenPaths) throws IOException {
+    FileUtil.rename(fromFile, toFile);
+    writtenPaths.add(toFile.getPath());
+  }
+
+  @Nullable
+  private JarInfo[] sortJars() {
+    final DFSTBuilder<JarInfo> builder = new DFSTBuilder<JarInfo>(GraphGenerator.create(CachingSemiGraph.create(new JarsGraph())));
+    if (!builder.isAcyclic()) {
+      final Pair<JarInfo, JarInfo> dependency = builder.getCircularDependency();
+      String message = CompilerBundle.message("packaging.compiler.error.cannot.build.circular.dependency.found.between.0.and.1",
+                                              dependency.getFirst().getPresentableDestination(),
+                                              dependency.getSecond().getPresentableDestination());
+      myContext.addMessage(CompilerMessageCategory.ERROR, message, null, -1, -1);
+      return null;
+    }
+
+    JarInfo[] jars = myJarsToBuild.toArray(new JarInfo[myJarsToBuild.size()]);
+    Arrays.sort(jars, builder.comparator());
+    jars = ArrayUtil.reverseArray(jars);
+    return jars;
+  }
+
+  public Set<JarInfo> getJarsToBuild() {
+    return myJarsToBuild;
+  }
+
+  private void buildJar(final JarInfo jar) throws IOException {
+    if (jar.getPackedFiles().isEmpty() && jar.getPackedJars().isEmpty()) {
+      myContext.addMessage(CompilerMessageCategory.WARNING, "Archive '" + jar.getPresentableDestination() + "' has no files so it won't be created", null, -1, -1);
+      return;
+    }
+
+    myContext.getProgressIndicator()
+      .setText(CompilerBundle.message("packaging.compiler.message.building.0", jar.getPresentableDestination()));
+    File jarFile = FileUtil.createTempFile("artifactCompiler", "tmp");
+    myBuiltJars.put(jar, jarFile);
+
+    FileUtil.createParentDirs(jarFile);
+
+    VirtualFile manifestFile = null;
+    for (Pair<String, VirtualFile> pair : jar.getPackedFiles()) {
+      if (pair.getFirst().equals(JarFile.MANIFEST_NAME)) {
+        manifestFile = pair.getSecond();
+      }
+    }
+    final JarOutputStream jarOutputStream = createJarOutputStream(jarFile, manifestFile);
+
+    try {
+      final THashSet<String> writtenPaths = new THashSet<String>();
+      for (Pair<String, VirtualFile> pair : jar.getPackedFiles()) {
+        if (pair.getFirst().equals(JarFile.MANIFEST_NAME)) continue;
+        final VirtualFile sourceFile = pair.getSecond();
+        if (sourceFile.isInLocalFileSystem()) {
+          File file = VfsUtil.virtualToIoFile(sourceFile);
+          addFileToJar(jarOutputStream, file, pair.getFirst(), writtenPaths);
+        }
+        else {
+          extractFileAndAddToJar(jarOutputStream, sourceFile, pair.getFirst(), writtenPaths);
+        }
+      }
+
+      for (Pair<String, JarInfo> nestedJar : jar.getPackedJars()) {
+        File nestedJarFile = myBuiltJars.get(nestedJar.getSecond());
+        if (nestedJarFile != null) {
+          addFileToJar(jarOutputStream, nestedJarFile, nestedJar.getFirst(), writtenPaths);
+        }
+        else {
+          LOG.debug("nested jar file " + nestedJar.getFirst() + " for " + jar.getPresentableDestination() + " not found");
+        }
+      }
+    }
+    finally {
+      jarOutputStream.close();
+    }
+  }
+
+  private static JarOutputStream createJarOutputStream(File jarFile, VirtualFile manifestFile) throws IOException {
+    final BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(jarFile));
+    final JarOutputStream jarOutputStream;
+    if (manifestFile != null) {
+      final InputStream manifestStream = manifestFile.getInputStream();
+      try {
+        jarOutputStream = new JarOutputStream(outputStream, new Manifest(manifestStream));
+      }
+      finally {
+        manifestStream.close();
+      }
+    }
+    else {
+      jarOutputStream = new JarOutputStream(outputStream);
+    }
+    return jarOutputStream;
+  }
+
+  private void extractFileAndAddToJar(JarOutputStream jarOutputStream, VirtualFile sourceFile, String relativePath, THashSet<String> writtenPaths)
+    throws IOException {
+    relativePath = addParentDirectories(jarOutputStream, writtenPaths, relativePath);
+    myContext.getProgressIndicator().setText2(relativePath);
+    if (!writtenPaths.add(relativePath)) return;
+
+    final BufferedInputStream input = ArtifactCompilerUtil.getJarEntryInputStream(sourceFile, myContext);
+    if (input == null) return;
+
+    ZipEntry entry = new ZipEntry(relativePath);
+    entry.setTime(ArtifactCompilerUtil.getJarFile(sourceFile).lastModified());
+    jarOutputStream.putNextEntry(entry);
+    FileUtil.copy(input, jarOutputStream);
+    jarOutputStream.closeEntry();
+  }
+
+  private void addFileToJar(final @NotNull JarOutputStream jarOutputStream, final @NotNull File file, @NotNull String relativePath,
+                            final @NotNull THashSet<String> writtenPaths) throws IOException {
+    if (!file.exists()) {
+      return;
+    }
+
+    relativePath = addParentDirectories(jarOutputStream, writtenPaths, relativePath);
+    myContext.getProgressIndicator().setText2(relativePath);
+    ZipUtil.addFileToZip(jarOutputStream, file, relativePath, writtenPaths, myFileFilter);
+  }
+
+  private static String addParentDirectories(JarOutputStream jarOutputStream, THashSet<String> writtenPaths, String relativePath) throws IOException {
+    while (StringUtil.startsWithChar(relativePath, '/')) {
+      relativePath = relativePath.substring(1);
+    }
+    int i = relativePath.indexOf('/');
+    while (i != -1) {
+      String prefix = relativePath.substring(0, i+1);
+      if (!writtenPaths.contains(prefix) && prefix.length() > 1) {
+        addEntry(jarOutputStream, prefix);
+        writtenPaths.add(prefix);
+      }
+      i = relativePath.indexOf('/', i + 1);
+    }
+    return relativePath;
+  }
+
+  private static void addEntry(final ZipOutputStream output, @NonNls final String relativePath) throws IOException {
+    ZipEntry e = new ZipEntry(relativePath);
+    e.setMethod(ZipEntry.STORED);
+    e.setSize(0);
+    e.setCrc(0);
+    output.putNextEntry(e);
+    output.closeEntry();
+  }
+
+  private class JarsGraph implements GraphGenerator.SemiGraph<JarInfo> {
+    public Collection<JarInfo> getNodes() {
+      return myJarsToBuild;
+    }
+
+    public Iterator<JarInfo> getIn(final JarInfo n) {
+      Set<JarInfo> ins = new HashSet<JarInfo>();
+      for (JarDestinationInfo destination : n.getJarDestinations()) {
+        ins.add(destination.getJarInfo());
+      }
+      return ins.iterator();
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/resourceCompiler/ResourceCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/resourceCompiler/ResourceCompiler.java
new file mode 100644
index 0000000..3efbec2
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/resourceCompiler/ResourceCompiler.java
@@ -0,0 +1,236 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: Jan 17, 2003
+ * Time: 3:48:26 PM
+ */
+package com.intellij.compiler.impl.resourceCompiler;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.CompilerConfigurationImpl;
+import com.intellij.compiler.impl.CompilerUtil;
+import com.intellij.compiler.make.MakeUtil;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.*;
+import com.intellij.openapi.compiler.ex.CompileContextEx;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.util.Chunk;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+public class ResourceCompiler implements TranslatingCompiler {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.resourceCompiler.ResourceCompiler");
+  private final Project myProject;
+  private final CompilerConfiguration myConfiguration;
+  private final ResourceCompilerExtension[] myResourceCompilerExtensions = ResourceCompilerExtension.EP_NAME.getExtensions();
+
+  public ResourceCompiler(Project project, CompilerConfiguration compilerConfiguration) {
+    myProject = project;
+    myConfiguration = compilerConfiguration;
+  }
+
+  @NotNull
+  public String getDescription() {
+    return CompilerBundle.message("resource.compiler.description");
+  }
+
+  public boolean validateConfiguration(CompileScope scope) {
+    ((CompilerConfigurationImpl) CompilerConfiguration.getInstance(myProject)).convertPatterns();
+    return true;
+  }
+
+  public boolean isCompilableFile(VirtualFile file, CompileContext context) {
+    final Module module = context.getModuleByFile(file);
+    if (module != null && skipStandardResourceCompiler(module)) {
+      return false;
+    }
+    return myConfiguration.isResourceFile(file);
+  }
+
+  public void compile(final CompileContext context, Chunk<Module> moduleChunk, final VirtualFile[] files, OutputSink sink) {
+    context.getProgressIndicator().pushState();
+    context.getProgressIndicator().setText(CompilerBundle.message("progress.copying.resources"));
+
+    final Map<String, Collection<OutputItem>> processed = new HashMap<String, Collection<OutputItem>>();
+    final LinkedList<CopyCommand> copyCommands = new LinkedList<CopyCommand>();
+    final Module singleChunkModule = moduleChunk.getNodes().size() == 1? moduleChunk.getNodes().iterator().next() : null;
+    ApplicationManager.getApplication().runReadAction(new Runnable() {
+      public void run() {
+        final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex();
+        for (final VirtualFile file : files) {
+          if (context.getProgressIndicator().isCanceled()) {
+            break;
+          }
+          final Module module = singleChunkModule != null? singleChunkModule : context.getModuleByFile(file);
+          if (module == null) {
+            continue; // looks like file invalidated
+          }
+          final VirtualFile fileRoot = MakeUtil.getSourceRoot(context, module, file);
+          if (fileRoot == null) {
+            continue;
+          }
+          final String sourcePath = file.getPath();
+          final String relativePath = VfsUtilCore.getRelativePath(file, fileRoot, '/');
+          final boolean inTests = ((CompileContextEx)context).isInTestSourceContent(file);
+          final VirtualFile outputDir = inTests? context.getModuleOutputDirectoryForTests(module) : context.getModuleOutputDirectory(module);
+          if (outputDir == null) {
+            continue;
+          }
+          final String outputPath = outputDir.getPath();
+          
+          final String packagePrefix = fileIndex.getPackageNameByDirectory(fileRoot);
+          final String targetPath;
+          if (packagePrefix != null && packagePrefix.length() > 0) {
+            targetPath = outputPath + "/" + packagePrefix.replace('.', '/') + "/" + relativePath;
+          }
+          else {
+            targetPath = outputPath + "/" + relativePath;
+          }
+          if (sourcePath.equals(targetPath)) {
+            addToMap(processed, outputPath, new MyOutputItem(targetPath, file));
+          }
+          else {
+            copyCommands.add(new CopyCommand(outputPath, sourcePath, targetPath, file));
+          }
+        }
+      }
+    });
+
+    final List<File> filesToRefresh = new ArrayList<File>();
+    // do actual copy outside of read action to reduce the time the application is locked on it
+    while (!copyCommands.isEmpty()) {
+      final CopyCommand command = copyCommands.removeFirst();
+      if (context.getProgressIndicator().isCanceled()) {
+        break;
+      }
+      //context.getProgressIndicator().setFraction((idx++) * 1.0 / total);
+      context.getProgressIndicator().setText2("Copying " + command.getFromPath() + "...");
+      try {
+        final MyOutputItem outputItem = command.copy(filesToRefresh);
+        addToMap(processed, command.getOutputPath(), outputItem);
+      }
+      catch (IOException e) {
+        context.addMessage(
+          CompilerMessageCategory.ERROR,
+          CompilerBundle.message("error.copying", command.getFromPath(), command.getToPath(), e.getMessage()),
+          command.getSourceFileUrl(), -1, -1
+        );
+      }
+    }
+
+    if (!filesToRefresh.isEmpty()) {
+      CompilerUtil.refreshIOFiles(filesToRefresh);
+      filesToRefresh.clear();
+    }
+
+    for (Iterator<Map.Entry<String, Collection<OutputItem>>> it = processed.entrySet().iterator(); it.hasNext();) {
+      Map.Entry<String, Collection<OutputItem>> entry = it.next();
+      sink.add(entry.getKey(), entry.getValue(), VirtualFile.EMPTY_ARRAY);
+      it.remove(); // to free memory
+    }
+    context.getProgressIndicator().popState();
+  }
+
+  private boolean skipStandardResourceCompiler(final Module module) {
+    for (ResourceCompilerExtension extension : myResourceCompilerExtensions) {
+      if (extension.skipStandardResourceCompiler(module)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private static void addToMap(Map<String, Collection<OutputItem>> map, String outputDir, OutputItem item) {
+    Collection<OutputItem> list = map.get(outputDir);
+    if (list == null) {
+      list = new ArrayList<OutputItem>();
+      map.put(outputDir, list);
+    }
+    list.add(item);
+  }
+
+  private static class CopyCommand {
+    private final String myOutputPath;
+    private final String myFromPath;
+    private final String myToPath;
+    private final VirtualFile mySourceFile;
+
+    private CopyCommand(String outputPath, String fromPath, String toPath, VirtualFile sourceFile) {
+      myOutputPath = outputPath;
+      myFromPath = fromPath;
+      myToPath = toPath;
+      mySourceFile = sourceFile;
+    }
+
+    public MyOutputItem copy(List<File> filesToRefresh) throws IOException {
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("Copying " + myFromPath + " to " + myToPath);
+      }
+      final File targetFile = new File(myToPath);
+      FileUtil.copyContent(new File(myFromPath), targetFile);
+      filesToRefresh.add(targetFile);
+      return new MyOutputItem(myToPath, mySourceFile);
+    }
+
+    public String getOutputPath() {
+      return myOutputPath;
+    }
+
+    public String getFromPath() {
+      return myFromPath;
+    }
+
+    public String getToPath() {
+      return myToPath;
+    }
+
+    public String getSourceFileUrl() {
+      // do not use mySourseFile.getUrl() directly as it requires read action
+      return VirtualFileManager.constructUrl(mySourceFile.getFileSystem().getProtocol(), myFromPath);
+    }
+  }
+
+  private static class MyOutputItem implements OutputItem {
+    private final String myTargetPath;
+    private final VirtualFile myFile;
+
+    private MyOutputItem(String targetPath, VirtualFile sourceFile) {
+      myTargetPath = targetPath;
+      myFile = sourceFile;
+    }
+
+    public String getOutputPath() {
+      return myTargetPath;
+    }
+
+    public VirtualFile getSourceFile() {
+      return myFile;
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/resourceCompiler/ResourceCompilerExtension.java b/java/compiler/impl/src/com/intellij/compiler/impl/resourceCompiler/ResourceCompilerExtension.java
new file mode 100644
index 0000000..47d901c
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/resourceCompiler/ResourceCompilerExtension.java
@@ -0,0 +1,14 @@
+package com.intellij.compiler.impl.resourceCompiler;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.module.Module;
+import org.jetbrains.annotations.NotNull;
+
+public abstract class ResourceCompilerExtension {
+  public static final ExtensionPointName<ResourceCompilerExtension> EP_NAME =
+    ExtensionPointName.create("com.intellij.compiler.resourceCompilerExtension");
+
+  public boolean skipStandardResourceCompiler(final @NotNull Module module) {
+    return false;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/rmiCompiler/JavacOutputParserPool.java b/java/compiler/impl/src/com/intellij/compiler/impl/rmiCompiler/JavacOutputParserPool.java
new file mode 100644
index 0000000..cc99f2d
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/rmiCompiler/JavacOutputParserPool.java
@@ -0,0 +1,117 @@
+/*
+ * 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 com.intellij.compiler.impl.rmiCompiler;
+
+import com.intellij.compiler.OutputParser;
+import com.intellij.compiler.impl.CompilerUtil;
+import com.intellij.compiler.impl.javaCompiler.CompilerParsingThread;
+import com.intellij.compiler.impl.javaCompiler.javac.JavacOutputParser;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.JavaSdkType;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.ex.JavaSdkUtil;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.rt.compiler.JavacResourcesReader;
+import com.intellij.util.ArrayUtil;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Oct 10, 2005
+ */
+public class JavacOutputParserPool {
+  protected final Project myProject;
+  private final CompileContext myContext;
+  private final Map<Sdk, OutputParser> myProjectToParserMap = new HashMap<Sdk, OutputParser>();
+
+  protected JavacOutputParserPool(Project project, final CompileContext context) {
+    myProject = project;
+    myContext = context;
+  }
+
+  public OutputParser getJavacOutputParser(Sdk jdk) throws IOException {
+    OutputParser outputParser = myProjectToParserMap.get(jdk);
+    if (outputParser == null) {
+      outputParser = createJavacOutputParser(jdk);
+      myProjectToParserMap.put(jdk, outputParser);
+    }
+    return outputParser;
+  }
+
+  private OutputParser createJavacOutputParser(final Sdk jdk) throws IOException {
+    final JavacOutputParser outputParser = new JavacOutputParser(myProject);
+    // first, need to setup the output parser
+    final String[] setupCmdLine = ApplicationManager.getApplication().runReadAction(new Computable<String[]>() {
+      public String[] compute() {
+        return createParserSetupCommand(jdk);
+      }
+    });
+    final Process setupProcess = Runtime.getRuntime().exec(setupCmdLine);
+
+    final CompilerParsingThread setupProcessParsingThread = new CompilerParsingThread(setupProcess, outputParser, true, true,myContext);
+    final Future<?> parsingThreadFuture = ApplicationManager.getApplication().executeOnPooledThread(setupProcessParsingThread);
+    try {
+      setupProcess.waitFor();
+    }
+    catch (InterruptedException ignored) {
+    }
+    finally {
+      setupProcessParsingThread.setProcessTerminated(true);
+    }
+    try {
+      parsingThreadFuture.get();
+    }
+    catch (InterruptedException e) {
+    }
+    catch (ExecutionException e) {
+    }
+    return outputParser;
+  }
+
+  private static String[] createParserSetupCommand(final Sdk jdk) {
+
+    final VirtualFile homeDirectory = jdk.getHomeDirectory();
+    if (homeDirectory == null) {
+      throw new IllegalArgumentException(CompilerBundle.jdkHomeNotFoundMessage(jdk));
+    }
+
+    final List<String> commandLine = new ArrayList<String>();
+    commandLine.add(((JavaSdkType)jdk.getSdkType()).getVMExecutablePath(jdk));
+
+    CompilerUtil.addLocaleOptions(commandLine, false);
+
+    //noinspection HardCodedStringLiteral
+    commandLine.add("-classpath");
+    commandLine.add(((JavaSdkType)jdk.getSdkType()).getToolsPath(jdk) + File.pathSeparator + JavaSdkUtil.getIdeaRtJarPath());
+
+    commandLine.add(JavacResourcesReader.class.getName());
+
+    return ArrayUtil.toStringArray(commandLine);
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/rmiCompiler/RmicCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/rmiCompiler/RmicCompiler.java
new file mode 100644
index 0000000..ae0f0b7
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/rmiCompiler/RmicCompiler.java
@@ -0,0 +1,522 @@
+/*
+ * 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 com.intellij.compiler.impl.rmiCompiler;
+
+import com.intellij.compiler.OutputParser;
+import com.intellij.compiler.impl.CompilerUtil;
+import com.intellij.compiler.impl.javaCompiler.CompilerParsingThread;
+import com.intellij.compiler.impl.javaCompiler.FileObject;
+import com.intellij.compiler.make.Cache;
+import com.intellij.compiler.make.CacheCorruptedException;
+import com.intellij.compiler.make.DependencyCache;
+import com.intellij.compiler.make.MakeUtil;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.*;
+import com.intellij.openapi.compiler.ex.CompileContextEx;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.OrderEnumerator;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Pair;
+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.util.ArrayUtil;
+import com.intellij.util.Chunk;
+import com.intellij.util.PathsList;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 29, 2004
+ */
+
+public class RmicCompiler implements ClassPostProcessingCompiler{
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.rmiCompiler.RmicCompiler");
+  //private static final FileFilter CLASSES_AND_DIRECTORIES_FILTER = new FileFilter() {
+  //  public boolean accept(File pathname) {
+  //    return pathname.isDirectory() || pathname.getName().endsWith(".class");
+  //  }
+  //};
+  //private static final String REMOTE_INTERFACE_NAME = Remote.class.getName();
+
+  @NotNull
+  public ProcessingItem[] getProcessingItems(final CompileContext context) {
+    if (!RmicConfiguration.getOptions(context.getProject()).IS_EANABLED) {
+      return ProcessingItem.EMPTY_ARRAY;
+    }
+    final Project project = context.getProject();
+    final List<ProcessingItem> items = new ArrayList<ProcessingItem>();
+    ApplicationManager.getApplication().runReadAction(new Runnable() {
+      public void run() {
+        DependencyCache dependencyCache = ((CompileContextEx)context).getDependencyCache();
+        try {
+          final Cache cache = dependencyCache.getCache();
+          final int[] allClassNames = cache.getAllClassNames();
+          final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
+          final LocalFileSystem lfs = LocalFileSystem.getInstance();
+          for (final int className : allClassNames) {
+            final boolean isRemoteObject = cache.isRemote(className) && !MakeUtil.isInterface(cache.getFlags(className));
+            if (!isRemoteObject && !dependencyCache.wasRemote(className)) {
+              continue;
+            }
+            final String outputPath = cache.getPath(className);
+            if (outputPath == null) {
+              continue;
+            }
+            final VirtualFile outputClassFile = lfs.findFileByPath(outputPath.replace(File.separatorChar, '/'));
+            if (outputClassFile == null) {
+              continue;
+            }
+            final VirtualFile sourceFile = ((CompileContextEx)context).getSourceFileByOutputFile(outputClassFile);
+            if (sourceFile == null) {
+              continue;
+            }
+            final Module module = context.getModuleByFile(sourceFile);
+            if (module == null) {
+              continue;
+            }
+            final VirtualFile outputDir = fileIndex.isInTestSourceContent(sourceFile)
+                                          ? context.getModuleOutputDirectoryForTests(module)
+                                          : context.getModuleOutputDirectory(module);
+            if (outputDir == null) {
+              continue;
+            }
+
+            if (!VfsUtil.isAncestor(outputDir, outputClassFile, true)) {
+              LOG.error(outputClassFile.getPath() + " should be located under the output root " + outputDir.getPath());
+            }
+
+            final ProcessingItem item = createProcessingItem(module, outputClassFile, outputDir,
+                                                             isRemoteObject, dependencyCache.resolve(className));
+            items.add(item);
+          }
+        }
+        catch (CacheCorruptedException e) {
+          context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
+          LOG.info(e);
+        }
+      }
+    });
+
+    return items.toArray(new ProcessingItem[items.size()]);
+  }
+
+  public static ProcessingItem createProcessingItem(final Module module,
+                                             final VirtualFile outputClassFile,
+                                             final VirtualFile outputDir,
+                                             final boolean remoteObject, String qName) {
+    final RmicProcessingItem item = new RmicProcessingItem(
+      module, outputClassFile, new File(outputDir.getPath()), qName
+    );
+    item.setIsRemoteObject(remoteObject);
+    return item;
+  }
+
+  public ProcessingItem[] process(CompileContext context, ProcessingItem[] items) {
+    final Project project = context.getProject();
+    if (!RmicConfiguration.getOptions(project).IS_EANABLED) {
+      return ProcessingItem.EMPTY_ARRAY;
+    }
+    final ProgressIndicator progressIndicator = context.getProgressIndicator();
+    progressIndicator.pushState();
+    try {
+      progressIndicator.setText(CompilerBundle.message("progress.generating.rmi.stubs"));
+      final Map<Pair<Module, File>, List<RmicProcessingItem>> sortedByModuleAndOutputPath = new HashMap<Pair<Module,File>, List<RmicProcessingItem>>();
+      for (ProcessingItem item1 : items) {
+        final RmicProcessingItem item = (RmicProcessingItem)item1;
+        final Pair<Module, File> moduleOutputPair = new Pair<Module, File>(item.getModule(), item.getOutputDir());
+        List<RmicProcessingItem> dirItems = sortedByModuleAndOutputPath.get(moduleOutputPair);
+        if (dirItems == null) {
+          dirItems = new ArrayList<RmicProcessingItem>();
+          sortedByModuleAndOutputPath.put(moduleOutputPair, dirItems);
+        }
+        dirItems.add(item);
+      }
+      final List<ProcessingItem> processed = new ArrayList<ProcessingItem>();
+
+      final JavacOutputParserPool parserPool = new JavacOutputParserPool(project, context);
+
+      for (final Pair<Module, File> pair : sortedByModuleAndOutputPath.keySet()) {
+        if (progressIndicator.isCanceled()) {
+          break;
+        }
+        final List<RmicProcessingItem> dirItems = sortedByModuleAndOutputPath.get(pair);
+        try {
+          // should delete all previously generated files for the remote class if there are any
+          for (Iterator itemIterator = dirItems.iterator(); itemIterator.hasNext();) {
+            final RmicProcessingItem item = (RmicProcessingItem)itemIterator.next();
+            item.deleteGeneratedFiles();
+            if (!item.isRemoteObject()) {
+              itemIterator
+                .remove(); // the object was remote and currently is not, so remove it from the list and do not generate stubs for it
+            }
+          }
+          if (!dirItems.isEmpty()) {
+            final RmicProcessingItem[] successfullyProcessed = invokeRmic(context, parserPool, pair.getFirst(), dirItems, pair.getSecond());
+            ContainerUtil.addAll(processed, successfullyProcessed);
+          }
+          progressIndicator.setFraction(((double)processed.size()) / ((double)items.length));
+        }
+        catch (IOException e) {
+          context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
+          LOG.info(e);
+        }
+      }
+      // update state so that the latest timestamps are recorded by make
+      final ProcessingItem[] processedItems = processed.toArray(new ProcessingItem[processed.size()]);
+      final List<File> filesToRefresh = new ArrayList<File>(processedItems.length * 3);
+      for (ProcessingItem processedItem : processedItems) {
+        RmicProcessingItem item = (RmicProcessingItem)processedItem;
+        item.updateState();
+        filesToRefresh.add(item.myStub);
+        filesToRefresh.add(item.mySkel);
+        filesToRefresh.add(item.myTie);
+      }
+      CompilerUtil.refreshIOFiles(filesToRefresh);
+      return processedItems;
+    }
+    finally {
+      progressIndicator.popState();
+    }
+  }
+
+  private static RmicProcessingItem[] invokeRmic(final CompileContext context,
+                                          final JavacOutputParserPool parserPool, final Module module,
+                                          final List<RmicProcessingItem> dirItems,
+                                          final File outputDir
+  ) throws IOException{
+
+    final Sdk jdk = ModuleRootManager.getInstance(module).getSdk();
+
+    final Map<String, RmicProcessingItem> pathToItemMap = new HashMap<String, RmicProcessingItem>();
+    final String[] cmdLine = ApplicationManager.getApplication().runReadAction(new Computable<String[]>() {
+      public String[] compute() {
+        for (final RmicProcessingItem item : dirItems) {
+          pathToItemMap.put(item.myStub.getPath().replace(File.separatorChar, '/'), item);
+          pathToItemMap.put(item.mySkel.getPath().replace(File.separatorChar, '/'), item);
+          pathToItemMap.put(item.myTie.getPath().replace(File.separatorChar, '/'), item);
+        }
+        return createStartupCommand(module, outputDir.getPath(), dirItems.toArray(new RmicProcessingItem[dirItems.size()]));
+      }
+    });
+
+    if (LOG.isDebugEnabled()) {
+      StringBuilder buf = new StringBuilder();
+      for (int idx = 0; idx < cmdLine.length; idx++) {
+        if (idx > 0) {
+          buf.append(" ");
+        }
+        buf.append(cmdLine[idx]);
+      }
+      LOG.debug(buf.toString());
+    }
+
+    // obtain parser before running the process because configuring parser may involve starting another process
+    final OutputParser outputParser = parserPool.getJavacOutputParser(jdk);
+
+    final Process process = Runtime.getRuntime().exec(cmdLine);
+    final Set<RmicProcessingItem> successfullyCompiledItems = new HashSet<RmicProcessingItem>();
+    final CompilerParsingThread parsingThread = new CompilerParsingThread(process, outputParser, false, true,context) {
+      protected void processCompiledClass(FileObject classFileToProcess) {
+        String key = classFileToProcess.getFile().getPath().replace(File.separatorChar, '/');
+        final RmicProcessingItem item = pathToItemMap.get(key);
+        if (item != null) {
+          successfullyCompiledItems.add(item);
+        }
+      }
+    };
+
+    final Future<?> parsingThreadFuture = ApplicationManager.getApplication().executeOnPooledThread(parsingThread);
+    try {
+      process.waitFor();
+    }
+    catch (InterruptedException ignored) {
+    }
+    finally {
+      parsingThread.setProcessTerminated(true);
+    }
+
+    try {
+      parsingThreadFuture.get();
+    }
+    catch (InterruptedException ignored) {
+    }
+    catch (ExecutionException ignored) {
+    }
+    return successfullyCompiledItems.toArray(new RmicProcessingItem[successfullyCompiledItems.size()]);
+  }
+
+  private static String[] createStartupCommand(final Module module, final String outputPath, final RmicProcessingItem[] items) {
+    final Sdk jdk = ModuleRootManager.getInstance(module).getSdk();
+
+    final VirtualFile homeDirectory = jdk.getHomeDirectory();
+    if (homeDirectory == null) {
+      throw new IllegalArgumentException(CompilerBundle.jdkHomeNotFoundMessage(jdk));
+    }
+    final String jdkPath = homeDirectory.getPath().replace('/', File.separatorChar);
+
+    @NonNls final String compilerPath = jdkPath + File.separator + "bin" + File.separator + "rmic";
+
+    @NonNls final List<String> commandLine = new ArrayList<String>();
+    commandLine.add(compilerPath);
+
+    CompilerUtil.addLocaleOptions(commandLine, true);
+
+    commandLine.add("-verbose");
+
+    final Project project = module.getProject();
+    ContainerUtil.addAll(commandLine, new RmicSettingsBuilder(RmicConfiguration.getOptions(project)).getOptions(new Chunk<Module>(module)));
+
+    commandLine.add("-classpath");
+
+    commandLine.add(getCompilationClasspath(module));
+
+    commandLine.add("-d");
+
+    commandLine.add(outputPath);
+
+    for (RmicProcessingItem item : items) {
+      commandLine.add(item.getClassQName());
+    }
+    return ArrayUtil.toStringArray(commandLine);
+  }
+
+  @NotNull
+  public String getDescription() {
+    return CompilerBundle.message("rmi.compiler.description");
+  }
+
+  public boolean validateConfiguration(CompileScope scope) {
+    return true;
+  }
+
+  /*
+  private void addAllRemoteFilesFromModuleOutput(final CompileContext context, final Module module, final List<ProcessingItem> items, final File outputDir, File fromDir, final JavaClass remoteInterface) {
+    final File[] children = fromDir.listFiles(CLASSES_AND_DIRECTORIES_FILTER);
+    for (int idx = 0; idx < children.length; idx++) {
+      final File child = children[idx];
+      if (child.isDirectory()) {
+        addAllRemoteFilesFromModuleOutput(context, module, items, outputDir, child, remoteInterface);
+      }
+      else {
+        final String path = child.getPath();
+        try {
+          final ClassParser classParser = new ClassParser(path);
+          final JavaClass javaClass = classParser.parse();
+          // important! Need this in order to resolve other classes in the project (e.g. superclasses)
+          javaClass.setRepository(BcelUtils.getActiveRepository());
+          if (isRmicCompilable(javaClass, remoteInterface)) {
+            ApplicationManager.getApplication().runReadAction(new Runnable() {
+              public void run() {
+                final VirtualFile outputClassFile = LocalFileSystem.getInstance().findFileByIoFile(child);
+                if (outputClassFile != null) {
+                  items.add(new RmicProcessingItem(module, outputClassFile, outputDir, javaClass.getClassName()));
+                }
+              }
+            });
+          }
+        }
+        catch (IOException e) {
+          context.addMessage(CompilerMessageCategory.ERROR, "Cannot parse class file " + path + ": " + e.toString(), null, -1, -1);
+        }
+        catch (ClassFormatException e) {
+          context.addMessage(CompilerMessageCategory.ERROR, "Class format exception: " + e.getMessage() + " File: " + path, null, -1, -1);
+        }
+      }
+    }
+  }
+  */
+
+  /*
+  private boolean isRmicCompilable(final JavaClass javaClass, final JavaClass remoteInterface) {
+    // stubs are needed for classes that _directly_ implement remote interfaces
+    if (javaClass.isInterface() || isGenerated(javaClass)) {
+      return false;
+    }
+    final JavaClass[] directlyImplementedInterfaces = javaClass.getInterfaces();
+    if (directlyImplementedInterfaces != null) {
+      for (int i = 0; i < directlyImplementedInterfaces.length; i++) {
+        if (directlyImplementedInterfaces[i].instanceOf(remoteInterface)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+  */
+
+  /*
+  private boolean isGenerated(JavaClass javaClass) {
+    final String sourceFileName = javaClass.getSourceFileName();
+    return sourceFileName == null || !sourceFileName.endsWith(".java");
+  }
+  */
+
+
+  public ValidityState createValidityState(DataInput in) throws IOException {
+    return new RemoteClassValidityState(in.readLong(), in.readLong(), in.readLong(), in.readLong());
+  }
+
+  private static String getCompilationClasspath(Module module) {
+    final OrderEnumerator enumerator = ModuleRootManager.getInstance(module).orderEntries().withoutSdk().compileOnly().recursively().exportedOnly();
+    final PathsList pathsList = enumerator.getPathsList();
+    return pathsList.getPathsString();
+  }
+
+  private static final class RemoteClassValidityState implements ValidityState {
+    private final long myRemoteClassTimestamp;
+    private final long myStubTimestamp;
+    private final long mySkelTimestamp;
+    private final long myTieTimestamp;
+
+    private RemoteClassValidityState(long remoteClassTimestamp, long stubTimestamp, long skelTimestamp, long tieTimestamp) {
+      myRemoteClassTimestamp = remoteClassTimestamp;
+      myStubTimestamp = stubTimestamp;
+      mySkelTimestamp = skelTimestamp;
+      myTieTimestamp = tieTimestamp;
+    }
+
+    public boolean equalsTo(ValidityState otherState) {
+      if (otherState instanceof RemoteClassValidityState) {
+        final RemoteClassValidityState state = (RemoteClassValidityState)otherState;
+        return myRemoteClassTimestamp == state.myRemoteClassTimestamp &&
+               myStubTimestamp == state.myStubTimestamp &&
+               mySkelTimestamp == state.mySkelTimestamp &&
+               myTieTimestamp == state.myTieTimestamp;
+      }
+      return false;
+    }
+
+    public void save(DataOutput out) throws IOException {
+      out.writeLong(myRemoteClassTimestamp);
+      out.writeLong(myStubTimestamp);
+      out.writeLong(mySkelTimestamp);
+      out.writeLong(myTieTimestamp);
+    }
+  }
+  private static final class RmicProcessingItem implements ProcessingItem {
+    private final Module myModule;
+    private final VirtualFile myOutputClassFile;
+    private final File myOutputDir;
+    private final String myQName;
+    private RemoteClassValidityState myState;
+    private final File myStub;
+    private final File mySkel;
+    private final File myTie;
+    private boolean myIsRemoteObject = false;
+
+    private RmicProcessingItem(Module module, final VirtualFile outputClassFile, File outputDir, String qName) {
+      myModule = module;
+      myOutputClassFile = outputClassFile;
+      myOutputDir = outputDir;
+      myQName = qName;
+      final String relativePath;
+      final String baseName;
+
+      final int index = qName.lastIndexOf('.');
+      if (index >= 0) {
+        relativePath = qName.substring(0, index + 1).replace('.', '/');
+        baseName = qName.substring(index + 1);
+      }
+      else {
+        relativePath = "";
+        baseName = qName;
+      }
+      final String path = outputDir.getPath().replace(File.separatorChar, '/') + "/" + relativePath;
+      //noinspection HardCodedStringLiteral
+      myStub = new File(path + "/" + baseName + "_Stub.class");
+      //noinspection HardCodedStringLiteral
+      mySkel = new File(path + "/" + baseName + "_Skel.class");
+      //noinspection HardCodedStringLiteral
+      myTie = new File(path + "/_" + baseName + "_Tie.class");
+      updateState();
+    }
+
+    public boolean isRemoteObject() {
+      return myIsRemoteObject;
+    }
+
+    public void setIsRemoteObject(boolean isRemote) {
+      myIsRemoteObject = isRemote;
+    }
+
+    @NotNull
+    public VirtualFile getFile() {
+      return myOutputClassFile;
+    }
+
+    public ValidityState getValidityState() {
+      return myState;
+    }
+
+    public void updateState() {
+      myState = new RemoteClassValidityState(
+        myOutputClassFile.getTimeStamp(),
+        getTimestamp(myStub),
+        getTimestamp(mySkel),
+        getTimestamp(myTie)
+      );
+
+    }
+
+    private static long getTimestamp(File file) {
+      long l = file.lastModified();
+      return l == 0 ? -1L : l;
+    }
+
+    public void deleteGeneratedFiles() {
+      if (FileUtil.delete(myStub)) {
+        CompilerUtil.refreshIOFile(myStub);
+      }
+      if (FileUtil.delete(mySkel)) {
+        CompilerUtil.refreshIOFile(mySkel);
+      }
+      if (FileUtil.delete(myTie)) {
+        CompilerUtil.refreshIOFile(myTie);
+      }
+    }
+
+    public String getClassQName() {
+      return myQName;
+    }
+
+    public File getOutputDir() {
+      return myOutputDir;
+    }
+
+    public Module getModule() {
+      return myModule;
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/rmiCompiler/RmicConfiguration.java b/java/compiler/impl/src/com/intellij/compiler/impl/rmiCompiler/RmicConfiguration.java
new file mode 100755
index 0000000..70b8b42
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/rmiCompiler/RmicConfiguration.java
@@ -0,0 +1,46 @@
+/*
+ * 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 com.intellij.compiler.impl.rmiCompiler;
+
+import com.intellij.openapi.components.*;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.jps.model.java.compiler.RmicCompilerOptions;
+
+@State(
+  name = "RmicSettings",
+  storages = {
+    @Storage( file = StoragePathMacros.PROJECT_FILE)
+   ,@Storage( file = StoragePathMacros.PROJECT_CONFIG_DIR + "/compiler.xml", scheme = StorageScheme.DIRECTORY_BASED)
+    }
+)
+public class RmicConfiguration implements PersistentStateComponent<RmicCompilerOptions> {
+  private final RmicCompilerOptions mySettings = new RmicCompilerOptions();
+
+  @NotNull
+  public RmicCompilerOptions getState() {
+    return mySettings;
+  }
+
+  public void loadState(RmicCompilerOptions state) {
+    XmlSerializerUtil.copyBean(state, mySettings);
+  }
+
+  public static RmicCompilerOptions getOptions(Project project) {
+    return ServiceManager.getService(project, RmicConfiguration.class).getState();
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/rmiCompiler/RmicSettingsBuilder.java b/java/compiler/impl/src/com/intellij/compiler/impl/rmiCompiler/RmicSettingsBuilder.java
new file mode 100644
index 0000000..e4b0aee
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/impl/rmiCompiler/RmicSettingsBuilder.java
@@ -0,0 +1,55 @@
+/*
+ * 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 com.intellij.compiler.impl.rmiCompiler;
+
+import com.intellij.compiler.impl.javaCompiler.javac.JavacSettingsBuilder;
+import com.intellij.openapi.module.Module;
+import com.intellij.util.Chunk;
+import org.jetbrains.jps.model.java.compiler.RmicCompilerOptions;
+
+import java.util.Collection;
+
+public class RmicSettingsBuilder extends JavacSettingsBuilder {
+
+  public RmicSettingsBuilder(final RmicCompilerOptions options) {
+    super(options);
+    getOptions().DEPRECATION = false; // in this configuration deprecation is false by default
+  }
+
+  @Override
+  public RmicCompilerOptions getOptions() {
+    return (RmicCompilerOptions)super.getOptions();
+  }
+
+  public Collection<String> getOptions(Chunk<Module> chunk) {
+    final Collection<String> options = super.getOptions(chunk);
+    if(getOptions().GENERATE_IIOP_STUBS) {
+      options.add("-iiop");
+    }
+    return options;
+  }
+
+  protected boolean acceptUserOption(String token) {
+    if (!super.acceptUserOption(token)) {
+      return false;
+    }
+    return !("-iiop".equals(token));
+  }
+
+  protected boolean acceptEncoding() {
+    return false;
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/AnnotationTargets.java b/java/compiler/impl/src/com/intellij/compiler/make/AnnotationTargets.java
new file mode 100644
index 0000000..100fd4a
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/AnnotationTargets.java
@@ -0,0 +1,66 @@
+/*
+ * 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 com.intellij.compiler.make;
+
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Apr 7, 2004
+ */
+public interface AnnotationTargets {
+  /** Class, interface or enum declaration */
+  int TYPE = 0x1;
+
+  @NonNls String TYPE_STR = "TYPE";
+
+  /** Field declaration (includes enum constants) */
+  int FIELD = 0x2;
+
+  @NonNls String FIELD_STR = "FIELD";
+
+  /** Method declaration */
+  int METHOD = 0x4;
+
+  @NonNls String METHOD_STR = "METHOD";
+
+  /** Parameter declaration */
+  int PARAMETER = 0x8;
+
+  @NonNls String PARAMETER_STR = "PARAMETER";
+
+  /** Constructor declaration */
+  int CONSTRUCTOR = 0x10;
+
+  @NonNls String CONSTRUCTOR_STR = "CONSTRUCTOR";
+
+  /** Local variable declaration */
+  int LOCAL_VARIABLE = 0x20;
+
+  @NonNls String LOCAL_VARIABLE_STR = "LOCAL_VARIABLE";
+
+  /** Annotation type declaration */
+  int ANNOTATION_TYPE = 0x40;
+
+  @NonNls String ANNOTATION_TYPE_STR = "ANNOTATION_TYPE";
+
+  /** Package declaration */
+  int PACKAGE = 0x80;
+
+  @NonNls String PACKAGE_STR = "PACKAGE";
+
+  int ALL = TYPE | FIELD | METHOD | PARAMETER | CONSTRUCTOR | LOCAL_VARIABLE | ANNOTATION_TYPE | PACKAGE;
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/BackwardDependenciesStorage.java b/java/compiler/impl/src/com/intellij/compiler/make/BackwardDependenciesStorage.java
new file mode 100644
index 0000000..fb7a5c6
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/BackwardDependenciesStorage.java
@@ -0,0 +1,511 @@
+/*
+ * 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 com.intellij.compiler.make;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Ref;
+import com.intellij.util.containers.SLRUCache;
+import com.intellij.util.io.DataExternalizer;
+import com.intellij.util.io.EnumeratorIntegerDescriptor;
+import com.intellij.util.io.PersistentHashMap;
+import gnu.trove.*;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Dec 1, 2008
+ */
+public class BackwardDependenciesStorage implements Flushable, Disposable {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.make.CompilerDependencyStorage");
+  protected final PersistentHashMap<Integer, DependenciesSet> myMap;
+  protected final SLRUCache<Integer, ReferencerSetHolder> myCache;
+  private Integer myKeyToRemove;
+  private static final int FIELD = 1;
+  private static final int METHOD = 2;
+  private static final int CLASS = 3;
+
+  public BackwardDependenciesStorage(File file, final int cacheSize) throws IOException {
+    myMap = new PersistentHashMap<Integer, DependenciesSet>(file, EnumeratorIntegerDescriptor.INSTANCE, new MyDataExternalizer());
+
+    myCache = new SLRUCache<Integer, ReferencerSetHolder>(cacheSize * 2, cacheSize) {
+      @NotNull
+      public ReferencerSetHolder createValue(Integer key) {
+        return new ReferencerSetHolder(key);
+      }
+
+      protected void onDropFromCache(Integer key, final ReferencerSetHolder holder) {
+        if (key.equals(myKeyToRemove) || !holder.isDirty()) {
+          return;
+        }
+        try {
+          if (holder.isDataLoaded() || !myMap.containsMapping(key)) {
+            myMap.put(key, new DependenciesSet(holder.getData()));
+          }
+          else {
+            myMap.appendData(key, new PersistentHashMap.ValueDataAppender() {
+              public void append(final DataOutput out) throws IOException {
+                final Ref<IOException> exception = new Ref<IOException>(null);
+                // process removed
+                holder.myRemoveRequested.forEach(new TIntProcedure() {
+                  public boolean execute(int qName) {
+                    try {
+                      out.writeInt(-qName);
+                      return true;
+                    }
+                    catch (IOException e) {
+                      exception.set(e);
+                    }
+                    return false;
+                  }
+                });
+                final IOException _ex = exception.get();
+                if (_ex != null) {
+                  throw _ex;
+                }
+                // process added members
+                for (ReferencerItem item : holder.myAdded) {
+                  item.save(out);
+                }
+              }
+            });
+          }
+        }
+        catch (IOException e) {
+          LOG.error(e);
+        }
+      }
+    };
+  }
+
+  public synchronized void remove(Integer qName) throws IOException {
+    myKeyToRemove = qName;
+    try {
+      myCache.remove(qName);
+    }
+    finally {
+      myKeyToRemove = null;
+    }
+    myMap.remove(qName);
+  }
+
+  public synchronized void removeReferencer(Integer qName, int referencerQName) throws IOException {
+    if (myMap.containsMapping(qName)) {
+      final ReferencerSetHolder set = myCache.get(qName);
+      set.removeReferencer(referencerQName);
+    }
+  }
+
+  public synchronized void addClassReferencer(Integer qName, int referencerQName) {
+    myCache.get(qName).addReferencer(new ReferencerItem(referencerQName));
+  }
+
+  public synchronized void addFieldReferencer(Integer qName, int referencerQName, int fieldName) {
+    myCache.get(qName).addReferencer(new FieldReferencerItem(referencerQName, fieldName));
+  }
+
+  public synchronized void addMethodReferencer(Integer qName, int referencerQName, int methodName, int descriptor) {
+    myCache.get(qName).addReferencer(new MethodReferencerItem(referencerQName, methodName, descriptor));
+  }
+
+  public synchronized Dependency[] getDependencies(Integer classQName) throws CacheCorruptedException {
+    try {
+      if (!myMap.containsMapping(classQName)) {
+        return Dependency.EMPTY_ARRAY;
+      }
+
+      return convertToDependencies(classQName.intValue(), myCache.get(classQName).getData());
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  private static Dependency[] convertToDependencies(int classToSkip, Set<ReferencerItem> data) {
+    final TIntObjectHashMap<Dependency> dependencies = new TIntObjectHashMap<Dependency>();
+    for (ReferencerItem item : data) {
+      if (item.qName == classToSkip) {
+        continue; // skip self-dependencies
+      }
+      final Dependency dependency = addDependency(dependencies, item.qName);
+      if (item instanceof FieldReferencerItem) {
+        dependency.addField(((FieldReferencerItem)item).name);
+      }
+      else if (item instanceof MethodReferencerItem) {
+        final MethodReferencerItem methodItem = (MethodReferencerItem)item;
+        dependency.addMethod(methodItem.name, methodItem.descriptor);
+      }
+    }
+
+    final Dependency[] dependencyArray = new Dependency[dependencies.size()];
+    dependencies.forEachValue(new TObjectProcedure<Dependency>() {
+      private int index = 0;
+      public boolean execute(Dependency object) {
+        dependencyArray[index++] = object;
+        return true;
+      }
+    });
+    return dependencyArray;
+  }
+
+  private static Dependency addDependency(TIntObjectHashMap<Dependency> container, int classQName) {
+    Dependency dependency = container.get(classQName);
+    if (dependency == null) {
+      dependency = new Dependency(classQName);
+      container.put(classQName, dependency);
+    }
+    return dependency;
+  }
+
+  public synchronized void flush() throws IOException {
+    myCache.clear();
+    myMap.force();
+  }
+
+  private void flush(Integer key) {
+    myCache.remove(key); // makes changes into PersistentHashMap
+    myMap.force(); // flushes internal caches (which consume memory) and writes unsaved data to disk
+  }
+
+  public synchronized void dispose() {
+    try {
+      flush();
+    }
+    catch (IOException e) {
+      LOG.info(e);
+    }
+    try {
+      myMap.close();
+    }
+    catch (IOException e) {
+      LOG.info(e);
+    }
+  }
+
+  private static class ReferencerItem {
+    public final int qName;
+
+    private ReferencerItem(int qName) {
+      this.qName = qName;
+    }
+
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      ReferencerItem that = (ReferencerItem)o;
+
+      if (qName != that.qName) return false;
+
+      return true;
+    }
+
+    public int hashCode() {
+      return qName;
+    }
+
+    public void save(DataOutput out) throws IOException {
+      out.writeInt(qName);
+      out.writeByte(CLASS);
+    }
+  }
+
+  private static final class MethodReferencerItem extends ReferencerItem {
+    public final int name;
+    public final int descriptor;
+
+    MethodReferencerItem(int qName, int name, int descriptor) {
+      super(qName);
+      this.name = name;
+      this.descriptor = descriptor;
+    }
+
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+      if (!super.equals(o)) return false;
+
+      MethodReferencerItem that = (MethodReferencerItem)o;
+
+      if (descriptor != that.descriptor) return false;
+      if (name != that.name) return false;
+
+      return true;
+    }
+
+    public int hashCode() {
+      int result = super.hashCode();
+      result = 31 * result + name;
+      result = 31 * result + descriptor;
+      return result;
+    }
+
+    public void save(DataOutput out) throws IOException {
+      out.writeInt(qName);
+      out.writeByte(METHOD);
+      out.writeInt(name);
+      out.writeInt(descriptor);
+    }
+  }
+
+  private static final class FieldReferencerItem extends ReferencerItem {
+    public final int name;
+
+    FieldReferencerItem(int qName, int name) {
+      super(qName);
+      this.name = name;
+    }
+
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+      if (!super.equals(o)) return false;
+
+      FieldReferencerItem that = (FieldReferencerItem)o;
+
+      if (name != that.name) return false;
+
+      return true;
+    }
+
+    public int hashCode() {
+      int result = super.hashCode();
+      result = 31 * result + name;
+      return result;
+    }
+
+    public void save(DataOutput out) throws IOException {
+      out.writeInt(qName);
+      out.writeByte(FIELD);
+      out.writeInt(name);
+    }
+  }
+
+  private class ReferencerSetHolder {
+    private final Integer myKey;
+    private TIntHashSet myRemoveRequested = new TIntHashSet();
+    private Set<ReferencerItem> myAdded = new THashSet<ReferencerItem>();
+    
+    private Set<ReferencerItem> myData = null;
+    private boolean myIsDirty = false;
+
+    public ReferencerSetHolder(Integer key) {
+      myKey = key;
+    }
+
+    public void addReferencer(ReferencerItem referencer) {
+      if (myData != null) {
+        myIsDirty |= myData.add(referencer);
+        return;
+      }
+      myAdded.add(referencer);
+    }
+
+    public void removeReferencer(int qName) {
+      if (myData != null) {
+        myIsDirty |= removeAllReferencerItems(myData, qName);
+        return;
+      }
+      myRemoveRequested.add(qName);
+      removeAllReferencerItems(myAdded, qName);
+    }
+
+    public boolean isDirty() {
+      return myData != null? myIsDirty : myRemoveRequested.size() > 0 || myAdded.size() > 0;
+    }
+
+    public boolean isDataLoaded() {
+      return myData != null;
+    }
+
+    public Set<ReferencerItem> getData() throws IOException {
+      if (myData == null) {
+        final DependenciesSet ds = myMap.get(myKey);
+        Set<ReferencerItem> set = null;
+        if (ds != null) {
+          set = ds.set;
+          myIsDirty |= ds.needsCompacting;
+        }
+        if (set == null) {
+          set = new THashSet<ReferencerItem>();
+        }
+        myIsDirty |= removeAllReferencerItems(set, myRemoveRequested);
+        myIsDirty |= set.addAll(myAdded);
+        myData = set;
+        myAdded = null;
+        myRemoveRequested = null;
+      }
+      return Collections.unmodifiableSet(myData);
+    }
+
+  }
+
+  private static class MyDataExternalizer implements DataExternalizer<DependenciesSet> {
+    public void save(DataOutput out, DependenciesSet ds) throws IOException {
+      final TIntHashSet classes = new TIntHashSet();
+      final Map<Dependency.FieldRef, TIntHashSet> fieldsMap = new HashMap<Dependency.FieldRef, TIntHashSet>();
+      final Map<Dependency.MethodRef, TIntHashSet> methodsMap = new HashMap<Dependency.MethodRef, TIntHashSet>();
+      for (ReferencerItem item : ds.set) {
+        if (item instanceof FieldReferencerItem) {
+          final Dependency.FieldRef ref = new Dependency.FieldRef(((FieldReferencerItem)item).name);
+          TIntHashSet referencers = fieldsMap.get(ref);
+          if (referencers == null) {
+            referencers = new TIntHashSet();
+            fieldsMap.put(ref, referencers);
+          }
+          referencers.add(item.qName);
+        }
+        else if (item instanceof MethodReferencerItem) {
+          final MethodReferencerItem _item = (MethodReferencerItem)item;
+          final Dependency.MethodRef ref = new Dependency.MethodRef(_item.name, _item.descriptor);
+          TIntHashSet referencers = methodsMap.get(ref);
+          if (referencers == null) {
+            referencers = new TIntHashSet();
+            methodsMap.put(ref, referencers);
+          }
+          referencers.add(item.qName);
+        }
+        else {
+          classes.add(item.qName);
+        }
+      }
+
+      out.writeInt(classes.size());
+      for (TIntIterator it = classes.iterator(); it.hasNext();) {
+        out.writeInt(it.next());
+      }
+
+      out.writeInt(fieldsMap.size());
+      for (Map.Entry<Dependency.FieldRef, TIntHashSet> entry : fieldsMap.entrySet()) {
+        out.writeInt(entry.getKey().name);
+        final TIntHashSet referencers = entry.getValue();
+        out.writeInt(referencers.size());
+        for (TIntIterator rit = referencers.iterator(); rit.hasNext();) {
+          out.writeInt(rit.next());
+        }
+      }
+
+      out.writeInt(methodsMap.size());
+      for (Map.Entry<Dependency.MethodRef, TIntHashSet> entry : methodsMap.entrySet()) {
+        final Dependency.MethodRef ref = entry.getKey();
+        out.writeInt(ref.name);
+        out.writeInt(ref.descriptor);
+        final TIntHashSet referencers = entry.getValue();
+        out.writeInt(referencers.size());
+        for (TIntIterator rit = referencers.iterator(); rit.hasNext();) {
+          out.writeInt(rit.next());
+        }
+      }
+    }
+
+    public DependenciesSet read(DataInput in) throws IOException {
+      final Set<ReferencerItem> set = new THashSet<ReferencerItem>();
+
+      int classesCount = in.readInt();
+      while (classesCount-- > 0) {
+        set.add(new ReferencerItem(in.readInt()));
+      }
+
+      int fieldsCount = in.readInt();
+      while (fieldsCount-- > 0) {
+        final int fieldName = in.readInt();
+        int referencersCount = in.readInt();
+        while (referencersCount-- > 0) {
+          set.add(new FieldReferencerItem(in.readInt(), fieldName));
+        }
+      }
+
+      int methodsCount = in.readInt();
+      while (methodsCount-- > 0) {
+        final int methodName = in.readInt();
+        final int methodDescriptor = in.readInt();
+        int referensersCount = in.readInt();
+        while (referensersCount-- > 0) {
+          set.add(new MethodReferencerItem(in.readInt(), methodName, methodDescriptor));
+        }
+      }
+      boolean needsCompacting = false;
+      // manage appends if exist qName, kind, {field | method}
+      final DataInputStream _in = (DataInputStream)in;
+      while (_in.available() > 0) {
+        needsCompacting = true;
+        final int qName = _in.readInt();
+        if (qName < 0) {
+          removeAllReferencerItems(set, -qName);
+        }
+        else {
+          final byte kind = _in.readByte();
+          if (kind == FIELD) {
+            set.add(new FieldReferencerItem(qName, _in.readInt()));
+          }
+          else if (kind == METHOD) {
+            final int name = _in.readInt();
+            final int descriptor = _in.readInt();
+            set.add(new MethodReferencerItem(qName, name, descriptor));
+          }
+          else if (kind == CLASS){
+            set.add(new ReferencerItem(qName));
+          }
+        }
+      }
+
+      return new DependenciesSet(set, needsCompacting);
+    }
+  }
+
+  private static final class DependenciesSet {
+    public final Set<ReferencerItem> set;
+    public final boolean needsCompacting;
+
+    public DependenciesSet(Set<ReferencerItem> set, boolean needsCompacting) {
+      this.set = set;
+      this.needsCompacting = needsCompacting;
+    }
+
+    public DependenciesSet(Set<ReferencerItem> set) {
+      this(set, false);
+    }
+  }
+
+
+  private static boolean removeAllReferencerItems(final Set<ReferencerItem> from, final TIntHashSet qNames) {
+    boolean removed = false;
+    for (Iterator<ReferencerItem> it = from.iterator(); it.hasNext();) {
+      final ReferencerItem item = it.next();
+      if (qNames.contains(item.qName)) {
+        it.remove();
+        removed = true;
+      }
+    }
+    return removed;
+  }
+
+  private static boolean removeAllReferencerItems(final Set<ReferencerItem> from, final int qNames) {
+    boolean removed = false;
+    for (Iterator<ReferencerItem> it = from.iterator(); it.hasNext();) {
+      final ReferencerItem item = it.next();
+      if (qNames == item.qName) {
+        it.remove();
+        removed = true;
+      }
+    }
+    return removed;
+  }
+
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/BoundsParser.java b/java/compiler/impl/src/com/intellij/compiler/make/BoundsParser.java
new file mode 100644
index 0000000..0ab9489
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/BoundsParser.java
@@ -0,0 +1,83 @@
+/*
+ * 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 com.intellij.compiler.make;
+
+import com.intellij.compiler.classParsing.SignatureParser;
+import com.intellij.compiler.classParsing.SignatureParsingException;
+import com.intellij.util.ArrayUtil;
+
+import java.text.CharacterIterator;
+import java.text.StringCharacterIterator;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 10, 2004
+ */
+public class BoundsParser extends SignatureParser{
+  final List<String> myInterfaceBounds = new ArrayList<String>();
+  private int myParsingBound;
+
+  public void parseClassBound(CharacterIterator it, StringBuilder buf) throws SignatureParsingException {
+    myParsingBound += 1;
+    try {
+      super.parseClassBound(it, buf);
+    }
+    finally {
+      myParsingBound -= 1;
+    }
+  }
+
+  public void parseInterfaceBound(CharacterIterator it, StringBuilder buf) throws SignatureParsingException {
+    myParsingBound += 1;
+    try {
+      super.parseInterfaceBound(it, buf);
+    }
+    finally {
+      myParsingBound -= 1;
+    }
+  }
+
+  public void parseClassTypeSignature(CharacterIterator it, StringBuilder buf) throws SignatureParsingException {
+    if (myParsingBound > 0) {
+      final int start = buf.length();
+
+      super.parseClassTypeSignature(it, buf);
+
+      final String qName = convertToQalifiedName(buf.substring(start + 1, buf.length() - 1));
+      myInterfaceBounds.add(qName);
+    }
+    else {
+      super.parseClassTypeSignature(it, buf);
+    }
+  }
+
+  private static String convertToQalifiedName(String ifaceSignature) {
+    ifaceSignature = ifaceSignature.replaceAll("<.*>", "");
+    return ifaceSignature.replace('/', '.');
+  }
+
+  public String[] getBounds() {
+    return ArrayUtil.toStringArray(myInterfaceBounds);
+  }
+
+  public static String[] getBounds(String classSignature) throws SignatureParsingException {
+    final BoundsParser parser = new BoundsParser();
+    parser.parseClassSignature(new StringCharacterIterator(classSignature), new StringBuilder());
+    return parser.getBounds();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/Cache.java b/java/compiler/impl/src/com/intellij/compiler/make/Cache.java
new file mode 100644
index 0000000..2e3d554
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/Cache.java
@@ -0,0 +1,520 @@
+/*
+ * 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 com.intellij.compiler.make;
+
+import com.intellij.compiler.SymbolTable;
+import com.intellij.compiler.classParsing.*;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.cls.ClsFormatException;
+import com.intellij.util.io.DataExternalizer;
+import com.intellij.util.io.EnumeratorIntegerDescriptor;
+import com.intellij.util.io.PersistentHashMap;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Aug 8, 2003
+ * Time: 7:03:56 PM
+ */
+public class Cache {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.make.Cache");
+  public static final int UNKNOWN = -1;
+
+  private final PersistentHashMap<Integer, ClassInfo> myQNameToClassInfoMap;
+
+  private final BackwardDependenciesStorage myDependencies;
+  private final CompilerDependencyStorage<Integer> myQNameToReferencedClassesMap;
+  private final CompilerDependencyStorage<Integer> myQNameToSubclassesMap;
+  private final PersistentHashMap<Integer, Boolean> myRemoteQNames;
+  private final String myStorePath;
+
+  public Cache(@NonNls final String storePath, final int cacheSize) throws IOException {
+    myStorePath = storePath;
+    new File(storePath).mkdirs();
+    myQNameToClassInfoMap = new CachedPersistentHashMap<Integer, ClassInfo>(getOrCreateFile("classes"), EnumeratorIntegerDescriptor.INSTANCE, new DataExternalizer<ClassInfo>() {
+      public void save(DataOutput out, ClassInfo value) throws IOException {
+        value.save(out);
+      }
+      public ClassInfo read(DataInput in) throws IOException {
+        return new ClassInfo(in);
+      }
+    }, cacheSize * 2) {
+      protected boolean isValueDirty(ClassInfo classInfo) {
+        return classInfo.isDirty();
+      }
+    };
+
+    myDependencies = new BackwardDependenciesStorage(getOrCreateFile("bdeps"), cacheSize);
+    myQNameToReferencedClassesMap = new CompilerDependencyStorage<Integer>(getOrCreateFile("fdeps"), EnumeratorIntegerDescriptor.INSTANCE, cacheSize);
+    myQNameToSubclassesMap = new CompilerDependencyStorage<Integer>(getOrCreateFile("subclasses"), EnumeratorIntegerDescriptor.INSTANCE, cacheSize);
+
+    myRemoteQNames = new PersistentHashMap<Integer, Boolean>(getOrCreateFile("remote"), EnumeratorIntegerDescriptor.INSTANCE, new DataExternalizer<Boolean>() {
+      public void save(DataOutput out, Boolean value) throws IOException {
+        out.writeBoolean(value.booleanValue());
+      }
+
+      public Boolean read(DataInput in) throws IOException {
+        return in.readBoolean();
+      }
+    }, cacheSize);
+  }
+
+  private File getOrCreateFile(final String fileName) throws IOException {
+    final File file = new File(myStorePath, fileName);
+    FileUtil.createIfDoesntExist(file);
+    return file;
+  }
+
+  public void dispose() throws CacheCorruptedException {
+    CacheCorruptedException ex = null;
+    try {
+      myQNameToClassInfoMap.close();
+    }
+    catch (IOException e) {
+      LOG.info(e);
+      ex = new CacheCorruptedException(e);
+    }
+    try {
+      myRemoteQNames.close();
+    }
+    catch (IOException e) {
+      LOG.info(e);
+      if (ex != null) {
+        ex = new CacheCorruptedException(e);
+      }
+    }
+
+    myQNameToReferencedClassesMap.dispose();
+    myDependencies.dispose();
+    myQNameToSubclassesMap.dispose();
+
+    if (ex != null) {
+      throw ex;
+    }
+
+  }
+
+  public int[] getAllClassNames() throws CacheCorruptedException {
+    try {
+      final Collection<Integer> allKeys = myQNameToClassInfoMap.getAllKeysWithExistingMapping();
+      final int[] array = ArrayUtil.newIntArray(allKeys.size());
+      int idx = 0;
+      for (Integer id : allKeys) {
+        array[idx++] = id.intValue();
+      }
+      return array;
+    }
+    catch (IOException e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public int importClassInfo(ClassFileReader reader, SymbolTable symbolTable) throws ClsFormatException, CacheCorruptedException {
+    try {
+      final ClassInfo classInfo = new ClassInfo(reader, symbolTable);
+      myQNameToClassInfoMap.put(classInfo.getQualifiedName(), classInfo);
+      return classInfo.getQualifiedName();
+    }
+    catch (IOException e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public void importClassInfo(Cache fromCache, final int qName) throws CacheCorruptedException {
+    try {
+      final ClassInfo classInfo = fromCache.myQNameToClassInfoMap.get(qName);
+      if (classInfo != null) {
+        final ClassInfo clone = classInfo.clone();
+        clone.clearReferences();
+        myQNameToClassInfoMap.put(qName, clone);
+      }
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public AnnotationConstantValue[] getRuntimeVisibleAnnotations(int classId) throws CacheCorruptedException {
+    try {
+      final ClassInfo classInfo = myQNameToClassInfoMap.get(classId);
+      return classInfo != null? classInfo.getRuntimeVisibleAnnotations() : AnnotationConstantValue.EMPTY_ARRAY;
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public AnnotationConstantValue[] getRuntimeInvisibleAnnotations(int classId) throws CacheCorruptedException {
+    try {
+      final ClassInfo classInfo = myQNameToClassInfoMap.get(classId);
+      return classInfo != null? classInfo.getRuntimeInvisibleAnnotations() : AnnotationConstantValue.EMPTY_ARRAY;
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public int[] getSubclasses(int classId) throws CacheCorruptedException {
+    try {
+      return myQNameToSubclassesMap.getValues(classId);
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public void addSubclass(int classId, int subclassQName) throws CacheCorruptedException {
+    try {
+      myQNameToSubclassesMap.addValue(classId, subclassQName);
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public void removeSubclass(int classId, int subclassQName) throws CacheCorruptedException {
+    try {
+      myQNameToSubclassesMap.removeValue(classId, subclassQName);
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public int[] getReferencedClasses(int qName) throws CacheCorruptedException {
+    try {
+      return myQNameToReferencedClassesMap.getValues(qName);
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public void clearReferencedClasses(int qName) throws CacheCorruptedException {
+    try {
+      myQNameToReferencedClassesMap.remove(qName);
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public Collection<ReferenceInfo> getReferences(int classId) throws CacheCorruptedException {
+    try {
+      final ClassInfo classInfo = myQNameToClassInfoMap.get(classId);
+      return classInfo != null? Arrays.asList(classInfo.getReferences()) : Collections.<ReferenceInfo>emptyList();
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public String getSourceFileName(int classId) throws CacheCorruptedException {
+    try {
+      final ClassInfo classInfo = myQNameToClassInfoMap.get(classId);
+      return classInfo != null? classInfo.getSourceFileName() : "";
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public boolean isRemote(int classId) throws CacheCorruptedException {
+    try {
+      return myRemoteQNames.containsMapping(classId);
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public void setRemote(int classId, boolean remote) throws CacheCorruptedException {
+    try {
+      if (remote) {
+        myRemoteQNames.put(classId, Boolean.TRUE);
+      }
+      else {
+        myRemoteQNames.remove(classId);
+      }
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public int getSuperQualifiedName(int classId) throws CacheCorruptedException {
+    try {
+      final ClassInfo classInfo = myQNameToClassInfoMap.get(classId);
+      return classInfo != null? classInfo.getSuperQualifiedName() : UNKNOWN;
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public String getPath(int classId) throws CacheCorruptedException {
+    try {
+      final ClassInfo classInfo = myQNameToClassInfoMap.get(classId);
+      return classInfo != null? classInfo.getPath() : "";
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public void setPath(int classId, String path) throws CacheCorruptedException {
+    try {
+      final ClassInfo classInfo = myQNameToClassInfoMap.get(classId);
+      if (classInfo != null) {
+        classInfo.setPath(path);
+      }
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+  
+  public int getGenericSignature(int classId) throws CacheCorruptedException {
+    try {
+      final ClassInfo classInfo = myQNameToClassInfoMap.get(classId);
+      return classInfo != null? classInfo.getGenericSignature() : UNKNOWN;
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public boolean containsClass(int qName) throws CacheCorruptedException {
+    try {
+      return myQNameToClassInfoMap.containsMapping(qName);
+    }
+    catch (IOException e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public int[] getSuperInterfaces(int classId) throws CacheCorruptedException {
+    try {
+      final ClassInfo classInfo = myQNameToClassInfoMap.get(classId);
+      return classInfo != null? classInfo.getSuperInterfaces() : ArrayUtil.EMPTY_INT_ARRAY;
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public int getFlags(int classId) throws CacheCorruptedException {
+    try {
+      final ClassInfo classInfo = myQNameToClassInfoMap.get(classId);
+      return classInfo != null? classInfo.getFlags() : UNKNOWN;
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public FieldInfo[] getFields(int qName) throws CacheCorruptedException{
+    try {
+      final ClassInfo classInfo = myQNameToClassInfoMap.get(qName);
+      return classInfo != null? classInfo.getFields() : FieldInfo.EMPTY_ARRAY;
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  @Nullable
+  public FieldInfo findField(final int classDeclarationId, final int name, final int descriptor) throws CacheCorruptedException{
+    try {
+      for (FieldInfo fieldInfo : getFields(classDeclarationId)) {
+        if (fieldInfo.getName() == name && fieldInfo.getDescriptor() == descriptor) {
+          return fieldInfo;
+        }
+      }
+      return null;
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  @Nullable
+  public FieldInfo findFieldByName(final int classDeclarationId, final int name) throws CacheCorruptedException{
+    try {
+      for (FieldInfo fieldInfo : getFields(classDeclarationId)) {
+        if (fieldInfo.getName() == name) {
+          return fieldInfo;
+        }
+      }
+      return null;
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public MethodInfo[] getMethods(int classQName) throws CacheCorruptedException{
+    try {
+      final ClassInfo classInfo = myQNameToClassInfoMap.get(classQName);
+      return classInfo != null? classInfo.getMethods() : MethodInfo.EMPTY_ARRAY;
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  @Nullable
+  public MethodInfo findMethod(final int classQName, final int name, final int descriptor) throws CacheCorruptedException{
+    try {
+      for (MethodInfo methodInfo : getMethods(classQName)) {
+        if (methodInfo.getName() == name && methodInfo.getDescriptor() == descriptor) {
+          return methodInfo;
+        }
+      }
+      return null;
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public List<MethodInfo> findMethodsByName(final int classDeclarationId, final int name) throws CacheCorruptedException{
+    try {
+      final List<MethodInfo> methods = new ArrayList<MethodInfo>();
+      for (MethodInfo methodInfo : getMethods(classDeclarationId)) {
+        if (methodInfo.getName() == name) {
+          methods.add(methodInfo);
+        }
+      }
+      return methods;
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  @Nullable
+  public MethodInfo findMethodsBySignature(final int classDeclarationId, final String signature, SymbolTable symbolTable) throws CacheCorruptedException{
+    try {
+      for (MethodInfo methodInfo : getMethods(classDeclarationId)) {
+        if (signature.equals(CacheUtils.getMethodSignature(symbolTable.getSymbol(methodInfo.getName()), symbolTable.getSymbol(methodInfo.getDescriptor())))) {
+          return methodInfo;
+        }
+      }
+      return null;
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public void addClassReferencer(int qName, int referencerQName) throws CacheCorruptedException {
+    try {
+      if (qName == referencerQName) {
+        return; // do not log self-dependencies
+      }
+      if (myQNameToClassInfoMap.containsMapping(qName)) {
+        myDependencies.addClassReferencer(qName, referencerQName);
+        myQNameToReferencedClassesMap.addValue(referencerQName, qName);
+      }
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public void removeClassReferencer(int qName, int referencerQName) throws CacheCorruptedException {
+    try {
+      myDependencies.removeReferencer(qName, referencerQName);
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public void addFieldReferencer(int qName, int fieldName, int referencerQName) throws CacheCorruptedException {
+    try {
+      if (qName != referencerQName && myQNameToClassInfoMap.containsMapping(qName)) {
+        myDependencies.addFieldReferencer(qName, referencerQName, fieldName);
+        myQNameToReferencedClassesMap.addValue(referencerQName, qName);
+      }
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public void addMethodReferencer(int qName, int methodName, int methodDescriptor, int referencerQName) throws CacheCorruptedException {
+    try {
+      if (qName != referencerQName && myQNameToClassInfoMap.containsMapping(qName)) {
+        myDependencies.addMethodReferencer(qName, referencerQName, methodName, methodDescriptor);
+        myQNameToReferencedClassesMap.addValue(referencerQName, qName);
+      }
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  /** @NotNull */
+  public Dependency[] getBackDependencies(final int classQName) throws CacheCorruptedException{
+    return myDependencies.getDependencies(classQName);
+  }
+
+  public void wipe() {
+    try {
+      dispose();
+    }
+    catch (CacheCorruptedException ignored) {
+    }
+    finally {
+      final File[] files = new File(myStorePath).listFiles();
+      if (files != null) {
+        for (File file : files) {
+          if (!file.isDirectory()) {
+            FileUtil.delete(file);
+          }
+        }
+      }
+    }
+  }
+
+  public void removeClass(final int qName) throws CacheCorruptedException {
+    try {
+      final ClassInfo classInfo = myQNameToClassInfoMap.get(qName);
+      if (classInfo == null) {
+        return;
+      }
+      myDependencies.remove(qName);
+      myQNameToClassInfoMap.remove(qName);
+      myQNameToReferencedClassesMap.remove(qName);
+      myQNameToSubclassesMap.remove(qName);
+      myRemoteQNames.remove(qName);
+    }
+    catch (Throwable e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/CacheCorruptedException.java b/java/compiler/impl/src/com/intellij/compiler/make/CacheCorruptedException.java
new file mode 100644
index 0000000..916a370
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/CacheCorruptedException.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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: Jul 10, 2003
+ * Time: 4:51:25 PM
+ */
+package com.intellij.compiler.make;
+
+import com.intellij.openapi.compiler.CompilerBundle;
+
+public class CacheCorruptedException extends Exception{
+  private static final String DEFAULT_MESSAGE = CompilerBundle.message("error.dependency.info.on.disk.corrupted");
+  public CacheCorruptedException(String message) {
+    super((message == null || message.length() == 0)? DEFAULT_MESSAGE : message);
+  }
+
+  public CacheCorruptedException(Throwable cause) {
+    super(DEFAULT_MESSAGE, cause);
+  }
+
+  public CacheCorruptedException(String message, Throwable cause) {
+    super((message == null || message.length() == 0)? DEFAULT_MESSAGE : message, cause);
+  }
+
+  public String getMessage() {
+    return super.getMessage();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/CacheUtils.java b/java/compiler/impl/src/com/intellij/compiler/make/CacheUtils.java
new file mode 100644
index 0000000..bf2a58e
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/CacheUtils.java
@@ -0,0 +1,211 @@
+/*
+ * 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 com.intellij.compiler.make;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.CompilerManagerImpl;
+import com.intellij.compiler.SymbolTable;
+import com.intellij.compiler.classParsing.MethodInfo;
+import com.intellij.compiler.impl.ExitException;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.compiler.CompilerMessage;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.compiler.ex.CompileContextEx;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.Function;
+import com.intellij.util.StringBuilderSpinAllocator;
+import gnu.trove.TIntHashSet;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * @author Eugene Zhuravlev
+ * Date: Aug 18, 2003
+ * Time: 6:32:32 PM
+ */
+public class CacheUtils {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.make.CacheUtils");
+
+  public static String[] getParameterSignatures(MethodInfo methodDeclarationId, SymbolTable symbolTable) throws CacheCorruptedException {
+    String descriptor = symbolTable.getSymbol(methodDeclarationId.getDescriptor());
+    int endIndex = descriptor.indexOf(')');
+    if (endIndex <= 0) {
+      LOG.error("Corrupted method descriptor: " + descriptor);
+    }
+    return parseSignature(descriptor.substring(1, endIndex));
+  }
+
+  private static String[] parseSignature(String signature) {
+    final ArrayList<String> list = new ArrayList<String>();
+    String paramSignature = parseParameterSignature(signature);
+    while (paramSignature != null && !"".equals(paramSignature)) {
+      list.add(paramSignature);
+      signature = signature.substring(paramSignature.length());
+      paramSignature = parseParameterSignature(signature);
+    }
+    return ArrayUtil.toStringArray(list);
+  }
+
+  @SuppressWarnings({"HardCodedStringLiteral"})
+  private static @Nullable String parseParameterSignature(String signature) {
+    if (StringUtil.startsWithChar(signature, 'B')) {
+      return "B";
+    }
+    if (StringUtil.startsWithChar(signature, 'C')) {
+      return "C";
+    }
+    if (StringUtil.startsWithChar(signature, 'D')) {
+      return "D";
+    }
+    if (StringUtil.startsWithChar(signature, 'F')) {
+      return "F";
+    }
+    if (StringUtil.startsWithChar(signature, 'I')) {
+      return "I";
+    }
+    if (StringUtil.startsWithChar(signature, 'J')) {
+      return "J";
+    }
+    if (StringUtil.startsWithChar(signature, 'S')) {
+      return "S";
+    }
+    if (StringUtil.startsWithChar(signature, 'Z')) {
+      return "Z";
+    }
+    if (StringUtil.startsWithChar(signature, 'L')) {
+      return signature.substring(0, signature.indexOf(";") + 1);
+    }
+    if (StringUtil.startsWithChar(signature, '[')) {
+      String s = parseParameterSignature(signature.substring(1));
+      return (s != null) ? ("[" + s) : null;
+    }
+    return null;
+  }
+
+  public static String getMethodSignature(String name, String descriptor) {
+    final StringBuilder builder = StringBuilderSpinAllocator.alloc();
+    try {
+      builder.append(name);
+      builder.append(descriptor.substring(0, descriptor.indexOf(')') + 1));
+      return builder.toString();
+    }
+    finally {
+      StringBuilderSpinAllocator.dispose(builder);
+    }
+  }
+
+  public static boolean areArraysContentsEqual(int[] exceptions1, int[] exceptions2) {
+    if (exceptions1.length != exceptions2.length) {
+      return false;
+    }
+    if (exceptions1.length != 0) { // optimization
+      TIntHashSet exceptionsSet = new TIntHashSet(exceptions1);
+      for (int exception : exceptions2) {
+        if (!exceptionsSet.contains(exception)) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  public static Collection<VirtualFile> findDependentFiles(
+    final CompileContextEx context, 
+    final Set<VirtualFile> compiledWithErrors, 
+    final @Nullable Function<Pair<int[], Set<VirtualFile>>, Pair<int[], Set<VirtualFile>>> filter)
+    throws CacheCorruptedException, ExitException {
+    
+    if (!CompilerConfiguration.MAKE_ENABLED) {
+      return Collections.emptyList();
+    }
+    context.getProgressIndicator().setText(CompilerBundle.message("progress.checking.dependencies"));
+
+    final DependencyCache dependencyCache = context.getDependencyCache();
+
+    final Pair<int[], Set<VirtualFile>> deps = dependencyCache.findDependentClasses(context, context.getProject(), compiledWithErrors);
+    final Pair<int[], Set<VirtualFile>> filteredDeps = filter != null? filter.fun(deps) : deps;
+
+    final Set<VirtualFile> dependentFiles = new HashSet<VirtualFile>();
+    final CacheCorruptedException[] _ex = {null};
+    ApplicationManager.getApplication().runReadAction(new Runnable() {
+      public void run() {
+        try {
+          CompilerConfiguration compilerConfiguration = CompilerConfiguration.getInstance(context.getProject());
+          SourceFileFinder sourceFileFinder = new SourceFileFinder(context.getProject(), context);
+          final Cache cache = dependencyCache.getCache();
+          for (final int infoQName : filteredDeps.getFirst()) {
+            final String qualifiedName = dependencyCache.resolve(infoQName);
+            final String sourceFileName = cache.getSourceFileName(infoQName);
+            final VirtualFile file = sourceFileFinder.findSourceFile(qualifiedName, sourceFileName, true);
+            if (file != null) {
+              dependentFiles.add(file);
+              if (ApplicationManager.getApplication().isUnitTestMode()) {
+                LOG.assertTrue(file.isValid());
+                CompilerManagerImpl.addRecompiledPath(file.getPath());
+              }
+            }
+            else {
+              LOG.info("No source file for " + dependencyCache.resolve(infoQName) + " found; source file name=" + sourceFileName);
+            }
+          }
+          for (final VirtualFile file : filteredDeps.getSecond()) {
+            if (!compilerConfiguration.isExcludedFromCompilation(file)) {
+              dependentFiles.add(file);
+              if (ApplicationManager.getApplication().isUnitTestMode()) {
+                LOG.assertTrue(file.isValid());
+                CompilerManagerImpl.addRecompiledPath(file.getPath());
+              }
+            }
+          }
+        }
+        catch (CacheCorruptedException e) {
+          _ex[0] = e;
+        }
+      }
+    });
+    if (_ex[0] != null) {
+      throw _ex[0];
+    }
+    context.getProgressIndicator().setText(
+      dependentFiles.size() > 0? CompilerBundle.message("progress.found.dependent.files", dependentFiles.size()) : ""
+    );
+
+    return dependentFiles;
+  }
+
+  @NotNull
+  public static Set<VirtualFile> getFilesCompiledWithErrors(final CompileContextEx context) {
+    CompilerMessage[] messages = context.getMessages(CompilerMessageCategory.ERROR);
+    Set<VirtualFile> compiledWithErrors = Collections.emptySet();
+    if (messages.length > 0) {
+      compiledWithErrors = new HashSet<VirtualFile>(messages.length);
+      for (CompilerMessage message : messages) {
+        final VirtualFile file = message.getVirtualFile();
+        if (file != null) {
+          compiledWithErrors.add(file);
+        }
+      }
+    }
+    return compiledWithErrors;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/CachedPersistentHashMap.java b/java/compiler/impl/src/com/intellij/compiler/make/CachedPersistentHashMap.java
new file mode 100644
index 0000000..b8b45cb
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/CachedPersistentHashMap.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 com.intellij.compiler.make;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.containers.SLRUMap;
+import com.intellij.util.io.DataExternalizer;
+import com.intellij.util.io.KeyDescriptor;
+import com.intellij.util.io.PersistentHashMap;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Dec 1, 2008
+ */
+public class CachedPersistentHashMap<Key, Value> extends PersistentHashMap<Key, Value> {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.make.CachedPersistentHashMap");
+  protected final SLRUMap<Key, Value> myCache;
+
+  public CachedPersistentHashMap(File file, KeyDescriptor<Key> keyDescriptor, DataExternalizer<Value> valDescriptor, final int cacheSize) throws IOException {
+    super(file, keyDescriptor, valDescriptor);
+    myCache = new SLRUMap<Key,Value>(cacheSize * 2, cacheSize) {
+      protected void onDropFromCache(Key key, Value value) {
+        if (isValueDirty(value)) {
+          try {
+            CachedPersistentHashMap.super.put(key, value);
+          }
+          catch (IOException e) {
+            LOG.info(e);
+          }
+        }
+      }
+    };
+  }
+
+  protected boolean isValueDirty(Value value) {
+    return false;
+  }
+
+  @Override
+  protected void doPut(Key key, Value value) throws IOException {
+    myCache.remove(key);
+    super.doPut(key, value);
+  }
+
+  @Override
+  protected void doAppendData(Key key, ValueDataAppender appender) throws IOException {
+    myCache.remove(key);
+    super.doAppendData(key, appender);
+  }
+
+  @Nullable
+  protected Value doGet(Key key) throws IOException {
+    Value value = myCache.get(key);
+    if (value == null) {
+      value = super.doGet(key);
+      if (value != null) {
+        myCache.put(key, value);
+      }
+    }
+    return value;
+  }
+
+  @Override
+  protected boolean doContainsMapping(Key key) throws IOException {
+    final Value value = myCache.get(key);
+    return value != null || super.doContainsMapping(key);
+  }
+
+  @Override
+  protected void doRemove(Key key) throws IOException {
+    myCache.remove(key);
+    super.doRemove(key);
+  }
+
+  @Override
+  protected void doForce() {
+    try {
+      clearCache();
+    }
+    finally {
+      super.doForce();
+    }
+  }
+
+  @Override
+  protected void doClose() throws IOException {
+    try {
+      clearCache();
+    }
+    finally {
+      super.doClose();
+    }
+  }
+
+  private void clearCache() {
+    myCache.clear();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/CachingSearcher.java b/java/compiler/impl/src/com/intellij/compiler/make/CachingSearcher.java
new file mode 100644
index 0000000..8ac89b3
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/CachingSearcher.java
@@ -0,0 +1,57 @@
+/*
+ * 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 com.intellij.compiler.make;
+
+import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pair;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.search.searches.ReferencesSearch;
+import com.intellij.util.Query;
+import com.intellij.util.containers.SoftHashMap;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Oct 15
+ * @author 2003
+ */
+public class CachingSearcher {
+  private final Project myProject;
+  private final Map<Pair<PsiElement, Boolean>, Collection<PsiReference>> myElementToReferencersMap = new SoftHashMap<Pair<PsiElement, Boolean>, Collection<PsiReference>>();
+
+  public CachingSearcher(Project project) {
+    myProject = project;
+  }
+
+  public Collection<PsiReference> findReferences(PsiElement element, final boolean ignoreAccessScope) {
+    final Pair<PsiElement, Boolean> key = new Pair<PsiElement, Boolean>(element, ignoreAccessScope? Boolean.TRUE : Boolean.FALSE);
+    Collection<PsiReference> psiReferences = myElementToReferencersMap.get(key);
+    if (psiReferences == null) {
+      GlobalSearchScope searchScope = GlobalSearchScope.projectScope(myProject);
+      searchScope = GlobalSearchScope.getScopeRestrictedByFileTypes(searchScope, StdFileTypes.JAVA);
+      final Query<PsiReference> query = ReferencesSearch.search(element, searchScope, ignoreAccessScope);
+      psiReferences = query.findAll();
+      myElementToReferencersMap.put(key, psiReferences);
+    }
+    return psiReferences;
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/ChangeDescription.java b/java/compiler/impl/src/com/intellij/compiler/make/ChangeDescription.java
new file mode 100644
index 0000000..1f5f8a3
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/ChangeDescription.java
@@ -0,0 +1,24 @@
+/*
+ * 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 com.intellij.compiler.make;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Apr 7, 2004
+ */
+abstract class ChangeDescription {
+  public abstract boolean isChanged();
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/ChangedConstantsDependencyProcessor.java b/java/compiler/impl/src/com/intellij/compiler/make/ChangedConstantsDependencyProcessor.java
new file mode 100644
index 0000000..bd919a6
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/ChangedConstantsDependencyProcessor.java
@@ -0,0 +1,373 @@
+/*
+ * 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.
+ */
+
+/**
+ * created at Feb 24, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.make;
+
+import com.intellij.compiler.classParsing.FieldInfo;
+import com.intellij.compiler.impl.ExitException;
+import com.intellij.compiler.impl.ExitStatus;
+import com.intellij.lang.StdLanguages;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.project.DumbService;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.search.*;
+import com.intellij.psi.util.PsiUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+public class ChangedConstantsDependencyProcessor {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.make.ChangedConstantsDependencyProcessor");
+  private final Project myProject;
+  private final CachingSearcher mySearcher;
+  private final DependencyCache myDependencyCache;
+  private final int myQName;
+  private final CompileContext myContext;
+  private final FieldChangeInfo[] myChangedFields;
+  private final FieldChangeInfo[] myRemovedFields;
+  private final int MAX_CONSTANT_SEARCHES = Registry.intValue("compiler.max.static.constants.searches");
+
+
+  public ChangedConstantsDependencyProcessor(Project project,
+                                             CachingSearcher searcher,
+                                             DependencyCache dependencyCache,
+                                             int qName, CompileContext context, FieldChangeInfo[] changedFields,
+                                             FieldChangeInfo[] removedFields) {
+    myProject = project;
+    mySearcher = searcher;
+    myDependencyCache = dependencyCache;
+    myQName = qName;
+    myContext = context;
+    myChangedFields = changedFields;
+    myRemovedFields = removedFields;
+  }
+
+  public void run() throws CacheCorruptedException, ExitException {
+    final Ref<CacheCorruptedException> _ex = new Ref<CacheCorruptedException>();
+    final Ref<ExitException> exitException = new Ref<ExitException>(null);
+    DumbService.getInstance(myProject).waitForSmartMode(); // ensure running in smart mode
+
+    ApplicationManager.getApplication().runReadAction(new Runnable() {
+      public void run() {
+        try {
+          final String qName = myDependencyCache.resolve(myQName);
+          PsiClass[] classes = JavaPsiFacade.getInstance(myProject).findClasses(qName.replace('$', '.'), GlobalSearchScope.allScope(myProject));
+          for (PsiClass aClass : classes) {
+            PsiField[] psiFields = aClass.getFields();
+            for (PsiField psiField : psiFields) {
+              final FieldChangeInfo changeInfo = findChangeInfo(psiField);
+              if (changeInfo != null) { // this field has been changed
+                processFieldChanged(psiField, aClass, changeInfo.isAccessibilityChange);
+              }
+            }
+            for (FieldChangeInfo removedField : myRemovedFields) {
+              processFieldRemoved(removedField.fieldInfo, aClass);
+            }
+          }
+        }
+        catch (CacheCorruptedException e) {
+          _ex.set(e);
+        }
+        catch (ExitException e) {
+          exitException.set(e);
+        }
+        catch (ProcessCanceledException e) {
+          // supressed deliberately
+        }
+      }
+    });
+    if (_ex.get() != null) {
+      throw _ex.get();
+    }
+    if (exitException.get() != null) {
+      throw exitException.get();
+    }
+  }
+
+  private void processFieldRemoved(FieldInfo info, final PsiClass aClass) throws CacheCorruptedException {
+    if (info.isPrivate()) {
+      return; // optimization: don't need to search, cause may be used only in this class
+    }
+    SearchScope searchScope = GlobalSearchScope.projectScope(myProject);
+    if (info.isPackageLocal()) {
+      final PsiFile containingFile = aClass.getContainingFile();
+      if (containingFile instanceof PsiJavaFile) {
+        final String packageName = ((PsiJavaFile)containingFile).getPackageName();
+        final PsiPackage aPackage = JavaPsiFacade.getInstance(myProject).findPackage(packageName);
+        if (aPackage != null) {
+          searchScope = PackageScope.packageScope(aPackage, false);
+          searchScope = searchScope.intersectWith(aClass.getUseScope());
+        }
+      }
+    }
+    final PsiSearchHelper psiSearchHelper = PsiSearchHelper.SERVICE.getInstance(myProject);
+
+    final Ref<CacheCorruptedException> exRef = new Ref<CacheCorruptedException>(null);
+    processIdentifiers(psiSearchHelper, new PsiElementProcessor<PsiIdentifier>() {
+      @Override
+      public synchronized boolean execute(@NotNull PsiIdentifier identifier) {
+        try {
+          final PsiElement parent = identifier.getParent();
+          if (parent instanceof PsiReferenceExpression) {
+            final PsiClass ownerClass = getOwnerClass(parent);
+            if (ownerClass != null && !ownerClass.equals(aClass)) {
+              final String _qName = ownerClass.getQualifiedName();
+              if (_qName != null) {
+                int qualifiedName = myDependencyCache.getSymbolTable().getId(_qName);
+                // should force marking of the class no matter was it compiled or not
+                // This will ensure the class was recompiled _after_ all the constants get their new values
+                if (myDependencyCache.markClass(qualifiedName, true)) {
+                  if (LOG.isDebugEnabled()) {
+                    LOG.debug("Mark dependent class " + myDependencyCache.resolve(qualifiedName) + "; reason: some constants were removed from " + myDependencyCache.resolve(myQName));
+                  }
+                }
+              }
+              else {
+                LOG.warn("Class with null qualified name was not expected here: " + ownerClass);
+              }
+            }
+          }
+          return true;
+        }
+        catch (CacheCorruptedException e) {
+          exRef.set(e);
+          return false;
+        }
+      }
+    }, myDependencyCache.resolve(info.getName()), searchScope, UsageSearchContext.IN_CODE);
+    
+    final CacheCorruptedException cacheCorruptedException = exRef.get();
+    if (cacheCorruptedException != null) {
+      throw cacheCorruptedException;
+    }
+  }
+
+  private static boolean processIdentifiers(PsiSearchHelper helper,
+                                            @NotNull final PsiElementProcessor<PsiIdentifier> processor,
+                                            @NotNull final String identifier,
+                                            @NotNull SearchScope searchScope,
+                                            short searchContext) {
+    TextOccurenceProcessor processor1 = new TextOccurenceProcessor() {
+      public boolean execute(PsiElement element, int offsetInElement) {
+        return !(element instanceof PsiIdentifier) || processor.execute((PsiIdentifier)element);
+      }
+    };
+    return helper.processElementsWithWord(processor1, searchScope, identifier, searchContext, true);
+  }
+
+  private void processFieldChanged(PsiField field, PsiClass aClass, final boolean isAccessibilityChange)
+    throws CacheCorruptedException, ExitException {
+    if (!isAccessibilityChange && field.hasModifierProperty(PsiModifier.PRIVATE)) {
+      return; // optimization: don't need to search, cause may be used only in this class
+    }
+    Set<PsiElement> usages = new HashSet<PsiElement>();
+    addUsages(field, usages, isAccessibilityChange);
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("++++++++++++++++++++++++++++++++++++++++++++++++");
+      LOG.debug("Processing changed field: " + aClass.getQualifiedName() + "." + field.getName());
+    }
+    for (final PsiElement usage : usages) {
+      PsiClass ownerClass = getOwnerClass(usage);
+      if (LOG.isDebugEnabled()) {
+        if (ownerClass != null) {
+          LOG.debug("Usage " + usage + " found in class: " + ownerClass.getQualifiedName());
+        }
+        else {
+          LOG.debug("Usage " + usage + " found in class: null");
+        }
+      }
+      if (ownerClass != null && !ownerClass.equals(aClass)) {
+        int qualifiedName = myDependencyCache.getSymbolTable().getId(ownerClass.getQualifiedName());
+        // should force marking of the class no matter was it compiled or not
+        // This will ensure the class was recompiled _after_ all the constants get their new values
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Marking class id = [" + qualifiedName + "], name=[" + myDependencyCache.resolve(qualifiedName) + "]");
+        }
+        if (myDependencyCache.markClass(qualifiedName, true)) {
+          if (LOG.isDebugEnabled()) {
+            LOG.debug("Marked dependent class " + myDependencyCache.resolve(qualifiedName) + "; reason: constants changed in " +
+                        myDependencyCache.resolve(myQName));
+          }
+        }
+      }
+      else if (ownerClass == null) {
+        final PsiFile containingFile = usage.getContainingFile();
+        if (containingFile != null) {
+          final VirtualFile file = containingFile.getVirtualFile();
+          if (file != null) {
+            myDependencyCache.markFile(file);
+          }
+        }
+      }
+    }
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("+++++++++++++++++++++++++++++++++++++++++++++++");
+    }
+  }
+
+  private void addUsages(PsiField psiField, Collection<PsiElement> usages, final boolean ignoreAccessScope) throws ExitException {
+    final int count = getConstantSearchesCount();
+    if (count > MAX_CONSTANT_SEARCHES) {
+      myContext.requestRebuildNextTime("Too many changed compile-time constants, project rebuild scheduled");
+      throw new ExitException(ExitStatus.CANCELLED);
+    }
+    Collection<PsiReference> references = mySearcher.findReferences(psiField, ignoreAccessScope)/*doFindReferences(searchHelper, psiField)*/;
+
+    incConstantSearchesCount();
+
+    for (final PsiReference ref : references) {
+      if (!(ref instanceof PsiReferenceExpression)) {
+        continue;
+      }
+      PsiElement e = ref.getElement();
+      usages.add(e);
+      PsiField ownerField = getOwnerField(e);
+      if (ownerField != null) {
+        if (ownerField.hasModifierProperty(PsiModifier.FINAL)) {
+          PsiExpression initializer = ownerField.getInitializer();
+          if (initializer != null && PsiUtil.isConstantExpression(initializer)) {
+            // if the field depends on the compile-time-constant expression and is itself final
+            addUsages(ownerField, usages, ignoreAccessScope);
+          }
+        }
+      }
+    }
+  }
+
+  /*
+  private PsiReference[] doFindReferences(final PsiSearchHelper searchHelper, final PsiField psiField) {
+    final ProgressManager progressManager = ProgressManager.getInstance();
+    final ProgressIndicator currentProgress = progressManager.getProgressIndicator();
+    final PsiReference[][] references = new PsiReference[][] {null};
+    progressManager.runProcess(new Runnable() {
+      public void run() {
+        references[0] = searchHelper.findReferences(psiField, GlobalSearchScope.projectScope(myProject), false);
+        if (ENABLE_TRACING) {
+          System.out.println("Finding referencers for " + psiField);
+        }
+      }
+    }, new NonCancellableProgressAdapter(currentProgress));
+    return references[0];
+  }
+  */
+
+  @Nullable
+  private static PsiField getOwnerField(PsiElement element) {
+    while (!(element instanceof PsiFile)) {
+      if (element instanceof PsiClass) {
+        break;
+      }
+      if (element instanceof PsiField) { // top-level class
+        return (PsiField)element;
+      }
+      element = element.getParent();
+    }
+    return null;
+  }
+
+  @Nullable
+  private FieldChangeInfo findChangeInfo(PsiField field) throws CacheCorruptedException {
+    String name = field.getName();
+    for (final FieldChangeInfo changeInfo : myChangedFields) {
+      if (name.equals(myDependencyCache.resolve(changeInfo.fieldInfo.getName()))) {
+        return changeInfo;
+      }
+    }
+    return null;
+  }
+
+  @Nullable
+  private static PsiClass getOwnerClass(PsiElement element) {
+    while (!(element instanceof PsiFile)) {
+      if (element instanceof PsiClass && element.getParent() instanceof PsiJavaFile) { // top-level class
+        final PsiClass psiClass = (PsiClass)element;
+        if (JspPsiUtil.isInJspFile(psiClass)) {
+          return null;
+        }
+        final PsiFile containingFile = psiClass.getContainingFile();
+        if (containingFile == null) {
+          return null;
+        }
+        return StdLanguages.JAVA.equals(containingFile.getLanguage())? psiClass : null;
+      }
+      element = element.getParent();
+    }
+    return null;
+  }
+
+  public static class FieldChangeInfo {
+    final FieldInfo fieldInfo;
+    final boolean isAccessibilityChange;
+
+    public FieldChangeInfo(final FieldInfo fieldId) {
+      this(fieldId, false);
+    }
+
+    public FieldChangeInfo(final FieldInfo fieldInfo, final boolean accessibilityChange) {
+      this.fieldInfo = fieldInfo;
+      isAccessibilityChange = accessibilityChange;
+    }
+
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      final FieldChangeInfo fieldChangeInfo = (FieldChangeInfo)o;
+
+      if (isAccessibilityChange != fieldChangeInfo.isAccessibilityChange) return false;
+      if (!fieldInfo.equals(fieldChangeInfo.fieldInfo)) return false;
+
+      return true;
+    }
+
+    public int hashCode() {
+      int result;
+      result = fieldInfo.hashCode();
+      result = 29 * result + (isAccessibilityChange ? 1 : 0);
+      return result;
+    }
+  }
+
+  private static final Key<Integer> CONSTANTS_COUNTER = Key.create("_constant_searches_counter_");
+
+  private int getConstantSearchesCount() {
+    return getConstantSearchesCount(myContext);
+  }
+
+  public static int getConstantSearchesCount(CompileContext context) {
+    final Integer value = CONSTANTS_COUNTER.get(context);
+    return value != null? value.intValue() : 0;
+  }
+
+  private void incConstantSearchesCount() {
+    CONSTANTS_COUNTER.set(myContext, getConstantSearchesCount() + 1);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/ChangedRetentionPolicyDependencyProcessor.java b/java/compiler/impl/src/com/intellij/compiler/make/ChangedRetentionPolicyDependencyProcessor.java
new file mode 100644
index 0000000..314fce9
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/ChangedRetentionPolicyDependencyProcessor.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 com.intellij.compiler.make;
+
+import com.intellij.compiler.SymbolTable;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.util.cls.ClsUtil;
+
+import java.util.Collection;
+
+public class ChangedRetentionPolicyDependencyProcessor {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.make.ChangedConstantsDependencyProcessor");
+  private final Project myProject;
+  private final CachingSearcher mySearcher;
+  private final DependencyCache myDependencyCache;
+
+  public ChangedRetentionPolicyDependencyProcessor(Project project, CachingSearcher searcher, DependencyCache dependencyCache) {
+    myProject = project;
+    mySearcher = searcher;
+    myDependencyCache = dependencyCache;
+  }
+
+  public void checkAnnotationRetentionPolicyChanges(final int annotationQName) throws CacheCorruptedException {
+    final Cache oldCache = myDependencyCache.getCache();
+    if (!ClsUtil.isAnnotation(oldCache.getFlags(annotationQName))) {
+      return;
+    }
+    if (!hasRetentionPolicyChanged(annotationQName, oldCache, myDependencyCache.getNewClassesCache(), myDependencyCache.getSymbolTable())) {
+      return;
+    }
+    final CacheCorruptedException[] _ex = new CacheCorruptedException[] {null};
+    ApplicationManager.getApplication().runReadAction(new Runnable() {
+      public void run() {
+        try {
+          final String qName = myDependencyCache.resolve(annotationQName);
+          PsiClass[] classes = JavaPsiFacade.getInstance(myProject).findClasses(qName.replace('$', '.'), GlobalSearchScope.allScope(myProject));
+          for (final PsiClass aClass : classes) {
+            if (!aClass.isAnnotationType()) {
+              continue;
+            }
+            final Collection<PsiReference> references = mySearcher.findReferences(aClass, true);
+            for (PsiReference reference : references) {
+              final PsiClass ownerClass = getOwnerClass(reference.getElement());
+              if (ownerClass != null && !ownerClass.equals(aClass)) {
+                int qualifiedName = myDependencyCache.getSymbolTable().getId(ownerClass.getQualifiedName());
+                if (myDependencyCache.markClass(qualifiedName, false)) {
+                  if (LOG.isDebugEnabled()) {
+                    LOG.debug("Marked dependent class " + myDependencyCache.resolve(qualifiedName) +
+                              "; reason: annotation's retention policy changed from SOURCE to CLASS or RUNTIME " +
+                              myDependencyCache.resolve(annotationQName));
+                  }
+                }
+              }
+            }
+          }
+        }
+        catch (CacheCorruptedException e) {
+         _ex[0] = e;
+        }
+        catch (ProcessCanceledException e) {
+          // supressed deliberately
+        }
+      }
+    });
+    if (_ex[0] != null) {
+      throw _ex[0];
+    }
+  }
+
+  private boolean hasRetentionPolicyChanged(int annotationQName, final Cache oldCache, final Cache newCache, SymbolTable symbolTable) throws CacheCorruptedException {
+    // if retention policy changed from SOURCE to CLASS or RUNTIME, all sources should be recompiled to propagate changes
+    final int oldPolicy = MakeUtil.getAnnotationRetentionPolicy(annotationQName, oldCache, symbolTable);
+    final int newPolicy = MakeUtil.getAnnotationRetentionPolicy(annotationQName, newCache, symbolTable);
+    if ((oldPolicy == RetentionPolicies.SOURCE) && (newPolicy == RetentionPolicies.CLASS || newPolicy == RetentionPolicies.RUNTIME)) {
+      return true;
+    }
+    return false;
+  }
+
+  private static PsiClass getOwnerClass(PsiElement element) {
+    while (!(element instanceof PsiFile)) {
+      if (element instanceof PsiClass && element.getParent() instanceof PsiJavaFile) { // top-level class
+        return (PsiClass)element;
+      }
+      element = element.getParent();
+    }
+    return null;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/ClassInfoProcessor.java b/java/compiler/impl/src/com/intellij/compiler/make/ClassInfoProcessor.java
new file mode 100644
index 0000000..5298a8c
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/ClassInfoProcessor.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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: May 14, 2003
+ * Time: 10:42:29 AM
+ */
+package com.intellij.compiler.make;
+
+
+
+public interface ClassInfoProcessor {
+  /**
+   * @param classQName of a class info to be processed
+   * @return true if superclasses of info should be processed and false otherwise
+   */
+  boolean process(int classQName) throws CacheCorruptedException;
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/CompilerDependencyStorage.java b/java/compiler/impl/src/com/intellij/compiler/make/CompilerDependencyStorage.java
new file mode 100644
index 0000000..ea93f21
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/CompilerDependencyStorage.java
@@ -0,0 +1,257 @@
+/*
+ * 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 com.intellij.compiler.make;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Ref;
+import com.intellij.util.containers.SLRUCache;
+import com.intellij.util.io.DataExternalizer;
+import com.intellij.util.io.KeyDescriptor;
+import com.intellij.util.io.PersistentHashMap;
+import gnu.trove.TIntHashSet;
+import gnu.trove.TIntProcedure;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.*;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Dec 1, 2008
+ */
+public class CompilerDependencyStorage<Key> implements Flushable, Disposable {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.make.CompilerDependencyStorage");
+  protected final PersistentHashMap<Key, int[]> myMap;
+  protected final SLRUCache<Key, IntSet> myCache;
+  private Key myKeyToRemove;
+
+  public CompilerDependencyStorage(File file, KeyDescriptor<Key> keyDescriptor, final int cacheSize) throws IOException {
+    myMap = new PersistentHashMap<Key, int[]>(file, keyDescriptor, new DataExternalizer<int[]>() {
+      public void save(DataOutput out, int[] array) throws IOException {
+        out.writeInt(array.length);
+        for (int value : array) {
+          out.writeInt(value);
+        }
+      }
+
+      public int[] read(DataInput in) throws IOException {
+        final TIntHashSet set = new TIntHashSet();
+        DataInputStream stream = (DataInputStream)in;
+        while(stream.available() > 0) {
+          final int size = stream.readInt();
+          final int _size = Math.abs(size);
+          for (int idx = 0; idx < _size; idx++) {
+            if (size > 0) {
+              set.add(stream.readInt());
+            }
+            else {
+              set.remove(stream.readInt());
+            }
+          }
+        }
+        return set.toArray();
+      }
+    });
+
+    myCache = new SLRUCache<Key, IntSet>(cacheSize * 2, cacheSize) {
+      @NotNull
+      public IntSet createValue(Key key) {
+        return new IntSet(key);
+      }
+
+      protected void onDropFromCache(Key key, final IntSet set) {
+        if (key == myKeyToRemove || !set.isDirty()) {
+          return;
+        }
+        try {
+          if (set.needsCompacting()) {
+            myMap.put(key, set.getValues());
+          }
+          else {
+            myMap.appendData(key, new PersistentHashMap.ValueDataAppender() {
+              public void append(final DataOutput out) throws IOException {
+                final Ref<IOException> exception = new Ref<IOException>(null);
+                final TIntProcedure saveProc = new TIntProcedure() {
+                  public boolean execute(int value) {
+                    try {
+                      out.writeInt(value);
+                      return true;
+                    }
+                    catch (IOException e) {
+                      exception.set(e);
+                      return false;
+                    }
+                  }
+                };
+
+                out.writeInt(-set.getRemovedCount());
+                set.processRemovedValues(saveProc);
+                if (exception.get() != null) {
+                  throw exception.get();
+                }
+
+                out.writeInt(set.getAddedCount());
+                set.processAddedValues(saveProc);
+                if (exception.get() != null) {
+                  throw exception.get();
+                }
+              }
+            });
+          }
+        }
+        catch (IOException e) {
+          LOG.error(e);
+        }
+      }
+    };
+  }
+
+  public synchronized void remove(Key key) throws IOException {
+    myKeyToRemove = key;
+    try {
+      myCache.remove(key);
+    }
+    finally {
+      myKeyToRemove = null;
+    }
+    myMap.remove(key);
+  }
+
+  public synchronized void removeValue(Key key, int value) throws IOException {
+    final IntSet set = myCache.get(key);
+    set.remove(value);
+    if (set.needsFlushing()) {
+      flush(key);
+    }
+  }
+
+
+  public synchronized void addValue(Key key, int value) throws IOException {
+    final IntSet set = myCache.get(key);
+    set.add(value);
+    if (set.needsFlushing()) {
+      flush(key);
+    }
+  }
+
+  public synchronized int[] getValues(Key key) throws IOException {
+    return myCache.get(key).getValues();
+  }
+
+
+  public synchronized void flush() throws IOException {
+    myCache.clear();
+    myMap.force();
+  }
+
+  private void flush(Key key) {
+    myCache.remove(key); // makes changes into PersistentHashMap
+    myMap.force(); // flushes internal caches (which consume memory) and writes unsaved data to disk
+  }
+
+  public synchronized void dispose() {
+    try {
+      flush();
+    }
+    catch (IOException e) {
+      LOG.info(e);
+    }
+    try {
+      myMap.close();
+    }
+    catch (IOException e) {
+      LOG.info(e);
+    }
+  }
+
+  private class IntSet {
+    private final TIntHashSet myAdded = new TIntHashSet();
+    private final TIntHashSet myRemoved = new TIntHashSet();
+    private TIntHashSet myMerged = null;
+    private final Key myKey;
+
+    public IntSet(Key key) {
+      myKey = key;
+    }
+
+    public void add(int value) {
+      if (myMerged != null) {
+        myMerged.add(value);
+      }
+      if (!myRemoved.remove(value)) {
+        myAdded.add(value);
+      }
+    }
+
+    public void remove(int value) {
+      if (myMerged != null) {
+        myMerged.remove(value);
+      }
+      if (!myAdded.remove(value)) {
+        myRemoved.add(value);
+      }
+    }
+
+    public boolean isDirty() {
+      return myAdded.size() > 0 || myRemoved.size() > 0;
+    }
+
+    public boolean needsCompacting() {
+      return myMerged != null;
+    }
+
+    public boolean needsFlushing() {
+      return myAdded.size() > 3000 || myRemoved.size() > 3000;
+    }
+
+    public int getAddedCount() {
+      return myAdded.size();
+    }
+
+    public void processAddedValues(final TIntProcedure procedure) {
+      myAdded.forEach(procedure);
+    }
+
+    public int getRemovedCount() {
+      return myRemoved.size();
+    }
+
+    public void processRemovedValues(final TIntProcedure procedure) {
+      myRemoved.forEach(procedure);
+    }
+
+    public int[] getValues() throws IOException {
+      return getMerged().toArray();
+    }
+
+    private TIntHashSet getMerged() throws IOException {
+      if (myMerged == null) {
+        myMerged = new TIntHashSet();
+        final int[] fromDisk = myMap.get(myKey);
+        if (fromDisk != null) {
+          myMerged.addAll(fromDisk);
+        }
+        if (myRemoved.size() > 0) {
+          myMerged.removeAll(myRemoved.toArray());
+        }
+        if (myAdded.size() > 0) {
+          myMerged.addAll(myAdded.toArray());
+        }
+      }
+      return myMerged;
+    }
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/Dependency.java b/java/compiler/impl/src/com/intellij/compiler/make/Dependency.java
new file mode 100644
index 0000000..bd2c5cf
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/Dependency.java
@@ -0,0 +1,188 @@
+/*
+ * 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.
+ */
+
+/**
+ * created at Jan 4, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.make;
+
+import com.intellij.compiler.SymbolTable;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.ArrayUtil;
+import org.jetbrains.annotations.NonNls;
+
+import java.util.*;
+
+public class Dependency {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.make.Dependency");
+  public static final Dependency[] EMPTY_ARRAY = new Dependency[0];
+  private final int myClassQualifiedName;
+  private Set<FieldRef> myUsedFields;
+  private Set<MethodRef> myUsedMethods;
+
+  public static class FieldRef {
+    public final int name;
+
+    public FieldRef(int name) {
+      this.name = name;
+    }
+
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      FieldRef fieldRef = (FieldRef)o;
+
+      if (name != fieldRef.name) return false;
+
+      return true;
+    }
+
+    public int hashCode() {
+      return name;
+    }
+  }
+
+  public static class MethodRef {
+    public final int name;
+    public final int descriptor;
+    private String[] myParameterDescriptors;
+
+    public MethodRef(int name, int descriptor) {
+      this.name = name;
+      this.descriptor = descriptor;
+    }
+
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      MethodRef methodRef = (MethodRef)o;
+
+      if (descriptor != methodRef.descriptor) return false;
+      if (name != methodRef.name) return false;
+
+      return true;
+    }
+
+    public int hashCode() {
+      int result = name;
+      result = 31 * result + descriptor;
+      return result;
+    }
+
+    public String getDescriptor(SymbolTable symbolTable) throws CacheCorruptedException {
+      final String descriptorStr = symbolTable.getSymbol(descriptor);
+      final String nameStr = symbolTable.getSymbol(name);
+      return CacheUtils.getMethodSignature(nameStr, descriptorStr);
+    }
+
+
+    public String[] getParameterDescriptors(SymbolTable symbolTable) throws CacheCorruptedException {
+      if (myParameterDescriptors == null) {
+        String descriptorStr = symbolTable.getSymbol(descriptor);
+        int endIndex = descriptorStr.indexOf(')');
+        if (endIndex <= 0) {
+          LOG.error("Corrupted method descriptor: " + descriptorStr);
+        }
+        myParameterDescriptors = parseParameterDescriptors(descriptorStr.substring(1, endIndex));
+      }
+      return myParameterDescriptors;
+    }
+  }
+
+  public Dependency(int classQualifiedName) {
+    myClassQualifiedName = classQualifiedName;
+  }
+
+  public int getClassQualifiedName() {
+    return myClassQualifiedName;
+  }
+
+  public void addMethod(int name, int descriptor) {
+    if (myUsedMethods == null) {
+      myUsedMethods = new HashSet<MethodRef>();
+    }
+    myUsedMethods.add(new MethodRef(name, descriptor));
+  }
+
+  public void addField(int name) {
+    if (myUsedFields == null) {
+      myUsedFields = new HashSet<FieldRef>();
+    }
+    myUsedFields.add(new FieldRef(name));
+  }
+
+  public Collection<FieldRef> getFieldRefs() {
+    return myUsedFields != null? Collections.unmodifiableSet(myUsedFields) : Collections.<FieldRef>emptySet();
+  }
+
+  public Collection<MethodRef> getMethodRefs() {
+    return myUsedMethods != null? Collections.unmodifiableSet(myUsedMethods) : Collections.<MethodRef>emptySet();
+  }
+
+  private static String[] parseParameterDescriptors(String signature) {
+    ArrayList<String> list = new ArrayList<String>();
+    String paramSignature = parseFieldType(signature);
+    while (paramSignature != null && !paramSignature.isEmpty()) {
+      list.add(paramSignature);
+      signature = signature.substring(paramSignature.length());
+      paramSignature = parseFieldType(signature);
+    }
+    return ArrayUtil.toStringArray(list);
+  }
+
+  private static String parseFieldType(@NonNls String signature) {
+    if (signature.isEmpty()) {
+      return null;
+    }
+    char first = signature.charAt(0);
+    if (first == 'I') {
+      return "I";
+    }
+    if (first == 'L') {
+      return signature.substring(0, signature.indexOf(';') + 1);
+    }
+    if (first == 'B') {
+      return "B";
+    }
+    if (first == 'C') {
+      return "C";
+    }
+    if (first == 'D') {
+      return "D";
+    }
+    if (first == 'F') {
+      return "F";
+    }
+    if (first == 'J') {
+      return "J";
+    }
+    if (first == 'S') {
+      return "S";
+    }
+    if (first == 'Z') {
+      return "Z";
+    }
+    if (first == '[') {
+      String s = parseFieldType(signature.substring(1));
+      return s == null ? null : "[" + s;
+    }
+    return null;
+  }
+
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/DependencyCache.java b/java/compiler/impl/src/com/intellij/compiler/make/DependencyCache.java
new file mode 100644
index 0000000..92f3285
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/DependencyCache.java
@@ -0,0 +1,659 @@
+/*
+ * 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.
+ */
+
+/**
+ * created at Jan 7, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.make;
+
+import com.intellij.compiler.DependencyProcessor;
+import com.intellij.compiler.SymbolTable;
+import com.intellij.compiler.classParsing.*;
+import com.intellij.compiler.impl.ExitException;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.cls.ClsFormatException;
+import com.intellij.util.cls.ClsUtil;
+import gnu.trove.TIntHashSet;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.rmi.Remote;
+import java.util.*;
+
+public class DependencyCache {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.make.DependencyCache");
+
+  private volatile Cache myCache;
+  private volatile Cache myNewClassesCache;
+
+  private static final String REMOTE_INTERFACE_NAME = Remote.class.getName();
+  private final TIntHashSet myToUpdate = new TIntHashSet(); // qName strings to be updated.
+  private final TIntHashSet myTraverseRoots = new TIntHashSet(); // Dependencies are calculated from these clasess
+  private final TIntHashSet myClassesWithSourceRemoved = new TIntHashSet();
+  private final TIntHashSet myPreviouslyRemoteClasses = new TIntHashSet(); // classes that were Remote, but became non-Remote for some reason
+  private final TIntHashSet myMarkedInfos = new TIntHashSet(); // classes to be recompiled
+  private final Set<VirtualFile> myMarkedFiles = new HashSet<VirtualFile>();
+  
+  private volatile DependencyCacheNavigator myCacheNavigator;
+  private volatile SymbolTable mySymbolTable;
+  private final String mySymbolTableFilePath;
+  private final String myStoreDirectoryPath;
+  @NonNls 
+  private static final String SYMBOLTABLE_FILE_NAME = "symboltable.dat";
+
+  public DependencyCache(@NonNls String storeDirectoryPath) {
+    myStoreDirectoryPath = storeDirectoryPath;
+    LOG.assertTrue(myStoreDirectoryPath != null);
+
+    mySymbolTableFilePath = myStoreDirectoryPath + "/" + SYMBOLTABLE_FILE_NAME;
+  }
+
+  public DependencyCacheNavigator getCacheNavigator() throws CacheCorruptedException {
+    if (myCacheNavigator == null) {
+      myCacheNavigator = new DependencyCacheNavigator(getCache());
+    }
+    return myCacheNavigator;
+  }
+
+  public void wipe() throws CacheCorruptedException {
+    getCache().wipe();
+    getNewClassesCache().wipe();
+  }
+
+  public Cache getCache() throws CacheCorruptedException {
+    try {
+      if (myCache == null) {
+        // base number of cached record views of each type
+        myCache = new Cache(myStoreDirectoryPath, 512);
+      }
+
+      return myCache;
+    }
+    catch (IOException e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public Cache getNewClassesCache() throws CacheCorruptedException {
+    try {
+      if (myNewClassesCache == null) {
+        myNewClassesCache = new Cache(myStoreDirectoryPath + "/tmp", 16);
+      }
+      return myNewClassesCache;
+    }
+    catch (IOException e) {
+      throw new CacheCorruptedException(e);
+    }
+  }
+
+  public void addTraverseRoot(int qName) {
+    myTraverseRoots.add(qName);
+  }
+
+  public void clearTraverseRoots() {
+    myTraverseRoots.clear();
+  }
+
+  public boolean hasUnprocessedTraverseRoots() {
+    return !myTraverseRoots.isEmpty();
+  }
+
+  public void markSourceRemoved(int qName) {
+    myClassesWithSourceRemoved.add(qName);
+  }
+
+  public void addClassToUpdate(int qName) {
+    myToUpdate.add(qName);
+  }
+
+  public int reparseClassFile(@NotNull File file, final byte[] fileContent) throws ClsFormatException, CacheCorruptedException {
+    SymbolTable symbolTable = getSymbolTable();
+
+    final int qName = getNewClassesCache().importClassInfo(new ClassFileReader(file, symbolTable, fileContent), symbolTable);
+    addClassToUpdate(qName);
+    addTraverseRoot(qName);
+    return qName;
+  }
+
+  // for profiling purposes
+  /*
+  private static void pause() {
+    System.out.println("PAUSED. ENTER A CHAR.");
+    byte[] buf = new byte[1];
+    try {
+      System.in.read(buf);
+    }
+    catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+  */
+
+  public void update() throws CacheCorruptedException {
+    if (myToUpdate.isEmpty()) {
+      return; // optimization
+    }
+
+    //pause();
+
+    final int[] namesToUpdate = myToUpdate.toArray();
+    final Cache cache = getCache();
+    final Cache newCache = getNewClassesCache();
+    final DependencyCacheNavigator navigator = getCacheNavigator();
+
+    // remove unnecesary dependencies
+    for (final int qName : namesToUpdate) {
+      // process use-dependencies
+      for (int referencedClassQName : cache.getReferencedClasses(qName)) {
+        if (!cache.containsClass(referencedClassQName)) {
+          continue;
+        }
+        cache.removeClassReferencer(referencedClassQName, qName);
+      }
+      cache.clearReferencedClasses(qName);
+      // process inheritance dependencies
+      navigator.walkSuperClasses(qName, new ClassInfoProcessor() {
+        public boolean process(int classQName) throws CacheCorruptedException {
+          cache.removeSubclass(classQName, qName);
+          return true;
+        }
+      });
+    }
+
+    // do update of classInfos
+    for (final int qName : namesToUpdate) {
+      cache.importClassInfo(newCache, qName);
+    }
+
+    // build forward-dependencies for the new infos, all new class infos must be already in the main cache!
+
+    final SymbolTable symbolTable = getSymbolTable();
+
+    for (final int qName : namesToUpdate) {
+      if (!newCache.containsClass(qName)) {
+        continue;
+      }
+      buildForwardDependencies(qName, newCache.getReferences(qName));
+      boolean isRemote = false;
+      // "remote objects" are classes that _directly_ implement remote interfaces
+      final int[] superInterfaces = cache.getSuperInterfaces(qName);
+      if (superInterfaces.length > 0) {
+        final int remoteInterfaceName = symbolTable.getId(REMOTE_INTERFACE_NAME);
+        for (int superInterface : superInterfaces) {
+          if (isRemoteInterface(cache, superInterface, remoteInterfaceName)) {
+            isRemote = true;
+            break;
+          }
+        }
+      }
+      final boolean wasRemote = cache.isRemote(qName);
+      if (wasRemote && !isRemote) {
+        synchronized (myPreviouslyRemoteClasses) {
+          myPreviouslyRemoteClasses.add(qName);
+        }
+      }
+      cache.setRemote(qName, isRemote);
+    }
+
+    // building subclass dependencies
+    for (final int qName : namesToUpdate) {
+      buildSubclassDependencies(getCache(), qName, qName);
+    }
+
+    for (final int qName : myClassesWithSourceRemoved.toArray()) {
+      cache.removeClass(qName);
+    }
+    myToUpdate.clear();
+
+    //pause();
+  }
+
+  private void buildForwardDependencies(final int classQName, final Collection<ReferenceInfo> references) throws CacheCorruptedException {
+    final Cache cache = getCache();
+
+    final int genericSignature = cache.getGenericSignature(classQName);
+    if (genericSignature != -1) {
+      final String genericClassSignature = resolve(genericSignature);
+      final int[] bounds = findBounds(genericClassSignature);
+      for (int boundClassQName : bounds) {
+        cache.addClassReferencer(boundClassQName, classQName);
+      }
+    }
+
+    buildAnnotationDependencies(classQName, cache.getRuntimeVisibleAnnotations(classQName));
+    buildAnnotationDependencies(classQName, cache.getRuntimeInvisibleAnnotations(classQName));
+
+    for (final ReferenceInfo refInfo : references) {
+      final int declaringClassName = getActualDeclaringClassForReference(refInfo);
+      if (declaringClassName == Cache.UNKNOWN) {
+        continue;
+      }
+      if (refInfo instanceof MemberReferenceInfo) {
+        final MemberInfo memberInfo = ((MemberReferenceInfo)refInfo).getMemberInfo();
+        if (memberInfo instanceof FieldInfo) {
+          cache.addFieldReferencer(declaringClassName, memberInfo.getName(), classQName);
+        }
+        else if (memberInfo instanceof MethodInfo) {
+          cache.addMethodReferencer(declaringClassName, memberInfo.getName(), memberInfo.getDescriptor(), classQName);
+        }
+        else {
+          LOG.error("Unknown member info class: " + memberInfo.getClass().getName());
+        }
+      }
+      else { // reference to class
+        cache.addClassReferencer(declaringClassName, classQName);
+      }
+    }
+    final SymbolTable symbolTable = getSymbolTable();
+
+    for (final FieldInfo fieldInfo : cache.getFields(classQName)) {
+      buildAnnotationDependencies(classQName, fieldInfo.getRuntimeVisibleAnnotations());
+      buildAnnotationDependencies(classQName, fieldInfo.getRuntimeInvisibleAnnotations());
+
+      String className = MakeUtil.parseObjectType(symbolTable.getSymbol(fieldInfo.getDescriptor()), 0);
+      if (className == null) {
+        continue;
+      }
+      final int cls = symbolTable.getId(className);
+      cache.addClassReferencer(cls, classQName);
+    }
+
+    for (final MethodInfo methodInfo : cache.getMethods(classQName)) {
+      buildAnnotationDependencies(classQName, methodInfo.getRuntimeVisibleAnnotations());
+      buildAnnotationDependencies(classQName, methodInfo.getRuntimeInvisibleAnnotations());
+      buildAnnotationDependencies(classQName, methodInfo.getRuntimeVisibleParameterAnnotations());
+      buildAnnotationDependencies(classQName, methodInfo.getRuntimeInvisibleParameterAnnotations());
+
+      if (methodInfo.isConstructor()) {
+        continue;
+      }
+
+      final String returnTypeClassName = MakeUtil.parseObjectType(methodInfo.getReturnTypeDescriptor(symbolTable), 0);
+      if (returnTypeClassName != null) {
+        final int returnTypeClassQName = symbolTable.getId(returnTypeClassName);
+        cache.addClassReferencer(returnTypeClassQName, classQName);
+      }
+
+      String[] parameterSignatures = CacheUtils.getParameterSignatures(methodInfo, symbolTable);
+      for (String parameterSignature : parameterSignatures) {
+        String paramClassName = MakeUtil.parseObjectType(parameterSignature, 0);
+        if (paramClassName != null) {
+          final int paramClassId = symbolTable.getId(paramClassName);
+          cache.addClassReferencer(paramClassId, classQName);
+        }
+      }
+    }
+  }
+
+  private static boolean isRemoteInterface(Cache cache, int ifaceName, final int remoteInterfaceName) throws CacheCorruptedException {
+    if (ifaceName == remoteInterfaceName) {
+      return true;
+    }
+    for (int superInterfaceName : cache.getSuperInterfaces(ifaceName)) {
+      if (isRemoteInterface(cache, superInterfaceName, remoteInterfaceName)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+
+  private void buildAnnotationDependencies(int classQName, AnnotationConstantValue[][] annotations) throws CacheCorruptedException {
+    if (annotations == null || annotations.length == 0) {
+      return;
+    }
+    for (AnnotationConstantValue[] annotation : annotations) {
+      buildAnnotationDependencies(classQName, annotation);
+    }
+  }
+
+  private void buildAnnotationDependencies(int classQName, AnnotationConstantValue[] annotations) throws CacheCorruptedException {
+    if (annotations == null || annotations.length == 0) {
+      return;
+    }
+    final Cache cache = getCache();
+    for (AnnotationConstantValue annotation : annotations) {
+      final int annotationQName = annotation.getAnnotationQName();
+
+      cache.addClassReferencer(annotationQName, classQName);
+
+      final AnnotationNameValuePair[] memberValues = annotation.getMemberValues();
+      for (final AnnotationNameValuePair nameValuePair : memberValues) {
+        for (MethodInfo annotationMember : cache.findMethodsByName(annotationQName, nameValuePair.getName())) {
+          cache.addMethodReferencer(annotationQName, annotationMember.getName(), annotationMember.getDescriptor(), classQName);
+        }
+      }
+    }
+  }
+
+  private int[] findBounds(final String genericClassSignature) throws CacheCorruptedException{
+    try {
+      final String[] boundInterfaces = BoundsParser.getBounds(genericClassSignature);
+      int[] ids = ArrayUtil.newIntArray(boundInterfaces.length);
+      for (int i = 0; i < boundInterfaces.length; i++) {
+        ids[i] = getSymbolTable().getId(boundInterfaces[i]);
+      }
+      return ids;
+    }
+    catch (SignatureParsingException e) {
+      return ArrayUtil.EMPTY_INT_ARRAY;
+    }
+  }
+
+  // fixes JDK 1.4 javac bug that generates references in the constant pool
+  // to the subclass even if the field was declared in a superclass
+  private int getActualDeclaringClassForReference(final ReferenceInfo refInfo) throws CacheCorruptedException {
+    if (!(refInfo instanceof MemberReferenceInfo)) {
+      return refInfo.getClassName();
+    }
+    final int declaringClassName = refInfo.getClassName();
+    final Cache cache = getCache();
+    final MemberInfo memberInfo = ((MemberReferenceInfo)refInfo).getMemberInfo();
+    if (memberInfo instanceof FieldInfo) {
+      if (cache.findFieldByName(declaringClassName, memberInfo.getName()) != null) {
+        return declaringClassName;
+      }
+    }
+    else if (memberInfo instanceof MethodInfo) {
+      if (cache.findMethod(declaringClassName, memberInfo.getName(), memberInfo.getDescriptor()) != null) {
+        return declaringClassName;
+      }
+    }
+    final DeclaringClassFinder finder = new DeclaringClassFinder(memberInfo);
+    getCacheNavigator().walkSuperClasses(declaringClassName, finder);
+    return finder.getDeclaringClassName();
+  }
+
+  /**
+   * @return qualified names of the classes that should be additionally recompiled
+   */
+  public Pair<int[], Set<VirtualFile>> findDependentClasses(CompileContext context, Project project, Set<VirtualFile> compiledWithErrors)
+    throws CacheCorruptedException, ExitException {
+
+    markDependencies(context, project, compiledWithErrors);
+    return new Pair<int[], Set<VirtualFile>>(myMarkedInfos.toArray(), Collections.unmodifiableSet(myMarkedFiles));
+  }
+
+  private void markDependencies(CompileContext context, Project project, final Set<VirtualFile> compiledWithErrors)
+    throws CacheCorruptedException, ExitException {
+    try {
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("====================Marking dependent files=====================");
+      }
+      // myToUpdate can be modified during the mark procedure, so use toArray() to iterate it
+      final int[] traverseRoots = myTraverseRoots.toArray();
+      final SourceFileFinder sourceFileFinder = new SourceFileFinder(project, context);
+      final CachingSearcher searcher = new CachingSearcher(project);
+      final ChangedRetentionPolicyDependencyProcessor changedRetentionPolicyDependencyProcessor = new ChangedRetentionPolicyDependencyProcessor(project, searcher, this);
+      for (final int qName : traverseRoots) {
+        if (!getCache().containsClass(qName)) {
+          continue;
+        }
+        if (getNewClassesCache().containsClass(qName)) { // there is a new class file created
+          new JavaDependencyProcessor(project, this, qName).run();
+          ArrayList<ChangedConstantsDependencyProcessor.FieldChangeInfo> changed =
+            new ArrayList<ChangedConstantsDependencyProcessor.FieldChangeInfo>();
+          ArrayList<ChangedConstantsDependencyProcessor.FieldChangeInfo> removed =
+            new ArrayList<ChangedConstantsDependencyProcessor.FieldChangeInfo>();
+          findModifiedConstants(qName, changed, removed);
+          if (!changed.isEmpty() || !removed.isEmpty()) {
+            new ChangedConstantsDependencyProcessor(
+              project, searcher, this, qName, context,
+              changed.toArray(new ChangedConstantsDependencyProcessor.FieldChangeInfo[changed.size()]),
+              removed.toArray(new ChangedConstantsDependencyProcessor.FieldChangeInfo[removed.size()])
+            ).run();
+          }
+          changedRetentionPolicyDependencyProcessor.checkAnnotationRetentionPolicyChanges(qName);
+          for (DependencyProcessor additionalProcessor : DependencyProcessor.EXTENSION_POINT_NAME.getExtensions()) {
+            additionalProcessor.processDependencies(context, qName, searcher);
+          }
+        }
+        else {
+          boolean isSourceDeleted = false;
+          if (myClassesWithSourceRemoved.contains(qName)) { // no recompiled class file, check whether the classfile exists
+            isSourceDeleted = true;
+          }
+          else if (!new File(getCache().getPath(qName)).exists()) {
+            final String qualifiedName = resolve(qName);
+            final String sourceFileName = getCache().getSourceFileName(qName);
+            final boolean markAsRemovedSource = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
+              public Boolean compute() {
+                VirtualFile sourceFile = sourceFileFinder.findSourceFile(qualifiedName, sourceFileName, false);
+                return sourceFile == null || !compiledWithErrors.contains(sourceFile) ? Boolean.TRUE : Boolean.FALSE;
+              }
+            }).booleanValue();
+            if (markAsRemovedSource) {
+              // for Inner classes: sourceFile may exist, but the inner class declaration inside it may not,
+              // thus the source for the class info should be considered removed
+              isSourceDeleted = true;
+              markSourceRemoved(qName);
+              myMarkedInfos.remove(qName); // if the info has been marked already, the mark should be removed
+            }
+          }
+          if (isSourceDeleted) {
+            Dependency[] backDependencies = getCache().getBackDependencies(qName);
+            for (Dependency backDependency : backDependencies) {
+              if (markTargetClassInfo(backDependency)) {
+                if (LOG.isDebugEnabled()) {
+                  LOG.debug(
+                    "Mark dependent class " + backDependency.getClassQualifiedName() + "; reason: no class file found for " + qName);
+                }
+              }
+            }
+          }
+        }
+      }
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("================================================================");
+      }
+    }
+    catch (ProcessCanceledException ignored) {
+      // deliberately suppressed
+    }
+  }
+
+  private void findModifiedConstants(
+    final int qName,
+    Collection<ChangedConstantsDependencyProcessor.FieldChangeInfo> changedConstants,
+    Collection<ChangedConstantsDependencyProcessor.FieldChangeInfo> removedConstants) throws CacheCorruptedException {
+
+    final Cache cache = getCache();
+    for (final FieldInfo field : cache.getFields(qName)) {
+      final int oldFlags = field.getFlags();
+      if (ClsUtil.isStatic(oldFlags) && ClsUtil.isFinal(oldFlags)) {
+        final Cache newClassesCache = getNewClassesCache();
+        FieldInfo newField = newClassesCache.findFieldByName(qName, field.getName());
+        if (newField == null) {
+          if (!ConstantValue.EMPTY_CONSTANT_VALUE.equals(field.getConstantValue())) {
+            // if the field was really compile time constant
+            removedConstants.add(new ChangedConstantsDependencyProcessor.FieldChangeInfo(field));
+          }
+        }
+        else {
+          final boolean visibilityRestricted = MakeUtil.isMoreAccessible(oldFlags, newField.getFlags());
+          if (!field.getConstantValue().equals(newField.getConstantValue()) || visibilityRestricted) {
+            changedConstants.add(new ChangedConstantsDependencyProcessor.FieldChangeInfo(field, visibilityRestricted));
+          }
+        }
+      }
+    }
+  }
+
+  private static void buildSubclassDependencies(Cache cache, final int qName, int targetClassId) throws CacheCorruptedException {
+    final int superQName = cache.getSuperQualifiedName(targetClassId);
+    if (superQName != Cache.UNKNOWN) {
+      cache.addSubclass(superQName, qName);
+      buildSubclassDependencies(cache, qName, superQName);
+    }
+
+    int[] interfaces = cache.getSuperInterfaces(targetClassId);
+    for (final int interfaceName : interfaces) {
+      cache.addSubclass(interfaceName, qName);
+      buildSubclassDependencies(cache, qName, interfaceName);
+    }
+  }
+
+
+  /**
+   * Marks ClassInfo targeted by the dependency
+   * @return true if really added, false otherwise
+   */
+  public boolean markTargetClassInfo(Dependency dependency) throws CacheCorruptedException {
+    return markClassInfo(dependency.getClassQualifiedName(), false);
+  }
+
+  /**
+   * Marks ClassInfo that corresponds to the specified qualified name
+   * If class info is already recompiled, it is not marked
+   * @return true if really added, false otherwise
+   */
+  public boolean markClass(int qualifiedName) throws CacheCorruptedException {
+    return markClass(qualifiedName, false);
+  }
+
+  /**
+   * Marks ClassInfo that corresponds to the specified qualified name
+   * If class info is already recompiled, it is not marked unless force parameter is true
+   * @return true if really added, false otherwise
+   */
+  public boolean markClass(int qualifiedName, boolean force) throws CacheCorruptedException {
+    return markClassInfo(qualifiedName, force);
+  }
+
+  public boolean isTargetClassInfoMarked(Dependency dependency) {
+    return isClassInfoMarked(dependency.getClassQualifiedName());
+  }
+
+  public boolean isClassInfoMarked(int qName) {
+    return myMarkedInfos.contains(qName);
+  }
+  
+  public void markFile(VirtualFile file) {
+    myMarkedFiles.add(file);
+  }
+
+  /**
+   * @return true if really marked, false otherwise
+   */
+  private boolean markClassInfo(int qName, boolean force) throws CacheCorruptedException {
+    if (!getCache().containsClass(qName)) {
+      return false;
+    }
+    if (myClassesWithSourceRemoved.contains(qName)) {
+      return false; // no need to recompile since source has been removed
+    }
+    if (!force) {
+      if (getNewClassesCache().containsClass(qName)) { // already recompiled
+        return false;
+      }
+    }
+    return myMarkedInfos.add(qName);
+  }
+
+  public void resetState() {
+    myClassesWithSourceRemoved.clear();
+    myMarkedFiles.clear();
+    myMarkedInfos.clear();
+    myToUpdate.clear();
+    myTraverseRoots.clear();
+    if (myNewClassesCache != null) {
+      myNewClassesCache.wipe();
+      myNewClassesCache = null;
+    }
+    myCacheNavigator = null;
+    try {
+      if (myCache != null) {
+        myCache.dispose();
+        myCache = null;
+      }
+    }
+    catch (CacheCorruptedException e) {
+      LOG.info(e);
+    }
+    try {
+      if (mySymbolTable != null) {
+        mySymbolTable.dispose();
+        mySymbolTable = null;
+      }
+    }
+    catch (CacheCorruptedException e) {
+      LOG.info(e);
+    }
+  }
+
+
+  public SymbolTable getSymbolTable() throws CacheCorruptedException {
+    if (mySymbolTable == null) {
+      mySymbolTable = new SymbolTable(new File(mySymbolTableFilePath));
+    }
+    return mySymbolTable;
+  }
+
+  public String resolve(int id) throws CacheCorruptedException {
+    return getSymbolTable().getSymbol(id);
+  }
+
+  public boolean wasRemote(int qName) {
+    return myPreviouslyRemoteClasses.contains(qName);
+  }
+
+  private class DeclaringClassFinder implements ClassInfoProcessor {
+    private final int myMemberName;
+    private final int myMemberDescriptor;
+    private int myDeclaringClass = Cache.UNKNOWN;
+    private final boolean myIsField;
+
+    private DeclaringClassFinder(MemberInfo memberInfo) {
+      myMemberName = memberInfo.getName();
+      myMemberDescriptor = memberInfo.getDescriptor();
+      myIsField = memberInfo instanceof FieldInfo;
+    }
+
+    public int getDeclaringClassName() {
+      return myDeclaringClass;
+    }
+
+    public boolean process(int classQName) throws CacheCorruptedException {
+      final Cache cache = getCache();
+      if (myIsField) {
+        final FieldInfo fieldId = cache.findField(classQName, myMemberName, myMemberDescriptor);
+        if (fieldId != null) {
+          myDeclaringClass = classQName;
+          return false;
+        }
+      }
+      else {
+        final MethodInfo methodId = cache.findMethod(classQName, myMemberName, myMemberDescriptor);
+        if (methodId != null) {
+          myDeclaringClass = classQName;
+          return false;
+        }
+      }
+      return true;
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/DependencyCacheNavigator.java b/java/compiler/impl/src/com/intellij/compiler/make/DependencyCacheNavigator.java
new file mode 100644
index 0000000..a93caa2
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/DependencyCacheNavigator.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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: May 26, 2003
+ * Time: 8:13:56 PM
+ */
+package com.intellij.compiler.make;
+
+import com.intellij.openapi.diagnostic.Logger;
+
+public class DependencyCacheNavigator {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.make.DependencyCacheNavigator");
+
+  private final Cache myCache;
+
+  public DependencyCacheNavigator(Cache cache) {
+    myCache = cache;
+  }
+
+  public void walkSuperClasses(int classQName, ClassInfoProcessor processor) throws CacheCorruptedException {
+    if (classQName == Cache.UNKNOWN) {
+      return;
+    }
+    int superQName = myCache.getSuperQualifiedName(classQName);
+
+    if (classQName == superQName) {
+      LOG.error("Superclass qualified name is the same as class' name: " + classQName);
+      return;
+    }
+
+    if (superQName != Cache.UNKNOWN) {
+      if (processor.process(superQName)) {
+        walkSuperClasses(superQName, processor);
+      }
+    }
+    for (int superInterfaceQName : myCache.getSuperInterfaces(classQName)) {
+      if (processor.process(superInterfaceQName)) {
+        walkSuperClasses(superInterfaceQName, processor);
+      }
+    }
+  }
+
+  public void walkSuperInterfaces(int classQName, ClassInfoProcessor processor) throws CacheCorruptedException {
+    if (classQName == Cache.UNKNOWN) {
+      return;
+    }
+
+    for (int superInterfaceQName : myCache.getSuperInterfaces(classQName)) {
+      if (processor.process(superInterfaceQName)) {
+        walkSuperInterfaces(superInterfaceQName, processor);
+      }
+    }
+  }
+
+  public void walkSubClasses(int fromClassQName, ClassInfoProcessor processor) throws CacheCorruptedException {
+    for (int subQName : myCache.getSubclasses(fromClassQName)) {
+      if (fromClassQName == subQName) {
+        LOG.error("Subclass qualified name is the same as class' name: " + fromClassQName);
+        return;
+      }
+      if (subQName != Cache.UNKNOWN) {
+        if (!processor.process(subQName)) {
+          break;
+        }
+      }
+    }
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/FieldChangeDescription.java b/java/compiler/impl/src/com/intellij/compiler/make/FieldChangeDescription.java
new file mode 100644
index 0000000..9d0ef47
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/FieldChangeDescription.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 com.intellij.compiler.make;
+
+import com.intellij.compiler.classParsing.FieldInfo;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Apr 7, 2004
+ */
+class FieldChangeDescription extends ChangeDescription {
+  public final boolean flagsChanged;
+  public final boolean descriptorChanged;
+  public final boolean genericSignatureChanged;
+
+  public FieldChangeDescription(final FieldInfo oldField, final FieldInfo newField) {
+    descriptorChanged = oldField.getDescriptor() != newField.getDescriptor();
+    flagsChanged = oldField.getFlags() != newField.getFlags();
+    genericSignatureChanged = oldField.getGenericSignature() != newField.getGenericSignature();
+  }
+
+  public boolean isChanged() {
+    return flagsChanged || descriptorChanged || genericSignatureChanged;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/JavaDependencyProcessor.java b/java/compiler/impl/src/com/intellij/compiler/make/JavaDependencyProcessor.java
new file mode 100644
index 0000000..b00d886
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/JavaDependencyProcessor.java
@@ -0,0 +1,1322 @@
+/*
+ * 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.
+ */
+
+/**
+ * created at Feb 19, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.make;
+
+import com.intellij.compiler.SymbolTable;
+import com.intellij.compiler.classParsing.*;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.util.IncorrectOperationException;
+import com.intellij.util.cls.ClsUtil;
+import gnu.trove.TIntHashSet;
+import gnu.trove.TIntObjectHashMap;
+import gnu.trove.TIntObjectIterator;
+import gnu.trove.TIntObjectProcedure;
+import org.jetbrains.annotations.NonNls;
+
+import java.util.*;
+
+class JavaDependencyProcessor {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.make.JavaDependencyProcessor");
+  private final DependencyCache myDependencyCache;
+  private final int myQName;
+  private final Map<Dependency.MethodRef, MethodInfo> myRefToMethodMap = new HashMap<Dependency.MethodRef, MethodInfo>();
+  private final Map<Dependency.FieldRef, FieldInfo> myRefToFieldMap = new HashMap<Dependency.FieldRef, FieldInfo>();
+  private final Set<MemberInfo> myAddedMembers = new HashSet<MemberInfo>();
+  private final Set<MemberInfo> myRemovedMembers = new HashSet<MemberInfo>();
+  private final Set<MemberInfo> myChangedMembers = new HashSet<MemberInfo>();
+  private final Map<MemberInfo, ChangeDescription> myChangeDescriptions = new HashMap<MemberInfo, ChangeDescription>();
+  private Dependency[] myBackDependencies;
+  private final boolean myMembersChanged;
+  private final boolean mySuperInterfaceAdded;
+  private final boolean mySuperInterfaceRemoved;
+  private final boolean mySuperClassChanged;
+  private final boolean mySuperlistGenericSignatureChanged;
+  private final boolean mySuperClassAdded;
+  private final Project myProject;
+  private final boolean myIsAnnotation;
+  private final boolean myIsRemoteInterface;
+  private final boolean myWereAnnotationTargetsRemoved;
+  private final boolean myRetentionPolicyChanged;
+  private final boolean myAnnotationSemanticsChanged;
+
+  public JavaDependencyProcessor(Project project, DependencyCache dependencyCache, int qName) throws CacheCorruptedException {
+    myProject = project;
+    myDependencyCache = dependencyCache;
+    myQName = qName;
+    final Cache cache = dependencyCache.getCache();
+    final Cache newClassesCache = dependencyCache.getNewClassesCache();
+
+    final MethodInfo[] oldMethods = cache.getMethods(qName);
+    for (MethodInfo method : oldMethods) {
+      myRefToMethodMap.put(new Dependency.MethodRef(method.getName(), method.getDescriptor()), method);
+    }
+    final TIntObjectHashMap<FieldInfo> oldFieldsMap = getFieldInfos(cache, qName);
+    oldFieldsMap.forEachEntry(new TIntObjectProcedure<FieldInfo>() {
+      public boolean execute(int fieldName, FieldInfo fieldInfo) {
+        myRefToFieldMap.put(new Dependency.FieldRef(fieldName), fieldInfo);
+        return true;
+      }
+    });
+    final Map<String, MethodInfoContainer> oldMethodsMap = getMethodInfos(oldMethods);
+    final Map<String, MethodInfoContainer> newMethodsMap = getMethodInfos(newClassesCache.getMethods(qName));
+    final TIntObjectHashMap<FieldInfo> newFieldsMap = getFieldInfos(newClassesCache, qName);
+    addAddedMembers(oldFieldsMap, oldMethodsMap, newFieldsMap, newMethodsMap, myAddedMembers);
+    addRemovedMembers(oldFieldsMap, oldMethodsMap, newFieldsMap, newMethodsMap, myRemovedMembers);
+    addChangedMembers(oldFieldsMap, oldMethodsMap, newFieldsMap, newMethodsMap, myChangedMembers);
+
+    myMembersChanged = !myAddedMembers.isEmpty() || !myRemovedMembers.isEmpty() || !myChangedMembers.isEmpty();
+    // track changes in super list
+
+    myIsRemoteInterface = MakeUtil.isInterface(cache.getFlags(myQName)) && cache.isRemote(qName);
+    myIsAnnotation = ClsUtil.isAnnotation(cache.getFlags(qName));
+    myWereAnnotationTargetsRemoved = myIsAnnotation && wereAnnotationTargesRemoved(cache, newClassesCache);
+    myRetentionPolicyChanged = myIsAnnotation && hasRetentionPolicyChanged(cache, newClassesCache);
+    myAnnotationSemanticsChanged = myIsAnnotation && hasAnnotationSemanticsChanged(cache, newClassesCache);
+
+    int[] oldInterfaces = cache.getSuperInterfaces(qName);
+    int[] newInterfaces = newClassesCache.getSuperInterfaces(qName);
+    mySuperInterfaceRemoved = wereInterfacesRemoved(oldInterfaces, newInterfaces);
+    mySuperInterfaceAdded = wereInterfacesRemoved(newInterfaces, oldInterfaces);
+
+    mySuperlistGenericSignatureChanged = isSuperlistGenericSignatureChanged(cache.getGenericSignature(qName), newClassesCache.getGenericSignature(qName));
+
+    boolean superclassesDiffer = cache.getSuperQualifiedName(qName) != newClassesCache.getSuperQualifiedName(qName);
+    boolean wasDerivedFromObject = CommonClassNames.JAVA_LANG_OBJECT.equals(dependencyCache.resolve(cache.getSuperQualifiedName(qName)));
+    mySuperClassChanged = !wasDerivedFromObject && superclassesDiffer;
+    mySuperClassAdded = wasDerivedFromObject && superclassesDiffer;
+  }
+
+  private static boolean hasMembersWithoutDefaults(Set<MemberInfo> addedMembers) {
+    for (final Object addedMember : addedMembers) {
+      MemberInfo memberInfo = (MemberInfo)addedMember;
+      if (memberInfo instanceof MethodInfo) {
+        final ConstantValue annotationDefault = ((MethodInfo)memberInfo).getAnnotationDefault();
+        if (annotationDefault == null || ConstantValue.EMPTY_CONSTANT_VALUE.equals(annotationDefault)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  private boolean wereAnnotationDefaultsRemoved() {
+    for (final MemberInfo memberInfo : myChangeDescriptions.keySet()) {
+      if (memberInfo instanceof MethodInfo) {
+        MethodChangeDescription description = (MethodChangeDescription)myChangeDescriptions.get(memberInfo);
+        if (description.removedAnnotationDefault) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  private boolean isSuperlistGenericSignatureChanged(int oldGenericSignature, int newGenericSignature) throws CacheCorruptedException {
+    if (oldGenericSignature == newGenericSignature) {
+      return false;
+    }
+    if (oldGenericSignature != -1 && newGenericSignature != -1) {
+      final SymbolTable symbolTable = myDependencyCache.getSymbolTable();
+      final String _oldGenericMethodSignature = cutFormalParams(symbolTable.getSymbol(oldGenericSignature));
+      final String _newGenericMethodSignature = cutFormalParams(symbolTable.getSymbol(newGenericSignature));
+      return !_oldGenericMethodSignature.equals(_newGenericMethodSignature);
+    }
+    return true;
+  }
+
+  private static String cutFormalParams(String genericClassSignature) {
+    if (genericClassSignature.charAt(0) == '<') {
+      int idx = genericClassSignature.indexOf('>');
+      return genericClassSignature.substring(idx + 1);
+    }
+    return genericClassSignature;
+  }
+
+  public void run() throws CacheCorruptedException {
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Checking dependencies for " + myDependencyCache.resolve(myQName));
+    }
+    final boolean superListChanged = mySuperClassChanged || mySuperClassAdded || mySuperInterfaceAdded || mySuperInterfaceRemoved || mySuperlistGenericSignatureChanged;
+    final Cache oldCache = myDependencyCache.getCache();
+    final Cache newCache = myDependencyCache.getNewClassesCache();
+
+    if (!myMembersChanged &&
+        oldCache.getFlags(myQName) == newCache.getFlags(myQName) &&
+        !superListChanged && !myWereAnnotationTargetsRemoved && !myRetentionPolicyChanged && !myAnnotationSemanticsChanged) {
+      return; // nothing to do
+    }
+
+    if (myIsAnnotation) {
+      if (myAnnotationSemanticsChanged) {
+        final TIntHashSet visited = new TIntHashSet();
+        visited.add(myQName);
+        markAnnotationDependenciesRecursively(getBackDependencies(), LOG.isDebugEnabled()? "; reason: semantics changed for " + myDependencyCache.resolve(myQName) : "", visited);
+        return;
+      }
+      if (hasMembersWithoutDefaults(myAddedMembers)) {
+        markAll(getBackDependencies(), LOG.isDebugEnabled()? "; reason: added annotation type member without default " + myDependencyCache.resolve(myQName) : "");
+        return;
+      }
+      if (!myRemovedMembers.isEmpty()) {
+        markAll(getBackDependencies(), LOG.isDebugEnabled()? "; reason: removed annotation type member " + myDependencyCache.resolve(myQName) : "");
+        return;
+      }
+      if (!myChangedMembers.isEmpty()) { // for annotations "changed" means return type changed
+        markAll(getBackDependencies(), LOG.isDebugEnabled()? "; reason: changed annotation member's type " + myDependencyCache.resolve(myQName) : "");
+        return;
+      }
+      if (wereAnnotationDefaultsRemoved()) {
+        markAll(getBackDependencies(), LOG.isDebugEnabled()? "; reason: removed annotation member's default value " + myDependencyCache.resolve(myQName): "");
+        return;
+      }
+      if (myWereAnnotationTargetsRemoved) {
+        markAll(getBackDependencies(), LOG.isDebugEnabled()? "; reason: removed annotation's targets " + myDependencyCache.resolve(myQName) : "");
+        return;
+      }
+      if (myRetentionPolicyChanged) {
+        markAll(getBackDependencies(), LOG.isDebugEnabled()? "; reason: retention policy changed for " + myDependencyCache.resolve(myQName) : "");
+        return;
+      }
+    }
+
+    final DependencyCacheNavigator cacheNavigator = myDependencyCache.getCacheNavigator();
+
+    if (mySuperClassChanged || mySuperInterfaceRemoved || mySuperlistGenericSignatureChanged) {
+      // superclass changed == old removed and possibly new added
+      // if anything (class or interface) in the superlist was removed, should recompile all subclasses (both direct and indirect)
+      // and all back-dependencies of this class and its subclasses
+      markAll(getBackDependencies(), LOG.isDebugEnabled()? "; reason: deleted items from the superlist or changed superlist generic signature of " + myDependencyCache.resolve(myQName) : "");
+      cacheNavigator.walkSubClasses(myQName, new ClassInfoProcessor() {
+        public boolean process(int classQName) throws CacheCorruptedException {
+          markAll(oldCache.getBackDependencies(classQName), LOG.isDebugEnabled()? "; reason: deleted items from the superlist or changed superlist generic signature of " + myDependencyCache.resolve(myQName) : "");
+          return true;
+        }
+      });
+      return;
+    }
+
+    final boolean isKindChanged =
+      (MakeUtil.isInterface(oldCache.getFlags(myQName)) && !MakeUtil.isInterface(newCache.getFlags(myQName))) ||
+      (!MakeUtil.isInterface(oldCache.getFlags(myQName)) && MakeUtil.isInterface(newCache.getFlags(myQName)));
+    if (isKindChanged) {
+      markAll(getBackDependencies(), LOG.isDebugEnabled()? "; reason: class kind changed (class/interface) " + myDependencyCache.resolve(myQName) : "");
+      cacheNavigator.walkSubClasses(myQName, new ClassInfoProcessor() {
+        public boolean process(int classQName) throws CacheCorruptedException {
+          markAll(oldCache.getBackDependencies(classQName), LOG.isDebugEnabled()? "; reason: class kind changed (class/interface) " + myDependencyCache.resolve(myQName) : "");
+          return true;
+        }
+      });
+      return;
+    }
+
+    boolean becameFinal = !ClsUtil.isFinal(oldCache.getFlags(myQName)) && ClsUtil.isFinal(newCache.getFlags(myQName));
+    if (becameFinal) {
+      markAll(getBackDependencies(), LOG.isDebugEnabled()? "; reason: class became final: " + myDependencyCache.resolve(myQName) : "");
+    }
+    else {
+      boolean becameAbstract = !ClsUtil.isAbstract(oldCache.getFlags(myQName)) && ClsUtil.isAbstract(newCache.getFlags(myQName));
+      boolean accessRestricted = MakeUtil.isMoreAccessible(oldCache.getFlags(myQName), newCache.getFlags(myQName));
+      Set<MethodInfo> removedMethods = null;
+      Set<MethodInfo> addedMethods = null;
+      for (Dependency backDependency : getBackDependencies()) {
+        if (myDependencyCache.isTargetClassInfoMarked(backDependency)) continue;
+
+        if (accessRestricted) {
+          if (myDependencyCache.markTargetClassInfo(backDependency)) {
+            if (LOG.isDebugEnabled()) {
+              LOG.debug("Mark dependent class " + myDependencyCache.resolve(backDependency.getClassQualifiedName()) + "; reason: " +
+                        myDependencyCache.resolve(myQName) + " made less accessible");
+            }
+          }
+          continue;
+        }
+        if (becameAbstract) {
+          if (processClassBecameAbstract(backDependency)) {
+            continue;
+          }
+        }
+        if (isDependentOnRemovedMembers(backDependency)) {
+          if (myDependencyCache.markTargetClassInfo(backDependency)) {
+            if (LOG.isDebugEnabled()) {
+              LOG.debug("Mark dependent class " + myDependencyCache.resolve(backDependency.getClassQualifiedName()) +
+                        "; reason: the class uses removed members of " + myDependencyCache.resolve(myQName));
+            }
+          }
+          continue;
+        }
+        if (isDependentOnChangedMembers(backDependency)) {
+          if (myDependencyCache.markTargetClassInfo(backDependency)) {
+            if (LOG.isDebugEnabled()) {
+              LOG.debug("Mark dependent class " + myDependencyCache.resolve(backDependency.getClassQualifiedName()) +
+                        "; reason: the class uses changed members of " + myDependencyCache.resolve(myQName));
+            }
+          }
+          continue;
+        }
+        final Collection<Dependency.MethodRef> usedMethods = backDependency.getMethodRefs();
+        if (removedMethods == null) {
+          removedMethods = extractMethods(myRemovedMembers, true);
+        }
+        if (isDependentOnEquivalentMethods(usedMethods, removedMethods)) {
+          if (myDependencyCache.markTargetClassInfo(backDependency)) {
+            if (LOG.isDebugEnabled()) {
+              LOG.debug("Mark dependent class " + myDependencyCache.resolve(backDependency.getClassQualifiedName()) +
+                        "; reason: some overloaded methods of " + myDependencyCache.resolve(myQName) + " were removed");
+            }
+          }
+          continue;
+        }
+        if (addedMethods == null) {
+          addedMethods = extractMethods(myAddedMembers, true);
+        }
+        if (isDependentOnEquivalentMethods(usedMethods, addedMethods)) {
+          if (myDependencyCache.markTargetClassInfo(backDependency)) {
+            if (LOG.isDebugEnabled()) {
+              LOG.debug("Mark dependent class " + myDependencyCache.resolve(backDependency.getClassQualifiedName()) +
+                        "; reason: some overloaded methods of " + myDependencyCache.resolve(myQName) + " were added");
+            }
+          }
+        }
+      }
+    }
+
+    final Set<MethodInfo> methodsToCheck = new HashSet<MethodInfo>();
+    extractMethods(myRemovedMembers, methodsToCheck, false);
+    
+    processInheritanceDependencies(methodsToCheck);
+
+    extractMethods(myAddedMembers, methodsToCheck, false);
+
+    if (!MakeUtil.isAnonymous(myDependencyCache.resolve(myQName))) {
+      // these checks make no sense for anonymous classes
+      
+      final TIntHashSet fieldNames = new TIntHashSet();
+      extractFieldNames(myAddedMembers, fieldNames);
+      int addedFieldsCount = fieldNames.size();
+      extractFieldNames(myRemovedMembers, fieldNames);
+      
+      if (!fieldNames.isEmpty()) {
+        cacheNavigator.walkSuperClasses(myQName, new ClassInfoProcessor() {
+          public boolean process(final int classQName) throws CacheCorruptedException {
+            markUseDependenciesOnFields(classQName, fieldNames);
+            return true;
+          }
+        });
+      }
+      
+      if (addedFieldsCount > 0 && MakeUtil.isInterface(oldCache.getFlags(myQName))) {
+        final TIntHashSet visitedClasses = new TIntHashSet();
+        visitedClasses.add(myQName);
+        cacheNavigator.walkSubClasses(myQName, new ClassInfoProcessor() {
+          public boolean process(int subclassQName) throws CacheCorruptedException {
+            markUseDependenciesOnFields(subclassQName, fieldNames);
+            visitedClasses.add(subclassQName);
+            cacheNavigator.walkSuperClasses(subclassQName, new ClassInfoProcessor() {
+              public boolean process(int superclassQName) throws CacheCorruptedException {
+                if (visitedClasses.contains(superclassQName)) {
+                  return false;
+                }
+                markUseDependenciesOnFields(superclassQName, fieldNames);
+                visitedClasses.add(superclassQName);
+                return true;
+              }
+            });
+            return true;
+          }
+        });
+      }
+
+      if (!methodsToCheck.isEmpty()) {
+        cacheNavigator.walkSuperClasses(myQName, new ClassInfoProcessor() {
+          public boolean process(int classQName) throws CacheCorruptedException {
+            markUseDependenciesOnEquivalentMethods(classQName, methodsToCheck, myQName);
+            return true;
+          }
+        });
+
+        cacheNavigator.walkSubClasses(myQName, new ClassInfoProcessor() {
+          public boolean process(int classQName) throws CacheCorruptedException {
+            markUseDependenciesOnEquivalentMethods(classQName, methodsToCheck, myQName);
+            return true;
+          }
+        });
+      }
+      // check referencing members in subclasses
+      
+      final TIntHashSet addedOrRemovedFields = new TIntHashSet();
+      final TIntHashSet addedOrRemovedMethods = new TIntHashSet();
+      for (Set<MemberInfo> infos : Arrays.asList(myAddedMembers, myRemovedMembers)) {
+        for (MemberInfo member : infos) {
+          if (!member.isPrivate()) {
+            if (member instanceof FieldInfo) {
+              addedOrRemovedFields.add(member.getName());
+            }
+            else if (member instanceof MethodInfo){
+              addedOrRemovedMethods.add(member.getName());
+            }
+          }
+        }
+        
+      }
+      if (!addedOrRemovedFields.isEmpty() || !addedOrRemovedMethods.isEmpty()) {
+        cacheNavigator.walkSubClasses(myQName, new ClassInfoProcessor() {
+          public boolean process(final int subclassQName) throws CacheCorruptedException {
+            if (!myDependencyCache.isClassInfoMarked(subclassQName)) {
+              if (referencesMembersWithNames(oldCache, subclassQName, addedOrRemovedFields, addedOrRemovedMethods)) {
+                final boolean marked = myDependencyCache.markClass(subclassQName);
+                if (marked && LOG.isDebugEnabled()) {
+                  LOG.debug("Mark dependent class " + myDependencyCache.resolve(subclassQName) + "; Reason: members were added/removed in superclass with names, that may clash with the names of members of another classes that this class references");
+                }
+              }
+            }
+            return true;
+          }
+        });
+      }
+    }
+  }
+  
+  private static boolean referencesMembersWithNames(Cache cache, final int qName, TIntHashSet fieldNames, TIntHashSet methodNames) throws CacheCorruptedException {
+    for (final int referencedClass : cache.getReferencedClasses(qName)) {
+      for (Dependency dependency : cache.getBackDependencies(referencedClass)) {
+        if (dependency.getClassQualifiedName() == qName) {
+          for (Dependency.FieldRef ref : dependency.getFieldRefs()) {
+            if (fieldNames.contains(ref.name)) {
+              return true;
+            }
+          }
+          for (Dependency.MethodRef ref : dependency.getMethodRefs()) {
+            if (methodNames.contains(ref.name)) {
+              return true;
+            }
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  private void markAnnotationDependenciesRecursively(final Dependency[] dependencies, final @NonNls String reason, final TIntHashSet visitedAnnotations)
+      throws CacheCorruptedException {
+    final Cache oldCache = myDependencyCache.getCache();
+    for (Dependency dependency : dependencies) {
+      if (myDependencyCache.markTargetClassInfo(dependency)) {
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Mark dependent class " + myDependencyCache.resolve(dependency.getClassQualifiedName()) + reason);
+        }
+      }
+      final int depQName = dependency.getClassQualifiedName();
+      if (ClsUtil.isAnnotation(oldCache.getFlags(depQName))) {
+        if (!visitedAnnotations.contains(depQName)) {
+          visitedAnnotations.add(depQName);
+          markAnnotationDependenciesRecursively(oldCache.getBackDependencies(depQName), LOG.isDebugEnabled()? "; reason: cascade semantics change for " + myDependencyCache.resolve(depQName) : "", visitedAnnotations);
+        }
+      }
+    }
+  }
+
+  private static final int[] ALL_TARGETS = {
+    AnnotationTargets.ANNOTATION_TYPE,
+    AnnotationTargets.CONSTRUCTOR,
+    AnnotationTargets.FIELD,
+    AnnotationTargets.LOCAL_VARIABLE,
+    AnnotationTargets.METHOD,
+    AnnotationTargets.PACKAGE,
+    AnnotationTargets.PARAMETER,
+    AnnotationTargets.TYPE
+  };
+  private boolean wereAnnotationTargesRemoved(final Cache oldCache, final Cache newCache) throws CacheCorruptedException {
+    final int oldAnnotationTargets = MakeUtil.getAnnotationTargets(oldCache, myQName, myDependencyCache.getSymbolTable());
+    final int newAnnotationTargets = MakeUtil.getAnnotationTargets(newCache, myQName, myDependencyCache.getSymbolTable());
+    if (oldAnnotationTargets == newAnnotationTargets) {
+      return false;
+    }
+    for (final int target : ALL_TARGETS) {
+      if ((oldAnnotationTargets & target) != 0 && (newAnnotationTargets & target) == 0) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private boolean hasRetentionPolicyChanged(final Cache oldCache, final Cache newCache) throws CacheCorruptedException {
+    // if retention policy changed from SOURCE to CLASS or RUNTIME, all sources should be recompiled to propagate changes
+    final int oldPolicy = MakeUtil.getAnnotationRetentionPolicy(myQName, oldCache, myDependencyCache.getSymbolTable());
+    final int newPolicy = MakeUtil.getAnnotationRetentionPolicy(myQName, newCache, myDependencyCache.getSymbolTable());
+    if (oldPolicy == RetentionPolicies.SOURCE && (newPolicy == RetentionPolicies.CLASS || newPolicy == RetentionPolicies.RUNTIME)) {
+      return true;
+    }
+    return oldPolicy == RetentionPolicies.CLASS && newPolicy == RetentionPolicies.RUNTIME;
+  }
+
+  private boolean hasAnnotationSemanticsChanged(final Cache oldCache, final Cache newCache) throws CacheCorruptedException {
+    final TIntObjectHashMap<AnnotationConstantValue> oldAnnotations = fetchAllAnnotations(oldCache);
+    final TIntObjectHashMap<AnnotationConstantValue> newAnnotations = fetchAllAnnotations(newCache);
+    // filter certain known annotation which are processed separately
+    final int retentionAnnotation = myDependencyCache.getSymbolTable().getId("java.lang.annotation.Retention");
+    final int targetAnnotation = myDependencyCache.getSymbolTable().getId("java.lang.annotation.Target");
+    oldAnnotations.remove(retentionAnnotation);
+    oldAnnotations.remove(targetAnnotation);
+    newAnnotations.remove(retentionAnnotation);
+    newAnnotations.remove(targetAnnotation);
+
+    if (oldAnnotations.size() != newAnnotations.size()) {
+      return true; // number of annotation has changed
+    }
+    for (int annotName : oldAnnotations.keys()) {
+      if (!newAnnotations.contains(annotName)) {
+        return true;
+      }
+      final AnnotationNameValuePair[] oldValues = oldAnnotations.get(annotName).getMemberValues();
+      final AnnotationNameValuePair[] newValues = newAnnotations.get(annotName).getMemberValues();
+      if (annotationValuesDiffer(oldValues, newValues)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private boolean annotationValuesDiffer(final AnnotationNameValuePair[] oldValues, final AnnotationNameValuePair[] newValues) {
+    if (oldValues.length != newValues.length) {
+      return true;
+    }
+    final TIntObjectHashMap<ConstantValue> names = new TIntObjectHashMap<ConstantValue>();
+    for (AnnotationNameValuePair value : oldValues) {
+      names.put(value.getName(), value.getValue());
+    }
+    for (AnnotationNameValuePair value : newValues) {
+      if (!names.containsKey(value.getName())) {
+        return true;
+      }
+      if (!value.getValue().equals(names.get(value.getName()))) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+
+  private TIntObjectHashMap<AnnotationConstantValue> fetchAllAnnotations(final Cache cache) throws CacheCorruptedException {
+    final int classId = myQName;
+    TIntObjectHashMap<AnnotationConstantValue> oldAnnotations = new TIntObjectHashMap<AnnotationConstantValue>();
+    for (AnnotationConstantValue annot : cache.getRuntimeVisibleAnnotations(classId)) {
+      oldAnnotations.put(annot.getAnnotationQName(), annot);
+    }
+    for (AnnotationConstantValue annot : cache.getRuntimeInvisibleAnnotations(classId)) {
+      oldAnnotations.put(annot.getAnnotationQName(), annot);
+    }
+    return oldAnnotations;
+  }
+
+  private void markAll(Dependency[] backDependencies, @NonNls String reason) throws CacheCorruptedException {
+    for (Dependency backDependency : backDependencies) {
+      if (myDependencyCache.markTargetClassInfo(backDependency)) {
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Mark dependent class " + myDependencyCache.resolve(backDependency.getClassQualifiedName()) + reason);
+        }
+      }
+    }
+  }
+
+  private static void extractFieldNames(Collection<MemberInfo> fromCollection, TIntHashSet toCollection) {
+    for (final Object aFromCollection : fromCollection) {
+      MemberInfo memberInfo = (MemberInfo)aFromCollection;
+      if (memberInfo instanceof FieldInfo) {
+        toCollection.add(memberInfo.getName());
+      }
+    }
+  }
+
+  private static Set<MethodInfo> extractMethods(Collection<MemberInfo> fromCollection, boolean includeConstructors) {
+    final Set<MethodInfo> methods = new HashSet<MethodInfo>();
+    extractMethods(fromCollection, methods, includeConstructors);
+    return methods;
+  }
+  
+  private static void extractMethods(Collection<MemberInfo> fromCollection, Collection<MethodInfo> toCollection, boolean includeConstructors) {
+    for (final MemberInfo memberInfo : fromCollection) {
+      if (memberInfo instanceof MethodInfo) {
+        final MethodInfo methodInfo = (MethodInfo)memberInfo;
+        if (includeConstructors) {
+          toCollection.add(methodInfo);
+        }
+        else {
+          if (!methodInfo.isConstructor()) {
+            toCollection.add(methodInfo);
+          }
+        }
+      }
+    }
+  }
+
+  private boolean processClassBecameAbstract(Dependency dependency) throws CacheCorruptedException {
+    for (Dependency.MethodRef ref : dependency.getMethodRefs()) {
+      final MethodInfo usedMethod = myRefToMethodMap.get(ref);
+      if (usedMethod == null) {
+        continue;
+      }
+      if (usedMethod.isConstructor()) {
+        if (myDependencyCache.markTargetClassInfo(dependency)) {
+          if (LOG.isDebugEnabled()) {
+            LOG.debug("Mark dependent class " + myDependencyCache.resolve(dependency.getClassQualifiedName()) + "; reason: " +
+                      myDependencyCache.resolve(myQName) + " made abstract");
+          }
+        }
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private boolean isDependentOnRemovedMembers(Dependency dependency) {
+    for (Dependency.MethodRef ref : dependency.getMethodRefs()) {
+      if (myRemovedMembers.contains(myRefToMethodMap.get(ref))) {
+        return true;
+      }
+    }
+    for (Dependency.FieldRef ref : dependency.getFieldRefs()) {
+      if (myRemovedMembers.contains(myRefToFieldMap.get(ref))) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private boolean isDependentOnChangedMembers(Dependency dependency) {
+    for (Dependency.FieldRef ref : dependency.getFieldRefs()) {
+      final FieldInfo fieldInfo = myRefToFieldMap.get(ref);
+      if (myChangedMembers.contains(fieldInfo)) {
+        return true;
+      }
+    }
+
+    for (Dependency.MethodRef ref : dependency.getMethodRefs()) {
+      final MethodInfo methodInfo = myRefToMethodMap.get(ref);
+      if (myChangedMembers.contains(methodInfo)) {
+        final MethodChangeDescription changeDescription = (MethodChangeDescription)myChangeDescriptions.get(methodInfo);
+        if (changeDescription.returnTypeDescriptorChanged ||
+            changeDescription.returnTypeGenericSignatureChanged ||
+            changeDescription.paramsGenericSignatureChanged ||
+            changeDescription.throwsListChanged ||
+            changeDescription.staticPropertyChanged ||
+            changeDescription.accessRestricted) {
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  private boolean isDependentOnEquivalentMethods(Collection<Dependency.MethodRef> checkedMembers, Set<MethodInfo> members) throws CacheCorruptedException {
+    // check if 'members' contains method with the same name and the same numbers of parameters, but with different types
+    if (checkedMembers.isEmpty() || members.isEmpty()) {
+      return false; // optimization
+    }
+    for (Dependency.MethodRef checkedMethod : checkedMembers) {
+      if (hasEquivalentMethod(members, checkedMethod)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private void markUseDependenciesOnEquivalentMethods(final int checkedInfoQName, Set<MethodInfo> methodsToCheck, int methodsClassName) throws CacheCorruptedException {
+    final Dependency[] backDependencies = myDependencyCache.getCache().getBackDependencies(checkedInfoQName);
+    for (Dependency dependency : backDependencies) {
+      if (myDependencyCache.isTargetClassInfoMarked(dependency)) {
+        continue;
+      }
+      if (isDependentOnEquivalentMethods(dependency.getMethodRefs(), methodsToCheck)) {
+        if (myDependencyCache.markTargetClassInfo(dependency)) {
+          if (LOG.isDebugEnabled()) {
+            LOG.debug("Mark dependent class " + myDependencyCache.resolve(dependency.getClassQualifiedName()) +
+                      "; reason: more specific methods added to " + myDependencyCache.resolve(methodsClassName));
+          }
+        }
+        myDependencyCache.addClassToUpdate(checkedInfoQName);
+      }
+    }
+  }
+
+  private void markUseDependenciesOnFields(final int classQName, TIntHashSet fieldNames) throws CacheCorruptedException {
+    final Cache oldCache = myDependencyCache.getCache();
+    for (Dependency useDependency : oldCache.getBackDependencies(classQName)) {
+      if (!myDependencyCache.isTargetClassInfoMarked(useDependency)) {
+        for (Dependency.FieldRef field : useDependency.getFieldRefs()) {
+          if (fieldNames.contains(field.name)) {
+            if (myDependencyCache.markTargetClassInfo(useDependency)) {
+              if (LOG.isDebugEnabled()) {
+                LOG.debug("Mark dependent class " + myDependencyCache.resolve(useDependency.getClassQualifiedName()) +
+                          "; reason: conflicting fields were added to the hierarchy of the class " + myDependencyCache.resolve(classQName));
+              }
+            }
+            myDependencyCache.addClassToUpdate(classQName);
+            break; // stop iterating fields
+          }
+        }
+      }
+    }
+  }
+  
+  private void processInheritanceDependencies(final Set<MethodInfo> removedMethods) throws CacheCorruptedException {
+    final Cache oldCache = myDependencyCache.getCache();
+    final Cache newCache = myDependencyCache.getNewClassesCache();
+
+    final boolean becameFinal = !ClsUtil.isFinal(oldCache.getFlags(myQName)) && ClsUtil.isFinal(newCache.getFlags(myQName));
+    final SymbolTable symbolTable = myDependencyCache.getSymbolTable();
+
+    final Set<MemberInfo> removedConcreteMethods = fetchNonAbstractMethods(myRemovedMembers);
+    final Set<MethodInfo> removedOverridableMethods;
+    if (!removedMethods.isEmpty()) {
+      removedOverridableMethods = new HashSet<MethodInfo>(removedMethods);
+      for (Iterator<MethodInfo> it = removedOverridableMethods.iterator(); it.hasNext();) {
+        final MethodInfo method = it.next();
+        if (method.isFinal() || method.isStatic() || method.isPrivate() || method.isConstructor()) {
+          it.remove();
+        }
+      }
+    }
+    else {
+      removedOverridableMethods = Collections.emptySet();
+    }
+    myDependencyCache.getCacheNavigator().walkSubClasses(myQName, new ClassInfoProcessor() {
+      public boolean process(final int subclassQName) throws CacheCorruptedException {
+        if (myDependencyCache.isClassInfoMarked(subclassQName)) {
+          return true;
+        }
+
+        if (!oldCache.containsClass(subclassQName)) {
+          return true;
+        }
+
+        if (!removedMethods.isEmpty() && myIsRemoteInterface && !MakeUtil.isInterface(oldCache.getFlags(subclassQName))) {
+          if (myDependencyCache.markClass(subclassQName)) {
+            if (LOG.isDebugEnabled()) {
+              LOG.debug("Mark dependent class " + myDependencyCache.resolve(subclassQName) +
+                        "; reason: methods were removed from remote interface: " + myDependencyCache.resolve(myQName));
+            }
+          }
+          return true;
+        }
+
+        if (mySuperClassAdded || mySuperInterfaceAdded) {
+          if (myDependencyCache.markClass(subclassQName)) {
+            if (LOG.isDebugEnabled()) {
+              LOG.debug("Mark dependent class " + myDependencyCache.resolve(subclassQName) + "; reason: the superlist of " +
+                        myDependencyCache.resolve(myQName) + " is changed");
+            }
+          }
+          return true;
+        }
+
+        // if info became final, mark direct inheritors
+        if (becameFinal) {
+          if (myQName == oldCache.getSuperQualifiedName(subclassQName)) {
+            if (myDependencyCache.markClass(subclassQName)) {
+              if (LOG.isDebugEnabled()) {
+                LOG.debug("Mark dependent class " + myDependencyCache.resolve(subclassQName) + "; reason: the class " +
+                          myDependencyCache.resolve(myQName) + " was made final");
+              }
+            }
+            return true;
+          }
+        }
+
+        // process added members
+        for (final MemberInfo member : myAddedMembers) {
+          if (member instanceof MethodInfo) {
+            final MethodInfo method = (MethodInfo)member;
+            if (method.isAbstract()) {
+              // all derived classes should be marked in case an abstract method was added
+              if (myDependencyCache.markClass(subclassQName)) {
+                if (LOG.isDebugEnabled()) {
+                  LOG.debug("Mark dependent class " + myDependencyCache.resolve(subclassQName) + "; reason: added abstract method to " +
+                            myDependencyCache.resolve(myQName));
+                }
+              }
+              return true;
+            }
+            if (!method.isPrivate()) {
+              final MethodInfo derivedMethod = oldCache.findMethodsBySignature(subclassQName, method.getDescriptor(symbolTable), symbolTable);
+              if (derivedMethod != null) {
+                if (!method.getReturnTypeDescriptor(symbolTable).equals(derivedMethod.getReturnTypeDescriptor(symbolTable))) {
+                  if (myDependencyCache.markClass(subclassQName)) {
+                    if (LOG.isDebugEnabled()) {
+                      LOG.debug("Mark dependent class " + myDependencyCache.resolve(subclassQName) + "; reason: return types of method " +
+                                method + " in base and derived classes are different");
+                    }
+                  }
+                  return true;
+                }
+                if (MakeUtil.isMoreAccessible(method.getFlags(), derivedMethod.getFlags())) {
+                  if (myDependencyCache.markClass(subclassQName)) {
+                    if (LOG.isDebugEnabled()) {
+                      LOG.debug("Mark dependent class " + myDependencyCache.resolve(subclassQName) + "; reason: the method " + method +
+                                " in derived class is less accessible than in base class");
+                    }
+                  }
+                  return true;
+                }
+                if (!method.isStatic() && derivedMethod.isStatic()) {
+                  if (myDependencyCache.markClass(subclassQName)) {
+                    if (LOG.isDebugEnabled()) {
+                      LOG.debug("Mark dependent class " + myDependencyCache.resolve(subclassQName) + "; reason: the method " + method +
+                                " in derived class is static, but added method in the base class is not");
+                    }
+                  }
+                  return true;
+                }
+                if (method.isFinal() && !derivedMethod.isFinal()) {
+                  if (myDependencyCache.markClass(subclassQName)) {
+                    if (LOG.isDebugEnabled()) {
+                      LOG.debug("Mark dependent class " + myDependencyCache.resolve(subclassQName) + "; reason: the method " + method +
+                                " in base class is final, but in derived class is not");
+                    }
+                  }
+                  return true;
+                }
+                if (!CacheUtils.areArraysContentsEqual(method.getThrownExceptions(), derivedMethod.getThrownExceptions())) {
+                  if (myDependencyCache.markClass(subclassQName)) {
+                    if (LOG.isDebugEnabled()) {
+                      LOG.debug("Mark dependent class " + myDependencyCache.resolve(subclassQName) + "; reason: exception lists of " +
+                                method + " in base and derived classes are different");
+                    }
+                  }
+                  return true;
+                }
+              }
+              if (hasGenericsNameClashes(method, oldCache, subclassQName)) {
+                if (myDependencyCache.markClass(subclassQName)) {
+                  if (LOG.isDebugEnabled()) {
+                    LOG.debug("Mark dependent class " + myDependencyCache.resolve(subclassQName) +
+                              "; reason: found method with the same name, different generic signature, but the same erasure as " + method);
+                  }
+                }
+                return true;
+              }
+            }
+          }
+          else if (member instanceof FieldInfo) {
+            if (oldCache.findFieldByName(subclassQName, member.getName()) != null) {
+              if (myDependencyCache.markClass(subclassQName)) {
+                if (LOG.isDebugEnabled()) {
+                  LOG.debug("Mark dependent class " + myDependencyCache.resolve(subclassQName) + "; reason: added field " + member +
+                            " to base class");
+                }
+              }
+              return true;
+            }
+          }
+        }
+
+        // process changed members
+        for (final MemberInfo changedMember : myChangedMembers) {
+          if (changedMember instanceof MethodInfo) {
+            final MethodInfo oldMethod = (MethodInfo)changedMember;
+            MethodChangeDescription changeDescription = (MethodChangeDescription)myChangeDescriptions.get(oldMethod);
+            if (changeDescription.becameAbstract) {
+              if (!ClsUtil.isAbstract(oldCache.getFlags(subclassQName))) { // if the subclass was not abstract
+                if (myDependencyCache.markClass(subclassQName)) {
+                  if (LOG.isDebugEnabled()) {
+                    LOG.debug(
+                      "Mark dependent class " + myDependencyCache.resolve(subclassQName) + "; reason: changed base method " + oldMethod);
+                  }
+                }
+                return true;
+              }
+            }
+            
+            final String oldMethodDescriptor = oldMethod.getDescriptor(symbolTable);
+            
+            final MethodInfo derivedMethod = oldCache.findMethodsBySignature(subclassQName, oldMethodDescriptor, symbolTable);
+            if (derivedMethod != null) {
+              if (myDependencyCache.markClass(subclassQName)) {
+                if (LOG.isDebugEnabled()) {
+                  LOG.debug("Mark dependent class " + myDependencyCache.resolve(subclassQName) + "; reason: changed base method " + oldMethod);
+                }
+              }
+              return true;
+            }
+            // now check if the changed method is compatible with methods declared in implemented interfaces of subclasses
+            myDependencyCache.getCacheNavigator().walkSuperInterfaces(subclassQName, new ClassInfoProcessor() {
+              boolean found = false;
+              public boolean process(final int ifaceQName) throws CacheCorruptedException {
+                if (found) {
+                  return false;
+                }
+                final MethodInfo implementee = oldCache.findMethodsBySignature(ifaceQName, oldMethodDescriptor, symbolTable);
+                if (implementee != null) {
+                  found = true;
+                  if (myDependencyCache.markClass(subclassQName)) {
+                    if (LOG.isDebugEnabled()) {
+                      LOG.debug("Mark dependent class " + myDependencyCache.resolve(subclassQName) + "; reason: changed base method, implementing corresponding method inherited from an interface" + oldMethod);
+                    }
+                  }
+                }
+                return !found;
+              }
+            });
+            if (myDependencyCache.isClassInfoMarked(subclassQName)) {
+              return true;
+            }
+          }
+        }
+
+        if (!ClsUtil.isAbstract(oldCache.getFlags(subclassQName))) {
+          if (hasUnimplementedAbstractMethods(subclassQName, new HashSet<MemberInfo>(removedConcreteMethods))) {
+            if (myDependencyCache.markClass(subclassQName)) {
+              if (LOG.isDebugEnabled()) {
+                LOG.debug("Mark dependent class " + myDependencyCache.resolve(subclassQName) + "; reason: the class should be declared abstract because abstract method implementation was removed from its superclass: " +
+                          myDependencyCache.resolve(myQName));
+              }
+            }
+            return true;
+          }
+        }
+
+        if (!removedOverridableMethods.isEmpty() && !myDependencyCache.isClassInfoMarked(subclassQName) && !myDependencyCache.getNewClassesCache().containsClass(subclassQName) /*not compiled in this session*/) {
+          final Cache cache = myDependencyCache.getCache();
+          for (MethodInfo subclassMethod : cache.getMethods(subclassQName)) {
+            if (!subclassMethod.isConstructor()) {
+              for (MethodInfo removedMethod : removedOverridableMethods) {
+                if (removedMethod.getName() == subclassMethod.getName() /*todo: check param signatures here for better accuracy*/) {
+                  // got it
+                  if (myDependencyCache.markClass(subclassQName)) {
+                    if (LOG.isDebugEnabled()) {
+                      LOG.debug("Mark dependent subclass " + myDependencyCache.resolve(subclassQName) + "; reason: the class has methods annotated with @Override and some methods were changed or removed in a base class" +
+                                myDependencyCache.resolve(myQName));
+                    }
+                  }
+                  return true;
+                }
+              }
+            }
+          }
+        }
+        // end of subclass processor
+        return true;
+      }
+    });
+  }
+
+  private static boolean hasGenericsNameClashes(final MethodInfo baseMethod, final Cache oldCache, final int subclassQName) throws CacheCorruptedException {
+    // it is illegal if 2 methods in a hierarchy have 1) same name 2) different signatures 3) same erasure
+    final List<MethodInfo> methods = oldCache.findMethodsByName(subclassQName, baseMethod.getName());
+    if (methods.size() > 0) {
+      for (final MethodInfo methodInSubclass : methods) {
+        if (ClsUtil.isBridge(methodInSubclass.getFlags())) {
+          continue;
+        }
+        if (baseMethod.getDescriptor() == methodInSubclass.getDescriptor() && baseMethod.getGenericSignature() != methodInSubclass.getGenericSignature()) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  private static Set<MemberInfo> fetchNonAbstractMethods(Set<MemberInfo> membersToCheck) {
+    final Set<MemberInfo> methodsToCheck = new HashSet<MemberInfo>();
+    for (final Object aMembersToCheck : membersToCheck) {
+      final MemberInfo memberInfo = (MemberInfo)aMembersToCheck;
+      if (memberInfo instanceof MethodInfo) {
+        final MethodInfo methodInfo = (MethodInfo)memberInfo;
+        if (!methodInfo.isAbstract() && !methodInfo.isConstructor()) {
+          methodsToCheck.add(memberInfo);
+        }
+      }
+    }
+    return methodsToCheck;
+  }
+
+  private boolean hasUnimplementedAbstractMethods(int superQName, final Set methodsToCheck) throws CacheCorruptedException {
+    if (myDependencyCache.getCache().containsClass(superQName)) {
+      return hasBaseAbstractMethods(superQName, methodsToCheck) ||
+             hasBaseAbstractMethodsInHierarchy(superQName, methodsToCheck);
+    }
+    else {
+      final String qName = myDependencyCache.resolve(superQName);
+      if (!CommonClassNames.JAVA_LANG_OBJECT.equals(qName)) {
+        if (hasBaseAbstractMethods2(qName, methodsToCheck)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  private boolean hasBaseAbstractMethodsInHierarchy(int fromClassQName, final Set methodsToCheck) throws CacheCorruptedException {
+    if (fromClassQName == Cache.UNKNOWN || methodsToCheck.isEmpty()) {
+      return false;
+    }
+    final Cache cache = myDependencyCache.getCache();
+    int superName = cache.getSuperQualifiedName(fromClassQName);
+    if (superName != Cache.UNKNOWN) {
+      if (hasUnimplementedAbstractMethods(superName, methodsToCheck)) {
+        return true;
+      }
+    }
+    if (methodsToCheck.isEmpty()) {
+      return false;
+    }
+    int[] superInterfaces = cache.getSuperInterfaces(fromClassQName);
+    for (int superInterface : superInterfaces) {
+      if (hasUnimplementedAbstractMethods(superInterface, methodsToCheck)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private boolean hasBaseAbstractMethods(int qName, Set methodsToCheck) throws CacheCorruptedException {
+    final SymbolTable symbolTable = myDependencyCache.getSymbolTable();
+    final Cache oldCache = myDependencyCache.getCache();
+    final Cache newCache = myDependencyCache.getNewClassesCache();
+    final Cache cache = newCache.containsClass(qName)? newCache : oldCache; // use recompiled version (if any) for searching methods
+    for (Iterator it = methodsToCheck.iterator(); it.hasNext();) {
+      final MethodInfo methodInfo = (MethodInfo)it.next();
+      final MethodInfo superMethod = cache.findMethodsBySignature(qName, methodInfo.getDescriptor(symbolTable), symbolTable);
+      if (superMethod != null) {
+        if (ClsUtil.isAbstract(superMethod.getFlags())) {
+          return true;
+        }
+        it.remove();
+      }
+    }
+    return false;
+  }
+
+  // search using PSI
+  private boolean hasBaseAbstractMethods2(final String qName, final Set methodsToCheck) throws CacheCorruptedException {
+    final boolean[] found = {false};
+    final CacheCorruptedException ex = ApplicationManager.getApplication().runReadAction(new Computable<CacheCorruptedException>() {
+      public CacheCorruptedException compute() {
+        try {
+          final PsiManager psiManager = PsiManager.getInstance(myProject);
+          final PsiClass aClass = JavaPsiFacade.getInstance(psiManager.getProject()).findClass(qName, GlobalSearchScope.allScope(myProject));
+          if (aClass == null) {
+            return null;
+          }
+          final PsiElementFactory factory = JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory();
+          final PsiNameHelper nameHelper = JavaPsiFacade.getInstance(myProject).getNameHelper();
+          for (Iterator it = methodsToCheck.iterator(); it.hasNext();) {
+            final MethodInfo methodInfo = (MethodInfo)it.next();
+            if (!nameHelper.isIdentifier(myDependencyCache.resolve(methodInfo.getName()), LanguageLevel.JDK_1_3)) { // fix for SCR 16068
+              continue;
+            }
+            // language level 1.3 will prevent exceptions from PSI if there are methods named "assert"
+            final PsiMethod methodPattern = factory.createMethodFromText(getMethodText(methodInfo), null, LanguageLevel.JDK_1_3);
+            final PsiMethod superMethod = aClass.findMethodBySignature(methodPattern, true);
+            if (superMethod != null) {
+              if (superMethod.hasModifierProperty(PsiModifier.ABSTRACT)) {
+                found[0] = true;
+                return null;
+              }
+              it.remove();
+            }
+          }
+        }
+        catch (IncorrectOperationException e) {
+          LOG.error(e);
+        }
+        catch (CacheCorruptedException e) {
+          return e;
+        }
+        return null;
+      }
+    });
+    if (ex != null) {
+      throw ex;
+    }
+    return found[0];
+  }
+
+  @SuppressWarnings({"HardCodedStringLiteral"})
+  private @NonNls String getMethodText(MethodInfo methodInfo) throws CacheCorruptedException {
+    final SymbolTable symbolTable = myDependencyCache.getSymbolTable();
+    StringBuilder text = new StringBuilder(16);
+    final String returnType = signatureToSourceTypeName(methodInfo.getReturnTypeDescriptor(symbolTable));
+    text.append(returnType);
+    text.append(" ");
+    text.append(myDependencyCache.resolve(methodInfo.getName()));
+    text.append("(");
+    final String[] parameterSignatures = methodInfo.getParameterDescriptors(symbolTable);
+    for (int idx = 0; idx < parameterSignatures.length; idx++) {
+      String parameterSignature = parameterSignatures[idx];
+      if (idx > 0) {
+        text.append(",");
+      }
+      text.append(signatureToSourceTypeName(parameterSignature));
+      text.append(" arg");
+      text.append(idx);
+    }
+    text.append(")");
+    return text.toString();
+  }
+
+  private static boolean wereInterfacesRemoved(int[] oldInterfaces, int[] newInterfaces) {
+    for (int oldInterface : oldInterfaces) {
+      boolean found = false;
+      for (int newInterface : newInterfaces) {
+        found = oldInterface == newInterface;
+        if (found) {
+          break;
+        }
+      }
+      if (!found) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /** @return a map [fieldName->FieldInfo]*/
+  private static TIntObjectHashMap<FieldInfo> getFieldInfos(Cache cache, int qName) throws CacheCorruptedException {
+    final TIntObjectHashMap<FieldInfo> map = new TIntObjectHashMap<FieldInfo>();
+    for (FieldInfo fieldInfo : cache.getFields(qName)) {
+      map.put(fieldInfo.getName(), fieldInfo);
+    }
+    return map;
+  }
+
+  /** @return a map [methodSignature->MethodInfo]*/
+  private Map<String, MethodInfoContainer> getMethodInfos(final MethodInfo[] methods) throws CacheCorruptedException {
+    final Map<String, MethodInfoContainer> map = new HashMap<String, MethodInfoContainer>();
+    final SymbolTable symbolTable = myDependencyCache.getSymbolTable();
+    for (MethodInfo methodInfo : methods) {
+      final String signature = methodInfo.getDescriptor(symbolTable);
+      final MethodInfoContainer currentValue = map.get(signature);
+      // covariant methods have the same signature, so there might be several MethodInfos for one key
+      if (currentValue == null) {
+        map.put(signature, new MethodInfoContainer(methodInfo));
+      }
+      else {
+        currentValue.add(methodInfo);
+      }
+    }
+    return map;
+  }
+
+  private static void addAddedMembers(TIntObjectHashMap<FieldInfo> oldFields, Map<String, MethodInfoContainer> oldMethods,
+                               TIntObjectHashMap<FieldInfo> newFields, Map<String, MethodInfoContainer> newMethods,
+                               Collection<MemberInfo> members) {
+
+    for (final TIntObjectIterator<FieldInfo> it = newFields.iterator(); it.hasNext();) {
+      it.advance();
+      final int fieldName = it.key();
+      final FieldInfo fieldInfo = it.value();
+      if (!oldFields.containsKey(fieldName)) {
+        members.add(fieldInfo);
+      }
+    }
+    for (final String signature : newMethods.keySet()) {
+      if (!oldMethods.containsKey(signature)) {
+        members.addAll(newMethods.get(signature).getMethods());
+      }
+    }
+  }
+
+  private static void addRemovedMembers(TIntObjectHashMap<FieldInfo> oldFields, Map<String, MethodInfoContainer> oldMethods,
+                               TIntObjectHashMap<FieldInfo> newFields, Map<String, MethodInfoContainer> newMethods,
+                               Collection<MemberInfo> members)  {
+    addAddedMembers(newFields, newMethods, oldFields, oldMethods, members);
+  }
+
+  private void addChangedMembers(TIntObjectHashMap<FieldInfo> oldFields, Map<String, MethodInfoContainer> oldMethods,
+                               TIntObjectHashMap<FieldInfo> newFields, Map<String, MethodInfoContainer> newMethods,
+                               Collection<MemberInfo> members) throws CacheCorruptedException {
+    for (final TIntObjectIterator<FieldInfo> it = oldFields.iterator(); it.hasNext();) {
+      it.advance();
+      final int fieldName = it.key();
+      final FieldInfo oldInfo = it.value();
+      final FieldInfo newInfo = newFields.get(fieldName);
+      if (newInfo != null) {
+        final FieldChangeDescription changeDescription = new FieldChangeDescription(oldInfo, newInfo);
+        if (changeDescription.isChanged()) {
+          members.add(oldInfo);
+          myChangeDescriptions.put(oldInfo, changeDescription);
+        }
+      }
+    }
+
+    if (!oldMethods.isEmpty()) {
+      final SymbolTable symbolTable = myDependencyCache.getSymbolTable();
+      final Set<MethodInfo> processed = new HashSet<MethodInfo>();
+      for (final String signature : oldMethods.keySet()) {
+        final MethodInfoContainer oldMethodsContainer = oldMethods.get(signature);
+        final MethodInfoContainer newMethodsContainer = newMethods.get(signature);
+        if (newMethodsContainer != null) {
+          processed.clear();
+          if (oldMethodsContainer.size() == newMethodsContainer.size()) {
+            // first, process all corresponding method infos
+            for (MethodInfo oldInfo : oldMethodsContainer.getMethods()) {
+              MethodInfo _newInfo = null;
+              for (MethodInfo newInfo : newMethodsContainer.getMethods()) {
+                if (oldInfo.equals(newInfo)) {
+                  _newInfo = newInfo;
+                  break;
+                }
+              }
+              if (_newInfo != null) {
+                processed.add(oldInfo);
+                processed.add(_newInfo);
+                final MethodChangeDescription changeDescription = new MethodChangeDescription(oldInfo, _newInfo, symbolTable);
+                if (changeDescription.isChanged()) {
+                  members.add(oldInfo);
+                  myChangeDescriptions.put(oldInfo, changeDescription);
+                }
+              }
+            }
+          }
+          // processing the rest of infos, each pair
+          for (MethodInfo oldInfo : oldMethodsContainer.getMethods()) {
+            if (processed.contains(oldInfo)) {
+              continue;
+            }
+            for (MethodInfo newInfo : newMethodsContainer.getMethods()) {
+              if (processed.contains(newInfo)) {
+                continue;
+              }
+              final MethodChangeDescription changeDescription = new MethodChangeDescription(oldInfo, newInfo, symbolTable);
+              if (changeDescription.isChanged()) {
+                members.add(oldInfo);
+                myChangeDescriptions.put(oldInfo, changeDescription);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  private boolean hasEquivalentMethod(Collection<MethodInfo> members, Dependency.MethodRef modelMethod) throws CacheCorruptedException {
+    final String[] modelSignature = modelMethod.getParameterDescriptors(myDependencyCache.getSymbolTable());
+    for (final MethodInfo method : members) {
+      if (modelMethod.name != method.getName()) {
+        continue;
+      }
+      final String[] methodSignature = method.getParameterDescriptors(myDependencyCache.getSymbolTable());
+      if (modelSignature.length != methodSignature.length) {
+        continue;
+      }
+
+      for (int i = 0; i < methodSignature.length; i++) {
+        if (!methodSignature[i].equals(modelSignature[i])) {
+          if (LOG.isDebugEnabled()) {
+            LOG.debug("Equivalent: " + modelMethod.getDescriptor(myDependencyCache.getSymbolTable()) + " <=> " +
+                      method.getDescriptor(myDependencyCache.getSymbolTable()));
+          }
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  private static @NonNls
+  String signatureToSourceTypeName(String signature)
+  {
+    try {
+      switch(signature.charAt(0)) {
+      case 'B' : return "byte";
+      case 'C' : return "char";
+      case 'D' : return "double";
+      case 'F' : return "float";
+      case 'I' : return "int";
+      case 'J' : return "long";
+
+      case 'L' : { // Full class name
+	int    index = signature.indexOf(';'); // Look for closing `;'
+
+	if(index < 0)
+	  throw new RuntimeException("Invalid signature: " + signature);
+
+        return signature.substring(1, index).replace('/', '.');
+      }
+
+      case 'S' : return "short";
+      case 'Z' : return "boolean";
+
+      case '[' : { // Array declaration
+	int          n;
+	StringBuffer brackets;
+	String       type;
+
+	brackets = new StringBuffer(); // Accumulate []'s
+
+	// Count opening brackets and look for optional size argument
+	for(n=0; signature.charAt(n) == '['; n++)
+	  brackets.append("[]");
+
+
+	// The rest of the string denotes a `<field_type>'
+	type = signatureToSourceTypeName(signature.substring(n));
+
+	return type + brackets.toString();
+      }
+
+      case 'V' : return "void";
+
+      default  : throw new RuntimeException("Invalid signature: `" +
+					    signature + "'");
+      }
+    } catch(StringIndexOutOfBoundsException e) { // Should never occur
+      throw new RuntimeException("Invalid signature: " + e + ":" + signature);
+    }
+  }
+
+  private Dependency[] getBackDependencies() throws CacheCorruptedException {
+    if (myBackDependencies == null) {
+      myBackDependencies = myDependencyCache.getCache().getBackDependencies(myQName);
+    }
+    return myBackDependencies;
+  }
+
+  private static class MethodInfoContainer {
+    private List<MethodInfo> myInfos = null;
+
+    protected MethodInfoContainer(MethodInfo info) {
+      myInfos = Collections.singletonList(info);
+    }
+
+    public List<MethodInfo> getMethods() {
+      return myInfos;
+    }
+
+    public int size() {
+      return myInfos.size();
+    }
+
+    public void add(MethodInfo info) {
+      if (myInfos.size() == 1) {
+        myInfos = new ArrayList<MethodInfo>(myInfos);
+      }
+      myInfos.add(info);
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/MakeUtil.java b/java/compiler/impl/src/com/intellij/compiler/make/MakeUtil.java
new file mode 100644
index 0000000..52f347f
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/MakeUtil.java
@@ -0,0 +1,300 @@
+/*
+ * 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.
+ */
+
+/**
+ * created at Jan 17, 2002
+ * @author Jeka
+ */
+package com.intellij.compiler.make;
+
+import com.intellij.compiler.SymbolTable;
+import com.intellij.compiler.classParsing.*;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.CompilerModuleExtension;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.util.cls.ClsUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+public class MakeUtil {
+
+  private MakeUtil() {
+  }
+
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.make.MakeUtil");
+
+
+  public static VirtualFile getSourceRoot(CompileContext context, Module module, VirtualFile file) {
+    final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(module.getProject()).getFileIndex();
+    final VirtualFile root = fileIndex.getSourceRootForFile(file);
+    if (root != null) {
+      return root;
+    }
+    // try to find among roots of generated files.
+    final VirtualFile[] sourceRoots = context.getSourceRoots(module);
+    for (final VirtualFile sourceRoot : sourceRoots) {
+      if (fileIndex.isInSourceContent(sourceRoot)) {
+        continue; // skip content source roots, need only roots for generated files
+      }
+      if (VfsUtil.isAncestor(sourceRoot, file, false)) {
+        return sourceRoot;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * cuts inner or anonymous class' parts and translates package names to lower case
+   */
+  private static String normalizeClassName(String qName) {
+    int index = qName.indexOf('$');
+    if (index >= 0) {
+      qName = qName.substring(0, index);
+    }
+    if (SystemInfo.isFileSystemCaseSensitive) {
+      return qName;
+    }
+    // the name of a dir should be lowercased because javac seem to allow difference in case
+    // between the physical directory and package name.
+    final int dotIndex = qName.lastIndexOf('.');
+    final StringBuilder builder = StringBuilderSpinAllocator.alloc();
+    try {
+      builder.append(qName);
+      for (int idx = 0; idx < dotIndex; idx++) {
+        builder.setCharAt(idx, Character.toLowerCase(builder.charAt(idx)));
+      }
+      return builder.toString();
+    }
+    finally {
+      StringBuilderSpinAllocator.dispose(builder);
+    }
+  }
+
+  public static boolean isAnonymous(String name) {
+    int index = name.lastIndexOf('$');
+    if (index >= 0) {
+      index++;
+      if (index < name.length()) {
+        try {
+          Integer.parseInt(name.substring(index));
+          return true;
+        }
+        catch (NumberFormatException e) {
+        }
+      }
+    }
+    return false;
+  }
+
+  /*
+     not needed currently
+  public static String getEnclosingClassName(String anonymousQName) {
+    return anonymousQName.substring(0, anonymousQName.lastIndexOf('$'));
+  }
+  */
+
+  /*
+   not needed currently
+  public static boolean isNative(int flags) {
+    return (ClsUtil.ACC_NATIVE & flags) != 0;
+  }
+  */
+
+  /**
+   * tests if the accessibility, denoted by flags1 is less restricted than the accessibility denoted by flags2
+   * @return true means flags1 is less restricted than flags2 <br>
+   *         false means flags1 define more restricted access than flags2 or they have equal accessibility
+   */
+  public static boolean isMoreAccessible(int flags1, int flags2) {
+    if (ClsUtil.isPrivate(flags2)) {
+      return ClsUtil.isPackageLocal(flags1) || ClsUtil.isProtected(flags1) || ClsUtil.isPublic(flags1);
+    }
+    if (ClsUtil.isPackageLocal(flags2)) {
+      return ClsUtil.isProtected(flags1) || ClsUtil.isPublic(flags1);
+    }
+    if (ClsUtil.isProtected(flags2)) {
+      return ClsUtil.isPublic(flags1);
+    }
+    return false;
+  }
+
+  @Nullable
+  @SuppressWarnings({"HardCodedStringLiteral"})
+  public static String relativeClassPathToQName(String relativePath, char separator) {
+    if (!relativePath.endsWith(".class")) {
+      return null;
+    }
+    int start = 0;
+    int end = relativePath.length() - ".class".length();
+    if (relativePath.startsWith(String.valueOf(separator))) {
+      start += 1;
+    }
+    return (start <= end)? relativePath.substring(start, end).replace(separator, '.') : null;
+  }
+
+  public static @NonNls String parseObjectType(final String descriptor, int fromIndex) {
+    int semicolonIndex = descriptor.indexOf(';', fromIndex);
+    if (descriptor.charAt(fromIndex) == 'L' && semicolonIndex > fromIndex) { // isObjectType
+      return descriptor.substring(fromIndex + 1, semicolonIndex).replace('/', '.');
+    }
+    if (descriptor.charAt(fromIndex) == '[' && (descriptor.length() - fromIndex) > 0) { // isArrayType
+      return parseObjectType(descriptor, fromIndex + 1);
+    }
+    return null;
+  }
+
+  @SuppressWarnings({"HardCodedStringLiteral"})
+  public static boolean isPrimitiveType(String descriptor) {
+    return
+      "V".equals(descriptor) ||
+      "B".equals(descriptor) ||
+      "C".equals(descriptor) ||
+      "D".equals(descriptor) ||
+      "F".equals(descriptor) ||
+      "I".equals(descriptor) ||
+      "J".equals(descriptor) ||
+      "S".equals(descriptor) ||
+      "Z".equals(descriptor);
+  }
+
+  public static boolean isArrayType(String descriptor) {
+    return StringUtil.startsWithChar(descriptor, '[');
+  }
+
+  public static String getComponentType(String descriptor) {
+    if (!isArrayType(descriptor)) {
+      return null;
+    }
+    return descriptor.substring(1);
+  }
+
+
+  /**
+   * @return a normalized path to source relative to a source root by class qualified name and sourcefile short name.
+   *  The path uses forward slashes "/".
+   */
+  public static String createRelativePathToSource(String qualifiedName, String srcName) {
+    qualifiedName = normalizeClassName(qualifiedName);
+    int index = qualifiedName.lastIndexOf('.');
+    if (index >= 0) {
+      srcName = qualifiedName.substring(0, index).replace('.', '/') + "/" + srcName;
+    }
+    return srcName;
+  }
+
+  public static boolean isInterface(int flags) {
+    return (ClsUtil.ACC_INTERFACE & flags) != 0;
+  }
+
+  public static int getAnnotationTargets(final Cache cache, final int annotationQName, final SymbolTable symbolTable) throws CacheCorruptedException {
+    final AnnotationConstantValue targetAnnotation = findAnnotation(
+      "java.lang.annotation.Target",
+      cache.getRuntimeVisibleAnnotations(annotationQName), symbolTable);
+    if (targetAnnotation == null) {
+      return AnnotationTargets.ALL; // all program elements are annotation targets by default
+    }
+    final AnnotationNameValuePair[] memberValues = targetAnnotation.getMemberValues();
+    ConstantValueArray value = (ConstantValueArray)memberValues[0].getValue();
+    final ConstantValue[] targets = value.getValue();
+    int annotationTargets = 0;
+    for (final ConstantValue target : targets) {
+      if (target instanceof EnumConstantValue) {
+        final String constantName = symbolTable.getSymbol(((EnumConstantValue)target).getConstantName());
+        if (AnnotationTargets.TYPE_STR.equals(constantName)) {
+          annotationTargets |= AnnotationTargets.TYPE;
+        }
+        if (AnnotationTargets.FIELD_STR.equals(constantName)) {
+          annotationTargets |= AnnotationTargets.FIELD;
+        }
+        if (AnnotationTargets.METHOD_STR.equals(constantName)) {
+          annotationTargets |= AnnotationTargets.METHOD;
+        }
+        if (AnnotationTargets.PARAMETER_STR.equals(constantName)) {
+          annotationTargets |= AnnotationTargets.PARAMETER;
+        }
+        if (AnnotationTargets.CONSTRUCTOR_STR.equals(constantName)) {
+          annotationTargets |= AnnotationTargets.CONSTRUCTOR;
+        }
+        if (AnnotationTargets.LOCAL_VARIABLE_STR.equals(constantName)) {
+          annotationTargets |= AnnotationTargets.LOCAL_VARIABLE;
+        }
+        if (AnnotationTargets.ANNOTATION_TYPE_STR.equals(constantName)) {
+          annotationTargets |= AnnotationTargets.ANNOTATION_TYPE;
+        }
+        if (AnnotationTargets.PACKAGE_STR.equals(constantName)) {
+          annotationTargets |= AnnotationTargets.PACKAGE;
+        }
+      }
+    }
+    return annotationTargets;
+  }
+
+  public static int getAnnotationRetentionPolicy(final int annotationQName, final Cache cache, final SymbolTable symbolTable) throws CacheCorruptedException {
+    final AnnotationConstantValue retentionPolicyAnnotation = findAnnotation(
+      "java.lang.annotation.Retention",
+      cache.getRuntimeVisibleAnnotations(annotationQName), symbolTable
+    );
+    if (retentionPolicyAnnotation == null) {
+      return RetentionPolicies.CLASS; // default retention policy
+    }
+    final AnnotationNameValuePair[] memberValues = retentionPolicyAnnotation.getMemberValues();
+    final EnumConstantValue value = (EnumConstantValue)memberValues[0].getValue();
+    final String constantName = symbolTable.getSymbol(value.getConstantName());
+    if (RetentionPolicies.SOURCE_STR.equals(constantName)) {
+      return RetentionPolicies.SOURCE;
+    }
+    if (RetentionPolicies.CLASS_STR.equals(constantName)) {
+      return RetentionPolicies.CLASS;
+    }
+    if (RetentionPolicies.RUNTIME_STR.equals(constantName)) {
+      return RetentionPolicies.RUNTIME;
+    }
+    LOG.error("Unknown retention policy: " + constantName);
+    return -1;
+  }
+
+  public static AnnotationConstantValue findAnnotation(@NonNls final String annotationQName,
+                                                       AnnotationConstantValue[] annotations, final SymbolTable symbolTable) throws CacheCorruptedException {
+    for (final AnnotationConstantValue annotation : annotations) {
+      if (annotationQName.equals(symbolTable.getSymbol(annotation.getAnnotationQName()))) {
+        return annotation;
+      }
+    }
+    return null;
+  }
+
+  public static String getModuleOutputDirPath(final Module module) {
+    return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
+      public String compute() {
+        final String url = CompilerModuleExtension.getInstance(module).getCompilerOutputUrl();
+        if (url == null) {
+          return null;
+        }
+        return VfsUtil.urlToPath(url);
+      }
+    });
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/MethodChangeDescription.java b/java/compiler/impl/src/com/intellij/compiler/make/MethodChangeDescription.java
new file mode 100644
index 0000000..2465703
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/MethodChangeDescription.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 com.intellij.compiler.make;
+
+import com.intellij.compiler.SymbolTable;
+import com.intellij.compiler.classParsing.ConstantValue;
+import com.intellij.compiler.classParsing.GenericMethodSignature;
+import com.intellij.compiler.classParsing.MethodInfo;
+import com.intellij.compiler.classParsing.SignatureParsingException;
+import com.intellij.util.cls.ClsUtil;
+
+import java.util.Arrays;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Apr 7, 2004
+ */
+class MethodChangeDescription extends ChangeDescription {
+  public final boolean returnTypeDescriptorChanged;
+  public final boolean returnTypeGenericSignatureChanged;
+  public final boolean paramsGenericSignatureChanged;
+  public final boolean throwsListChanged;
+  public final boolean flagsChanged;
+  public final boolean staticPropertyChanged;
+  public final boolean accessRestricted;
+  public final boolean becameAbstract;
+  public final boolean removedAnnotationDefault;
+
+  public MethodChangeDescription(final MethodInfo oldMethod, final MethodInfo newMethod, SymbolTable symbolTable) throws CacheCorruptedException {
+    final String oldRtDescriptor = oldMethod.getReturnTypeDescriptor(symbolTable);
+    final String newRtDescriptor = newMethod.getReturnTypeDescriptor(symbolTable);
+    returnTypeDescriptorChanged = !oldRtDescriptor.equals(newRtDescriptor);
+
+    final int oldGenericSignature = oldMethod.getGenericSignature();
+    final int newGenericSignature = newMethod.getGenericSignature();
+    if (oldGenericSignature == newGenericSignature) {
+      returnTypeGenericSignatureChanged = false;
+      paramsGenericSignatureChanged = false;
+    }
+    else {
+      if (oldGenericSignature != -1 && newGenericSignature != -1) {
+        try {
+          final GenericMethodSignature _oldGenericMethodSignature = GenericMethodSignature.parse(symbolTable.getSymbol(oldGenericSignature));
+          final GenericMethodSignature _newGenericMethodSignature = GenericMethodSignature.parse(symbolTable.getSymbol(newGenericSignature));
+          returnTypeGenericSignatureChanged = !_oldGenericMethodSignature.getReturnTypeSignature().equals(_newGenericMethodSignature.getReturnTypeSignature());
+          paramsGenericSignatureChanged = !_oldGenericMethodSignature.getFormalTypeParams().equals(_newGenericMethodSignature.getFormalTypeParams()) ||
+                                          !Arrays.equals(_oldGenericMethodSignature.getParamSignatures(), _newGenericMethodSignature.getParamSignatures());
+        }
+        catch (SignatureParsingException e) {
+          throw new CacheCorruptedException(e);
+        }
+      }
+      else {
+        returnTypeGenericSignatureChanged = true;
+        paramsGenericSignatureChanged = true;
+      }
+    }
+
+    throwsListChanged = !CacheUtils.areArraysContentsEqual(oldMethod.getThrownExceptions(), newMethod.getThrownExceptions());
+
+    final int oldFlags = oldMethod.getFlags();
+    final int newFlags = newMethod.getFlags();
+    flagsChanged = oldFlags != newFlags;
+
+    staticPropertyChanged = (ClsUtil.isStatic(oldFlags) && !ClsUtil.isStatic(newFlags)) ||  (!ClsUtil.isStatic(oldFlags) && ClsUtil.isStatic(newFlags)); // was not static and became static or was static and became not static
+    accessRestricted = MakeUtil.isMoreAccessible(oldFlags, newFlags);
+    becameAbstract = !ClsUtil.isAbstract(oldFlags) && ClsUtil.isAbstract(newFlags);
+
+    final ConstantValue oldDefault = oldMethod.getAnnotationDefault();
+    final ConstantValue newDefault = newMethod.getAnnotationDefault();
+    removedAnnotationDefault = (oldDefault != null && !ConstantValue.EMPTY_CONSTANT_VALUE.equals(oldDefault)) && (newDefault == null || ConstantValue.EMPTY_CONSTANT_VALUE.equals(newDefault));
+  }
+
+  public boolean isChanged() {
+    return returnTypeDescriptorChanged || throwsListChanged || flagsChanged || returnTypeGenericSignatureChanged || paramsGenericSignatureChanged || removedAnnotationDefault;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/RetentionPolicies.java b/java/compiler/impl/src/com/intellij/compiler/make/RetentionPolicies.java
new file mode 100644
index 0000000..13cfaa5
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/RetentionPolicies.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 com.intellij.compiler.make;
+
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Apr 7, 2004
+ */
+public interface RetentionPolicies {
+  /**
+   * Annotations are to be discarded by the compiler.
+   */
+  int SOURCE = 0x1;
+
+  @NonNls String SOURCE_STR = "SOURCE";
+
+  /**
+   * Annotations are to be recorded in the class file by the compiler
+   * but need not be retained by the VM at run time.  This is the default
+   * behavior.
+   */
+  int CLASS = 0x2;
+
+  @NonNls String CLASS_STR = "CLASS";
+
+  /**
+   * Annotations are to be recorded in the class file by the compiler and
+   * retained by the VM at run time, so they may be read reflectively.
+   */
+  int RUNTIME = 0x4;
+
+  @NonNls String RUNTIME_STR = "RUNTIME";
+
+  int ALL = SOURCE | CLASS | RUNTIME;
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/make/SourceFileFinder.java b/java/compiler/impl/src/com/intellij/compiler/make/SourceFileFinder.java
new file mode 100644
index 0000000..1e17b86
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/make/SourceFileFinder.java
@@ -0,0 +1,122 @@
+/*
+ * 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 com.intellij.compiler.make;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.util.containers.HashMap;
+
+import java.util.Map;
+
+/**
+ * Assumes that source roots in the project has not changed and caches the snapshot of source roots for effective searching
+ * User: JEKA
+ * Date: Jul 17, 2003
+ * Time: 9:52:26 PM
+ */
+public class SourceFileFinder {
+  private final Project myProject;
+  private final CompileContext myCompileContext;
+  private Map<VirtualFile, String> myProjectSourceRoots = null;
+  private final CompilerConfiguration myCompilerConfiguration;
+
+  public SourceFileFinder(Project project, CompileContext compileContext) {
+    myProject = project;
+    myCompileContext = compileContext;
+    myCompilerConfiguration = CompilerConfiguration.getInstance(project);
+  }
+
+  public VirtualFile findSourceFile(String qualifiedName, final String srcName, boolean checkIfExcludedFromMake) {
+    // optimization
+    final int dollar = qualifiedName.indexOf('$');
+    final String outerQName = (dollar >= 0)? qualifiedName.substring(0, dollar) : qualifiedName;
+    final PsiClass[] classes = JavaPsiFacade.getInstance(myProject).findClasses(outerQName, GlobalSearchScope.projectScope(myProject));
+    for (PsiClass aClass : classes) {
+      final PsiFile file = aClass.getContainingFile();
+      if (srcName.equals(file.getName())) {
+        final VirtualFile vFile = file.getVirtualFile();
+        if (vFile != null && (!checkIfExcludedFromMake || !myCompilerConfiguration.isExcludedFromCompilation(vFile))) {
+          return vFile;
+        }
+      }
+    }
+
+    String relativePath = MakeUtil.createRelativePathToSource(qualifiedName, srcName);
+    Map<VirtualFile, String> dirs = getAllSourceRoots();
+    if (!StringUtil.startsWithChar(relativePath, '/')) {
+      relativePath = "/" + relativePath;
+    }
+    LocalFileSystem fs = LocalFileSystem.getInstance();
+    for (final VirtualFile virtualFile : dirs.keySet()) {
+      final String prefix = dirs.get(virtualFile);
+      String path;
+      if (prefix.length() > 0) {
+        if (FileUtil.startsWith(relativePath, prefix)) {
+          // if there is package prefix assigned to the root, the relative path should be corrected
+          path = virtualFile.getPath() + relativePath.substring(prefix.length() - 1);
+        }
+        else {
+          // if there is package prefix, but the relative path does not match it, skip the root
+          continue;
+        }
+      }
+      else {
+        path = virtualFile.getPath() + relativePath;
+      }
+      VirtualFile file = fs.findFileByPath(path);
+      if (file != null && (!checkIfExcludedFromMake || !myCompilerConfiguration.isExcludedFromCompilation(virtualFile))) {
+        return file;
+      }
+    }
+    return null;
+  }
+
+  private Map<VirtualFile, String> getAllSourceRoots() {
+    if (myProjectSourceRoots == null) {
+      myProjectSourceRoots = new HashMap<VirtualFile, String>();
+      ApplicationManager.getApplication().runReadAction(new Runnable() {
+        public void run() {
+          final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex();
+          final Module[] allModules = ModuleManager.getInstance(myProject).getModules();
+          for (Module allModule : allModules) {
+            final VirtualFile[] sourceRoots = myCompileContext.getSourceRoots(allModule);
+            for (final VirtualFile sourceRoot : sourceRoots) {
+              String packageName = fileIndex.getPackageNameByDirectory(sourceRoot);
+              myProjectSourceRoots
+                .put(sourceRoot, packageName == null || packageName.length() == 0 ? "" : "/" + packageName.replace('.', '/') + "/");
+            }
+          }
+        }
+      });
+    }
+    return myProjectSourceRoots;
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/options/AnnotationProcessorsConfigurable.java b/java/compiler/impl/src/com/intellij/compiler/options/AnnotationProcessorsConfigurable.java
new file mode 100644
index 0000000..74ed9e1
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/options/AnnotationProcessorsConfigurable.java
@@ -0,0 +1,105 @@
+/*
+ * 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 com.intellij.compiler.options;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.CompilerConfigurationImpl;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.SearchableConfigurable;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.jps.model.java.compiler.ProcessorConfigProfile;
+
+import javax.swing.*;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Oct 5, 2009
+ */
+public class AnnotationProcessorsConfigurable implements SearchableConfigurable, Configurable.NoScroll {
+
+  private final Project myProject;
+  private AnnotationProcessorsPanel myMainPanel;
+
+  public AnnotationProcessorsConfigurable(final Project project) {
+    myProject = project;
+  }
+
+  public String getDisplayName() {
+    return "Annotation Processors";
+  }
+
+  public String getHelpTopic() {
+    return "reference.projectsettings.compiler.annotationProcessors";
+  }
+
+  @NotNull
+  public String getId() {
+    return getHelpTopic();
+  }
+
+  public Runnable enableSearch(String option) {
+    return null;
+  }
+
+  public JComponent createComponent() {
+    myMainPanel = new AnnotationProcessorsPanel(myProject);
+    return myMainPanel;
+  }
+
+  public boolean isModified() {
+    final CompilerConfigurationImpl config = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(myProject);
+
+    if (!config.getDefaultProcessorProfile().equals(myMainPanel.getDefaultProfile())) {
+      return true;
+    }
+
+    final Map<String, ProcessorConfigProfile> configProfiles = new java.util.HashMap<String, ProcessorConfigProfile>();
+    for (ProcessorConfigProfile profile : config.getModuleProcessorProfiles()) {
+      configProfiles.put(profile.getName(), profile);
+    }
+    final List<ProcessorConfigProfile> panelProfiles = myMainPanel.getModuleProfiles();
+    if (configProfiles.size() != panelProfiles.size()) {
+      return true;
+    }
+    for (ProcessorConfigProfile panelProfile : panelProfiles) {
+      final ProcessorConfigProfile configProfile = configProfiles.get(panelProfile.getName());
+      if (configProfile == null || !configProfile.equals(panelProfile)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  public void apply() throws ConfigurationException {
+    final CompilerConfigurationImpl config = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(myProject);
+    config.setDefaultProcessorProfile(myMainPanel.getDefaultProfile());
+    config.setModuleProcessorProfiles(myMainPanel.getModuleProfiles());
+  }
+
+  public void reset() {
+    final CompilerConfigurationImpl config = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(myProject);
+    myMainPanel.initProfiles(config.getDefaultProcessorProfile(), config.getModuleProcessorProfiles());
+  }
+
+  public void disposeUIResources() {
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/options/AnnotationProcessorsPanel.java b/java/compiler/impl/src/com/intellij/compiler/options/AnnotationProcessorsPanel.java
new file mode 100644
index 0000000..37a8ceb
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/options/AnnotationProcessorsPanel.java
@@ -0,0 +1,390 @@
+/*
+ * 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 com.intellij.compiler.options;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.ShortcutSet;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.InputValidatorEx;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.Splitter;
+import com.intellij.openapi.ui.popup.JBPopup;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.ui.AnActionButton;
+import com.intellij.ui.ColoredTreeCellRenderer;
+import com.intellij.ui.IdeBorderFactory;
+import com.intellij.ui.ToolbarDecorator;
+import com.intellij.ui.awt.RelativePoint;
+import com.intellij.ui.components.JBList;
+import com.intellij.ui.treeStructure.Tree;
+import com.intellij.util.ui.EditableTreeModel;
+import com.intellij.util.ui.tree.TreeUtil;
+import org.jetbrains.jps.model.java.compiler.ProcessorConfigProfile;
+import org.jetbrains.jps.model.java.impl.compiler.ProcessorConfigProfileImpl;
+
+import javax.swing.*;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreePath;
+import java.awt.*;
+import java.awt.event.MouseEvent;
+import java.util.*;
+import java.util.List;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+@SuppressWarnings({"unchecked", "UseOfObsoleteCollectionType"})
+public class AnnotationProcessorsPanel extends JPanel {
+  private final ProcessorConfigProfile myDefaultProfile = new ProcessorConfigProfileImpl("");
+  private final List<ProcessorConfigProfile> myModuleProfiles = new ArrayList<ProcessorConfigProfile>();
+  private final Map<String, Module> myAllModulesMap = new HashMap<String, Module>();
+  private final Project myProject;
+  private final Tree myTree;
+  private final ProcessorProfilePanel myProfilePanel;
+  private ProcessorConfigProfile mySelectedProfile = null;
+
+  public AnnotationProcessorsPanel(Project project) {
+    super(new BorderLayout());
+    Splitter splitter = new Splitter(false, 0.3f);
+    add(splitter, BorderLayout.CENTER);
+    myProject = project;
+    for (Module module : ModuleManager.getInstance(project).getModules()) {
+      myAllModulesMap.put(module.getName(), module);
+    }
+    myTree = new Tree(new MyTreeModel());
+    myTree.setRootVisible(false);
+        final JPanel treePanel =
+          ToolbarDecorator.createDecorator(myTree).addExtraAction(new AnActionButton("Move to", AllIcons.Actions.Nextfile) {
+            @Override
+            public void actionPerformed(AnActionEvent e) {
+              final MyModuleNode node = (MyModuleNode)myTree.getSelectionPath().getLastPathComponent();
+              final TreePath[] selectedNodes = myTree.getSelectionPaths();
+              final ProcessorConfigProfile nodeProfile = ((ProfileNode)node.getParent()).myProfile;
+              final List<ProcessorConfigProfile> profiles = new ArrayList<ProcessorConfigProfile>();
+              profiles.add(myDefaultProfile);
+              for (ProcessorConfigProfile profile : myModuleProfiles) {
+                profiles.add(profile);
+              }
+              profiles.remove(nodeProfile);
+              final JBList list = new JBList(profiles);
+              final JBPopup popup = JBPopupFactory.getInstance().createListPopupBuilder(list)
+                .setTitle("Move to")
+                .setItemChoosenCallback(new Runnable() {
+                  @Override
+                  public void run() {
+                    final Object value = list.getSelectedValue();
+                    if (value instanceof ProcessorConfigProfile) {
+                      final ProcessorConfigProfile chosenProfile = (ProcessorConfigProfile)value;
+                      final Module toSelect = (Module)node.getUserObject();
+                      if (selectedNodes != null) {
+                        for (TreePath selectedNode : selectedNodes) {
+                          final Object node = selectedNode.getLastPathComponent();
+                          if (node instanceof MyModuleNode) {
+                            final Module module = (Module)((MyModuleNode)node).getUserObject();
+                            if (nodeProfile != myDefaultProfile) {
+                              nodeProfile.removeModuleName(module.getName());
+                            }
+                            if (chosenProfile != myDefaultProfile) {
+                              chosenProfile.addModuleName(module.getName());
+                            }
+                          }
+                        }
+                      }
+
+                      final RootNode root = (RootNode)myTree.getModel().getRoot();
+                      root.sync();
+                      final DefaultMutableTreeNode node = TreeUtil.findNodeWithObject(root, toSelect);
+                      if (node != null) {
+                        TreeUtil.selectNode(myTree, node);
+                      }
+                    }
+                  }
+                })
+                .createPopup();
+              RelativePoint point =
+                e.getInputEvent() instanceof MouseEvent ? getPreferredPopupPoint() : TreeUtil.getPointForSelection(myTree);
+              popup.show(point);
+            }
+
+            @Override
+            public ShortcutSet getShortcut() {
+              return ActionManager.getInstance().getAction("Move").getShortcutSet();
+            }
+
+            @Override
+            public boolean isEnabled() {
+              return myTree.getSelectionPath() != null
+                     && myTree.getSelectionPath().getLastPathComponent() instanceof MyModuleNode
+                     && !myModuleProfiles.isEmpty();
+            }
+          }).createPanel();
+    splitter.setFirstComponent(treePanel);
+    myTree.setCellRenderer(new MyCellRenderer());
+
+    myTree.addTreeSelectionListener(new TreeSelectionListener() {
+      @Override
+      public void valueChanged(TreeSelectionEvent e) {
+        final TreePath path = myTree.getSelectionPath();
+        if (path != null) {
+          Object node = path.getLastPathComponent();
+          if (node instanceof MyModuleNode) {
+            node = ((MyModuleNode)node).getParent();
+          }
+          if (node instanceof ProfileNode) {
+            final ProcessorConfigProfile nodeProfile = ((ProfileNode)node).myProfile;
+            final ProcessorConfigProfile selectedProfile = mySelectedProfile;
+            if (nodeProfile != selectedProfile) {
+              if (selectedProfile != null) {
+                myProfilePanel.saveTo(selectedProfile);
+              }
+              mySelectedProfile = nodeProfile;
+              myProfilePanel.setProfile(nodeProfile);
+            }
+          }
+        }
+      }
+    });
+    myProfilePanel = new ProcessorProfilePanel(project);
+    myProfilePanel.setBorder(IdeBorderFactory.createEmptyBorder(0, 6, 0, 0));
+    splitter.setSecondComponent(myProfilePanel);
+  }
+
+  public void initProfiles(ProcessorConfigProfile defaultProfile, Collection<ProcessorConfigProfile> moduleProfiles) {
+    myDefaultProfile.initFrom(defaultProfile);
+    myModuleProfiles.clear();
+    for (ProcessorConfigProfile profile : moduleProfiles) {
+      ProcessorConfigProfile copy = new ProcessorConfigProfileImpl("");
+      copy.initFrom(profile);
+      myModuleProfiles.add(copy);
+    }
+    final RootNode root = (RootNode)myTree.getModel().getRoot();
+    root.sync();
+    final DefaultMutableTreeNode node = TreeUtil.findNodeWithObject(root, myDefaultProfile);
+    if (node != null) {
+      TreeUtil.selectNode(myTree, node);
+    }
+
+  }
+
+  public ProcessorConfigProfile getDefaultProfile() {
+    final ProcessorConfigProfile selectedProfile = mySelectedProfile;
+    if (myDefaultProfile == selectedProfile) {
+      myProfilePanel.saveTo(selectedProfile);
+    }
+    return myDefaultProfile;
+  }
+
+  public List<ProcessorConfigProfile> getModuleProfiles() {
+    final ProcessorConfigProfile selectedProfile = mySelectedProfile;
+    if (myDefaultProfile != selectedProfile) {
+      myProfilePanel.saveTo(selectedProfile);
+    }
+    return myModuleProfiles;
+  }
+
+  private static void expand(JTree tree) {
+    int oldRowCount = 0;
+    do {
+      int rowCount = tree.getRowCount();
+      if (rowCount == oldRowCount) break;
+      oldRowCount = rowCount;
+      for (int i = 0; i < rowCount; i++) {
+        tree.expandRow(i);
+      }
+    }
+    while (true);
+  }
+
+  private class MyTreeModel extends DefaultTreeModel implements EditableTreeModel{
+    public MyTreeModel() {
+      super(new RootNode());
+    }
+
+    @Override
+    public TreePath addNode(TreePath parentOrNeighbour) {
+      final String newProfileName = Messages.showInputDialog(
+        myProject, "Profile name", "Create new profile", null, "",
+        new InputValidatorEx() {
+          @Override
+          public boolean checkInput(String inputString) {
+            if (StringUtil.isEmpty(inputString) ||
+                Comparing.equal(inputString, myDefaultProfile.getName())) {
+              return false;
+            }
+            for (ProcessorConfigProfile profile : myModuleProfiles) {
+              if (Comparing.equal(inputString, profile.getName())) {
+                return false;
+              }
+            }
+            return true;
+          }
+
+          @Override
+          public boolean canClose(String inputString) {
+            return checkInput(inputString);
+          }
+
+          @Override
+          public String getErrorText(String inputString) {
+            if (checkInput(inputString)) {
+              return null;
+            }
+            return StringUtil.isEmpty(inputString)
+                   ? "Profile name shouldn't be empty"
+                   : "Profile " + inputString + " already exists";
+          }
+        });
+      if (newProfileName != null) {
+        final ProcessorConfigProfile profile = new ProcessorConfigProfileImpl(newProfileName);
+        myModuleProfiles.add(profile);
+        ((DataSynchronizable)getRoot()).sync();
+        final DefaultMutableTreeNode object = TreeUtil.findNodeWithObject((DefaultMutableTreeNode)getRoot(), profile);
+        if (object != null) {
+          TreeUtil.selectNode(myTree, object);
+        }
+      }
+      return null;
+    }
+
+    @Override
+    public void removeNode(TreePath nodePath) {
+      Object node = nodePath.getLastPathComponent();
+      if (node instanceof ProfileNode) {
+        final ProcessorConfigProfile nodeProfile = ((ProfileNode)node).myProfile;
+        if (nodeProfile != myDefaultProfile) {
+          if (mySelectedProfile == nodeProfile) {
+            mySelectedProfile = null;
+          }
+          myModuleProfiles.remove(nodeProfile);
+          ((DataSynchronizable)getRoot()).sync();
+          final DefaultMutableTreeNode object = TreeUtil.findNodeWithObject((DefaultMutableTreeNode)getRoot(), myDefaultProfile);
+          if (object != null) {
+            TreeUtil.selectNode(myTree, object);
+          }
+        }
+      }
+    }
+
+    @Override
+    public void moveNodeTo(TreePath parentOrNeighbour) {
+    }
+
+  }
+
+
+  private class RootNode extends DefaultMutableTreeNode implements DataSynchronizable {
+    @Override
+    public DataSynchronizable sync() {
+      final Vector newKids =  new Vector();
+      newKids.add(new ProfileNode(myDefaultProfile, this, true).sync());
+      for (ProcessorConfigProfile profile : myModuleProfiles) {
+        newKids.add(new ProfileNode(profile, this, false).sync());
+      }
+      children = newKids;
+      ((DefaultTreeModel)myTree.getModel()).reload();
+      expand(myTree);
+      return this;
+    }
+  }
+
+  private interface DataSynchronizable {
+    DataSynchronizable sync();
+  }
+
+  private class ProfileNode extends DefaultMutableTreeNode implements DataSynchronizable {
+    private final ProcessorConfigProfile myProfile;
+    private final boolean myIsDefault;
+
+    public ProfileNode(ProcessorConfigProfile profile, RootNode parent, boolean isDefault) {
+      super(profile);
+      setParent(parent);
+      myIsDefault = isDefault;
+      myProfile = profile;
+    }
+
+    @Override
+    public DataSynchronizable sync() {
+      final List<Module> nodeModules = new ArrayList<Module>();
+      if (myIsDefault) {
+        final Set<String> nonDefaultProfileModules = new HashSet<String>();
+        for (ProcessorConfigProfile profile : myModuleProfiles) {
+          nonDefaultProfileModules.addAll(profile.getModuleNames());
+        }
+        for (Map.Entry<String, Module> entry : myAllModulesMap.entrySet()) {
+          if (!nonDefaultProfileModules.contains(entry.getKey())) {
+            nodeModules.add(entry.getValue());
+          }
+        }
+      }
+      else {
+        for (String moduleName : myProfile.getModuleNames()) {
+          final Module module = myAllModulesMap.get(moduleName);
+          if (module != null) {
+            nodeModules.add(module);
+          }
+        }
+      }
+      Collections.sort(nodeModules, ModuleComparator.INSTANCE);
+      final Vector vector = new Vector();
+      for (Module module : nodeModules) {
+        vector.add(new MyModuleNode(module, this));
+      }
+      children = vector;
+      return this;
+    }
+
+  }
+
+  private static class MyModuleNode extends DefaultMutableTreeNode {
+    public MyModuleNode(Module module, ProfileNode parent) {
+      super(module);
+      setParent(parent);
+      setAllowsChildren(false);
+    }
+  }
+
+  private static class MyCellRenderer extends ColoredTreeCellRenderer {
+    @Override
+    public void customizeCellRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
+      if (value instanceof ProfileNode) {
+        append(((ProfileNode)value).myProfile.getName());
+      }
+      else if (value instanceof MyModuleNode) {
+        final Module module = (Module)((MyModuleNode)value).getUserObject();
+        setIcon(AllIcons.Nodes.Module);
+        append(module.getName());
+      }
+    }
+  }
+
+  private static class ModuleComparator implements Comparator<Module> {
+    static final ModuleComparator INSTANCE = new ModuleComparator();
+    @Override
+    public int compare(Module o1, Module o2) {
+      return o1.getName().compareTo(o2.getName());
+    }
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/options/ComparingUtils.java b/java/compiler/impl/src/com/intellij/compiler/options/ComparingUtils.java
new file mode 100644
index 0000000..bac1d24
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/options/ComparingUtils.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 com.intellij.compiler.options;
+
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.ui.RawCommandLineEditor;
+
+import javax.swing.*;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 30, 2004
+ */
+public class ComparingUtils {
+  public static boolean isModified(TextFieldWithBrowseButton field, String value) {
+    return !field.getText().equals(value);
+  }
+
+  public static boolean isModified(JCheckBox checkBox, boolean value) {
+    return checkBox.isSelected() != value;
+  }
+
+  public static boolean isModified(JTextField textField, int value) {
+    try {
+      int fieldValue = Integer.parseInt(textField.getText().trim());
+      return fieldValue != value;
+    }
+    catch(NumberFormatException e) {
+      return false;
+    }
+  }
+
+  public static boolean isModified(RawCommandLineEditor editor, String value) {
+    return !editor.getText().equals(value);
+  }
+
+  public static boolean isModified(JTextField textField, String value) {
+    return !textField.getText().equals(value);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/options/CompilerConfigurable.java b/java/compiler/impl/src/com/intellij/compiler/options/CompilerConfigurable.java
new file mode 100644
index 0000000..3cb2305
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/options/CompilerConfigurable.java
@@ -0,0 +1,103 @@
+/*
+ * 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 com.intellij.compiler.options;
+
+import com.intellij.compiler.CompilerSettingsFactory;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.SearchableConfigurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.NullableFunction;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class CompilerConfigurable implements SearchableConfigurable.Parent, Configurable.NoScroll {
+
+  private final Project myProject;
+  private final CompilerUIConfigurable myCompilerUIConfigurable;
+  private Configurable[] myKids;
+
+  public CompilerConfigurable(Project project) {
+    myProject = project;
+    myCompilerUIConfigurable = new CompilerUIConfigurable(myProject);
+  }
+
+  public String getDisplayName() {
+    return CompilerBundle.message("compiler.configurable.display.name");
+  }
+
+  public String getHelpTopic() {
+    return "project.propCompiler";
+  }
+
+  @NotNull
+  public String getId() {
+    return getHelpTopic();
+  }
+
+  @Nullable
+  public Runnable enableSearch(String option) {
+    return null;
+  }
+
+  public JComponent createComponent() {
+    return myCompilerUIConfigurable.createComponent();
+  }
+
+  public boolean hasOwnContent() {
+    return true;
+  }
+
+  public boolean isVisible() {
+    return true;
+  }
+
+  public boolean isModified() {
+    return myCompilerUIConfigurable.isModified();
+  }
+
+  public void apply() throws ConfigurationException {
+    myCompilerUIConfigurable.apply();
+  }
+
+  public void reset() {
+    myCompilerUIConfigurable.reset();
+  }
+
+  public void disposeUIResources() {
+    myCompilerUIConfigurable.disposeUIResources();
+  }
+
+  public Configurable[] getConfigurables() {
+    if (myKids == null) {
+      final CompilerSettingsFactory[] factories = Extensions.getExtensions(CompilerSettingsFactory.EP_NAME, myProject);
+      myKids = ContainerUtil.mapNotNull(factories, new NullableFunction<CompilerSettingsFactory, Configurable>() {
+        @Nullable
+        @Override
+        public Configurable fun(CompilerSettingsFactory factory) {
+            return factory.create(myProject);
+        }
+      }, new Configurable[0]);
+    }
+
+    return myKids;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/options/CompilerOptionsPanel.form b/java/compiler/impl/src/com/intellij/compiler/options/CompilerOptionsPanel.form
new file mode 100644
index 0000000..729a320
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/options/CompilerOptionsPanel.form
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.compiler.options.CompilerUIConfigurable">
+  <grid id="1663f" binding="myPanel" layout-manager="GridLayoutManager" row-count="10" 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>
+      <xy x="28" y="24" width="883" height="379"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <vspacer id="67edf">
+        <constraints>
+          <grid row="9" column="0" row-span="1" col-span="3" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+        </constraints>
+      </vspacer>
+      <grid id="b341d" layout-manager="GridLayoutManager" row-count="2" 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="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="b0748" 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="messages/CompilerBundle" key="label.option.resource.patterns.text"/>
+            </properties>
+          </component>
+          <component id="263f1" class="javax.swing.JTextField" binding="myResourcePatternsField">
+            <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>
+              <focusable value="true"/>
+              <margin top="0" left="2" bottom="0" right="0"/>
+              <text value="" noi18n="true"/>
+            </properties>
+          </component>
+          <component id="78174" class="com.intellij.ui.components.JBLabel" binding="myPatternLegendLabel">
+            <constraints>
+              <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="8" fill="0" indent="1" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <componentStyle value="SMALL"/>
+              <fontColor value="BRIGHTER"/>
+              <text value="label"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <component id="889a8" class="javax.swing.JCheckBox" binding="myCbClearOutputDirectory">
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text resource-bundle="messages/CompilerBundle" key="label.option.clear.output.directory.on.rebuild"/>
+        </properties>
+      </component>
+      <component id="366e" class="javax.swing.JCheckBox" binding="myCbAssertNotNull">
+        <constraints>
+          <grid row="2" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text resource-bundle="messages/CompilerBundle" key="add.notnull.assertions"/>
+        </properties>
+      </component>
+      <component id="348ee" class="javax.swing.JCheckBox" binding="myCbAutoShowFirstError">
+        <constraints>
+          <grid row="3" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text resource-bundle="messages/CompilerBundle" key="label.option.autoshow.first.error"/>
+        </properties>
+      </component>
+      <component id="ce617" class="javax.swing.JCheckBox" binding="myCbUseExternalBuild" default-binding="true">
+        <constraints>
+          <grid row="4" 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 value="Use external build"/>
+        </properties>
+      </component>
+      <component id="b9b2d" class="javax.swing.JCheckBox" binding="myCbEnableAutomake">
+        <constraints>
+          <grid row="5" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="2" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="Make project automatically"/>
+        </properties>
+      </component>
+      <component id="17126" class="javax.swing.JLabel" binding="myHeapSizeLabel">
+        <constraints>
+          <grid row="7" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="2" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="Compiler process heap size (Mbytes):"/>
+        </properties>
+      </component>
+      <component id="a28b8" class="javax.swing.JTextField" binding="myHeapSizeField">
+        <constraints>
+          <grid row="7" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="6" anchor="8" fill="0" indent="0" use-parent-layout="false">
+            <preferred-size width="50" height="-1"/>
+          </grid>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="5b86a" class="javax.swing.JLabel" binding="myVMOptionsLabel">
+        <constraints>
+          <grid row="8" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="2" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="Additional compiler process VM options:"/>
+        </properties>
+      </component>
+      <component id="b5547" class="javax.swing.JTextField" binding="myVMOptionsField">
+        <constraints>
+          <grid row="8" column="1" row-span="1" col-span="2" 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="ba694" class="javax.swing.JCheckBox" binding="myCbParallelCompilation">
+        <constraints>
+          <grid row="6" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="2" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="Compile independent modules in parallel"/>
+        </properties>
+      </component>
+      <component id="91979" class="javax.swing.JLabel">
+        <constraints>
+          <grid row="6" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="(may require larger heap size)"/>
+        </properties>
+      </component>
+      <component id="732b1" class="javax.swing.JLabel">
+        <constraints>
+          <grid row="5" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="(only works while not running / debugging)"/>
+        </properties>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java b/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java
new file mode 100644
index 0000000..21863bd
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java
@@ -0,0 +1,216 @@
+/*
+ * 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 com.intellij.compiler.options;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.CompilerConfigurationImpl;
+import com.intellij.compiler.CompilerWorkspaceConfiguration;
+import com.intellij.compiler.MalformedPatternException;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.SearchableConfigurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.Gray;
+import com.intellij.ui.JBColor;
+import com.intellij.ui.components.JBLabel;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public class CompilerUIConfigurable implements SearchableConfigurable, Configurable.NoScroll {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.options.CompilerUIConfigurable");
+  private JPanel myPanel;
+  private final Project myProject;
+
+  private JTextField myResourcePatternsField;
+  private JCheckBox myCbClearOutputDirectory;
+  private JCheckBox myCbAssertNotNull;
+  private JBLabel myPatternLegendLabel;
+  private JCheckBox myCbAutoShowFirstError;
+  private JCheckBox myCbUseExternalBuild;
+  private JCheckBox myCbEnableAutomake;
+  private JCheckBox myCbParallelCompilation;
+  private JTextField myHeapSizeField;
+  private JTextField myVMOptionsField;
+  private JLabel myHeapSizeLabel;
+  private JLabel myVMOptionsLabel;
+
+  public CompilerUIConfigurable(final Project project) {
+    myProject = project;
+
+    myPatternLegendLabel.setText("<html><body>" +
+                                 "Use <b>;</b> to separate patterns and <b>!</b> to negate a pattern. " +
+                                 "Accepted wildcards: <b>?</b> &mdash; exactly one symbol; <b>*</b> &mdash; zero or more symbols; " +
+                                 "<b>/</b> &mdash; path separator; <b>/**/</b> &mdash; any number of directories; " +
+                                 "<i>&lt;dir_name&gt;</i>:<i>&lt;pattern&gt;</i> &mdash; restrict to source roots with the specified name" +
+                                 "</body></html>");
+    myPatternLegendLabel.setForeground(new JBColor(Gray._50, Gray._130));
+    myCbUseExternalBuild.addItemListener(new ItemListener() {
+      @Override
+      public void itemStateChanged(ItemEvent e) {
+        updateExternalMakeOptionControls(myCbUseExternalBuild.isSelected());
+      }
+    });
+  }
+
+  public void reset() {
+
+    final CompilerConfigurationImpl configuration = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(myProject);
+    final CompilerWorkspaceConfiguration workspaceConfiguration = CompilerWorkspaceConfiguration.getInstance(myProject);
+    myCbAutoShowFirstError.setSelected(workspaceConfiguration.AUTO_SHOW_ERRORS_IN_EDITOR);
+    myCbClearOutputDirectory.setSelected(workspaceConfiguration.CLEAR_OUTPUT_DIRECTORY);
+    myCbAssertNotNull.setSelected(configuration.isAddNotNullAssertions());
+    myCbUseExternalBuild.setSelected(workspaceConfiguration.USE_COMPILE_SERVER);
+    myCbEnableAutomake.setSelected(workspaceConfiguration.MAKE_PROJECT_ON_SAVE);
+    myCbParallelCompilation.setSelected(workspaceConfiguration.PARALLEL_COMPILATION);
+    myHeapSizeField.setText(String.valueOf(workspaceConfiguration.COMPILER_PROCESS_HEAP_SIZE));
+    final String options = workspaceConfiguration.COMPILER_PROCESS_ADDITIONAL_VM_OPTIONS;
+    myVMOptionsField.setText(options == null? "" : options.trim());
+    updateExternalMakeOptionControls(myCbUseExternalBuild.isSelected());
+
+    configuration.convertPatterns();
+
+    myResourcePatternsField.setText(patternsToString(configuration.getResourceFilePatterns()));
+  }
+
+  private static String patternsToString(final String[] patterns) {
+    final StringBuilder extensionsString = new StringBuilder();
+    for (int idx = 0; idx < patterns.length; idx++) {
+      if (idx > 0) {
+        extensionsString.append(";");
+      }
+      extensionsString.append(patterns[idx]);
+    }
+    return extensionsString.toString();
+  }
+
+  public void apply() throws ConfigurationException {
+
+    CompilerConfigurationImpl configuration = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(myProject);
+    final CompilerWorkspaceConfiguration workspaceConfiguration = CompilerWorkspaceConfiguration.getInstance(myProject);
+    workspaceConfiguration.AUTO_SHOW_ERRORS_IN_EDITOR = myCbAutoShowFirstError.isSelected();
+    workspaceConfiguration.CLEAR_OUTPUT_DIRECTORY = myCbClearOutputDirectory.isSelected();
+    boolean wasUsingExternalMake = workspaceConfiguration.USE_COMPILE_SERVER;
+    workspaceConfiguration.USE_COMPILE_SERVER = myCbUseExternalBuild.isSelected();
+    workspaceConfiguration.MAKE_PROJECT_ON_SAVE = myCbEnableAutomake.isSelected();
+    workspaceConfiguration.PARALLEL_COMPILATION = myCbParallelCompilation.isSelected();
+    try {
+      workspaceConfiguration.COMPILER_PROCESS_HEAP_SIZE = Integer.parseInt(myHeapSizeField.getText().trim());
+    }
+    catch (NumberFormatException ignored) {
+      LOG.info(ignored);
+    }
+    workspaceConfiguration.COMPILER_PROCESS_ADDITIONAL_VM_OPTIONS = myVMOptionsField.getText().trim();
+
+    configuration.setAddNotNullAssertions(myCbAssertNotNull.isSelected());
+    configuration.removeResourceFilePatterns();
+    String extensionString = myResourcePatternsField.getText().trim();
+    applyResourcePatterns(extensionString, (CompilerConfigurationImpl)CompilerConfiguration.getInstance(myProject));
+    if (wasUsingExternalMake != workspaceConfiguration.USE_COMPILE_SERVER) {
+      myProject.getMessageBus().syncPublisher(ExternalBuildOptionListener.TOPIC).externalBuildOptionChanged(workspaceConfiguration.USE_COMPILE_SERVER);
+    }
+  }
+
+  private static void applyResourcePatterns(String extensionString, final CompilerConfigurationImpl configuration)
+    throws ConfigurationException {
+    StringTokenizer tokenizer = new StringTokenizer(extensionString, ";", false);
+    List<String[]> errors = new ArrayList<String[]>();
+
+    while (tokenizer.hasMoreTokens()) {
+      String namePattern = tokenizer.nextToken();
+      try {
+        configuration.addResourceFilePattern(namePattern);
+      }
+      catch (MalformedPatternException e) {
+        errors.add(new String[]{namePattern, e.getLocalizedMessage()});
+      }
+    }
+
+    if (errors.size() > 0) {
+      final StringBuilder pattersnsWithErrors = new StringBuilder();
+      for (final Object error : errors) {
+        String[] pair = (String[])error;
+        pattersnsWithErrors.append("\n");
+        pattersnsWithErrors.append(pair[0]);
+        pattersnsWithErrors.append(": ");
+        pattersnsWithErrors.append(pair[1]);
+      }
+
+      throw new ConfigurationException(
+        CompilerBundle.message("error.compiler.configurable.malformed.patterns", pattersnsWithErrors.toString()), CompilerBundle.message("bad.resource.patterns.dialog.title")
+      );
+    }
+  }
+
+  public boolean isModified() {
+    boolean isModified = false;
+    final CompilerWorkspaceConfiguration workspaceConfiguration = CompilerWorkspaceConfiguration.getInstance(myProject);
+    isModified |= ComparingUtils.isModified(myCbAutoShowFirstError, workspaceConfiguration.AUTO_SHOW_ERRORS_IN_EDITOR);
+    isModified |= ComparingUtils.isModified(myCbUseExternalBuild, workspaceConfiguration.USE_COMPILE_SERVER);
+    isModified |= ComparingUtils.isModified(myCbEnableAutomake, workspaceConfiguration.MAKE_PROJECT_ON_SAVE);
+    isModified |= ComparingUtils.isModified(myCbParallelCompilation, workspaceConfiguration.PARALLEL_COMPILATION);
+    isModified |= ComparingUtils.isModified(myHeapSizeField, workspaceConfiguration.COMPILER_PROCESS_HEAP_SIZE);
+    isModified |= ComparingUtils.isModified(myVMOptionsField, workspaceConfiguration.COMPILER_PROCESS_ADDITIONAL_VM_OPTIONS);
+
+    final CompilerConfigurationImpl compilerConfiguration = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(myProject);
+    isModified |= ComparingUtils.isModified(myCbAssertNotNull, compilerConfiguration.isAddNotNullAssertions());
+    isModified |= ComparingUtils.isModified(myCbClearOutputDirectory, workspaceConfiguration.CLEAR_OUTPUT_DIRECTORY);
+    isModified |= ComparingUtils.isModified(myResourcePatternsField, patternsToString(compilerConfiguration.getResourceFilePatterns()));
+
+    return isModified;
+  }
+
+  public String getDisplayName() {
+    return "General";
+  }
+
+  public String getHelpTopic() {
+    return null;
+  }
+
+  @NotNull
+  public String getId() {
+    return "compiler.general";
+  }
+
+  public Runnable enableSearch(String option) {
+    return null;
+  }
+
+  public JComponent createComponent() {
+    return myPanel;
+  }
+
+  public void disposeUIResources() {
+  }
+
+  private void updateExternalMakeOptionControls(boolean enabled) {
+    myCbEnableAutomake.setEnabled(enabled);
+    myCbParallelCompilation.setEnabled(enabled);
+    myHeapSizeField.setEnabled(enabled);
+    myVMOptionsField.setEnabled(enabled);
+    myHeapSizeLabel.setEnabled(enabled);
+    myVMOptionsLabel.setEnabled(enabled);
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/options/ExternalBuildOptionListener.java b/java/compiler/impl/src/com/intellij/compiler/options/ExternalBuildOptionListener.java
new file mode 100644
index 0000000..c3c92ad
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/options/ExternalBuildOptionListener.java
@@ -0,0 +1,29 @@
+/*
+ * 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 com.intellij.compiler.options;
+
+import com.intellij.util.messages.Topic;
+
+import java.util.EventListener;
+
+/**
+ * @author nik
+ */
+public interface ExternalBuildOptionListener extends EventListener {
+  Topic<ExternalBuildOptionListener> TOPIC = Topic.create("External build option", ExternalBuildOptionListener.class);
+
+  void externalBuildOptionChanged(boolean externalBuildEnabled);
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/options/JavaCompilersTab.form b/java/compiler/impl/src/com/intellij/compiler/options/JavaCompilersTab.form
new file mode 100644
index 0000000..40c0fc0
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/options/JavaCompilersTab.form
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.compiler.options.JavaCompilersTab">
+  <grid id="3d361" binding="myPanel" layout-manager="GridLayoutManager" row-count="4" 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="63" y="62" width="325" height="176"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <grid id="d786c" layout-manager="GridLayoutManager" row-count="1" 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="0" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="9fc5b" 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="99748"/>
+              <text resource-bundle="messages/CompilerBundle" key="option.use.compiler.text"/>
+            </properties>
+          </component>
+          <component id="99748" class="javax.swing.JComboBox" binding="myCompiler">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+          </component>
+        </children>
+      </grid>
+      <xy id="12adb" binding="myContentPanel" layout-manager="XYLayout" 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="1" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children/>
+      </xy>
+      <grid id="a6152" binding="myTargetOptionsPanel" 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="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children/>
+      </grid>
+      <vspacer id="7c703">
+        <constraints>
+          <grid row="3" 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>
+</form>
diff --git a/java/compiler/impl/src/com/intellij/compiler/options/JavaCompilersTab.java b/java/compiler/impl/src/com/intellij/compiler/options/JavaCompilersTab.java
new file mode 100644
index 0000000..09adaba
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/options/JavaCompilersTab.java
@@ -0,0 +1,170 @@
+/*
+ * 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 com.intellij.compiler.options;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.compiler.CompilerConfigurationImpl;
+import com.intellij.compiler.impl.javaCompiler.BackendCompiler;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.SearchableConfigurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.ui.ListCellRendererWrapper;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Vector;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 30, 2004
+ */
+public class JavaCompilersTab implements SearchableConfigurable, Configurable.NoScroll {
+  private JPanel myPanel;
+  private JPanel myContentPanel;
+  private JComboBox myCompiler;
+  private JPanel myTargetOptionsPanel;
+  private final CardLayout myCardLayout;
+
+  private final Project myProject;
+  private final BackendCompiler myDefaultCompiler;
+  private BackendCompiler mySelectedCompiler;
+  private final CompilerConfigurationImpl myCompilerConfiguration;
+  private final Collection<Configurable> myConfigurables;
+  private final TargetOptionsComponent myTargetLevelComponent;
+
+  public JavaCompilersTab(final Project project) {
+    this(project, ((CompilerConfigurationImpl)CompilerConfiguration.getInstance(project)).getRegisteredJavaCompilers(),
+         ((CompilerConfigurationImpl)CompilerConfiguration.getInstance(project)).getDefaultCompiler());
+  }
+
+  public JavaCompilersTab(final Project project, Collection<BackendCompiler> compilers, BackendCompiler defaultCompiler) {
+    myProject = project;
+    myDefaultCompiler = defaultCompiler;
+    myCompilerConfiguration = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(project);
+    myConfigurables = new ArrayList<Configurable>(compilers.size());
+
+    myCardLayout = new CardLayout();
+    myContentPanel.setLayout(myCardLayout);
+
+    myTargetOptionsPanel.setLayout(new BorderLayout());
+    myTargetLevelComponent = new TargetOptionsComponent(project);
+    myTargetOptionsPanel.add(myTargetLevelComponent, BorderLayout.CENTER);
+
+    for (BackendCompiler compiler : compilers) {
+      Configurable configurable = compiler.createConfigurable();
+      myConfigurables.add(configurable);
+
+      myContentPanel.add(configurable.createComponent(), compiler.getId());
+    }
+    myCompiler.setModel(new DefaultComboBoxModel(new Vector(compilers)));
+    myCompiler.setRenderer(new ListCellRendererWrapper<BackendCompiler>() {
+      @Override
+      public void customize(final JList list, final BackendCompiler value, final int index, final boolean selected, final boolean hasFocus) {
+        setText(value != null ? value.getPresentableName() : "");
+      }
+    });
+    myCompiler.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        final BackendCompiler compiler = (BackendCompiler)myCompiler.getSelectedItem();
+        if (compiler != null) {
+          selectCompiler(compiler);
+        }
+      }
+    });
+  }
+
+  public String getDisplayName() {
+    return CompilerBundle.message("java.compiler.description");
+  }
+
+  public String getHelpTopic() {
+    return "reference.projectsettings.compiler.javacompiler";
+  }
+
+  @NotNull
+  public String getId() {
+    return getHelpTopic();
+  }
+
+  public Runnable enableSearch(String option) {
+    return null;
+  }
+
+  public JComponent createComponent() {
+    return myPanel;
+  }
+
+  public boolean isModified() {
+    if (!Comparing.equal(mySelectedCompiler, myCompilerConfiguration.getDefaultCompiler())) {
+      return true;
+    }
+    for (Configurable configurable : myConfigurables) {
+      if (configurable.isModified()) {
+        return true;
+      }
+    }
+    if (!Comparing.equal(myTargetLevelComponent.getProjectBytecodeTarget(), myCompilerConfiguration.getProjectBytecodeTarget())) {
+      return true;
+    }
+    if (!Comparing.equal(myTargetLevelComponent.getModulesBytecodeTargetMap(), myCompilerConfiguration.getModulesBytecodeTargetMap())) {
+      return true;
+    }
+    return false;
+  }
+
+  public void apply() throws ConfigurationException {
+    for (Configurable configurable : myConfigurables) {
+      configurable.apply();
+    }
+    myCompilerConfiguration.setDefaultCompiler(mySelectedCompiler);
+    myCompilerConfiguration.setProjectBytecodeTarget(myTargetLevelComponent.getProjectBytecodeTarget());
+    myCompilerConfiguration.setModulesBytecodeTargetMap(myTargetLevelComponent.getModulesBytecodeTargetMap());
+
+    myTargetLevelComponent.setProjectBytecodeTargetLevel(myCompilerConfiguration.getProjectBytecodeTarget());
+    myTargetLevelComponent.setModuleTargetLevels(myCompilerConfiguration.getModulesBytecodeTargetMap());
+  }
+
+  public void reset() {
+    for (Configurable configurable : myConfigurables) {
+      configurable.reset();
+    }
+    selectCompiler(myCompilerConfiguration.getDefaultCompiler());
+    myTargetLevelComponent.setProjectBytecodeTargetLevel(myCompilerConfiguration.getProjectBytecodeTarget());
+    myTargetLevelComponent.setModuleTargetLevels(myCompilerConfiguration.getModulesBytecodeTargetMap());
+  }
+
+  public void disposeUIResources() {
+  }
+
+  private void selectCompiler(BackendCompiler compiler) {
+    if(compiler == null) {
+      compiler = myDefaultCompiler;
+    }
+    myCompiler.setSelectedItem(compiler);
+    mySelectedCompiler = compiler;
+    myCardLayout.show(myContentPanel, compiler.getId());
+    myContentPanel.revalidate();
+    myContentPanel.repaint();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/options/ProcessedModulesTable.java b/java/compiler/impl/src/com/intellij/compiler/options/ProcessedModulesTable.java
new file mode 100644
index 0000000..f95025b
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/options/ProcessedModulesTable.java
@@ -0,0 +1,439 @@
+/*
+ * 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 com.intellij.compiler.options;
+
+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.ui.configuration.ChooseModulesDialog;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.ui.SpeedSearchBase;
+import com.intellij.ui.SpeedSearchComparator;
+import com.intellij.ui.TableUtil;
+import com.intellij.ui.ToolbarDecorator;
+import com.intellij.ui.table.JBTable;
+import com.intellij.util.ui.EditableModel;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.table.*;
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+
+public class ProcessedModulesTable extends JPanel {
+  private JBTable myTable = null;
+  private MyTableModel myTableModel = null;
+
+  public ProcessedModulesTable(final Project project) {
+    super(new BorderLayout());
+
+    myTableModel = new MyTableModel(project);
+    myTable = new JBTable(myTableModel);
+    myTable.getEmptyText().setText("No modules configured");
+
+    //myTable.setShowGrid(false);
+    myTable.setIntercellSpacing(new Dimension(0, 0));
+    myTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
+    myTable.setColumnSelectionAllowed(false);
+
+    final TableColumnModel columnModel = myTable.getColumnModel();
+
+    final TableColumn dirNameColumn = columnModel.getColumn(myTableModel.DIRNAME_COLUMN_INDEX);
+    final String title = "Generated Sources Directory Name";
+    dirNameColumn.setHeaderValue(title);
+    final JTableHeader tableHeader = myTable.getTableHeader();
+    final FontMetrics metrics = tableHeader.getFontMetrics(tableHeader.getFont());
+    final int preferredWidth = metrics.stringWidth(title) + 12;
+    dirNameColumn.setPreferredWidth(preferredWidth);
+    dirNameColumn.setMaxWidth(preferredWidth + 20);
+    dirNameColumn.setCellRenderer(new MyElementColumnCellRenderer());
+
+    final TableColumn moduleColumn = columnModel.getColumn(myTableModel.ELEMENT_COLUMN_INDEX);
+    moduleColumn.setHeaderValue("Module");
+    moduleColumn.setCellRenderer(new MyElementColumnCellRenderer());
+
+    final JPanel panel = ToolbarDecorator.createDecorator(myTable)
+      .disableUpDownActions()
+      .setPreferredSize(new Dimension(100, 155))
+      .createPanel();
+    add(panel, BorderLayout.CENTER);
+
+    final SpeedSearchBase<JBTable> speedSearch = new SpeedSearchBase<JBTable>(myTable) {
+      public int getSelectedIndex() {
+        return myTable.getSelectedRow();
+      }
+
+      @Override
+      protected int convertIndexToModel(int viewIndex) {
+        return myTable.convertRowIndexToModel(viewIndex);
+      }
+
+      public Object[] getAllElements() {
+        final int count = myTableModel.getRowCount();
+        Object[] elements = new Object[count];
+        for (int idx = 0; idx < count; idx++) {
+          elements[idx] = myTableModel.getModuleAt(idx);
+        }
+        return elements;
+      }
+
+      public String getElementText(Object element) {
+        return ((Module)element).getName() + " (" + FileUtil.toSystemDependentName(((Module)element).getModuleFilePath()) + ")";
+      }
+
+      public void selectElement(Object element, String selectedText) {
+        final int count = myTableModel.getRowCount();
+        for (int row = 0; row < count; row++) {
+          if (element.equals(myTableModel.getModuleAt(row))) {
+            final int viewRow = myTable.convertRowIndexToView(row);
+            myTable.getSelectionModel().setSelectionInterval(viewRow, viewRow);
+            TableUtil.scrollSelectionToVisible(myTable);
+            break;
+          }
+        }
+      }
+    };
+    speedSearch.setComparator(new SpeedSearchComparator(false));
+  }
+
+  public void refresh() {
+    myTableModel.fireTableDataChanged();
+  }
+
+  public void refresh(Module element) {
+    final int row = myTableModel.getElementRow(element);
+    if (row >= 0) {
+      myTableModel.fireTableRowsUpdated(row, row);
+    }
+  }
+
+  private int[] mySavedSelection = null;
+  public void saveSelection() {
+    mySavedSelection = myTable.getSelectedRows();
+  }
+
+  public void restoreSelection() {
+    if (mySavedSelection != null) {
+      TableUtil.selectRows(myTable, mySavedSelection);
+      mySavedSelection = null;
+    }
+  }
+
+  public void addModule(Module element, String dirName) {
+    myTableModel.addElement(element, dirName);
+    selectRow(myTableModel.getRowCount() - 1);
+    myTable.requestFocus();
+  }
+
+  public void removeModule(Module element) {
+    final int elementRow = myTableModel.getElementRow(element);
+    if (elementRow < 0) {
+      return; // no such element
+    }
+    final boolean wasSelected = myTable.getSelectionModel().isSelectedIndex(elementRow);
+
+    myTableModel.removeElement(element);
+
+    if (wasSelected) {
+      final int rowCount = myTableModel.getRowCount();
+      if (rowCount > 0) {
+        selectRow(elementRow % rowCount);
+      }
+      else {
+        myTable.getSelectionModel().clearSelection();
+      }
+    }
+    myTable.requestFocus();
+  }
+
+  public void removeAllElements() {
+    myTableModel.removeAllElements();
+    myTable.getSelectionModel().clearSelection();
+  }
+
+  private void selectRow(final int row) {
+    myTable.getSelectionModel().setSelectionInterval(row, row);
+    myTable.scrollRectToVisible(myTable.getCellRect(row, 0, true));
+  }
+
+  @Nullable
+  public Module getSelectedElement() {
+    final int selectedRow = getSelectedElementRow();
+    return selectedRow < 0? null : myTableModel.getModuleAt(selectedRow);
+  }
+
+  public int getSelectedElementRow() {
+    return myTable.getSelectedRow();
+  }
+
+  public List<Module> getSelectedElements() {
+    final List<Module> elements = new ArrayList<Module>();
+    final int[] selectedRows = myTable.getSelectedRows();
+    for (int selectedRow : selectedRows) {
+      if (selectedRow < 0) {
+        continue;
+      }
+      elements.add(myTableModel.getModuleAt(selectedRow));
+    }
+    return elements;
+  }
+
+  public void selectElements(Collection<? extends Module> elements) {
+    if (elements.size() == 0) {
+      myTable.clearSelection();
+      return;
+    }
+    final int[] rows = getElementsRows(elements);
+    TableUtil.selectRows(myTable, rows);
+    TableUtil.scrollSelectionToVisible(myTable);
+    myTable.requestFocus();
+  }
+
+  private int[] getElementsRows(final Collection<? extends Module> elements) {
+    final int[] rows = new int[elements.size()];
+    int index = 0;
+    for (final Module element : elements) {
+      rows[index++] = myTableModel.getElementRow(element);
+    }
+    return rows;
+  }
+
+  public List<Pair<Module, String>> getAllModules() {
+    final int count = myTableModel.getRowCount();
+    List<Pair<Module, String>> elements = new ArrayList<Pair<Module, String>>();
+    for (int idx = 0; idx < count; idx++) {
+      final Module module = myTableModel.getModuleAt(idx);
+      elements.add(new Pair<Module, String>(module, myTableModel.getGenDirName(module)));
+    }
+    return elements;
+  }
+
+  public void sort(Comparator<Module> comparator) {
+    myTableModel.sort(comparator);
+  }
+
+  public void setEnabled(boolean enabled) {
+    super.setEnabled(enabled);
+    myTable.setRowSelectionAllowed(enabled);
+    myTableModel.fireTableDataChanged();
+  }
+
+  public void stopEditing() {
+    TableCellEditor editor = myTable.getCellEditor();
+    if (editor != null) {
+      editor.stopCellEditing();
+    }
+  }
+
+  public JComponent getComponent() {
+    return myTable;
+  }
+
+  public void clear() {
+    myTableModel.clear();
+  }
+
+  public int getElementCount() {
+    return myTableModel.getRowCount();
+  }
+
+  public Module getElementAt(int row) {
+    return myTableModel.getModuleAt(row);
+  }
+
+  private final class MyTableModel extends AbstractTableModel implements EditableModel {
+    private final List<Module> myElements = new ArrayList<Module>();
+    private final Map<Module, String> myDirNameMap = new HashMap<Module, String>();
+    public final int ELEMENT_COLUMN_INDEX = 0;
+    public final int DIRNAME_COLUMN_INDEX = 1;
+    private final Project myProject;
+
+    private MyTableModel(Project project) {
+      myProject = project;
+    }
+
+    public void sort(Comparator<Module> comparator) {
+      Collections.sort(myElements, comparator);
+      fireTableDataChanged();
+    }
+
+    public List<Module> getAllModules() {
+      return Collections.unmodifiableList(myElements);
+    }
+
+    public Module getModuleAt(int index) {
+      return myElements.get(index);
+    }
+
+    public String getGenDirName(Module module) {
+      return myDirNameMap.get(module);
+    }
+
+    void addElement(Module module, final String dirName) {
+      myElements.add(module);
+      if (dirName != null && dirName.length() > 0) {
+        myDirNameMap.put(module, dirName);
+      }
+      int row = myElements.size() - 1;
+      fireTableRowsInserted(row, row);
+    }
+
+    @Override
+    public void addRow() {
+      final Set<Module> projectModules = new HashSet<Module>(Arrays.asList(ModuleManager.getInstance(myProject).getModules()));
+      projectModules.removeAll(getAllModules());
+      final ChooseModulesDialog chooser = new ChooseModulesDialog(ProcessedModulesTable.this, new ArrayList<Module>(projectModules), "ChooseModule");
+      chooser.show();
+      if (chooser.isOK()) {
+        final List<Module> chosen = chooser.getChosenElements();
+        for (Module module : chosen) {
+          addElement(module, null);
+        }
+      }      
+    }
+
+    public void removeRow(int idx) {
+      final Module element = myElements.remove(idx);
+      myDirNameMap.remove(element);
+      fireTableRowsDeleted(idx, idx);
+    }
+
+    @Override
+    public void exchangeRows(int oldIndex, int newIndex) {
+    }
+
+    @Override
+    public boolean canExchangeRows(int oldIndex, int newIndex) {
+      return false;
+    }
+
+    public void removeElement(Module element) {
+      final boolean reallyRemoved = myElements.remove(element);
+      if (reallyRemoved) {
+        myDirNameMap.remove(element);
+        fireTableDataChanged();
+      }
+    }
+
+    public int getElementRow(Module element) {
+      return myElements.indexOf(element);
+    }
+
+    public void removeAllElements() {
+      myElements.clear();
+      fireTableDataChanged();
+    }
+
+    public int getRowCount() {
+      return myElements.size();
+    }
+
+    public int getColumnCount() {
+      return 2;
+    }
+
+    @Nullable
+    public Object getValueAt(int rowIndex, int columnIndex) {
+      Module element = myElements.get(rowIndex);
+      if (columnIndex == ELEMENT_COLUMN_INDEX) {
+        return element;
+      }
+      if (columnIndex == DIRNAME_COLUMN_INDEX) {
+        return myDirNameMap.get(element);
+      }
+      return null;
+    }
+
+    public void setValueAt(Object value, int rowIndex, int columnIndex) {
+      if (columnIndex == DIRNAME_COLUMN_INDEX) {
+        final Module module = myElements.get(rowIndex);
+        if (value != null) {
+          String dir = FileUtil.toSystemIndependentName((String)value);
+          while (dir.startsWith("/")) {
+            dir = dir.substring(1);
+          }
+          if (dir.length() > 0) {
+            myDirNameMap.put(module, dir);
+          }
+          else {
+            myDirNameMap.remove(module);
+          }
+        }
+        else {
+          myDirNameMap.remove(module);
+        }
+        fireTableRowsUpdated(rowIndex, rowIndex);
+      }
+    }
+
+    public Class getColumnClass(int columnIndex) {
+      if (columnIndex == DIRNAME_COLUMN_INDEX) {
+        return String.class;
+      }
+      return super.getColumnClass(columnIndex);
+    }
+
+    public boolean isCellEditable(int rowIndex, int columnIndex) {
+      if (!ProcessedModulesTable.this.isEnabled()) {
+        return false;
+      }
+      if (columnIndex == DIRNAME_COLUMN_INDEX) {
+        return true;
+      }
+      return false;
+    }
+
+    public void clear() {
+      myElements.clear();
+      myDirNameMap.clear();
+      fireTableDataChanged();
+    }
+  }
+
+  private class MyElementColumnCellRenderer extends DefaultTableCellRenderer {
+    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+      final Color color = UIUtil.getTableFocusCellBackground();
+      Component component;
+      final Module module = value instanceof Module? (Module)value : null;
+      try {
+        UIManager.put(UIUtil.TABLE_FOCUS_CELL_BACKGROUND_PROPERTY, table.getSelectionBackground());
+        component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+        if (module != null) {
+          setText(module.getName() + " (" + FileUtil.toSystemDependentName(module.getModuleFilePath()) + ")");
+        }
+        if (component instanceof JLabel) {
+          ((JLabel)component).setBorder(noFocusBorder);
+        }
+      }
+      finally {
+        UIManager.put(UIUtil.TABLE_FOCUS_CELL_BACKGROUND_PROPERTY, color);
+      }
+      component.setEnabled(ProcessedModulesTable.this.isEnabled());
+      if (component instanceof JLabel) {
+        final Icon icon = module != null ? ModuleType.get(module).getIcon() : null;
+        JLabel label = (JLabel)component;
+        label.setIcon(icon);
+        label.setDisabledIcon(icon);
+      }
+      component.setForeground(isSelected ? table.getSelectionForeground() : table.getForeground());
+      return component;
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/options/ProcessorProfilePanel.java b/java/compiler/impl/src/com/intellij/compiler/options/ProcessorProfilePanel.java
new file mode 100644
index 0000000..1553b84
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/options/ProcessorProfilePanel.java
@@ -0,0 +1,513 @@
+/*
+ * 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 com.intellij.compiler.options;
+
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.*;
+import com.intellij.ui.table.JBTable;
+import com.intellij.util.ui.EditableModel;
+import org.jetbrains.jps.model.java.compiler.ProcessorConfigProfile;
+
+import javax.swing.*;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.JTableHeader;
+import javax.swing.table.TableCellEditor;
+import javax.swing.table.TableModel;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.io.File;
+import java.util.*;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: 5/28/12
+ */
+public class ProcessorProfilePanel extends JPanel {
+  private final Project myProject;
+
+  private JRadioButton myRbClasspath;
+  private JRadioButton myRbProcessorsPath;
+  private TextFieldWithBrowseButton myProcessorPathField;
+  private JTextField myGeneratedProductionDirField;
+  private JTextField myGeneratedTestsDirField;
+  private JRadioButton myRbRelativeToOutputRoot;
+  private JRadioButton myRbRelativeToContentRoot;
+  private ProcessorTableModel myProcessorsModel;
+  private JCheckBox myCbEnableProcessing;
+  private JBTable myProcessorTable;
+  private JBTable myOptionsTable;
+  private JPanel myProcessorPanel;
+  private JPanel myOptionsPanel;
+  private OptionsTableModel myOptionsModel;
+  private JLabel myWarninglabel;
+  private JLabel myStoreGenSourcesLabel;
+  private JLabel myProductionLabel;
+  private JLabel myTestLabel;
+  private JPanel myProcessorTablePanel;
+  private JPanel myOptionsTablePanel;
+
+
+  public ProcessorProfilePanel(Project project) {
+    super(new GridBagLayout());
+    myProject = project;
+
+    myCbEnableProcessing = new JCheckBox("Enable annotation processing");
+
+    {
+      myRbClasspath = new JRadioButton("Obtain processors from project classpath");
+      myRbProcessorsPath = new JRadioButton("Processor path:");
+      ButtonGroup group = new ButtonGroup();
+      group.add(myRbClasspath);
+      group.add(myRbProcessorsPath);
+    }
+
+    {
+      myRbRelativeToContentRoot = new JRadioButton("Module content root");
+      myRbRelativeToOutputRoot = new JRadioButton("Module output directory");
+      final ButtonGroup group = new ButtonGroup();
+      group.add(myRbRelativeToContentRoot);
+      group.add(myRbRelativeToOutputRoot);
+    }
+
+    myProcessorPathField = new TextFieldWithBrowseButton(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createAllButJarContentsDescriptor();
+        final VirtualFile[] files = FileChooser.chooseFiles(descriptor, myProcessorPathField, myProject, null);
+        if (files.length > 0) {
+          final StringBuilder builder = new StringBuilder();
+          for (VirtualFile file : files) {
+            if (builder.length() > 0) {
+              builder.append(File.pathSeparator);
+            }
+            builder.append(FileUtil.toSystemDependentName(file.getPath()));
+          }
+          myProcessorPathField.setText(builder.toString());
+        }
+      }
+    });
+
+    myProcessorTablePanel = new JPanel(new BorderLayout());
+    myProcessorsModel = new ProcessorTableModel();
+    myProcessorTablePanel.setBorder(IdeBorderFactory.createTitledBorder("Annotation Processors", false));
+    myProcessorTable = new JBTable(myProcessorsModel);
+    myProcessorTable.getEmptyText().setText("Compiler will run all automatically discovered processors");
+    myProcessorPanel = createTablePanel(myProcessorTable);
+    myProcessorTablePanel.add(myProcessorPanel, BorderLayout.CENTER);
+
+    myOptionsTablePanel = new JPanel(new BorderLayout());
+    myOptionsModel = new OptionsTableModel();
+    myOptionsTablePanel.setBorder(IdeBorderFactory.createTitledBorder("Annotation Processor options", false));
+    myOptionsTable = new JBTable(myOptionsModel);
+    myOptionsTable.getEmptyText().setText("No processor-specific options configured");
+    myOptionsPanel = createTablePanel(myOptionsTable);
+    myOptionsTablePanel.add(myOptionsPanel, BorderLayout.CENTER);
+
+    myGeneratedProductionDirField = new JTextField();
+    myGeneratedTestsDirField = new JTextField();
+
+    myWarninglabel = new JLabel("<html>WARNING!<br>" +
+                                              /*"All source files located in the generated sources output directory WILL BE EXCLUDED from annotation processing. " +*/
+                                              "If option 'Clear output directory on rebuild' is enabled, " +
+                                              "the entire contents of directories where generated sources are stored WILL BE CLEARED on rebuild.</html>");
+    myWarninglabel.setFont(myWarninglabel.getFont().deriveFont(Font.BOLD));
+
+    add(myCbEnableProcessing,
+        new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+    add(myRbClasspath,
+        new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10, 0, 0, 0), 0, 0));
+    add(myRbProcessorsPath,
+        new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 0, 0, 0), 0, 0));
+    add(myProcessorPathField,
+        new GridBagConstraints(1, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 0, 0), 0, 0));
+
+    myStoreGenSourcesLabel = new JLabel("Store generated sources relative to: ");
+    add(myStoreGenSourcesLabel,
+        new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(15, 5, 0, 0), 0, 0));
+    add(myRbRelativeToOutputRoot,
+        new GridBagConstraints(1, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(15, 5, 0, 0), 0, 0));
+    add(myRbRelativeToContentRoot,
+        new GridBagConstraints(2, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(15, 5, 0, 0), 0, 0));
+
+    myProductionLabel = new JLabel("Production sources directory:");
+    add(myProductionLabel,
+        new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 5, 0, 0), 0, 0));
+    add(myGeneratedProductionDirField,
+        new GridBagConstraints(1, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 5, 0, 0), 0, 0));
+
+    myTestLabel = new JLabel("Test sources directory:");
+    add(myTestLabel,
+        new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 5, 0, 0), 0, 0));
+    add(myGeneratedTestsDirField,
+        new GridBagConstraints(1, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 5, 0, 0), 0, 0));
+
+    add(myProcessorTablePanel,
+        new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(10, 0, 0, 0), 0, 0));
+    add(myOptionsTablePanel,
+        new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(10, 0, 0, 0), 0, 0));
+    add(myWarninglabel,
+        new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 5, 0, 0), 0, 0));
+
+    myRbClasspath.addItemListener(new ItemListener() {
+      public void itemStateChanged(ItemEvent e) {
+        updateEnabledState();
+      }
+    });
+
+    myProcessorTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
+      public void valueChanged(ListSelectionEvent e) {
+        if (!e.getValueIsAdjusting()) {
+          updateEnabledState();
+        }
+      }
+    });
+
+    myCbEnableProcessing.addItemListener(new ItemListener() {
+      public void itemStateChanged(ItemEvent e) {
+        updateEnabledState();
+      }
+    });
+
+    updateEnabledState();
+
+  }
+
+  public void setProfile(ProcessorConfigProfile config) {
+    myCbEnableProcessing.setSelected(config.isEnabled());
+
+    (config.isObtainProcessorsFromClasspath()? myRbClasspath : myRbProcessorsPath).setSelected(true);
+    myProcessorPathField.setText(FileUtil.toSystemDependentName(config.getProcessorPath()));
+
+    final String productionDirName = config.getGeneratedSourcesDirectoryName(false);
+    myGeneratedProductionDirField.setText(productionDirName != null? productionDirName.trim() : "");
+    final String testsDirName = config.getGeneratedSourcesDirectoryName(true);
+    myGeneratedTestsDirField.setText(testsDirName != null? testsDirName.trim() : "");
+    if (config.isOutputRelativeToContentRoot()) {
+      myRbRelativeToContentRoot.setSelected(true);
+    }
+    else {
+      myRbRelativeToOutputRoot.setSelected(true);
+    }
+    myProcessorsModel.setProcessors(config.getProcessors());
+    myOptionsModel.setOptions(config.getProcessorOptions());
+
+    updateEnabledState();
+  }
+
+  public void saveTo(ProcessorConfigProfile profile) {
+    profile.setEnabled(myCbEnableProcessing.isSelected());
+    profile.setObtainProcessorsFromClasspath(myRbClasspath.isSelected());
+    profile.setProcessorPath(myProcessorPathField.getText().trim());
+
+    final String productionDir = myGeneratedProductionDirField.getText().trim();
+    profile.setGeneratedSourcesDirectoryName(StringUtil.isEmpty(productionDir)? null : productionDir, false);
+    final String testsDir = myGeneratedTestsDirField.getText().trim();
+    profile.setGeneratedSourcesDirectoryName(StringUtil.isEmpty(testsDir)? null : testsDir, true);
+
+    profile.setOutputRelativeToContentRoot(myRbRelativeToContentRoot.isSelected());
+
+    profile.clearProcessors();
+    for (String processor : myProcessorsModel.getProcessors()) {
+      profile.addProcessor(processor);
+    }
+    profile.clearProcessorOptions();
+    for (Map.Entry<String, String> entry : myOptionsModel.getOptions().entrySet()) {
+      profile.setOption(entry.getKey(), entry.getValue());
+    }
+  }
+
+  private static JPanel createTablePanel(final JBTable table) {
+    return ToolbarDecorator.createDecorator(table)
+      .disableUpAction()
+      .disableDownAction()
+      .setAddAction(new AnActionButtonRunnable() {
+        @Override
+        public void run(AnActionButton anActionButton) {
+          final TableCellEditor cellEditor = table.getCellEditor();
+          if (cellEditor != null) {
+            cellEditor.stopCellEditing();
+          }
+          final TableModel model = table.getModel();
+          ((EditableModel)model).addRow();
+          TableUtil.editCellAt(table, model.getRowCount() - 1, 0);
+        }
+      })
+      .createPanel();
+  }
+
+  private void updateEnabledState() {
+   final boolean enabled = myCbEnableProcessing.isSelected();
+    final boolean useProcessorpath = !myRbClasspath.isSelected();
+    myRbClasspath.setEnabled(enabled);
+    myRbProcessorsPath.setEnabled(enabled);
+    myProcessorPathField.setEnabled(enabled && useProcessorpath);
+    updateTable(myProcessorPanel, myProcessorTable, enabled);
+    updateTable(myOptionsPanel, myOptionsTable, enabled);
+    myGeneratedProductionDirField.setEnabled(enabled);
+    myGeneratedTestsDirField.setEnabled(enabled);
+    myRbRelativeToOutputRoot.setEnabled(enabled);
+    myRbRelativeToContentRoot.setEnabled(enabled);
+    myWarninglabel.setEnabled(enabled);
+    myStoreGenSourcesLabel.setEnabled(enabled);
+    myProductionLabel.setEnabled(enabled);
+    myTestLabel.setEnabled(enabled);
+    myProcessorTablePanel.setEnabled(enabled);
+    myOptionsTablePanel.setEnabled(enabled);
+  }
+
+  private static void updateTable(final JPanel tablePanel, final JBTable table, boolean enabled) {
+    final AnActionButton addButton = ToolbarDecorator.findAddButton(tablePanel);
+    if (addButton != null) {
+      addButton.setEnabled(enabled);
+    }
+    final AnActionButton removeButton = ToolbarDecorator.findRemoveButton(tablePanel);
+    if (removeButton != null) {
+      removeButton.setEnabled(enabled && table.getSelectedRow() >= 0);
+    }
+    table.setEnabled(enabled);
+    final JTableHeader header = table.getTableHeader();
+    if (header != null) {
+      header.repaint();
+    }
+  }
+
+  private static class OptionsTableModel extends AbstractTableModel implements EditableModel {
+    private final java.util.List<KeyValuePair> myRows = new ArrayList<KeyValuePair>();
+
+    public String getColumnName(int column) {
+      switch (column) {
+        case 0: return "Option Name";
+        case 1: return "Value";
+      }
+      return super.getColumnName(column);
+    }
+
+    public Class<?> getColumnClass(int columnIndex) {
+      return String.class;
+    }
+
+    public int getRowCount() {
+      return myRows.size();
+    }
+
+    public int getColumnCount() {
+      return 2;
+    }
+
+    public boolean isCellEditable(int rowIndex, int columnIndex) {
+      return columnIndex == 0 || columnIndex == 1;
+    }
+
+    public Object getValueAt(int rowIndex, int columnIndex) {
+      switch (columnIndex) {
+        case 0: return myRows.get(rowIndex).key;
+        case 1: return myRows.get(rowIndex).value;
+      }
+      return null;
+    }
+
+    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+      if (aValue != null) {
+        switch (columnIndex) {
+          case 0:
+            myRows.get(rowIndex).key = (String)aValue;
+            break;
+          case 1:
+            myRows.get(rowIndex).value = (String)aValue;
+            break;
+        }
+      }
+    }
+
+    public void removeRow(int idx) {
+      myRows.remove(idx);
+      fireTableRowsDeleted(idx, idx);
+    }
+
+    @Override
+    public void exchangeRows(int oldIndex, int newIndex) {
+    }
+
+    @Override
+    public boolean canExchangeRows(int oldIndex, int newIndex) {
+      return false;
+    }
+
+    public void addRow() {
+      myRows.add(new KeyValuePair());
+      final int index = myRows.size() - 1;
+      fireTableRowsInserted(index, index);
+    }
+
+    public void setOptions(Map<String, String> options) {
+      clear();
+      if (!options.isEmpty()) {
+        for (Map.Entry<String, String> entry : options.entrySet()) {
+          myRows.add(new KeyValuePair(entry.getKey(), entry.getValue()));
+        }
+        Collections.sort(myRows, new Comparator<KeyValuePair>() {
+          @Override
+          public int compare(KeyValuePair o1, KeyValuePair o2) {
+            return o1.key.compareToIgnoreCase(o2.key);
+          }
+        });
+        fireTableRowsInserted(0, options.size()-1);
+      }
+    }
+
+    public void clear() {
+      final int count = myRows.size();
+      if (count > 0) {
+        myRows.clear();
+        fireTableRowsDeleted(0, count-1);
+      }
+    }
+
+    public Map<String, String> getOptions() {
+      final Map<String, String> map = new java.util.HashMap<String, String>();
+      for (KeyValuePair pair : myRows) {
+        map.put(pair.key.trim(), pair.value.trim());
+      }
+      map.remove("");
+      return map;
+    }
+
+    private static final class KeyValuePair {
+      String key;
+      String value;
+
+      KeyValuePair() {
+        this("", "");
+      }
+
+      KeyValuePair(String key, String value) {
+        this.key = key;
+        this.value = value;
+      }
+    }
+  }
+
+  private static class ProcessorTableModel extends AbstractTableModel implements EditableModel {
+    private final List<String> myRows = new ArrayList<String>();
+
+    public String getColumnName(int column) {
+      switch (column) {
+        case 0: return "Processor FQ Name";
+      }
+      return super.getColumnName(column);
+    }
+
+    public Class<?> getColumnClass(int columnIndex) {
+      return String.class;
+    }
+
+    public int getRowCount() {
+      return myRows.size();
+    }
+
+    public int getColumnCount() {
+      return 1;
+    }
+
+    public boolean isCellEditable(int rowIndex, int columnIndex) {
+      return columnIndex == 0;
+    }
+
+    public Object getValueAt(int rowIndex, int columnIndex) {
+      switch (columnIndex) {
+        case 0: return myRows.get(rowIndex);
+      }
+      return null;
+    }
+
+    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+      if (aValue != null) {
+        switch (columnIndex) {
+          case 0:
+            myRows.set(rowIndex, (String)aValue);
+            break;
+        }
+      }
+    }
+
+    public void removeRow(int idx) {
+      myRows.remove(idx);
+      fireTableRowsDeleted(idx, idx);
+    }
+
+    @Override
+    public void exchangeRows(int oldIndex, int newIndex) {
+    }
+
+    @Override
+    public boolean canExchangeRows(int oldIndex, int newIndex) {
+      return false;
+    }
+
+    public void addRow() {
+      myRows.add("");
+      final int index = myRows.size() - 1;
+      fireTableRowsInserted(index, index);
+    }
+
+    public void setProcessors(Collection<String> processors) {
+      clear();
+      if (!processors.isEmpty()) {
+        for (String processor : processors) {
+          myRows.add(processor);
+        }
+        Collections.sort(myRows, new Comparator<String>() {
+          public int compare(String o1, String o2) {
+            return o1.compareToIgnoreCase(o2);
+          }
+        });
+        fireTableRowsInserted(0, processors.size()-1);
+      }
+    }
+
+    public void clear() {
+      final int count = myRows.size();
+      if (count > 0) {
+        myRows.clear();
+        fireTableRowsDeleted(0, count-1);
+      }
+    }
+
+    public Collection<String> getProcessors() {
+      final Set<String> set = new HashSet<String>();
+      for (String row : myRows) {
+        if (row != null) {
+          set.add(row.trim());
+        }
+      }
+      set.remove("");
+      return set;
+    }
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/options/RmicConfigurable.java b/java/compiler/impl/src/com/intellij/compiler/options/RmicConfigurable.java
new file mode 100644
index 0000000..96b4aab
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/options/RmicConfigurable.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 com.intellij.compiler.options;
+
+import com.intellij.compiler.impl.rmiCompiler.RmicConfiguration;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.SearchableConfigurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.ui.RawCommandLineEditor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.jps.model.java.compiler.RmicCompilerOptions;
+
+import javax.swing.*;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 30, 2004
+ */
+public class RmicConfigurable implements SearchableConfigurable, Configurable.NoScroll {
+  private JPanel myPanel;
+  private JCheckBox myCbEnabled;
+  private JCheckBox myCbGenerateIiopStubs;
+  private JCheckBox myCbDebuggingInfo;
+  private JCheckBox myCbGenerateNoWarnings;
+  private RawCommandLineEditor myAdditionalOptionsField;
+  private final RmicCompilerOptions myRmicSettings;
+  private JLabel myFieldLabel;
+
+  public RmicConfigurable(final Project project) {
+    myRmicSettings = RmicConfiguration.getOptions(project);
+    myCbEnabled.addItemListener(new ItemListener() {
+      public void itemStateChanged(ItemEvent e) {
+        setOptionsEnabled(e.getStateChange() == ItemEvent.SELECTED);
+      }
+    });
+    myAdditionalOptionsField.setDialogCaption(myFieldLabel.getText());
+  }
+
+  private void setOptionsEnabled(final boolean selected) {
+    myCbGenerateIiopStubs.setEnabled(selected);
+    myCbGenerateNoWarnings.setEnabled(selected);
+    myCbDebuggingInfo.setEnabled(selected);
+    myFieldLabel.setEnabled(selected);
+    myAdditionalOptionsField.setEnabled(selected);
+  }
+
+  public String getDisplayName() {
+    return CompilerBundle.message("rmi.compiler.description");
+  }
+
+  public String getHelpTopic() {
+    return "reference.projectsettings.compiler.rmicompiler";
+  }
+
+  @NotNull
+  public String getId() {
+    return getHelpTopic();
+  }
+
+  public Runnable enableSearch(String option) {
+    return null;
+  }
+
+  public JComponent createComponent() {
+    return myPanel;
+  }
+
+  public boolean isModified() {
+    boolean isModified = false;
+    isModified |= ComparingUtils.isModified(myCbEnabled, myRmicSettings.IS_EANABLED);
+    isModified |= ComparingUtils.isModified(myCbGenerateIiopStubs, myRmicSettings.GENERATE_IIOP_STUBS);
+    isModified |= ComparingUtils.isModified(myCbDebuggingInfo, myRmicSettings.DEBUGGING_INFO);
+    isModified |= ComparingUtils.isModified(myCbGenerateNoWarnings, myRmicSettings.GENERATE_NO_WARNINGS);
+    isModified |= ComparingUtils.isModified(myAdditionalOptionsField, myRmicSettings.ADDITIONAL_OPTIONS_STRING);
+    return isModified;
+  }
+
+  public void apply() throws ConfigurationException {
+    myRmicSettings.IS_EANABLED =  myCbEnabled.isSelected();
+    myRmicSettings.GENERATE_IIOP_STUBS =  myCbGenerateIiopStubs.isSelected();
+    myRmicSettings.DEBUGGING_INFO = myCbDebuggingInfo.isSelected();
+    myRmicSettings.GENERATE_NO_WARNINGS = myCbGenerateNoWarnings.isSelected();
+    myRmicSettings.ADDITIONAL_OPTIONS_STRING = myAdditionalOptionsField.getText();
+  }
+
+  public void reset() {
+    myCbEnabled.setSelected(myRmicSettings.IS_EANABLED);
+    setOptionsEnabled(myRmicSettings.IS_EANABLED);
+    myCbGenerateIiopStubs.setSelected(myRmicSettings.GENERATE_IIOP_STUBS);
+    myCbDebuggingInfo.setSelected(myRmicSettings.DEBUGGING_INFO);
+    myCbGenerateNoWarnings.setSelected(myRmicSettings.GENERATE_NO_WARNINGS);
+    myAdditionalOptionsField.setText(myRmicSettings.ADDITIONAL_OPTIONS_STRING);
+  }
+
+  public void disposeUIResources() {
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/options/RmicOptionsPanel.form b/java/compiler/impl/src/com/intellij/compiler/options/RmicOptionsPanel.form
new file mode 100644
index 0000000..a5a4aaf
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/options/RmicOptionsPanel.form
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.compiler.options.RmicConfigurable">
+  <grid id="49284" binding="myPanel" layout-manager="GridLayoutManager" row-count="2" 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="37" y="129" width="521" height="280"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <grid id="9b938" layout-manager="GridLayoutManager" row-count="4" 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="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="3c8c0" class="javax.swing.JCheckBox" binding="myCbEnabled">
+            <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="messages/CompilerBundle" key="rmic.option.enable.rmi.stubs"/>
+            </properties>
+          </component>
+          <component id="70faa" class="javax.swing.JCheckBox" binding="myCbGenerateIiopStubs">
+            <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="messages/CompilerBundle" key="rmic.option.generate.iiop.stubs"/>
+            </properties>
+          </component>
+          <component id="3d38f" class="javax.swing.JCheckBox" binding="myCbDebuggingInfo">
+            <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>
+              <selected value="false"/>
+              <text resource-bundle="messages/CompilerBundle" key="java.compiler.option.generate.debugging.info"/>
+            </properties>
+          </component>
+          <component id="90803" class="javax.swing.JCheckBox" binding="myCbGenerateNoWarnings">
+            <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="messages/CompilerBundle" key="java.compiler.option.generate.no.warnings"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <grid id="3f1e9" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="10" left="8" bottom="0" right="0"/>
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="f8a80" class="javax.swing.JLabel" binding="myFieldLabel">
+            <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="messages/CompilerBundle" key="java.compiler.option.additional.command.line.parameters"/>
+            </properties>
+          </component>
+          <component id="479c2" class="com.intellij.ui.RawCommandLineEditor" binding="myAdditionalOptionsField">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
+                <preferred-size width="150" height="-1"/>
+              </grid>
+            </constraints>
+            <properties/>
+          </component>
+        </children>
+      </grid>
+    </children>
+  </grid>
+</form>
diff --git a/java/compiler/impl/src/com/intellij/compiler/options/TargetOptionsComponent.java b/java/compiler/impl/src/com/intellij/compiler/options/TargetOptionsComponent.java
new file mode 100644
index 0000000..a4864c8
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/options/TargetOptionsComponent.java
@@ -0,0 +1,395 @@
+/*
+ * 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 com.intellij.compiler.options;
+
+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.ui.configuration.ChooseModulesDialog;
+import com.intellij.openapi.ui.ComboBox;
+import com.intellij.ui.*;
+import com.intellij.ui.table.JBTable;
+import com.intellij.util.ui.ItemRemovable;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.plaf.basic.BasicComboBoxEditor;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.TableColumn;
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: 5/9/12
+ */
+public class TargetOptionsComponent extends JPanel {
+  private static final String[] KNOWN_TARGETS = new String[] {"1.1", "1.2", "1.3","1.4","1.5", "1.6", "1.7", "1.8"};
+  private static final String COMPILER_DEFAULT = "JDK default";
+
+  private ComboBox myCbProjectTargetLevel;
+  private JBTable myTable;
+  private final Project myProject;
+
+  public TargetOptionsComponent(Project project) {
+    super(new GridBagLayout());
+    myProject = project;
+    //setBorder(BorderFactory.createTitledBorder("Bytecode target level"));
+    myCbProjectTargetLevel = createTargetOptionsCombo();
+
+    myTable = new JBTable(new TargetLevelTableModel());
+    myTable.setRowHeight(22);
+    myTable.getEmptyText().setText("All modules will be compiled with project bytecode version");
+
+    final TableColumn moduleColumn = myTable.getColumnModel().getColumn(0);
+    moduleColumn.setHeaderValue("Module");
+    moduleColumn.setCellRenderer(new ModuleCellRenderer());
+
+    final TableColumn targetLevelColumn = myTable.getColumnModel().getColumn(1);
+    final String columnTitle = "Target bytecode version";
+    targetLevelColumn.setHeaderValue(columnTitle);
+    targetLevelColumn.setCellEditor(new TargetLevelCellEditor());
+    targetLevelColumn.setCellRenderer(new TargetLevelCellRenderer());
+    final int width = myTable.getFontMetrics(myTable.getFont()).stringWidth(columnTitle) + 10;
+    targetLevelColumn.setPreferredWidth(width);
+    targetLevelColumn.setMinWidth(width);
+    targetLevelColumn.setMaxWidth(width);
+
+    add(new JLabel("Project bytecode version (leave blank for jdk default): "),
+        constraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NONE));
+    add(myCbProjectTargetLevel, constraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.NONE));
+    add(new JLabel("Per-module bytecode version:"), constraints(0, 1, 2, 1, 1.0, 0.0, GridBagConstraints.NONE));
+    final JPanel tableComp = ToolbarDecorator.createDecorator(myTable)
+      .disableUpAction()
+      .disableDownAction()
+      .setAddAction(new AnActionButtonRunnable() {
+        @Override
+        public void run(AnActionButton anActionButton) {
+          addModules();
+        }
+      })
+      .setRemoveAction(new AnActionButtonRunnable() {
+        @Override
+        public void run(AnActionButton anActionButton) {
+          removeSelectedModules();
+        }
+      }).createPanel();
+
+    tableComp.setPreferredSize(new Dimension(myTable.getWidth(), 150));
+    add(tableComp, constraints(0, 2, 2, 1, 1.0, 1.0, GridBagConstraints.BOTH));
+  }
+
+  private void removeSelectedModules() {
+    final int[] rows = myTable.getSelectedRows();
+    if (rows.length > 0) {
+      TableUtil.removeSelectedItems(myTable);
+    }
+  }
+
+  private void addModules() {
+    final TargetLevelTableModel model = (TargetLevelTableModel)myTable.getModel();
+    final List<Module> items = new ArrayList<Module>(Arrays.asList(ModuleManager.getInstance(myProject).getModules()));
+    Set<Module> alreadyAdded = new HashSet<Module>();
+    for (TargetLevelTableModel.Item item : model.getItems()) {
+      alreadyAdded.add(item.module);
+    }
+    for (Iterator<Module> it = items.iterator(); it.hasNext(); ) {
+      Module module = it.next();
+      if (alreadyAdded.contains(module)) {
+        it.remove();
+      }
+    }
+    Collections.sort(items, new Comparator<Module>() {
+      @Override
+      public int compare(Module o1, Module o2) {
+        return o1.getName().compareTo(o2.getName());
+      }
+    });
+    final ChooseModulesDialog chooser = new ChooseModulesDialog(this, items, "Choose module");
+    chooser.show();
+    final List<Module> elements = chooser.getChosenElements();
+    if (!elements.isEmpty()) {
+      model.addItems(elements);
+    }
+  }
+
+  public void setProjectBytecodeTargetLevel(String level) {
+    myCbProjectTargetLevel.setSelectedItem(level == null? "" : level);
+  }
+
+  @Nullable
+  public String getProjectBytecodeTarget() {
+    final String item = ((String)myCbProjectTargetLevel.getSelectedItem()).trim();
+    return "".equals(item)? null : item;
+  }
+
+  public Map<String, String> getModulesBytecodeTargetMap() {
+    TargetLevelTableModel model = (TargetLevelTableModel)myTable.getModel();
+    final Map<String, String> map = new HashMap<String, String>();
+    for (TargetLevelTableModel.Item item : model.getItems()) {
+      map.put(item.module.getName(), item.targetLevel);
+    }
+    return map;
+  }
+
+  public void setModuleTargetLevels(Map<String, String> moduleLevels) {
+    final Map<Module, String> map = new HashMap<Module, String>();
+    for (Module module : ModuleManager.getInstance(myProject).getModules()) {
+      final String target = moduleLevels.get(module.getName());
+      if (target != null) {
+        map.put(module, target);
+      }
+    }
+    ((TargetLevelTableModel)myTable.getModel()).setItems(map);
+  }
+
+  private static GridBagConstraints constraints(final int gridx, final int gridy, final int gridwidth, final int gridheight, final double weightx, final double weighty, final int fill) {
+    return new GridBagConstraints(gridx, gridy, gridwidth, gridheight, weightx, weighty, GridBagConstraints.WEST, fill, new Insets(5, 5, 0, 0), 0, 0);
+  }
+
+  private static final class TargetLevelTableModel extends AbstractTableModel implements ItemRemovable{
+    private final List<Item> myItems = new ArrayList<Item>();
+    @Override
+    public int getRowCount() {
+      return myItems.size();
+    }
+
+    @Override
+    public int getColumnCount() {
+      return 2;
+    }
+
+    @Override
+    public boolean isCellEditable(int rowIndex, int columnIndex) {
+      return columnIndex != 0;
+    }
+
+    @Override
+    public Object getValueAt(int rowIndex, int columnIndex) {
+      final Item item = myItems.get(rowIndex);
+      return columnIndex == 0? item.module : item.targetLevel;
+    }
+
+    @Override
+    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+      final Item item = myItems.get(rowIndex);
+      item.targetLevel = ((String)aValue).trim();
+      fireTableCellUpdated(rowIndex, columnIndex);
+    }
+
+    //public void addItem(Module module)  {
+    //  final int size = myItems.size();
+    //  myItems.add(new Item(module.getName()));
+    //  fireTableRowsInserted(size, size);
+    //}
+
+    public void addItems(Collection<Module> modules)  {
+      for (Module module : modules) {
+        myItems.add(new Item(module));
+      }
+      sorItems();
+      fireTableDataChanged();
+    }
+
+    private void sorItems() {
+      Collections.sort(myItems, new Comparator<Item>() {
+        @Override
+        public int compare(Item o1, Item o2) {
+          return o1.module.getName().compareTo(o2.module.getName());
+        }
+      });
+    }
+
+    public List<Item> getItems() {
+      return myItems;
+    }
+
+    @Override
+    public void removeRow(int idx) {
+      myItems.remove(idx);
+    }
+
+    public void setItems(Map<Module, String> items) {
+      myItems.clear();
+      for (Map.Entry<Module, String> entry : items.entrySet()) {
+        myItems.add(new Item(entry.getKey(), entry.getValue()));
+      }
+      sorItems();
+      fireTableDataChanged();
+    }
+
+    private static final class Item {
+      final Module module;
+      String targetLevel = "";
+
+      Item(Module module) {
+        this.module = module;
+      }
+
+      Item(Module module, String targetLevel) {
+        this.module = module;
+        this.targetLevel = targetLevel;
+      }
+    }
+  }
+
+  private static final class TargetLevelComboboxModel extends AbstractListModel implements ComboBoxModel{
+
+    private final List<String> myOptions = new ArrayList<String>();
+    private String mySelectedItem = "";
+
+    TargetLevelComboboxModel() {
+      //myOptions.add("");
+      for (int i = KNOWN_TARGETS.length - 1; i >= 0; i--) {
+        myOptions.add(KNOWN_TARGETS[i]);
+      }
+    }
+
+    @Override
+    public int getSize() {
+      return myOptions.size();
+    }
+
+    @Override
+    public void setSelectedItem(Object anItem) {
+      mySelectedItem = toModelItem((String)anItem);
+      fireContentsChanged(this, 0, myOptions.size());
+    }
+
+    @Override
+    public Object getSelectedItem() {
+      return mySelectedItem;
+    }
+
+    @Override
+    public Object getElementAt(int index) {
+      return myOptions.get(index);
+    }
+
+    private String toModelItem(String item) {
+      item = item.trim();
+      for (String option : myOptions) {
+        if (option.equals(item)) {
+          return option;
+        }
+      }
+      return item;
+    }
+  }
+
+  private static ComboBox createTargetOptionsCombo() {
+    final ComboBox combo = new ComboBox(new TargetLevelComboboxModel());
+    //combo.setRenderer(new DefaultListCellRenderer() {
+    //  @Override
+    //  public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+    //    try {
+    //      return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+    //    }
+    //    finally {
+    //      //if ("".equals(value)) {
+    //      //  setText(COMPILER_DEFAULT);
+    //      //}
+    //    }
+    //  }
+    //});
+    combo.setEditable(true);
+    combo.setEditor(new BasicComboBoxEditor() {
+      @Override
+      protected JTextField createEditorComponent() {
+        return new HintTextField(COMPILER_DEFAULT, 10);
+      }
+    });
+    return combo;
+  }
+
+  private static class ModuleCellRenderer extends DefaultTableCellRenderer {
+    @Override
+    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+      try {
+        return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+      }
+      finally {
+        final Module module = (Module)value;
+        setText(module.getName());
+        setIcon(ModuleType.get(module).getIcon());
+      }
+    }
+  }
+
+  private static class TargetLevelCellEditor extends DefaultCellEditor {
+    private TargetLevelCellEditor() {
+      super(createTargetOptionsCombo());
+      setClickCountToStart(0);
+    }
+  }
+
+  private static class TargetLevelCellRenderer extends DefaultTableCellRenderer {
+    @Override
+    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+      final Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+      if (component instanceof JLabel) {
+        final JLabel comp = (JLabel)component;
+        comp.setHorizontalAlignment(SwingConstants.CENTER);
+        if ("".equals(value)) {
+          comp.setForeground(JBColor.GRAY);
+          comp.setText(COMPILER_DEFAULT);
+        }
+        else {
+          comp.setForeground(table.getForeground());
+        }
+      }
+      return component;
+    }
+  }
+
+  static class HintTextField extends JTextField {
+    private final char[] myHint;
+
+    public HintTextField(final String hint) {
+      this(hint, 0);
+    }
+
+    public HintTextField(final String hint, final int columns) {
+      super(hint, columns);
+      myHint = hint.toCharArray();
+    }
+
+    @Override
+    protected void paintComponent(Graphics g) {
+      super.paintComponent(g);
+      final boolean isFocused = isFocusOwner();
+      if (!isFocused && getText().isEmpty()) {
+        final Color oldColor = g.getColor();
+        final Font oldFont = g.getFont();
+        try {
+          g.setColor(JBColor.GRAY);
+          //g.setFont(oldFont.deriveFont(Font.ITALIC));
+          final FontMetrics metrics = g.getFontMetrics();
+          int x = Math.abs(getWidth() - metrics.charsWidth(myHint, 0, myHint.length)) / 2;
+          int y = Math.abs(getHeight() - metrics.getHeight()) / 2 + metrics.getAscent();
+          g.drawChars(myHint, 0, myHint.length, x, y);
+        }
+        finally {
+          g.setColor(oldColor);
+          g.setFont(oldFont);
+        }
+      }
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/options/ValidationConfiguration.java b/java/compiler/impl/src/com/intellij/compiler/options/ValidationConfiguration.java
new file mode 100644
index 0000000..2b8a43e
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/options/ValidationConfiguration.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 com.intellij.compiler.options;
+
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.Compiler;
+import com.intellij.openapi.compiler.options.ExcludedEntriesConfiguration;
+import com.intellij.openapi.components.*;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Dmitry Avdeev
+ */
+@State(
+  name = "ValidationConfiguration",
+  storages = {
+    @Storage( file = StoragePathMacros.WORKSPACE_FILE),
+    @Storage( file = StoragePathMacros.PROJECT_CONFIG_DIR + "/validation.xml", scheme = StorageScheme.DIRECTORY_BASED)
+  }
+)
+public class ValidationConfiguration implements PersistentStateComponent<ValidationConfiguration> {
+
+  public boolean VALIDATE_ON_BUILD = false;
+  public Map<String, Boolean> VALIDATORS = new HashMap<String, Boolean>();
+
+  public static boolean shouldValidate(Compiler validator, CompileContext context) {
+    ValidationConfiguration configuration = getInstance(context.getProject());
+    return (configuration.VALIDATE_ON_BUILD) && configuration.isSelected(validator);
+  }
+
+  public boolean isSelected(Compiler validator) {
+    return isSelected(validator.getDescription());
+  }
+
+  public boolean isSelected(String validatorDescription) {
+    final Boolean selected = VALIDATORS.get(validatorDescription);
+    return selected == null || selected.booleanValue();
+  }
+
+  public void setSelected(Compiler validator, boolean selected) {
+    setSelected(validator.getDescription(), selected);
+  }
+
+  public void setSelected(String validatorDescription, boolean selected) {
+    VALIDATORS.put(validatorDescription, selected);
+  }
+
+  public static ValidationConfiguration getInstance(Project project) {
+    return ServiceManager.getService(project, ValidationConfiguration.class);
+  }
+
+  public static ExcludedEntriesConfiguration getExcludedEntriesConfiguration(Project project) {
+    return ServiceManager.getService(project, ExcludedFromValidationConfiguration.class);
+  }
+
+  public ValidationConfiguration getState() {
+    return this;
+  }
+
+  public void loadState(final ValidationConfiguration state) {
+    XmlSerializerUtil.copyBean(state, this);
+  }
+
+  @State(
+      name = "ExcludeFromValidation",
+      storages = {
+          @Storage( file = StoragePathMacros.PROJECT_FILE),
+          @Storage( file = StoragePathMacros.PROJECT_CONFIG_DIR + "/excludeFromValidation.xml", scheme = StorageScheme.DIRECTORY_BASED)
+      }
+  )
+  public static class ExcludedFromValidationConfiguration extends ExcludedEntriesConfiguration {}
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/progress/CompilerTask.java b/java/compiler/impl/src/com/intellij/compiler/progress/CompilerTask.java
new file mode 100644
index 0000000..527b627
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/progress/CompilerTask.java
@@ -0,0 +1,607 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author: Eugene Zhuravlev
+ * Date: Jan 22, 2003
+ * Time: 2:25:31 PM
+ */
+package com.intellij.compiler.progress;
+
+import com.intellij.compiler.CompilerManagerImpl;
+import com.intellij.compiler.CompilerMessageImpl;
+import com.intellij.compiler.impl.CompilerErrorTreeView;
+import com.intellij.ide.errorTreeView.NewErrorTreeViewPanel;
+import com.intellij.ide.errorTreeView.impl.ErrorTreeViewConfiguration;
+import com.intellij.ide.impl.ProjectUtil;
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.compiler.*;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.progress.EmptyProgressIndicator;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.progress.util.ProgressIndicatorBase;
+import com.intellij.openapi.project.DumbModeAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.project.ProjectManagerListener;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.wm.AppIconScheme;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.openapi.wm.ToolWindowId;
+import com.intellij.openapi.wm.ToolWindowManager;
+import com.intellij.openapi.wm.ex.ProgressIndicatorEx;
+import com.intellij.pom.Navigatable;
+import com.intellij.problems.WolfTheProblemSolver;
+import com.intellij.ui.AppIcon;
+import com.intellij.ui.content.*;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.messages.MessageBusConnection;
+import com.intellij.util.ui.MessageCategory;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class CompilerTask extends Task.Backgroundable {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.progress.CompilerProgressIndicator");
+  private static final Key<Key<?>> CONTENT_ID_KEY = Key.create("CONTENT_ID");
+  private static final String APP_ICON_ID = "compiler";
+  private Key<Key<?>> myContentIdKey = CONTENT_ID_KEY;
+  private final Key<Key<?>> myContentId = Key.create("compile_content");
+  private NewErrorTreeViewPanel myErrorTreeView;
+  private final Object myMessageViewLock = new Object();
+  private final String myContentName;
+  private final boolean myHeadlessMode;
+  private final boolean myForceAsyncExecution;
+  private int myErrorCount = 0;
+  private int myWarningCount = 0;
+  private boolean myMessagesAutoActivated = false;
+
+  private volatile ProgressIndicator myIndicator = new EmptyProgressIndicator();
+  private Runnable myCompileWork;
+  private final AtomicBoolean myMessageViewWasPrepared = new AtomicBoolean(false);
+  private Runnable myRestartWork;
+
+  public CompilerTask(@NotNull Project project, String contentName, final boolean headlessMode, boolean forceAsync) {
+    super(project, contentName);
+    myContentName = contentName;
+    myHeadlessMode = headlessMode;
+    myForceAsyncExecution = forceAsync;
+  }
+
+  public void setContentIdKey(Key<Key<?>> contentIdKey) {
+    myContentIdKey = contentIdKey != null? contentIdKey : CONTENT_ID_KEY;
+  }
+
+  public String getProcessId() {
+    return "compilation";
+  }
+
+  @Override
+  public DumbModeAction getDumbModeAction() {
+    return DumbModeAction.WAIT;
+  }
+
+  public boolean shouldStartInBackground() {
+    return true;
+  }
+
+  public ProgressIndicator getIndicator() {
+    return myIndicator;
+  }
+
+  @Nullable
+  public NotificationInfo getNotificationInfo() {
+    return new NotificationInfo(myErrorCount > 0? "Compiler (errors)" : "Compiler (success)", "Compilation Finished", myErrorCount + " Errors, " + myWarningCount + " Warnings", true);
+  }
+
+  private CloseListener myCloseListener;
+
+  public void run(@NotNull final ProgressIndicator indicator) {
+    myIndicator = indicator;
+
+    final ProjectManager projectManager = ProjectManager.getInstance();
+    projectManager.addProjectManagerListener(myProject, myCloseListener = new CloseListener());
+
+    final Semaphore semaphore = ((CompilerManagerImpl)CompilerManager.getInstance(myProject)).getCompilationSemaphore();
+    boolean acquired = false;
+    try {
+
+      try {
+        while (!acquired) {
+          acquired = semaphore.tryAcquire(500, TimeUnit.MILLISECONDS);
+          if (indicator.isCanceled()) {
+            // give up obtaining the semaphore,
+            // let compile work begin in order to stop gracefuly on cancel event
+            break;
+          }
+        }
+      }
+      catch (InterruptedException ignored) {
+      }
+
+      if (!isHeadless()) {
+        addIndicatorDelegate();
+      }
+      myCompileWork.run();
+    }
+    finally {
+      try {
+        indicator.stop();
+        projectManager.removeProjectManagerListener(myProject, myCloseListener);
+      }
+      finally {
+        if (acquired) {
+          semaphore.release();
+        }
+      }
+    }
+  }
+
+  private void prepareMessageView() {
+    if (!myIndicator.isRunning()) {
+      return;
+    }
+    if (myMessageViewWasPrepared.getAndSet(true)) {
+      return;
+    }
+
+    ApplicationManager.getApplication().invokeLater(new Runnable() {
+      public void run() {
+        if (myProject.isDisposed()) return;
+        synchronized (myMessageViewLock) {
+          // clear messages from the previous compilation
+          if (myErrorTreeView == null) {
+            // if message view != null, the contents has already been cleared
+            removeAllContents(myProject, null);
+          }
+        }
+      }
+    });
+  }
+
+  private void addIndicatorDelegate() {
+    ProgressIndicator indicator = myIndicator;
+    if (!(indicator instanceof ProgressIndicatorEx)) return;
+    ((ProgressIndicatorEx)indicator).addStateDelegate(new ProgressIndicatorBase() {
+
+      public void cancel() {
+        super.cancel();
+        closeUI();
+        stopAppIconProgress();
+      }
+
+      public void stop() {
+        super.stop();
+        if (!isCanceled()) {
+          closeUI();
+        }
+        stopAppIconProgress();
+      }
+
+      private void stopAppIconProgress() {
+        UIUtil.invokeLaterIfNeeded(new Runnable() {
+          public void run() {
+            AppIcon appIcon = AppIcon.getInstance();
+            if (appIcon.hideProgress(myProject, APP_ICON_ID)) {
+              if (myErrorCount > 0) {
+                appIcon.setErrorBadge(myProject, String.valueOf(myErrorCount));
+                appIcon.requestAttention(myProject, true);
+              } else {
+                appIcon.setOkBadge(myProject, true);
+                appIcon.requestAttention(myProject, false);
+              }
+            }
+          }
+        });
+      }
+
+      public void setText(final String text) {
+        super.setText(text);
+        updateProgressText();
+      }
+
+      public void setText2(final String text) {
+        super.setText2(text);
+        updateProgressText();
+      }
+
+      public void setFraction(final double fraction) {
+        super.setFraction(fraction);
+        updateProgressText();
+        UIUtil.invokeLaterIfNeeded(new Runnable() {
+          public void run() {
+            AppIcon.getInstance().setProgress(myProject, APP_ICON_ID, AppIconScheme.Progress.BUILD, fraction, true);
+          }
+        });
+      }
+
+      protected void onProgressChange() {
+        prepareMessageView();
+      }
+    });
+  }
+
+  public void cancel() {
+    if (!myIndicator.isCanceled()) {
+      myIndicator.cancel();
+    }
+  }
+
+  public void addMessage(final CompilerMessage message) {
+    prepareMessageView();
+
+    final CompilerMessageCategory messageCategory = message.getCategory();
+    if (CompilerMessageCategory.WARNING.equals(messageCategory)) {
+      myWarningCount += 1;
+    }
+    else if (CompilerMessageCategory.ERROR.equals(messageCategory)) {
+      myErrorCount += 1;
+      informWolf(message);
+    }
+
+    if (ApplicationManager.getApplication().isDispatchThread()) {
+      openMessageView();
+      doAddMessage(message);
+    }
+    else {
+      final Window window = getWindow();
+      final ModalityState modalityState = window != null ? ModalityState.stateForComponent(window) : ModalityState.NON_MODAL;
+      ApplicationManager.getApplication().invokeLater(new Runnable() {
+        public void run() {
+          if (!myProject.isDisposed()) {
+            openMessageView();
+            doAddMessage(message);
+          }
+        }
+      }, modalityState);
+    }
+  }
+
+  private void informWolf(final CompilerMessage message) {
+    WolfTheProblemSolver wolf = WolfTheProblemSolver.getInstance(myProject);
+    VirtualFile file = getVirtualFile(message);
+    wolf.queue(file);
+  }
+
+  private void doAddMessage(final CompilerMessage message) {
+    synchronized (myMessageViewLock) {
+      if (myErrorTreeView != null) {
+        final Navigatable navigatable = message.getNavigatable();
+        final VirtualFile file = message.getVirtualFile();
+        final CompilerMessageCategory category = message.getCategory();
+        final int type = translateCategory(category);
+        final String[] text = convertMessage(message);
+        if (navigatable != null) {
+          final String groupName = file != null? file.getPresentableUrl() : category.getPresentableText();
+          myErrorTreeView.addMessage(type, text, groupName, navigatable, message.getExportTextPrefix(), message.getRenderTextPrefix(), message.getVirtualFile());
+        }
+        else {
+          myErrorTreeView.addMessage(type, text, file, -1, -1, message.getVirtualFile());
+        }
+
+        final boolean shouldAutoActivate =
+          !myMessagesAutoActivated &&
+          (
+            CompilerMessageCategory.ERROR.equals(category) ||
+            (CompilerMessageCategory.WARNING.equals(category) && !ErrorTreeViewConfiguration.getInstance(myProject).isHideWarnings())
+          );
+        if (shouldAutoActivate) {
+          myMessagesAutoActivated = true;
+          activateMessageView();
+        }
+      }
+    }
+  }
+
+  private static String[] convertMessage(final CompilerMessage message) {
+    String text = message.getMessage();
+    if (!text.contains("\n")) {
+      return new String[]{text};
+    }
+    ArrayList<String> lines = new ArrayList<String>();
+    StringTokenizer tokenizer = new StringTokenizer(text, "\n", false);
+    while (tokenizer.hasMoreTokens()) {
+      lines.add(tokenizer.nextToken());
+    }
+    return ArrayUtil.toStringArray(lines);
+  }
+
+  public static int translateCategory(CompilerMessageCategory category) {
+    if (CompilerMessageCategory.ERROR.equals(category)) {
+      return MessageCategory.ERROR;
+    }
+    if (CompilerMessageCategory.WARNING.equals(category)) {
+      return MessageCategory.WARNING;
+    }
+    if (CompilerMessageCategory.STATISTICS.equals(category)) {
+      return MessageCategory.STATISTICS;
+    }
+    if (CompilerMessageCategory.INFORMATION.equals(category)) {
+      return MessageCategory.INFORMATION;
+    }
+    LOG.error("Unknown message category: " + category);
+    return 0;
+  }
+
+  public void start(Runnable compileWork, Runnable restartWork) {
+    myCompileWork = compileWork;
+    myRestartWork = restartWork;
+    queue();
+  }
+
+  private void updateProgressText() {
+    if (isHeadlessMode()) {
+      return;
+    }
+  }
+
+  // error tree view initialization must be invoked from event dispatch thread
+  private void openMessageView() {
+    if (isHeadlessMode()) {
+      return;
+    }
+    if (myIndicator.isCanceled()) {
+      return;
+    }
+    
+    final JComponent component;
+    synchronized (myMessageViewLock) {
+      if (myErrorTreeView != null) {
+        return;
+      }
+      myErrorTreeView = new CompilerErrorTreeView(
+          myProject,
+          myRestartWork
+      );
+      
+      myErrorTreeView.setProcessController(new NewErrorTreeViewPanel.ProcessController() {
+        public void stopProcess() {
+          cancel();
+        }
+
+        public boolean isProcessStopped() {
+          return !myIndicator.isRunning();
+        }
+      });
+      component = myErrorTreeView.getComponent();
+    }
+    
+    final MessageView messageView = MessageView.SERVICE.getInstance(myProject);
+    final Content content = ContentFactory.SERVICE.getInstance().createContent(component, myContentName, true);
+    content.putUserData(myContentIdKey, myContentId);
+    messageView.getContentManager().addContent(content);
+    myCloseListener.setContent(content, messageView.getContentManager());
+    removeAllContents(myProject, content);
+    messageView.getContentManager().setSelectedContent(content);
+  }
+
+  public void showCompilerContent() {
+    synchronized (myMessageViewLock) {
+      if (myErrorTreeView != null) {
+        final MessageView messageView = MessageView.SERVICE.getInstance(myProject);
+        Content[] contents = messageView.getContentManager().getContents();
+        for (Content content : contents) {
+          if (content.getUserData(myContentIdKey) != null) {
+            messageView.getContentManager().setSelectedContent(content);
+            return;
+          }
+        }
+      }
+    }
+  }
+
+  private void removeAllContents(Project project, Content notRemove) {
+    final MessageView messageView = MessageView.SERVICE.getInstance(project);
+    Content[] contents = messageView.getContentManager().getContents();
+    for (Content content : contents) {
+      if (content.isPinned()) {
+        continue;
+      }
+      if (content == notRemove) {
+        continue;
+      }
+      if (content.getUserData(myContentIdKey) != null) { // the content was added by me
+        messageView.getContentManager().removeContent(content, true);
+      }
+    }
+  }
+
+  private void activateMessageView() {
+    synchronized (myMessageViewLock) {
+      if (myErrorTreeView != null) {
+        final ToolWindow tw = ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.MESSAGES_WINDOW);
+        if (tw != null) {
+          tw.activate(null, false);
+        }
+      }
+    }
+  }
+
+  private void closeUI() {
+    if (isHeadlessMode()) {
+      return;
+    }
+    Window window = getWindow();
+    ModalityState modalityState = window != null ? ModalityState.stateForComponent(window) : ModalityState.NON_MODAL;
+    final Application application = ApplicationManager.getApplication();
+    application.invokeLater(new Runnable() {
+      public void run() {
+        synchronized (myMessageViewLock) {
+          if (myErrorTreeView != null) {
+            final boolean shouldRetainView = myErrorCount > 0 || myWarningCount > 0 && !myErrorTreeView.isHideWarnings();
+            if (shouldRetainView) {
+              addMessage(new CompilerMessageImpl(myProject, CompilerMessageCategory.STATISTICS, CompilerBundle.message("statistics.error.count", myErrorCount)));
+              addMessage(new CompilerMessageImpl(myProject, CompilerMessageCategory.STATISTICS, CompilerBundle.message("statistics.warnings.count", myWarningCount)));
+              //activateMessageView();
+              myErrorTreeView.selectFirstMessage();
+            }
+            else {
+              removeAllContents(myProject, null);
+            }
+          }
+        }
+      }
+    }, modalityState);
+  }
+
+  public Window getWindow(){
+    return null;
+  }
+
+  public boolean isHeadless() {
+    return myHeadlessMode && !myForceAsyncExecution;
+  }
+
+  private boolean isHeadlessMode() {
+    return myHeadlessMode;
+  }
+
+  private static VirtualFile getVirtualFile(final CompilerMessage message) {
+    VirtualFile virtualFile = message.getVirtualFile();
+    if (virtualFile == null) {
+      Navigatable navigatable = message.getNavigatable();
+      if (navigatable instanceof OpenFileDescriptor) {
+        virtualFile = ((OpenFileDescriptor)navigatable).getFile();
+      }
+    }
+    return virtualFile;
+  }
+
+  public static TextRange getTextRange(final CompilerMessage message) {
+    Navigatable navigatable = message.getNavigatable();
+    if (navigatable instanceof OpenFileDescriptor) {
+      int offset = ((OpenFileDescriptor)navigatable).getOffset();
+      return new TextRange(offset, offset);
+    }
+    return TextRange.EMPTY_RANGE;
+  }
+
+  private class CloseListener extends ContentManagerAdapter implements ProjectManagerListener {
+    private Content myContent;
+    private ContentManager myContentManager;
+    private boolean myIsApplicationExitingOrProjectClosing = false;
+    private boolean myUserAcceptedCancel = false;
+
+    public boolean canCloseProject(final Project project) {
+      assert project != null;
+      if (!project.equals(myProject)) {
+        return true;
+      }
+      if (shouldAskUser()) {
+        int result = Messages.showOkCancelDialog(
+          myProject,
+          CompilerBundle.message("warning.compiler.running.on.project.close"),
+          CompilerBundle.message("compiler.running.dialog.title"),
+          Messages.getQuestionIcon()
+        );
+        if (result != 0) {
+          return false; // veto closing
+        }
+        myUserAcceptedCancel = true;
+
+        final MessageBusConnection connection = project.getMessageBus().connect();
+        connection.subscribe(CompilerTopics.COMPILATION_STATUS, new CompilationStatusAdapter() {
+          public void compilationFinished(boolean aborted, int errors, int warnings, final CompileContext compileContext) {
+            connection.disconnect();
+            ProjectUtil.closeAndDispose(project);
+          }
+        });
+        cancel();
+        return false; // cancel compiler and let it finish, after compilation close the project, but currently - veto closing
+      }
+      return !myIndicator.isRunning();
+    }
+
+    public void setContent(Content content, ContentManager contentManager) {
+      myContent = content;
+      myContentManager = contentManager;
+      contentManager.addContentManagerListener(this);
+    }
+
+    public void contentRemoved(ContentManagerEvent event) {
+      if (event.getContent() == myContent) {
+        synchronized (myMessageViewLock) {
+          if (myErrorTreeView != null) {
+            myErrorTreeView.dispose();
+            myErrorTreeView = null;
+            if (myIndicator.isRunning()) {
+              cancel();
+            }
+            if (AppIcon.getInstance().hideProgress(myProject, "compiler")) {
+              AppIcon.getInstance().setErrorBadge(myProject, null);
+            }
+          }
+        }
+        myContentManager.removeContentManagerListener(this);
+        myContent.release();
+        myContent = null;
+      }
+    }
+
+    public void contentRemoveQuery(ContentManagerEvent event) {
+      if (event.getContent() == myContent) {
+        if (!myIndicator.isCanceled() && shouldAskUser()) {
+          int result = Messages.showOkCancelDialog(
+            myProject,
+            CompilerBundle.message("warning.compiler.running.on.toolwindow.close"),
+            CompilerBundle.message("compiler.running.dialog.title"),
+            Messages.getQuestionIcon()
+          );
+          if (result != 0) {
+            event.consume(); // veto closing
+          }
+          myUserAcceptedCancel = true;
+        }
+      }
+    }
+
+    private boolean shouldAskUser() {
+      // do not ask second time if user already accepted closing
+      return !myUserAcceptedCancel && !myIsApplicationExitingOrProjectClosing && myIndicator.isRunning();
+    }
+
+    public void projectOpened(Project project) {
+    }
+
+    public void projectClosed(Project project) {
+      if (project.equals(myProject) && myContent != null) {
+        myContentManager.removeContent(myContent, true);
+      }
+    }
+
+    public void projectClosing(Project project) {
+      if (project.equals(myProject)) {
+        myIsApplicationExitingOrProjectClosing = true;
+      }
+    }
+  }
+}
+                                      
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/compiler/server/AutoMakeMessageHandler.java b/java/compiler/impl/src/com/intellij/compiler/server/AutoMakeMessageHandler.java
new file mode 100644
index 0000000..05bfe72
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/server/AutoMakeMessageHandler.java
@@ -0,0 +1,171 @@
+/*
+ * 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 com.intellij.compiler.server;
+
+import com.intellij.compiler.CompilerMessageImpl;
+import com.intellij.compiler.ProblemsView;
+import com.intellij.notification.Notification;
+import com.intellij.openapi.compiler.CompilationStatusListener;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.compiler.CompilerTopics;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.MessageType;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.problems.Problem;
+import com.intellij.problems.WolfTheProblemSolver;
+import org.jetbrains.jps.api.CmdlineRemoteProto;
+
+import java.util.Collections;
+import java.util.UUID;
+
+/**
+* @author Eugene Zhuravlev
+*         Date: 4/25/12
+*/
+class AutoMakeMessageHandler extends DefaultMessageHandler {
+  private static final Key<Notification> LAST_AUTO_MAKE_NOFITICATION = Key.create("LAST_AUTO_MAKE_NOFITICATION");
+  private CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.Status myBuildStatus;
+  private final Project myProject;
+  private final WolfTheProblemSolver myWolf;
+
+  public AutoMakeMessageHandler(Project project) {
+    super(project);
+    myProject = project;
+    myBuildStatus = CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.Status.SUCCESS;
+    myWolf = WolfTheProblemSolver.getInstance(project);
+  }
+
+  @Override
+  public void buildStarted(UUID sessionId) {
+  }
+
+  @Override
+  protected void handleBuildEvent(UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.BuildEvent event) {
+    if (myProject.isDisposed()) {
+      return;
+    }
+    switch (event.getEventType()) {
+      case BUILD_COMPLETED:
+        if (event.hasCompletionStatus()) {
+          myBuildStatus = event.getCompletionStatus();
+        }
+        return;
+
+      case FILES_GENERATED:
+        final CompilationStatusListener publisher = myProject.getMessageBus().syncPublisher(CompilerTopics.COMPILATION_STATUS);
+        for (CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.GeneratedFile generatedFile : event.getGeneratedFilesList()) {
+          final String root = FileUtil.toSystemIndependentName(generatedFile.getOutputRoot());
+          final String relativePath = FileUtil.toSystemIndependentName(generatedFile.getRelativePath());
+          publisher.fileGenerated(root, relativePath);
+        }
+        return;
+
+      default:
+        return;
+    }
+  }
+
+  @Override
+  protected void handleCompileMessage(final UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.CompileMessage message) {
+    if (myProject.isDisposed()) {
+      return;
+    }
+    final CmdlineRemoteProto.Message.BuilderMessage.CompileMessage.Kind kind = message.getKind();
+    if (kind == CmdlineRemoteProto.Message.BuilderMessage.CompileMessage.Kind.PROGRESS) {
+      final ProblemsView view = ProblemsView.SERVICE.getInstance(myProject);
+      if (message.hasDone()) {
+        view.setProgress(message.getText(), message.getDone());
+      }
+      else {
+        view.setProgress(message.getText());
+      }
+    }
+    else if (kind == CmdlineRemoteProto.Message.BuilderMessage.CompileMessage.Kind.ERROR) {
+      informWolf(myProject, message);
+
+      final String sourceFilePath = message.hasSourceFilePath() ? message.getSourceFilePath() : null;
+      final VirtualFile vFile = sourceFilePath != null? LocalFileSystem.getInstance().findFileByPath(FileUtil.toSystemIndependentName(sourceFilePath)) : null;
+      final long line = message.hasLine() ? message.getLine() : -1;
+      final long column = message.hasColumn() ? message.getColumn() : -1;
+      ProblemsView.SERVICE.getInstance(myProject).addMessage(new CompilerMessageImpl(myProject, CompilerMessageCategory.ERROR, message.getText(), vFile, (int)line, (int)column, null), sessionId);
+    }
+  }
+
+  @Override
+  public void handleFailure(UUID sessionId, CmdlineRemoteProto.Message.Failure failure) {
+    final String msg = "Auto make failure: " + failure.getDescription();
+    CompilerManager.NOTIFICATION_GROUP.createNotification(msg, MessageType.INFO);
+    ProblemsView.SERVICE.getInstance(myProject).addMessage(new CompilerMessageImpl(myProject, CompilerMessageCategory.ERROR, msg), sessionId);
+  }
+
+  @Override
+  public void sessionTerminated(UUID sessionId) {
+    String statusMessage = null/*"Auto make completed"*/;
+    switch (myBuildStatus) {
+      case SUCCESS:
+        //statusMessage = "Auto make completed successfully";
+        break;
+      case UP_TO_DATE:
+        //statusMessage = "All files are up-to-date";
+        break;
+      case ERRORS:
+        statusMessage = "Auto make completed with errors";
+        break;
+      case CANCELED:
+        //statusMessage = "Auto make has been canceled";
+        break;
+    }
+    if (statusMessage != null) {
+      final Notification notification = CompilerManager.NOTIFICATION_GROUP.createNotification(statusMessage, MessageType.INFO);
+      if (!myProject.isDisposed()) {
+        notification.notify(myProject);
+      }
+      myProject.putUserData(LAST_AUTO_MAKE_NOFITICATION, notification);
+    } 
+    else {
+      Notification notification = myProject.getUserData(LAST_AUTO_MAKE_NOFITICATION);
+      if (notification != null) {
+        notification.expire();
+        myProject.putUserData(LAST_AUTO_MAKE_NOFITICATION, null);
+      }
+    }
+    final ProblemsView view = ProblemsView.SERVICE.getInstance(myProject);
+    view.clearProgress();
+    view.clearOldMessages(null, sessionId);
+  }
+
+  private void informWolf(Project project, CmdlineRemoteProto.Message.BuilderMessage.CompileMessage message) {
+    final String srcPath = message.getSourceFilePath();
+    if (srcPath != null && !project.isDisposed()) {
+      final VirtualFile vFile = LocalFileSystem.getInstance().findFileByPath(srcPath);
+      if (vFile != null) {
+        final int line = (int)message.getLine();
+        final int column = (int)message.getColumn();
+        if (line > 0 && column > 0) {
+          final Problem problem = myWolf.convertToProblem(vFile, line, column, new String[]{message.getText()});
+          myWolf.weHaveGotProblems(vFile, Collections.singletonList(problem));
+        }
+        else {
+          myWolf.queue(vFile);
+        }
+      }
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java
new file mode 100644
index 0000000..a819b47
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java
@@ -0,0 +1,1100 @@
+/*
+ * 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 com.intellij.compiler.server;
+
+import com.intellij.ProjectTopics;
+import com.intellij.application.options.PathMacrosImpl;
+import com.intellij.compiler.CompilerWorkspaceConfiguration;
+import com.intellij.compiler.impl.javaCompiler.javac.JavacConfiguration;
+import com.intellij.compiler.server.impl.CompileServerClasspathManager;
+import com.intellij.execution.ExecutionAdapter;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionManager;
+import com.intellij.execution.configurations.GeneralCommandLine;
+import com.intellij.execution.configurations.RunProfile;
+import com.intellij.execution.process.*;
+import com.intellij.execution.ui.RunContentDescriptor;
+import com.intellij.execution.ui.RunContentManager;
+import com.intellij.ide.PowerSaveMode;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.PathMacros;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.components.ApplicationComponent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectCoreUtil;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.project.ProjectManagerAdapter;
+import com.intellij.openapi.projectRoots.JavaSdk;
+import com.intellij.openapi.projectRoots.JavaSdkType;
+import com.intellij.openapi.projectRoots.JavaSdkVersion;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
+import com.intellij.openapi.roots.ModuleRootAdapter;
+import com.intellij.openapi.roots.ModuleRootEvent;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.ShutDownTracker;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.CharsetToolkit;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.openapi.vfs.newvfs.BulkFileListener;
+import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
+import com.intellij.util.Alarm;
+import com.intellij.util.Function;
+import com.intellij.util.concurrency.SequentialTaskExecutor;
+import com.intellij.util.io.storage.HeavyProcessLatch;
+import com.intellij.util.messages.MessageBusConnection;
+import com.intellij.util.net.NetUtils;
+import gnu.trove.THashSet;
+import org.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.*;
+import org.jboss.netty.channel.group.ChannelGroup;
+import org.jboss.netty.channel.group.ChannelGroupFuture;
+import org.jboss.netty.channel.group.DefaultChannelGroup;
+import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
+import org.jboss.netty.handler.codec.protobuf.ProtobufDecoder;
+import org.jboss.netty.handler.codec.protobuf.ProtobufEncoder;
+import org.jboss.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
+import org.jboss.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.TestOnly;
+import org.jetbrains.ide.PooledThreadExecutor;
+import org.jetbrains.jps.api.CmdlineProtoUtil;
+import org.jetbrains.jps.api.CmdlineRemoteProto;
+import org.jetbrains.jps.api.GlobalOptions;
+import org.jetbrains.jps.api.RequestFuture;
+import org.jetbrains.jps.cmdline.BuildMain;
+import org.jetbrains.jps.cmdline.ClasspathBootstrap;
+import org.jetbrains.jps.incremental.Utils;
+
+import javax.tools.*;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.charset.Charset;
+import java.util.*;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage.TargetTypeBuildScope;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: 9/6/11
+ */
+public class BuildManager implements ApplicationComponent{
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.server.BuildManager");
+  private static final String SYSTEM_ROOT = "compile-server";
+  private static final String LOGGER_CONFIG = "log.xml";
+  private static final String DEFAULT_LOGGER_CONFIG = "defaultLogConfig.xml";
+  private static final int MAKE_TRIGGER_DELAY = 3 * 1000 /*3 seconds*/;
+  private final boolean IS_UNIT_TEST_MODE;
+  private static final String IWS_EXTENSION = ".iws";
+  private static final String IPR_EXTENSION = ".ipr";
+  private static final String IDEA_PROJECT_DIR_PATTERN = "/.idea/";
+  private static final Function<String, Boolean> PATH_FILTER = 
+    SystemInfo.isFileSystemCaseSensitive?
+    new Function<String, Boolean>() {
+      @Override
+      public Boolean fun(String s) {
+        return !(s.contains(IDEA_PROJECT_DIR_PATTERN) || s.endsWith(IWS_EXTENSION) || s.endsWith(IPR_EXTENSION));
+      }
+    } :
+    new Function<String, Boolean>() {
+      @Override
+      public Boolean fun(String s) {
+        return !(StringUtil.endsWithIgnoreCase(s, IWS_EXTENSION) || StringUtil.endsWithIgnoreCase(s, IPR_EXTENSION) || StringUtil.containsIgnoreCase(s, IDEA_PROJECT_DIR_PATTERN));
+      }
+    };
+
+  private final File mySystemDirectory;
+  private final ProjectManager myProjectManager;
+
+  private final Map<RequestFuture, Project> myAutomakeFutures = new HashMap<RequestFuture, Project>();
+  private final Map<String, RequestFuture> myBuildsInProgress = Collections.synchronizedMap(new HashMap<String, RequestFuture>());
+  private final CompileServerClasspathManager myClasspathManager = new CompileServerClasspathManager();
+  private final Executor myPooledThreadExecutor = new PooledThreadExecutor();
+  private final SequentialTaskExecutor myRequestsProcessor = new SequentialTaskExecutor(myPooledThreadExecutor);
+  private final Map<String, ProjectData> myProjectDataMap = Collections.synchronizedMap(new HashMap<String, ProjectData>());
+
+  private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
+  private final AtomicBoolean myAutoMakeInProgress = new AtomicBoolean(false);
+
+  private final ChannelGroup myAllOpenChannels = new DefaultChannelGroup("build-manager");
+  private final BuildMessageDispatcher myMessageDispatcher = new BuildMessageDispatcher();
+  private volatile int myListenPort = -1;
+  private volatile CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings myGlobals;
+  @Nullable
+  private final Charset mySystemCharset;
+
+  public BuildManager(final ProjectManager projectManager) {
+    IS_UNIT_TEST_MODE = ApplicationManager.getApplication().isUnitTestMode();
+    myProjectManager = projectManager;
+    mySystemCharset = CharsetToolkit.getDefaultSystemCharset();
+    final String systemPath = PathManager.getSystemPath();
+    File system = new File(systemPath);
+    try {
+      system = system.getCanonicalFile();
+    }
+    catch (IOException e) {
+      LOG.info(e);
+    }
+    mySystemDirectory = system;
+
+    projectManager.addProjectManagerListener(new ProjectWatcher());
+    
+    final MessageBusConnection conn = ApplicationManager.getApplication().getMessageBus().connect();
+    conn.subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener.Adapter() {
+      @Override
+      public void after(@NotNull List<? extends VFileEvent> events) {
+        if (shouldTriggerMake(events)) {
+          scheduleAutoMake();
+        }
+      }
+
+      private boolean shouldTriggerMake(List<? extends VFileEvent> events) {
+        if (PowerSaveMode.isEnabled()) {
+          return false;
+        }
+        List<Project> activeProjects = null;
+        for (VFileEvent event : events) {
+          final VirtualFile eventFile = event.getFile();
+          if (eventFile == null || ProjectCoreUtil.isProjectOrWorkspaceFile(eventFile)) {
+            continue;
+          }
+          
+          if (activeProjects == null) {
+            activeProjects = getActiveProjects();
+            if (activeProjects.isEmpty()) {
+              return false;
+            }
+          }
+          // todo: probably we do not need this excessive filtering
+          for (Project project : activeProjects) {
+            if (!project.isInitialized() || ProjectRootManager.getInstance(project).getFileIndex().isInContent(eventFile)) {
+              return true;
+            }
+          }
+        }
+        return false;
+      }
+
+      private List<Project> getActiveProjects() {
+        final Project[] projects = myProjectManager.getOpenProjects();
+        if (projects.length == 0) {
+          return Collections.emptyList();
+        }
+        final List<Project> projectList = new ArrayList<Project>();
+        for (Project project : projects) {
+          if (project.isDefault() || project.isDisposed()) {
+            continue;
+          }
+          projectList.add(project);
+        }
+        return projectList;
+      }
+    });
+
+    ShutDownTracker.getInstance().registerShutdownTask(new Runnable() {
+      @Override
+      public void run() {
+        stopListening();
+      }
+    });
+  }
+
+  public static BuildManager getInstance() {
+    return ApplicationManager.getApplication().getComponent(BuildManager.class);
+  }
+
+  public void notifyFilesChanged(final Collection<File> paths) {
+    doNotify(paths, false);
+  }
+
+  public void notifyFilesDeleted(Collection<File> paths) {
+    doNotify(paths, true);
+  }
+
+  public void runCommand(Runnable command) {
+    myRequestsProcessor.submit(command);
+  }
+
+  private void doNotify(final Collection<File> paths, final boolean notifyDeletion) {
+    // ensure events processed in the order they arrived
+    runCommand(new Runnable() {
+
+      @Override
+      public void run() {
+        final List<String> filtered = new ArrayList<String>(paths.size());
+        for (File file : paths) {
+          final String path = FileUtil.toSystemIndependentName(file.getPath());
+          if (PATH_FILTER.fun(path)) {
+            filtered.add(path);
+          }
+        }
+        if (filtered.isEmpty()) {
+          return;
+        }
+        synchronized (myProjectDataMap) {
+          if (IS_UNIT_TEST_MODE) {
+            if (notifyDeletion) {
+              LOG.info("Registering deleted paths: " + filtered);
+            }
+            else {
+              LOG.info("Registering changed paths: " + filtered);
+            }
+          }
+          for (Map.Entry<String, ProjectData> entry : myProjectDataMap.entrySet()) {
+            final ProjectData data = entry.getValue();
+            if (notifyDeletion) {
+              data.addDeleted(filtered);
+            }
+            else {
+              data.addChanged(filtered);
+            }
+            final RequestFuture future = myBuildsInProgress.get(entry.getKey());
+            if (future != null && !future.isCancelled() && !future.isDone()) {
+              final UUID sessionId = future.getRequestID();
+              final Channel channel = myMessageDispatcher.getConnectedChannel(sessionId);
+              if (channel != null) {
+                final CmdlineRemoteProto.Message.ControllerMessage message =
+                  CmdlineRemoteProto.Message.ControllerMessage.newBuilder().setType(
+                    CmdlineRemoteProto.Message.ControllerMessage.Type.FS_EVENT).setFsEvent(data.createNextEvent()).build();
+                Channels.write(channel, CmdlineProtoUtil.toMessage(sessionId, message));
+              }
+            }
+          }
+        }
+      }
+    });
+  }
+
+  public static void forceModelLoading(CompileContext context) {
+    context.getCompileScope().putUserData(BuildMain.FORCE_MODEL_LOADING_PARAMETER, Boolean.TRUE.toString());
+  }
+
+  public void clearState(Project project) {
+    myGlobals = null;
+    final String projectPath = getProjectPath(project);
+    synchronized (myProjectDataMap) {
+      final ProjectData data = myProjectDataMap.get(projectPath);
+      if (data != null) {
+        data.dropChanges();
+      }
+    }
+    scheduleAutoMake();
+  }
+
+  public boolean rescanRequired(Project project) {
+    final String projectPath = getProjectPath(project);
+    synchronized (myProjectDataMap) {
+      final ProjectData data = myProjectDataMap.get(projectPath);
+      return data == null || data.myNeedRescan;
+    }
+  }
+
+  @Nullable
+  public List<String> getFilesChangedSinceLastCompilation(Project project) {
+    String projectPath = getProjectPath(project);
+    synchronized (myProjectDataMap) {
+      ProjectData data = myProjectDataMap.get(projectPath);
+      if (data != null && !data.myNeedRescan) {
+        return new ArrayList<String>(data.myChanged);
+      }
+      return null;
+    }
+  }
+
+  @Nullable
+  private static String getProjectPath(final Project project) {
+    final String url = project.getPresentableUrl();
+    if (url == null) {
+      return null;
+    }
+    return VirtualFileManager.extractPath(url);
+  }
+
+  public void scheduleAutoMake() {
+    if (IS_UNIT_TEST_MODE || PowerSaveMode.isEnabled()) {
+      return;
+    }
+    addMakeRequest(new Runnable() {
+      @Override
+      public void run() {
+        if (!HeavyProcessLatch.INSTANCE.isRunning() && !myAutoMakeInProgress.getAndSet(true)) {
+          try {
+            ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
+              @Override
+              public void run() {
+                try {
+                  runAutoMake();
+                }
+                finally {
+                  myAutoMakeInProgress.set(false);
+                }
+              }
+            });
+          }
+          catch (RejectedExecutionException ignored) {
+            // we were shut down
+            myAutoMakeInProgress.set(false);
+          }
+          catch (Throwable e) {
+            myAutoMakeInProgress.set(false);
+            throw new RuntimeException(e);
+          }
+        }
+        else {
+          addMakeRequest(this);
+        }
+      }
+    });
+  }
+
+  private void addMakeRequest(Runnable runnable) {
+    myAlarm.cancelAllRequests();
+    final int delay = Math.max(50, Registry.intValue("compiler.automake.trigger.delay", MAKE_TRIGGER_DELAY));
+    myAlarm.addRequest(runnable, delay);
+  }
+
+  private void runAutoMake() {
+    final Project[] openProjects = myProjectManager.getOpenProjects();
+    if (openProjects.length > 0) {
+      final List<RequestFuture> futures = new ArrayList<RequestFuture>();
+      for (final Project project : openProjects) {
+        if (project.isDefault() || project.isDisposed()) {
+          continue;
+        }
+        final CompilerWorkspaceConfiguration config = CompilerWorkspaceConfiguration.getInstance(project);
+        if (!config.useOutOfProcessBuild() || !config.MAKE_PROJECT_ON_SAVE) {
+          continue;
+        }
+        if (!config.allowAutoMakeWhileRunningApplication()) {
+          final RunContentManager contentManager = ExecutionManager.getInstance(project).getContentManager();
+          boolean hasRunningProcesses = false;
+          for (RunContentDescriptor descriptor : contentManager.getAllDescriptors()) {
+            final ProcessHandler handler = descriptor.getProcessHandler();
+            if (handler != null && !handler.isProcessTerminated()) { // active process
+              hasRunningProcesses = true;
+              break;
+            }
+          }
+          if (hasRunningProcesses) {
+            continue;
+          }
+        }
+
+        final List<String> emptyList = Collections.emptyList();
+        final RequestFuture future = scheduleBuild(
+          project, false, true, false, CmdlineProtoUtil.createAllModulesScopes(), emptyList, Collections.<String, String>emptyMap(), new AutoMakeMessageHandler(project)
+        );
+        if (future != null) {
+          futures.add(future);
+          synchronized (myAutomakeFutures) {
+            myAutomakeFutures.put(future, project);
+          }
+        }
+      }
+      try {
+        for (RequestFuture future : futures) {
+          future.waitFor();
+        }
+      }
+      finally {
+        synchronized (myAutomakeFutures) {
+          myAutomakeFutures.keySet().removeAll(futures);
+        }
+      }
+    }
+  }
+
+  public Collection<RequestFuture> cancelAutoMakeTasks(Project project) {
+    final Collection<RequestFuture> futures = new ArrayList<RequestFuture>();
+    synchronized (myAutomakeFutures) {
+      for (Map.Entry<RequestFuture, Project> entry : myAutomakeFutures.entrySet()) {
+        if (entry.getValue().equals(project)) {
+          final RequestFuture future = entry.getKey();
+          future.cancel(false);
+          futures.add(future);
+        }
+      }
+    }
+    return futures;
+  }
+
+  @Nullable
+  public RequestFuture scheduleBuild(
+    final Project project, final boolean isRebuild, final boolean isMake,
+    final boolean onlyCheckUpToDate, final List<TargetTypeBuildScope> scopes,
+    final Collection<String> paths,
+    final Map<String, String> userData, final DefaultMessageHandler handler) {
+
+    final String projectPath = getProjectPath(project);
+    final UUID sessionId = UUID.randomUUID();
+
+    // ensure server is listening
+    if (myListenPort < 0) {
+      try {
+        synchronized (this) {
+          if (myListenPort < 0) {
+            myListenPort = startListening();
+          }
+        }
+      }
+      catch (Exception e) {
+        handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), null));
+        handler.sessionTerminated(sessionId);
+        return null;
+      }
+    }
+
+    try {
+      final RequestFuture<BuilderMessageHandler> future = new RequestFuture<BuilderMessageHandler>(handler, sessionId, new RequestFuture.CancelAction<BuilderMessageHandler>() {
+        @Override
+        public void cancel(RequestFuture<BuilderMessageHandler> future) throws Exception {
+          myMessageDispatcher.cancelSession(future.getRequestID());
+        }
+      });
+      // by using the same queue that processes events we ensure that
+      // the build will be aware of all events that have happened before this request
+      runCommand(new Runnable() {
+        @Override
+        public void run() {
+          if (future.isCancelled() || project.isDisposed()) {
+            handler.sessionTerminated(sessionId);
+            future.setDone();
+            return;
+          }
+
+          CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings globals = myGlobals;
+          if (globals == null) {
+            globals = buildGlobalSettings();
+            myGlobals = globals;
+          }
+          CmdlineRemoteProto.Message.ControllerMessage.FSEvent currentFSChanges;
+          final SequentialTaskExecutor projectTaskQueue;
+          synchronized (myProjectDataMap) {
+            ProjectData data = myProjectDataMap.get(projectPath);
+            if (data == null) {
+              data = new ProjectData(new SequentialTaskExecutor(myPooledThreadExecutor));
+              myProjectDataMap.put(projectPath, data);
+            }
+            if (isRebuild) {
+              data.dropChanges();
+            }
+            if (IS_UNIT_TEST_MODE) {
+              LOG.info("Scheduling build for " +
+                       projectPath +
+                       "; CHANGED: " +
+                       new HashSet<String>(data.myChanged) +
+                       "; DELETED: " +
+                       new HashSet<String>(data.myDeleted));
+            }
+            currentFSChanges = data.getAndResetRescanFlag() ? null : data.createNextEvent();
+            projectTaskQueue = data.taskQueue;
+          }
+
+          final CmdlineRemoteProto.Message.ControllerMessage params;
+          if (isRebuild) {
+            params = CmdlineProtoUtil.createRebuildRequest(projectPath, scopes, userData, globals);
+          }
+          else if (onlyCheckUpToDate) {
+            params = CmdlineProtoUtil.createUpToDateCheckRequest(projectPath, scopes, paths, userData, globals, currentFSChanges);
+          }
+          else {
+            params = isMake ?
+                     CmdlineProtoUtil.createMakeRequest(projectPath, scopes, userData, globals, currentFSChanges) :
+                     CmdlineProtoUtil.createForceCompileRequest(projectPath, scopes, paths, userData, globals, currentFSChanges);
+          }
+
+          myMessageDispatcher.registerBuildMessageHandler(sessionId, new BuilderMessageHandlerWrapper(handler) {
+            @Override
+            public void sessionTerminated(UUID sessionId) {
+              try {
+                super.sessionTerminated(sessionId);
+              }
+              finally {
+                future.setDone();
+              }
+            }
+          }, params);
+
+          try {
+            projectTaskQueue.submit(new Runnable() {
+              @Override
+              public void run() {
+                ExecutionException execFailure = null;
+                try {
+                  if (project.isDisposed()) {
+                    return;
+                  }
+                  myBuildsInProgress.put(projectPath, future);
+                  final OSProcessHandler processHandler = launchBuildProcess(project, myListenPort, sessionId);
+                  final StringBuilder stdErrOutput = new StringBuilder();
+                  processHandler.addProcessListener(new ProcessAdapter() {
+                    @Override
+                    public void onTextAvailable(ProcessEvent event, Key outputType) {
+                      // re-translate builder's output to idea.log
+                      final String text = event.getText();
+                      if (!StringUtil.isEmptyOrSpaces(text)) {
+                        LOG.info("BUILDER_PROCESS [" + outputType.toString() + "]: " + text.trim());
+                        if (stdErrOutput.length() < 1024 && ProcessOutputTypes.STDERR.equals(outputType)) {
+                          stdErrOutput.append(text);
+                        }
+                      }
+                    }
+                  });
+                  processHandler.startNotify();
+                  final boolean terminated = processHandler.waitFor();
+                  if (terminated) {
+                    final int exitValue = processHandler.getProcess().exitValue();
+                    if (exitValue != 0) {
+                      final StringBuilder msg = new StringBuilder();
+                      msg.append("Abnormal build process termination: ");
+                      if (stdErrOutput.length() > 0) {
+                        msg.append("\n").append(stdErrOutput);
+                      }
+                      else {
+                        msg.append("unknown error");
+                      }
+                      handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(msg.toString(), null));
+                    }
+                  }
+                  else {
+                    handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure("Disconnected from build process", null));
+                  }
+                }
+                catch (ExecutionException e) {
+                  execFailure = e;
+                }
+                finally {
+                  myBuildsInProgress.remove(projectPath);
+                  if (myMessageDispatcher.getAssociatedChannel(sessionId) == null) {
+                    // either the connection has never been established (process not started or execution failed), or no messages were sent from the launched process.
+                    // in this case the session cannot be unregistered by the message dispatcher
+                    final BuilderMessageHandler unregistered = myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
+                    if (unregistered != null) {
+                      if (execFailure != null) {
+                        unregistered.handleFailure(sessionId, CmdlineProtoUtil.createFailure(execFailure.getMessage(), execFailure));
+                      }
+                      unregistered.sessionTerminated(sessionId);
+                    }
+                  }
+                }
+              }
+            });
+          }
+          catch (Throwable e) {
+            final BuilderMessageHandler unregistered = myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
+            if (unregistered != null) {
+              unregistered.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e));
+              unregistered.sessionTerminated(sessionId);
+            }
+          }
+        }
+      });
+
+      return future;
+    }
+    catch (Throwable e) {
+      handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e));
+      handler.sessionTerminated(sessionId);
+    }
+
+    return null;
+  }
+
+  @Override
+  public void initComponent() {
+  }
+
+  @Override
+  public void disposeComponent() {
+    stopListening();
+  }
+
+  @NotNull
+  @Override
+  public String getComponentName() {
+    return "com.intellij.compiler.server.BuildManager";
+  }
+
+  private static CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings buildGlobalSettings() {
+    final Map<String, String> data = new HashMap<String, String>();
+
+    for (Map.Entry<String, String> entry : PathMacrosImpl.getGlobalSystemMacros().entrySet()) {
+      data.put(entry.getKey(), FileUtil.toSystemIndependentName(entry.getValue()));
+    }
+
+    final PathMacros pathVars = PathMacros.getInstance();
+    for (String name : pathVars.getAllMacroNames()) {
+      final String path = pathVars.getValue(name);
+      if (path != null) {
+        data.put(name, FileUtil.toSystemIndependentName(path));
+      }
+    }
+
+    final CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings.Builder cmdBuilder =
+      CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings.newBuilder();
+
+    cmdBuilder.setGlobalOptionsPath(PathManager.getOptionsPath());
+
+    if (!data.isEmpty()) {
+      for (Map.Entry<String, String> entry : data.entrySet()) {
+        final String var = entry.getKey();
+        final String value = entry.getValue();
+        if (var != null && value != null) {
+          cmdBuilder.addPathVariable(CmdlineProtoUtil.createPair(var, value));
+        }
+      }
+    }
+
+    return cmdBuilder.build();
+  }
+
+  private OSProcessHandler launchBuildProcess(Project project, final int port, final UUID sessionId) throws ExecutionException {
+    // choosing sdk with which the build process should be run
+    Sdk projectJdk = null;
+    JavaSdkVersion sdkVersion = null;
+    int sdkMinorVersion = 0;
+
+    final Set<Sdk> candidates = new HashSet<Sdk>();
+    for (Module module : ModuleManager.getInstance(project).getModules()) {
+      final Sdk sdk = ModuleRootManager.getInstance(module).getSdk();
+      if (sdk != null && sdk.getSdkType() instanceof JavaSdk) {
+        candidates.add(sdk);
+      }
+    }
+    // now select the latest version from the sdks that are used in the project, but not older than the internal sdk version
+    for (Sdk candidate : candidates) {
+      final String vs = candidate.getVersionString();
+      if (vs != null) {
+        final JavaSdkVersion candidateVersion = ((JavaSdk)candidate.getSdkType()).getVersion(vs);
+        if (candidateVersion != null) {
+          final int candidateMinorVersion = getMinorVersion(vs);
+          if (projectJdk == null) {
+            sdkVersion = candidateVersion;
+            sdkMinorVersion = candidateMinorVersion;
+            projectJdk = candidate;
+          }
+          else {
+            final int result = candidateVersion.compareTo(sdkVersion);
+            if (result > 0 || (result == 0 && candidateMinorVersion > sdkMinorVersion)) {
+              sdkVersion = candidateVersion;
+              sdkMinorVersion = candidateMinorVersion;
+              projectJdk = candidate;
+            }
+          }
+        }
+      }
+    }
+
+    final Sdk internalJdk = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk();
+    if (projectJdk == null || sdkVersion == null || !sdkVersion.isAtLeast(JavaSdkVersion.JDK_1_6)) {
+      projectJdk = internalJdk;
+    }
+
+    // validate tools.jar presence
+    final String compilerPath;
+    if (projectJdk.equals(internalJdk)) {
+      final JavaCompiler systemCompiler = ToolProvider.getSystemJavaCompiler();
+      if (systemCompiler == null) {
+        throw new ExecutionException("No system java compiler is provided by the JRE. Make sure tools.jar is present in IntelliJ IDEA classpath.");
+      }
+      compilerPath = ClasspathBootstrap.getResourcePath(systemCompiler.getClass());
+    }
+    else {
+      compilerPath = ((JavaSdk)projectJdk.getSdkType()).getToolsPath(projectJdk);
+      if (compilerPath == null) {
+        throw new ExecutionException("Cannot determine path to 'tools.jar' library for " + projectJdk.getName() + " (" + projectJdk.getHomePath() + ")");
+      }
+    }
+
+    final CompilerWorkspaceConfiguration config = CompilerWorkspaceConfiguration.getInstance(project);
+    final GeneralCommandLine cmdLine = new GeneralCommandLine();
+    final String vmExecutablePath = ((JavaSdkType)projectJdk.getSdkType()).getVMExecutablePath(projectJdk);
+    cmdLine.setExePath(vmExecutablePath);
+    //cmdLine.addParameter("-XX:MaxPermSize=150m");
+    //cmdLine.addParameter("-XX:ReservedCodeCacheSize=64m");
+    int heapSize = config.COMPILER_PROCESS_HEAP_SIZE;
+
+    // todo: remove when old make implementation is removed
+    if (heapSize == CompilerWorkspaceConfiguration.DEFAULT_COMPILE_PROCESS_HEAP_SIZE) {
+      // check if javac is set to use larger heap, and if so, use it.
+      heapSize = Math.max(heapSize, JavacConfiguration.getOptions(project, JavacConfiguration.class).MAXIMUM_HEAP_SIZE);
+    }
+
+    cmdLine.addParameter("-Xmx" + heapSize + "m");
+
+    if (SystemInfo.isMac && sdkVersion != null && JavaSdkVersion.JDK_1_6.equals(sdkVersion) && Registry.is("compiler.process.32bit.vm.on.mac")) {
+      // unfortunately -d32 is supported on jdk 1.6 only
+      cmdLine.addParameter("-d32");
+    }
+
+    cmdLine.addParameter("-Djava.awt.headless=true");
+    if (IS_UNIT_TEST_MODE) {
+      cmdLine.addParameter("-Dtest.mode=true");
+    }
+    cmdLine.addParameter("-Djdt.compiler.useSingleThread=true"); // always run eclipse compiler in single-threaded mode
+
+    final String shouldGenerateIndex = System.getProperty(GlobalOptions.GENERATE_CLASSPATH_INDEX_OPTION);
+    if (shouldGenerateIndex != null) {
+      cmdLine.addParameter("-D"+ GlobalOptions.GENERATE_CLASSPATH_INDEX_OPTION +"=" + shouldGenerateIndex);
+    }
+    cmdLine.addParameter("-D"+ GlobalOptions.COMPILE_PARALLEL_OPTION +"=" + Boolean.toString(config.PARALLEL_COMPILATION));
+
+    boolean isProfilingMode = false;
+    final String additionalOptions = config.COMPILER_PROCESS_ADDITIONAL_VM_OPTIONS;
+    if (!StringUtil.isEmpty(additionalOptions)) {
+      final StringTokenizer tokenizer = new StringTokenizer(additionalOptions, " ", false);
+      while (tokenizer.hasMoreTokens()) {
+        final String option = tokenizer.nextToken();
+        if ("-Dprofiling.mode=true".equals(option)) {
+          isProfilingMode = true;
+        }
+        cmdLine.addParameter(option);
+      }
+    }
+
+    // debugging
+    final int debugPort = Registry.intValue("compiler.process.debug.port");
+    if (debugPort > 0) {
+      cmdLine.addParameter("-XX:+HeapDumpOnOutOfMemoryError");
+      cmdLine.addParameter("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=" + debugPort);
+    }
+
+    if (Registry.is("compiler.process.use.memory.temp.cache")) {
+      cmdLine.addParameter("-D"+ GlobalOptions.USE_MEMORY_TEMP_CACHE_OPTION);
+    }
+    if (Registry.is("compiler.process.use.external.javac")) {
+      cmdLine.addParameter("-D"+ GlobalOptions.USE_EXTERNAL_JAVAC_OPTION);
+    }
+
+    // javac's VM should use the same default locale that IDEA uses in order for javac to print messages in 'correct' language
+    if (mySystemCharset != null) {
+      cmdLine.setCharset(mySystemCharset);
+      cmdLine.addParameter("-D" + CharsetToolkit.FILE_ENCODING_PROPERTY + "=" + mySystemCharset.name());
+    }
+    for (String name : new String[]{"user.language", "user.country", "user.region", PathManager.PROPERTY_HOME_PATH}) {
+      final String value = System.getProperty(name);
+      if (value != null) {
+        cmdLine.addParameter("-D" + name + "=" + value);
+      }
+    }
+
+    final File workDirectory = getBuildSystemDirectory();
+    workDirectory.mkdirs();
+    ensureLogConfigExists(workDirectory);
+
+    final List<String> cp = ClasspathBootstrap.getBuildProcessApplicationClasspath();
+    cp.add(compilerPath);
+    cp.addAll(myClasspathManager.getCompileServerPluginsClasspath(project));
+    if (isProfilingMode) {
+      cp.add(new File(workDirectory, "yjp-controller-api-redist.jar").getPath());
+      cmdLine.addParameter("-agentlib:yjpagent=disablej2ee,disablealloc,sessionname=ExternalBuild");
+    }
+
+    cmdLine.addParameter("-classpath");
+    cmdLine.addParameter(classpathToString(cp));
+
+    cmdLine.addParameter(BuildMain.class.getName());
+    cmdLine.addParameter("127.0.0.1");
+    cmdLine.addParameter(Integer.toString(port));
+    cmdLine.addParameter(sessionId.toString());
+
+    cmdLine.addParameter(FileUtil.toSystemIndependentName(workDirectory.getPath()));
+
+    cmdLine.setWorkDirectory(workDirectory);
+
+    final Process process = cmdLine.createProcess();
+
+    return new OSProcessHandler(process, null, mySystemCharset) {
+      @Override
+      protected boolean shouldDestroyProcessRecursively() {
+        return true;
+      }
+    };
+  }
+
+  public File getBuildSystemDirectory() {
+    return new File(mySystemDirectory, SYSTEM_ROOT);
+  }
+
+  @Nullable
+  public File getProjectSystemDirectory(Project project) {
+    final String projectPath = getProjectPath(project);
+    return projectPath != null? Utils.getDataStorageRoot(getBuildSystemDirectory(), projectPath) : null;
+  }
+
+  private static int getMinorVersion(String vs) {
+    final int dashIndex = vs.lastIndexOf('_');
+    if (dashIndex >= 0) {
+      StringBuilder builder = new StringBuilder();
+      for (int idx = dashIndex + 1; idx < vs.length(); idx++) {
+        final char ch = vs.charAt(idx);
+        if (Character.isDigit(ch)) {
+          builder.append(ch);
+        }
+        else {
+          break;
+        }
+      }
+      if (builder.length() > 0) {
+        try {
+          return Integer.parseInt(builder.toString());
+        }
+        catch (NumberFormatException ignored) {
+        }
+      }
+    }
+    return 0;
+  }
+
+  private static void ensureLogConfigExists(File workDirectory) {
+    final File logConfig = new File(workDirectory, LOGGER_CONFIG);
+    if (!logConfig.exists()) {
+      FileUtil.createIfDoesntExist(logConfig);
+      try {
+        final InputStream in = BuildMain.class.getResourceAsStream("/" + DEFAULT_LOGGER_CONFIG);
+        if (in != null) {
+          try {
+            final FileOutputStream out = new FileOutputStream(logConfig);
+            try {
+              FileUtil.copy(in, out);
+            }
+            finally {
+              out.close();
+            }
+          }
+          finally {
+            in.close();
+          }
+        }
+      }
+      catch (IOException e) {
+        LOG.error(e);
+      }
+    }
+  }
+
+  public void stopListening() {
+    final ChannelGroupFuture closeFuture = myAllOpenChannels.close();
+    closeFuture.awaitUninterruptibly();
+  }
+
+  private int startListening() throws Exception {
+    final ChannelFactory channelFactory = new NioServerSocketChannelFactory(myPooledThreadExecutor, myPooledThreadExecutor, 1);
+    final SimpleChannelUpstreamHandler channelRegistrar = new SimpleChannelUpstreamHandler() {
+      public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
+        myAllOpenChannels.add(e.getChannel());
+        super.channelOpen(ctx, e);
+      }
+
+      @Override
+      public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
+        myAllOpenChannels.remove(e.getChannel());
+        super.channelClosed(ctx, e);
+      }
+    };
+    ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() {
+      public ChannelPipeline getPipeline() throws Exception {
+        return Channels.pipeline(
+          channelRegistrar,
+          new ProtobufVarint32FrameDecoder(),
+          new ProtobufDecoder(CmdlineRemoteProto.Message.getDefaultInstance()),
+          new ProtobufVarint32LengthFieldPrepender(),
+          new ProtobufEncoder(),
+          myMessageDispatcher
+        );
+      }
+    };
+    final ServerBootstrap bootstrap = new ServerBootstrap(channelFactory);
+    bootstrap.setPipelineFactory(pipelineFactory);
+    bootstrap.setOption("child.tcpNoDelay", true);
+    bootstrap.setOption("child.keepAlive", true);
+    final int listenPort = NetUtils.findAvailableSocketPort();
+    final Channel serverChannel = bootstrap.bind(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), listenPort));
+    myAllOpenChannels.add(serverChannel);
+    return listenPort;
+  }
+
+  @TestOnly
+  public void stopWatchingProject(Project project) {
+    myProjectDataMap.remove(getProjectPath(project));
+  }
+
+  private static String classpathToString(List<String> cp) {
+    StringBuilder builder = new StringBuilder();
+    for (String file : cp) {
+      if (builder.length() > 0) {
+        builder.append(File.pathSeparator);
+      }
+      builder.append(FileUtil.toCanonicalPath(file));
+    }
+    return builder.toString();
+  }
+
+  private static class BuilderMessageHandlerWrapper implements BuilderMessageHandler {
+    private final DefaultMessageHandler myHandler;
+
+    public BuilderMessageHandlerWrapper(DefaultMessageHandler handler) {
+      myHandler = handler;
+    }
+
+    @Override
+    public void buildStarted(UUID sessionId) {
+      myHandler.buildStarted(sessionId);
+    }
+
+    @Override
+    public void handleBuildMessage(Channel channel, UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage msg) {
+      myHandler.handleBuildMessage(channel, sessionId, msg);
+    }
+
+    @Override
+    public void handleFailure(UUID sessionId, CmdlineRemoteProto.Message.Failure failure) {
+      myHandler.handleFailure(sessionId, failure);
+    }
+
+    @Override
+    public void sessionTerminated(UUID sessionId) {
+      myHandler.sessionTerminated(sessionId);
+    }
+  }
+
+  private class ProjectWatcher extends ProjectManagerAdapter {
+    private final Map<Project, MessageBusConnection> myConnections = new HashMap<Project, MessageBusConnection>();
+
+    @Override
+    public void projectOpened(final Project project) {
+      final MessageBusConnection conn = project.getMessageBus().connect();
+      myConnections.put(project, conn);
+      conn.subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() {
+        @Override
+        public void rootsChanged(final ModuleRootEvent event) {
+          final Object source = event.getSource();
+          if (source instanceof Project) {
+            clearState((Project)source);
+          }
+        }
+      });
+      conn.subscribe(ExecutionManager.EXECUTION_TOPIC, new ExecutionAdapter() {
+        @Override
+        public void processTerminated(@NotNull RunProfile runProfile, @NotNull ProcessHandler handler) {
+          scheduleAutoMake();
+        }
+      });
+      final String projectPath = getProjectPath(project);
+      Disposer.register(project, new Disposable() {
+        @Override
+        public void dispose() {
+          myProjectDataMap.remove(projectPath);
+        }
+      });
+      scheduleAutoMake(); // run automake on project opening
+    }
+
+    @Override
+    public boolean canCloseProject(Project project) {
+      cancelAutoMakeTasks(project);
+      return super.canCloseProject(project);
+    }
+
+    @Override
+    public void projectClosing(Project project) {
+      for (RequestFuture future : cancelAutoMakeTasks(project)) {
+        future.waitFor(500, TimeUnit.MILLISECONDS);
+      }
+    }
+
+    @Override
+    public void projectClosed(Project project) {
+      myProjectDataMap.remove(getProjectPath(project));
+      final MessageBusConnection conn = myConnections.remove(project);
+      if (conn != null) {
+        conn.disconnect();
+      }
+    }
+  }
+
+  private static class ProjectData {
+    final SequentialTaskExecutor taskQueue;
+    private final Set<String> myChanged = new THashSet<String>(FileUtil.PATH_HASHING_STRATEGY);
+    private final Set<String> myDeleted = new THashSet<String>(FileUtil.PATH_HASHING_STRATEGY);
+    private long myNextEventOrdinal = 0L;
+    private boolean myNeedRescan = true;
+
+    private ProjectData(SequentialTaskExecutor taskQueue) {
+      this.taskQueue = taskQueue;
+    }
+
+    public void addChanged(Collection<String> paths) {
+      if (!myNeedRescan) {
+        myDeleted.removeAll(paths);
+        myChanged.addAll(paths);
+      }
+    }
+
+    public void addDeleted(Collection<String> paths) {
+      if (!myNeedRescan) {
+        myChanged.removeAll(paths);
+        myDeleted.addAll(paths);
+      }
+    }
+
+    public CmdlineRemoteProto.Message.ControllerMessage.FSEvent createNextEvent() {
+      final CmdlineRemoteProto.Message.ControllerMessage.FSEvent.Builder builder =
+        CmdlineRemoteProto.Message.ControllerMessage.FSEvent.newBuilder();
+      builder.setOrdinal(++myNextEventOrdinal);
+      builder.addAllChangedPaths(myChanged);
+      myChanged.clear();
+      builder.addAllDeletedPaths(myDeleted);
+      myDeleted.clear();
+      return builder.build();
+    }
+
+    public boolean getAndResetRescanFlag() {
+      final boolean rescan = myNeedRescan;
+      myNeedRescan = false;
+      return rescan;
+    }
+
+    public void dropChanges() {
+      myNeedRescan = true;
+      myNextEventOrdinal = 0L;
+      myChanged.clear();
+      myDeleted.clear();
+    }
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/server/BuildMessageDispatcher.java b/java/compiler/impl/src/com/intellij/compiler/server/BuildMessageDispatcher.java
new file mode 100644
index 0000000..0c49759
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/server/BuildMessageDispatcher.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 com.intellij.compiler.server;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.containers.ConcurrentHashSet;
+import org.jboss.netty.channel.*;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.api.CmdlineProtoUtil;
+import org.jetbrains.jps.api.CmdlineRemoteProto;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+* @author Eugene Zhuravlev
+*         Date: 4/25/12
+*/
+class BuildMessageDispatcher extends SimpleChannelHandler {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.server.BuildMessageDispatcher");
+  private final Map<UUID, SessionData> myMessageHandlers = new ConcurrentHashMap<UUID, SessionData>();
+  private final Set<UUID> myCanceledSessions = new ConcurrentHashSet<UUID>();
+
+  public void registerBuildMessageHandler(UUID sessionId,
+                                          BuilderMessageHandler handler,
+                                          CmdlineRemoteProto.Message.ControllerMessage params) {
+    myMessageHandlers.put(sessionId, new SessionData(sessionId, handler, params));
+  }
+
+  @Nullable
+  public BuilderMessageHandler unregisterBuildMessageHandler(UUID sessionId) {
+    myCanceledSessions.remove(sessionId);
+    final SessionData data = myMessageHandlers.remove(sessionId);
+    return data != null? data.handler : null;
+  }
+
+  public void cancelSession(UUID sessionId) {
+    if (myCanceledSessions.add(sessionId)) {
+      final Channel channel = getConnectedChannel(sessionId);
+      if (channel != null) {
+        Channels.write(channel, CmdlineProtoUtil.toMessage(sessionId, CmdlineProtoUtil.createCancelCommand()));
+      }
+    }
+  }
+
+  @Nullable
+  public Channel getConnectedChannel(final UUID sessionId) {
+    final Channel channel = getAssociatedChannel(sessionId);
+    return channel != null && channel.isConnected()? channel : null;
+  }
+
+  @Nullable
+  public Channel getAssociatedChannel(final UUID sessionId) {
+    final SessionData data = myMessageHandlers.get(sessionId);
+    return data != null? data.channel : null;
+  }
+
+
+  @Override
+  public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
+    final CmdlineRemoteProto.Message message = (CmdlineRemoteProto.Message)e.getMessage();
+
+    SessionData sessionData = (SessionData)ctx.getAttachment();
+
+    UUID sessionId;
+    if (sessionData == null) {
+      // this is the first message for this session, so fill session data with missing info
+      final CmdlineRemoteProto.Message.UUID id = message.getSessionId();
+      sessionId = new UUID(id.getMostSigBits(), id.getLeastSigBits());
+
+      sessionData = myMessageHandlers.get(sessionId);
+      if (sessionData != null) {
+        sessionData.channel = ctx.getChannel();
+        ctx.setAttachment(sessionData);
+      }
+      if (myCanceledSessions.contains(sessionId)) {
+        Channels.write(ctx.getChannel(), CmdlineProtoUtil.toMessage(sessionId, CmdlineProtoUtil.createCancelCommand()));
+      }
+    }
+    else {
+      sessionId = sessionData.sessionId;
+    }
+
+    final BuilderMessageHandler handler = sessionData != null? sessionData.handler : null;
+    if (handler == null) {
+      // todo
+      LOG.info("No message handler registered for session " + sessionId);
+      return;
+    }
+
+    final CmdlineRemoteProto.Message.Type messageType = message.getType();
+    switch (messageType) {
+      case FAILURE:
+        handler.handleFailure(sessionId, message.getFailure());
+        break;
+
+      case BUILDER_MESSAGE:
+        final CmdlineRemoteProto.Message.BuilderMessage builderMessage = message.getBuilderMessage();
+        final CmdlineRemoteProto.Message.BuilderMessage.Type msgType = builderMessage.getType();
+        if (msgType == CmdlineRemoteProto.Message.BuilderMessage.Type.PARAM_REQUEST) {
+          final CmdlineRemoteProto.Message.ControllerMessage params = sessionData.params;
+          if (params != null) {
+            handler.buildStarted(sessionId);
+            sessionData.params = null;
+            Channels.write(ctx.getChannel(), CmdlineProtoUtil.toMessage(sessionId, params));
+          }
+          else {
+            cancelSession(sessionId);
+          }
+        }
+        else {
+          handler.handleBuildMessage(ctx.getChannel(), sessionId, builderMessage);
+        }
+        break;
+
+      default:
+        LOG.info("Unsupported message type " + messageType);
+        break;
+    }
+  }
+
+  @Override
+  public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
+    try {
+      super.channelClosed(ctx, e);
+    }
+    finally {
+      final SessionData sessionData = (SessionData)ctx.getAttachment();
+      if (sessionData != null) {
+        final BuilderMessageHandler handler = unregisterBuildMessageHandler(sessionData.sessionId);
+        if (handler != null) {
+          // notify the handler only if it has not been notified yet
+          handler.sessionTerminated(sessionData.sessionId);
+        }
+      }
+    }
+  }
+
+  @Override
+  public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
+    final Throwable cause = e.getCause();
+    if (cause != null) {
+      LOG.info(cause);
+    }
+    ctx.sendUpstream(e);
+  }
+
+  @Override
+  public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
+    try {
+      super.channelDisconnected(ctx, e);
+    }
+    finally {
+      final SessionData sessionData = (SessionData)ctx.getAttachment();
+      if (sessionData != null) { // this is the context corresponding to some session
+        final Channel channel = e.getChannel();
+        ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
+          @Override
+          public void run() {
+            channel.close();
+          }
+        });
+      }
+    }
+  }
+
+  private static final class SessionData {
+    final UUID sessionId;
+    final BuilderMessageHandler handler;
+    volatile CmdlineRemoteProto.Message.ControllerMessage params;
+    volatile Channel channel;
+
+    private SessionData(UUID sessionId, BuilderMessageHandler handler, CmdlineRemoteProto.Message.ControllerMessage params) {
+      this.sessionId = sessionId;
+      this.handler = handler;
+      this.params = params;
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/server/BuilderMessageHandler.java b/java/compiler/impl/src/com/intellij/compiler/server/BuilderMessageHandler.java
new file mode 100644
index 0000000..fadf63b
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/server/BuilderMessageHandler.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 com.intellij.compiler.server;
+
+import org.jboss.netty.channel.Channel;
+import org.jetbrains.jps.api.CmdlineRemoteProto;
+
+import java.util.UUID;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: 4/25/12
+ */
+public interface BuilderMessageHandler {
+  void buildStarted(UUID sessionId);
+  
+  void handleBuildMessage(Channel channel, UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage msg);
+
+  void handleFailure(UUID sessionId, CmdlineRemoteProto.Message.Failure failure);
+
+  void sessionTerminated(UUID sessionId);
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/server/CompileServerPathProvider.java b/java/compiler/impl/src/com/intellij/compiler/server/CompileServerPathProvider.java
new file mode 100644
index 0000000..f4f2bb4
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/server/CompileServerPathProvider.java
@@ -0,0 +1,30 @@
+/*
+ * 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 com.intellij.compiler.server;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * Project-level extension points to dynamically vary classpath of external build process.
+ */
+public interface CompileServerPathProvider {
+  ExtensionPointName<CompileServerPathProvider> EP_NAME = ExtensionPointName.create("com.intellij.compileServer.pathProvider");
+
+  @NotNull List<String> getClassPath();
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/server/CompileServerPlugin.java b/java/compiler/impl/src/com/intellij/compiler/server/CompileServerPlugin.java
new file mode 100644
index 0000000..2040b6c
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/server/CompileServerPlugin.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 com.intellij.compiler.server;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.extensions.PluginAware;
+import com.intellij.openapi.extensions.PluginDescriptor;
+import com.intellij.util.xmlb.annotations.Attribute;
+
+/**
+ * @author nik
+ */
+public class CompileServerPlugin implements PluginAware {
+  public static final ExtensionPointName<CompileServerPlugin> EP_NAME = ExtensionPointName.create("com.intellij.compileServer.plugin");
+
+  private PluginDescriptor myPluginDescriptor;
+  private String myClasspath;
+
+  /**
+   * <p>Specifies semicolon-separated list of paths which should be added to the classpath of the compile server.
+   * The paths are relative to the plugin 'lib' directory.</p>
+   *
+   * <p>In the development mode the name of each file without extension is treated as a module name and the output directory of the module
+   * is added to the classpath. If such file doesn't exists the jar is searched under 'lib' directory of the plugin sources home directory.</p>
+   */
+  @Attribute("classpath")
+  public String getClasspath() {
+    return myClasspath;
+  }
+
+  @SuppressWarnings("UnusedDeclaration")
+  public void setClasspath(String classpath) {
+    myClasspath = classpath;
+  }
+
+  public PluginDescriptor getPluginDescriptor() {
+    return myPluginDescriptor;
+  }
+
+  @Override
+  public final void setPluginDescriptor(PluginDescriptor pluginDescriptor) {
+    myPluginDescriptor = pluginDescriptor;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/server/CustomBuilderMessageHandler.java b/java/compiler/impl/src/com/intellij/compiler/server/CustomBuilderMessageHandler.java
new file mode 100644
index 0000000..74bf679
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/server/CustomBuilderMessageHandler.java
@@ -0,0 +1,29 @@
+/*
+ * 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 com.intellij.compiler.server;
+
+import com.intellij.util.messages.Topic;
+
+import java.util.EventListener;
+
+/**
+ * @author nik
+ */
+public interface CustomBuilderMessageHandler extends EventListener {
+  Topic<CustomBuilderMessageHandler> TOPIC = Topic.create("custom builder message", CustomBuilderMessageHandler.class);
+
+  void messageReceived(String builderId, String messageType, String messageText);
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/server/DefaultMessageHandler.java b/java/compiler/impl/src/com/intellij/compiler/server/DefaultMessageHandler.java
new file mode 100644
index 0000000..add20bd
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/server/DefaultMessageHandler.java
@@ -0,0 +1,361 @@
+/*
+ * 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 com.intellij.compiler.server;
+
+import com.intellij.compiler.make.CachingSearcher;
+import com.intellij.lang.StdLanguages;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.project.DumbService;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.search.*;
+import com.intellij.psi.util.PsiUtil;
+import com.intellij.util.cls.ClsUtil;
+import com.intellij.util.concurrency.SequentialTaskExecutor;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.Channels;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.api.CmdlineProtoUtil;
+import org.jetbrains.jps.api.CmdlineRemoteProto;
+
+import java.util.*;
+import java.util.concurrent.Executor;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: 4/18/12
+ */
+public abstract class DefaultMessageHandler implements BuilderMessageHandler {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.server.DefaultMessageHandler");
+  private final int MAX_CONSTANT_SEARCHES = Registry.intValue("compiler.max.static.constants.searches");
+  private final Project myProject;
+  private int myConstantSearchesCount = 0;
+  private final CachingSearcher mySearcher;
+  private final SequentialTaskExecutor myTaskExecutor = new SequentialTaskExecutor(new Executor() {
+    @Override
+    public void execute(Runnable command) {
+      ApplicationManager.getApplication().executeOnPooledThread(command);
+    }
+  });
+
+  protected DefaultMessageHandler(Project project) {
+    myProject = project;
+    mySearcher = new CachingSearcher(project);
+  }
+
+  @Override
+  public void buildStarted(UUID sessionId) {
+  }
+
+  @Override
+  public final void handleBuildMessage(final Channel channel, final UUID sessionId, final CmdlineRemoteProto.Message.BuilderMessage msg) {
+    switch (msg.getType()) {
+      case BUILD_EVENT:
+        handleBuildEvent(sessionId, msg.getBuildEvent());
+        break;
+      case COMPILE_MESSAGE:
+        handleCompileMessage(sessionId, msg.getCompileMessage());
+        break;
+      case CONSTANT_SEARCH_TASK:
+        final CmdlineRemoteProto.Message.BuilderMessage.ConstantSearchTask task = msg.getConstantSearchTask();
+        myTaskExecutor.submit(new Runnable() {
+          @Override
+          public void run() {
+            handleConstantSearchTask(channel, sessionId, task);
+          }
+        });
+        break;
+    }
+  }
+
+  protected abstract void handleCompileMessage(UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.CompileMessage message);
+
+  protected abstract void handleBuildEvent(UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.BuildEvent event);
+
+  private void handleConstantSearchTask(Channel channel, UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.ConstantSearchTask task) {
+    final String ownerClassName = task.getOwnerClassName();
+    final String fieldName = task.getFieldName();
+    final int accessFlags = task.getAccessFlags();
+    final boolean accessChanged = task.getIsAccessChanged();
+    final boolean isRemoved = task.getIsFieldRemoved();
+    final Ref<Boolean> isSuccess = Ref.create(Boolean.TRUE);
+    final Set<String> affectedPaths = Collections.synchronizedSet(new HashSet<String>()); // PsiSearchHelper runs multiple threads
+    try {
+      if (isDumbMode()) {
+        // do not wait until dumb mode finishes
+        isSuccess.set(Boolean.FALSE);
+        LOG.debug("Constant search task: cannot search in dumb mode");
+      }
+      else {
+        ApplicationManager.getApplication().runReadAction(new Runnable() {
+          public void run() {
+            try {
+              String qualifiedName = ownerClassName.replace('$', '.');
+              final PsiClass[] classes = JavaPsiFacade.getInstance(myProject).findClasses(qualifiedName, GlobalSearchScope.allScope(myProject));
+              if (isRemoved) {
+                if (classes.length > 0) {
+                  for (PsiClass aClass : classes) {
+                    final boolean success = performRemovedConstantSearch(aClass, fieldName, accessFlags, affectedPaths);
+                    if (!success) {
+                      isSuccess.set(Boolean.FALSE);
+                      break;
+                    }
+                  }
+                }
+                else {
+                  isSuccess.set(
+                    performRemovedConstantSearch(null, fieldName, accessFlags, affectedPaths)
+                  );
+                }
+              }
+              else {
+                if (classes.length > 0) {
+                  boolean foundAtLeastOne = false;
+                  for (PsiClass aClass : classes) {
+                    PsiField changedField = null;
+                    for (PsiField psiField : aClass.getFields()) {
+                      if (fieldName.equals(psiField.getName())) {
+                        changedField = psiField;
+                        break;
+                      }
+                    }
+                    if (changedField == null) {
+                      continue;
+                    }
+                    foundAtLeastOne = true;
+                    final boolean sucess = performChangedConstantSearch(aClass, changedField, accessFlags, accessChanged, affectedPaths);
+                    if (!sucess) {
+                      isSuccess.set(Boolean.FALSE);
+                      break;
+                    }
+                  }
+                  if (!foundAtLeastOne) {
+                    isSuccess.set(Boolean.FALSE);
+                    LOG.debug("Constant search task: field " + fieldName + " not found in classes " + qualifiedName);
+                  }
+                }
+                else {
+                  isSuccess.set(Boolean.FALSE);
+                  LOG.debug("Constant search task: class " + qualifiedName + " not found");
+                }
+              }
+            }
+            catch (Throwable e) {
+              isSuccess.set(Boolean.FALSE);
+              LOG.debug("Constant search task: failed with message " + e.getMessage());
+            }
+          }
+        });
+      }
+    }
+    finally {
+      final CmdlineRemoteProto.Message.ControllerMessage.ConstantSearchResult.Builder builder = CmdlineRemoteProto.Message.ControllerMessage.ConstantSearchResult.newBuilder();
+      builder.setOwnerClassName(ownerClassName);
+      builder.setFieldName(fieldName);
+      if (isSuccess.get()) {
+        builder.setIsSuccess(true);
+        builder.addAllPath(affectedPaths);
+        LOG.debug("Constant search task: " + affectedPaths.size() + " affected files found");
+      }
+      else {
+        builder.setIsSuccess(false);
+        LOG.debug("Constant search task: unsuccessful");
+      }
+      Channels.write(channel, CmdlineProtoUtil.toMessage(sessionId, CmdlineRemoteProto.Message.ControllerMessage.newBuilder().setType(
+        CmdlineRemoteProto.Message.ControllerMessage.Type.CONSTANT_SEARCH_RESULT).setConstantSearchResult(builder.build()).build()
+      ));
+    }
+  }
+
+  private boolean isDumbMode() {
+    final DumbService dumbService = DumbService.getInstance(myProject);
+    boolean isDumb = dumbService.isDumb();
+    if (isDumb) {
+      // wait some time
+      for (int idx = 0; idx < 5; idx++) {
+        try {
+          Thread.sleep(10L);
+        }
+        catch (InterruptedException ignored) {
+        }
+        isDumb = dumbService.isDumb();
+        if (!isDumb) {
+          break;
+        }
+      }
+    }
+    return isDumb;
+  }
+
+  private boolean performChangedConstantSearch(PsiClass aClass, PsiField field, int accessFlags, boolean isAccessibilityChange, Set<String> affectedPaths) {
+    if (!isAccessibilityChange && ClsUtil.isPrivate(accessFlags)) {
+      return true; // optimization: don't need to search, cause may be used only in this class
+    }
+    final Set<PsiElement> usages = new HashSet<PsiElement>();
+    try {
+      addUsages(field, usages, isAccessibilityChange);
+      for (final PsiElement usage : usages) {
+        affect(usage, affectedPaths);
+        //final PsiClass ownerClass = getOwnerClass(usage);
+        //if (ownerClass != null && !ownerClass.equals(aClass)) {
+        //  affect(ownerClass, affectedPaths);
+        //}
+        //else if (ownerClass == null) {
+        //  affect(usage, affectedPaths);
+        //}
+      }
+    }
+    catch (PsiInvalidElementAccessException e) {
+      LOG.debug("Constant search task: PIEAE thrown while searching of usages of changed constant");
+      return false;
+    }
+    catch (ProcessCanceledException e) {
+      LOG.debug("Constant search task: PCE thrown while searching of usages of changed constant");
+      return false;
+    }
+    return true;
+  }
+
+
+  private boolean performRemovedConstantSearch(@Nullable final PsiClass aClass, String fieldName, int accessFlags, final Set<String> affectedPaths) {
+    SearchScope searchScope = GlobalSearchScope.projectScope(myProject);
+    if (aClass != null && ClsUtil.isPackageLocal(accessFlags)) {
+      final PsiFile containingFile = aClass.getContainingFile();
+      if (containingFile instanceof PsiJavaFile) {
+        final String packageName = ((PsiJavaFile)containingFile).getPackageName();
+        final PsiPackage aPackage = JavaPsiFacade.getInstance(myProject).findPackage(packageName);
+        if (aPackage != null) {
+          searchScope = PackageScope.packageScope(aPackage, false);
+          searchScope = searchScope.intersectWith(aClass.getUseScope());
+        }
+      }
+    }
+    final PsiSearchHelper psiSearchHelper = PsiSearchHelper.SERVICE.getInstance(myProject);
+
+    final Ref<Boolean> result = new Ref<Boolean>(Boolean.TRUE);
+    processIdentifiers(psiSearchHelper, new PsiElementProcessor<PsiIdentifier>() {
+      @Override
+      public boolean execute(@NotNull PsiIdentifier identifier) {
+        try {
+          final PsiElement parent = identifier.getParent();
+          if (parent instanceof PsiReferenceExpression) {
+            final PsiClass ownerClass = getOwnerClass(parent);
+            if (ownerClass != null /*&& !ownerClass.equals(aClass)*/) {
+              if (ownerClass.getQualifiedName() != null) {
+                affect(ownerClass, affectedPaths);
+              }
+            }
+          }
+          return true;
+        }
+        catch (PsiInvalidElementAccessException e) {
+          result.set(Boolean.FALSE);
+          LOG.debug("Constant search task: PIEAE thrown while searching of usages of removed constant");
+          return false;
+        }
+      }
+    }, fieldName, searchScope, UsageSearchContext.IN_CODE);
+
+    return result.get();
+  }
+
+  private static void affect(PsiElement ownerClass, Set<String> affectedPaths) {
+    final PsiFile containingPsi = ownerClass.getContainingFile();
+    if (containingPsi != null) {
+      final VirtualFile vFile = containingPsi.getOriginalFile().getVirtualFile();
+      if (vFile != null) {
+        affectedPaths.add(vFile.getPath());
+      }
+    }
+  }
+
+  private static boolean processIdentifiers(PsiSearchHelper helper, @NotNull final PsiElementProcessor<PsiIdentifier> processor, @NotNull final String identifier, @NotNull SearchScope searchScope, short searchContext) {
+    TextOccurenceProcessor processor1 = new TextOccurenceProcessor() {
+      public boolean execute(PsiElement element, int offsetInElement) {
+        return !(element instanceof PsiIdentifier) || processor.execute((PsiIdentifier)element);
+      }
+    };
+    return helper.processElementsWithWord(processor1, searchScope, identifier, searchContext, true);
+  }
+
+  private void addUsages(PsiField psiField, Collection<PsiElement> usages, final boolean ignoreAccessScope) throws ProcessCanceledException {
+    final int count = myConstantSearchesCount;
+    if (count > MAX_CONSTANT_SEARCHES) {
+      throw new ProcessCanceledException();
+    }
+    Collection<PsiReference> references = mySearcher.findReferences(psiField, ignoreAccessScope)/*doFindReferences(searchHelper, psiField)*/;
+
+    myConstantSearchesCount++;
+
+    for (final PsiReference ref : references) {
+      if (!(ref instanceof PsiReferenceExpression)) {
+        continue;
+      }
+      PsiElement e = ref.getElement();
+      usages.add(e);
+      PsiField ownerField = getOwnerField(e);
+      if (ownerField != null) {
+        if (ownerField.hasModifierProperty(PsiModifier.FINAL)) {
+          PsiExpression initializer = ownerField.getInitializer();
+          if (initializer != null && PsiUtil.isConstantExpression(initializer)) {
+            // if the field depends on the compile-time-constant expression and is itself final
+            addUsages(ownerField, usages, ignoreAccessScope);
+          }
+        }
+      }
+    }
+  }
+
+  @Nullable
+  private static PsiClass getOwnerClass(PsiElement element) {
+    while (!(element instanceof PsiFile)) {
+      if (element instanceof PsiClass && element.getParent() instanceof PsiJavaFile) { // top-level class
+        final PsiClass psiClass = (PsiClass)element;
+        if (JspPsiUtil.isInJspFile(psiClass)) {
+          return null;
+        }
+        final PsiFile containingFile = psiClass.getContainingFile();
+        if (containingFile == null) {
+          return null;
+        }
+        return StdLanguages.JAVA.equals(containingFile.getLanguage())? psiClass : null;
+      }
+      element = element.getParent();
+    }
+    return null;
+  }
+
+  @Nullable
+  private static PsiField getOwnerField(PsiElement element) {
+    while (!(element instanceof PsiFile)) {
+      if (element instanceof PsiClass) {
+        break;
+      }
+      if (element instanceof PsiField) { // top-level class
+        return (PsiField)element;
+      }
+      element = element.getParent();
+    }
+    return null;
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/compiler/server/impl/CompileServerClasspathManager.java b/java/compiler/impl/src/com/intellij/compiler/server/impl/CompileServerClasspathManager.java
new file mode 100644
index 0000000..f95d151
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/compiler/server/impl/CompileServerClasspathManager.java
@@ -0,0 +1,135 @@
+/*
+ * 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 com.intellij.compiler.server.impl;
+
+import com.intellij.compiler.server.CompileServerPathProvider;
+import com.intellij.compiler.server.CompileServerPlugin;
+import com.intellij.ide.plugins.IdeaPluginDescriptor;
+import com.intellij.ide.plugins.PluginManager;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.extensions.PluginId;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.PathUtil;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class CompileServerClasspathManager {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.server.impl.CompileServerClasspathManager");
+
+  private List<String> myCompileServerPluginsClasspath;
+
+  public List<String> getCompileServerPluginsClasspath(Project project) {
+    List<String> staticClasspath = getStaticClasspath();
+    List<String> dynamicClasspath = getDynamicClasspath(project);
+
+    if (dynamicClasspath.isEmpty()) {
+      return staticClasspath;
+    }
+    else {
+      dynamicClasspath.addAll(staticClasspath);
+      return dynamicClasspath;
+    }
+  }
+
+  private List<String> getStaticClasspath() {
+    if (myCompileServerPluginsClasspath == null) {
+      myCompileServerPluginsClasspath = computeCompileServerPluginsClasspath();
+    }
+    return myCompileServerPluginsClasspath;
+  }
+
+  private static List<String> computeCompileServerPluginsClasspath() {
+    final List<String> classpath = ContainerUtil.newArrayList();
+    for (CompileServerPlugin serverPlugin : CompileServerPlugin.EP_NAME.getExtensions()) {
+      final PluginId pluginId = serverPlugin.getPluginDescriptor().getPluginId();
+      final IdeaPluginDescriptor plugin = PluginManager.getPlugin(pluginId);
+      LOG.assertTrue(plugin != null, pluginId);
+      final File baseFile = plugin.getPath();
+      if (baseFile.isFile()) {
+        classpath.add(baseFile.getPath());
+      }
+      else if (baseFile.isDirectory()) {
+        for (String relativePath : StringUtil.split(serverPlugin.getClasspath(), ";")) {
+          final File jarFile = new File(new File(baseFile, "lib"), relativePath);
+          File classesDir = new File(baseFile, "classes");
+          if (jarFile.exists()) {
+            classpath.add(jarFile.getPath());
+          }
+          else if (classesDir.isDirectory()) {
+            //'plugin run configuration': all module output are copied to 'classes' folder
+            classpath.add(classesDir.getPath());
+          }
+          else {
+            //development mode: add directory out/classes/production/<jar-name> to classpath, assuming that jar-name is equal to module name
+            final String moduleName = FileUtil.getNameWithoutExtension(PathUtil.getFileName(relativePath));
+            final File dir = new File(baseFile.getParentFile(), moduleName);
+            if (dir.exists()) {
+              classpath.add(dir.getPath());
+            }
+            else {
+              //looks like <jar-name> refers to a library, try to find it under <plugin-dir>/lib
+              File pluginDir = getPluginDir(plugin);
+              if (pluginDir != null) {
+                File libraryFile = new File(pluginDir, "lib" + File.separator + PathUtil.getFileName(relativePath));
+                if (libraryFile.exists()) {
+                  classpath.add(libraryFile.getPath());
+                }
+                else {
+                  LOG.error("Cannot add plugin '" + plugin.getName() + "' to external compiler classpath: " +
+                            "library " + libraryFile.getAbsolutePath() + " not found");
+                }
+              }
+              else {
+                LOG.error("Cannot add plugin '" + plugin.getName() + "' to external compiler classpath: home directory of plugin not found");
+              }
+            }
+          }
+        }
+      }
+    }
+    return classpath;
+  }
+
+  @Nullable
+  private static File getPluginDir(IdeaPluginDescriptor plugin) {
+    String pluginDirName = StringUtil.getShortName(plugin.getPluginId().getIdString());
+    String[] roots = {PathManager.getHomePath(), PathManager.getHomePath() + File.separator + "community"};
+    for (String root : roots) {
+      File pluginDir = new File(root, "plugins" + File.separator + pluginDirName);
+      if (pluginDir.isDirectory()) {
+        return pluginDir;
+      }
+    }
+    return null;
+  }
+
+  private static List<String> getDynamicClasspath(Project project) {
+    List<String> classpath = ContainerUtil.newArrayList();
+    for (CompileServerPathProvider provider : project.getExtensions(CompileServerPathProvider.EP_NAME)) {
+      classpath.addAll(provider.getClassPath());
+    }
+    return classpath;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/openapi/compiler/ex/CompileContextEx.java b/java/compiler/impl/src/com/intellij/openapi/compiler/ex/CompileContextEx.java
new file mode 100644
index 0000000..8f8f4e3
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/openapi/compiler/ex/CompileContextEx.java
@@ -0,0 +1,59 @@
+/*
+ * 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 com.intellij.openapi.compiler.ex;
+
+import com.intellij.compiler.make.DependencyCache;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.CompilerMessage;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.Set;
+
+public interface CompileContextEx extends CompileContext {
+  DependencyCache getDependencyCache();
+
+  @Nullable
+  VirtualFile getSourceFileByOutputFile(VirtualFile outputFile);
+
+  void addMessage(CompilerMessage message);
+
+  @NotNull
+  Set<VirtualFile> getTestOutputDirectories();
+  
+  /**
+   * the same as FileIndex.isInTestSourceContent(), but takes into account generated output dirs
+   */
+  boolean isInTestSourceContent(@NotNull VirtualFile fileOrDir);
+
+  boolean isInSourceContent(@NotNull VirtualFile fileOrDir);
+
+  void addScope(CompileScope additionalScope);
+
+  long getStartCompilationStamp();
+
+  void recalculateOutputDirs();
+
+  void markGenerated(Collection<VirtualFile> files);
+
+  boolean isGenerated(VirtualFile file);
+
+  void assignModule(@NotNull VirtualFile root, @NotNull Module module, boolean isTestSource, @Nullable com.intellij.openapi.compiler.Compiler compiler);
+}
diff --git a/java/compiler/impl/src/com/intellij/openapi/compiler/generic/BuildTarget.java b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/BuildTarget.java
new file mode 100644
index 0000000..27f16ad
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/BuildTarget.java
@@ -0,0 +1,39 @@
+/*
+ * 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 com.intellij.openapi.compiler.generic;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public abstract class BuildTarget {
+  public static final BuildTarget DEFAULT = new BuildTarget() {
+    @NotNull
+    @Override
+    public String getId() {
+      return "<default>";
+    }
+  };
+
+  @NotNull
+  public abstract String getId();
+
+  @Override
+  public String toString() {
+    return "Build Target: " + getId();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/openapi/compiler/generic/CompileItem.java b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/CompileItem.java
new file mode 100644
index 0000000..3bd9972
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/CompileItem.java
@@ -0,0 +1,41 @@
+/*
+ * 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 com.intellij.openapi.compiler.generic;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public abstract class CompileItem<Key, SourceState, OutputState> {
+  @NotNull
+  public abstract Key getKey();
+
+  public abstract boolean isSourceUpToDate(@NotNull SourceState state);
+
+  @NotNull
+  public abstract SourceState computeSourceState();
+
+
+  public abstract boolean isOutputUpToDate(@NotNull OutputState state);
+
+  @NotNull
+  public abstract OutputState computeOutputState();
+
+  public boolean isExcluded() {
+    return false;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/openapi/compiler/generic/DummyPersistentState.java b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/DummyPersistentState.java
new file mode 100644
index 0000000..b3d66ce
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/DummyPersistentState.java
@@ -0,0 +1,44 @@
+/*
+ * 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 com.intellij.openapi.compiler.generic;
+
+import com.intellij.util.io.DataExternalizer;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+/**
+ * @author nik
+ */
+public class DummyPersistentState {
+  public static final DummyPersistentState INSTANCE = new DummyPersistentState();
+  public static final DataExternalizer<DummyPersistentState> EXTERNALIZER = new DummyPersistentStateExternalizer();
+
+  private DummyPersistentState() {
+  }
+
+  private static class DummyPersistentStateExternalizer implements DataExternalizer<DummyPersistentState> {
+    @Override
+    public void save(DataOutput out, DummyPersistentState value) throws IOException {
+    }
+
+    @Override
+    public DummyPersistentState read(DataInput in) throws IOException {
+      return INSTANCE;
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/openapi/compiler/generic/GenericCompiler.java b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/GenericCompiler.java
new file mode 100644
index 0000000..b09c8e5
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/GenericCompiler.java
@@ -0,0 +1,72 @@
+/*
+ * 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 com.intellij.openapi.compiler.generic;
+
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.Compiler;
+import com.intellij.util.io.DataExternalizer;
+import com.intellij.util.io.EnumeratorStringDescriptor;
+import com.intellij.util.io.KeyDescriptor;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public abstract class GenericCompiler<Key, SourceState, OutputState> implements Compiler {
+  protected static final KeyDescriptor<String> STRING_KEY_DESCRIPTOR = new EnumeratorStringDescriptor();
+  private final String myId;
+  private final int myVersion;
+  private final CompileOrderPlace myOrderPlace;
+
+  protected GenericCompiler(@NotNull String id, int version, @NotNull CompileOrderPlace orderPlace) {
+    myId = id;
+    myVersion = version;
+    myOrderPlace = orderPlace;
+  }
+
+  @NotNull
+  public abstract KeyDescriptor<Key> getItemKeyDescriptor();
+  @NotNull
+  public abstract DataExternalizer<SourceState> getSourceStateExternalizer();
+  @NotNull
+  public abstract DataExternalizer<OutputState> getOutputStateExternalizer();
+
+  @NotNull
+  public abstract GenericCompilerInstance<?, ? extends CompileItem<Key, SourceState, OutputState>, Key, SourceState, OutputState> createInstance(@NotNull CompileContext context);
+
+  public final String getId() {
+    return myId;
+  }
+
+  public final int getVersion() {
+    return myVersion;
+  }
+
+  @Override
+  public boolean validateConfiguration(CompileScope scope) {
+    return true;
+  }
+
+  public CompileOrderPlace getOrderPlace() {
+    return myOrderPlace;
+  }
+
+  public static enum CompileOrderPlace {
+    CLASS_INSTRUMENTING, CLASS_POST_PROCESSING, PACKAGING, VALIDATING
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/openapi/compiler/generic/GenericCompilerCacheState.java b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/GenericCompilerCacheState.java
new file mode 100644
index 0000000..bfc4468
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/GenericCompilerCacheState.java
@@ -0,0 +1,48 @@
+/*
+ * 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 com.intellij.openapi.compiler.generic;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class GenericCompilerCacheState<Key, SourceState, OutputState> {
+  private final Key myKey;
+  private final SourceState mySourceState;
+  private final OutputState myOutputState;
+
+  public GenericCompilerCacheState(@NotNull Key key, @NotNull SourceState sourceState, @NotNull OutputState outputState) {
+    myKey = key;
+    mySourceState = sourceState;
+    myOutputState = outputState;
+  }
+
+  @NotNull
+  public Key getKey() {
+    return myKey;
+  }
+
+  @NotNull
+  public SourceState getSourceState() {
+    return mySourceState;
+  }
+
+  @NotNull
+  public OutputState getOutputState() {
+    return myOutputState;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/openapi/compiler/generic/GenericCompilerInstance.java b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/GenericCompilerInstance.java
new file mode 100644
index 0000000..6bb9274
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/GenericCompilerInstance.java
@@ -0,0 +1,61 @@
+/*
+ * 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 com.intellij.openapi.compiler.generic;
+
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class GenericCompilerInstance<T extends BuildTarget, Item extends CompileItem<Key, SourceState, OutputState>, Key, SourceState, OutputState> {
+  protected final CompileContext myContext;
+
+  protected GenericCompilerInstance(CompileContext context) {
+    myContext = context;
+  }
+
+  protected Project getProject() {
+    return myContext.getProject();
+  }
+
+  @NotNull
+  public abstract List<T> getAllTargets();
+
+  @NotNull
+  public abstract List<T> getSelectedTargets();
+
+  public abstract void processObsoleteTarget(@NotNull String targetId, @NotNull List<GenericCompilerCacheState<Key, SourceState, OutputState>> obsoleteItems);
+
+
+  @NotNull
+  public abstract List<Item> getItems(@NotNull T target);
+
+  public abstract void processItems(@NotNull T target, @NotNull List<GenericCompilerProcessingItem<Item, SourceState, OutputState>> changedItems, @NotNull List<GenericCompilerCacheState<Key, SourceState, OutputState>> obsoleteItems,
+                                    @NotNull OutputConsumer<Item> consumer);
+
+  public interface OutputConsumer<Item extends CompileItem<?,?,?>> {
+    void addFileToRefresh(@NotNull File file);
+
+    void addDirectoryToRefresh(@NotNull File dir);
+
+    void addProcessedItem(@NotNull Item sourceItem);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/openapi/compiler/generic/GenericCompilerProcessingItem.java b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/GenericCompilerProcessingItem.java
new file mode 100644
index 0000000..e62a5b3
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/GenericCompilerProcessingItem.java
@@ -0,0 +1,49 @@
+/*
+ * 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 com.intellij.openapi.compiler.generic;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public class GenericCompilerProcessingItem<Item extends CompileItem<?, SourceState, OutputState>, SourceState, OutputState> {
+  private final Item myItem;
+  private final SourceState myCachedSourceState;
+  private final OutputState myCachedOutputState;
+
+  public GenericCompilerProcessingItem(@NotNull Item item, @Nullable SourceState cachedSourceState, @Nullable OutputState cachedOutputState) {
+    myItem = item;
+    myCachedSourceState = cachedSourceState;
+    myCachedOutputState = cachedOutputState;
+  }
+
+  @NotNull
+  public Item getItem() {
+    return myItem;
+  }
+
+  @Nullable
+  public SourceState getCachedSourceState() {
+    return myCachedSourceState;
+  }
+
+  @Nullable 
+  public OutputState getCachedOutputState() {
+    return myCachedOutputState;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/openapi/compiler/generic/VirtualFileCompileItem.java b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/VirtualFileCompileItem.java
new file mode 100644
index 0000000..b1e5394
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/VirtualFileCompileItem.java
@@ -0,0 +1,53 @@
+/*
+ * 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 com.intellij.openapi.compiler.generic;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public abstract class VirtualFileCompileItem<OutputState> extends CompileItem<String, VirtualFilePersistentState, OutputState> {
+  protected final VirtualFile myFile;
+
+  public VirtualFileCompileItem(@NotNull VirtualFile file) {
+    myFile = file;
+  }
+
+  @NotNull
+  public VirtualFile getFile() {
+    return myFile;
+  }
+
+  @NotNull
+  @Override
+  public VirtualFilePersistentState computeSourceState() {
+    return new VirtualFilePersistentState(myFile.getTimeStamp());
+  }
+
+  @Override
+  public boolean isSourceUpToDate(@NotNull VirtualFilePersistentState state) {
+    return myFile.getTimeStamp() == state.getSourceTimestamp();
+  }
+
+  @NotNull
+  @Override
+  public String getKey() {
+    return myFile.getUrl();
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/openapi/compiler/generic/VirtualFilePersistentState.java b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/VirtualFilePersistentState.java
new file mode 100644
index 0000000..e9ea970
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/VirtualFilePersistentState.java
@@ -0,0 +1,51 @@
+/*
+ * 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 com.intellij.openapi.compiler.generic;
+
+import com.intellij.util.io.DataExternalizer;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+/**
+* @author nik
+*/
+public class VirtualFilePersistentState {
+  public static DataExternalizer<VirtualFilePersistentState> EXTERNALIZER = new VirtualFileStateExternalizer();
+  private final long mySourceTimestamp;
+
+  public VirtualFilePersistentState(long sourceTimestamp) {
+    mySourceTimestamp = sourceTimestamp;
+  }
+
+  public final long getSourceTimestamp() {
+    return mySourceTimestamp;
+  }
+
+  private static class VirtualFileStateExternalizer implements DataExternalizer<VirtualFilePersistentState> {
+    @Override
+    public void save(DataOutput out, VirtualFilePersistentState value) throws IOException {
+      out.writeLong(value.getSourceTimestamp());
+    }
+
+    @Override
+    public VirtualFilePersistentState read(DataInput in) throws IOException {
+      return new VirtualFilePersistentState(in.readLong());
+    }
+
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/openapi/compiler/generic/VirtualFileSetState.java b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/VirtualFileSetState.java
new file mode 100644
index 0000000..a22e80d
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/VirtualFileSetState.java
@@ -0,0 +1,91 @@
+/*
+ * 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 com.intellij.openapi.compiler.generic;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.io.DataExternalizer;
+import com.intellij.util.io.IOUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class VirtualFileSetState {
+  public static final DataExternalizer<VirtualFileSetState> EXTERNALIZER = new VirtualFileWithDependenciesExternalizer();
+  private Map<String, Long> myTimestamps = new HashMap<String, Long>();
+
+  public VirtualFileSetState() {
+  }
+
+  public VirtualFileSetState(Collection<? extends VirtualFile> files) {
+    for (VirtualFile file : files) {
+      addFile(file);
+    }
+  }
+
+  public void addFile(@NotNull VirtualFile file) {
+    myTimestamps.put(file.getUrl(), file.getTimeStamp());
+  }
+
+  public boolean isUpToDate(Set<? extends VirtualFile> files) {
+    if (files.size() != myTimestamps.size()) {
+      return false;
+    }
+
+    for (VirtualFile file : files) {
+      final Long timestamp = myTimestamps.get(file.getUrl());
+      if (timestamp == null || timestamp != file.getTimeStamp()) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+
+  private static class VirtualFileWithDependenciesExternalizer implements DataExternalizer<VirtualFileSetState> {
+    private byte[] myBuffer = IOUtil.allocReadWriteUTFBuffer();
+
+    @Override
+    public void save(DataOutput out, VirtualFileSetState value) throws IOException {
+      final Map<String, Long> dependencies = value.myTimestamps;
+      out.writeInt(dependencies.size());
+      for (Map.Entry<String, Long> entry : dependencies.entrySet()) {
+        IOUtil.writeUTFFast(myBuffer, out, entry.getKey());
+        out.writeLong(entry.getValue());
+      }
+    }
+
+    @Override
+    public VirtualFileSetState read(DataInput in) throws IOException {
+      final VirtualFileSetState state = new VirtualFileSetState();
+      int size = in.readInt();
+      while (size-- > 0) {
+        final String url = IOUtil.readUTFFast(myBuffer, in);
+        final long timestamp = in.readLong();
+        state.myTimestamps.put(url, timestamp);
+      }
+      return state;
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/openapi/compiler/generic/VirtualFileWithDependenciesState.java b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/VirtualFileWithDependenciesState.java
new file mode 100644
index 0000000..9f4a815
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/VirtualFileWithDependenciesState.java
@@ -0,0 +1,88 @@
+/*
+ * 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 com.intellij.openapi.compiler.generic;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.util.io.DataExternalizer;
+import com.intellij.util.io.IOUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author nik
+ */
+public class VirtualFileWithDependenciesState {
+  public static final DataExternalizer<VirtualFileWithDependenciesState> EXTERNALIZER = new VirtualFileWithDependenciesExternalizer();
+  private long mySourceTimestamp;
+  private Map<String, Long> myDependencies = new HashMap<String, Long>();
+
+  public VirtualFileWithDependenciesState(long sourceTimestamp) {
+    mySourceTimestamp = sourceTimestamp;
+  }
+
+  public void addDependency(@NotNull VirtualFile file) {
+    myDependencies.put(file.getUrl(), file.getTimeStamp());
+  }
+
+  public boolean isUpToDate(@NotNull VirtualFile sourceFile) {
+    if (sourceFile.getTimeStamp() != mySourceTimestamp) {
+      return false;
+    }
+
+    VirtualFileManager manager = VirtualFileManager.getInstance();
+    for (Map.Entry<String, Long> entry : myDependencies.entrySet()) {
+      final VirtualFile file = manager.findFileByUrl(entry.getKey());
+      if (file == null || file.getTimeStamp() != entry.getValue()) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+
+  private static class VirtualFileWithDependenciesExternalizer implements DataExternalizer<VirtualFileWithDependenciesState> {
+    private byte[] myBuffer = IOUtil.allocReadWriteUTFBuffer();
+
+    @Override
+    public void save(DataOutput out, VirtualFileWithDependenciesState value) throws IOException {
+      out.writeLong(value.mySourceTimestamp);
+      final Map<String, Long> dependencies = value.myDependencies;
+      out.writeInt(dependencies.size());
+      for (Map.Entry<String, Long> entry : dependencies.entrySet()) {
+        IOUtil.writeUTFFast(myBuffer, out, entry.getKey());
+        out.writeLong(entry.getValue());
+      }
+    }
+
+    @Override
+    public VirtualFileWithDependenciesState read(DataInput in) throws IOException {
+      final VirtualFileWithDependenciesState state = new VirtualFileWithDependenciesState(in.readLong());
+      int size = in.readInt();
+      while (size-- > 0) {
+        final String url = IOUtil.readUTFFast(myBuffer, in);
+        final long timestamp = in.readLong();
+        state.myDependencies.put(url, timestamp);
+      }
+      return state;
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/openapi/compiler/util/InspectionValidatorUtil.java b/java/compiler/impl/src/com/intellij/openapi/compiler/util/InspectionValidatorUtil.java
new file mode 100644
index 0000000..b41b5b0
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/openapi/compiler/util/InspectionValidatorUtil.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.
+ */
+package com.intellij.openapi.compiler.util;
+
+import com.intellij.compiler.impl.FileSetCompileScope;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.ex.CompileContextEx;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiFile;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.descriptors.ConfigFile;
+import gnu.trove.THashSet;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+
+/**
+ * @author peter
+ */
+public class InspectionValidatorUtil {
+  private InspectionValidatorUtil() {
+  }
+
+  public static void addDescriptor(@NotNull final Collection<VirtualFile> result, @Nullable final ConfigFile configFile) {
+    if (configFile != null) {
+      ContainerUtil.addIfNotNull(configFile.getVirtualFile(), result);
+    }
+  }
+
+  public static void addFile(@NotNull final Collection<VirtualFile> result, @Nullable final PsiFile psiFile) {
+    if (psiFile != null) {
+      ContainerUtil.addIfNotNull(psiFile.getVirtualFile(), result);
+    }
+  }
+
+
+  public static Collection<VirtualFile> expandCompileScopeIfNeeded(final Collection<VirtualFile> result, final CompileContext context) {
+    final ProjectFileIndex index = ProjectRootManager.getInstance(context.getProject()).getFileIndex();
+    final THashSet<VirtualFile> set = new THashSet<VirtualFile>();
+    final THashSet<Module> modules = new THashSet<Module>();
+    for (VirtualFile file : result) {
+      if (index.getSourceRootForFile(file) == null) {
+        set.add(file);
+        ContainerUtil.addIfNotNull(index.getModuleForFile(file), modules);
+      }
+    }
+    if (!set.isEmpty()) {
+      ((CompileContextEx)context).addScope(new FileSetCompileScope(set, modules.toArray(new Module[modules.size()])));
+    }
+    return result;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/openapi/compiler/util/InspectionValidatorWrapper.java b/java/compiler/impl/src/com/intellij/openapi/compiler/util/InspectionValidatorWrapper.java
new file mode 100644
index 0000000..1b84ca3
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/openapi/compiler/util/InspectionValidatorWrapper.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2000-2007 JetBrains s.r.o. All Rights Reserved.
+ */
+package com.intellij.openapi.compiler.util;
+
+import com.intellij.codeHighlighting.HighlightDisplayLevel;
+import com.intellij.codeInsight.daemon.HighlightDisplayKey;
+import com.intellij.codeInsight.daemon.impl.AnnotationHolderImpl;
+import com.intellij.codeInsight.daemon.impl.HighlightInfo;
+import com.intellij.codeInspection.*;
+import com.intellij.compiler.options.ValidationConfiguration;
+import com.intellij.lang.ExternalLanguageAnnotators;
+import com.intellij.lang.StdLanguages;
+import com.intellij.lang.annotation.Annotation;
+import com.intellij.lang.annotation.AnnotationSession;
+import com.intellij.lang.annotation.ExternalAnnotator;
+import com.intellij.lang.annotation.HighlightSeverity;
+import com.intellij.openapi.application.AccessToken;
+import com.intellij.openapi.application.ReadAction;
+import com.intellij.openapi.application.ReadActionProcessor;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.compiler.*;
+import com.intellij.openapi.compiler.options.ExcludedEntriesConfiguration;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.util.Processor;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.hash.LinkedHashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author peter
+ */
+public class InspectionValidatorWrapper implements Validator {
+  private final InspectionValidator myValidator;
+  private final PsiManager myPsiManager;
+  private final CompilerManager myCompilerManager;
+  private final InspectionManager myInspectionManager;
+  private final InspectionProjectProfileManager myProfileManager;
+  private final PsiDocumentManager myPsiDocumentManager;
+  private static final ThreadLocal<Boolean> ourCompilationThreads = new ThreadLocal<Boolean>() {
+    protected Boolean initialValue() {
+      return Boolean.FALSE;
+    }
+  };
+
+  public InspectionValidatorWrapper(final CompilerManager compilerManager, final InspectionManager inspectionManager,
+                                    final InspectionProjectProfileManager profileManager, final PsiDocumentManager psiDocumentManager,
+                                    final PsiManager psiManager, final InspectionValidator validator) {
+    myCompilerManager = compilerManager;
+    myInspectionManager = inspectionManager;
+    myProfileManager = profileManager;
+    myPsiDocumentManager = psiDocumentManager;
+    myPsiManager = psiManager;
+    myValidator = validator;
+  }
+
+  public static boolean isCompilationThread() {
+    return ourCompilationThreads.get().booleanValue();
+  }
+
+  private class MyValidatorProcessingItem implements ProcessingItem {
+    private final VirtualFile myVirtualFile;
+    private final PsiFile myPsiFile;
+    private PsiElementsValidityState myValidityState;
+
+    public MyValidatorProcessingItem(@NotNull final PsiFile psiFile) {
+      myPsiFile = psiFile;
+      myVirtualFile = psiFile.getVirtualFile();
+    }
+
+    @NotNull
+    public VirtualFile getFile() {
+      return myVirtualFile;
+    }
+
+    @Nullable
+    public ValidityState getValidityState() {
+      if (myValidityState == null) {
+        myValidityState = computeValidityState();
+      }
+      return myValidityState;
+    }
+
+    private PsiElementsValidityState computeValidityState() {
+      final PsiElementsValidityState state = new PsiElementsValidityState();
+      for (PsiElement psiElement : myValidator.getDependencies(myPsiFile)) {
+        state.addDependency(psiElement);
+      }
+      return state;
+    }
+
+    public PsiFile getPsiFile() {
+      return myPsiFile;
+    }
+  }
+
+  @NotNull
+  public ProcessingItem[] getProcessingItems(final CompileContext context) {
+    final Project project = context.getProject();
+    if (project.isDefault() || !ValidationConfiguration.shouldValidate(this, context)) {
+      return ProcessingItem.EMPTY_ARRAY;
+    }
+    final ExcludedEntriesConfiguration excludedEntriesConfiguration = ValidationConfiguration.getExcludedEntriesConfiguration(project);
+    final List<ProcessingItem> items = new ReadAction<List<ProcessingItem>>() {
+      protected void run(final Result<List<ProcessingItem>> result) {
+        final CompileScope compileScope = context.getCompileScope();
+        if (!myValidator.isAvailableOnScope(compileScope)) return;
+
+        final ArrayList<ProcessingItem> items = new ArrayList<ProcessingItem>();
+
+        final Processor<VirtualFile> processor = new ReadActionProcessor<VirtualFile>() {
+          @Override
+          public boolean processInReadAction(VirtualFile file) {
+            if (!file.isValid()) {
+              return true;
+            }
+
+            if (myCompilerManager.isExcludedFromCompilation(file) ||
+                excludedEntriesConfiguration.isExcluded(file)) {
+              return true;
+            }
+
+            final Module module = context.getModuleByFile(file);
+            if (module != null) {
+              final PsiFile psiFile = myPsiManager.findFile(file);
+              if (psiFile != null) {
+                items.add(new MyValidatorProcessingItem(psiFile));
+              }
+            }
+            return true;
+          }
+        };
+        ContainerUtil.process(myValidator.getFilesToProcess(myPsiManager.getProject(), context), processor);
+
+        result.setResult(items);
+      }
+    }.execute().getResultObject();
+    if (items == null) return ProcessingItem.EMPTY_ARRAY;
+
+    return items.toArray(new ProcessingItem[items.size()]);
+  }
+
+  public ProcessingItem[] process(final CompileContext context, final ProcessingItem[] items) {
+    context.getProgressIndicator().setText(myValidator.getProgressIndicatorText());
+
+    final List<ProcessingItem> processedItems = new ArrayList<ProcessingItem>();
+    final List<LocalInspectionTool> inspections = new ArrayList<LocalInspectionTool>();
+    for (final Class aClass : myValidator.getInspectionToolClasses(context)) {
+      try {
+        inspections.add((LocalInspectionTool)aClass.newInstance());
+      }
+      catch (RuntimeException e) {
+        throw e;
+      }
+      catch (Exception e) {
+        throw new Error(e);
+      }
+    }
+    for (int i = 0; i < items.length; i++) {
+      final MyValidatorProcessingItem item = (MyValidatorProcessingItem)items[i];
+      context.getProgressIndicator().checkCanceled();
+      context.getProgressIndicator().setFraction((double)i / items.length);
+
+      try {
+        ourCompilationThreads.set(Boolean.TRUE);
+
+        if (checkFile(inspections, item.getPsiFile(), context)) {
+          processedItems.add(item);
+        }
+      }
+      finally {
+        ourCompilationThreads.set(Boolean.FALSE);
+      }
+    }
+
+    return processedItems.toArray(new ProcessingItem[processedItems.size()]);
+  }
+
+  private boolean checkFile(List<LocalInspectionTool> inspections, final PsiFile file, final CompileContext context) {
+    if (!checkUnderReadAction(file, context, new Computable<Map<ProblemDescriptor, HighlightDisplayLevel>>() {
+      @Override
+      public Map<ProblemDescriptor, HighlightDisplayLevel> compute() {
+        return myValidator.checkAdditionally(file);
+      }
+    })) {
+      return false;
+    }
+
+    if (!checkUnderReadAction(file, context, new Computable<Map<ProblemDescriptor, HighlightDisplayLevel>>() {
+      @Override
+      public Map<ProblemDescriptor, HighlightDisplayLevel> compute() {
+        if (file instanceof XmlFile) {
+          return runXmlFileSchemaValidation((XmlFile)file);
+        }
+        return Collections.emptyMap();
+      }
+    })) return false;
+
+
+    final InspectionProfile inspectionProfile = myProfileManager.getInspectionProfile();
+    for (final LocalInspectionTool inspectionTool : inspections) {
+      if (!checkUnderReadAction(file, context, new Computable<Map<ProblemDescriptor, HighlightDisplayLevel>>() {
+        @Override
+        public Map<ProblemDescriptor, HighlightDisplayLevel> compute() {
+          if (getHighlightDisplayLevel(inspectionTool, inspectionProfile, file) != HighlightDisplayLevel.DO_NOT_SHOW) {
+            return runInspectionTool(file, inspectionTool, getHighlightDisplayLevel(inspectionTool, inspectionProfile, file)
+            );
+          }
+          return Collections.emptyMap();
+        }
+      })) return false;
+    }
+    return true;
+  }
+
+  private boolean checkUnderReadAction(PsiFile file, CompileContext context, Computable<Map<ProblemDescriptor, HighlightDisplayLevel>> runnable) {
+    AccessToken token = ReadAction.start();
+    try {
+      if (!file.isValid()) return false;
+
+      final Document document = myPsiDocumentManager.getCachedDocument(file);
+      if (document != null && myPsiDocumentManager.isUncommited(document)) {
+        final String url = file.getViewProvider().getVirtualFile().getUrl();
+        context.addMessage(CompilerMessageCategory.WARNING, CompilerBundle.message("warning.text.file.has.been.changed"), url, -1, -1);
+        return false;
+      }
+
+      if (reportProblems(context, runnable.compute())) return false;
+    }
+    finally {
+      token.finish();
+    }
+    return true;
+  }
+
+  private boolean reportProblems(CompileContext context, Map<ProblemDescriptor, HighlightDisplayLevel> problemsMap) {
+    if (problemsMap.isEmpty()) {
+      return false;
+    }
+
+    for (Map.Entry<ProblemDescriptor, HighlightDisplayLevel> entry : problemsMap.entrySet()) {
+      ProblemDescriptor problemDescriptor = entry.getKey();
+      final PsiElement element = problemDescriptor.getPsiElement();
+      final PsiFile psiFile = element.getContainingFile();
+      if (psiFile == null) continue;
+
+      final VirtualFile virtualFile = psiFile.getVirtualFile();
+      if (virtualFile == null) continue;
+
+      final CompilerMessageCategory category = myValidator.getCategoryByHighlightDisplayLevel(entry.getValue(), virtualFile, context);
+      final Document document = myPsiDocumentManager.getDocument(psiFile);
+
+      final int offset = problemDescriptor.getStartElement().getTextOffset();
+      assert document != null;
+      final int line = document.getLineNumber(offset);
+      final int column = offset - document.getLineStartOffset(line);
+      context.addMessage(category, problemDescriptor.getDescriptionTemplate(), virtualFile.getUrl(), line + 1, column + 1);
+    }
+    return true;
+  }
+
+  private static Map<ProblemDescriptor, HighlightDisplayLevel> runInspectionTool(final PsiFile file,
+                                                                                 final LocalInspectionTool inspectionTool,
+                                                                                 final HighlightDisplayLevel level) {
+    Map<ProblemDescriptor, HighlightDisplayLevel> problemsMap = new LinkedHashMap<ProblemDescriptor, HighlightDisplayLevel>();
+    for (CommonProblemDescriptor descriptor : InspectionRunningUtil.runInspectionOnFile(file, inspectionTool)) {
+      if (descriptor instanceof ProblemDescriptor) {
+        problemsMap.put((ProblemDescriptor)descriptor, level);
+      }
+    }
+    return problemsMap;
+  }
+
+  private static HighlightDisplayLevel getHighlightDisplayLevel(final LocalInspectionTool inspectionTool,
+                                                                final InspectionProfile inspectionProfile, PsiElement file) {
+    final HighlightDisplayKey key = HighlightDisplayKey.find(inspectionTool.getShortName());
+    return inspectionProfile.isToolEnabled(key, file) ? inspectionProfile.getErrorLevel(key, file) : HighlightDisplayLevel.DO_NOT_SHOW;
+  }
+
+  private Map<ProblemDescriptor, HighlightDisplayLevel> runXmlFileSchemaValidation(@NotNull XmlFile xmlFile) {
+    final AnnotationHolderImpl holder = new AnnotationHolderImpl(new AnnotationSession(xmlFile));
+
+    final List<ExternalAnnotator> annotators = ExternalLanguageAnnotators.allForFile(StdLanguages.XML, xmlFile);
+    for (ExternalAnnotator annotator : annotators) {
+      annotator.annotate(xmlFile, holder);
+    }
+
+    if (!holder.hasAnnotations()) return Collections.emptyMap();
+
+    Map<ProblemDescriptor, HighlightDisplayLevel> problemsMap = new LinkedHashMap<ProblemDescriptor, HighlightDisplayLevel>();
+    for (final Annotation annotation : holder) {
+      final HighlightInfo info = HighlightInfo.fromAnnotation(annotation);
+      if (info.getSeverity() == HighlightSeverity.INFORMATION) continue;
+
+      final PsiElement startElement = xmlFile.findElementAt(info.startOffset);
+      final PsiElement endElement = info.startOffset == info.endOffset ? startElement : xmlFile.findElementAt(info.endOffset - 1);
+      if (startElement == null || endElement == null) continue;
+
+      final ProblemDescriptor descriptor =
+        myInspectionManager.createProblemDescriptor(startElement, endElement, info.description, ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
+                                                    false);
+      final HighlightDisplayLevel level = info.getSeverity() == HighlightSeverity.ERROR? HighlightDisplayLevel.ERROR: HighlightDisplayLevel.WARNING;
+      problemsMap.put(descriptor, level);
+    }
+    return problemsMap;
+  }
+
+
+  @NotNull
+  public String getDescription() {
+    return myValidator.getDescription();
+  }
+
+  public boolean validateConfiguration(final CompileScope scope) {
+    return true;
+  }
+
+  public ValidityState createValidityState(final DataInput in) throws IOException {
+    return PsiElementsValidityState.load(in);
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/openapi/compiler/util/PsiElementsValidityState.java b/java/compiler/impl/src/com/intellij/openapi/compiler/util/PsiElementsValidityState.java
new file mode 100644
index 0000000..ce79281
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/openapi/compiler/util/PsiElementsValidityState.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.
+ */
+package com.intellij.openapi.compiler.util;
+
+import com.intellij.compiler.CompilerIOUtil;
+import com.intellij.openapi.compiler.ValidityState;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.util.io.IOUtil;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class PsiElementsValidityState implements ValidityState {
+  private final Map<String, Long> myDependencies = new HashMap<String, Long>();
+
+  public PsiElementsValidityState() {
+  }
+
+  public void addDependency(final String key, final Long value) {
+    myDependencies.put(key, value);
+  }
+
+  public boolean equalsTo(ValidityState otherState) {
+    return otherState instanceof PsiElementsValidityState &&
+           myDependencies.equals(((PsiElementsValidityState)otherState).myDependencies);
+  }
+
+  public void save(DataOutput out) throws IOException {
+    final Set<Map.Entry<String, Long>> entries = myDependencies.entrySet();
+    out.writeInt(entries.size());
+    for (Map.Entry<String, Long> entry : entries) {
+      IOUtil.writeString(entry.getKey(), out);
+      out.writeLong(entry.getValue().longValue());
+    }
+  }
+
+  public static PsiElementsValidityState load(DataInput input) throws IOException {
+    int size = input.readInt();
+    final PsiElementsValidityState state = new PsiElementsValidityState();
+    while (size-- > 0) {
+      final String s = CompilerIOUtil.readString(input);
+      final long timestamp = input.readLong();
+      state.addDependency(s, timestamp);
+    }
+    return state;
+  }
+
+  public void addDependency(final PsiElement element) {
+    final PsiFile psiFile = element.getContainingFile();
+    if (psiFile != null) {
+      VirtualFile file = psiFile.getVirtualFile();
+      if (file != null) {
+        addDependency(file.getUrl(), file.getTimeStamp());
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/openapi/deployment/DeploymentUtilImpl.java b/java/compiler/impl/src/com/intellij/openapi/deployment/DeploymentUtilImpl.java
new file mode 100644
index 0000000..d36d28f
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/openapi/deployment/DeploymentUtilImpl.java
@@ -0,0 +1,177 @@
+/*
+ * 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 com.intellij.openapi.deployment;
+
+import com.intellij.compiler.impl.packagingCompiler.BuildRecipeImpl;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.compiler.make.BuildRecipe;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.xml.XmlDocument;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.util.descriptors.ConfigFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * @author Alexey Kudravtsev
+ */
+public class DeploymentUtilImpl extends DeploymentUtil {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.deployment.MakeUtilImpl");
+
+  public void copyFile(@NotNull final File fromFile,
+                       @NotNull final File toFile,
+                       @NotNull CompileContext context,
+                       @Nullable Set<String> writtenPaths,
+                       @Nullable FileFilter fileFilter) throws IOException {
+    if (fileFilter != null && !fileFilter.accept(fromFile)) {
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("Skipping " + fromFile.getAbsolutePath() + ": it wasn't accepted by filter " + fileFilter);
+      }
+      return;
+    }
+    checkPathDoNotNavigatesUpFromFile(fromFile);
+    checkPathDoNotNavigatesUpFromFile(toFile);
+    if (fromFile.isDirectory()) {
+      final File[] fromFiles = fromFile.listFiles();
+      toFile.mkdirs();
+      for (File file : fromFiles) {
+        copyFile(file, new File(toFile, file.getName()), context, writtenPaths, fileFilter);
+      }
+      return;
+    }
+    if (toFile.isDirectory()) {
+      context.addMessage(CompilerMessageCategory.ERROR,
+                         CompilerBundle.message("message.text.destination.is.directory", createCopyErrorMessage(fromFile, toFile)), null, -1, -1);
+      return;
+    }
+    if (FileUtil.filesEqual(fromFile, toFile) || writtenPaths != null && !writtenPaths.add(toFile.getPath())) {
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("Skipping " + fromFile.getAbsolutePath() + ": " + toFile.getAbsolutePath() + " is already written");
+      }
+      return;
+    }
+    if (!FileUtil.isFilePathAcceptable(toFile, fileFilter)) {
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("Skipping " + fromFile.getAbsolutePath() + ": " + toFile.getAbsolutePath() + " wasn't accepted by filter " + fileFilter);
+      }
+      return;
+    }
+    context.getProgressIndicator().setText("Copying files");
+    context.getProgressIndicator().setText2(fromFile.getPath());
+    try {
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("Copy file '" + fromFile + "' to '"+toFile+"'");
+      }
+      if (toFile.exists() && !SystemInfo.isFileSystemCaseSensitive) {
+        File canonicalFile = toFile.getCanonicalFile();
+        if (!canonicalFile.getAbsolutePath().equals(toFile.getAbsolutePath())) {
+          FileUtil.delete(toFile);
+        }
+      }
+      FileUtil.copy(fromFile, toFile);
+    }
+    catch (IOException e) {
+      context.addMessage(CompilerMessageCategory.ERROR, createCopyErrorMessage(fromFile, toFile) + ": "+e.getLocalizedMessage(), null, -1, -1);
+    }
+  }
+
+  // OS X is sensitive for that
+  private static void checkPathDoNotNavigatesUpFromFile(File file) {
+    String path = file.getPath();
+    int i = path.indexOf("..");
+    if (i != -1) {
+      String filepath = path.substring(0,i-1);
+      File filepart = new File(filepath);
+      if (filepart.exists() && !filepart.isDirectory()) {
+        LOG.error("Incorrect file path: '" + path + '\'');
+      }
+    }
+  }
+
+  private static String createCopyErrorMessage(final File fromFile, final File toFile) {
+    return CompilerBundle.message("message.text.error.copying.file.to.file", FileUtil.toSystemDependentName(fromFile.getPath()),
+                              FileUtil.toSystemDependentName(toFile.getPath()));
+  }
+
+  public final boolean addItemsRecursively(@NotNull BuildRecipe items, @NotNull File root, String outputRelativePath) {
+    if (outputRelativePath == null) outputRelativePath = "";
+    outputRelativePath = trimForwardSlashes(outputRelativePath);
+
+    items.addFileCopyInstruction(root, true, outputRelativePath);
+    return true;
+  }
+
+  public void reportDeploymentDescriptorDoesNotExists(ConfigFile descriptor, CompileContext context, Module module) {
+    final String description = ModuleType.get(module).getName() + " '" + module.getName() + '\'';
+    String descriptorPath = VfsUtil.urlToPath(descriptor.getUrl());
+    final String message =
+      CompilerBundle.message("message.text.compiling.item.deployment.descriptor.could.not.be.found", description, descriptorPath);
+    context.addMessage(CompilerMessageCategory.ERROR, message, null, -1, -1);
+  }
+
+  public void checkConfigFile(final ConfigFile descriptor, final CompileContext compileContext, final Module module) {
+    if (new File(VfsUtil.urlToPath(descriptor.getUrl())).exists()) {
+      String message = getConfigFileErrorMessage(descriptor);
+      if (message != null) {
+        final String moduleDescription = ModuleType.get(module).getName() + " '" + module.getName() + '\'';
+        compileContext.addMessage(CompilerMessageCategory.ERROR,
+                                CompilerBundle.message("message.text.compiling.module.message", moduleDescription, message),
+                                  descriptor.getUrl(), -1, -1);
+      }
+    }
+    else {
+      DeploymentUtil.getInstance().reportDeploymentDescriptorDoesNotExists(descriptor, compileContext, module);
+    }
+  }
+
+  public BuildRecipe createBuildRecipe() {
+    return new BuildRecipeImpl();
+  }
+
+  @Nullable
+  public String getConfigFileErrorMessage(final ConfigFile configFile) {
+    if (configFile.getVirtualFile() == null) {
+      String path = FileUtil.toSystemDependentName(VfsUtil.urlToPath(configFile.getUrl()));
+      return CompilerBundle.message("mesage.text.deployment.descriptor.file.not.exist", path);
+    }
+    PsiFile psiFile = configFile.getPsiFile();
+    if (psiFile == null || !psiFile.isValid()) {
+      return CompilerBundle.message("message.text.deployment.description.invalid.file");
+    }
+
+    if (psiFile instanceof XmlFile) {
+      XmlDocument document = ((XmlFile)psiFile).getDocument();
+      if (document == null || document.getRootTag() == null) {
+        return CompilerBundle.message("message.text.xml.file.invalid", FileUtil.toSystemDependentName(VfsUtil.urlToPath(configFile.getUrl())));
+      }
+    }
+    return null;
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/openapi/projectRoots/impl/MockJdkWrapper.java b/java/compiler/impl/src/com/intellij/openapi/projectRoots/impl/MockJdkWrapper.java
new file mode 100644
index 0000000..c5b6c66
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/openapi/projectRoots/impl/MockJdkWrapper.java
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+/**
+ * @author cdr
+ */
+package com.intellij.openapi.projectRoots.impl;
+
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkAdditionalData;
+import com.intellij.openapi.projectRoots.SdkModificator;
+import com.intellij.openapi.projectRoots.SdkTypeId;
+import com.intellij.openapi.roots.RootProvider;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+
+/**
+ * used to override JdkHome location in order to provide correct paths
+ */
+public final class MockJdkWrapper implements Sdk {
+  private final String myHomePath;
+  private final Sdk myDelegate;
+
+  public MockJdkWrapper(String homePath, @NotNull Sdk delegate) {
+    myHomePath = homePath;
+    myDelegate = delegate;
+  }
+
+  public VirtualFile getHomeDirectory() {
+    return LocalFileSystem.getInstance().findFileByIoFile(new File(getHomePath()));
+  }
+
+  public String getHomePath() {
+    final String homePath = FileUtil.toSystemDependentName(myHomePath == null ? myDelegate.getHomePath() : myHomePath);
+    return StringUtil.trimEnd(homePath, File.separator);
+  }
+
+  @NotNull
+  public SdkTypeId getSdkType() {
+    return myDelegate.getSdkType();
+  }
+
+  @NotNull
+  public String getName() {
+    return myDelegate.getName();
+  }
+
+  public String getVersionString() {
+    return myDelegate.getVersionString();
+  }
+
+  @NotNull
+  public RootProvider getRootProvider() {
+    return myDelegate.getRootProvider();
+  }
+
+  public <T> T getUserData(@NotNull Key<T> key) {
+    return myDelegate.getUserData(key);
+  }
+
+  public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
+    myDelegate.putUserData(key, value);
+  }
+
+  @NotNull
+  public Object clone() throws CloneNotSupportedException {
+    throw new CloneNotSupportedException();
+  }
+
+  public SdkAdditionalData getSdkAdditionalData() {
+    return null;
+  }
+
+  @NotNull
+  public SdkModificator getSdkModificator() {
+    return null;
+  }
+
+  public Sdk getDelegate() {
+    return myDelegate;
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactBySourceFileFinder.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactBySourceFileFinder.java
new file mode 100644
index 0000000..5a2211b
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactBySourceFileFinder.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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactBySourceFileFinder {
+  public static ArtifactBySourceFileFinder getInstance(@NotNull Project project) {
+    return ServiceManager.getService(project, ArtifactBySourceFileFinder.class);
+  }
+
+  public abstract Collection<? extends Artifact> findArtifacts(@NotNull VirtualFile sourceFile);
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactBySourceFileFinderImpl.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactBySourceFileFinderImpl.java
new file mode 100644
index 0000000..0083ead
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactBySourceFileFinderImpl.java
@@ -0,0 +1,118 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.ModificationTracker;
+import com.intellij.openapi.util.MultiValuesMap;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactManager;
+import com.intellij.packaging.elements.ComplexPackagingElementType;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementFactory;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.impl.elements.FileOrDirectoryCopyPackagingElement;
+import com.intellij.packaging.impl.elements.ModuleOutputPackagingElement;
+import com.intellij.psi.util.CachedValue;
+import com.intellij.psi.util.CachedValueProvider;
+import com.intellij.psi.util.CachedValuesManager;
+import com.intellij.util.SmartList;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ArtifactBySourceFileFinderImpl extends ArtifactBySourceFileFinder {
+  private CachedValue<MultiValuesMap<VirtualFile, Artifact>> myFile2Artifacts;
+  private final Project myProject;
+
+  public ArtifactBySourceFileFinderImpl(Project project) {
+    myProject = project;
+  }
+
+  public CachedValue<MultiValuesMap<VirtualFile, Artifact>> getFileToArtifactsMap() {
+    if (myFile2Artifacts == null) {
+      myFile2Artifacts =
+        CachedValuesManager.getManager(myProject).createCachedValue(new CachedValueProvider<MultiValuesMap<VirtualFile, Artifact>>() {
+          public Result<MultiValuesMap<VirtualFile, Artifact>> compute() {
+            MultiValuesMap<VirtualFile, Artifact> result = computeFileToArtifactsMap();
+            List<ModificationTracker> trackers = new ArrayList<ModificationTracker>();
+            trackers.add(ArtifactManager.getInstance(myProject).getModificationTracker());
+            for (ComplexPackagingElementType<?> type : PackagingElementFactory.getInstance().getComplexElementTypes()) {
+              ContainerUtil.addIfNotNull(type.getAllSubstitutionsModificationTracker(myProject), trackers);
+            }
+            return Result.create(result, trackers.toArray(new ModificationTracker[trackers.size()]));
+          }
+        }, false);
+    }
+    return myFile2Artifacts;
+  }
+
+  private MultiValuesMap<VirtualFile, Artifact> computeFileToArtifactsMap() {
+    final MultiValuesMap<VirtualFile, Artifact> result = new MultiValuesMap<VirtualFile, Artifact>();
+    final ArtifactManager artifactManager = ArtifactManager.getInstance(myProject);
+    for (final Artifact artifact : artifactManager.getArtifacts()) {
+      final PackagingElementResolvingContext context = artifactManager.getResolvingContext();
+      ArtifactUtil.processPackagingElements(artifact, null, new PackagingElementProcessor<PackagingElement<?>>() {
+        @Override
+        public boolean process(@NotNull PackagingElement<?> element, @NotNull PackagingElementPath path) {
+          if (element instanceof FileOrDirectoryCopyPackagingElement<?>) {
+            final VirtualFile root = ((FileOrDirectoryCopyPackagingElement)element).findFile();
+            if (root != null) {
+              result.put(root, artifact);
+            }
+          }
+          else if (element instanceof ModuleOutputPackagingElement) {
+            for (VirtualFile sourceRoot : ((ModuleOutputPackagingElement)element).getSourceRoots(context)) {
+              result.put(sourceRoot, artifact);
+            }
+          }
+          return true;
+        }
+      }, context, true);
+    }
+    return result;
+  }
+
+  @Override
+  public Collection<? extends Artifact> findArtifacts(@NotNull VirtualFile sourceFile) {
+    final MultiValuesMap<VirtualFile, Artifact> map = getFileToArtifactsMap().getValue();
+    if (map.isEmpty()) {
+      return Collections.emptyList();
+    }
+
+    List<Artifact> result = null;
+    VirtualFile file = sourceFile;
+    while (file != null) {
+      final Collection<Artifact> artifacts = map.get(file);
+      if (artifacts != null) {
+        if (result == null) {
+          result = new SmartList<Artifact>();
+        }
+        result.addAll(artifacts);
+      }
+      file = file.getParent();
+    }
+    return result != null ? result : Collections.<Artifact>emptyList();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactImpl.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactImpl.java
new file mode 100644
index 0000000..65763c0
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactImpl.java
@@ -0,0 +1,188 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.openapi.util.UserDataHolderBase;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.*;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.impl.elements.ArchivePackagingElement;
+import com.intellij.util.EventDispatcher;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author nik
+ */
+public class ArtifactImpl extends UserDataHolderBase implements ModifiableArtifact {
+  private CompositePackagingElement<?> myRootElement;
+  private String myName;
+  private boolean myBuildOnMake;
+  private String myOutputPath;
+  private final EventDispatcher<ArtifactListener> myDispatcher;
+  private ArtifactType myArtifactType;
+  private Map<ArtifactPropertiesProvider, ArtifactProperties<?>> myProperties;
+
+  public ArtifactImpl(@NotNull String name, @NotNull ArtifactType artifactType, boolean buildOnMake, @NotNull CompositePackagingElement<?> rootElement,
+                      String outputPath) {
+    this(name, artifactType, buildOnMake, rootElement, outputPath, null);
+  }
+  public ArtifactImpl(@NotNull String name, @NotNull ArtifactType artifactType, boolean buildOnMake, @NotNull CompositePackagingElement<?> rootElement,
+                      String outputPath,
+                      EventDispatcher<ArtifactListener> dispatcher) {
+    myName = name;
+    myArtifactType = artifactType;
+    myBuildOnMake = buildOnMake;
+    myRootElement = rootElement;
+    myOutputPath = outputPath;
+    myDispatcher = dispatcher;
+    myProperties = new HashMap<ArtifactPropertiesProvider, ArtifactProperties<?>>();
+    resetProperties();
+  }
+
+  private void resetProperties() {
+    myProperties.clear();
+    for (ArtifactPropertiesProvider provider : ArtifactPropertiesProvider.getProviders()) {
+      if (provider.isAvailableFor(myArtifactType)) {
+        myProperties.put(provider, provider.createProperties(myArtifactType));
+      }
+    }
+  }
+
+  @NotNull
+  public ArtifactType getArtifactType() {
+    return myArtifactType;
+  }
+
+  public String getName() {
+    return myName;
+  }
+
+  public boolean isBuildOnMake() {
+    return myBuildOnMake;
+  }
+
+  @NotNull
+  public CompositePackagingElement<?> getRootElement() {
+    return myRootElement;
+  }
+
+  public String getOutputPath() {
+    return myOutputPath;
+  }
+
+  public Collection<? extends ArtifactPropertiesProvider> getPropertiesProviders() {
+    return Collections.unmodifiableCollection(myProperties.keySet());
+  }
+
+  public ArtifactImpl createCopy(EventDispatcher<ArtifactListener> dispatcher) {
+    final ArtifactImpl artifact = new ArtifactImpl(myName, myArtifactType, myBuildOnMake, myRootElement, myOutputPath, dispatcher);
+    for (Map.Entry<ArtifactPropertiesProvider, ArtifactProperties<?>> entry : myProperties.entrySet()) {
+      final ArtifactProperties newProperties = artifact.myProperties.get(entry.getKey());
+      //noinspection unchecked
+      newProperties.loadState(entry.getValue().getState());
+    }
+    return artifact;
+  }
+
+  public void setName(@NotNull String name) {
+    String oldName = myName;
+    myName = name;
+    if (myDispatcher != null) {
+      myDispatcher.getMulticaster().artifactChanged(this, oldName);
+    }
+  }
+
+  @NonNls @Override
+  public String toString() {
+    return "artifact:" + myName;
+  }
+
+  public void setRootElement(CompositePackagingElement<?> root) {
+    myRootElement = root;
+  }
+
+  public void setProperties(ArtifactPropertiesProvider provider, ArtifactProperties<?> properties) {
+    if (properties != null) {
+      myProperties.put(provider, properties);
+    }
+    else {
+      myProperties.remove(provider);
+    }
+  }
+
+  public void setArtifactType(@NotNull ArtifactType selected) {
+    myArtifactType = selected;
+    resetProperties();
+  }
+
+  public void setBuildOnMake(boolean buildOnMake) {
+    myBuildOnMake = buildOnMake;
+  }
+
+  public void setOutputPath(String outputPath) {
+    myOutputPath = outputPath;
+  }
+
+  public ArtifactProperties<?> getProperties(@NotNull ArtifactPropertiesProvider provider) {
+    return myProperties.get(provider);
+  }
+
+  @Override
+  public VirtualFile getOutputFile() {
+    String filePath = getOutputFilePath();
+    return !StringUtil.isEmpty(filePath) ? LocalFileSystem.getInstance().findFileByPath(filePath) : null;
+  }
+
+  @Override
+  public String getOutputFilePath() {
+    if (StringUtil.isEmpty(myOutputPath)) return null;
+
+    String filePath;
+    if (myRootElement instanceof ArchivePackagingElement) {
+      filePath = myOutputPath + "/" + ((ArchivePackagingElement)myRootElement).getArchiveFileName();
+    }
+    else {
+      filePath = myOutputPath;
+    }
+    return filePath;
+  }
+
+  @Nullable
+  public String getOutputDirectoryPathToCleanOnRebuild() {
+    if (myRootElement instanceof ArchivePackagingElement || StringUtil.isEmpty(myOutputPath)) {
+      return null;
+    }
+    return myOutputPath;
+  }
+
+  public void copyFrom(ArtifactImpl modified) {
+    myName = modified.getName();
+    myOutputPath = modified.getOutputPath();
+    myBuildOnMake = modified.isBuildOnMake();
+    myRootElement = modified.getRootElement();
+    myProperties = modified.myProperties;
+    myArtifactType = modified.getArtifactType();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactLoadingErrorDescription.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactLoadingErrorDescription.java
new file mode 100644
index 0000000..2b990dc
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactLoadingErrorDescription.java
@@ -0,0 +1,60 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.module.ConfigurationErrorDescription;
+import com.intellij.openapi.module.ConfigurationErrorType;
+import com.intellij.openapi.project.Project;
+import com.intellij.packaging.artifacts.ArtifactManager;
+import com.intellij.packaging.artifacts.ModifiableArtifactModel;
+
+/**
+ * @author nik
+ */
+public class ArtifactLoadingErrorDescription extends ConfigurationErrorDescription {
+  private static final ConfigurationErrorType INVALID_ARTIFACT = new ConfigurationErrorType("artifact", false);
+  private final Project myProject;
+  private final InvalidArtifact myArtifact;
+
+  public ArtifactLoadingErrorDescription(Project project, InvalidArtifact artifact) {
+    super(artifact.getName(), artifact.getErrorMessage(), INVALID_ARTIFACT);
+    myProject = project;
+    myArtifact = artifact;
+  }
+
+  @Override
+  public void ignoreInvalidElement() {
+    final ModifiableArtifactModel model = ArtifactManager.getInstance(myProject).createModifiableModel();
+    model.removeArtifact(myArtifact);
+    new WriteAction() {
+      protected void run(final Result result) {
+        model.commit();
+      }
+    }.execute();
+  }
+
+  @Override
+  public String getIgnoreConfirmationMessage() {
+    return "Would you like to remove artifact '" + myArtifact.getName() + "?";
+  }
+
+  @Override
+  public boolean isValid() {
+    return ArtifactManager.getInstance(myProject).getAllArtifactsIncludingInvalid().contains(myArtifact);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactManagerImpl.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactManagerImpl.java
new file mode 100644
index 0000000..c3896a8
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactManagerImpl.java
@@ -0,0 +1,450 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.compiler.server.BuildManager;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.components.*;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.ProjectLoadingErrorsNotifier;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ex.ProjectRootManagerEx;
+import com.intellij.openapi.util.ModificationTracker;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.packaging.artifacts.*;
+import com.intellij.packaging.elements.*;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters;
+import com.intellij.util.xmlb.XmlSerializer;
+import gnu.trove.THashSet;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.model.serialization.artifact.ArtifactManagerState;
+import org.jetbrains.jps.model.serialization.artifact.ArtifactPropertiesState;
+import org.jetbrains.jps.model.serialization.artifact.ArtifactState;
+
+import java.util.*;
+
+/**
+ * @author nik
+ */
+@State(
+    name = ArtifactManagerImpl.COMPONENT_NAME,
+    storages = {
+        @Storage( file = StoragePathMacros.PROJECT_FILE),
+        @Storage( file = StoragePathMacros.PROJECT_CONFIG_DIR + "/artifacts/", scheme = StorageScheme.DIRECTORY_BASED, stateSplitter = ArtifactManagerStateSplitter.class)
+    }
+)
+public class ArtifactManagerImpl extends ArtifactManager implements ProjectComponent, PersistentStateComponent<ArtifactManagerState> {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.packaging.impl.artifacts.ArtifactManagerImpl");
+  @NonNls public static final String COMPONENT_NAME = "ArtifactManager";
+  @NonNls public static final String PACKAGING_ELEMENT_NAME = "element";
+  @NonNls public static final String TYPE_ID_ATTRIBUTE = "id";
+  private final ArtifactManagerModel myModel;
+  private final Project myProject;
+  private final DefaultPackagingElementResolvingContext myResolvingContext;
+  private boolean myInsideCommit = false;
+  private boolean myLoaded;
+  private long myModificationCount;
+  private final ModificationTracker myModificationTracker = new ModificationTracker() {
+    public long getModificationCount() {
+      return myModificationCount;
+    }
+  };
+  private final Map<String, LocalFileSystem.WatchRequest> myWatchedOutputs = new HashMap<String, LocalFileSystem.WatchRequest>();
+
+  public ArtifactManagerImpl(Project project) {
+    myProject = project;
+    myModel = new ArtifactManagerModel();
+    myResolvingContext = new DefaultPackagingElementResolvingContext(myProject);
+    ((ArtifactPointerManagerImpl)ArtifactPointerManager.getInstance(project)).setArtifactManager(this);
+  }
+
+  @NotNull
+  public Artifact[] getArtifacts() {
+    return myModel.getArtifacts();
+  }
+
+  public Artifact findArtifact(@NotNull String name) {
+    return myModel.findArtifact(name);
+  }
+
+  @NotNull
+  public Artifact getArtifactByOriginal(@NotNull Artifact artifact) {
+    return myModel.getArtifactByOriginal(artifact);
+  }
+
+  @NotNull
+  public Artifact getOriginalArtifact(@NotNull Artifact artifact) {
+    return myModel.getOriginalArtifact(artifact);
+  }
+
+  public Collection<? extends Artifact> getArtifactsByType(@NotNull ArtifactType type) {
+    return myModel.getArtifactsByType(type);
+  }
+
+  @Override
+  public List<? extends Artifact> getAllArtifactsIncludingInvalid() {
+    return myModel.getAllArtifactsIncludingInvalid();
+  }
+
+  public ArtifactManagerState getState() {
+    final ArtifactManagerState state = new ArtifactManagerState();
+    for (Artifact artifact : getAllArtifactsIncludingInvalid()) {
+      final ArtifactState artifactState;
+      if (artifact instanceof InvalidArtifact) {
+        artifactState = ((InvalidArtifact)artifact).getState();
+      }
+      else {
+        artifactState = new ArtifactState();
+        artifactState.setBuildOnMake(artifact.isBuildOnMake());
+        artifactState.setName(artifact.getName());
+        artifactState.setOutputPath(artifact.getOutputPath());
+        artifactState.setRootElement(serializePackagingElement(artifact.getRootElement()));
+        artifactState.setArtifactType(artifact.getArtifactType().getId());
+        for (ArtifactPropertiesProvider provider : artifact.getPropertiesProviders()) {
+          final ArtifactPropertiesState propertiesState = serializeProperties(provider, artifact.getProperties(provider));
+          if (propertiesState != null) {
+            artifactState.getPropertiesList().add(propertiesState);
+          }
+        }
+        Collections.sort(artifactState.getPropertiesList(), new Comparator<ArtifactPropertiesState>() {
+          public int compare(ArtifactPropertiesState o1, ArtifactPropertiesState o2) {
+            return o1.getId().compareTo(o2.getId());
+          }
+        });
+      }
+      state.getArtifacts().add(artifactState);
+    }
+    return state;
+  }
+
+  @Nullable
+  private static <S> ArtifactPropertiesState serializeProperties(ArtifactPropertiesProvider provider, ArtifactProperties<S> properties) {
+    final ArtifactPropertiesState state = new ArtifactPropertiesState();
+    state.setId(provider.getId());
+    final Element options = new Element("options");
+    XmlSerializer.serializeInto(properties.getState(), options, new SkipDefaultValuesSerializationFilters());
+    if (options.getContent().isEmpty() && options.getAttributes().isEmpty()) return null;
+    state.setOptions(options);
+    return state;
+  }
+
+  private static Element serializePackagingElement(PackagingElement<?> packagingElement) {
+    Element element = new Element(PACKAGING_ELEMENT_NAME);
+    element.setAttribute(TYPE_ID_ATTRIBUTE, packagingElement.getType().getId());
+    final Object bean = packagingElement.getState();
+    if (bean != null) {
+      XmlSerializer.serializeInto(bean, element, new SkipDefaultValuesSerializationFilters());
+    }
+    if (packagingElement instanceof CompositePackagingElement) {
+      for (PackagingElement<?> child : ((CompositePackagingElement<?>)packagingElement).getChildren()) {
+        element.addContent(serializePackagingElement(child));
+      }
+    }
+    return element;
+  }
+
+  private <T> PackagingElement<T> deserializeElement(Element element) throws UnknownPackagingElementTypeException {
+    final String id = element.getAttributeValue(TYPE_ID_ATTRIBUTE);
+    PackagingElementType<?> type = PackagingElementFactory.getInstance().findElementType(id);
+    if (type == null) {
+      throw new UnknownPackagingElementTypeException(id);
+    }
+
+    PackagingElement<T> packagingElement = (PackagingElement<T>)type.createEmpty(myProject);
+    T state = packagingElement.getState();
+    if (state != null) {
+      XmlSerializer.deserializeInto(state, element);
+      packagingElement.loadState(state);
+    }
+    final List children = element.getChildren(PACKAGING_ELEMENT_NAME);
+    //noinspection unchecked
+    for (Element child : (List<? extends Element>)children) {
+      ((CompositePackagingElement<?>)packagingElement).addOrFindChild(deserializeElement(child));
+    }
+    return packagingElement;
+  }
+
+  public void loadState(ArtifactManagerState managerState) {
+    final List<ArtifactImpl> artifacts = new ArrayList<ArtifactImpl>();
+    for (ArtifactState state : managerState.getArtifacts()) {
+      artifacts.add(loadArtifact(state));
+    }
+
+    if (myLoaded) {
+      final ArtifactModelImpl model = new ArtifactModelImpl(this, artifacts);
+      doCommit(model);
+    }
+    else {
+      myModel.setArtifactsList(artifacts);
+      myLoaded = true;
+    }
+  }
+
+  private ArtifactImpl loadArtifact(ArtifactState state) {
+    ArtifactType type = ArtifactType.findById(state.getArtifactType());
+    if (type == null) {
+      return createInvalidArtifact(state, "Unknown artifact type: " + state.getArtifactType());
+    }
+
+    final Element element = state.getRootElement();
+    final String artifactName = state.getName();
+    final CompositePackagingElement<?> rootElement;
+    if (element != null) {
+      try {
+        rootElement = (CompositePackagingElement<?>)deserializeElement(element);
+      }
+      catch (UnknownPackagingElementTypeException e) {
+        return createInvalidArtifact(state, "Unknown element: " + e.getTypeId());
+      }
+    }
+    else {
+      rootElement = type.createRootElement(artifactName);
+    }
+
+    final ArtifactImpl artifact = new ArtifactImpl(artifactName, type, state.isBuildOnMake(), rootElement, state.getOutputPath());
+    final List<ArtifactPropertiesState> propertiesList = state.getPropertiesList();
+    for (ArtifactPropertiesState propertiesState : propertiesList) {
+      final ArtifactPropertiesProvider provider = ArtifactPropertiesProvider.findById(propertiesState.getId());
+      if (provider != null) {
+        deserializeProperties(artifact.getProperties(provider), propertiesState);
+      }
+      else {
+        return createInvalidArtifact(state, "Unknown artifact properties: " + propertiesState.getId());
+      }
+    }
+    return artifact;
+  }
+
+  private InvalidArtifact createInvalidArtifact(ArtifactState state, String errorMessage) {
+    final InvalidArtifact artifact = new InvalidArtifact(state, errorMessage);
+    ProjectLoadingErrorsNotifier.getInstance(myProject).registerError(new ArtifactLoadingErrorDescription(myProject, artifact));
+    return artifact;
+  }
+
+  private static <S> void deserializeProperties(ArtifactProperties<S> artifactProperties, ArtifactPropertiesState propertiesState) {
+    final Element options = propertiesState.getOptions();
+    if (artifactProperties == null || options == null) {
+      return;
+    }
+    final S state = artifactProperties.getState();
+    if (state != null) {
+      XmlSerializer.deserializeInto(state, options);
+      artifactProperties.loadState(state);
+    }
+  }
+
+  public void disposeComponent() {
+    LocalFileSystem.getInstance().removeWatchedRoots(myWatchedOutputs.values());
+  }
+
+  @NotNull
+  public String getComponentName() {
+    return COMPONENT_NAME;
+  }
+
+  public void initComponent() {
+    VirtualFileManager.getInstance().addVirtualFileListener(new ArtifactVirtualFileListener(myProject, this), myProject);
+    updateWatchedRoots();
+  }
+
+  private void updateWatchedRoots() {
+    Set<String> pathsToRemove = new HashSet<String>(myWatchedOutputs.keySet());
+    Set<String> toAdd = new HashSet<String>();
+    for (Artifact artifact : getArtifacts()) {
+      final String path = artifact.getOutputPath();
+      if (path != null && path.length() > 0) {
+        pathsToRemove.remove(path);
+        if (!myWatchedOutputs.containsKey(path)) {
+          toAdd.add(path);
+        }
+      }
+    }
+
+    List<LocalFileSystem.WatchRequest> requestsToRemove = new ArrayList<LocalFileSystem.WatchRequest>();
+    for (String path : pathsToRemove) {
+      final LocalFileSystem.WatchRequest request = myWatchedOutputs.remove(path);
+      ContainerUtil.addIfNotNull(request, requestsToRemove);
+    }
+
+    Set<LocalFileSystem.WatchRequest> newRequests = LocalFileSystem.getInstance().replaceWatchedRoots(requestsToRemove, toAdd, null);
+    for (LocalFileSystem.WatchRequest request : newRequests) {
+      myWatchedOutputs.put(request.getRootPath(), request);
+    }
+  }
+
+  public void projectOpened() {
+  }
+
+  public void projectClosed() {
+  }
+
+  @Override
+  public Artifact[] getSortedArtifacts() {
+    return myModel.getSortedArtifacts();
+  }
+
+  @Override
+  public ModifiableArtifactModel createModifiableModel() {
+    return new ArtifactModelImpl(this, getArtifactsList());
+  }
+
+  @Override
+  public PackagingElementResolvingContext getResolvingContext() {
+    return myResolvingContext;
+  }
+
+  public List<ArtifactImpl> getArtifactsList() {
+    return myModel.myArtifactsList;
+  }
+
+  public void commit(ArtifactModelImpl artifactModel) {
+    ApplicationManager.getApplication().assertWriteAccessAllowed();
+
+    doCommit(artifactModel);
+  }
+
+  private void doCommit(ArtifactModelImpl artifactModel) {
+    boolean hasChanges;
+    LOG.assertTrue(!myInsideCommit, "Recursive commit");
+    myInsideCommit = true;
+    try {
+
+      final List<ArtifactImpl> allArtifacts = artifactModel.getOriginalArtifacts();
+
+      final Set<ArtifactImpl> removed = new THashSet<ArtifactImpl>(myModel.myArtifactsList);
+      final List<ArtifactImpl> added = new ArrayList<ArtifactImpl>();
+      final List<Pair<ArtifactImpl, String>> changed = new ArrayList<Pair<ArtifactImpl, String>>();
+
+      for (ArtifactImpl artifact : allArtifacts) {
+        final boolean isAdded = !removed.remove(artifact);
+        final ArtifactImpl modifiableCopy = artifactModel.getModifiableCopy(artifact);
+        if (isAdded) {
+          added.add(artifact);
+        }
+        else if (modifiableCopy != null && !modifiableCopy.equals(artifact)) {
+          final String oldName = artifact.getName();
+          artifact.copyFrom(modifiableCopy);
+          changed.add(Pair.create(artifact, oldName));
+        }
+      }
+
+      myModel.setArtifactsList(allArtifacts);
+      myModificationCount++;
+      final ArtifactListener publisher = myProject.getMessageBus().syncPublisher(TOPIC);
+      hasChanges = !removed.isEmpty() || !added.isEmpty() || !changed.isEmpty();
+      ProjectRootManagerEx.getInstanceEx(myProject).mergeRootsChangesDuring(new Runnable() {
+        public void run() {
+          for (ArtifactImpl artifact : removed) {
+            publisher.artifactRemoved(artifact);
+          }
+          //it's important to send 'removed' events before 'added'. Otherwise when artifacts are reloaded from xml artifact pointers will be damaged
+          for (ArtifactImpl artifact : added) {
+            publisher.artifactAdded(artifact);
+          }
+          for (Pair<ArtifactImpl, String> pair : changed) {
+            publisher.artifactChanged(pair.getFirst(), pair.getSecond());
+          }
+        }
+      });
+    }
+    finally {
+      myInsideCommit = false;
+    }
+    updateWatchedRoots();
+    if (hasChanges) {
+      BuildManager.getInstance().clearState(myProject);
+    }
+  }
+
+  public Project getProject() {
+    return myProject;
+  }
+
+  @NotNull
+  public Artifact addArtifact(@NotNull final String name, @NotNull final ArtifactType type, final CompositePackagingElement<?> root) {
+    return new WriteAction<Artifact>() {
+      protected void run(final Result<Artifact> result) {
+        final ModifiableArtifactModel model = createModifiableModel();
+        final ModifiableArtifact artifact = model.addArtifact(name, type);
+        if (root != null) {
+          artifact.setRootElement(root);
+        }
+        model.commit();
+        result.setResult(artifact);
+      }
+    }.execute().getResultObject();
+  }
+
+  @Override
+  public void addElementsToDirectory(@NotNull Artifact artifact, @NotNull String relativePath, @NotNull PackagingElement<?> element) {
+    addElementsToDirectory(artifact, relativePath, Collections.singletonList(element));
+  }
+
+  @Override
+  public void addElementsToDirectory(@NotNull Artifact artifact, @NotNull String relativePath,
+                                     @NotNull Collection<? extends PackagingElement<?>> elements) {
+    final ModifiableArtifactModel model = createModifiableModel();
+    final CompositePackagingElement<?> root = model.getOrCreateModifiableArtifact(artifact).getRootElement();
+    PackagingElementFactory.getInstance().getOrCreateDirectory(root, relativePath).addOrFindChildren(elements);
+    new WriteAction() {
+      protected void run(final Result result) {
+        model.commit();
+      }
+    }.execute();
+  }
+
+  @Override
+  public ModificationTracker getModificationTracker() {
+    return myModificationTracker;
+  }
+
+  private static class ArtifactManagerModel extends ArtifactModelBase {
+    private List<ArtifactImpl> myArtifactsList = new ArrayList<ArtifactImpl>();
+    private Artifact[] mySortedArtifacts;
+
+    public void setArtifactsList(List<ArtifactImpl> artifactsList) {
+      myArtifactsList = artifactsList;
+      artifactsChanged();
+    }
+
+    @Override
+    protected void artifactsChanged() {
+      super.artifactsChanged();
+      mySortedArtifacts = null;
+    }
+
+    protected List<? extends Artifact> getArtifactsList() {
+      return myArtifactsList;
+    }
+
+    public Artifact[] getSortedArtifacts() {
+      if (mySortedArtifacts == null) {
+        mySortedArtifacts = getArtifacts().clone();
+        Arrays.sort(mySortedArtifacts, ARTIFACT_COMPARATOR);
+      }
+      return mySortedArtifacts;
+    }
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactManagerStateSplitter.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactManagerStateSplitter.java
new file mode 100644
index 0000000..90ee240
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactManagerStateSplitter.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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.openapi.components.StateSplitter;
+import com.intellij.openapi.util.JDOMUtil;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.util.text.UniqueNameGenerator;
+import org.jdom.Element;
+import org.jetbrains.jps.model.serialization.artifact.ArtifactState;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ArtifactManagerStateSplitter implements StateSplitter {
+  public List<Pair<Element, String>> splitState(Element e) {
+    final UniqueNameGenerator generator = new UniqueNameGenerator();
+
+    List<Pair<Element, String>> result = new ArrayList<Pair<Element, String>>();
+
+    for (Element element : JDOMUtil.getElements(e)) {
+      final String name = generator.generateUniqueName(FileUtil.sanitizeFileName(element.getAttributeValue(ArtifactState.NAME_ATTRIBUTE))) + ".xml";
+      result.add(new Pair<Element, String>(element, name));
+    }
+
+    return result;
+  }
+
+  public void mergeStatesInto(Element target, Element[] elements) {
+    for (Element e : elements) {
+      target.addContent(e);
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactModelBase.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactModelBase.java
new file mode 100644
index 0000000..9dbfa2a
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactModelBase.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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.openapi.util.Condition;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactModel;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactModelBase implements ArtifactModel {
+  private Map<String, Artifact> myArtifactsMap;
+  private Artifact[] myArtifactsArray;
+  public static final Condition<Artifact> VALID_ARTIFACT_CONDITION = new Condition<Artifact>() {
+    @Override
+    public boolean value(Artifact artifact) {
+      return !(artifact instanceof InvalidArtifact);
+    }
+  };
+
+  protected abstract List<? extends Artifact> getArtifactsList();
+
+  @NotNull
+  public Artifact[] getArtifacts() {
+    if (myArtifactsArray == null) {
+      final List<? extends Artifact> validArtifacts = ContainerUtil.findAll(getArtifactsList(), VALID_ARTIFACT_CONDITION);
+      myArtifactsArray = validArtifacts.toArray(new Artifact[validArtifacts.size()]);
+    }
+    return myArtifactsArray;
+  }
+
+  @Override
+  public List<? extends Artifact> getAllArtifactsIncludingInvalid() {
+    return Collections.unmodifiableList(getArtifactsList());
+  }
+
+  public Artifact findArtifact(@NotNull String name) {
+    if (myArtifactsMap == null) {
+      myArtifactsMap = new HashMap<String, Artifact>();
+      for (Artifact artifact : getArtifactsList()) {
+        myArtifactsMap.put(artifact.getName(), artifact);
+      }
+    }
+    return myArtifactsMap.get(name);
+  }
+
+  @NotNull
+  public Artifact getArtifactByOriginal(@NotNull Artifact artifact) {
+    return artifact;
+  }
+
+  @NotNull
+  public Artifact getOriginalArtifact(@NotNull Artifact artifact) {
+    return artifact;
+  }
+
+  public Collection<? extends Artifact> getArtifactsByType(@NotNull ArtifactType type) {
+    final List<Artifact> result = new ArrayList<Artifact>();
+    for (Artifact artifact : getArtifacts()) {
+      if (artifact.getArtifactType().equals(type)) {
+        result.add(artifact);
+      }
+    }
+    return result;
+  }
+
+  protected void artifactsChanged() {
+    myArtifactsMap = null;
+    myArtifactsArray = null;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactModelImpl.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactModelImpl.java
new file mode 100644
index 0000000..38f46db
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactModelImpl.java
@@ -0,0 +1,175 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.packaging.artifacts.*;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.util.EventDispatcher;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author nik
+ */
+public class ArtifactModelImpl extends ArtifactModelBase implements ModifiableArtifactModel {
+  private final List<ArtifactImpl> myOriginalArtifacts;
+  private final ArtifactManagerImpl myArtifactManager;
+  private final Map<ArtifactImpl, ArtifactImpl> myArtifact2ModifiableCopy = new HashMap<ArtifactImpl, ArtifactImpl>();
+  private final Map<ArtifactImpl, ArtifactImpl> myModifiable2Original = new HashMap<ArtifactImpl, ArtifactImpl>();
+  private final EventDispatcher<ArtifactListener> myDispatcher = EventDispatcher.create(ArtifactListener.class);
+
+  public ArtifactModelImpl(ArtifactManagerImpl artifactManager, List<ArtifactImpl> originalArtifacts) {
+    myArtifactManager = artifactManager;
+    myOriginalArtifacts = new ArrayList<ArtifactImpl>(originalArtifacts);
+    addListener(new ArtifactAdapter() {
+      @Override
+      public void artifactChanged(@NotNull Artifact artifact, @NotNull String oldName) {
+        artifactsChanged();
+      }
+    });
+  }
+
+  protected List<? extends Artifact> getArtifactsList() {
+    final List<ArtifactImpl> list = new ArrayList<ArtifactImpl>();
+    for (ArtifactImpl artifact : myOriginalArtifacts) {
+      final ArtifactImpl copy = myArtifact2ModifiableCopy.get(artifact);
+      if (copy != null) {
+        list.add(copy);
+      }
+      else {
+        list.add(artifact);
+      }
+    }
+    return list;
+  }
+
+  @NotNull
+  public ModifiableArtifact addArtifact(@NotNull final String name, @NotNull ArtifactType artifactType) {
+    return addArtifact(name, artifactType, artifactType.createRootElement(name));
+  }
+
+  @NotNull
+  public ModifiableArtifact addArtifact(@NotNull String name, @NotNull ArtifactType artifactType, CompositePackagingElement<?> rootElement) {
+    final String uniqueName = generateUniqueName(name);
+    final String outputPath = ArtifactUtil.getDefaultArtifactOutputPath(uniqueName, myArtifactManager.getProject());
+    final ArtifactImpl artifact = new ArtifactImpl(uniqueName, artifactType, false, rootElement, outputPath, myDispatcher);
+    myOriginalArtifacts.add(artifact);
+    myArtifact2ModifiableCopy.put(artifact, artifact);
+    myModifiable2Original.put(artifact, artifact);
+
+    artifactsChanged();
+    myDispatcher.getMulticaster().artifactAdded(artifact);
+    return artifact;
+  }
+
+  private String generateUniqueName(String baseName) {
+    String name = baseName;
+    int i = 2;
+    while (true) {
+      if (findArtifact(name) == null) {
+        return name;
+      }
+      name = baseName + i++;
+    }
+  }
+
+  public void addListener(@NotNull ArtifactListener listener) {
+    myDispatcher.addListener(listener);
+  }
+
+  public void removeListener(@NotNull ArtifactListener listener) {
+    myDispatcher.addListener(listener);
+  }
+
+  public void removeArtifact(@NotNull Artifact artifact) {
+    final ArtifactImpl artifactImpl = (ArtifactImpl)artifact;
+    ArtifactImpl original = myModifiable2Original.remove(artifactImpl);
+    if (original != null) {
+      myOriginalArtifacts.remove(original);
+    }
+    else {
+      original = artifactImpl;
+    }
+    myArtifact2ModifiableCopy.remove(original);
+    myOriginalArtifacts.remove(original);
+    artifactsChanged();
+    myDispatcher.getMulticaster().artifactRemoved(original);
+  }
+
+  @NotNull
+  public ModifiableArtifact getOrCreateModifiableArtifact(@NotNull Artifact artifact) {
+    final ArtifactImpl artifactImpl = (ArtifactImpl)artifact;
+    if (myModifiable2Original.containsKey(artifactImpl)) {
+      return artifactImpl;
+    }
+
+    ArtifactImpl modifiableCopy = myArtifact2ModifiableCopy.get(artifactImpl);
+    if (modifiableCopy == null) {
+      modifiableCopy = artifactImpl.createCopy(myDispatcher);
+      myDispatcher.getMulticaster().artifactChanged(modifiableCopy, artifact.getName());
+      myArtifact2ModifiableCopy.put(artifactImpl, modifiableCopy);
+      myModifiable2Original.put(modifiableCopy, artifactImpl);
+      artifactsChanged();
+    }
+    return modifiableCopy;
+  }
+
+  @NotNull
+  public Artifact getOriginalArtifact(@NotNull Artifact artifact) {
+    final ArtifactImpl original = myModifiable2Original.get(artifact);
+    return original != null ? original : artifact;
+  }
+
+  @NotNull
+  public ArtifactImpl getArtifactByOriginal(@NotNull Artifact artifact) {
+    final ArtifactImpl artifactImpl = (ArtifactImpl)artifact;
+    final ArtifactImpl copy = myArtifact2ModifiableCopy.get(artifactImpl);
+    return copy != null ? copy : artifactImpl;
+  }
+
+  public boolean isModified() {
+    return !myOriginalArtifacts.equals(myArtifactManager.getArtifactsList()) || !myArtifact2ModifiableCopy.isEmpty();
+  }
+
+  public void commit() {
+    myArtifactManager.commit(this);
+  }
+
+  public void dispose() {
+    List<Artifact> artifacts = new ArrayList<Artifact>();
+    for (ArtifactImpl artifact : myModifiable2Original.keySet()) {
+      if (myModifiable2Original.get(artifact).equals(artifact)) {
+        artifacts.add(artifact);
+      }
+    }
+    ((ArtifactPointerManagerImpl)ArtifactPointerManager.getInstance(myArtifactManager.getProject())).disposePointers(artifacts);
+  }
+
+  @Nullable
+  public ArtifactImpl getModifiableCopy(Artifact artifact) {
+    //noinspection SuspiciousMethodCalls
+    return myArtifact2ModifiableCopy.get(artifact);
+  }
+
+  public List<ArtifactImpl> getOriginalArtifacts() {
+    return myOriginalArtifacts;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactPointerImpl.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactPointerImpl.java
new file mode 100644
index 0000000..b2b60bb
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactPointerImpl.java
@@ -0,0 +1,70 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactModel;
+import com.intellij.packaging.artifacts.ArtifactPointer;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class ArtifactPointerImpl implements ArtifactPointer {
+  private String myName;
+  private Artifact myArtifact;
+
+  public ArtifactPointerImpl(@NotNull String name) {
+    myName = name;
+  }
+
+  public ArtifactPointerImpl(@NotNull Artifact artifact) {
+    myArtifact = artifact;
+    myName = artifact.getName();
+  }
+
+  @NotNull
+  public String getArtifactName() {
+    return myName;
+  }
+
+  public Artifact getArtifact() {
+    return myArtifact;
+  }
+
+  @NotNull
+  public String getArtifactName(@NotNull ArtifactModel artifactModel) {
+    if (myArtifact != null) {
+      return artifactModel.getArtifactByOriginal(myArtifact).getName();
+    }
+    return myName;
+  }
+
+  public Artifact findArtifact(@NotNull ArtifactModel artifactModel) {
+    if (myArtifact != null) {
+      return artifactModel.getArtifactByOriginal(myArtifact);
+    }
+    return artifactModel.findArtifact(myName);
+  }
+
+  void setArtifact(Artifact artifact) {
+    myArtifact = artifact;
+  }
+
+  void setName(String name) {
+    myName = name;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactPointerManagerImpl.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactPointerManagerImpl.java
new file mode 100644
index 0000000..cc9d5db
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactPointerManagerImpl.java
@@ -0,0 +1,117 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.packaging.artifacts.*;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author nik
+ */
+public class ArtifactPointerManagerImpl extends ArtifactPointerManager {
+  private final Map<String, ArtifactPointerImpl> myUnresolvedPointers = new HashMap<String, ArtifactPointerImpl>();
+  private final Map<Artifact, ArtifactPointerImpl> myPointers = new HashMap<Artifact, ArtifactPointerImpl>();
+  private ArtifactManager myArtifactManager;
+
+  public ArtifactPointerManagerImpl(Project project) {
+    project.getMessageBus().connect().subscribe(ArtifactManager.TOPIC, new ArtifactAdapter() {
+      @Override
+      public void artifactRemoved(@NotNull Artifact artifact) {
+        disposePointer(artifact);
+      }
+
+      @Override
+      public void artifactAdded(@NotNull Artifact artifact) {
+        final ArtifactPointerImpl pointer = myPointers.get(artifact);
+        if (pointer != null) {
+          pointer.setName(artifact.getName());
+        }
+        
+        final ArtifactPointerImpl unresolved = myUnresolvedPointers.remove(artifact.getName());
+        if (unresolved != null) {
+          unresolved.setArtifact(artifact);
+          if (pointer == null) {
+            myPointers.put(artifact, unresolved);
+          }
+        }
+      }
+
+      @Override
+      public void artifactChanged(@NotNull Artifact artifact, @NotNull String oldName) {
+        artifactAdded(artifact);
+      }
+    });
+  }
+
+  public void setArtifactManager(ArtifactManager artifactManager) {
+    myArtifactManager = artifactManager;
+  }
+
+  private void disposePointer(Artifact artifact) {
+    final ArtifactPointerImpl pointer = myPointers.remove(artifact);
+    if (pointer != null) {
+      pointer.setArtifact(null);
+      myUnresolvedPointers.put(pointer.getArtifactName(), pointer);
+    }
+  }
+
+  public ArtifactPointer createPointer(@NotNull String name) {
+    if (myArtifactManager != null) {
+      final Artifact artifact = myArtifactManager.findArtifact(name);
+      if (artifact != null) {
+        return createPointer(artifact);
+      }
+    }
+
+    ArtifactPointerImpl pointer = myUnresolvedPointers.get(name);
+    if (pointer == null) {
+      pointer = new ArtifactPointerImpl(name);
+      myUnresolvedPointers.put(name, pointer);
+    }
+    return pointer;
+  }
+
+  public ArtifactPointer createPointer(@NotNull Artifact artifact) {
+    ArtifactPointerImpl pointer = myPointers.get(artifact);
+    if (pointer == null) {
+      pointer = myUnresolvedPointers.get(artifact.getName());
+      if (pointer != null) {
+        pointer.setArtifact(artifact);
+      }
+      else {
+        pointer = new ArtifactPointerImpl(artifact);
+      }
+      myPointers.put(artifact, pointer);
+    }
+    return pointer;
+  }
+
+  @Override
+  public ArtifactPointer createPointer(@NotNull Artifact artifact, @NotNull ArtifactModel artifactModel) {
+    return createPointer(artifactModel.getOriginalArtifact(artifact));
+  }
+
+  public void disposePointers(List<Artifact> artifacts) {
+    for (Artifact artifact : artifacts) {
+      disposePointer(artifact);
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactSortingUtil.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactSortingUtil.java
new file mode 100644
index 0000000..b3fe783
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactSortingUtil.java
@@ -0,0 +1,36 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactSortingUtil {
+  public static ArtifactSortingUtil getInstance(@NotNull Project project) {
+    return ServiceManager.getService(project, ArtifactSortingUtil.class);
+  }
+
+  public abstract Map<String, String> getArtifactToSelfIncludingNameMap();
+
+  public abstract List<String> getArtifactsSortedByInclusion();
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactSortingUtilImpl.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactSortingUtilImpl.java
new file mode 100644
index 0000000..e06f9de
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactSortingUtilImpl.java
@@ -0,0 +1,176 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactManager;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.impl.elements.ArtifactElementType;
+import com.intellij.packaging.impl.elements.ArtifactPackagingElement;
+import com.intellij.psi.util.CachedValue;
+import com.intellij.psi.util.CachedValueProvider;
+import com.intellij.psi.util.CachedValuesManager;
+import com.intellij.util.graph.CachingSemiGraph;
+import com.intellij.util.graph.DFSTBuilder;
+import com.intellij.util.graph.GraphGenerator;
+import gnu.trove.TIntArrayList;
+import gnu.trove.TIntProcedure;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class ArtifactSortingUtilImpl extends ArtifactSortingUtil {
+  private final Project myProject;
+  private CachedValue<Map<String, String>> myArtifactToSelfIncludingName;
+  private CachedValue<List<String>> mySortedArtifacts;
+
+  public ArtifactSortingUtilImpl(Project project) {
+    myProject = project;
+  }
+
+  @Override
+  public Map<String, String> getArtifactToSelfIncludingNameMap() {
+    if (myArtifactToSelfIncludingName == null) {
+      myArtifactToSelfIncludingName = CachedValuesManager.getManager(myProject).createCachedValue(new CachedValueProvider<Map<String, String>>() {
+        public Result<Map<String, String>> compute() {
+          return Result.create(computeArtifactToSelfIncludingNameMap(), ArtifactManager.getInstance(myProject).getModificationTracker());
+        }
+      }, false);
+    }
+    return myArtifactToSelfIncludingName.getValue();
+  }
+
+  @Override
+  public List<String> getArtifactsSortedByInclusion() {
+    if (mySortedArtifacts == null) {
+      mySortedArtifacts = CachedValuesManager.getManager(myProject).createCachedValue(new CachedValueProvider<List<String>>() {
+          @Override
+          public Result<List<String>> compute() {
+            return Result.create(doGetSortedArtifacts(), ArtifactManager.getInstance(myProject).getModificationTracker());
+          }
+        }, false);
+    }
+    return mySortedArtifacts.getValue();
+  }
+
+  private List<String> doGetSortedArtifacts() {
+    GraphGenerator<String> graph = createArtifactsGraph();
+    DFSTBuilder<String> builder = new DFSTBuilder<String>(graph);
+    builder.buildDFST();
+    List<String> names = new ArrayList<String>();
+    names.addAll(graph.getNodes());
+    Collections.sort(names, builder.comparator());
+    return names;
+  }
+
+  private Map<String, String> computeArtifactToSelfIncludingNameMap() {
+    final Map<String, String> result = new HashMap<String, String>();
+    final GraphGenerator<String> graph = createArtifactsGraph();
+    for (String artifactName : graph.getNodes()) {
+      final Iterator<String> in = graph.getIn(artifactName);
+      while (in.hasNext()) {
+        String next = in.next();
+        if (next.equals(artifactName)) {
+          result.put(artifactName, artifactName);
+          break;
+        }
+      }
+    }
+
+    final DFSTBuilder<String> builder = new DFSTBuilder<String>(graph);
+    builder.buildDFST();
+    if (builder.isAcyclic() && result.isEmpty()) return Collections.emptyMap();
+
+    final TIntArrayList sccs = builder.getSCCs();
+    sccs.forEach(new TIntProcedure() {
+      int myTNumber = 0;
+      public boolean execute(int size) {
+        if (size > 1) {
+          for (int j = 0; j < size; j++) {
+            final String artifactName = builder.getNodeByTNumber(myTNumber + j);
+            result.put(artifactName, artifactName);
+          }
+        }
+        myTNumber += size;
+        return true;
+      }
+    });
+
+    for (int i = 0; i < graph.getNodes().size(); i++) {
+      final String artifactName = builder.getNodeByTNumber(i);
+      if (!result.containsKey(artifactName)) {
+        final Iterator<String> in = graph.getIn(artifactName);
+        while (in.hasNext()) {
+          final String name = result.get(in.next());
+          if (name != null) {
+            result.put(artifactName, name);
+          }
+        }
+      }
+    }
+
+    return result;
+  }
+
+  private GraphGenerator<String> createArtifactsGraph() {
+    final ArtifactManager artifactManager = ArtifactManager.getInstance(myProject);
+    return GraphGenerator.create(CachingSemiGraph.create(new ArtifactsGraph(artifactManager)));
+  }
+
+  private class ArtifactsGraph implements GraphGenerator.SemiGraph<String> {
+    private final ArtifactManager myArtifactManager;
+    private final Set<String> myArtifactNames;
+
+    public ArtifactsGraph(ArtifactManager artifactManager) {
+      myArtifactManager = artifactManager;
+      myArtifactNames = new LinkedHashSet<String>();
+      for (Artifact artifact : myArtifactManager.getSortedArtifacts()) {
+        myArtifactNames.add(artifact.getName());
+      }
+    }
+
+    @Override
+    public Collection<String> getNodes() {
+      return myArtifactNames;
+    }
+
+    @Override
+    public Iterator<String> getIn(String name) {
+      final Set<String> included = new LinkedHashSet<String>();
+      final PackagingElementResolvingContext context = myArtifactManager.getResolvingContext();
+      final Artifact artifact = context.getArtifactModel().findArtifact(name);
+      if (artifact != null) {
+        ArtifactUtil.processPackagingElements(artifact, ArtifactElementType.ARTIFACT_ELEMENT_TYPE, new PackagingElementProcessor<ArtifactPackagingElement>() {
+          @Override
+          public boolean process(@NotNull ArtifactPackagingElement element,
+                                 @NotNull PackagingElementPath path) {
+            final String artifactName = element.getArtifactName();
+            if (myArtifactNames.contains(artifactName)) {
+              included.add(artifactName);
+            }
+            return true;
+          }
+        }, context, false);
+      }
+      return included.iterator();
+    }
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactUtil.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactUtil.java
new file mode 100644
index 0000000..0a272e8
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactUtil.java
@@ -0,0 +1,565 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.CompilerProjectExtension;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.Trinity;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+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.artifacts.ArtifactManager;
+import com.intellij.packaging.artifacts.ArtifactProperties;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.*;
+import com.intellij.packaging.impl.elements.*;
+import com.intellij.util.PathUtil;
+import com.intellij.util.Processor;
+import com.intellij.util.SmartList;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.FList;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class ArtifactUtil {
+  private ArtifactUtil() {
+  }
+
+  public static CompositePackagingElement<?> copyFromRoot(@NotNull CompositePackagingElement<?> oldRoot, @NotNull Project project) {
+    final CompositePackagingElement<?> newRoot = (CompositePackagingElement<?>)copyElement(oldRoot, project);
+    copyChildren(oldRoot, newRoot, project);
+    return newRoot;
+  }
+
+
+  public static void copyChildren(CompositePackagingElement<?> oldParent, CompositePackagingElement<?> newParent, @NotNull Project project) {
+    for (PackagingElement<?> child : oldParent.getChildren()) {
+      newParent.addOrFindChild(copyWithChildren(child, project));
+    }
+  }
+
+  @NotNull
+  public static <S> PackagingElement<S> copyWithChildren(@NotNull PackagingElement<S> element, @NotNull Project project) {
+    final PackagingElement<S> copy = copyElement(element, project);
+    if (element instanceof CompositePackagingElement<?>) {
+      copyChildren((CompositePackagingElement<?>)element, (CompositePackagingElement<?>)copy, project);
+    }
+    return copy;
+  }
+
+  @NotNull
+  private static <S> PackagingElement<S> copyElement(@NotNull PackagingElement<S> element, @NotNull Project project) {
+    //noinspection unchecked
+    final PackagingElement<S> copy = (PackagingElement<S>)element.getType().createEmpty(project);
+    copy.loadState(element.getState());
+    return copy;
+  }
+
+  public static <E extends PackagingElement<?>> boolean processPackagingElements(@NotNull Artifact artifact, @Nullable PackagingElementType<E> type,
+                                                                                 @NotNull final Processor<? super E> processor,
+                                                                                 final @NotNull PackagingElementResolvingContext resolvingContext,
+                                                                                 final boolean processSubstitutions) {
+    return processPackagingElements(artifact, type, new PackagingElementProcessor<E>() {
+      @Override
+      public boolean process(@NotNull E e, @NotNull PackagingElementPath path) {
+        return processor.process(e);
+      }
+    }, resolvingContext, processSubstitutions);
+  }
+
+  public static <E extends PackagingElement<?>> boolean processPackagingElements(@NotNull Artifact artifact, @Nullable PackagingElementType<E> type,
+                                                                                 @NotNull PackagingElementProcessor<? super E> processor,
+                                                                                 final @NotNull PackagingElementResolvingContext resolvingContext,
+                                                                                 final boolean processSubstitutions) {
+    return processPackagingElements(artifact.getRootElement(), type, processor, resolvingContext, processSubstitutions, artifact.getArtifactType());
+  }
+
+  public static <E extends PackagingElement<?>> boolean processPackagingElements(final PackagingElement<?> rootElement, @Nullable PackagingElementType<E> type,
+                                                                                 @NotNull PackagingElementProcessor<? super E> processor,
+                                                                                 final @NotNull PackagingElementResolvingContext resolvingContext,
+                                                                                 final boolean processSubstitutions,
+                                                                                 final ArtifactType artifactType) {
+    return processElementRecursively(rootElement, type, processor, resolvingContext, processSubstitutions, artifactType,
+                          PackagingElementPath.EMPTY, new HashSet<PackagingElement<?>>());
+  }
+
+  private static <E extends PackagingElement<?>> boolean processElementsRecursively(final List<? extends PackagingElement<?>> elements,
+                                                                         @Nullable PackagingElementType<E> type,
+                                                                         @NotNull PackagingElementProcessor<? super E> processor,
+                                                                         final @NotNull PackagingElementResolvingContext resolvingContext,
+                                                                         final boolean processSubstitutions, ArtifactType artifactType,
+                                                                         @NotNull PackagingElementPath path,
+                                                                         Set<PackagingElement<?>> processed) {
+    for (PackagingElement<?> element : elements) {
+      if (!processElementRecursively(element, type, processor, resolvingContext, processSubstitutions, artifactType, path, processed)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  private static <E extends PackagingElement<?>> boolean processElementRecursively(@NotNull PackagingElement<?> element, @Nullable PackagingElementType<E> type,
+                                                                         @NotNull PackagingElementProcessor<? super E> processor,
+                                                                         @NotNull PackagingElementResolvingContext resolvingContext,
+                                                                         final boolean processSubstitutions,
+                                                                         ArtifactType artifactType,
+                                                                         @NotNull PackagingElementPath path, Set<PackagingElement<?>> processed) {
+    if (!processor.shouldProcess(element) || !processed.add(element)) {
+      return true;
+    }
+    if (type == null || element.getType().equals(type)) {
+      if (!processor.process((E)element, path)) {
+        return false;
+      }
+    }
+    if (element instanceof CompositePackagingElement<?>) {
+      final CompositePackagingElement<?> composite = (CompositePackagingElement<?>)element;
+      return processElementsRecursively(composite.getChildren(), type, processor, resolvingContext, processSubstitutions, artifactType,
+                             path.appendComposite(composite), processed);
+    }
+    else if (element instanceof ComplexPackagingElement<?> && processSubstitutions) {
+      final ComplexPackagingElement<?> complexElement = (ComplexPackagingElement<?>)element;
+      if (processor.shouldProcessSubstitution(complexElement)) {
+        final List<? extends PackagingElement<?>> substitution = complexElement.getSubstitution(resolvingContext, artifactType);
+        if (substitution != null) {
+          return processElementsRecursively(substitution, type, processor, resolvingContext, processSubstitutions, artifactType,
+                                 path.appendComplex(complexElement), processed);
+        }
+      }
+    }
+    return true;
+  }
+
+  public static void removeDuplicates(@NotNull CompositePackagingElement<?> parent) {
+    List<PackagingElement<?>> prevChildren = new ArrayList<PackagingElement<?>>();
+
+    List<PackagingElement<?>> toRemove = new ArrayList<PackagingElement<?>>();
+    for (PackagingElement<?> child : parent.getChildren()) {
+      if (child instanceof CompositePackagingElement<?>) {
+        removeDuplicates((CompositePackagingElement<?>)child);
+      }
+      boolean merged = false;
+      for (PackagingElement<?> prevChild : prevChildren) {
+        if (child.isEqualTo(prevChild)) {
+          if (child instanceof CompositePackagingElement<?>) {
+            for (PackagingElement<?> childElement : ((CompositePackagingElement<?>)child).getChildren()) {
+              ((CompositePackagingElement<?>)prevChild).addOrFindChild(childElement);
+            }
+          }
+          merged = true;
+          break;
+        }
+      }
+      if (merged) {
+        toRemove.add(child);
+      }
+      else {
+        prevChildren.add(child);
+      }
+    }
+
+    for (PackagingElement<?> child : toRemove) {
+      parent.removeChild(child);
+    }
+  }
+
+  public static <S> void copyProperties(ArtifactProperties<?> from, ArtifactProperties<S> to) {
+    //noinspection unchecked
+    to.loadState((S)from.getState());
+  }
+
+  @Nullable
+  public static String getDefaultArtifactOutputPath(@NotNull String artifactName, final @NotNull Project project) {
+    final CompilerProjectExtension extension = CompilerProjectExtension.getInstance(project);
+    if (extension == null) return null;
+    String outputUrl = extension.getCompilerOutputUrl();
+    if (outputUrl == null || outputUrl.length() == 0) {
+      final VirtualFile baseDir = project.getBaseDir();
+      if (baseDir == null) return null;
+      outputUrl = baseDir.getUrl() + "/out";
+    }
+    return VfsUtil.urlToPath(outputUrl) + "/artifacts/" + FileUtil.sanitizeFileName(artifactName);
+  }
+
+  public static <E extends PackagingElement<?>> boolean processElementsWithSubstitutions(@NotNull List<? extends PackagingElement<?>> elements,
+                                        @NotNull PackagingElementResolvingContext context,
+                                        @NotNull ArtifactType artifactType,
+                                        @NotNull PackagingElementPath parentPath,
+                                        @NotNull PackagingElementProcessor<E> processor) {
+    return processElementsWithSubstitutions(elements, context, artifactType, parentPath, processor, new HashSet<PackagingElement<?>>());
+  }
+
+  private static <E extends PackagingElement<?>> boolean processElementsWithSubstitutions(@NotNull List<? extends PackagingElement<?>> elements,
+                                                                                         @NotNull PackagingElementResolvingContext context,
+                                                                                         @NotNull ArtifactType artifactType,
+                                                                                         @NotNull PackagingElementPath parentPath,
+                                                                                         @NotNull PackagingElementProcessor<E> processor,
+                                                                                         final Set<PackagingElement<?>> processed) {
+    for (PackagingElement<?> element : elements) {
+      if (!processed.add(element)) {
+        continue;
+      }
+
+      if (element instanceof ComplexPackagingElement<?> && processor.shouldProcessSubstitution((ComplexPackagingElement)element)) {
+        final ComplexPackagingElement<?> complexElement = (ComplexPackagingElement<?>)element;
+        final List<? extends PackagingElement<?>> substitution = complexElement.getSubstitution(context, artifactType);
+        if (substitution != null &&
+            !processElementsWithSubstitutions(substitution, context, artifactType, parentPath.appendComplex(complexElement), processor, processed)) {
+          return false;
+        }
+      }
+      else if (!processor.process((E)element, parentPath)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  public static List<PackagingElement<?>> findByRelativePath(@NotNull CompositePackagingElement<?> parent, @NotNull String relativePath,
+                                                             @NotNull PackagingElementResolvingContext context, @NotNull ArtifactType artifactType) {
+    final List<PackagingElement<?>> result = new ArrayList<PackagingElement<?>>();
+    processElementsByRelativePath(parent, relativePath, context, artifactType, PackagingElementPath.EMPTY, new PackagingElementProcessor<PackagingElement<?>>() {
+      @Override
+      public boolean process(@NotNull PackagingElement<?> packagingElement, @NotNull PackagingElementPath path) {
+        result.add(packagingElement);
+        return true;
+      }
+    });
+    return result;
+  }
+
+  public static boolean processElementsByRelativePath(@NotNull final CompositePackagingElement<?> parent, @NotNull String relativePath,
+                                                       @NotNull final PackagingElementResolvingContext context, @NotNull final ArtifactType artifactType,
+                                                       @NotNull PackagingElementPath parentPath,
+                                                       @NotNull final PackagingElementProcessor<PackagingElement<?>> processor) {
+    relativePath = StringUtil.trimStart(relativePath, "/");
+    if (relativePath.length() == 0) {
+      return true;
+    }
+
+    int i = relativePath.indexOf('/');
+    final String firstName = i != -1 ? relativePath.substring(0, i) : relativePath;
+    final String tail = i != -1 ? relativePath.substring(i+1) : "";
+
+    return processElementsWithSubstitutions(parent.getChildren(), context, artifactType, parentPath.appendComposite(parent), new PackagingElementProcessor<PackagingElement<?>>() {
+      @Override
+      public boolean process(@NotNull PackagingElement<?> element, @NotNull PackagingElementPath path) {
+        boolean process = false;
+        if (element instanceof CompositePackagingElement && firstName.equals(((CompositePackagingElement<?>)element).getName())) {
+          process = true;
+        }
+        else if (element instanceof FileCopyPackagingElement) {
+          final FileCopyPackagingElement fileCopy = (FileCopyPackagingElement)element;
+          if (firstName.equals(fileCopy.getOutputFileName())) {
+            process = true;
+          }
+        }
+        
+        if (process) {
+          if (tail.length() == 0) {
+            if (!processor.process(element, path)) return false;
+          }
+          else if (element instanceof CompositePackagingElement<?>) {
+            return processElementsByRelativePath((CompositePackagingElement)element, tail, context, artifactType, path, processor);
+          }
+        }
+        return true;
+      }
+    });
+  }
+
+  public static boolean processDirectoryChildren(@NotNull CompositePackagingElement<?> parent,
+                                                 @NotNull PackagingElementPath pathToParent,
+                                                 @NotNull String relativePath,
+                                                 @NotNull final PackagingElementResolvingContext context,
+                                                 @NotNull final ArtifactType artifactType,
+                                                 @NotNull final PackagingElementProcessor<PackagingElement<?>> processor) {
+    return processElementsByRelativePath(parent, relativePath, context, artifactType, pathToParent, new PackagingElementProcessor<PackagingElement<?>>() {
+      @Override
+      public boolean process(@NotNull PackagingElement<?> element, @NotNull PackagingElementPath path) {
+        if (element instanceof DirectoryPackagingElement) {
+          final List<PackagingElement<?>> children = ((DirectoryPackagingElement)element).getChildren();
+          if (!processElementsWithSubstitutions(children, context, artifactType, path.appendComposite((DirectoryPackagingElement)element), processor)) {
+            return false;
+          }
+        }
+        return true;
+      }
+    });
+  }
+
+  public static void processFileOrDirectoryCopyElements(Artifact artifact,
+                                                         PackagingElementProcessor<FileOrDirectoryCopyPackagingElement<?>> processor,
+                                                         PackagingElementResolvingContext context,
+                                                         boolean processSubstitutions) {
+    processPackagingElements(artifact, PackagingElementFactoryImpl.FILE_COPY_ELEMENT_TYPE, processor, context, processSubstitutions);
+    processPackagingElements(artifact, PackagingElementFactoryImpl.DIRECTORY_COPY_ELEMENT_TYPE, processor, context, processSubstitutions);
+    processPackagingElements(artifact, PackagingElementFactoryImpl.EXTRACTED_DIRECTORY_ELEMENT_TYPE, processor, context, processSubstitutions);
+  }
+
+  public static Collection<Trinity<Artifact, PackagingElementPath, String>> findContainingArtifactsWithOutputPaths(@NotNull final VirtualFile file,
+                                                                                                                   @NotNull Project project,
+                                                                                                                   final Artifact[] artifacts) {
+    final boolean isResourceFile = CompilerConfiguration.getInstance(project).isResourceFile(file);
+    final List<Trinity<Artifact, PackagingElementPath, String>> result = new ArrayList<Trinity<Artifact, PackagingElementPath, String>>();
+    final PackagingElementResolvingContext context = ArtifactManager.getInstance(project).getResolvingContext();
+    for (final Artifact artifact : artifacts) {
+      processPackagingElements(artifact, null, new PackagingElementProcessor<PackagingElement<?>>() {
+        @Override
+        public boolean process(@NotNull PackagingElement<?> element, @NotNull PackagingElementPath path) {
+          if (element instanceof FileOrDirectoryCopyPackagingElement<?>) {
+            final VirtualFile root = ((FileOrDirectoryCopyPackagingElement)element).findFile();
+            if (root != null && VfsUtil.isAncestor(root, file, false)) {
+              final String relativePath;
+              if (root.equals(file) && element instanceof FileCopyPackagingElement) {
+                relativePath = ((FileCopyPackagingElement)element).getOutputFileName();
+              }
+              else {
+                relativePath = VfsUtilCore.getRelativePath(file, root, '/');
+              }
+              result.add(Trinity.create(artifact, path, relativePath));
+              return false;
+            }
+          }
+          else if (isResourceFile && element instanceof ModuleOutputPackagingElement) {
+            final String relativePath = getRelativePathInSources(file, (ModuleOutputPackagingElement)element, context);
+            if (relativePath != null) {
+              result.add(Trinity.create(artifact, path, relativePath));
+              return false;
+            }
+          }
+          return true;
+        }
+      }, context, true);
+    }
+    return result;
+  }
+
+  @Nullable
+  private static String getRelativePathInSources(@NotNull VirtualFile file, final @NotNull ModuleOutputPackagingElement moduleElement,
+                                                @NotNull PackagingElementResolvingContext context) {
+    for (VirtualFile sourceRoot : moduleElement.getSourceRoots(context)) {
+      if (VfsUtil.isAncestor(sourceRoot, file, true)) {
+        return VfsUtilCore.getRelativePath(file, sourceRoot, '/');
+      }
+    }
+    return null;
+  }
+
+  @Nullable
+  public static VirtualFile findSourceFileByOutputPath(Artifact artifact, String outputPath, PackagingElementResolvingContext context) {
+    final List<VirtualFile> files = findSourceFilesByOutputPath(artifact.getRootElement(), outputPath, context, artifact.getArtifactType());
+    return files.isEmpty() ? null : files.get(0);
+  }
+
+  @Nullable
+  public static VirtualFile findSourceFileByOutputPath(CompositePackagingElement<?> parent, String outputPath,
+                                                 PackagingElementResolvingContext context, ArtifactType artifactType) {
+    final List<VirtualFile> files = findSourceFilesByOutputPath(parent, outputPath, context, artifactType);
+    return files.isEmpty() ? null : files.get(0);
+  }
+
+  public static List<VirtualFile> findSourceFilesByOutputPath(CompositePackagingElement<?> parent, final String outputPath,
+                                                              final PackagingElementResolvingContext context, final ArtifactType artifactType) {
+    final String path = StringUtil.trimStart(outputPath, "/");
+    if (path.length() == 0) {
+      return Collections.emptyList();
+    }
+
+    int i = path.indexOf('/');
+    final String firstName = i != -1 ? path.substring(0, i) : path;
+    final String tail = i != -1 ? path.substring(i+1) : "";
+
+    final List<VirtualFile> result = new SmartList<VirtualFile>();
+    processElementsWithSubstitutions(parent.getChildren(), context, artifactType, PackagingElementPath.EMPTY, new PackagingElementProcessor<PackagingElement<?>>() {
+      @Override
+      public boolean process(@NotNull PackagingElement<?> element, @NotNull PackagingElementPath elementPath) {
+        //todo[nik] replace by method findSourceFile() in PackagingElement
+        if (element instanceof CompositePackagingElement) {
+          final CompositePackagingElement<?> compositeElement = (CompositePackagingElement<?>)element;
+          if (firstName.equals(compositeElement.getName())) {
+            result.addAll(findSourceFilesByOutputPath(compositeElement, tail, context, artifactType));
+          }
+        }
+        else if (element instanceof FileCopyPackagingElement) {
+          final FileCopyPackagingElement fileCopyElement = (FileCopyPackagingElement)element;
+          if (firstName.equals(fileCopyElement.getOutputFileName()) && tail.length() == 0) {
+            ContainerUtil.addIfNotNull(fileCopyElement.findFile(), result);
+          }
+        }
+        else if (element instanceof DirectoryCopyPackagingElement || element instanceof ExtractedDirectoryPackagingElement) {
+          final VirtualFile sourceRoot = ((FileOrDirectoryCopyPackagingElement<?>)element).findFile();
+          if (sourceRoot != null) {
+            ContainerUtil.addIfNotNull(sourceRoot.findFileByRelativePath(path), result);
+          }
+        }
+        else if (element instanceof ModuleOutputPackagingElement) {
+          final CompilerConfiguration compilerConfiguration = CompilerConfiguration.getInstance(context.getProject());
+          for (VirtualFile sourceRoot : ((ModuleOutputPackagingElement)element).getSourceRoots(context)) {
+            final VirtualFile sourceFile = sourceRoot.findFileByRelativePath(path);
+            if (sourceFile != null && compilerConfiguration.isResourceFile(sourceFile)) {
+              result.add(sourceFile);
+            }
+          }
+        }
+        return true;
+      }
+    });
+
+    return result;
+  }
+
+  public static boolean processParents(@NotNull Artifact artifact,
+                                       @NotNull PackagingElementResolvingContext context,
+                                       @NotNull ParentElementProcessor processor,
+                                       int maxLevel) {
+    return processParents(artifact, context, processor, FList.<Pair<Artifact, CompositePackagingElement<?>>>emptyList(), maxLevel,
+                          new HashSet<Artifact>());
+  }
+
+  private static boolean processParents(@NotNull final Artifact artifact, @NotNull final PackagingElementResolvingContext context,
+                                        @NotNull final ParentElementProcessor processor, FList<Pair<Artifact, CompositePackagingElement<?>>> pathToElement,
+                                        final int maxLevel, final Set<Artifact> processed) {
+    if (!processed.add(artifact)) return true;
+
+    final FList<Pair<Artifact, CompositePackagingElement<?>>> pathFromRoot;
+    final CompositePackagingElement<?> rootElement = artifact.getRootElement();
+    if (rootElement instanceof ArtifactRootElement<?>) {
+      pathFromRoot = pathToElement;
+    }
+    else {
+      if (!processor.process(rootElement, pathToElement, artifact)) {
+        return false;
+      }
+      pathFromRoot = pathToElement.prepend(new Pair<Artifact, CompositePackagingElement<?>>(artifact, rootElement));
+    }
+    if (pathFromRoot.size() > maxLevel) return true;
+
+    for (final Artifact anArtifact : context.getArtifactModel().getArtifacts()) {
+      if (processed.contains(anArtifact)) continue;
+
+      final PackagingElementProcessor<ArtifactPackagingElement> elementProcessor =
+          new PackagingElementProcessor<ArtifactPackagingElement>() {
+            @Override
+            public boolean shouldProcessSubstitution(ComplexPackagingElement<?> element) {
+              return !(element instanceof ArtifactPackagingElement);
+            }
+
+            @Override
+            public boolean process(@NotNull ArtifactPackagingElement element, @NotNull PackagingElementPath path) {
+              if (artifact.getName().equals(element.getArtifactName())) {
+                FList<Pair<Artifact, CompositePackagingElement<?>>> currentPath = pathFromRoot;
+                final List<CompositePackagingElement<?>> parents = path.getParents();
+                for (int i = 0, parentsSize = parents.size(); i < parentsSize - 1; i++) {
+                  CompositePackagingElement<?> parent = parents.get(i);
+                  if (!processor.process(parent, currentPath, anArtifact)) {
+                    return false;
+                  }
+                  currentPath = currentPath.prepend(new Pair<Artifact, CompositePackagingElement<?>>(anArtifact, parent));
+                  if (currentPath.size() > maxLevel) {
+                    return true;
+                  }
+                }
+
+                if (!parents.isEmpty()) {
+                  CompositePackagingElement<?> lastParent = parents.get(parents.size() - 1);
+                  if (lastParent instanceof ArtifactRootElement<?> && !processor.process(lastParent, currentPath, anArtifact)) {
+                    return false;
+                  }
+                }
+                return processParents(anArtifact, context, processor, currentPath, maxLevel, processed);
+              }
+              return true;
+            }
+          };
+      if (!processPackagingElements(anArtifact, ArtifactElementType.ARTIFACT_ELEMENT_TYPE, elementProcessor, context, true)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  public static boolean isArchiveName(String name) {
+    return name.length() >= 4 && name.charAt(name.length() - 4) == '.' && StringUtil.endsWithIgnoreCase(name, "ar");
+  }
+
+  public static void removeChildrenRecursively(@NotNull CompositePackagingElement<?> element, @NotNull Condition<PackagingElement<?>> condition) {
+    List<PackagingElement<?>> toRemove = new ArrayList<PackagingElement<?>>();
+    for (PackagingElement<?> child : element.getChildren()) {
+      if (child instanceof CompositePackagingElement<?>) {
+        final CompositePackagingElement<?> compositeChild = (CompositePackagingElement<?>)child;
+        removeChildrenRecursively(compositeChild, condition);
+        if (compositeChild.getChildren().isEmpty()) {
+          toRemove.add(child);
+        }
+      }
+      else if (condition.value(child)) {
+        toRemove.add(child);
+      }
+    }
+
+    element.removeChildren(toRemove);
+  }
+
+  public static boolean shouldClearArtifactOutputBeforeRebuild(Artifact artifact) {
+    final String outputPath = artifact.getOutputPath();
+    return !StringUtil.isEmpty(outputPath) && artifact.getRootElement() instanceof ArtifactRootElement<?>;
+  }
+
+  public static Set<Module> getModulesIncludedInArtifacts(final @NotNull Collection<? extends Artifact> artifacts, final @NotNull Project project) {
+    final Set<Module> modules = new HashSet<Module>();
+    final PackagingElementResolvingContext resolvingContext = ArtifactManager.getInstance(project).getResolvingContext();
+    for (Artifact artifact : artifacts) {
+      processPackagingElements(artifact, null, new Processor<PackagingElement<?>>() {
+        @Override
+        public boolean process(PackagingElement<?> element) {
+          if (element instanceof ModuleOutputPackagingElement) {
+            ContainerUtil.addIfNotNull(modules, ((ModuleOutputPackagingElement)element).findModule(resolvingContext));
+          }
+          return true;
+        }
+      }, resolvingContext, true);
+    }
+    return modules;
+  }
+
+  public static List<Artifact> getArtifactWithOutputPaths(Project project) {
+    final List<Artifact> result = new ArrayList<Artifact>();
+    for (Artifact artifact : ArtifactManager.getInstance(project).getSortedArtifacts()) {
+      if (!StringUtil.isEmpty(artifact.getOutputPath())) {
+        result.add(artifact);
+      }
+    }
+    return result;
+  }
+
+  public static String suggestArtifactFileName(String artifactName) {
+    return PathUtil.suggestFileName(artifactName, true, true);
+  }
+}
+
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactVirtualFileListener.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactVirtualFileListener.java
new file mode 100644
index 0000000..641bae7
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactVirtualFileListener.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.
+ */
+package com.intellij.packaging.impl.artifacts;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.MultiValuesMap;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileAdapter;
+import com.intellij.openapi.vfs.VirtualFileMoveEvent;
+import com.intellij.openapi.vfs.VirtualFilePropertyEvent;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ModifiableArtifactModel;
+import com.intellij.packaging.impl.elements.FileOrDirectoryCopyPackagingElement;
+import com.intellij.psi.util.CachedValue;
+import com.intellij.psi.util.CachedValueProvider;
+import com.intellij.psi.util.CachedValuesManager;
+import com.intellij.util.PathUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+
+/**
+ * @author nik
+ */
+public class ArtifactVirtualFileListener extends VirtualFileAdapter {
+  private final CachedValue<MultiValuesMap<String, Artifact>> myParentPathsToArtifacts;
+  private final ArtifactManagerImpl myArtifactManager;
+
+  public ArtifactVirtualFileListener(Project project, final ArtifactManagerImpl artifactManager) {
+    myArtifactManager = artifactManager;
+    myParentPathsToArtifacts =
+      CachedValuesManager.getManager(project).createCachedValue(new CachedValueProvider<MultiValuesMap<String, Artifact>>() {
+        public Result<MultiValuesMap<String, Artifact>> compute() {
+          MultiValuesMap<String, Artifact> result = computeParentPathToArtifactMap();
+          return Result.createSingleDependency(result, artifactManager.getModificationTracker());
+        }
+      }, false);
+  }
+
+  private MultiValuesMap<String, Artifact> computeParentPathToArtifactMap() {
+    final MultiValuesMap<String, Artifact> result = new MultiValuesMap<String, Artifact>();
+    for (final Artifact artifact : myArtifactManager.getArtifacts()) {
+      ArtifactUtil.processFileOrDirectoryCopyElements(artifact, new PackagingElementProcessor<FileOrDirectoryCopyPackagingElement<?>>() {
+        @Override
+        public boolean process(@NotNull FileOrDirectoryCopyPackagingElement<?> element, @NotNull PackagingElementPath pathToElement) {
+          String path = element.getFilePath();
+          while (path.length() > 0) {
+            result.put(path, artifact);
+            path = PathUtil.getParentPath(path);
+          }
+          return true;
+        }
+      }, myArtifactManager.getResolvingContext(), false);
+    }
+    return result;
+  }
+
+
+  @Override
+  public void fileMoved(VirtualFileMoveEvent event) {
+    final String oldPath = event.getOldParent().getPath() + "/" + event.getFileName();
+    filePathChanged(oldPath, event.getNewParent().getPath() + "/" + event.getFileName());
+  }
+
+  private void filePathChanged(@NotNull final String oldPath, @NotNull final String newPath) {
+    final Collection<Artifact> artifacts = myParentPathsToArtifacts.getValue().get(oldPath);
+    if (artifacts != null) {
+      final ModifiableArtifactModel model = myArtifactManager.createModifiableModel();
+      for (Artifact artifact : artifacts) {
+        final Artifact copy = model.getOrCreateModifiableArtifact(artifact);
+        ArtifactUtil.processFileOrDirectoryCopyElements(copy, new PackagingElementProcessor<FileOrDirectoryCopyPackagingElement<?>>() {
+          @Override
+          public boolean process(@NotNull FileOrDirectoryCopyPackagingElement<?> element, @NotNull PackagingElementPath pathToElement) {
+            final String path = element.getFilePath();
+            if (FileUtil.startsWith(path, oldPath)) {
+              element.setFilePath(newPath + path.substring(oldPath.length()));
+            }
+            return true;
+          }
+        }, myArtifactManager.getResolvingContext(), false);
+      }
+      model.commit();
+    }
+  }
+
+  @Override
+  public void propertyChanged(VirtualFilePropertyEvent event) {
+    if (VirtualFile.PROP_NAME.equals(event.getPropertyName())) {
+      final VirtualFile parent = event.getParent();
+      if (parent != null) {
+        filePathChanged(parent.getPath() + "/" + event.getOldValue(), parent.getPath() + "/" + event.getNewValue());
+      }
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/DefaultManifestFileProvider.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/DefaultManifestFileProvider.java
new file mode 100644
index 0000000..c80617b
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/DefaultManifestFileProvider.java
@@ -0,0 +1,49 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.ManifestFileProvider;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.impl.elements.ManifestFileUtil;
+import com.intellij.packaging.ui.ManifestFileConfiguration;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+* @author nik
+*/
+public class DefaultManifestFileProvider implements ManifestFileProvider {
+  private final PackagingElementResolvingContext myContext;
+
+  public DefaultManifestFileProvider(PackagingElementResolvingContext context) {
+    myContext = context;
+  }
+
+  @Override
+  public List<String> getClasspathFromManifest(@NotNull CompositePackagingElement<?> archiveRoot, @NotNull ArtifactType artifactType) {
+    final VirtualFile manifestFile = ManifestFileUtil.findManifestFile(archiveRoot, myContext, artifactType);
+    if (manifestFile == null) {
+      return null;
+    }
+
+    ManifestFileConfiguration configuration = ManifestFileUtil.createManifestFileConfiguration(manifestFile);
+    return configuration.getClasspath();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/DefaultPackagingElementResolvingContext.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/DefaultPackagingElementResolvingContext.java
new file mode 100644
index 0000000..16000b4
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/DefaultPackagingElementResolvingContext.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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.facet.impl.DefaultFacetsProvider;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import com.intellij.openapi.roots.ui.configuration.DefaultModulesProvider;
+import com.intellij.openapi.roots.ui.configuration.FacetsProvider;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.packaging.artifacts.ArtifactManager;
+import com.intellij.packaging.artifacts.ArtifactModel;
+import com.intellij.packaging.elements.ManifestFileProvider;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+* @author nik
+*/
+public class DefaultPackagingElementResolvingContext implements PackagingElementResolvingContext {
+  private final Project myProject;
+  private final DefaultModulesProvider myModulesProvider;
+
+  public DefaultPackagingElementResolvingContext(Project project) {
+    myProject = project;
+    myModulesProvider = new DefaultModulesProvider(myProject);
+  }
+
+  @NotNull
+  public Project getProject() {
+    return myProject;
+  }
+
+  @NotNull
+  public ArtifactModel getArtifactModel() {
+    return ArtifactManager.getInstance(myProject);
+  }
+
+  @NotNull
+  public ModulesProvider getModulesProvider() {
+    return myModulesProvider;
+  }
+
+  @NotNull
+  public FacetsProvider getFacetsProvider() {
+    return DefaultFacetsProvider.INSTANCE;
+  }
+
+  public Library findLibrary(@NotNull String level, @NotNull String libraryName) {
+    return findLibrary(myProject, level, libraryName);
+  }
+
+  @NotNull
+  @Override
+  public ManifestFileProvider getManifestFileProvider() {
+    return new DefaultManifestFileProvider(this);
+  }
+
+  @Nullable
+  public static Library findLibrary(Project project, String level, String libraryName) {
+    LibraryTable table = LibraryTablesRegistrar.getInstance().getLibraryTableByLevel(level, project);
+    return table != null ? table.getLibraryByName(libraryName) : null;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/InvalidArtifact.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/InvalidArtifact.java
new file mode 100644
index 0000000..c6df02e
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/InvalidArtifact.java
@@ -0,0 +1,41 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.packaging.elements.PackagingElementFactory;
+import org.jetbrains.jps.model.serialization.artifact.ArtifactState;
+
+/**
+ * @author nik
+ */
+public class InvalidArtifact extends ArtifactImpl {
+  private ArtifactState myState;
+  private final String myErrorMessage;
+
+  public InvalidArtifact(ArtifactState state, String errorMessage) {
+    super(state.getName(), InvalidArtifactType.getInstance(), false, PackagingElementFactory.getInstance().createArtifactRootElement(), "");
+    myState = state;
+    myErrorMessage = errorMessage;
+  }
+
+  public String getErrorMessage() {
+    return myErrorMessage;
+  }
+
+  public ArtifactState getState() {
+    return myState;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/InvalidArtifactType.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/InvalidArtifactType.java
new file mode 100644
index 0000000..30a0010
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/InvalidArtifactType.java
@@ -0,0 +1,57 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElementFactory;
+import com.intellij.packaging.elements.PackagingElementOutputKind;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public class InvalidArtifactType extends ArtifactType {
+
+  public static InvalidArtifactType getInstance() {
+    return ServiceManager.getService(InvalidArtifactType.class);
+  }
+
+  public InvalidArtifactType() {
+    super("invalid", "Invalid");
+  }
+
+  @NotNull
+  @Override
+  public Icon getIcon() {
+    return AllIcons.FileTypes.Unknown;
+  }
+
+  @Override
+  public String getDefaultPathFor(@NotNull PackagingElementOutputKind kind) {
+    return "";
+  }
+
+  @NotNull
+  @Override
+  public CompositePackagingElement<?> createRootElement(@NotNull String artifactName) {
+    return PackagingElementFactory.getInstance().createArtifactRootElement();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactFromModulesDialog.form b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactFromModulesDialog.form
new file mode 100644
index 0000000..eed2b88
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactFromModulesDialog.form
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.packaging.impl.artifacts.JarArtifactFromModulesDialog">
+  <grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="7" 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>
+      <xy x="20" y="20" width="521" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <component id="c89c9" class="javax.swing.JLabel" binding="myMainClassLabel">
+        <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 value="Main &amp;Class:"/>
+        </properties>
+      </component>
+      <vspacer id="8cb38">
+        <constraints>
+          <grid row="6" 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>
+      <component id="535a8" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myMainClassField">
+        <constraints>
+          <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="2fb86" 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="4853e"/>
+          <text value="&amp;Module:"/>
+        </properties>
+      </component>
+      <component id="4853e" class="javax.swing.JComboBox" binding="myModuleComboBox">
+        <constraints>
+          <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false">
+            <preferred-size width="400" height="-1"/>
+          </grid>
+        </constraints>
+        <properties/>
+      </component>
+      <component id="8e551" class="javax.swing.JLabel" binding="myManifestDirLabel">
+        <constraints>
+          <grid row="3" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="&amp;Directory for META-INF/MANIFEST.MF:"/>
+        </properties>
+      </component>
+      <component id="1ea2d" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myManifestDirField">
+        <constraints>
+          <grid row="4" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="2" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+      </component>
+      <grid id="53c3f" layout-manager="GridLayoutManager" row-count="2" 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="2" column="0" row-span="1" col-span="2" 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="Jar files from libraries"/>
+        <children>
+          <component id="f9a17" class="javax.swing.JRadioButton" binding="myExtractJarsRadioButton">
+            <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>
+              <selected value="true"/>
+              <text value="&amp;extract to the target jar"/>
+            </properties>
+          </component>
+          <hspacer id="90c4f">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+            </constraints>
+          </hspacer>
+          <component id="b3972" class="javax.swing.JRadioButton" binding="myCopyJarsRadioButton">
+            <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 value="copy &amp;to the output directory and link via manifest"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <component id="d5a56" class="javax.swing.JCheckBox" binding="myIncludeTestsCheckBox">
+        <constraints>
+          <grid row="5" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="&amp;Include tests"/>
+        </properties>
+      </component>
+    </children>
+  </grid>
+  <buttonGroups>
+    <group name="jarFiles">
+      <member id="f9a17"/>
+      <member id="b3972"/>
+    </group>
+  </buttonGroups>
+</form>
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactFromModulesDialog.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactFromModulesDialog.java
new file mode 100644
index 0000000..02d9e42
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactFromModulesDialog.java
@@ -0,0 +1,178 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.ModulesAlphaComparator;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.impl.elements.ManifestFileUtil;
+import com.intellij.ui.ComboboxSpeedSearch;
+import com.intellij.ui.DocumentAdapter;
+import com.intellij.ui.ListCellRendererWrapper;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Arrays;
+
+/**
+ * @author nik
+ */
+public class JarArtifactFromModulesDialog extends DialogWrapper {
+  private JPanel myMainPanel;
+  private TextFieldWithBrowseButton myMainClassField;
+  private JComboBox myModuleComboBox;
+  private JLabel myMainClassLabel;
+  private TextFieldWithBrowseButton myManifestDirField;
+  private JLabel myManifestDirLabel;
+  private JRadioButton myExtractJarsRadioButton;
+  private JRadioButton myCopyJarsRadioButton;
+  private JCheckBox myIncludeTestsCheckBox;
+  private PackagingElementResolvingContext myContext;
+
+  public JarArtifactFromModulesDialog(PackagingElementResolvingContext context) {
+    super(context.getProject());
+    myContext = context;
+    setTitle("Create Jar from Modules");
+    myMainClassLabel.setLabelFor(myMainClassField.getTextField());
+    myManifestDirLabel.setLabelFor(myManifestDirField.getTextField());
+
+    final Project project = myContext.getProject();
+    ManifestFileUtil.setupMainClassField(project, myMainClassField);
+    myMainClassField.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
+      @Override
+      protected void textChanged(DocumentEvent e) {
+        updateManifestDirField();
+      }
+    });
+    final ActionListener actionListener = new ActionListener() {
+      @Override
+      public void actionPerformed(ActionEvent e) {
+        updateManifestDirField();
+      }
+    };
+    myExtractJarsRadioButton.addActionListener(actionListener);
+    myCopyJarsRadioButton.addActionListener(actionListener);
+
+    updateManifestDirField();
+    myManifestDirField.addBrowseFolderListener(null, null, project, ManifestFileUtil.createDescriptorForManifestDirectory());
+
+    setupModulesCombobox(context);
+    init();
+  }
+
+  private void setupModulesCombobox(PackagingElementResolvingContext context) {
+    final Module[] modules = context.getModulesProvider().getModules().clone();
+    Arrays.sort(modules, ModulesAlphaComparator.INSTANCE);
+    if (modules.length > 1) {
+      myModuleComboBox.addItem(null);
+    }
+    for (Module module : modules) {
+      myModuleComboBox.addItem(module);
+    }
+    myModuleComboBox.setRenderer(new ModuleListRenderer(myModuleComboBox));
+    new ComboboxSpeedSearch(myModuleComboBox) {
+      @Override
+      protected String getElementText(Object element) {
+        return element instanceof Module ? ((Module)element).getName() : "";
+      }
+    };
+  }
+
+  private void updateManifestDirField() {
+    final boolean enable = !myMainClassField.getText().isEmpty() || !myExtractJarsRadioButton.isSelected();
+    setManifestDirFieldEnabled(enable);
+    if (enable && myManifestDirField.getText().isEmpty()) {
+      final VirtualFile file = ManifestFileUtil.suggestManifestFileDirectory(myContext.getProject(), getSelectedModule());
+      if (file != null) {
+        myManifestDirField.setText(FileUtil.toSystemDependentName(file.getPath()));
+      }
+    }
+  }
+
+  @Nullable
+  private Module getSelectedModule() {
+    return (Module)myModuleComboBox.getSelectedItem();
+  }
+
+  @NotNull
+  public Module[] getSelectedModules() {
+    final Module module = getSelectedModule();
+    if (module != null) {
+      return new Module[]{module};
+    }
+    return myContext.getModulesProvider().getModules();
+  }
+
+  @NotNull
+  public String getDirectoryForManifest() {
+    return FileUtil.toSystemIndependentName(myManifestDirField.getText());
+  }
+
+  public boolean isExtractLibrariesToJar() {
+    return myExtractJarsRadioButton.isSelected();
+  }
+
+  public boolean isIncludeTests() {
+    return myIncludeTestsCheckBox.isSelected();
+  }
+
+  public String getMainClassName() {
+    return myMainClassField.getText();
+  }
+
+  private void setManifestDirFieldEnabled(boolean enabled) {
+    myManifestDirLabel.setEnabled(enabled);
+    myManifestDirField.setEnabled(enabled);
+  }
+
+  @Override
+  protected JComponent createCenterPanel() {
+    return myMainPanel;
+  }
+
+  @Override
+  protected String getHelpId() {
+    return "reference.project.structure.artifacts.jar.from.module";
+  }
+
+  private static class ModuleListRenderer extends ListCellRendererWrapper<Module> {
+    public ModuleListRenderer(JComboBox comboBox) {
+      super();
+    }
+
+    @Override
+    public void customize(JList list, Module value, int index, boolean selected, boolean hasFocus) {
+      if (value != null) {
+        setIcon(ModuleType.get(value).getIcon());
+        setText(value.getName());
+      }
+      else {
+        setText("<All Modules>");
+        setIcon(null);
+      }
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactType.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactType.java
new file mode 100644
index 0000000..19571e4
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactType.java
@@ -0,0 +1,65 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.packaging.artifacts.ArtifactTemplate;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElementOutputKind;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.impl.elements.ArchivePackagingElement;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class JarArtifactType extends ArtifactType {
+  public JarArtifactType() {
+    super("jar", "Jar");
+  }
+
+  public static JarArtifactType getInstance() {
+    return EP_NAME.findExtension(JarArtifactType.class);
+  }
+
+  @NotNull
+  @Override
+  public Icon getIcon() {
+    return AllIcons.Nodes.Artifact;
+  }
+
+  @Override
+  public String getDefaultPathFor(@NotNull PackagingElementOutputKind kind) {
+    return "/";
+  }
+
+  @NotNull
+  @Override
+  public CompositePackagingElement<?> createRootElement(@NotNull String artifactName) {
+    return new ArchivePackagingElement(ArtifactUtil.suggestArtifactFileName(artifactName) + ".jar");
+  }
+
+  @NotNull
+  @Override
+  public List<? extends ArtifactTemplate> getNewArtifactTemplates(@NotNull PackagingElementResolvingContext context) {
+    return Collections.singletonList(new JarFromModulesTemplate(context));
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarFromModulesTemplate.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarFromModulesTemplate.java
new file mode 100644
index 0000000..308a211
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarFromModulesTemplate.java
@@ -0,0 +1,191 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.CommonBundle;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.OrderEnumerator;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.ArtifactTemplate;
+import com.intellij.packaging.elements.*;
+import com.intellij.packaging.impl.elements.LibraryPackagingElement;
+import com.intellij.packaging.impl.elements.ManifestFileUtil;
+import com.intellij.packaging.impl.elements.ProductionModuleOutputElementType;
+import com.intellij.packaging.impl.elements.TestModuleOutputElementType;
+import com.intellij.util.CommonProcessors;
+import com.intellij.util.PathUtil;
+import com.intellij.util.Processor;
+import gnu.trove.THashSet;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class JarFromModulesTemplate extends ArtifactTemplate {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.packaging.impl.artifacts.JarFromModulesTemplate");
+
+  private PackagingElementResolvingContext myContext;
+
+  public JarFromModulesTemplate(PackagingElementResolvingContext context) {
+    myContext = context;
+  }
+
+  @Override
+  public NewArtifactConfiguration createArtifact() {
+    JarArtifactFromModulesDialog dialog = new JarArtifactFromModulesDialog(myContext);
+    dialog.show();
+    if (!dialog.isOK()) {
+      return null;
+    }
+
+    return doCreateArtifact(dialog.getSelectedModules(), dialog.getMainClassName(), dialog.getDirectoryForManifest(),
+                            dialog.isExtractLibrariesToJar(), dialog.isIncludeTests());
+  }
+
+  @Nullable
+  public NewArtifactConfiguration doCreateArtifact(final Module[] modules, final String mainClassName,
+                                                   final String directoryForManifest,
+                                                   final boolean extractLibrariesToJar,
+                                                   final boolean includeTests) {
+    VirtualFile manifestFile = null;
+    final Project project = myContext.getProject();
+    if (mainClassName != null && !mainClassName.isEmpty() || !extractLibrariesToJar) {
+      final VirtualFile directory;
+      try {
+        directory = VfsUtil.createDirectoryIfMissing(directoryForManifest);
+      }
+      catch (IOException e) {
+        LOG.info(e);
+        Messages.showErrorDialog(project, "Cannot create directory '" + directoryForManifest + "': " + e.getMessage(),
+                                 CommonBundle.getErrorTitle());
+        return null;
+      }
+      if (directory == null) return null;
+
+      manifestFile = ManifestFileUtil.createManifestFile(directory, project);
+      if (manifestFile == null) {
+        return null;
+      }
+      ManifestFileUtil.updateManifest(manifestFile, mainClassName, null, true);
+    }
+
+    String name = modules.length == 1 ? modules[0].getName() : project.getName();
+
+    final PackagingElementFactory factory = PackagingElementFactory.getInstance();
+    final CompositePackagingElement<?> archive = factory.createArchive(ArtifactUtil.suggestArtifactFileName(name) + ".jar");
+
+    OrderEnumerator orderEnumerator = ProjectRootManager.getInstance(project).orderEntries(Arrays.asList(modules));
+
+    final Set<Library> libraries = new THashSet<Library>();
+    if (!includeTests) {
+      orderEnumerator = orderEnumerator.productionOnly();
+    }
+    final ModulesProvider modulesProvider = myContext.getModulesProvider();
+    final OrderEnumerator enumerator = orderEnumerator.using(modulesProvider).withoutSdk().runtimeOnly().recursively();
+    enumerator.forEachLibrary(new CommonProcessors.CollectProcessor<Library>(libraries));
+    enumerator.forEachModule(new Processor<Module>() {
+      @Override
+      public boolean process(Module module) {
+        if (ProductionModuleOutputElementType.ELEMENT_TYPE.isSuitableModule(modulesProvider, module)) {
+          archive.addOrFindChild(factory.createModuleOutput(module));
+        }
+        if (includeTests && TestModuleOutputElementType.ELEMENT_TYPE.isSuitableModule(modulesProvider, module)) {
+          archive.addOrFindChild(factory.createTestModuleOutput(module));
+        }
+        return true;
+      }
+    });
+
+    final JarArtifactType jarArtifactType = JarArtifactType.getInstance();
+    if (manifestFile != null && !manifestFile.equals(ManifestFileUtil.findManifestFile(archive, myContext, jarArtifactType))) {
+      archive.addFirstChild(factory.createFileCopyWithParentDirectories(manifestFile.getPath(), ManifestFileUtil.MANIFEST_DIR_NAME));
+    }
+
+    final String artifactName = name + ":jar";
+    if (extractLibrariesToJar) {
+      addExtractedLibrariesToJar(archive, factory, libraries);
+      return new NewArtifactConfiguration(archive, artifactName, jarArtifactType);
+    }
+    else {
+      final ArtifactRootElement<?> root = factory.createArtifactRootElement();
+      List<String> classpath = new ArrayList<String>();
+      root.addOrFindChild(archive);
+      addLibraries(libraries, root, archive, classpath);
+      ManifestFileUtil.updateManifest(manifestFile, mainClassName, classpath, true);
+      return new NewArtifactConfiguration(root, artifactName, PlainArtifactType.getInstance());
+    }
+  }
+
+  private void addLibraries(Set<Library> libraries, ArtifactRootElement<?> root, CompositePackagingElement<?> archive,
+                            List<String> classpath) {
+    PackagingElementFactory factory = PackagingElementFactory.getInstance();
+    for (Library library : libraries) {
+      if (LibraryPackagingElement.getKindForLibrary(library).containsDirectoriesWithClasses()) {
+        for (VirtualFile classesRoot : library.getFiles(OrderRootType.CLASSES)) {
+          if (classesRoot.isInLocalFileSystem()) {
+            archive.addOrFindChild(factory.createDirectoryCopyWithParentDirectories(classesRoot.getPath(), "/"));
+          }
+          else {
+            final PackagingElement<?> child = factory.createFileCopyWithParentDirectories(PathUtil.getLocalFile(classesRoot).getPath(), "/");
+            root.addOrFindChild(child);
+            classpath.addAll(ManifestFileUtil.getClasspathForElements(Collections.singletonList(child), myContext, PlainArtifactType.getInstance()));
+          }
+        }
+
+      }
+      else {
+        final List<? extends PackagingElement<?>> children = factory.createLibraryElements(library);
+        classpath.addAll(ManifestFileUtil.getClasspathForElements(children, myContext, PlainArtifactType.getInstance()));
+        root.addOrFindChildren(children);
+      }
+    }
+  }
+
+  private static void addExtractedLibrariesToJar(CompositePackagingElement<?> archive, PackagingElementFactory factory, Set<Library> libraries) {
+    for (Library library : libraries) {
+      if (LibraryPackagingElement.getKindForLibrary(library).containsJarFiles()) {
+        for (VirtualFile classesRoot : library.getFiles(OrderRootType.CLASSES)) {
+          if (classesRoot.isInLocalFileSystem()) {
+            archive.addOrFindChild(factory.createDirectoryCopyWithParentDirectories(classesRoot.getPath(), "/"));
+          }
+          else {
+            archive.addOrFindChild(factory.createExtractedDirectory(classesRoot));
+          }
+        }
+
+      }
+      else {
+        archive.addOrFindChildren(factory.createLibraryElements(library));
+      }
+    }
+  }
+
+  @Override
+  public String getPresentableName() {
+    return "From modules with dependencies...";
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/PackagingElementPath.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/PackagingElementPath.java
new file mode 100644
index 0000000..dda0981
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/PackagingElementPath.java
@@ -0,0 +1,138 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.ComplexPackagingElement;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.impl.elements.ArtifactPackagingElement;
+import com.intellij.util.SmartList;
+import com.intellij.util.StringBuilderSpinAllocator;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class PackagingElementPath {
+  public static final PackagingElementPath EMPTY = new PackagingElementPath(null, null);
+  private final PackagingElementPath myParentPath;
+  private final PackagingElement<?> myLastElement;
+
+  private PackagingElementPath(PackagingElementPath parentPath, PackagingElement<?> lastElement) {
+    myParentPath = parentPath;
+    myLastElement = lastElement;
+  }
+
+  public PackagingElementPath appendComplex(ComplexPackagingElement<?> element) {
+    return new PackagingElementPath(this, element);
+  }
+
+  public PackagingElementPath appendComposite(CompositePackagingElement<?> element) {
+    return new PackagingElementPath(this, element);
+  }
+
+  @NotNull
+  public String getPathString() {
+    return getPathString("/");
+  }
+
+  @NotNull
+  public String getPathString(String separator) {
+    return getPathStringFrom(separator, null);
+  }
+
+  @NotNull
+  public String getPathStringFrom(String separator, @Nullable CompositePackagingElement<?> ancestor) {
+    final StringBuilder builder = StringBuilderSpinAllocator.alloc();
+    try {
+      final List<CompositePackagingElement<?>> parents = getParentsFrom(ancestor);
+      for (int i = parents.size() - 1; i >= 0; i--) {
+        builder.append(parents.get(i).getName());
+        if (i > 0) {
+          builder.append(separator);
+        }
+      }
+      return builder.toString();
+    }
+    finally {
+      StringBuilderSpinAllocator.dispose(builder);
+    }
+  }
+  
+  public List<CompositePackagingElement<?>> getParents() {
+    return getParentsFrom(null);
+  }
+
+  public List<CompositePackagingElement<?>> getParentsFrom(@Nullable CompositePackagingElement<?> ancestor) {
+    List<CompositePackagingElement<?>> result = new SmartList<CompositePackagingElement<?>>();
+    PackagingElementPath path = this;
+    while (path != EMPTY && path.myLastElement != ancestor) {
+      if (path.myLastElement instanceof CompositePackagingElement<?>) {
+        result.add((CompositePackagingElement)path.myLastElement);
+      }
+      path = path.myParentPath;
+    }
+    return result;
+  }
+
+  public List<PackagingElement<?>> getAllElements() {
+    List<PackagingElement<?>> result = new SmartList<PackagingElement<?>>();
+    PackagingElementPath path = this;
+    while (path != EMPTY) {
+      result.add(path.myLastElement);
+      path = path.myParentPath;
+    }
+    return result;
+  }
+
+  @Nullable
+  public CompositePackagingElement<?> getLastParent() {
+    PackagingElementPath path = this;
+    while (path != EMPTY) {
+      if (path.myLastElement instanceof CompositePackagingElement<?>) {
+        return (CompositePackagingElement)path.myLastElement;
+      }
+      path = path.myParentPath;
+    }
+    return null;
+  }
+
+  @Nullable
+  public Artifact findLastArtifact(PackagingElementResolvingContext context) {
+    PackagingElementPath path = this;
+    while (path != EMPTY) {
+      final PackagingElement<?> element = path.myLastElement;
+      if (element instanceof ArtifactPackagingElement) {
+        return ((ArtifactPackagingElement)element).findArtifact(context);
+      }
+      path = path.myParentPath;
+    }
+    return null;
+  }
+
+  public static PackagingElementPath createPath(@NotNull List<PackagingElement<?>> elements) {
+    PackagingElementPath path = EMPTY;
+    for (PackagingElement<?> element : elements) {
+      path = new PackagingElementPath(path, element);
+    }
+    return path;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/PackagingElementProcessor.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/PackagingElementProcessor.java
new file mode 100644
index 0000000..048217f
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/PackagingElementProcessor.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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.packaging.elements.ComplexPackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public abstract class PackagingElementProcessor<E extends PackagingElement<?>> {
+  public boolean shouldProcessSubstitution(ComplexPackagingElement<?> element) {
+    return true;
+  }
+
+  public boolean shouldProcess(PackagingElement<?> element) {
+    return true;
+  }
+
+  public abstract boolean process(@NotNull E element, @NotNull PackagingElementPath path);
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ParentElementProcessor.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ParentElementProcessor.java
new file mode 100644
index 0000000..6f09d18
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ParentElementProcessor.java
@@ -0,0 +1,33 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class ParentElementProcessor {
+
+  public abstract boolean process(@NotNull CompositePackagingElement<?> element, @NotNull List<Pair<Artifact,CompositePackagingElement<?>>> parents,
+                                  @NotNull Artifact artifact);
+
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/PlainArtifactType.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/PlainArtifactType.java
new file mode 100644
index 0000000..9337db5
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/PlainArtifactType.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.
+ */
+package com.intellij.packaging.impl.artifacts;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElementOutputKind;
+import com.intellij.packaging.impl.elements.ArtifactRootElementImpl;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public class PlainArtifactType extends ArtifactType {
+  @NonNls public static final String ID = "plain";
+
+  public static PlainArtifactType getInstance() {
+    return EP_NAME.findExtension(PlainArtifactType.class);
+  }
+
+  public PlainArtifactType() {
+    super(ID, CompilerBundle.message("artifact.type.plain"));
+  }
+
+  @NotNull
+  @Override
+  public Icon getIcon() {
+    return AllIcons.Nodes.Artifact;
+  }
+
+  @Override
+  public String getDefaultPathFor(@NotNull PackagingElementOutputKind kind) {
+    return "/";
+  }
+
+  @NotNull
+  public CompositePackagingElement<?> createRootElement(@NotNull String artifactName) {
+    return new ArtifactRootElementImpl();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/UnknownPackagingElementTypeException.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/UnknownPackagingElementTypeException.java
new file mode 100644
index 0000000..9856fec
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/UnknownPackagingElementTypeException.java
@@ -0,0 +1,31 @@
+/*
+ * 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 com.intellij.packaging.impl.artifacts;
+
+/**
+ * @author nik
+ */
+class UnknownPackagingElementTypeException extends Exception {
+  private final String myTypeId;
+
+  public UnknownPackagingElementTypeException(String typeId) {
+    myTypeId = typeId;
+  }
+
+  public String getTypeId() {
+    return myTypeId;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactAdditionalCompileScopeProvider.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactAdditionalCompileScopeProvider.java
new file mode 100644
index 0000000..ceb8bb4
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactAdditionalCompileScopeProvider.java
@@ -0,0 +1,49 @@
+/*
+ * 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 com.intellij.packaging.impl.compiler;
+
+import com.intellij.compiler.impl.AdditionalCompileScopeProvider;
+import com.intellij.openapi.application.ReadAction;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.CompilerFilter;
+import com.intellij.openapi.project.Project;
+import com.intellij.packaging.artifacts.Artifact;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class ArtifactAdditionalCompileScopeProvider extends AdditionalCompileScopeProvider {
+  @Override
+  public CompileScope getAdditionalScope(@NotNull final CompileScope baseScope, @NotNull CompilerFilter filter, @NotNull final Project project) {
+    if (ArtifactCompileScope.getArtifacts(baseScope) != null) {
+      return null;
+    }
+    final ArtifactsCompiler compiler = ArtifactsCompiler.getInstance(project);
+    if (compiler == null || !filter.acceptCompiler(compiler)) {
+      return null;
+    }
+    return new ReadAction<CompileScope>() {
+      protected void run(final Result<CompileScope> result) {
+        final Set<Artifact> artifacts = ArtifactCompileScope.getArtifactsToBuild(project, baseScope, false);
+        result.setResult(ArtifactCompileScope.createScopeForModulesInArtifacts(project, artifacts));
+      } 
+    }.execute().getResultObject();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactAwareCompiler.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactAwareCompiler.java
new file mode 100644
index 0000000..1d10459
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactAwareCompiler.java
@@ -0,0 +1,30 @@
+/*
+ * 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 com.intellij.packaging.impl.compiler;
+
+import com.intellij.packaging.artifacts.Artifact;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+
+/**
+ * @author nik
+ */
+public interface ArtifactAwareCompiler {
+
+  boolean shouldRun(@NotNull Collection<? extends Artifact> changedArtifacts);
+
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactBuildTarget.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactBuildTarget.java
new file mode 100644
index 0000000..4a29afc
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactBuildTarget.java
@@ -0,0 +1,41 @@
+/*
+ * 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 com.intellij.packaging.impl.compiler;
+
+import com.intellij.openapi.compiler.generic.BuildTarget;
+import com.intellij.packaging.artifacts.Artifact;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class ArtifactBuildTarget extends BuildTarget {
+  private Artifact myArtifact;
+
+  public ArtifactBuildTarget(Artifact artifact) {
+    myArtifact = artifact;
+  }
+
+  public Artifact getArtifact() {
+    return myArtifact;
+  }
+
+  @NotNull
+  @Override
+  public String getId() {
+    return myArtifact.getName();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactBuildTargetScopeProvider.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactBuildTargetScopeProvider.java
new file mode 100644
index 0000000..c6af259
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactBuildTargetScopeProvider.java
@@ -0,0 +1,70 @@
+/*
+ * 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 com.intellij.packaging.impl.compiler;
+
+import com.intellij.compiler.impl.BuildTargetScopeProvider;
+import com.intellij.compiler.impl.CompileScopeUtil;
+import com.intellij.openapi.application.ReadAction;
+import com.intellij.openapi.application.Result;
+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 com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import org.jetbrains.annotations.NotNull;
+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;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class ArtifactBuildTargetScopeProvider extends BuildTargetScopeProvider {
+  @NotNull
+  @Override
+  public List<TargetTypeBuildScope> getBuildTargetScopes(@NotNull final CompileScope baseScope, @NotNull CompilerFilter filter,
+                                                         @NotNull final Project project) {
+    final ArtifactsCompiler compiler = ArtifactsCompiler.getInstance(project);
+    if (compiler == null || !filter.acceptCompiler(compiler)) {
+      return Collections.emptyList();
+    }
+    final List<TargetTypeBuildScope> scopes = new ArrayList<TargetTypeBuildScope>();
+    new ReadAction() {
+      protected void run(final Result result) {
+        final Set<Artifact> artifacts = ArtifactCompileScope.getArtifactsToBuild(project, baseScope, false);
+        if (ArtifactCompileScope.getArtifacts(baseScope) == null) {
+          Set<Module> modules = ArtifactUtil.getModulesIncludedInArtifacts(artifacts, project);
+          CompileScopeUtil.addScopesForModules(modules, scopes);
+        }
+        if (!artifacts.isEmpty()) {
+          TargetTypeBuildScope.Builder builder =
+            TargetTypeBuildScope.newBuilder().setTypeId(ArtifactBuildTargetType.INSTANCE.getTypeId());
+          for (Artifact artifact : artifacts) {
+            builder.addTargetId(artifact.getName());
+          }
+          scopes.add(builder.build());
+        }
+      }
+    }.execute();
+
+    return scopes;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompileScope.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompileScope.java
new file mode 100644
index 0000000..b434eac
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompileScope.java
@@ -0,0 +1,145 @@
+/*
+ * 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 com.intellij.packaging.impl.compiler;
+
+import com.intellij.compiler.impl.ModuleCompileScope;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactManager;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.impl.elements.ArtifactElementType;
+import com.intellij.packaging.impl.elements.ArtifactPackagingElement;
+import com.intellij.packaging.impl.elements.ModuleOutputPackagingElement;
+import com.intellij.packaging.impl.elements.ProductionModuleOutputElementType;
+import com.intellij.util.Processor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class ArtifactCompileScope {
+  private static final Key<Artifact[]> ARTIFACTS_KEY = Key.create("artifacts");
+  private static final Key<Set<Artifact>> CACHED_ARTIFACTS_KEY = Key.create("cached_artifacts");
+  private static final Key<Key<?>> ARTIFACTS_CONTENT_ID_KEY = Key.create("build_artifacts_task");
+
+  private ArtifactCompileScope() {
+  }
+
+  public static ModuleCompileScope createScopeForModulesInArtifacts(@NotNull Project project, @NotNull Collection<? extends Artifact> artifacts) {
+    final Set<Module> modules = ArtifactUtil.getModulesIncludedInArtifacts(artifacts, project);
+    return new ModuleCompileScope(project, modules.toArray(new Module[modules.size()]), true);
+  }
+
+  public static CompileScope createArtifactsScope(@NotNull Project project, @NotNull Collection<Artifact> artifacts) {
+    return createScopeWithArtifacts(createScopeForModulesInArtifacts(project, artifacts), artifacts, true);
+  }
+
+  public static CompileScope createScopeWithArtifacts(final CompileScope baseScope, @NotNull Collection<Artifact> artifacts, boolean useCustomContentId) {
+    baseScope.putUserData(ARTIFACTS_KEY, artifacts.toArray(new Artifact[artifacts.size()]));
+    if (useCustomContentId) {
+      baseScope.putUserData(CompilerManager.CONTENT_ID_KEY, ARTIFACTS_CONTENT_ID_KEY);
+    }
+    return baseScope;
+  }
+
+  public static Set<Artifact> getArtifactsToBuild(final Project project,
+                                                  final CompileScope compileScope,
+                                                  final boolean addIncludedArtifactsWithOutputPathsOnly) {
+    final Artifact[] artifactsFromScope = getArtifacts(compileScope);
+    final ArtifactManager artifactManager = ArtifactManager.getInstance(project);
+    PackagingElementResolvingContext context = artifactManager.getResolvingContext();
+    if (artifactsFromScope != null) {
+      return addIncludedArtifacts(Arrays.asList(artifactsFromScope), context, addIncludedArtifactsWithOutputPathsOnly);
+    }
+
+    final Set<Artifact> cached = compileScope.getUserData(CACHED_ARTIFACTS_KEY);
+    if (cached != null) {
+      return cached;
+    }
+
+    Set<Artifact> artifacts = new HashSet<Artifact>();
+    final Set<Module> modules = new HashSet<Module>(Arrays.asList(compileScope.getAffectedModules()));
+    final List<Module> allModules = Arrays.asList(ModuleManager.getInstance(project).getModules());
+    for (Artifact artifact : artifactManager.getArtifacts()) {
+      if (artifact.isBuildOnMake()) {
+        if (modules.containsAll(allModules)
+            || containsModuleOutput(artifact, modules, context)) {
+          artifacts.add(artifact);
+        }
+      }
+    }
+    Set<Artifact> result = addIncludedArtifacts(artifacts, context, addIncludedArtifactsWithOutputPathsOnly);
+    compileScope.putUserData(CACHED_ARTIFACTS_KEY, result);
+    return result;
+  }
+
+  @Nullable
+  public static Artifact[] getArtifacts(CompileScope compileScope) {
+    return compileScope.getUserData(ARTIFACTS_KEY);
+  }
+
+  private static boolean containsModuleOutput(Artifact artifact, final Set<Module> modules, final PackagingElementResolvingContext context) {
+    return !ArtifactUtil.processPackagingElements(artifact, ProductionModuleOutputElementType.ELEMENT_TYPE,
+                                                         new Processor<ModuleOutputPackagingElement>() {
+                                                           public boolean process(ModuleOutputPackagingElement moduleOutputPackagingElement) {
+                                                             final Module module = moduleOutputPackagingElement.findModule(context);
+                                                             return module == null || !modules.contains(module);
+                                                           }
+                                                         }, context, true);
+  }
+
+  @NotNull
+  private static Set<Artifact> addIncludedArtifacts(@NotNull Collection<Artifact> artifacts,
+                                                    @NotNull PackagingElementResolvingContext context,
+                                                    final boolean withOutputPathOnly) {
+    Set<Artifact> result = new HashSet<Artifact>();
+    for (Artifact artifact : artifacts) {
+      collectIncludedArtifacts(artifact, context, new HashSet<Artifact>(), result, withOutputPathOnly);
+    }
+    return result;
+  }
+
+  private static void collectIncludedArtifacts(Artifact artifact, final PackagingElementResolvingContext context,
+                                               final Set<Artifact> processed, final Set<Artifact> result, final boolean withOutputPathOnly) {
+    if (!processed.add(artifact)) {
+      return;
+    }
+    if (!withOutputPathOnly || !StringUtil.isEmpty(artifact.getOutputPath())) {
+      result.add(artifact);
+    }
+
+    ArtifactUtil.processPackagingElements(artifact, ArtifactElementType.ARTIFACT_ELEMENT_TYPE, new Processor<ArtifactPackagingElement>() {
+        @Override
+        public boolean process(ArtifactPackagingElement element) {
+          Artifact included = element.findArtifact(context);
+          if (included != null) {
+            collectIncludedArtifacts(included, context, processed, result, withOutputPathOnly);
+          }
+          return true;
+        }
+      }, context, false);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompilerCompileItem.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompilerCompileItem.java
new file mode 100644
index 0000000..b4e1865
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompilerCompileItem.java
@@ -0,0 +1,86 @@
+/*
+ * 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 com.intellij.packaging.impl.compiler;
+
+import com.intellij.openapi.compiler.generic.VirtualFileCompileItem;
+import com.intellij.compiler.impl.packagingCompiler.DestinationInfo;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.SmartList;
+import com.intellij.util.io.DataExternalizer;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ArtifactCompilerCompileItem extends VirtualFileCompileItem<ArtifactPackagingItemOutputState> {
+  public static final DataExternalizer<ArtifactPackagingItemOutputState> OUTPUT_EXTERNALIZER = new ArtifactPackagingItemExternalizer();
+  private final List<DestinationInfo> myDestinations = new SmartList<DestinationInfo>();
+
+  public ArtifactCompilerCompileItem(VirtualFile file) {
+    super(file);
+  }
+
+  public void addDestination(DestinationInfo info) {
+    myDestinations.add(info);
+  }
+
+  public List<DestinationInfo> getDestinations() {
+    return myDestinations;
+  }
+
+  @NotNull
+  @Override
+  public ArtifactPackagingItemOutputState computeOutputState() {
+    final SmartList<Pair<String, Long>> pairs = new SmartList<Pair<String, Long>>();
+    for (DestinationInfo destination : myDestinations) {
+      destination.update();
+      final VirtualFile outputFile = destination.getOutputFile();
+      long timestamp = outputFile != null ? outputFile.getTimeStamp() : -1;
+      pairs.add(Pair.create(destination.getOutputPath(), timestamp));
+    }
+    return new ArtifactPackagingItemOutputState(pairs);
+  }
+
+  @Override
+  public boolean isOutputUpToDate(@NotNull ArtifactPackagingItemOutputState state) {
+    final SmartList<Pair<String, Long>> cachedDestinations = state.myDestinations;
+    if (cachedDestinations.size() != myDestinations.size()) {
+      return false;
+    }
+
+    for (DestinationInfo info : myDestinations) {
+      final VirtualFile outputFile = info.getOutputFile();
+      long timestamp = outputFile != null ? outputFile.getTimeStamp() : -1;
+      final String path = info.getOutputPath();
+      boolean found = false;
+      //todo[nik] use map if list contains many items
+      for (Pair<String, Long> cachedDestination : cachedDestinations) {
+        if (cachedDestination.first.equals(path)) {
+          if (cachedDestination.second != timestamp) return false;
+          found = true;
+          break;
+        }
+      }
+      if (!found) return false;
+    }
+
+    return true;
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompilerUtil.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompilerUtil.java
new file mode 100644
index 0000000..0de7e05
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactCompilerUtil.java
@@ -0,0 +1,177 @@
+/*
+ * 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 com.intellij.packaging.impl.compiler;
+
+import com.intellij.facet.Facet;
+import com.intellij.facet.FacetManager;
+import com.intellij.facet.FacetRootsProvider;
+import com.intellij.openapi.application.ReadAction;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactManager;
+import com.intellij.packaging.elements.ComplexPackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.impl.artifacts.PackagingElementPath;
+import com.intellij.packaging.impl.artifacts.PackagingElementProcessor;
+import com.intellij.packaging.impl.elements.ArtifactPackagingElement;
+import com.intellij.packaging.impl.elements.FileOrDirectoryCopyPackagingElement;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.MultiMap;
+import gnu.trove.THashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage.TargetTypeBuildScope;
+import org.jetbrains.jps.incremental.artifacts.ArtifactBuildTargetType;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * @author nik
+ */
+public class ArtifactCompilerUtil {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.packaging.impl.compiler.ArtifactCompilerUtil");
+
+  private ArtifactCompilerUtil() {
+  }
+
+  @Nullable
+  public static BufferedInputStream getJarEntryInputStream(VirtualFile sourceFile, final CompileContext context) throws IOException {
+    final String fullPath = sourceFile.getPath();
+    final int jarEnd = fullPath.indexOf(JarFileSystem.JAR_SEPARATOR);
+    LOG.assertTrue(jarEnd != -1, fullPath);
+    String pathInJar = fullPath.substring(jarEnd + JarFileSystem.JAR_SEPARATOR.length());
+    String jarPath = fullPath.substring(0, jarEnd);
+    final ZipFile jarFile = new ZipFile(new File(FileUtil.toSystemDependentName(jarPath)));
+    final ZipEntry entry = jarFile.getEntry(pathInJar);
+    if (entry == null) {
+      context.addMessage(CompilerMessageCategory.ERROR, "Cannot extract '" + pathInJar + "' from '" + jarFile.getName() + "': entry not found", null, -1, -1);
+      return null;
+    }
+
+    return new BufferedInputStream(jarFile.getInputStream(entry)) {
+      @Override
+      public void close() throws IOException {
+        super.close();
+        jarFile.close();
+      }
+    };
+  }
+
+  public static File getJarFile(VirtualFile jarEntry) {
+    String fullPath = jarEntry.getPath();
+    return new File(FileUtil.toSystemDependentName(fullPath.substring(fullPath.indexOf(JarFileSystem.JAR_SEPARATOR))));
+  }
+
+
+  @NotNull
+  public static Set<VirtualFile> getArtifactOutputsContainingSourceFiles(final @NotNull Project project) {
+    final List<VirtualFile> allOutputs = new ArrayList<VirtualFile>();
+    for (Artifact artifact : ArtifactManager.getInstance(project).getArtifacts()) {
+      ContainerUtil.addIfNotNull(artifact.getOutputFile(), allOutputs);
+    }
+
+    final Set<VirtualFile> roots = new HashSet<VirtualFile>();
+    final PackagingElementResolvingContext context = ArtifactManager.getInstance(project).getResolvingContext();
+    for (Artifact artifact : ArtifactManager.getInstance(project).getArtifacts()) {
+      ArtifactUtil.processPackagingElements(artifact, null, new PackagingElementProcessor<PackagingElement<?>>() {
+        @Override
+        public boolean shouldProcessSubstitution(ComplexPackagingElement<?> element) {
+          return !(element instanceof ArtifactPackagingElement);
+        }
+
+        @Override
+        public boolean process(@NotNull PackagingElement<?> element, @NotNull PackagingElementPath path) {
+          if (element instanceof FileOrDirectoryCopyPackagingElement<?>) {
+            final VirtualFile file = ((FileOrDirectoryCopyPackagingElement)element).findFile();
+            if (file != null) {
+              roots.add(file);
+            }
+          }
+          return true;
+        }
+      }, context, true);
+    }
+
+    final Module[] modules = ModuleManager.getInstance(project).getModules();
+    for (Module module : modules) {
+      final Facet[] facets = FacetManager.getInstance(module).getAllFacets();
+      for (Facet facet : facets) {
+        if (facet instanceof FacetRootsProvider) {
+          roots.addAll(((FacetRootsProvider)facet).getFacetRoots());
+        }
+      }
+    }
+
+    final Set<VirtualFile> affectedOutputPaths = new HashSet<VirtualFile>();
+    for (VirtualFile output : allOutputs) {
+      for (VirtualFile root : roots) {
+        if (VfsUtilCore.isAncestor(output, root, false)) {
+          affectedOutputPaths.add(output);
+        }
+      }
+    }
+    return affectedOutputPaths;
+  }
+
+  public static boolean containsArtifacts(List<TargetTypeBuildScope> scopes) {
+    for (TargetTypeBuildScope scope : scopes) {
+      if (ArtifactBuildTargetType.INSTANCE.getTypeId().equals(scope.getTypeId())) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public static MultiMap<String, Artifact> createOutputToArtifactMap(final Project project) {
+    final MultiMap<String, Artifact> result = new MultiMap<String, Artifact>() {
+      @Override
+      protected Map<String, Collection<Artifact>> createMap() {
+        return new THashMap<String, Collection<Artifact>>(FileUtil.PATH_HASHING_STRATEGY);
+      }
+    };
+    new ReadAction() {
+      protected void run(final Result r) {
+        for (Artifact artifact : ArtifactManager.getInstance(project).getArtifacts()) {
+          String outputPath = artifact.getOutputFilePath();
+          if (!StringUtil.isEmpty(outputPath)) {
+            result.putValue(outputPath, artifact);
+          }
+        }
+      }
+    }.execute();
+
+
+    return result;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactPackagingItemExternalizer.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactPackagingItemExternalizer.java
new file mode 100644
index 0000000..030e948
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactPackagingItemExternalizer.java
@@ -0,0 +1,53 @@
+/*
+ * 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 com.intellij.packaging.impl.compiler;
+
+import com.intellij.openapi.util.Pair;
+import com.intellij.util.SmartList;
+import com.intellij.util.io.DataExternalizer;
+import com.intellij.util.io.IOUtil;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+/**
+* @author nik
+*/
+public class ArtifactPackagingItemExternalizer implements DataExternalizer<ArtifactPackagingItemOutputState> {
+  private byte[] myBuffer = IOUtil.allocReadWriteUTFBuffer();
+
+  @Override
+  public void save(DataOutput out, ArtifactPackagingItemOutputState value) throws IOException {
+    out.writeInt(value.myDestinations.size());
+    for (Pair<String, Long> pair : value.myDestinations) {
+      IOUtil.writeUTFFast(myBuffer, out, pair.getFirst());
+      out.writeLong(pair.getSecond());
+    }
+  }
+
+  @Override
+  public ArtifactPackagingItemOutputState read(DataInput in) throws IOException {
+    int size = in.readInt();
+    SmartList<Pair<String, Long>> destinations = new SmartList<Pair<String, Long>>();
+    while (size-- > 0) {
+      String path = IOUtil.readUTFFast(myBuffer, in);
+      long outputTimestamp = in.readLong();
+      destinations.add(Pair.create(path, outputTimestamp));
+    }
+    return new ArtifactPackagingItemOutputState(destinations);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactPackagingItemOutputState.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactPackagingItemOutputState.java
new file mode 100644
index 0000000..29ac0ae
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactPackagingItemOutputState.java
@@ -0,0 +1,30 @@
+/*
+ * 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 com.intellij.packaging.impl.compiler;
+
+import com.intellij.openapi.util.Pair;
+import com.intellij.util.SmartList;
+
+/**
+* @author nik
+*/
+public class ArtifactPackagingItemOutputState {
+  public final SmartList<Pair<String, Long>> myDestinations;
+
+  public ArtifactPackagingItemOutputState(SmartList<Pair<String, Long>> destinations) {
+    myDestinations = destinations;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsCompiler.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsCompiler.java
new file mode 100644
index 0000000..9875816
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsCompiler.java
@@ -0,0 +1,109 @@
+/*
+ * 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 com.intellij.packaging.impl.compiler;
+
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.compiler.generic.CompileItem;
+import com.intellij.openapi.compiler.generic.GenericCompiler;
+import com.intellij.openapi.compiler.generic.GenericCompilerInstance;
+import com.intellij.openapi.compiler.generic.VirtualFilePersistentState;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.util.io.DataExternalizer;
+import com.intellij.util.io.KeyDescriptor;
+import gnu.trove.THashSet;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class ArtifactsCompiler extends GenericCompiler<String, VirtualFilePersistentState, ArtifactPackagingItemOutputState> {
+  private static final Key<Set<String>> WRITTEN_PATHS_KEY = Key.create("artifacts_written_paths");
+  private static final Key<Set<Artifact>> CHANGED_ARTIFACTS = Key.create("affected_artifacts");
+
+  public ArtifactsCompiler() {
+    super("artifacts_compiler", 0, GenericCompiler.CompileOrderPlace.PACKAGING);
+  }
+
+  @Nullable
+  public static ArtifactsCompiler getInstance(@NotNull Project project) {
+    final ArtifactsCompiler[] compilers = CompilerManager.getInstance(project).getCompilers(ArtifactsCompiler.class);
+    return compilers.length == 1 ? compilers[0] : null;
+  }
+
+  public static void addChangedArtifact(final CompileContext context, Artifact artifact) {
+    Set<Artifact> artifacts = context.getUserData(CHANGED_ARTIFACTS);
+    if (artifacts == null) {
+      artifacts = new THashSet<Artifact>();
+      context.putUserData(CHANGED_ARTIFACTS, artifacts);
+    }
+    artifacts.add(artifact);
+  }
+
+  public static void addWrittenPaths(final CompileContext context, Set<String> writtenPaths) {
+    Set<String> paths = context.getUserData(WRITTEN_PATHS_KEY);
+    if (paths == null) {
+      paths = new THashSet<String>();
+      context.putUserData(WRITTEN_PATHS_KEY, paths);
+    }
+    paths.addAll(writtenPaths);
+  }
+
+  @NotNull
+  @Override
+  public KeyDescriptor<String> getItemKeyDescriptor() {
+    return STRING_KEY_DESCRIPTOR;
+  }
+
+  @NotNull
+  @Override
+  public DataExternalizer<VirtualFilePersistentState> getSourceStateExternalizer() {
+    return VirtualFilePersistentState.EXTERNALIZER;
+  }
+
+  @NotNull
+  @Override
+  public DataExternalizer<ArtifactPackagingItemOutputState> getOutputStateExternalizer() {
+    return new ArtifactPackagingItemExternalizer();
+  }
+
+  @NotNull
+  @Override
+  public GenericCompilerInstance<ArtifactBuildTarget, ? extends CompileItem<String, VirtualFilePersistentState, ArtifactPackagingItemOutputState>, String, VirtualFilePersistentState, ArtifactPackagingItemOutputState> createInstance(
+    @NotNull CompileContext context) {
+    return new ArtifactsCompilerInstance(context);
+  }
+
+  @NotNull
+  public String getDescription() {
+    return "Artifacts Packaging Compiler";
+  }
+
+  @Nullable
+  public static Set<Artifact> getChangedArtifacts(final CompileContext compileContext) {
+    return compileContext.getUserData(CHANGED_ARTIFACTS);
+  }
+
+  @Nullable
+  public static Set<String> getWrittenPaths(@NotNull CompileContext context) {
+    return context.getUserData(WRITTEN_PATHS_KEY);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsCompilerInstance.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsCompilerInstance.java
new file mode 100644
index 0000000..1edb774
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsCompilerInstance.java
@@ -0,0 +1,425 @@
+/*
+ * 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 com.intellij.packaging.impl.compiler;
+
+import com.intellij.compiler.CompilerManagerImpl;
+import com.intellij.compiler.impl.CompilerUtil;
+import com.intellij.compiler.impl.packagingCompiler.*;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ReadAction;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.compiler.generic.GenericCompilerCacheState;
+import com.intellij.openapi.compiler.generic.GenericCompilerInstance;
+import com.intellij.openapi.compiler.generic.GenericCompilerProcessingItem;
+import com.intellij.openapi.compiler.generic.VirtualFilePersistentState;
+import com.intellij.openapi.compiler.make.BuildParticipant;
+import com.intellij.openapi.compiler.make.BuildParticipantProvider;
+import com.intellij.openapi.deployment.DeploymentUtil;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.project.DumbService;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.*;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactManager;
+import com.intellij.packaging.artifacts.ArtifactProperties;
+import com.intellij.packaging.artifacts.ArtifactPropertiesProvider;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.impl.artifacts.ArtifactSortingUtil;
+import com.intellij.util.ThrowableRunnable;
+import com.intellij.util.containers.ContainerUtil;
+import gnu.trove.THashSet;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public class ArtifactsCompilerInstance extends GenericCompilerInstance<ArtifactBuildTarget, ArtifactCompilerCompileItem,
+  String, VirtualFilePersistentState, ArtifactPackagingItemOutputState> {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.packaging.impl.compiler.ArtifactsCompilerInstance");
+  public static final Logger FULL_LOG = Logger.getInstance("#com.intellij.full-artifacts-compiler-log");
+  private ArtifactsProcessingItemsBuilderContext myBuilderContext;
+
+  public ArtifactsCompilerInstance(CompileContext context) {
+    super(context);
+  }
+
+  @NotNull
+  @Override
+  public List<ArtifactBuildTarget> getAllTargets() {
+    return getArtifactTargets(false);
+  }
+
+  @NotNull
+  @Override
+  public List<ArtifactBuildTarget> getSelectedTargets() {
+    return getArtifactTargets(true);
+  }
+
+  private List<ArtifactBuildTarget> getArtifactTargets(final boolean selectedOnly) {
+    final List<ArtifactBuildTarget> targets = new ArrayList<ArtifactBuildTarget>();
+    new ReadAction() {
+      protected void run(final Result result) {
+        final Set<Artifact> artifacts;
+        if (selectedOnly) {
+          artifacts = ArtifactCompileScope.getArtifactsToBuild(getProject(), myContext.getCompileScope(), true);
+        }
+        else {
+          artifacts = new HashSet<Artifact>(Arrays.asList(ArtifactManager.getInstance(getProject()).getArtifacts()));
+        }
+
+        Map<String, Artifact> artifactsMap = new HashMap<String, Artifact>();
+        for (Artifact artifact : artifacts) {
+          artifactsMap.put(artifact.getName(), artifact);
+        }
+        for (String name : ArtifactSortingUtil.getInstance(getProject()).getArtifactsSortedByInclusion()) {
+          Artifact artifact = artifactsMap.get(name);
+          if (artifact != null) {
+            targets.add(new ArtifactBuildTarget(artifact));
+          }
+        }
+
+        for (BuildParticipantProvider provider : BuildParticipantProvider.EXTENSION_POINT_NAME.getExtensions()) {
+          for (Module module : ModuleManager.getInstance(getProject()).getModules()) {
+            final Collection<? extends BuildParticipant> participants = provider.getParticipants(module);
+            for (BuildParticipant participant : participants) {
+              Artifact artifact = participant.createArtifact(myContext);
+              if (artifact != null) {
+                LOG.debug("additional artifact to build: " + artifact);
+                targets.add(new ArtifactBuildTarget(artifact));
+              }
+            }
+          }
+        }
+      }
+    }.execute();
+    return targets;
+  }
+
+  @Override
+  public void processObsoleteTarget(@NotNull String targetId,
+                                    @NotNull List<GenericCompilerCacheState<String, VirtualFilePersistentState, ArtifactPackagingItemOutputState>> obsoleteItems) {
+    deleteFiles(obsoleteItems, Collections.<GenericCompilerProcessingItem<ArtifactCompilerCompileItem, VirtualFilePersistentState, ArtifactPackagingItemOutputState>>emptyList());
+  }
+
+  @NotNull
+  @Override
+  public List<ArtifactCompilerCompileItem> getItems(@NotNull ArtifactBuildTarget target) {
+    myBuilderContext = new ArtifactsProcessingItemsBuilderContext(myContext);
+    final Artifact artifact = target.getArtifact();
+
+    final Map<String, String> selfIncludingArtifacts = new ReadAction<Map<String, String>>() {
+      protected void run(final Result<Map<String, String>> result) {
+        result.setResult(ArtifactSortingUtil.getInstance(getProject()).getArtifactToSelfIncludingNameMap());
+      }
+    }.execute().getResultObject();
+    final String selfIncludingName = selfIncludingArtifacts.get(artifact.getName());
+    if (selfIncludingName != null) {
+      String name = selfIncludingName.equals(artifact.getName()) ? "it" : "'" + selfIncludingName + "' artifact";
+      myContext.addMessage(CompilerMessageCategory.ERROR, "Cannot build '" + artifact.getName() + "' artifact: " + name + " includes itself in the output layout", null, -1, -1);
+      return Collections.emptyList();
+    }
+
+    final String outputPath = artifact.getOutputPath();
+    if (outputPath == null || outputPath.length() == 0) {
+      myContext.addMessage(CompilerMessageCategory.ERROR, "Cannot build '" + artifact.getName() + "' artifact: output path is not specified",
+                      null, -1, -1);
+      return Collections.emptyList();
+    }
+
+    DumbService.getInstance(getProject()).waitForSmartMode();
+    new ReadAction() {
+      protected void run(final Result result) {
+        collectItems(artifact, outputPath);
+      }
+    }.execute();
+    return new ArrayList<ArtifactCompilerCompileItem>(myBuilderContext.getProcessingItems());
+  }
+
+  private void collectItems(@NotNull Artifact artifact, @NotNull String outputPath) {
+    final CompositePackagingElement<?> rootElement = artifact.getRootElement();
+    final VirtualFile outputFile = LocalFileSystem.getInstance().findFileByPath(outputPath);
+    final CopyToDirectoryInstructionCreator instructionCreator = new CopyToDirectoryInstructionCreator(myBuilderContext, outputPath, outputFile);
+    final PackagingElementResolvingContext resolvingContext = ArtifactManager.getInstance(getProject()).getResolvingContext();
+    FULL_LOG.debug("Collecting items for " + artifact.getName());
+    rootElement.computeIncrementalCompilerInstructions(instructionCreator, resolvingContext, myBuilderContext, artifact.getArtifactType());
+  }
+
+  private boolean doBuild(@NotNull Artifact artifact,
+                          final List<GenericCompilerProcessingItem<ArtifactCompilerCompileItem, VirtualFilePersistentState, ArtifactPackagingItemOutputState>> changedItems,
+                          final Set<ArtifactCompilerCompileItem> processedItems,
+                          final @NotNull Set<String> writtenPaths,
+                          final Set<String> deletedJars) {
+    FULL_LOG.debug("Building " + artifact.getName());
+    final boolean testMode = ApplicationManager.getApplication().isUnitTestMode();
+
+    final DeploymentUtil deploymentUtil = DeploymentUtil.getInstance();
+    final FileFilter fileFilter = new IgnoredFileFilter();
+    final Set<JarInfo> changedJars = new THashSet<JarInfo>();
+    for (String deletedJar : deletedJars) {
+      ContainerUtil.addIfNotNull(myBuilderContext.getJarInfo(deletedJar), changedJars);
+    }
+
+    try {
+      onBuildStartedOrFinished(artifact, false);
+      if (myContext.getMessageCount(CompilerMessageCategory.ERROR) > 0) {
+        return false;
+      }
+
+      int i = 0;
+      for (final GenericCompilerProcessingItem<ArtifactCompilerCompileItem, VirtualFilePersistentState, ArtifactPackagingItemOutputState> item : changedItems) {
+        final ArtifactCompilerCompileItem sourceItem = item.getItem();
+        myContext.getProgressIndicator().checkCanceled();
+
+        final Ref<IOException> exception = Ref.create(null);
+        new ReadAction() {
+          protected void run(final Result result) {
+            final VirtualFile sourceFile = sourceItem.getFile();
+            for (DestinationInfo destination : sourceItem.getDestinations()) {
+              if (destination instanceof ExplodedDestinationInfo) {
+                final ExplodedDestinationInfo explodedDestination = (ExplodedDestinationInfo)destination;
+                File toFile = new File(FileUtil.toSystemDependentName(explodedDestination.getOutputPath()));
+                try {
+                  if (sourceFile.isInLocalFileSystem()) {
+                    final File ioFromFile = VfsUtilCore.virtualToIoFile(sourceFile);
+                    if (ioFromFile.exists()) {
+                      deploymentUtil.copyFile(ioFromFile, toFile, myContext, writtenPaths, fileFilter);
+                    }
+                    else {
+                      LOG.debug("Cannot copy " + ioFromFile.getAbsolutePath() + ": file doesn't exist");
+                    }
+                  }
+                  else {
+                    extractFile(sourceFile, toFile, writtenPaths, fileFilter);
+                  }
+                }
+                catch (IOException e) {
+                  exception.set(e);
+                  return;
+                }
+              }
+              else {
+                changedJars.add(((JarDestinationInfo)destination).getJarInfo());
+              }
+            }
+          }
+        }.execute();
+        if (exception.get() != null) {
+          throw exception.get();
+        }
+
+        myContext.getProgressIndicator().setFraction(++i * 1.0 / changedItems.size());
+        processedItems.add(sourceItem);
+        if (testMode) {
+          CompilerManagerImpl.addRecompiledPath(FileUtil.toSystemDependentName(sourceItem.getFile().getPath()));
+        }
+      }
+
+      JarsBuilder builder = new JarsBuilder(changedJars, fileFilter, myContext);
+      final boolean processed = builder.buildJars(writtenPaths);
+      if (!processed) {
+        return false;
+      }
+
+      Set<VirtualFile> recompiledSources = new HashSet<VirtualFile>();
+      for (JarInfo info : builder.getJarsToBuild()) {
+        for (Pair<String, VirtualFile> pair : info.getPackedFiles()) {
+          recompiledSources.add(pair.getSecond());
+        }
+      }
+      for (VirtualFile source : recompiledSources) {
+        ArtifactCompilerCompileItem item = myBuilderContext.getItemBySource(source);
+        LOG.assertTrue(item != null, source);
+        processedItems.add(item);
+        if (testMode) {
+          CompilerManagerImpl.addRecompiledPath(FileUtil.toSystemDependentName(item.getFile().getPath()));
+        }
+      }
+
+      onBuildStartedOrFinished(artifact, true);
+    }
+    catch (ProcessCanceledException e) {
+      throw e;
+    }
+    catch (Exception e) {
+      LOG.info(e);
+      myContext.addMessage(CompilerMessageCategory.ERROR, e.getLocalizedMessage(), null, -1, -1);
+      return false;
+    }
+    return true;
+  }
+
+  private void extractFile(VirtualFile sourceFile, File toFile, Set<String> writtenPaths, FileFilter fileFilter) throws IOException {
+    if (!writtenPaths.add(toFile.getPath())) {
+      return;
+    }
+
+    if (!FileUtil.createParentDirs(toFile)) {
+      myContext.addMessage(CompilerMessageCategory.ERROR, "Cannot create directory for '" + toFile.getAbsolutePath() + "' file", null, -1, -1);
+      return;
+    }
+
+    final BufferedInputStream input = ArtifactCompilerUtil.getJarEntryInputStream(sourceFile, myContext);
+    if (input == null) return;
+    final BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(toFile));
+    try {
+      FileUtil.copy(input, output);
+    }
+    finally {
+      input.close();
+      output.close();
+    }
+  }
+
+  private void onBuildStartedOrFinished(@NotNull Artifact artifact, final boolean finished) throws Exception {
+    for (ArtifactPropertiesProvider provider : artifact.getPropertiesProviders()) {
+      final ArtifactProperties<?> properties = artifact.getProperties(provider);
+      if (finished) {
+        properties.onBuildFinished(artifact, myContext);
+      }
+      else {
+        properties.onBuildStarted(artifact, myContext);
+      }
+    }
+  }
+
+  private static THashSet<String> createPathsHashSet() {
+    return new THashSet<String>(FileUtil.PATH_HASHING_STRATEGY);
+  }
+
+  @Override
+  public void processItems(@NotNull final ArtifactBuildTarget target,
+                           @NotNull final List<GenericCompilerProcessingItem<ArtifactCompilerCompileItem,VirtualFilePersistentState,ArtifactPackagingItemOutputState>> changedItems,
+                           @NotNull List<GenericCompilerCacheState<String, VirtualFilePersistentState, ArtifactPackagingItemOutputState>> obsoleteItems,
+                           @NotNull OutputConsumer<ArtifactCompilerCompileItem> consumer) {
+
+    final THashSet<String> deletedJars = deleteFiles(obsoleteItems, changedItems);
+
+    final Set<String> writtenPaths = createPathsHashSet();
+    final Ref<Boolean> built = Ref.create(false);
+    final Set<ArtifactCompilerCompileItem> processedItems = new HashSet<ArtifactCompilerCompileItem>();
+    CompilerUtil.runInContext(myContext, "Copying files", new ThrowableRunnable<RuntimeException>() {
+      public void run() throws RuntimeException {
+        built.set(doBuild(target.getArtifact(), changedItems, processedItems, writtenPaths, deletedJars));
+      }
+    });
+    if (!built.get()) {
+      return;
+    }
+
+    myContext.getProgressIndicator().setText(CompilerBundle.message("packaging.compiler.message.updating.caches"));
+    myContext.getProgressIndicator().setText2("");
+    for (String path : writtenPaths) {
+      consumer.addFileToRefresh(new File(path));
+    }
+    for (ArtifactCompilerCompileItem item : processedItems) {
+      consumer.addProcessedItem(item);
+    }
+    ArtifactsCompiler.addWrittenPaths(myContext, writtenPaths);
+    ArtifactsCompiler.addChangedArtifact(myContext, target.getArtifact());
+  }
+
+  private THashSet<String> deleteFiles(List<GenericCompilerCacheState<String, VirtualFilePersistentState, ArtifactPackagingItemOutputState>> obsoleteItems,
+                                       List<GenericCompilerProcessingItem<ArtifactCompilerCompileItem, VirtualFilePersistentState, ArtifactPackagingItemOutputState>> changedItems) {
+    myContext.getProgressIndicator().setText(CompilerBundle.message("packaging.compiler.message.deleting.outdated.files"));
+
+    final boolean testMode = ApplicationManager.getApplication().isUnitTestMode();
+    final THashSet<String> deletedJars = new THashSet<String>();
+    final THashSet<String> notDeletedJars = new THashSet<String>();
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Deleting outdated files...");
+    }
+
+    Set<String> pathToDelete = new THashSet<String>();
+    for (GenericCompilerProcessingItem<ArtifactCompilerCompileItem, VirtualFilePersistentState, ArtifactPackagingItemOutputState> item : changedItems) {
+      final ArtifactPackagingItemOutputState cached = item.getCachedOutputState();
+      if (cached != null) {
+        for (Pair<String, Long> destination : cached.myDestinations) {
+          pathToDelete.add(destination.getFirst());
+        }
+      }
+    }
+    for (GenericCompilerProcessingItem<ArtifactCompilerCompileItem, VirtualFilePersistentState, ArtifactPackagingItemOutputState> item : changedItems) {
+      for (DestinationInfo destination : item.getItem().getDestinations()) {
+        pathToDelete.remove(destination.getOutputPath());
+      }
+    }
+    for (GenericCompilerCacheState<String, VirtualFilePersistentState, ArtifactPackagingItemOutputState> item : obsoleteItems) {
+      for (Pair<String, Long> destination : item.getOutputState().myDestinations) {
+        pathToDelete.add(destination.getFirst());
+      }
+    }
+
+    int notDeletedFilesCount = 0;
+    List<File> filesToRefresh = new ArrayList<File>();
+
+    for (String fullPath : pathToDelete) {
+      int end = fullPath.indexOf(JarFileSystem.JAR_SEPARATOR);
+      boolean isJar = end != -1;
+      String filePath = isJar ? fullPath.substring(0, end) : fullPath;
+      boolean deleted = false;
+      if (isJar) {
+      if (notDeletedJars.contains(filePath)) {
+        continue;
+      }
+      deleted = deletedJars.contains(filePath);
+    }
+
+      File file = new File(FileUtil.toSystemDependentName(filePath));
+      if (!deleted) {
+      filesToRefresh.add(file);
+      deleted = FileUtil.delete(file);
+    }
+
+      if (deleted) {
+      if (isJar) {
+        deletedJars.add(filePath);
+      }
+      if (testMode) {
+        CompilerManagerImpl.addDeletedPath(file.getAbsolutePath());
+      }
+    }
+    else {
+      if (isJar) {
+        notDeletedJars.add(filePath);
+      }
+      if (notDeletedFilesCount++ > 50) {
+        myContext.addMessage(CompilerMessageCategory.WARNING, "Deletion of outdated files stopped because too many files cannot be deleted", null, -1, -1);
+        break;
+      }
+      myContext.addMessage(CompilerMessageCategory.WARNING, "Cannot delete file '" + filePath + "'", null, -1, -1);
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("Cannot delete file " + file);
+      }
+    }
+    }
+
+    CompilerUtil.refreshIOFiles(filesToRefresh);
+    return deletedJars;
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsProcessingItemsBuilderContext.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsProcessingItemsBuilderContext.java
new file mode 100644
index 0000000..a96663c
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsProcessingItemsBuilderContext.java
@@ -0,0 +1,112 @@
+/*
+ * 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 com.intellij.packaging.impl.compiler;
+
+import com.intellij.compiler.impl.packagingCompiler.DestinationInfo;
+import com.intellij.compiler.impl.packagingCompiler.ExplodedDestinationInfo;
+import com.intellij.compiler.impl.packagingCompiler.JarInfo;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.elements.ArtifactIncrementalCompilerContext;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author nik
+ */
+public class ArtifactsProcessingItemsBuilderContext implements ArtifactIncrementalCompilerContext {
+  protected final Map<VirtualFile, ArtifactCompilerCompileItem> myItemsBySource;
+  private final Map<String, VirtualFile> mySourceByOutput;
+  private final Map<String, JarInfo> myJarByPath;
+  private final CompileContext myCompileContext;
+  private final boolean myPrintToLog;
+
+  public ArtifactsProcessingItemsBuilderContext(CompileContext compileContext) {
+    myCompileContext = compileContext;
+    myItemsBySource = new HashMap<VirtualFile, ArtifactCompilerCompileItem>();
+    mySourceByOutput = new HashMap<String, VirtualFile>();
+    myJarByPath = new HashMap<String, JarInfo>();
+    myPrintToLog = ArtifactsCompilerInstance.FULL_LOG.isDebugEnabled();
+  }
+
+  public boolean addDestination(@NotNull VirtualFile sourceFile, @NotNull DestinationInfo destinationInfo) {
+    if (destinationInfo instanceof ExplodedDestinationInfo && sourceFile.equals(destinationInfo.getOutputFile())) {
+      return false;
+    }
+
+    if (checkOutputPath(destinationInfo.getOutputPath(), sourceFile)) {
+      if (myPrintToLog) {
+        ArtifactsCompilerInstance.FULL_LOG.debug("  " + sourceFile.getPath() + " -> " + destinationInfo);
+      }
+      getOrCreateProcessingItem(sourceFile).addDestination(destinationInfo);
+      return true;
+    }
+    return false;
+  }
+
+  public Collection<ArtifactCompilerCompileItem> getProcessingItems() {
+    return myItemsBySource.values();
+  }
+
+  public boolean checkOutputPath(final String outputPath, final VirtualFile sourceFile) {
+    VirtualFile old = mySourceByOutput.get(outputPath);
+    if (old == null) {
+      mySourceByOutput.put(outputPath, sourceFile);
+      return true;
+    }
+    //todo[nik] show warning?
+    return false;
+  }
+
+  public ArtifactCompilerCompileItem getItemBySource(VirtualFile source) {
+    return myItemsBySource.get(source);
+  }
+
+  public boolean registerJarFile(@NotNull JarInfo jarInfo, @NotNull String outputPath) {
+    if (mySourceByOutput.containsKey(outputPath) || myJarByPath.containsKey(outputPath)) {
+      return false;
+    }
+    myJarByPath.put(outputPath, jarInfo);
+    return true;
+  }
+
+  @Nullable
+  public JarInfo getJarInfo(String outputPath) {
+    return myJarByPath.get(outputPath);
+  }
+
+  @Nullable
+  public VirtualFile getSourceByOutput(String outputPath) {
+    return mySourceByOutput.get(outputPath);
+  }
+
+  public CompileContext getCompileContext() {
+    return myCompileContext;
+  }
+
+  public ArtifactCompilerCompileItem getOrCreateProcessingItem(VirtualFile sourceFile) {
+    ArtifactCompilerCompileItem item = myItemsBySource.get(sourceFile);
+    if (item == null) {
+      item = new ArtifactCompilerCompileItem(sourceFile);
+      myItemsBySource.put(sourceFile, item);
+    }
+    return item;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsWorkspaceSettings.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsWorkspaceSettings.java
new file mode 100644
index 0000000..a25f4d0
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactsWorkspaceSettings.java
@@ -0,0 +1,83 @@
+/*
+ * 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 com.intellij.packaging.impl.compiler;
+
+import com.intellij.openapi.components.*;
+import com.intellij.openapi.components.StoragePathMacros;
+import com.intellij.openapi.project.Project;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactManager;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.xmlb.annotations.AbstractCollection;
+import com.intellij.util.xmlb.annotations.Tag;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+@State(name = "ArtifactsWorkspaceSettings",
+  storages = {
+    @Storage( file = StoragePathMacros.WORKSPACE_FILE)
+  })
+public class ArtifactsWorkspaceSettings implements PersistentStateComponent<ArtifactsWorkspaceSettings.ArtifactsWorkspaceSettingsState> {
+  private ArtifactsWorkspaceSettingsState myState = new ArtifactsWorkspaceSettingsState();
+  private final Project myProject;
+
+  public ArtifactsWorkspaceSettings(Project project) {
+    myProject = project;
+  }
+
+  public static ArtifactsWorkspaceSettings getInstance(@NotNull Project project) {
+    return ServiceManager.getService(project, ArtifactsWorkspaceSettings.class);
+  }
+
+  public List<Artifact> getArtifactsToBuild() {
+    final List<Artifact> result = new ArrayList<Artifact>();
+    final ArtifactManager artifactManager = ArtifactManager.getInstance(myProject);
+    for (String name : myState.myArtifactsToBuild) {
+      ContainerUtil.addIfNotNull(artifactManager.findArtifact(name), result);
+    }
+    return result;
+  }
+
+  public void setArtifactsToBuild(@NotNull Collection<? extends Artifact> artifacts) {
+    myState.myArtifactsToBuild.clear();
+    for (Artifact artifact : artifacts) {
+      myState.myArtifactsToBuild.add(artifact.getName());
+    }
+    Collections.sort(myState.myArtifactsToBuild);
+  }
+
+  public ArtifactsWorkspaceSettingsState getState() {
+    return myState;
+  }
+
+  public void loadState(ArtifactsWorkspaceSettingsState state) {
+    myState = state;
+  }
+
+  public static class ArtifactsWorkspaceSettingsState {
+    @Tag("artifacts-to-build")
+    @AbstractCollection(surroundWithTag = false, elementTag = "artifact", elementValueAttribute = "name")
+    public List<String> myArtifactsToBuild = new ArrayList<String>();
+
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/CopyToDirectoryInstructionCreator.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/CopyToDirectoryInstructionCreator.java
new file mode 100644
index 0000000..3043468
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/CopyToDirectoryInstructionCreator.java
@@ -0,0 +1,63 @@
+/*
+ * 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 com.intellij.packaging.impl.compiler;
+
+import com.intellij.compiler.impl.packagingCompiler.ExplodedDestinationInfo;
+import com.intellij.compiler.impl.packagingCompiler.JarInfo;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.elements.IncrementalCompilerInstructionCreator;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public class CopyToDirectoryInstructionCreator extends IncrementalCompilerInstructionCreatorBase {
+  private final String myOutputPath;
+  private final @Nullable VirtualFile myOutputFile;
+
+  public CopyToDirectoryInstructionCreator(ArtifactsProcessingItemsBuilderContext context, String outputPath,
+                                           @Nullable VirtualFile outputFile) {
+    super(context);
+    myOutputPath = outputPath;
+    myOutputFile = outputFile;
+  }
+
+  public void addFileCopyInstruction(@NotNull VirtualFile file, @NotNull String outputFileName) {
+    myContext.addDestination(file, new ExplodedDestinationInfo(myOutputPath + "/" + outputFileName, outputChild(outputFileName)));
+  }
+
+  public CopyToDirectoryInstructionCreator subFolder(@NotNull String directoryName) {
+    return new CopyToDirectoryInstructionCreator(myContext, myOutputPath + "/" + directoryName, outputChild(directoryName));
+  }
+
+  public IncrementalCompilerInstructionCreator archive(@NotNull String archiveFileName) {
+    String jarOutputPath = myOutputPath + "/" + archiveFileName;
+    final JarInfo jarInfo = new JarInfo();
+    if (!myContext.registerJarFile(jarInfo, jarOutputPath)) {
+      return new SkipAllInstructionCreator(myContext);
+    }
+    VirtualFile outputFile = outputChild(archiveFileName);
+    final ExplodedDestinationInfo destination = new ExplodedDestinationInfo(jarOutputPath, outputFile);
+    jarInfo.addDestination(destination);
+    return new PackIntoArchiveInstructionCreator(myContext, jarInfo, "", destination);
+  }
+
+  @Nullable
+  private VirtualFile outputChild(String name) {
+    return myOutputFile != null ? myOutputFile.findChild(name) : null;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/IncrementalCompilerInstructionCreatorBase.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/IncrementalCompilerInstructionCreatorBase.java
new file mode 100644
index 0000000..9837b9f
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/IncrementalCompilerInstructionCreatorBase.java
@@ -0,0 +1,98 @@
+/*
+ * 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 com.intellij.packaging.impl.compiler;
+
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileVisitor;
+import com.intellij.packaging.elements.IncrementalCompilerInstructionCreator;
+import com.intellij.packaging.elements.PackagingFileFilter;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class IncrementalCompilerInstructionCreatorBase implements IncrementalCompilerInstructionCreator {
+  protected final ArtifactsProcessingItemsBuilderContext myContext;
+
+  public IncrementalCompilerInstructionCreatorBase(ArtifactsProcessingItemsBuilderContext context) {
+    myContext = context;
+  }
+
+  public void addDirectoryCopyInstructions(@NotNull VirtualFile directory) {
+    addDirectoryCopyInstructions(directory, null);
+  }
+
+  public void addDirectoryCopyInstructions(@NotNull VirtualFile directory, @Nullable PackagingFileFilter filter) {
+    final ProjectFileIndex index = ProjectRootManager.getInstance(myContext.getCompileContext().getProject()).getFileIndex();
+    final boolean copyExcluded = index.isIgnored(directory);
+    collectInstructionsRecursively(directory, this, filter, index, copyExcluded);
+  }
+
+  private static void collectInstructionsRecursively(VirtualFile directory,
+                                                     final IncrementalCompilerInstructionCreatorBase creator,
+                                                     @Nullable final PackagingFileFilter filter,
+                                                     final ProjectFileIndex index,
+                                                     final boolean copyExcluded) {
+    final FileTypeManager fileTypeManager = FileTypeManager.getInstance();
+    VfsUtilCore.visitChildrenRecursively(directory, new VirtualFileVisitor<IncrementalCompilerInstructionCreatorBase>(VirtualFileVisitor.SKIP_ROOT) {
+      { setValueForChildren(creator); }
+
+      @Override
+      public boolean visitFile(@NotNull VirtualFile child) {
+        if (copyExcluded) {
+          if (fileTypeManager.isFileIgnored(child)) return false;
+        }
+        else {
+          if (index.isIgnored(child)) return false;
+        }
+
+        final IncrementalCompilerInstructionCreatorBase creator = getCurrentValue();
+        if (filter != null && !filter.accept(child, creator.myContext.getCompileContext())) {
+          return false;
+        }
+
+        if (!child.isDirectory()) {
+          creator.addFileCopyInstruction(child, child.getName());
+        }
+        else {
+          setValueForChildren(creator.subFolder(child.getName()));
+        }
+
+        return true;
+      }
+    });
+  }
+
+  @Override
+  public abstract IncrementalCompilerInstructionCreatorBase subFolder(@NotNull String directoryName);
+
+  public IncrementalCompilerInstructionCreator subFolderByRelativePath(@NotNull String relativeDirectoryPath) {
+    final List<String> folders = StringUtil.split(relativeDirectoryPath, "/");
+    IncrementalCompilerInstructionCreator current = this;
+    for (String folder : folders) {
+      current = current.subFolder(folder);
+    }
+    return current;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/PackIntoArchiveInstructionCreator.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/PackIntoArchiveInstructionCreator.java
new file mode 100644
index 0000000..c51b8e7
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/PackIntoArchiveInstructionCreator.java
@@ -0,0 +1,66 @@
+/*
+ * 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 com.intellij.packaging.impl.compiler;
+
+import com.intellij.compiler.impl.packagingCompiler.DestinationInfo;
+import com.intellij.compiler.impl.packagingCompiler.JarDestinationInfo;
+import com.intellij.compiler.impl.packagingCompiler.JarInfo;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.elements.IncrementalCompilerInstructionCreator;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class PackIntoArchiveInstructionCreator extends IncrementalCompilerInstructionCreatorBase {
+  private final DestinationInfo myJarDestination;
+  private final JarInfo myJarInfo;
+  private final String myPathInJar;
+
+  public PackIntoArchiveInstructionCreator(ArtifactsProcessingItemsBuilderContext context, JarInfo jarInfo,
+                                           String pathInJar, DestinationInfo jarDestination) {
+    super(context);
+    myJarInfo = jarInfo;
+    myPathInJar = pathInJar;
+    myJarDestination = jarDestination;
+  }
+
+  public void addFileCopyInstruction(@NotNull VirtualFile file, @NotNull String outputFileName) {
+    final String pathInJar = childPathInJar(outputFileName);
+    if (myContext.addDestination(file, new JarDestinationInfo(pathInJar, myJarInfo, myJarDestination))) {
+      myJarInfo.addContent(pathInJar, file);
+    }
+  }
+
+  private String childPathInJar(String fileName) {
+    return myPathInJar.length() == 0 ? fileName : myPathInJar + "/" + fileName;
+  }
+
+  public PackIntoArchiveInstructionCreator subFolder(@NotNull String directoryName) {
+    return new PackIntoArchiveInstructionCreator(myContext, myJarInfo, childPathInJar(directoryName), myJarDestination);
+  }
+
+  public IncrementalCompilerInstructionCreator archive(@NotNull String archiveFileName) {
+    final JarInfo jarInfo = new JarInfo();
+    final String outputPath = myJarDestination.getOutputPath() + "/" + archiveFileName;
+    if (!myContext.registerJarFile(jarInfo, outputPath)) {
+      return new SkipAllInstructionCreator(myContext);
+    }
+    final JarDestinationInfo destination = new JarDestinationInfo(childPathInJar(archiveFileName), myJarInfo, myJarDestination);
+    jarInfo.addDestination(destination);
+    return new PackIntoArchiveInstructionCreator(myContext, jarInfo, "", destination);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/SkipAllInstructionCreator.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/SkipAllInstructionCreator.java
new file mode 100644
index 0000000..b7d409f
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/SkipAllInstructionCreator.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 com.intellij.packaging.impl.compiler;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.elements.IncrementalCompilerInstructionCreator;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class SkipAllInstructionCreator extends IncrementalCompilerInstructionCreatorBase {
+  public SkipAllInstructionCreator(ArtifactsProcessingItemsBuilderContext context) {
+    super(context);
+  }
+
+  public void addFileCopyInstruction(@NotNull VirtualFile file, @NotNull String outputFileName) {
+  }
+
+  public SkipAllInstructionCreator subFolder(@NotNull String directoryName) {
+    return this;
+  }
+
+  public IncrementalCompilerInstructionCreator archive(@NotNull String archiveFileName) {
+    return this;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ArchiveElementType.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ArchiveElementType.java
new file mode 100644
index 0000000..0dca4cb
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ArchiveElementType.java
@@ -0,0 +1,68 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.CompositePackagingElementType;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.impl.ui.properties.ArchiveElementPropertiesPanel;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingElementPropertiesPanel;
+import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+* @author nik
+*/
+class ArchiveElementType extends CompositePackagingElementType<ArchivePackagingElement> {
+  ArchiveElementType() {
+    super("archive", CompilerBundle.message("element.type.name.archive"));
+  }
+
+  @Override
+  public Icon getCreateElementIcon() {
+    return PlatformIcons.JAR_ICON;
+  }
+
+  @NotNull
+  @Override
+  public ArchivePackagingElement createEmpty(@NotNull Project project) {
+    return new ArchivePackagingElement();
+  }
+
+  @Override
+  public PackagingElementPropertiesPanel createElementPropertiesPanel(@NotNull ArchivePackagingElement element,
+                                                                                               @NotNull ArtifactEditorContext context) {
+    final String name = element.getArchiveFileName();
+    if (ArtifactUtil.isArchiveName(name)) {
+      return new ArchiveElementPropertiesPanel(element, context);
+    }
+    return null;
+  }
+
+  public CompositePackagingElement<?> createComposite(CompositePackagingElement<?> parent, @Nullable String baseName, @NotNull ArtifactEditorContext context) {
+    final String initialValue = PackagingElementFactoryImpl.suggestFileName(parent, baseName != null ? baseName : "archive", ".jar");
+    final String path = Messages.showInputDialog(context.getProject(), "Enter archive name: ", "New Archive", null, initialValue, new FilePathValidator());
+    if (path == null) return null;
+    return PackagingElementFactoryImpl.createDirectoryOrArchiveWithParents(path, true);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ArchivePackagingElement.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ArchivePackagingElement.java
new file mode 100644
index 0000000..a144df7
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ArchivePackagingElement.java
@@ -0,0 +1,118 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.compiler.ant.BuildProperties;
+import com.intellij.compiler.ant.Generator;
+import com.intellij.compiler.ant.Tag;
+import com.intellij.compiler.ant.artifacts.ArchiveAntCopyInstructionCreator;
+import com.intellij.compiler.ant.taskdefs.Jar;
+import com.intellij.compiler.ant.taskdefs.Zip;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.*;
+import com.intellij.packaging.impl.ui.ArchiveElementPresentation;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingElementPresentation;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+import com.intellij.util.xmlb.annotations.Attribute;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ArchivePackagingElement extends CompositeElementWithManifest<ArchivePackagingElement> {
+  @NonNls public static final String NAME_ATTRIBUTE = "name";
+  private String myArchiveFileName;
+
+  public ArchivePackagingElement() {
+    super(PackagingElementFactoryImpl.ARCHIVE_ELEMENT_TYPE);
+  }
+
+  public ArchivePackagingElement(@NotNull String archiveFileName) {
+    super(PackagingElementFactoryImpl.ARCHIVE_ELEMENT_TYPE);
+    myArchiveFileName = archiveFileName;
+  }
+
+  public PackagingElementPresentation createPresentation(@NotNull ArtifactEditorContext context) {
+    return new ArchiveElementPresentation(this);
+  }
+
+  @Override
+  public List<? extends Generator> computeAntInstructions(@NotNull PackagingElementResolvingContext resolvingContext, @NotNull AntCopyInstructionCreator creator,
+                                                          @NotNull ArtifactAntGenerationContext generationContext,
+                                                          @NotNull ArtifactType artifactType) {
+    final String tempJarProperty = generationContext.createNewTempFileProperty("temp.jar.path." + myArchiveFileName, myArchiveFileName);
+    String jarPath = BuildProperties.propertyRef(tempJarProperty);
+    final Tag jar;
+    if (myArchiveFileName.endsWith(".jar")) {
+      jar = new Jar(jarPath, "preserve", true);
+    }
+    else {
+      jar = new Zip(jarPath);
+    }
+    for (Generator generator : computeChildrenGenerators(resolvingContext, new ArchiveAntCopyInstructionCreator(""), generationContext, artifactType)) {
+      jar.add(generator);
+    }
+    generationContext.runBeforeCurrentArtifact(jar);
+    return Collections.singletonList(creator.createFileCopyInstruction(jarPath, myArchiveFileName));
+  }
+
+  @Override
+  public void computeIncrementalCompilerInstructions(@NotNull IncrementalCompilerInstructionCreator creator,
+                                                     @NotNull PackagingElementResolvingContext resolvingContext,
+                                                     @NotNull ArtifactIncrementalCompilerContext compilerContext, @NotNull ArtifactType artifactType) {
+    computeChildrenInstructions(creator.archive(myArchiveFileName), resolvingContext, compilerContext, artifactType);
+  }
+
+  @Attribute(NAME_ATTRIBUTE)
+  public String getArchiveFileName() {
+    return myArchiveFileName;
+  }
+
+  @NonNls @Override
+  public String toString() {
+    return "archive:" + myArchiveFileName;
+  }
+
+  public ArchivePackagingElement getState() {
+    return this;
+  }
+
+  public void setArchiveFileName(String archiveFileName) {
+    myArchiveFileName = archiveFileName;
+  }
+
+  public String getName() {
+    return myArchiveFileName;
+  }
+
+  public void rename(@NotNull String newName) {
+    myArchiveFileName = newName;
+  }
+
+  @Override
+  public boolean isEqualTo(@NotNull PackagingElement<?> element) {
+    return element instanceof ArchivePackagingElement && ((ArchivePackagingElement)element).getArchiveFileName().equals(myArchiveFileName);
+  }
+
+  public void loadState(ArchivePackagingElement state) {
+    XmlSerializerUtil.copyBean(state, this);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ArtifactElementType.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ArtifactElementType.java
new file mode 100644
index 0000000..ed599a8
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ArtifactElementType.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 com.intellij.packaging.impl.elements;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.project.Project;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactManager;
+import com.intellij.packaging.artifacts.ArtifactPointerManager;
+import com.intellij.packaging.elements.ComplexPackagingElementType;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.util.Processor;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.util.*;
+
+/**
+* @author nik
+*/
+public class ArtifactElementType extends ComplexPackagingElementType<ArtifactPackagingElement> {
+  public static final ArtifactElementType ARTIFACT_ELEMENT_TYPE = new ArtifactElementType();
+
+  ArtifactElementType() {
+    super("artifact", CompilerBundle.message("element.type.name.artifact"));
+  }
+
+  @Override
+  public Icon getCreateElementIcon() {
+    return AllIcons.Nodes.Artifact;
+  }
+
+  @Override
+  public boolean canCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact) {
+    return !getAvailableArtifacts(context, artifact, false).isEmpty();
+  }
+
+  @NotNull
+  public List<? extends ArtifactPackagingElement> chooseAndCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact,
+                                                                   @NotNull CompositePackagingElement<?> parent) {
+    final Project project = context.getProject();
+    List<Artifact> artifacts = context.chooseArtifacts(getAvailableArtifacts(context, artifact, false), CompilerBundle.message("dialog.title.choose.artifacts"));
+    final List<ArtifactPackagingElement> elements = new ArrayList<ArtifactPackagingElement>();
+    for (Artifact selected : artifacts) {
+      elements.add(new ArtifactPackagingElement(project, ArtifactPointerManager.getInstance(project).createPointer(selected, context.getArtifactModel())));
+    }
+    return elements;
+  }
+
+  @NotNull
+  public static List<? extends Artifact> getAvailableArtifacts(@NotNull final ArtifactEditorContext context,
+                                                               @NotNull final Artifact artifact,
+                                                               final boolean notIncludedOnly) {
+    final Set<Artifact> result = new HashSet<Artifact>(Arrays.asList(context.getArtifactModel().getArtifacts()));
+    if (notIncludedOnly) {
+      ArtifactUtil.processPackagingElements(artifact, ARTIFACT_ELEMENT_TYPE, new Processor<ArtifactPackagingElement>() {
+        public boolean process(ArtifactPackagingElement artifactPackagingElement) {
+          result.remove(artifactPackagingElement.findArtifact(context));
+          return true;
+        }
+      }, context, true);
+    }
+    result.remove(artifact);
+    final Iterator<Artifact> iterator = result.iterator();
+    while (iterator.hasNext()) {
+      Artifact another = iterator.next();
+      final boolean notContainThis =
+          ArtifactUtil.processPackagingElements(another, ARTIFACT_ELEMENT_TYPE, new Processor<ArtifactPackagingElement>() {
+            public boolean process(ArtifactPackagingElement element) {
+              return !artifact.getName().equals(element.getArtifactName());
+            }
+          }, context, true);
+      if (!notContainThis) {
+        iterator.remove();
+      }
+    }
+    final ArrayList<Artifact> list = new ArrayList<Artifact>(result);
+    Collections.sort(list, ArtifactManager.ARTIFACT_COMPARATOR);
+    return list;
+  }
+
+  @NotNull
+  public ArtifactPackagingElement createEmpty(@NotNull Project project) {
+    return new ArtifactPackagingElement(project);
+  }
+
+  @Override
+  public String getShowContentActionText() {
+    return "Show Content of Included Artifacts";
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ArtifactPackagingElement.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ArtifactPackagingElement.java
new file mode 100644
index 0000000..7d507b6
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ArtifactPackagingElement.java
@@ -0,0 +1,190 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.compiler.ant.BuildProperties;
+import com.intellij.compiler.ant.Generator;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactPointer;
+import com.intellij.packaging.artifacts.ArtifactPointerManager;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.*;
+import com.intellij.packaging.impl.ui.ArtifactElementPresentation;
+import com.intellij.packaging.impl.ui.DelegatedPackagingElementPresentation;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingElementPresentation;
+import com.intellij.util.xmlb.annotations.Attribute;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ArtifactPackagingElement extends ComplexPackagingElement<ArtifactPackagingElement.ArtifactPackagingElementState> {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.packaging.impl.elements.ArtifactPackagingElement");
+  private final Project myProject;
+  private ArtifactPointer myArtifactPointer;
+  @NonNls public static final String ARTIFACT_NAME_ATTRIBUTE = "artifact-name";
+
+  public ArtifactPackagingElement(@NotNull Project project) {
+    super(ArtifactElementType.ARTIFACT_ELEMENT_TYPE);
+    myProject = project;
+  }
+
+  public ArtifactPackagingElement(@NotNull Project project, @NotNull ArtifactPointer artifactPointer) {
+    this(project);
+    myArtifactPointer = artifactPointer;
+  }
+
+  public List<? extends PackagingElement<?>> getSubstitution(@NotNull PackagingElementResolvingContext context, @NotNull ArtifactType artifactType) {
+    final Artifact artifact = findArtifact(context);
+    if (artifact != null) {
+      final ArtifactType type = artifact.getArtifactType();
+      List<? extends PackagingElement<?>> substitution = type.getSubstitution(artifact, context, artifactType);
+      if (substitution != null) {
+        return substitution;
+      }
+
+      final List<PackagingElement<?>> elements = new ArrayList<PackagingElement<?>>();
+      final CompositePackagingElement<?> rootElement = artifact.getRootElement();
+      if (rootElement instanceof ArtifactRootElement<?>) {
+        elements.addAll(rootElement.getChildren());
+      }
+      else {
+        elements.add(rootElement);
+      }
+      return elements;
+    }
+    return null;
+  }
+
+  @Override
+  public List<? extends Generator> computeAntInstructions(@NotNull PackagingElementResolvingContext resolvingContext, @NotNull AntCopyInstructionCreator creator,
+                                                          @NotNull ArtifactAntGenerationContext generationContext,
+                                                          @NotNull ArtifactType artifactType) {
+    final Artifact artifact = findArtifact(resolvingContext);
+    if (artifact != null) {
+      if (artifact.getArtifactType().getSubstitution(artifact, resolvingContext, artifactType) != null) {
+        return super.computeAntInstructions(resolvingContext, creator, generationContext, artifactType);
+      }
+      final String outputPath = BuildProperties.propertyRef(generationContext.getArtifactOutputProperty(artifact));
+      return Collections.singletonList(creator.createDirectoryContentCopyInstruction(outputPath));
+    }
+    return Collections.emptyList();
+  }
+
+  @Override
+  public void computeIncrementalCompilerInstructions(@NotNull IncrementalCompilerInstructionCreator creator,
+                                                     @NotNull PackagingElementResolvingContext resolvingContext,
+                                                     @NotNull ArtifactIncrementalCompilerContext compilerContext,
+                                                     @NotNull ArtifactType artifactType) {
+    Artifact artifact = findArtifact(resolvingContext);
+    if (artifact == null) return;
+
+    if (StringUtil.isEmpty(artifact.getOutputPath())
+        || artifact.getArtifactType().getSubstitution(artifact, resolvingContext, artifactType) != null) {
+      super.computeIncrementalCompilerInstructions(creator, resolvingContext, compilerContext, artifactType);
+      return;
+    }
+
+    VirtualFile outputFile = artifact.getOutputFile();
+    if (outputFile == null) {
+      LOG.debug("Output file for " + artifact + " not found");
+      return;
+    }
+    if (!outputFile.isValid()) {
+      LOG.debug("Output file for " + artifact + "(" + outputFile + ") is not valid");
+      return;
+    }
+
+    if (outputFile.isDirectory()) {
+      creator.addDirectoryCopyInstructions(outputFile);
+    }
+    else {
+      creator.addFileCopyInstruction(outputFile, outputFile.getName());
+    }
+  }
+
+  @Override
+  protected ArtifactType getArtifactTypeForSubstitutedElements(PackagingElementResolvingContext resolvingContext, ArtifactType artifactType) {
+    final Artifact artifact = findArtifact(resolvingContext);
+    if (artifact != null) {
+      return artifact.getArtifactType();
+    }
+    return artifactType;
+  }
+
+  public PackagingElementPresentation createPresentation(@NotNull ArtifactEditorContext context) {
+    return new DelegatedPackagingElementPresentation(new ArtifactElementPresentation(myArtifactPointer, context));
+  }
+
+  public ArtifactPackagingElementState getState() {
+    final ArtifactPackagingElementState state = new ArtifactPackagingElementState();
+    if (myArtifactPointer != null) {
+      state.setArtifactName(myArtifactPointer.getArtifactName());
+    }
+    return state;
+  }
+
+  public void loadState(ArtifactPackagingElementState state) {
+    final String name = state.getArtifactName();
+    myArtifactPointer = name != null ? ArtifactPointerManager.getInstance(myProject).createPointer(name) : null;
+  }
+
+  @Override
+  public String toString() {
+    return "artifact:" + getArtifactName();
+  }
+
+  @Override
+  public boolean isEqualTo(@NotNull PackagingElement<?> element) {
+    return element instanceof ArtifactPackagingElement && myArtifactPointer != null
+           && myArtifactPointer.equals(((ArtifactPackagingElement)element).myArtifactPointer);
+  }
+
+  @Nullable
+  public Artifact findArtifact(@NotNull PackagingElementResolvingContext context) {
+    return myArtifactPointer != null ? myArtifactPointer.findArtifact(context.getArtifactModel()) : null;
+  }
+
+  @Nullable
+  public String getArtifactName() {
+    return myArtifactPointer != null ? myArtifactPointer.getArtifactName() : null;
+  }
+
+
+  public static class ArtifactPackagingElementState {
+    private String myArtifactName;
+
+    @Attribute(ARTIFACT_NAME_ATTRIBUTE)
+    public String getArtifactName() {
+      return myArtifactName;
+    }
+
+    public void setArtifactName(String artifactName) {
+      myArtifactName = artifactName;
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ArtifactRootElementImpl.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ArtifactRootElementImpl.java
new file mode 100644
index 0000000..5fcba6f
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ArtifactRootElementImpl.java
@@ -0,0 +1,96 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.compiler.ant.Generator;
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.*;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingElementPresentation;
+import com.intellij.ui.SimpleTextAttributes;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ArtifactRootElementImpl extends ArtifactRootElement<Object> {
+  public ArtifactRootElementImpl() {
+    super(PackagingElementFactoryImpl.ARTIFACT_ROOT_ELEMENT_TYPE);
+  }
+
+  public PackagingElementPresentation createPresentation(@NotNull ArtifactEditorContext context) {
+    return new PackagingElementPresentation() {
+      @Override
+      public String getPresentableName() {
+        return CompilerBundle.message("packaging.element.text.output.root");
+      }
+
+      @Override
+      public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes,
+                         SimpleTextAttributes commentAttributes) {
+        presentationData.setIcon(AllIcons.Nodes.Artifact);
+        presentationData.addText(getPresentableName(), mainAttributes);
+      }
+
+      @Override
+      public int getWeight() {
+        return 0;
+      }
+    };
+  }
+
+  public Object getState() {
+    return null;
+  }
+
+  public void loadState(Object state) {
+  }
+
+  @Override
+  public boolean canBeRenamed() {
+    return false;
+  }
+
+  public void rename(@NotNull String newName) {
+  }
+
+  public List<? extends Generator> computeAntInstructions(@NotNull PackagingElementResolvingContext resolvingContext, @NotNull AntCopyInstructionCreator creator,
+                                                          @NotNull ArtifactAntGenerationContext generationContext,
+                                                          @NotNull ArtifactType artifactType) {
+    return computeChildrenGenerators(resolvingContext, creator, generationContext, artifactType);
+  }
+
+  @Override
+  public void computeIncrementalCompilerInstructions(@NotNull IncrementalCompilerInstructionCreator creator,
+                                                     @NotNull PackagingElementResolvingContext resolvingContext,
+                                                     @NotNull ArtifactIncrementalCompilerContext compilerContext, @NotNull ArtifactType artifactType) {
+    computeChildrenInstructions(creator, resolvingContext, compilerContext, artifactType);
+  }
+
+  public String getName() {
+    return "";
+  }
+
+  @Override
+  public String toString() {
+    return "<root>";
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/CompositeElementWithManifest.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/CompositeElementWithManifest.java
new file mode 100644
index 0000000..fcaf889
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/CompositeElementWithManifest.java
@@ -0,0 +1,28 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElementType;
+
+/**
+ * @author nik
+ */
+public abstract class CompositeElementWithManifest<T> extends CompositePackagingElement<T> {
+  protected CompositeElementWithManifest(PackagingElementType type) {
+    super(type);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/DirectoryCopyElementType.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/DirectoryCopyElementType.java
new file mode 100644
index 0000000..bc7258d
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/DirectoryCopyElementType.java
@@ -0,0 +1,69 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElementType;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+* @author nik
+*/
+public class DirectoryCopyElementType extends PackagingElementType<DirectoryCopyPackagingElement> {
+
+  DirectoryCopyElementType() {
+    super("dir-copy", "Directory Content");
+  }
+
+  @Override
+  public Icon getCreateElementIcon() {
+    return AllIcons.Nodes.CopyOfFolder;
+  }
+
+  @Override
+  public boolean canCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact) {
+    return true;
+  }
+
+  @NotNull
+  public List<? extends DirectoryCopyPackagingElement> chooseAndCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact,
+                                                                   @NotNull CompositePackagingElement<?> parent) {
+    final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createMultipleFoldersDescriptor();
+    final VirtualFile[] files = FileChooser.chooseFiles(descriptor, context.getProject(), null);
+    final List<DirectoryCopyPackagingElement> list = new ArrayList<DirectoryCopyPackagingElement>();
+    for (VirtualFile file : files) {
+      list.add(new DirectoryCopyPackagingElement(file.getPath()));
+    }
+    return list;
+  }
+
+  @NotNull
+  public DirectoryCopyPackagingElement createEmpty(@NotNull Project project) {
+    return new DirectoryCopyPackagingElement();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/DirectoryCopyPackagingElement.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/DirectoryCopyPackagingElement.java
new file mode 100644
index 0000000..c5c10dc
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/DirectoryCopyPackagingElement.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 com.intellij.packaging.impl.elements;
+
+import com.intellij.compiler.ant.Generator;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.*;
+import com.intellij.packaging.impl.ui.DirectoryCopyPresentation;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingElementPresentation;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class DirectoryCopyPackagingElement extends FileOrDirectoryCopyPackagingElement<DirectoryCopyPackagingElement> {
+  public DirectoryCopyPackagingElement() {
+    super(PackagingElementFactoryImpl.DIRECTORY_COPY_ELEMENT_TYPE);
+  }
+
+  public DirectoryCopyPackagingElement(String directoryPath) {
+    this();
+    myFilePath = directoryPath;
+  }
+
+  @Override
+  public PackagingElementPresentation createPresentation(@NotNull ArtifactEditorContext context) {
+    return new DirectoryCopyPresentation(myFilePath);
+  }
+
+  @Override
+  public List<? extends Generator> computeAntInstructions(@NotNull PackagingElementResolvingContext resolvingContext,
+                                                          @NotNull AntCopyInstructionCreator creator,
+                                                          @NotNull ArtifactAntGenerationContext generationContext,
+                                                          @NotNull ArtifactType artifactType) {
+    final String path = generationContext.getSubstitutedPath(myFilePath);
+    return Collections.singletonList(creator.createDirectoryContentCopyInstruction(path));
+  }
+
+  @Override
+  public void computeIncrementalCompilerInstructions(@NotNull IncrementalCompilerInstructionCreator creator,
+                                                     @NotNull PackagingElementResolvingContext resolvingContext,
+                                                     @NotNull ArtifactIncrementalCompilerContext compilerContext,
+                                                     @NotNull ArtifactType artifactType) {
+    final VirtualFile file = findFile();
+    if (file != null && file.isValid() && file.isDirectory()) {
+      creator.addDirectoryCopyInstructions(file);
+    }
+  }
+
+  public DirectoryCopyPackagingElement getState() {
+    return this;
+  }
+
+  public void loadState(DirectoryCopyPackagingElement state) {
+    myFilePath = state.getFilePath();
+  }
+
+  @Override
+  public String toString() {
+    return "dir:" + myFilePath;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/DirectoryElementType.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/DirectoryElementType.java
new file mode 100644
index 0000000..5732faf
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/DirectoryElementType.java
@@ -0,0 +1,67 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.CompositePackagingElementType;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.impl.ui.properties.DirectoryElementPropertiesPanel;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingElementPropertiesPanel;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+/**
+* @author nik
+*/
+class DirectoryElementType extends CompositePackagingElementType<DirectoryPackagingElement> {
+
+  DirectoryElementType() {
+    super("directory", CompilerBundle.message("element.type.name.directory"));
+  }
+
+  @Override
+  public Icon getCreateElementIcon() {
+    return AllIcons.Actions.NewFolder;
+  }
+
+  @NotNull
+  public DirectoryPackagingElement createEmpty(@NotNull Project project) {
+    return new DirectoryPackagingElement();
+  }
+
+  @Override
+  public PackagingElementPropertiesPanel createElementPropertiesPanel(@NotNull DirectoryPackagingElement element,
+                                                                                                 @NotNull ArtifactEditorContext context) {
+    if (ArtifactUtil.isArchiveName(element.getDirectoryName())) {
+      return new DirectoryElementPropertiesPanel(element, context);
+    }
+    return null;
+  }
+
+  public CompositePackagingElement<?> createComposite(CompositePackagingElement<?> parent, String baseName, @NotNull ArtifactEditorContext context) {
+    final String initialValue = PackagingElementFactoryImpl.suggestFileName(parent, baseName != null ? baseName : "folder", "");
+    String path = Messages.showInputDialog(context.getProject(), "Enter directory name: ", "New Directory", null, initialValue, new FilePathValidator());
+    if (path == null) return null;
+    return PackagingElementFactoryImpl.createDirectoryOrArchiveWithParents(path, false);
+  }
+
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/DirectoryPackagingElement.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/DirectoryPackagingElement.java
new file mode 100644
index 0000000..0fd3a34
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/DirectoryPackagingElement.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.
+ */
+package com.intellij.packaging.impl.elements;
+
+import com.intellij.compiler.ant.Generator;
+import com.intellij.packaging.elements.*;
+import com.intellij.packaging.impl.ui.DirectoryElementPresentation;
+import com.intellij.packaging.ui.PackagingElementPresentation;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.util.xmlb.annotations.Attribute;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.NonNls;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author nik
+ *
+ * classpath is used for exploded WAR and EJB directories under exploded EAR
+ */
+public class DirectoryPackagingElement extends CompositeElementWithManifest<DirectoryPackagingElement> {
+  @NonNls public static final String NAME_ATTRIBUTE = "name";
+  private String myDirectoryName;
+
+  public DirectoryPackagingElement() {
+    super(PackagingElementFactoryImpl.DIRECTORY_ELEMENT_TYPE);
+  }
+
+  public DirectoryPackagingElement(String directoryName) {
+    super(PackagingElementFactoryImpl.DIRECTORY_ELEMENT_TYPE);
+    myDirectoryName = directoryName;
+  }
+
+  public PackagingElementPresentation createPresentation(@NotNull ArtifactEditorContext context) {
+    return new DirectoryElementPresentation(this); 
+  }
+
+  @Override
+  public List<? extends Generator> computeAntInstructions(@NotNull PackagingElementResolvingContext resolvingContext, @NotNull AntCopyInstructionCreator creator,
+                                                          @NotNull ArtifactAntGenerationContext generationContext,
+                                                          @NotNull ArtifactType artifactType) {
+
+    final List<Generator> children = new ArrayList<Generator>();
+    final Generator command = creator.createSubFolderCommand(myDirectoryName);
+    if (command != null) {
+      children.add(command);
+    }
+    children.addAll(computeChildrenGenerators(resolvingContext, creator.subFolder(myDirectoryName), generationContext, artifactType));
+    return children;
+  }
+
+  @Override
+  public void computeIncrementalCompilerInstructions(@NotNull IncrementalCompilerInstructionCreator creator,
+                                                     @NotNull PackagingElementResolvingContext resolvingContext,
+                                                     @NotNull ArtifactIncrementalCompilerContext compilerContext, @NotNull ArtifactType artifactType) {
+    computeChildrenInstructions(creator.subFolder(myDirectoryName), resolvingContext, compilerContext, artifactType);
+  }
+
+  public DirectoryPackagingElement getState() {
+    return this;
+  }
+
+  @NonNls @Override
+  public String toString() {
+    return "dir:" + myDirectoryName;
+  }
+
+  @Attribute(NAME_ATTRIBUTE)
+  public String getDirectoryName() {
+    return myDirectoryName;
+  }
+
+  public void setDirectoryName(String directoryName) {
+    myDirectoryName = directoryName;
+  }
+
+  public void rename(@NotNull String newName) {
+    myDirectoryName = newName;
+  }
+
+  public String getName() {
+    return myDirectoryName;
+  }
+
+  @Override
+  public boolean isEqualTo(@NotNull PackagingElement<?> element) {
+    return element instanceof DirectoryPackagingElement && ((DirectoryPackagingElement)element).getDirectoryName().equals(myDirectoryName);
+  }
+
+  public void loadState(DirectoryPackagingElement state) {
+    XmlSerializerUtil.copyBean(state, this);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ExtractedDirectoryElementType.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ExtractedDirectoryElementType.java
new file mode 100644
index 0000000..8f487f3
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ExtractedDirectoryElementType.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 com.intellij.packaging.impl.elements;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementFactory;
+import com.intellij.packaging.elements.PackagingElementType;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+* @author nik
+*/
+public class ExtractedDirectoryElementType extends PackagingElementType<ExtractedDirectoryPackagingElement> {
+
+  ExtractedDirectoryElementType() {
+    super("extracted-dir", "Extracted Directory");
+  }
+
+  @Override
+  public Icon getCreateElementIcon() {
+    return AllIcons.Nodes.ExtractedFolder;
+  }
+
+  @Override
+  public boolean canCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact) {
+    return true;
+  }
+
+  @NotNull
+  public List<? extends PackagingElement<?>> chooseAndCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact,
+                                                                   @NotNull CompositePackagingElement<?> parent) {
+    final FileChooserDescriptor descriptor = new FileChooserDescriptor(false, true, true, false, true, true) {
+      @Override
+      public boolean isFileSelectable(VirtualFile file) {
+        if (file.isInLocalFileSystem() && file.isDirectory()) return false;
+        return super.isFileSelectable(file);
+      }
+    };
+    final VirtualFile[] files = FileChooser.chooseFiles(descriptor, context.getProject(), null);
+    final List<PackagingElement<?>> list = new ArrayList<PackagingElement<?>>();
+    final PackagingElementFactory factory = PackagingElementFactory.getInstance();
+    for (VirtualFile file : files) {
+      list.add(factory.createExtractedDirectory(file));
+    }
+    return list;
+  }
+
+  @NotNull
+  public ExtractedDirectoryPackagingElement createEmpty(@NotNull Project project) {
+    return new ExtractedDirectoryPackagingElement();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ExtractedDirectoryPackagingElement.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ExtractedDirectoryPackagingElement.java
new file mode 100644
index 0000000..a0b4dda
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ExtractedDirectoryPackagingElement.java
@@ -0,0 +1,143 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.compiler.ant.BuildProperties;
+import com.intellij.compiler.ant.Generator;
+import com.intellij.compiler.ant.taskdefs.Include;
+import com.intellij.compiler.ant.taskdefs.Mkdir;
+import com.intellij.compiler.ant.taskdefs.PatternSet;
+import com.intellij.compiler.ant.taskdefs.Unzip;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.*;
+import com.intellij.packaging.impl.ui.ExtractedDirectoryPresentation;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingElementPresentation;
+import com.intellij.util.PathUtil;
+import com.intellij.util.xmlb.annotations.Attribute;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ExtractedDirectoryPackagingElement extends FileOrDirectoryCopyPackagingElement<ExtractedDirectoryPackagingElement> {
+  private String myPathInJar;
+
+  public ExtractedDirectoryPackagingElement() {
+    super(PackagingElementFactoryImpl.EXTRACTED_DIRECTORY_ELEMENT_TYPE);
+  }
+
+  public ExtractedDirectoryPackagingElement(String jarPath, String pathInJar) {
+    super(PackagingElementFactoryImpl.EXTRACTED_DIRECTORY_ELEMENT_TYPE, jarPath);
+    myPathInJar = pathInJar;
+    if (!StringUtil.startsWithChar(myPathInJar, '/')) {
+      myPathInJar = "/" + myPathInJar;
+    }
+    if (!StringUtil.endsWithChar(myPathInJar, '/')) {
+      myPathInJar += "/";
+    }
+  }
+
+  @Override
+  public PackagingElementPresentation createPresentation(@NotNull ArtifactEditorContext context) {
+    return new ExtractedDirectoryPresentation(this); 
+  }
+
+  @Override
+  public String toString() {
+    return "extracted:" + myFilePath + "!" + myPathInJar;
+  }
+
+  @Override
+  public VirtualFile findFile() {
+    final VirtualFile jarFile = super.findFile();
+    if (jarFile == null) return null;
+
+    final VirtualFile jarRoot = JarFileSystem.getInstance().getJarRootForLocalFile(jarFile);
+    if ("/".equals(myPathInJar)) return jarRoot;
+    return jarRoot != null ? jarRoot.findFileByRelativePath(myPathInJar) : null;
+  }
+
+  @Override
+  public List<? extends Generator> computeAntInstructions(@NotNull PackagingElementResolvingContext resolvingContext,
+                                                          @NotNull AntCopyInstructionCreator creator,
+                                                          @NotNull ArtifactAntGenerationContext generationContext,
+                                                          @NotNull ArtifactType artifactType) {
+    final String jarPath = generationContext.getSubstitutedPath(myFilePath);
+    final String pathInJar = StringUtil.trimStart(myPathInJar, "/");
+    if (pathInJar.length() == 0) {
+      return Collections.singletonList(creator.createExtractedDirectoryInstruction(jarPath));
+    }
+
+    final String archiveName = PathUtil.getFileName(myFilePath);
+    final String tempDirProperty = generationContext.createNewTempFileProperty("temp.unpacked.path." + archiveName, archiveName);
+    final String tempDirPath = BuildProperties.propertyRef(tempDirProperty);
+    generationContext.runBeforeCurrentArtifact(new Mkdir(tempDirPath));
+
+    final Unzip unzip = new Unzip(jarPath, tempDirPath);
+    final PatternSet patterns = new PatternSet(null);
+    patterns.add(new Include(pathInJar + "**"));
+    unzip.add(patterns);
+    generationContext.runBeforeCurrentArtifact(unzip);
+
+    return Collections.singletonList(creator.createDirectoryContentCopyInstruction(tempDirPath + "/" + pathInJar));
+  }
+
+
+  @Override
+  public void computeIncrementalCompilerInstructions(@NotNull IncrementalCompilerInstructionCreator creator,
+                                                     @NotNull PackagingElementResolvingContext resolvingContext,
+                                                     @NotNull ArtifactIncrementalCompilerContext compilerContext,
+                                                     @NotNull ArtifactType artifactType) {
+    final VirtualFile file = findFile();
+    if (file != null && file.isValid() && file.isDirectory()) {
+      creator.addDirectoryCopyInstructions(file);
+    }
+  }
+
+  @Override
+  public boolean isEqualTo(@NotNull PackagingElement<?> element) {
+    return element instanceof ExtractedDirectoryPackagingElement && super.isEqualTo(element)
+           && Comparing.equal(myPathInJar, ((ExtractedDirectoryPackagingElement)element).getPathInJar());
+  }
+
+  @Override
+  public ExtractedDirectoryPackagingElement getState() {
+    return this;
+  }
+
+  @Override
+  public void loadState(ExtractedDirectoryPackagingElement state) {
+    myFilePath = state.getFilePath();
+    myPathInJar = state.getPathInJar();
+  }
+
+  @Attribute("path-in-jar")
+  public String getPathInJar() {
+    return myPathInJar;
+  }
+
+  public void setPathInJar(String pathInJar) {
+    myPathInJar = pathInJar;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/FacetBasedPackagingElement.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/FacetBasedPackagingElement.java
new file mode 100644
index 0000000..a34fdaf
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/FacetBasedPackagingElement.java
@@ -0,0 +1,29 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.facet.Facet;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public interface FacetBasedPackagingElement {
+  @Nullable
+  Facet findFacet(@NotNull PackagingElementResolvingContext context);
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/FacetBasedPackagingElementType.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/FacetBasedPackagingElementType.java
new file mode 100644
index 0000000..dc0383f
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/FacetBasedPackagingElementType.java
@@ -0,0 +1,112 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.facet.Facet;
+import com.intellij.facet.FacetTypeId;
+import com.intellij.facet.FacetTypeRegistry;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.ide.util.ChooseElementsDialog;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementType;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class FacetBasedPackagingElementType<E extends PackagingElement<?>, F extends Facet> extends PackagingElementType<E> {
+  private final FacetTypeId<F> myFacetType;
+
+  protected FacetBasedPackagingElementType(@NotNull @NonNls String id, @NotNull String presentableName, FacetTypeId<F> facetType) {
+    super(id, presentableName);
+    myFacetType = facetType;
+  }
+
+  @Override
+  public boolean canCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact) {
+    return !getFacets(context).isEmpty();
+  }
+
+  @Override
+  public Icon getCreateElementIcon() {
+    return FacetTypeRegistry.getInstance().findFacetType(myFacetType).getIcon();
+  }
+
+  @NotNull
+  @Override
+  public List<? extends E> chooseAndCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact, @NotNull CompositePackagingElement<?> parent) {
+    final List<F> facets = getFacets(context);
+    ChooseFacetsDialog dialog = new ChooseFacetsDialog(context.getProject(), facets, getDialogTitle(), getDialogDescription());
+    dialog.show();
+    if (dialog.isOK()) {
+      final List<E> elements = new ArrayList<E>();
+      for (F facet : dialog.getChosenElements()) {
+        elements.add(createElement(context.getProject(), facet));
+      }
+      return elements;
+    }
+    return Collections.emptyList();
+  }
+
+  private List<F> getFacets(ArtifactEditorContext context) {
+    final Module[] modules = context.getModulesProvider().getModules();
+    final List<F> facets = new ArrayList<F>();
+    for (Module module : modules) {
+      facets.addAll(context.getFacetsProvider().getFacetsByType(module, myFacetType));
+    }
+    return facets;
+  }
+
+  protected abstract E createElement(Project project, F facet);
+
+  protected abstract String getDialogTitle();
+
+  protected abstract String getDialogDescription();
+
+  protected abstract String getItemText(F item);
+
+  @Nullable
+  protected Icon getIcon(F item) {
+    return FacetTypeRegistry.getInstance().findFacetType(myFacetType).getIcon();
+  }
+
+  private class ChooseFacetsDialog extends ChooseElementsDialog<F> {
+    private ChooseFacetsDialog(Project project, List<? extends F> items, String title, String description) {
+      super(project, items, title, description, true);
+    }
+
+    @Override
+    protected String getItemText(F item) {
+      return FacetBasedPackagingElementType.this.getItemText(item);
+    }
+
+    @Override
+    protected Icon getItemIcon(F item) {
+      return FacetBasedPackagingElementType.this.getIcon(item);
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/FileCopyElementType.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/FileCopyElementType.java
new file mode 100644
index 0000000..ee3f52a
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/FileCopyElementType.java
@@ -0,0 +1,68 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElementType;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+* @author nik
+*/
+public class FileCopyElementType extends PackagingElementType<FileCopyPackagingElement> {
+
+  FileCopyElementType() {
+    super("file-copy", "File");
+  }
+
+  @Override
+  public Icon getCreateElementIcon() {
+    return AllIcons.FileTypes.Text;
+  }
+
+  @Override
+  public boolean canCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact) {
+    return true;
+  }
+
+  @NotNull
+  public List<? extends FileCopyPackagingElement> chooseAndCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact,
+                                                                   @NotNull CompositePackagingElement<?> parent) {
+    final FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, true, true, false, true);
+    final VirtualFile[] files = FileChooser.chooseFiles(descriptor, context.getProject(), null);
+    final List<FileCopyPackagingElement> list = new ArrayList<FileCopyPackagingElement>();
+    for (VirtualFile file : files) {
+      list.add(new FileCopyPackagingElement(file.getPath()));
+    }
+    return list;
+  }
+
+  @NotNull
+  public FileCopyPackagingElement createEmpty(@NotNull Project project) {
+    return new FileCopyPackagingElement();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/FileCopyPackagingElement.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/FileCopyPackagingElement.java
new file mode 100644
index 0000000..6513c76
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/FileCopyPackagingElement.java
@@ -0,0 +1,141 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.compiler.ant.Generator;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.*;
+import com.intellij.packaging.impl.ui.FileCopyPresentation;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingElementPresentation;
+import com.intellij.util.PathUtil;
+import com.intellij.util.xmlb.annotations.Attribute;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class FileCopyPackagingElement extends FileOrDirectoryCopyPackagingElement<FileCopyPackagingElement> implements RenameablePackagingElement {
+  @NonNls public static final String OUTPUT_FILE_NAME_ATTRIBUTE = "output-file-name";
+  private String myRenamedOutputFileName;
+
+  public FileCopyPackagingElement() {
+    super(PackagingElementFactoryImpl.FILE_COPY_ELEMENT_TYPE);
+  }
+
+  public FileCopyPackagingElement(String filePath) {
+    this();
+    myFilePath = filePath;
+  }
+
+  public FileCopyPackagingElement(String filePath, String outputFileName) {
+    this(filePath);
+    myRenamedOutputFileName = outputFileName;
+  }
+
+  public PackagingElementPresentation createPresentation(@NotNull ArtifactEditorContext context) {
+    return new FileCopyPresentation(myFilePath, getOutputFileName());
+  }
+
+  @Override
+  public List<? extends Generator> computeAntInstructions(@NotNull PackagingElementResolvingContext resolvingContext, @NotNull AntCopyInstructionCreator creator,
+                                                          @NotNull ArtifactAntGenerationContext generationContext,
+                                                          @NotNull ArtifactType artifactType) {
+    if (isDirectory()) {
+      return Collections.emptyList();
+    }
+    final String path = generationContext.getSubstitutedPath(myFilePath);
+    return Collections.singletonList((Generator)creator.createFileCopyInstruction(path, getOutputFileName()));
+  }
+
+  public String getOutputFileName() {
+    return myRenamedOutputFileName != null ? myRenamedOutputFileName : PathUtil.getFileName(myFilePath);
+  }
+
+  @Override
+  public void computeIncrementalCompilerInstructions(@NotNull IncrementalCompilerInstructionCreator creator,
+                                                     @NotNull PackagingElementResolvingContext resolvingContext,
+                                                     @NotNull ArtifactIncrementalCompilerContext compilerContext, @NotNull ArtifactType artifactType) {
+    final VirtualFile file = findFile();
+    if (file != null && file.isValid() && !file.isDirectory()) {
+      creator.addFileCopyInstruction(file, getOutputFileName());
+    }
+  }
+
+  @NonNls @Override
+  public String toString() {
+    return "file:" + myFilePath + (myRenamedOutputFileName != null ? ",rename to:" + myRenamedOutputFileName : "");
+  }
+
+  public boolean isDirectory() {
+    return new File(FileUtil.toSystemDependentName(myFilePath)).isDirectory();
+  }
+
+
+  @Override
+  public boolean isEqualTo(@NotNull PackagingElement<?> element) {
+    return element instanceof FileCopyPackagingElement && super.isEqualTo(element)
+           && Comparing.equal(myRenamedOutputFileName, ((FileCopyPackagingElement)element).getRenamedOutputFileName());
+  }
+
+  public FileCopyPackagingElement getState() {
+    return this;
+  }
+
+  public void loadState(FileCopyPackagingElement state) {
+    setFilePath(state.getFilePath());
+    setRenamedOutputFileName(state.getRenamedOutputFileName());
+  }
+
+  @Nullable
+  @Attribute(OUTPUT_FILE_NAME_ATTRIBUTE)
+  public String getRenamedOutputFileName() {
+    return myRenamedOutputFileName;
+  }
+
+  public void setRenamedOutputFileName(String renamedOutputFileName) {
+    myRenamedOutputFileName = renamedOutputFileName;
+  }
+
+  public String getName() {
+    return getOutputFileName();
+  }
+
+  public boolean canBeRenamed() {
+    return !isDirectory();
+  }
+
+  public void rename(@NotNull String newName) {
+    myRenamedOutputFileName = newName.equals(PathUtil.getFileName(myFilePath)) ? null : newName;
+  }
+
+  @Nullable
+  public VirtualFile getLibraryRoot() {
+    final String url = VfsUtil.getUrlForLibraryRoot(new File(FileUtil.toSystemDependentName(getFilePath())));
+    return VirtualFileManager.getInstance().findFileByUrl(url);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/FileOrDirectoryCopyPackagingElement.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/FileOrDirectoryCopyPackagingElement.java
new file mode 100644
index 0000000..eca74ff
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/FileOrDirectoryCopyPackagingElement.java
@@ -0,0 +1,63 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementType;
+import com.intellij.util.xmlb.annotations.Attribute;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public abstract class FileOrDirectoryCopyPackagingElement<T extends FileOrDirectoryCopyPackagingElement> extends PackagingElement<T> {
+  @NonNls public static final String PATH_ATTRIBUTE = "path";
+  protected String myFilePath;
+
+  public FileOrDirectoryCopyPackagingElement(PackagingElementType type) {
+    super(type);
+  }
+
+  protected FileOrDirectoryCopyPackagingElement(PackagingElementType type, String filePath) {
+    super(type);
+    myFilePath = filePath;
+  }
+
+  @Nullable
+  public VirtualFile findFile() {
+    return LocalFileSystem.getInstance().findFileByPath(myFilePath);
+  }
+
+  @Override
+  public boolean isEqualTo(@NotNull PackagingElement<?> element) {
+    return element instanceof FileOrDirectoryCopyPackagingElement &&
+           myFilePath != null &&
+           myFilePath.equals(((FileOrDirectoryCopyPackagingElement)element).getFilePath());
+  }
+
+  @Attribute(PATH_ATTRIBUTE)
+  public String getFilePath() {
+    return myFilePath;
+  }
+
+  public void setFilePath(String filePath) {
+    myFilePath = filePath;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/FilePathValidator.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/FilePathValidator.java
new file mode 100644
index 0000000..c31e006
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/FilePathValidator.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.
+ */
+package com.intellij.packaging.impl.elements;
+
+import com.intellij.openapi.ui.InputValidator;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.PathUtil;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+class FilePathValidator implements InputValidator {
+  public boolean checkInput(String inputString) {
+    final List<String> fileNames = StringUtil.split(FileUtil.toSystemIndependentName(inputString), "/");
+    if (fileNames.isEmpty()) {
+      return false;
+    }
+    for (String fileName : fileNames) {
+      if (!PathUtil.isValidFileName(fileName)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  public boolean canClose(String inputString) {
+    return checkInput(inputString);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/LibraryElementType.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/LibraryElementType.java
new file mode 100644
index 0000000..591c50c
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/LibraryElementType.java
@@ -0,0 +1,82 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.ComplexPackagingElementType;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.util.PlatformIcons;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+* @author nik
+*/
+public class LibraryElementType extends ComplexPackagingElementType<LibraryPackagingElement> {
+  public static final LibraryElementType LIBRARY_ELEMENT_TYPE = new LibraryElementType();
+
+  LibraryElementType() {
+    super("library", CompilerBundle.message("element.type.name.library.files"));
+  }
+
+  @Override
+  public Icon getCreateElementIcon() {
+    return PlatformIcons.LIBRARY_ICON;
+  }
+
+  @Override
+  public boolean canCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact) {
+    return !getAllLibraries(context).isEmpty();
+  }
+
+  @NotNull
+  public List<? extends LibraryPackagingElement> chooseAndCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact,
+                                                                  @NotNull CompositePackagingElement<?> parent) {
+    final List<Library> selected = context.chooseLibraries(ProjectBundle.message("dialog.title.packaging.choose.library"));
+    final List<LibraryPackagingElement> elements = new ArrayList<LibraryPackagingElement>();
+    for (Library library : selected) {
+      elements.add(new LibraryPackagingElement(library.getTable().getTableLevel(), library.getName(), null));
+    }
+    return elements;
+  }
+
+  private static List<Library> getAllLibraries(ArtifactEditorContext context) {
+    List<Library> libraries = new ArrayList<Library>();
+    ContainerUtil.addAll(libraries, LibraryTablesRegistrar.getInstance().getLibraryTable().getLibraries());
+    ContainerUtil.addAll(libraries, LibraryTablesRegistrar.getInstance().getLibraryTable(context.getProject()).getLibraries());
+    return libraries;
+  }
+
+  @NotNull
+  public LibraryPackagingElement createEmpty(@NotNull Project project) {
+    return new LibraryPackagingElement();
+  }
+
+  @Override
+  public String getShowContentActionText() {
+    return "Show Library Files";
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/LibraryPackagingElement.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/LibraryPackagingElement.java
new file mode 100644
index 0000000..d776410
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/LibraryPackagingElement.java
@@ -0,0 +1,181 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.LibraryOrderEntry;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.ComplexPackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementOutputKind;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.impl.ui.LibraryElementPresentation;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingElementPresentation;
+import com.intellij.util.PathUtil;
+import com.intellij.util.xmlb.annotations.Attribute;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class LibraryPackagingElement extends ComplexPackagingElement<LibraryPackagingElement> {
+  @NonNls public static final String LIBRARY_NAME_ATTRIBUTE = "name";
+  @NonNls public static final String MODULE_NAME_ATTRIBUTE = "module-name";
+  @NonNls public static final String LIBRARY_LEVEL_ATTRIBUTE = "level";
+  private String myLevel;
+  private String myLibraryName;
+  private String myModuleName;
+
+  public LibraryPackagingElement() {
+    super(LibraryElementType.LIBRARY_ELEMENT_TYPE);
+  }
+
+  public LibraryPackagingElement(String level, String libraryName, String moduleName) {
+    super(LibraryElementType.LIBRARY_ELEMENT_TYPE);
+    myLevel = level;
+    myLibraryName = libraryName;
+    myModuleName = moduleName;
+  }
+
+  public List<? extends PackagingElement<?>> getSubstitution(@NotNull PackagingElementResolvingContext context, @NotNull ArtifactType artifactType) {
+    final Library library = findLibrary(context);
+    if (library != null) {
+      final VirtualFile[] files = library.getFiles(OrderRootType.CLASSES);
+      final List<PackagingElement<?>> elements = new ArrayList<PackagingElement<?>>();
+      for (VirtualFile file : files) {
+        final String path = FileUtil.toSystemIndependentName(PathUtil.getLocalPath(file));
+        elements.add(file.isDirectory() && file.isInLocalFileSystem() ? new DirectoryCopyPackagingElement(path) : new FileCopyPackagingElement(path));
+      }
+      return elements;
+    }
+    return null;
+  }
+
+  @NotNull
+  @Override
+  public PackagingElementOutputKind getFilesKind(PackagingElementResolvingContext context) {
+    final Library library = findLibrary(context);
+    return library != null ? getKindForLibrary(library) : PackagingElementOutputKind.OTHER;
+  }
+
+  public PackagingElementPresentation createPresentation(@NotNull ArtifactEditorContext context) {
+    return new LibraryElementPresentation(myLibraryName, myLevel, myModuleName, findLibrary(context), context);
+  }
+
+  @Override
+  public boolean isEqualTo(@NotNull PackagingElement<?> element) {
+    if (!(element instanceof LibraryPackagingElement)) {
+      return false;
+    }
+
+    LibraryPackagingElement packagingElement = (LibraryPackagingElement)element;
+    return myLevel != null && myLibraryName != null && myLevel.equals(packagingElement.getLevel())
+           && myLibraryName.equals(packagingElement.getLibraryName())
+           && Comparing.equal(myModuleName, packagingElement.getModuleName());
+  }
+
+  public LibraryPackagingElement getState() {
+    return this;
+  }
+
+  public void loadState(LibraryPackagingElement state) {
+    myLevel = state.getLevel();
+    myLibraryName = state.getLibraryName();
+    myModuleName = state.getModuleName();
+  }
+
+  @Attribute(LIBRARY_LEVEL_ATTRIBUTE)
+  public String getLevel() {
+    return myLevel;
+  }
+
+  public void setLevel(String level) {
+    myLevel = level;
+  }
+
+  @Attribute(LIBRARY_NAME_ATTRIBUTE)
+  public String getLibraryName() {
+    return myLibraryName;
+  }
+
+  public void setLibraryName(String libraryName) {
+    myLibraryName = libraryName;
+  }
+
+  @Attribute(MODULE_NAME_ATTRIBUTE)
+  public String getModuleName() {
+    return myModuleName;
+  }
+
+  public void setModuleName(String moduleName) {
+    myModuleName = moduleName;
+  }
+
+  @Override
+  public String toString() {
+    return "lib:" + myLibraryName + "(" + (myModuleName != null ? "module " + myModuleName: myLevel ) + ")";
+  }
+
+  @Nullable
+  public Library findLibrary(@NotNull PackagingElementResolvingContext context) {
+    if (myModuleName == null) {
+      return context.findLibrary(myLevel, myLibraryName);
+    }
+    final ModulesProvider modulesProvider = context.getModulesProvider();
+    final Module module = modulesProvider.getModule(myModuleName);
+    if (module != null) {
+      for (OrderEntry entry : modulesProvider.getRootModel(module).getOrderEntries()) {
+        if (entry instanceof LibraryOrderEntry) {
+          final LibraryOrderEntry libraryEntry = (LibraryOrderEntry)entry;
+          if (libraryEntry.isModuleLevel()) {
+            final String libraryName = libraryEntry.getLibraryName();
+            if (libraryName != null && libraryName.equals(myLibraryName)) {
+              return libraryEntry.getLibrary();
+            }
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  public static PackagingElementOutputKind getKindForLibrary(final Library library) {
+    boolean containsDirectories = false;
+    boolean containsJars = false;
+    for (VirtualFile file : library.getFiles(OrderRootType.CLASSES)) {
+      if (file.isInLocalFileSystem()) {
+        containsDirectories = true;
+      }
+      else {
+        containsJars = true;
+      }
+    }
+    return new PackagingElementOutputKind(containsDirectories, containsJars);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ManifestFileUtil.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ManifestFileUtil.java
new file mode 100644
index 0000000..4aeda08
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ManifestFileUtil.java
@@ -0,0 +1,361 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.CommonBundle;
+import com.intellij.ide.util.ClassFilter;
+import com.intellij.ide.util.TreeClassChooser;
+import com.intellij.ide.util.TreeClassChooserFactory;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.compiler.make.ManifestBuilder;
+import com.intellij.openapi.deployment.DeploymentUtil;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.OrderEnumerator;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementFactory;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.impl.artifacts.PackagingElementPath;
+import com.intellij.packaging.impl.artifacts.PackagingElementProcessor;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.ManifestFileConfiguration;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.util.PsiMethodUtil;
+import com.intellij.util.PathUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+/**
+ * @author nik
+ */
+public class ManifestFileUtil {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactEditorContextImpl");
+  public static final String MANIFEST_PATH = JarFile.MANIFEST_NAME;
+  public static final String MANIFEST_FILE_NAME = PathUtil.getFileName(MANIFEST_PATH);
+  public static final String MANIFEST_DIR_NAME = PathUtil.getParentPath(MANIFEST_PATH);
+
+  private ManifestFileUtil() {
+  }
+
+  @Nullable
+  public static VirtualFile findManifestFile(@NotNull CompositePackagingElement<?> root, PackagingElementResolvingContext context, ArtifactType artifactType) {
+    return ArtifactUtil.findSourceFileByOutputPath(root, MANIFEST_PATH, context, artifactType);
+  }
+
+  @Nullable
+  public static VirtualFile suggestManifestFileDirectory(@NotNull CompositePackagingElement<?> root, PackagingElementResolvingContext context, ArtifactType artifactType) {
+    final VirtualFile metaInfDir = ArtifactUtil.findSourceFileByOutputPath(root, MANIFEST_DIR_NAME, context, artifactType);
+    if (metaInfDir != null) {
+      return metaInfDir;
+    }
+
+    final Ref<VirtualFile> sourceDir = Ref.create(null);
+    final Ref<VirtualFile> sourceFile = Ref.create(null);
+    ArtifactUtil.processElementsWithSubstitutions(root.getChildren(), context, artifactType, PackagingElementPath.EMPTY, new PackagingElementProcessor<PackagingElement<?>>() {
+      @Override
+      public boolean process(@NotNull PackagingElement<?> element, @NotNull PackagingElementPath path) {
+        if (element instanceof FileCopyPackagingElement) {
+          final VirtualFile file = ((FileCopyPackagingElement)element).findFile();
+          if (file != null) {
+            sourceFile.set(file);
+          }
+        }
+        else if (element instanceof DirectoryCopyPackagingElement) {
+          final VirtualFile file = ((DirectoryCopyPackagingElement)element).findFile();
+          if (file != null) {
+            sourceDir.set(file);
+            return false;
+          }
+        }
+        return true;
+      }
+    });
+
+    if (!sourceDir.isNull()) {
+      return sourceDir.get();
+    }
+
+
+    final Project project = context.getProject();
+    return suggestBaseDir(project, sourceFile.get());
+  }
+
+  @Nullable
+  public static VirtualFile suggestManifestFileDirectory(@NotNull Project project, @Nullable Module module) {
+    OrderEnumerator enumerator = module != null ? OrderEnumerator.orderEntries(module) : OrderEnumerator.orderEntries(project);
+    final VirtualFile[] files = enumerator.withoutDepModules().withoutLibraries().withoutSdk().productionOnly().sources().getRoots();
+    if (files.length > 0) {
+      return files[0];
+    }
+    return suggestBaseDir(project, null);
+  }
+
+
+  @Nullable
+  private static VirtualFile suggestBaseDir(@NotNull Project project, final @Nullable VirtualFile file) {
+    final VirtualFile[] contentRoots = ProjectRootManager.getInstance(project).getContentRoots();
+    if (file == null && contentRoots.length > 0) {
+      return contentRoots[0];
+    }
+
+    if (file != null) {
+      for (VirtualFile contentRoot : contentRoots) {
+        if (VfsUtil.isAncestor(contentRoot, file, false)) {
+          return contentRoot;
+        }
+      }
+    }
+
+    return project.getBaseDir();
+  }
+
+  public static Manifest readManifest(@NotNull VirtualFile manifestFile) {
+    try {
+      final InputStream inputStream = manifestFile.getInputStream();
+      final Manifest manifest;
+      try {
+        manifest = new Manifest(inputStream);
+      }
+      finally {
+        inputStream.close();
+      }
+      return manifest;
+    }
+    catch (IOException ignored) {
+      return new Manifest();
+    }
+  }
+
+  public static void updateManifest(@NotNull VirtualFile file, final @Nullable String mainClass, final @Nullable List<String> classpath, final boolean replaceValues) {
+    final Manifest manifest = readManifest(file);
+    final Attributes mainAttributes = manifest.getMainAttributes();
+
+    if (mainClass != null) {
+      mainAttributes.put(Attributes.Name.MAIN_CLASS, mainClass);
+    }
+    else if (replaceValues) {
+      mainAttributes.remove(Attributes.Name.MAIN_CLASS);
+    }
+
+    if (classpath != null && !classpath.isEmpty()) {
+      List<String> updatedClasspath;
+      if (replaceValues) {
+        updatedClasspath = classpath;
+      }
+      else {
+        updatedClasspath = new ArrayList<String>();
+        final String oldClasspath = (String)mainAttributes.get(Attributes.Name.CLASS_PATH);
+        if (!StringUtil.isEmpty(oldClasspath)) {
+          updatedClasspath.addAll(StringUtil.split(oldClasspath, " "));
+        }
+        for (String path : classpath) {
+          if (!updatedClasspath.contains(path)) {
+            updatedClasspath.add(path);
+          }
+        }
+      }
+      mainAttributes.put(Attributes.Name.CLASS_PATH, StringUtil.join(updatedClasspath, " "));
+    }
+    else if (replaceValues) {
+      mainAttributes.remove(Attributes.Name.CLASS_PATH);
+    }
+
+    ManifestBuilder.setVersionAttribute(mainAttributes);
+
+    try {
+      final OutputStream outputStream = file.getOutputStream(ManifestFileUtil.class);
+      try {
+        manifest.write(outputStream);
+      }
+      finally {
+        outputStream.close();
+      }
+    }
+    catch (IOException e) {
+      LOG.info(e);
+    }
+  }
+
+  @NotNull
+  public static ManifestFileConfiguration createManifestFileConfiguration(@NotNull VirtualFile manifestFile) {
+    final String path = manifestFile.getPath();
+    Manifest manifest = readManifest(manifestFile);
+    String mainClass = manifest.getMainAttributes().getValue(Attributes.Name.MAIN_CLASS);
+    final String classpathText = manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
+    final List<String> classpath = new ArrayList<String>();
+    if (classpathText != null) {
+      classpath.addAll(StringUtil.split(classpathText, " "));
+    }
+    return new ManifestFileConfiguration(path, classpath, mainClass, manifestFile.isWritable());
+  }
+
+  public static List<String> getClasspathForElements(List<? extends PackagingElement<?>> elements, PackagingElementResolvingContext context, final ArtifactType artifactType) {
+    final List<String> classpath = new ArrayList<String>();
+    final PackagingElementProcessor<PackagingElement<?>> processor = new PackagingElementProcessor<PackagingElement<?>>() {
+      @Override
+      public boolean process(@NotNull PackagingElement<?> element, @NotNull PackagingElementPath path) {
+        if (element instanceof FileCopyPackagingElement) {
+          final String fileName = ((FileCopyPackagingElement)element).getOutputFileName();
+          classpath.add(DeploymentUtil.appendToPath(path.getPathString(), fileName));
+        }
+        else if (element instanceof DirectoryCopyPackagingElement) {
+          classpath.add(path.getPathString());
+        }
+        else if (element instanceof ArchivePackagingElement) {
+          final String archiveName = ((ArchivePackagingElement)element).getName();
+          classpath.add(DeploymentUtil.appendToPath(path.getPathString(), archiveName));
+        }
+        return true;
+      }
+    };
+    for (PackagingElement<?> element : elements) {
+      ArtifactUtil.processPackagingElements(element, null, processor, context, true, artifactType);
+    }
+    return classpath;
+  }
+
+  @Nullable
+  public static VirtualFile showDialogAndCreateManifest(final ArtifactEditorContext context, final CompositePackagingElement<?> element) {
+    FileChooserDescriptor descriptor = createDescriptorForManifestDirectory();
+    final VirtualFile directory = suggestManifestFileDirectory(element, context, context.getArtifactType());
+    final VirtualFile file = FileChooser.chooseFile(descriptor, context.getProject(), directory);
+    if (file == null) {
+      return null;
+    }
+
+    return createManifestFile(file, context.getProject());
+  }
+
+  @Nullable
+  public static VirtualFile createManifestFile(final @NotNull VirtualFile directory, final @NotNull Project project) {
+    ApplicationManager.getApplication().assertIsDispatchThread();
+    final Ref<IOException> exc = Ref.create(null);
+    final VirtualFile file = new WriteAction<VirtualFile>() {
+      protected void run(final Result<VirtualFile> result) {
+        VirtualFile dir = directory;
+        try {
+          if (!dir.getName().equals(MANIFEST_DIR_NAME)) {
+            dir = VfsUtil.createDirectoryIfMissing(dir, MANIFEST_DIR_NAME);
+          }
+          final VirtualFile file = dir.createChildData(this, MANIFEST_FILE_NAME);
+          final OutputStream output = file.getOutputStream(this);
+          try {
+            final Manifest manifest = new Manifest();
+            ManifestBuilder.setVersionAttribute(manifest.getMainAttributes());
+            manifest.write(output);
+          }
+          finally {
+            output.close();
+          }
+          result.setResult(file);
+        }
+        catch (IOException e) {
+          exc.set(e);
+        }
+      }
+    }.execute().getResultObject();
+
+    final IOException exception = exc.get();
+    if (exception != null) {
+      LOG.info(exception);
+      Messages.showErrorDialog(project, exception.getMessage(), CommonBundle.getErrorTitle());
+      return null;
+    }
+    return file;
+  }
+
+  public static FileChooserDescriptor createDescriptorForManifestDirectory() {
+    FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
+    descriptor.setTitle("Select Directory for META-INF/MANIFEST.MF file");
+    return descriptor;
+  }
+
+  public static void addManifestFileToLayout(final @NotNull String path, final @NotNull ArtifactEditorContext context,
+                                             final @NotNull CompositePackagingElement<?> element) {
+    context.editLayout(context.getArtifact(), new Runnable() {
+      public void run() {
+        final VirtualFile file = findManifestFile(element, context, context.getArtifactType());
+        if (file == null || !FileUtil.pathsEqual(file.getPath(), path)) {
+          PackagingElementFactory.getInstance().addFileCopy(element, MANIFEST_DIR_NAME, path, MANIFEST_FILE_NAME);
+        }
+      }
+    });
+  }
+
+  @Nullable
+  public static PsiClass selectMainClass(Project project, final @Nullable String initialClassName) {
+    final TreeClassChooserFactory chooserFactory = TreeClassChooserFactory.getInstance(project);
+    final GlobalSearchScope searchScope = GlobalSearchScope.allScope(project);
+    final PsiClass aClass = initialClassName != null ? JavaPsiFacade.getInstance(project).findClass(initialClassName, searchScope) : null;
+    final TreeClassChooser chooser =
+        chooserFactory.createWithInnerClassesScopeChooser("Select Main Class", searchScope, new MainClassFilter(), aClass);
+    chooser.showDialog();
+    return chooser.getSelected();
+  }
+
+  public static void setupMainClassField(final Project project, final TextFieldWithBrowseButton field) {
+    field.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        final PsiClass selected = selectMainClass(project, field.getText());
+        if (selected != null) {
+          field.setText(selected.getQualifiedName());
+        }
+      }
+    });
+  }
+
+  private static class MainClassFilter implements ClassFilter {
+    public boolean isAccepted(final PsiClass aClass) {
+      return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
+        @Override
+        public Boolean compute() {
+          return PsiMethodUtil.MAIN_CLASS.value(aClass) && PsiMethodUtil.hasMainMethod(aClass);
+        }
+      });
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ModuleOutputElementTypeBase.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ModuleOutputElementTypeBase.java
new file mode 100644
index 0000000..6fed025
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ModuleOutputElementTypeBase.java
@@ -0,0 +1,75 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModulePointer;
+import com.intellij.openapi.module.ModulePointerManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementType;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class ModuleOutputElementTypeBase<E extends ModuleOutputPackagingElementBase> extends PackagingElementType<E> {
+  public ModuleOutputElementTypeBase(String id, String presentableName) {
+    super(id, presentableName);
+  }
+
+  @Override
+  public boolean canCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact) {
+    return !getSuitableModules(context).isEmpty();
+  }
+
+  @NotNull
+  public List<? extends PackagingElement<?>> chooseAndCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact,
+                                                                       @NotNull CompositePackagingElement<?> parent) {
+    List<Module> suitableModules = getSuitableModules(context);
+    List<Module> selected = context.chooseModules(suitableModules, ProjectBundle.message("dialog.title.packaging.choose.module"));
+
+    final List<PackagingElement<?>> elements = new ArrayList<PackagingElement<?>>();
+    final ModulePointerManager pointerManager = ModulePointerManager.getInstance(context.getProject());
+    for (Module module : selected) {
+      elements.add(createElement(context.getProject(), pointerManager.create(module)));
+    }
+    return elements;
+  }
+
+  protected abstract ModuleOutputPackagingElementBase createElement(@NotNull Project project, @NotNull ModulePointer pointer);
+
+  private List<Module> getSuitableModules(ArtifactEditorContext context) {
+    ModulesProvider modulesProvider = context.getModulesProvider();
+    ArrayList<Module> modules = new ArrayList<Module>();
+    for (Module module : modulesProvider.getModules()) {
+      if (isSuitableModule(modulesProvider, module)) {
+        modules.add(module);
+      }
+    }
+    return modules;
+  }
+
+  public abstract boolean isSuitableModule(ModulesProvider modulesProvider, Module module);
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ModuleOutputPackagingElement.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ModuleOutputPackagingElement.java
new file mode 100644
index 0000000..d73572d
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ModuleOutputPackagingElement.java
@@ -0,0 +1,38 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+
+/**
+ * @author nik
+ */
+public interface ModuleOutputPackagingElement {
+  @Nullable
+  String getModuleName();
+
+  @Nullable
+  Module findModule(PackagingElementResolvingContext context);
+
+  @NotNull
+  Collection<VirtualFile> getSourceRoots(PackagingElementResolvingContext context);
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ModuleOutputPackagingElementBase.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ModuleOutputPackagingElementBase.java
new file mode 100644
index 0000000..084cf6e
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ModuleOutputPackagingElementBase.java
@@ -0,0 +1,150 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.compiler.ant.BuildProperties;
+import com.intellij.compiler.ant.Generator;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModulePointer;
+import com.intellij.openapi.module.ModulePointerManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.CompilerModuleExtension;
+import com.intellij.openapi.roots.ui.configuration.DefaultModulesProvider;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.elements.*;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.xmlb.annotations.Attribute;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class ModuleOutputPackagingElementBase extends PackagingElement<ModuleOutputPackagingElementBase.ModuleOutputPackagingElementState> implements ModuleOutputPackagingElement {
+  @NonNls public static final String MODULE_NAME_ATTRIBUTE = "name";
+  protected ModulePointer myModulePointer;
+  protected final Project myProject;
+
+  public ModuleOutputPackagingElementBase(PackagingElementType type, Project project, ModulePointer modulePointer) {
+    super(type);
+    myProject = project;
+    myModulePointer = modulePointer;
+  }
+
+  public ModuleOutputPackagingElementBase(PackagingElementType type, Project project) {
+    super(type);
+    myProject = project;
+  }
+
+  @Override
+  public List<? extends Generator> computeAntInstructions(@NotNull PackagingElementResolvingContext resolvingContext, @NotNull AntCopyInstructionCreator creator,
+                                                          @NotNull ArtifactAntGenerationContext generationContext,
+                                                          @NotNull ArtifactType artifactType) {
+    if (myModulePointer != null) {
+      final String moduleOutput = BuildProperties.propertyRef(getModuleOutputAntProperty(generationContext));
+      return Collections.singletonList(creator.createDirectoryContentCopyInstruction(moduleOutput));
+    }
+    return Collections.emptyList();
+  }
+
+  protected abstract String getModuleOutputAntProperty(ArtifactAntGenerationContext generationContext);
+
+  @Override
+  public void computeIncrementalCompilerInstructions(@NotNull IncrementalCompilerInstructionCreator creator,
+                                                     @NotNull PackagingElementResolvingContext resolvingContext,
+                                                     @NotNull ArtifactIncrementalCompilerContext compilerContext, @NotNull ArtifactType artifactType) {
+    final Module module = findModule(resolvingContext);
+    if (module != null) {
+      final CompilerModuleExtension extension = CompilerModuleExtension.getInstance(module);
+      if (extension != null) {
+        final VirtualFile output = getModuleOutputPath(extension);
+        if (output != null) {
+          creator.addDirectoryCopyInstructions(output, null);
+        }
+      }
+    }
+  }
+
+  @Nullable
+  protected abstract VirtualFile getModuleOutputPath(CompilerModuleExtension extension);
+
+  @NotNull
+  @Override
+  public PackagingElementOutputKind getFilesKind(PackagingElementResolvingContext context) {
+    return PackagingElementOutputKind.DIRECTORIES_WITH_CLASSES;
+  }
+
+  @Override
+  public boolean isEqualTo(@NotNull PackagingElement<?> element) {
+    return element.getClass() == getClass() && myModulePointer != null
+           && myModulePointer.equals(((ModuleOutputPackagingElementBase)element).myModulePointer);
+  }
+
+  public ModuleOutputPackagingElementState getState() {
+    final ModuleOutputPackagingElementState state = new ModuleOutputPackagingElementState();
+    if (myModulePointer != null) {
+      state.setModuleName(myModulePointer.getModuleName());
+    }
+    return state;
+  }
+
+  public void loadState(ModuleOutputPackagingElementState state) {
+    final String moduleName = state.getModuleName();
+    myModulePointer = moduleName != null ? ModulePointerManager.getInstance(myProject).create(moduleName) : null;
+  }
+
+  @Override
+  @Nullable
+  public String getModuleName() {
+    return myModulePointer != null ? myModulePointer.getModuleName() : null;
+  }
+
+  @Override
+  @Nullable
+  public Module findModule(PackagingElementResolvingContext context) {
+    if (myModulePointer != null) {
+      final Module module = myModulePointer.getModule();
+      final ModulesProvider modulesProvider = context.getModulesProvider();
+      if (module != null) {
+        if (modulesProvider instanceof DefaultModulesProvider//optimization
+           || ArrayUtil.contains(module, modulesProvider.getModules())) {
+          return module;
+        }
+      }
+      return modulesProvider.getModule(myModulePointer.getModuleName());
+    }
+    return null;
+  }
+
+  public static class ModuleOutputPackagingElementState {
+    private String myModuleName;
+
+    @Attribute(MODULE_NAME_ATTRIBUTE)
+    public String getModuleName() {
+      return myModuleName;
+    }
+
+    public void setModuleName(String moduleName) {
+      myModuleName = moduleName;
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/PackagingElementFactoryImpl.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/PackagingElementFactoryImpl.java
new file mode 100644
index 0000000..0d1737b
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/PackagingElementFactoryImpl.java
@@ -0,0 +1,354 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModulePointer;
+import com.intellij.openapi.module.ModulePointerManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.impl.libraries.LibraryImpl;
+import com.intellij.openapi.roots.impl.libraries.LibraryTableImplUtil;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactPointer;
+import com.intellij.packaging.artifacts.ArtifactPointerManager;
+import com.intellij.packaging.elements.*;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.PathUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class PackagingElementFactoryImpl extends PackagingElementFactory {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.packaging.impl.elements.PackagingElementFactoryImpl");
+  public static final PackagingElementType<DirectoryPackagingElement> DIRECTORY_ELEMENT_TYPE = new DirectoryElementType();
+  public static final PackagingElementType<ArchivePackagingElement> ARCHIVE_ELEMENT_TYPE = new ArchiveElementType();
+  public static final PackagingElementType<FileCopyPackagingElement> FILE_COPY_ELEMENT_TYPE = new FileCopyElementType();
+  public static final PackagingElementType<DirectoryCopyPackagingElement> DIRECTORY_COPY_ELEMENT_TYPE = new DirectoryCopyElementType();
+  public static final PackagingElementType<ExtractedDirectoryPackagingElement> EXTRACTED_DIRECTORY_ELEMENT_TYPE = new ExtractedDirectoryElementType();
+  public static final PackagingElementType<ArtifactRootElement<?>> ARTIFACT_ROOT_ELEMENT_TYPE = new ArtifactRootElementType();
+  private static final PackagingElementType[] STANDARD_TYPES = {
+      DIRECTORY_ELEMENT_TYPE, ARCHIVE_ELEMENT_TYPE,
+      LibraryElementType.LIBRARY_ELEMENT_TYPE, ProductionModuleOutputElementType.ELEMENT_TYPE, TestModuleOutputElementType.ELEMENT_TYPE,
+      ArtifactElementType.ARTIFACT_ELEMENT_TYPE, FILE_COPY_ELEMENT_TYPE, DIRECTORY_COPY_ELEMENT_TYPE, EXTRACTED_DIRECTORY_ELEMENT_TYPE
+  };
+
+  @NotNull
+  @Override
+  public PackagingElementType<?>[] getNonCompositeElementTypes() {
+    final List<PackagingElementType> elementTypes = new ArrayList<PackagingElementType>();
+    for (PackagingElementType elementType : getAllElementTypes()) {
+      if (!(elementType instanceof CompositePackagingElementType)) {
+        elementTypes.add(elementType);
+      }
+    }
+    return elementTypes.toArray(new PackagingElementType[elementTypes.size()]);
+  }
+
+  @Override
+  @NotNull
+  public ComplexPackagingElementType<?>[] getComplexElementTypes() {
+    List<ComplexPackagingElementType<?>> types = new ArrayList<ComplexPackagingElementType<?>>();
+    for (PackagingElementType type : getAllElementTypes()) {
+      if (type instanceof ComplexPackagingElementType) {
+        types.add((ComplexPackagingElementType)type);
+      }
+    }
+    return types.toArray(new ComplexPackagingElementType[types.size()]);
+  }
+
+  @NotNull
+  @Override
+  public CompositePackagingElementType<?>[] getCompositeElementTypes() {
+    final List<CompositePackagingElementType> elementTypes = new ArrayList<CompositePackagingElementType>();
+    for (PackagingElementType elementType : getAllElementTypes()) {
+      if (elementType instanceof CompositePackagingElementType) {
+        elementTypes.add((CompositePackagingElementType)elementType);
+      }
+    }
+    return elementTypes.toArray(new CompositePackagingElementType[elementTypes.size()]);
+  }
+
+  @Override
+  public PackagingElementType<?> findElementType(String id) {
+    for (PackagingElementType elementType : getAllElementTypes()) {
+      if (elementType.getId().equals(id)) {
+        return elementType;
+      }
+    }
+    if (id.equals(ARTIFACT_ROOT_ELEMENT_TYPE.getId())) {
+      return ARTIFACT_ROOT_ELEMENT_TYPE;
+    }
+    return null;
+  }
+
+  @NotNull
+  @Override
+  public PackagingElementType[] getAllElementTypes() {
+    final PackagingElementType[] types = Extensions.getExtensions(PackagingElementType.EP_NAME);
+    return ArrayUtil.mergeArrays(STANDARD_TYPES, types);
+  }
+
+  @NotNull
+  @Override
+  public PackagingElement<?> createArtifactElement(@NotNull Artifact artifact, @NotNull Project project) {
+    return new ArtifactPackagingElement(project, ArtifactPointerManager.getInstance(project).createPointer(artifact));
+  }
+
+  @NotNull
+  public DirectoryPackagingElement createDirectory(@NotNull @NonNls String directoryName) {
+    return new DirectoryPackagingElement(directoryName);
+  }
+
+  @NotNull
+  @Override
+  public ArtifactRootElement<?> createArtifactRootElement() {
+    return new ArtifactRootElementImpl();
+  }
+
+  @Override
+  @NotNull
+  public CompositePackagingElement<?> getOrCreateDirectory(@NotNull CompositePackagingElement<?> parent, @NotNull String relativePath) {
+    return getOrCreateDirectoryOrArchive(parent, relativePath, true);
+  }
+
+  @NotNull
+  @Override
+  public CompositePackagingElement<?> getOrCreateArchive(@NotNull CompositePackagingElement<?> parent, @NotNull String relativePath) {
+    return getOrCreateDirectoryOrArchive(parent, relativePath, false);
+  }
+
+  @Override
+  public void addFileCopy(@NotNull CompositePackagingElement<?> root, @NotNull String outputDirectoryPath, @NotNull String sourceFilePath) {
+    addFileCopy(root, outputDirectoryPath, sourceFilePath, null);
+  }
+
+  @Override
+  public void addFileCopy(@NotNull CompositePackagingElement<?> root, @NotNull String outputDirectoryPath, @NotNull String sourceFilePath,
+                          @Nullable String outputFileName) {
+    final String fileName = PathUtil.getFileName(sourceFilePath);
+    if (outputFileName != null && outputFileName.equals(fileName)) {
+      outputFileName = null;
+    }
+    getOrCreateDirectory(root, outputDirectoryPath).addOrFindChild(createFileCopy(sourceFilePath, outputFileName));
+  }
+
+  @NotNull
+  private CompositePackagingElement<?> getOrCreateDirectoryOrArchive(@NotNull CompositePackagingElement<?> root,
+                                                                     @NotNull @NonNls String path, final boolean directory) {
+    path = StringUtil.trimStart(StringUtil.trimEnd(path, "/"), "/");
+    if (path.length() == 0) {
+      return root;
+    }
+    int index = path.lastIndexOf('/');
+    String lastName = path.substring(index + 1);
+    String parentPath = index != -1 ? path.substring(0, index) : "";
+
+    final CompositePackagingElement<?> parent = getOrCreateDirectoryOrArchive(root, parentPath, true);
+    final CompositePackagingElement<?> last = directory ? createDirectory(lastName) : createArchive(lastName);
+    return parent.addOrFindChild(last);
+  }
+
+  @NotNull
+  public PackagingElement<?> createModuleOutput(@NotNull String moduleName, @NotNull Project project) {
+    final ModulePointer pointer = ModulePointerManager.getInstance(project).create(moduleName);
+    return new ProductionModuleOutputPackagingElement(project, pointer);
+  }
+
+  @NotNull
+  @Override
+  public PackagingElement<?> createModuleOutput(@NotNull Module module) {
+    final ModulePointer modulePointer = ModulePointerManager.getInstance(module.getProject()).create(module);
+    return new ProductionModuleOutputPackagingElement(module.getProject(), modulePointer);
+  }
+
+  @NotNull
+  @Override
+  public PackagingElement<?> createTestModuleOutput(@NotNull Module module) {
+    ModulePointer pointer = ModulePointerManager.getInstance(module.getProject()).create(module);
+    return new TestModuleOutputPackagingElement(module.getProject(), pointer);
+  }
+
+  @NotNull
+  @Override
+  public List<? extends PackagingElement<?>> createLibraryElements(@NotNull Library library) {
+    final LibraryTable table = library.getTable();
+    final String libraryName = library.getName();
+    if (table != null) {
+      return Collections.singletonList(createLibraryFiles(libraryName, table.getTableLevel(), null));
+    }
+    if (libraryName != null) {
+      final Module module = ((LibraryImpl)library).getModule();
+      if (module != null) {
+        return Collections.singletonList(createLibraryFiles(libraryName, LibraryTableImplUtil.MODULE_LEVEL, module.getName()));
+      }
+    }
+    final List<PackagingElement<?>> elements = new ArrayList<PackagingElement<?>>();
+    for (VirtualFile file : library.getFiles(OrderRootType.CLASSES)) {
+      final String path = FileUtil.toSystemIndependentName(PathUtil.getLocalPath(file));
+      elements.add(file.isDirectory() && file.isInLocalFileSystem() ? new DirectoryCopyPackagingElement(path) : new FileCopyPackagingElement(path));
+    }
+    return elements;
+  }
+
+  @NotNull
+  @Override
+  public PackagingElement<?> createArtifactElement(@NotNull ArtifactPointer artifactPointer, @NotNull Project project) {
+    return new ArtifactPackagingElement(project, artifactPointer);
+  }
+
+  @NotNull
+  @Override
+  public PackagingElement<?> createLibraryFiles(@NotNull String libraryName, @NotNull String level, String moduleName) {
+    return new LibraryPackagingElement(level, libraryName, moduleName);
+  }
+
+  @NotNull
+  public CompositePackagingElement<?> createArchive(@NotNull @NonNls String archiveFileName) {
+    return new ArchivePackagingElement(archiveFileName);
+  }
+
+  @Nullable
+  private static PackagingElement<?> findArchiveOrDirectoryByName(@NotNull CompositePackagingElement<?> parent, @NotNull String name) {
+    for (PackagingElement<?> element : parent.getChildren()) {
+      if (element instanceof ArchivePackagingElement && ((ArchivePackagingElement)element).getArchiveFileName().equals(name) ||
+          element instanceof DirectoryPackagingElement && ((DirectoryPackagingElement)element).getDirectoryName().equals(name)) {
+        return element;
+      }
+    }
+    return null;
+  }
+
+  @NotNull
+  public static String suggestFileName(@NotNull CompositePackagingElement<?> parent, @NonNls @NotNull String prefix, @NonNls @NotNull String suffix) {
+    String name = prefix + suffix;
+    int i = 2;
+    while (findArchiveOrDirectoryByName(parent, name) != null) {
+      name = prefix + i++ + suffix;
+    }
+    return name;
+  }
+
+  @NotNull
+  @Override
+  public PackagingElement<?> createDirectoryCopyWithParentDirectories(@NotNull String filePath, @NotNull String relativeOutputPath) {
+    return createParentDirectories(relativeOutputPath, new DirectoryCopyPackagingElement(filePath));
+  }
+
+  @Override
+  @NotNull
+  public PackagingElement<?> createExtractedDirectoryWithParentDirectories(@NotNull String jarPath, @NotNull String pathInJar,
+                                                                           @NotNull String relativeOutputPath) {
+    return createParentDirectories(relativeOutputPath, new ExtractedDirectoryPackagingElement(jarPath, pathInJar));
+  }
+
+  @NotNull
+  @Override
+  public PackagingElement<?> createExtractedDirectory(@NotNull VirtualFile jarEntry) {
+    LOG.assertTrue(jarEntry.getFileSystem() instanceof JarFileSystem, "Expected file from jar but file from " + jarEntry.getFileSystem() + " found");
+    final String fullPath = jarEntry.getPath();
+    final int jarEnd = fullPath.indexOf(JarFileSystem.JAR_SEPARATOR);
+    return new ExtractedDirectoryPackagingElement(fullPath.substring(0, jarEnd), fullPath.substring(jarEnd + 1));
+  }
+
+  @NotNull
+  @Override
+  public PackagingElement<?> createFileCopyWithParentDirectories(@NotNull String filePath, @NotNull String relativeOutputPath) {
+    return createFileCopyWithParentDirectories(filePath, relativeOutputPath, null);
+  }
+
+  @NotNull
+  @Override
+  public PackagingElement<?> createFileCopyWithParentDirectories(@NotNull String filePath,
+                                                                 @NotNull String relativeOutputPath,
+                                                                 @Nullable String outputFileName) {
+    return createParentDirectories(relativeOutputPath, createFileCopy(filePath, outputFileName));
+  }
+
+  @Override
+  public PackagingElement<?> createFileCopy(@NotNull String filePath, String outputFileName) {
+    return new FileCopyPackagingElement(filePath, outputFileName);
+  }
+
+  @NotNull
+  @Override
+  public PackagingElement<?> createParentDirectories(@NotNull String relativeOutputPath, @NotNull PackagingElement<?> element) {
+    return createParentDirectories(relativeOutputPath, Collections.singletonList(element)).get(0);
+  }
+
+  @NotNull
+  @Override
+  public List<? extends PackagingElement<?>> createParentDirectories(@NotNull String relativeOutputPath, @NotNull List<? extends PackagingElement<?>> elements) {
+    relativeOutputPath = StringUtil.trimStart(relativeOutputPath, "/");
+    if (relativeOutputPath.length() == 0) {
+      return elements;
+    }
+    int slash = relativeOutputPath.indexOf('/');
+    if (slash == -1) slash = relativeOutputPath.length();
+    String rootName = relativeOutputPath.substring(0, slash);
+    String pathTail = relativeOutputPath.substring(slash);
+    final DirectoryPackagingElement root = createDirectory(rootName);
+    final CompositePackagingElement<?> last = getOrCreateDirectory(root, pathTail);
+    last.addOrFindChildren(elements);
+    return Collections.singletonList(root);
+  }
+
+  public static CompositePackagingElement<?> createDirectoryOrArchiveWithParents(@NotNull String path, final boolean archive) {
+    path = FileUtil.toSystemIndependentName(path);
+    final String parentPath = PathUtil.getParentPath(path);
+    final String fileName = PathUtil.getFileName(path);
+    final PackagingElement<?> element = archive ? new ArchivePackagingElement(fileName) : new DirectoryPackagingElement(fileName);
+    return (CompositePackagingElement<?>)getInstance().createParentDirectories(parentPath, element);
+  }
+
+  private static class ArtifactRootElementType extends PackagingElementType<ArtifactRootElement<?>> {
+    protected ArtifactRootElementType() {
+      super("root", "");
+    }
+
+    @Override
+    public boolean canCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact) {
+      return false;
+    }
+
+    @NotNull
+    public List<? extends ArtifactRootElement<?>> chooseAndCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact,
+                                                                   @NotNull CompositePackagingElement<?> parent) {
+      throw new UnsupportedOperationException("'create' not implemented in " + getClass().getName());
+    }
+
+    @NotNull
+    public ArtifactRootElement<?> createEmpty(@NotNull Project project) {
+      return new ArtifactRootElementImpl();
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ProductionModuleOutputElementType.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ProductionModuleOutputElementType.java
new file mode 100644
index 0000000..3ec8f0c
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ProductionModuleOutputElementType.java
@@ -0,0 +1,56 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModulePointer;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+/**
+* @author nik
+*/
+public class ProductionModuleOutputElementType extends ModuleOutputElementTypeBase<ProductionModuleOutputPackagingElement> {
+  public static final ProductionModuleOutputElementType ELEMENT_TYPE = new ProductionModuleOutputElementType();
+
+  ProductionModuleOutputElementType() {
+    super("module-output", CompilerBundle.message("element.type.name.module.output"));
+  }
+
+  @NotNull
+  public ProductionModuleOutputPackagingElement createEmpty(@NotNull Project project) {
+    return new ProductionModuleOutputPackagingElement(project);
+  }
+
+  protected ModuleOutputPackagingElementBase createElement(@NotNull Project project, @NotNull ModulePointer pointer) {
+    return new ProductionModuleOutputPackagingElement(project, pointer);
+  }
+
+  @Override
+  public Icon getCreateElementIcon() {
+    return AllIcons.Nodes.Module;
+  }
+
+  @Override
+  public boolean isSuitableModule(ModulesProvider modulesProvider, Module module) {
+    return modulesProvider.getRootModel(module).getSourceRootUrls(false).length > 0;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/ProductionModuleOutputPackagingElement.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ProductionModuleOutputPackagingElement.java
new file mode 100644
index 0000000..6ee8e8c
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/ProductionModuleOutputPackagingElement.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.
+ */
+package com.intellij.packaging.impl.elements;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModulePointer;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.CompilerModuleExtension;
+import com.intellij.openapi.roots.ModuleRootModel;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.elements.ArtifactAntGenerationContext;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.impl.ui.DelegatedPackagingElementPresentation;
+import com.intellij.packaging.impl.ui.ModuleElementPresentation;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingElementPresentation;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * @author nik
+ */
+public class ProductionModuleOutputPackagingElement extends ModuleOutputPackagingElementBase {
+  public ProductionModuleOutputPackagingElement(@NotNull Project project) {
+    super(ProductionModuleOutputElementType.ELEMENT_TYPE, project);
+  }
+
+  public ProductionModuleOutputPackagingElement(@NotNull Project project, @NotNull ModulePointer modulePointer) {
+    super(ProductionModuleOutputElementType.ELEMENT_TYPE, project, modulePointer);
+  }
+
+  @NonNls @Override
+  public String toString() {
+    return "module:" + getModuleName();
+  }
+
+  protected String getModuleOutputAntProperty(ArtifactAntGenerationContext generationContext) {
+    return generationContext.getModuleOutputPath(myModulePointer.getModuleName());
+  }
+
+  protected VirtualFile getModuleOutputPath(CompilerModuleExtension extension) {
+    return extension.getCompilerOutputPath();
+  }
+
+  @NotNull
+  @Override
+  public Collection<VirtualFile> getSourceRoots(PackagingElementResolvingContext context) {
+    Module module = findModule(context);
+    if (module == null) return Collections.emptyList();
+
+    ModuleRootModel rootModel = context.getModulesProvider().getRootModel(module);
+    return Arrays.asList(rootModel.getSourceRoots(false));
+  }
+
+  public PackagingElementPresentation createPresentation(@NotNull ArtifactEditorContext context) {
+    return new DelegatedPackagingElementPresentation(new ModuleElementPresentation(myModulePointer, context, false));
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/TestModuleOutputElementType.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/TestModuleOutputElementType.java
new file mode 100644
index 0000000..c65cb16
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/TestModuleOutputElementType.java
@@ -0,0 +1,63 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModulePointer;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ContentEntry;
+import com.intellij.openapi.roots.SourceFolder;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public class TestModuleOutputElementType extends ModuleOutputElementTypeBase<TestModuleOutputPackagingElement> {
+  public static final TestModuleOutputElementType ELEMENT_TYPE = new TestModuleOutputElementType();
+
+  public TestModuleOutputElementType() {
+    super("module-test-output", CompilerBundle.message("element.type.name.module.test.output"));
+  }
+
+  @NotNull
+  @Override
+  public TestModuleOutputPackagingElement createEmpty(@NotNull Project project) {
+    return new TestModuleOutputPackagingElement(project);
+  }
+
+  protected ModuleOutputPackagingElementBase createElement(@NotNull Project project, @NotNull ModulePointer pointer) {
+    return new TestModuleOutputPackagingElement(project, pointer);
+  }
+
+  @Override
+  public Icon getCreateElementIcon() {
+    return PlatformIcons.TEST_SOURCE_FOLDER;
+  }
+
+  public boolean isSuitableModule(ModulesProvider modulesProvider, Module module) {
+    for (ContentEntry entry : modulesProvider.getRootModel(module).getContentEntries()) {
+      for (SourceFolder folder : entry.getSourceFolders()) {
+        if (folder.isTestSource()) return true;
+      }
+    }
+    return false;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/TestModuleOutputPackagingElement.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/TestModuleOutputPackagingElement.java
new file mode 100644
index 0000000..f3110a0
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/TestModuleOutputPackagingElement.java
@@ -0,0 +1,86 @@
+/*
+ * 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 com.intellij.packaging.impl.elements;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModulePointer;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.CompilerModuleExtension;
+import com.intellij.openapi.roots.ContentEntry;
+import com.intellij.openapi.roots.ModuleRootModel;
+import com.intellij.openapi.roots.SourceFolder;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.elements.ArtifactAntGenerationContext;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.impl.ui.DelegatedPackagingElementPresentation;
+import com.intellij.packaging.impl.ui.ModuleElementPresentation;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingElementPresentation;
+import com.intellij.util.SmartList;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class TestModuleOutputPackagingElement extends ModuleOutputPackagingElementBase {
+  public TestModuleOutputPackagingElement(Project project) {
+    super(TestModuleOutputElementType.ELEMENT_TYPE, project);
+  }
+
+  public TestModuleOutputPackagingElement(Project project, ModulePointer modulePointer) {
+    super(TestModuleOutputElementType.ELEMENT_TYPE, project, modulePointer);
+  }
+
+  @Override
+  public String toString() {
+    return "module-tests:" + getModuleName();
+  }
+
+  protected String getModuleOutputAntProperty(ArtifactAntGenerationContext generationContext) {
+    return generationContext.getModuleTestOutputPath(myModulePointer.getModuleName());
+  }
+
+  protected VirtualFile getModuleOutputPath(CompilerModuleExtension extension) {
+    return extension.getCompilerOutputPathForTests();
+  }
+
+  @NotNull
+  @Override
+  public Collection<VirtualFile> getSourceRoots(PackagingElementResolvingContext context) {
+    Module module = findModule(context);
+    if (module == null) return Collections.emptyList();
+
+    List<VirtualFile> roots = new SmartList<VirtualFile>();
+    ModuleRootModel rootModel = context.getModulesProvider().getRootModel(module);
+    for (ContentEntry entry : rootModel.getContentEntries()) {
+      for (SourceFolder folder : entry.getSourceFolders()) {
+        if (folder.isTestSource()) {
+          ContainerUtil.addIfNotNull(folder.getFile(), roots);
+        }
+      }
+    }
+    return roots;
+  }
+
+  public PackagingElementPresentation createPresentation(@NotNull ArtifactEditorContext context) {
+    return new DelegatedPackagingElementPresentation(new ModuleElementPresentation(myModulePointer, context, true));
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/run/ArtifactChooser.java b/java/compiler/impl/src/com/intellij/packaging/impl/run/ArtifactChooser.java
new file mode 100644
index 0000000..c6e7aca
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/run/ArtifactChooser.java
@@ -0,0 +1,69 @@
+/*
+ * 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 com.intellij.packaging.impl.run;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.util.ElementsChooser;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactPointer;
+import com.intellij.ui.JBColor;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ArtifactChooser extends ElementsChooser<ArtifactPointer> {
+  private static final Comparator<ArtifactPointer> ARTIFACT_COMPARATOR = new Comparator<ArtifactPointer>() {
+    public int compare(ArtifactPointer o1, ArtifactPointer o2) {
+      return o1.getArtifactName().compareToIgnoreCase(o2.getArtifactName());
+    }
+  };
+  private static final ElementProperties INVALID_ARTIFACT_PROPERTIES = new ElementProperties() {
+    public Icon getIcon() {
+      return AllIcons.Nodes.Artifact;
+    }
+
+    public Color getColor() {
+      return JBColor.RED;
+    }
+  };
+
+  public ArtifactChooser(List<ArtifactPointer> pointers) {
+    super(pointers, false);
+    for (ArtifactPointer pointer : pointers) {
+      if (pointer.getArtifact() == null) {
+        setElementProperties(pointer, INVALID_ARTIFACT_PROPERTIES);
+      }
+    }
+    sort(ARTIFACT_COMPARATOR);
+  }
+
+  @Override
+  protected String getItemText(@NotNull ArtifactPointer value) {
+    return value.getArtifactName();
+  }
+
+  @Override
+  protected Icon getItemIcon(@NotNull ArtifactPointer value) {
+    final Artifact artifact = value.getArtifact();
+    return artifact != null ? artifact.getArtifactType().getIcon() : null;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/run/BuildArtifactsBeforeRunTask.java b/java/compiler/impl/src/com/intellij/packaging/impl/run/BuildArtifactsBeforeRunTask.java
new file mode 100644
index 0000000..690b679
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/run/BuildArtifactsBeforeRunTask.java
@@ -0,0 +1,117 @@
+/*
+ * 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 com.intellij.packaging.impl.run;
+
+import com.intellij.execution.BeforeRunTask;
+import com.intellij.openapi.project.Project;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactPointer;
+import com.intellij.packaging.artifacts.ArtifactPointerManager;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class BuildArtifactsBeforeRunTask extends BeforeRunTask<BuildArtifactsBeforeRunTask> {
+  @NonNls public static final String NAME_ATTRIBUTE = "name";
+  @NonNls public static final String ARTIFACT_ELEMENT = "artifact";
+  private List<ArtifactPointer> myArtifactPointers = new ArrayList<ArtifactPointer>();
+  private final Project myProject;
+
+  public BuildArtifactsBeforeRunTask(Project project) {
+    super(BuildArtifactsBeforeRunTaskProvider.ID);
+    myProject = project;
+  }
+
+  @Override
+  public void readExternal(Element element) {
+    super.readExternal(element);
+    final List<Element> children = element.getChildren(ARTIFACT_ELEMENT);
+    final ArtifactPointerManager pointerManager = ArtifactPointerManager.getInstance(myProject);
+    for (Element child : children) {
+      myArtifactPointers.add(pointerManager.createPointer(child.getAttributeValue(NAME_ATTRIBUTE)));
+    }
+  }
+
+  @Override
+  public void writeExternal(Element element) {
+    super.writeExternal(element);
+    for (ArtifactPointer pointer : myArtifactPointers) {
+      element.addContent(new Element(ARTIFACT_ELEMENT).setAttribute(NAME_ATTRIBUTE, pointer.getArtifactName()));
+    }
+  }
+
+  @Override
+  public BeforeRunTask clone() {
+    final BuildArtifactsBeforeRunTask task = (BuildArtifactsBeforeRunTask)super.clone();
+    task.myArtifactPointers = new ArrayList<ArtifactPointer>(myArtifactPointers);
+    return task;
+  }
+
+  @Override
+  public int getItemsCount() {
+    return myArtifactPointers.size();
+  }
+
+  public List<ArtifactPointer> getArtifactPointers() {
+    return Collections.unmodifiableList(myArtifactPointers);
+  }
+
+  public void setArtifactPointers(List<ArtifactPointer> artifactPointers) {
+    myArtifactPointers = new ArrayList<ArtifactPointer>(artifactPointers);
+  }
+
+  public void addArtifact(Artifact artifact) {
+    final ArtifactPointer pointer = ArtifactPointerManager.getInstance(myProject).createPointer(artifact);
+    if (!myArtifactPointers.contains(pointer)) {
+      myArtifactPointers.add(pointer);
+    }
+  }
+
+  public void removeArtifact(@NotNull Artifact artifact) {
+    removeArtifact(ArtifactPointerManager.getInstance(myProject).createPointer(artifact));
+  }
+
+  public void removeArtifact(final @NotNull ArtifactPointer pointer) {
+    myArtifactPointers.remove(pointer);
+  }
+
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    if (!super.equals(o)) return false;
+
+    BuildArtifactsBeforeRunTask that = (BuildArtifactsBeforeRunTask)o;
+
+    if (!myArtifactPointers.equals(that.myArtifactPointers)) return false;
+    if (!myProject.equals(that.myProject)) return false;
+
+    return true;
+  }
+
+  public int hashCode() {
+    int result = super.hashCode();
+    result = 31 * result + myArtifactPointers.hashCode();
+    result = 31 * result + myProject.hashCode();
+    return result;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/run/BuildArtifactsBeforeRunTaskProvider.java b/java/compiler/impl/src/com/intellij/packaging/impl/run/BuildArtifactsBeforeRunTaskProvider.java
new file mode 100644
index 0000000..a1fa1b4
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/run/BuildArtifactsBeforeRunTaskProvider.java
@@ -0,0 +1,260 @@
+/*
+ * 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 com.intellij.packaging.impl.run;
+
+import com.intellij.execution.BeforeRunTask;
+import com.intellij.execution.BeforeRunTaskProvider;
+import com.intellij.execution.RunManagerEx;
+import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.execution.impl.ConfigurationSettingsEditorWrapper;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.DataManager;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.application.ReadAction;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.compiler.*;
+import com.intellij.openapi.compiler.Compiler;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogBuilder;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Ref;
+import com.intellij.packaging.artifacts.*;
+import com.intellij.packaging.impl.compiler.ArtifactAwareCompiler;
+import com.intellij.packaging.impl.compiler.ArtifactCompileScope;
+import com.intellij.packaging.impl.compiler.ArtifactsCompiler;
+import com.intellij.util.concurrency.Semaphore;
+import com.intellij.util.containers.ContainerUtil;
+import gnu.trove.THashSet;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public class BuildArtifactsBeforeRunTaskProvider extends BeforeRunTaskProvider<BuildArtifactsBeforeRunTask> {
+  @NonNls public static final String BUILD_ARTIFACTS_ID = "BuildArtifacts";
+  public static final Key<BuildArtifactsBeforeRunTask> ID = Key.create(BUILD_ARTIFACTS_ID);
+  private final Project myProject;
+
+  public BuildArtifactsBeforeRunTaskProvider(Project project) {
+    myProject = project;
+    project.getMessageBus().connect().subscribe(ArtifactManager.TOPIC, new ArtifactAdapter() {
+      @Override
+      public void artifactRemoved(@NotNull Artifact artifact) {
+        final RunManagerEx runManager = RunManagerEx.getInstanceEx(myProject);
+        for (RunConfiguration configuration : runManager.getAllConfigurations()) {
+          final List<BuildArtifactsBeforeRunTask> tasks = runManager.getBeforeRunTasks(configuration, ID);
+          for (BuildArtifactsBeforeRunTask task : tasks) {
+            final String artifactName = artifact.getName();
+            final List<ArtifactPointer> pointersList = task.getArtifactPointers();
+            final ArtifactPointer[] pointers = pointersList.toArray(new ArtifactPointer[pointersList.size()]);
+            for (ArtifactPointer pointer : pointers) {
+              if (pointer.getArtifactName().equals(artifactName) && ArtifactManager.getInstance(myProject).findArtifact(artifactName) == null) {
+                task.removeArtifact(pointer);
+              }
+            }
+          }
+        }
+      }
+    });
+  }
+
+  public Key<BuildArtifactsBeforeRunTask> getId() {
+    return ID;
+  }
+
+  @Override
+  public Icon getIcon() {
+    return AllIcons.Nodes.Artifact;
+  }
+
+  @Override
+  public String getName() {
+    return CompilerBundle.message("build.artifacts.before.run.description.empty");
+  }
+
+  @Override
+  public Icon getTaskIcon(BuildArtifactsBeforeRunTask task) {
+    List<ArtifactPointer> pointers = task.getArtifactPointers();
+    if (pointers == null || pointers.isEmpty())
+      return getIcon();
+    Artifact artifact = pointers.get(0).getArtifact();
+    if (artifact == null)
+      return getIcon();
+    return artifact.getArtifactType().getIcon();
+  }
+
+  @Override
+  public String getDescription(BuildArtifactsBeforeRunTask task) {
+    final List<ArtifactPointer> pointers = task.getArtifactPointers();
+    if (pointers.isEmpty()) {
+      return CompilerBundle.message("build.artifacts.before.run.description.empty");
+    }
+    if (pointers.size() == 1) {
+      return CompilerBundle.message("build.artifacts.before.run.description.single", pointers.get(0).getArtifactName());
+    }
+    return CompilerBundle.message("build.artifacts.before.run.description.multiple", pointers.size());
+  }
+
+  public boolean isConfigurable() {
+    return true;
+  }
+
+  public BuildArtifactsBeforeRunTask createTask(RunConfiguration runConfiguration) {
+    if (myProject.isDefault()) return null;
+    return new BuildArtifactsBeforeRunTask(myProject);
+  }
+
+  public boolean configureTask(RunConfiguration runConfiguration, BuildArtifactsBeforeRunTask task) {
+    final Artifact[] artifacts = ArtifactManager.getInstance(myProject).getArtifacts();
+    Set<ArtifactPointer> pointers = new THashSet<ArtifactPointer>();
+    for (Artifact artifact : artifacts) {
+      pointers.add(ArtifactPointerManager.getInstance(myProject).createPointer(artifact));
+    }
+    pointers.addAll(task.getArtifactPointers());
+    ArtifactChooser chooser = new ArtifactChooser(new ArrayList<ArtifactPointer>(pointers));
+    chooser.markElements(task.getArtifactPointers());
+    chooser.setPreferredSize(new Dimension(400, 300));
+
+    DialogBuilder builder = new DialogBuilder(myProject);
+    builder.setTitle(CompilerBundle.message("build.artifacts.before.run.selector.title"));
+    builder.setDimensionServiceKey("#BuildArtifactsBeforeRunChooser");
+    builder.addOkAction();
+    builder.addCancelAction();
+    builder.setCenterPanel(chooser);
+    builder.setPreferedFocusComponent(chooser);
+    if (builder.show() == DialogWrapper.OK_EXIT_CODE) {
+      task.setArtifactPointers(chooser.getMarkedElements());
+      return true;
+    }
+    return false;
+  }
+
+  @Override
+  public boolean canExecuteTask(RunConfiguration configuration, BuildArtifactsBeforeRunTask task) {
+    for (ArtifactPointer pointer:  task.getArtifactPointers()) {
+      if (pointer.getArtifact() != null)
+        return true;
+    }
+    return false;
+  }
+
+  public boolean executeTask(DataContext context,
+                             RunConfiguration configuration,
+                             ExecutionEnvironment env,
+                             final BuildArtifactsBeforeRunTask task) {
+    final Ref<Boolean> result = Ref.create(false);
+    final Semaphore finished = new Semaphore();
+
+    final List<Artifact> artifacts = new ArrayList<Artifact>();
+    new ReadAction() {
+      protected void run(final Result result) {
+        for (ArtifactPointer pointer : task.getArtifactPointers()) {
+          ContainerUtil.addIfNotNull(pointer.getArtifact(), artifacts);
+        }
+      }
+    }.execute();
+    
+    final CompileStatusNotification callback = new CompileStatusNotification() {
+      public void finished(boolean aborted, int errors, int warnings, CompileContext compileContext) {
+        result.set(!aborted && errors == 0);
+        finished.up();
+      }
+    };
+    final CompilerFilter compilerFilter = new CompilerFilter() {
+      public boolean acceptCompiler(Compiler compiler) {
+        return compiler instanceof ArtifactsCompiler
+               || compiler instanceof ArtifactAwareCompiler && ((ArtifactAwareCompiler)compiler).shouldRun(artifacts);
+      }
+    };
+
+    ApplicationManager.getApplication().invokeAndWait(new Runnable() {
+      public void run() {
+        final CompilerManager manager = CompilerManager.getInstance(myProject);
+        finished.down();
+        manager.make(ArtifactCompileScope.createArtifactsScope(myProject, artifacts), compilerFilter, callback);
+      }
+    }, ModalityState.NON_MODAL);
+
+    finished.waitFor();
+    return result.get();
+  }
+
+  public static void setBuildArtifactBeforeRunOption(@NotNull JComponent runConfigurationEditorComponent,
+                                                     Project project,
+                                                     @NotNull Artifact artifact,
+                                                     final boolean enable) {
+    final DataContext dataContext = DataManager.getInstance().getDataContext(runConfigurationEditorComponent);
+    final ConfigurationSettingsEditorWrapper editor = ConfigurationSettingsEditorWrapper.CONFIGURATION_EDITOR_KEY.getData(dataContext);
+    if (editor != null) {
+      List<BeforeRunTask> tasks = editor.getStepsBeforeLaunch();
+      List<BuildArtifactsBeforeRunTask> myTasks = new ArrayList<BuildArtifactsBeforeRunTask>();
+      for (BeforeRunTask task : tasks) {
+        if (task instanceof BuildArtifactsBeforeRunTask) {
+          myTasks.add((BuildArtifactsBeforeRunTask)task);
+        }
+      }
+      if (enable && myTasks.isEmpty()) {
+        BuildArtifactsBeforeRunTask task = new BuildArtifactsBeforeRunTask(project);
+        task.addArtifact(artifact);
+        task.setEnabled(true);
+        editor.addBeforeLaunchStep(task);
+      }
+      else {
+        for (BuildArtifactsBeforeRunTask task : myTasks) {
+          if (enable) {
+            task.addArtifact(artifact);
+            task.setEnabled(true);
+          }
+          else {
+            task.removeArtifact(artifact);
+            if (task.getArtifactPointers().isEmpty()) {
+              task.setEnabled(false);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  public static void setBuildArtifactBeforeRun(@NotNull Project project, @NotNull RunConfiguration configuration, @NotNull Artifact artifact) {
+    RunManagerEx runManager = RunManagerEx.getInstanceEx(project);
+    final List<BuildArtifactsBeforeRunTask> buildArtifactsTasks = runManager.getBeforeRunTasks(configuration, ID);
+    if (buildArtifactsTasks.isEmpty()) { //Add new task if absent
+      BuildArtifactsBeforeRunTask task = new BuildArtifactsBeforeRunTask(project);
+      buildArtifactsTasks.add(task);
+      List<BeforeRunTask> tasks = runManager.getBeforeRunTasks(configuration);
+      tasks.add(task);
+      runManager.setBeforeRunTasks(configuration, tasks, false);
+    }
+
+    for (BuildArtifactsBeforeRunTask task : buildArtifactsTasks) {
+      task.setEnabled(true);
+      task.addArtifact(artifact);
+
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/ArchiveElementPresentation.java b/java/compiler/impl/src/com/intellij/packaging/impl/ui/ArchiveElementPresentation.java
new file mode 100644
index 0000000..d48b43a
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/ArchiveElementPresentation.java
@@ -0,0 +1,49 @@
+/*
+ * 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 com.intellij.packaging.impl.ui;
+
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.packaging.impl.elements.ArchivePackagingElement;
+import com.intellij.packaging.ui.PackagingElementPresentation;
+import com.intellij.packaging.ui.PackagingElementWeights;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class ArchiveElementPresentation extends PackagingElementPresentation {
+  private final ArchivePackagingElement myElement;
+
+  public ArchiveElementPresentation(ArchivePackagingElement element) {
+    myElement = element;
+  }
+
+  public String getPresentableName() {
+    return myElement.getArchiveFileName();
+  }
+
+  public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes, SimpleTextAttributes commentAttributes) {
+    presentationData.setIcon(PlatformIcons.JAR_ICON);
+    presentationData.addText(myElement.getArchiveFileName(), mainAttributes);
+  }
+
+  @Override
+  public int getWeight() {
+    return PackagingElementWeights.FILE_COPY;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/ArtifactElementPresentation.java b/java/compiler/impl/src/com/intellij/packaging/impl/ui/ArtifactElementPresentation.java
new file mode 100644
index 0000000..27535c4
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/ArtifactElementPresentation.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.
+ */
+package com.intellij.packaging.impl.ui;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactPointer;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingElementWeights;
+import com.intellij.packaging.ui.TreeNodePresentation;
+import com.intellij.ui.SimpleTextAttributes;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * @author nik
+ */
+public class ArtifactElementPresentation extends TreeNodePresentation {
+  private final ArtifactPointer myArtifactPointer;
+  private final ArtifactEditorContext myContext;
+
+  public ArtifactElementPresentation(ArtifactPointer artifactPointer, ArtifactEditorContext context) {
+    myArtifactPointer = artifactPointer;
+    myContext = context;
+  }
+
+  public String getPresentableName() {
+    return myArtifactPointer != null ? myArtifactPointer.getArtifactName(myContext.getArtifactModel()) : "<unknown>";
+  }
+
+  @Override
+  public boolean canNavigateToSource() {
+    return findArtifact() != null;
+  }
+
+  @Override
+  public void navigateToSource() {
+    final Artifact artifact = findArtifact();
+    if (artifact != null) {
+      myContext.selectArtifact(artifact);
+    }
+  }
+
+  public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes, SimpleTextAttributes commentAttributes) {
+    final Artifact artifact = findArtifact();
+    Icon icon = artifact != null ? artifact.getArtifactType().getIcon() : AllIcons.Nodes.Artifact;
+    presentationData.setIcon(icon);
+    presentationData.addText(getPresentableName(), artifact != null ? mainAttributes : SimpleTextAttributes.ERROR_ATTRIBUTES);
+  }
+
+  @Nullable
+  private Artifact findArtifact() {
+    return myArtifactPointer != null ? myArtifactPointer.findArtifact(myContext.getArtifactModel()) : null;
+  }
+
+  @Override
+  public int getWeight() {
+    return PackagingElementWeights.ARTIFACT;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/ArtifactProblemsHolderBase.java b/java/compiler/impl/src/com/intellij/packaging/impl/ui/ArtifactProblemsHolderBase.java
new file mode 100644
index 0000000..c14e2a8
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/ArtifactProblemsHolderBase.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 com.intellij.packaging.impl.ui;
+
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.ui.ArtifactProblemsHolder;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactProblemsHolderBase implements ArtifactProblemsHolder {
+  private final PackagingElementResolvingContext myContext;
+
+  protected ArtifactProblemsHolderBase(PackagingElementResolvingContext context) {
+    myContext = context;
+  }
+
+  @NotNull
+  public PackagingElementResolvingContext getContext() {
+    return myContext;
+  }
+
+  public void registerError(@NotNull String message, @NotNull String problemTypeId) {
+    registerError(message, problemTypeId, null);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/ChooseArtifactsDialog.java b/java/compiler/impl/src/com/intellij/packaging/impl/ui/ChooseArtifactsDialog.java
new file mode 100644
index 0000000..0dcb2f4
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/ChooseArtifactsDialog.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 com.intellij.packaging.impl.ui;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.ide.util.ChooseElementsDialog;
+import com.intellij.packaging.artifacts.Artifact;
+
+import javax.swing.*;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class ChooseArtifactsDialog extends ChooseElementsDialog<Artifact> {
+
+  public ChooseArtifactsDialog(Project project, List<? extends Artifact> items, String title, String description) {
+    super(project, items, title, description, true);
+  }
+
+  protected String getItemText(Artifact item) {
+    return item.getName();
+  }
+
+  protected Icon getItemIcon(Artifact item) {
+    return item.getArtifactType().getIcon();
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/DelegatedPackagingElementPresentation.java b/java/compiler/impl/src/com/intellij/packaging/impl/ui/DelegatedPackagingElementPresentation.java
new file mode 100644
index 0000000..8c5ac7d
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/DelegatedPackagingElementPresentation.java
@@ -0,0 +1,63 @@
+/*
+ * 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 com.intellij.packaging.impl.ui;
+
+import com.intellij.packaging.ui.PackagingElementPresentation;
+import com.intellij.packaging.ui.TreeNodePresentation;
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.ui.SimpleTextAttributes;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public class DelegatedPackagingElementPresentation extends PackagingElementPresentation {
+  private final TreeNodePresentation myDelegate;
+
+  public DelegatedPackagingElementPresentation(TreeNodePresentation delegate) {
+    myDelegate = delegate;
+  }
+
+  public String getPresentableName() {
+    return myDelegate.getPresentableName();
+  }
+
+  public String getSearchName() {
+    return myDelegate.getSearchName();
+  }
+
+  public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes, SimpleTextAttributes commentAttributes) {
+    myDelegate.render(presentationData, mainAttributes, commentAttributes);
+  }
+
+  @Nullable
+  public String getTooltipText() {
+    return myDelegate.getTooltipText();
+  }
+
+  public boolean canNavigateToSource() {
+    return myDelegate.canNavigateToSource();
+  }
+
+  public void navigateToSource() {
+    myDelegate.navigateToSource();
+  }
+
+  public int getWeight() {
+    return myDelegate.getWeight();
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/DirectoryCopyPresentation.java b/java/compiler/impl/src/com/intellij/packaging/impl/ui/DirectoryCopyPresentation.java
new file mode 100644
index 0000000..52786c3
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/DirectoryCopyPresentation.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.
+ */
+package com.intellij.packaging.impl.ui;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.ui.PackagingElementPresentation;
+import com.intellij.packaging.ui.PackagingElementWeights;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.PathUtil;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class DirectoryCopyPresentation extends PackagingElementPresentation {
+  private final String mySourcePath;
+  private final String mySourceFileName;
+  private final VirtualFile myFile;
+
+  public DirectoryCopyPresentation(String filePath) {
+    mySourceFileName = PathUtil.getFileName(filePath);
+
+    String parentPath;
+    myFile = LocalFileSystem.getInstance().findFileByPath(filePath);
+    if (myFile != null) {
+      final VirtualFile parent = myFile.getParent();
+      parentPath = parent != null ? FileUtil.toSystemDependentName(parent.getPath()) : "";
+    }
+    else {
+      parentPath = FileUtil.toSystemDependentName(PathUtil.getParentPath(filePath));
+    }
+
+    mySourcePath = parentPath;
+  }
+
+  public String getPresentableName() {
+    return mySourceFileName;
+  }
+
+  public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes, SimpleTextAttributes commentAttributes) {
+    presentationData.setIcon(AllIcons.Nodes.CopyOfFolder);
+    if (myFile == null || !myFile.isDirectory()) {
+      mainAttributes = SimpleTextAttributes.ERROR_ATTRIBUTES;
+      final VirtualFile parentFile = LocalFileSystem.getInstance().findFileByPath(FileUtil.toSystemIndependentName(mySourcePath));
+      if (parentFile == null) {
+        commentAttributes = SimpleTextAttributes.ERROR_ATTRIBUTES;
+      }
+    }
+    presentationData.addText(CompilerBundle.message("node.text.0.directory.content", mySourceFileName), mainAttributes);
+    presentationData.addText(" (" + mySourcePath + ")", commentAttributes);
+  }
+
+  @Override
+  public int getWeight() {
+    return PackagingElementWeights.DIRECTORY_COPY;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/DirectoryElementPresentation.java b/java/compiler/impl/src/com/intellij/packaging/impl/ui/DirectoryElementPresentation.java
new file mode 100644
index 0000000..cfdf2bb
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/DirectoryElementPresentation.java
@@ -0,0 +1,49 @@
+/*
+ * 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 com.intellij.packaging.impl.ui;
+
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.packaging.impl.elements.DirectoryPackagingElement;
+import com.intellij.packaging.ui.PackagingElementPresentation;
+import com.intellij.packaging.ui.PackagingElementWeights;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class DirectoryElementPresentation extends PackagingElementPresentation {
+  private final DirectoryPackagingElement myElement;
+
+  public DirectoryElementPresentation(DirectoryPackagingElement element) {
+    myElement = element;
+  }
+
+  public String getPresentableName() {
+    return myElement.getDirectoryName();
+  }
+
+  public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes, SimpleTextAttributes commentAttributes) {
+    presentationData.setIcon(PlatformIcons.DIRECTORY_CLOSED_ICON);
+    presentationData.addText(myElement.getDirectoryName(), mainAttributes);
+  }
+
+  @Override
+  public int getWeight() {
+    return PackagingElementWeights.DIRECTORY;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/ExtractedDirectoryPresentation.java b/java/compiler/impl/src/com/intellij/packaging/impl/ui/ExtractedDirectoryPresentation.java
new file mode 100644
index 0000000..6f9d0ac
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/ExtractedDirectoryPresentation.java
@@ -0,0 +1,65 @@
+/*
+ * 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 com.intellij.packaging.impl.ui;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.impl.elements.ExtractedDirectoryPackagingElement;
+import com.intellij.packaging.ui.PackagingElementPresentation;
+import com.intellij.packaging.ui.PackagingElementWeights;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.PathUtil;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class ExtractedDirectoryPresentation extends PackagingElementPresentation {
+  private final String myJarPath;
+  private final String myPathInJar;
+  private final VirtualFile myFile;
+
+  public ExtractedDirectoryPresentation(ExtractedDirectoryPackagingElement element) {
+    myFile = element.findFile();
+    myJarPath = element.getFilePath();
+    myPathInJar = element.getPathInJar();
+  }
+
+  public String getPresentableName() {
+    return PathUtil.getFileName(myJarPath) + myPathInJar;
+  }
+
+  public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes, SimpleTextAttributes commentAttributes) {
+    presentationData.setIcon(AllIcons.Nodes.ExtractedFolder);
+    final String parentPath = PathUtil.getParentPath(myJarPath);
+    if (myFile == null || !myFile.isDirectory()) {
+      mainAttributes = SimpleTextAttributes.ERROR_ATTRIBUTES;
+      final VirtualFile parentFile = LocalFileSystem.getInstance().findFileByPath(parentPath);
+      if (parentFile == null) {
+        commentAttributes = SimpleTextAttributes.ERROR_ATTRIBUTES;
+      }
+    }
+    presentationData.addText("Extracted '" + PathUtil.getFileName(myJarPath) + myPathInJar + "'", mainAttributes);
+    presentationData.addText(" (" + parentPath + ")", commentAttributes);
+  }
+
+  @Override
+  public int getWeight() {
+    return PackagingElementWeights.EXTRACTED_DIRECTORY;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/FileCopyPresentation.java b/java/compiler/impl/src/com/intellij/packaging/impl/ui/FileCopyPresentation.java
new file mode 100644
index 0000000..bbd3304
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/FileCopyPresentation.java
@@ -0,0 +1,83 @@
+/*
+ * 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 com.intellij.packaging.impl.ui;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.presentation.VirtualFilePresentation;
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.ui.PackagingElementPresentation;
+import com.intellij.packaging.ui.PackagingElementWeights;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.PathUtil;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class FileCopyPresentation extends PackagingElementPresentation {
+  private final String mySourcePath;
+  private final String myOutputFileName;
+  private final VirtualFile myFile;
+
+  public FileCopyPresentation(String filePath, String outputFileName) {
+    myOutputFileName = outputFileName;
+
+    String parentPath;
+    myFile = LocalFileSystem.getInstance().findFileByPath(filePath);
+    if (myFile != null) {
+      final VirtualFile parent = myFile.getParent();
+      parentPath = parent != null ? FileUtil.toSystemDependentName(parent.getPath()) : "";
+    }
+    else {
+      parentPath = FileUtil.toSystemDependentName(PathUtil.getParentPath(filePath));
+    }
+
+    String sourceFileName = PathUtil.getFileName(filePath);
+    if (!sourceFileName.equals(myOutputFileName)) {
+      mySourcePath = parentPath + "/" + sourceFileName;
+    }
+    else {
+      mySourcePath = parentPath;
+    }
+  }
+
+  public String getPresentableName() {
+    return myOutputFileName;
+  }
+
+  public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes, SimpleTextAttributes commentAttributes) {
+    if (myFile != null && !myFile.isDirectory()) {
+      presentationData.setIcon(VirtualFilePresentation.getIcon(myFile));
+      presentationData.addText(myOutputFileName, mainAttributes);
+      presentationData.addText(" (" + mySourcePath + ")", commentAttributes);
+    }
+    else {
+      presentationData.setIcon(AllIcons.FileTypes.Text);
+      presentationData.addText(myOutputFileName, SimpleTextAttributes.ERROR_ATTRIBUTES);
+      final VirtualFile parentFile = LocalFileSystem.getInstance().findFileByPath(FileUtil.toSystemIndependentName(mySourcePath));
+      presentationData.addText("(" + mySourcePath + ")",
+                      parentFile != null ? commentAttributes : SimpleTextAttributes.ERROR_ATTRIBUTES);
+    }
+  }
+
+  @Override
+  public int getWeight() {
+    return PackagingElementWeights.FILE_COPY;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/LibraryElementPresentation.java b/java/compiler/impl/src/com/intellij/packaging/impl/ui/LibraryElementPresentation.java
new file mode 100644
index 0000000..44905f8
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/LibraryElementPresentation.java
@@ -0,0 +1,101 @@
+/*
+ * 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 com.intellij.packaging.impl.ui;
+
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.impl.ModuleLibraryTable;
+import com.intellij.openapi.roots.impl.libraries.LibraryImpl;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTable;
+import com.intellij.openapi.roots.libraries.LibraryTablePresentation;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingElementPresentation;
+import com.intellij.packaging.ui.PackagingElementWeights;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public class LibraryElementPresentation extends PackagingElementPresentation {
+  private final String myLevel;
+  private final String myModuleName;
+  private final Library myLibrary;
+  private final String myLibraryName;
+  private final ArtifactEditorContext myContext;
+
+  public LibraryElementPresentation(String libraryName, String level, @Nullable String moduleName, Library library, ArtifactEditorContext context) {
+    myLevel = level;
+    myModuleName = moduleName;
+    myLibrary = library;
+    myLibraryName = libraryName;
+    myContext = context;
+  }
+
+  public String getPresentableName() {
+    return myLibraryName;
+  }
+
+  @Override
+  public boolean canNavigateToSource() {
+    return myLibrary != null;
+  }
+
+  @Override
+  public void navigateToSource() {
+    myContext.selectLibrary(myLibrary);
+  }
+
+  public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes, SimpleTextAttributes commentAttributes) {
+    if (myLibrary != null) {
+      presentationData.setIcon(PlatformIcons.LIBRARY_ICON);
+      presentationData.addText(myLibraryName, mainAttributes);
+      presentationData.addText(getLibraryTableComment(myLibrary), commentAttributes);
+    }
+    else {
+      presentationData.addText(myLibraryName + " (" + (myModuleName != null ? "module '" + myModuleName + "'" : myLevel) + ")", 
+                               SimpleTextAttributes.ERROR_ATTRIBUTES);
+    }
+  }
+
+  @Override
+  public int getWeight() {
+    return PackagingElementWeights.LIBRARY;
+  }
+
+  public static String getLibraryTableDisplayName(final Library library) {
+    LibraryTable table = library.getTable();
+    LibraryTablePresentation presentation = table != null ? table.getPresentation() : ModuleLibraryTable.MODULE_LIBRARY_TABLE_PRESENTATION;
+    return presentation.getDisplayName(false);
+  }
+
+  public static String getLibraryTableComment(final Library library) {
+    LibraryTable libraryTable = library.getTable();
+    String displayName;
+    if (libraryTable != null) {
+      displayName = libraryTable.getPresentation().getDisplayName(false);
+    }
+    else {
+      Module module = ((LibraryImpl)library).getModule();
+      String tableName = getLibraryTableDisplayName(library);
+      displayName = module != null ? "'" + module.getName() + "' " + tableName : tableName;
+    }
+    return " (" + displayName + ")";
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/ModuleElementPresentation.java b/java/compiler/impl/src/com/intellij/packaging/impl/ui/ModuleElementPresentation.java
new file mode 100644
index 0000000..af59bc0
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/ModuleElementPresentation.java
@@ -0,0 +1,103 @@
+/*
+ * 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 com.intellij.packaging.impl.ui;
+
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.module.ModifiableModuleModel;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModulePointer;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingElementWeights;
+import com.intellij.packaging.ui.TreeNodePresentation;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public class ModuleElementPresentation extends TreeNodePresentation {
+  private final ModulePointer myModulePointer;
+  private final ArtifactEditorContext myContext;
+  private final boolean myTestOutput;
+
+  public ModuleElementPresentation(@Nullable ModulePointer modulePointer, @NotNull ArtifactEditorContext context, final boolean testOutput) {
+    myModulePointer = modulePointer;
+    myContext = context;
+    myTestOutput = testOutput;
+  }
+
+  public String getPresentableName() {
+    return myModulePointer != null ? myModulePointer.getModuleName() : "<unknown>";
+  }
+
+  @Override
+  public boolean canNavigateToSource() {
+    return findModule() != null;
+  }
+
+  @Nullable
+  private Module findModule() {
+    return myModulePointer != null ? myModulePointer.getModule() : null;
+  }
+
+  @Override
+  public void navigateToSource() {
+    final Module module = findModule();
+    if (module != null) {
+      myContext.selectModule(module);
+    }
+  }
+
+  public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes, SimpleTextAttributes commentAttributes) {
+    final Module module = findModule();
+    if (myTestOutput) {
+      presentationData.setIcon(PlatformIcons.TEST_SOURCE_FOLDER);
+    }
+    else if (module != null) {
+      presentationData.setIcon(ModuleType.get(module).getIcon());
+    }
+    String moduleName;
+    if (module != null) {
+      moduleName = module.getName();
+      final ModifiableModuleModel moduleModel = myContext.getModifiableModuleModel();
+      if (moduleModel != null) {
+        final String newName = moduleModel.getNewName(module);
+        if (newName != null) {
+          moduleName = newName;
+        }
+      }
+    }
+    else if (myModulePointer != null) {
+      moduleName = myModulePointer.getModuleName();
+    }
+    else {
+      moduleName = "<unknown>";
+    }
+
+    String text = myTestOutput ? CompilerBundle.message("node.text.0.test.compile.output", moduleName)
+                               : CompilerBundle.message("node.text.0.compile.output", moduleName);
+    presentationData.addText(text, module != null ? mainAttributes : SimpleTextAttributes.ERROR_ATTRIBUTES);
+  }
+
+  @Override
+  public int getWeight() {
+    return PackagingElementWeights.MODULE;
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/actions/PackageFileAction.java b/java/compiler/impl/src/com/intellij/packaging/impl/ui/actions/PackageFileAction.java
new file mode 100644
index 0000000..f72bd75
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/actions/PackageFileAction.java
@@ -0,0 +1,104 @@
+/**
+ * @author cdr
+ */
+package com.intellij.packaging.impl.ui.actions;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.Clock;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.wm.WindowManager;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactManager;
+import com.intellij.packaging.impl.artifacts.ArtifactBySourceFileFinder;
+import com.intellij.util.text.SyncDateFormat;
+import org.jetbrains.annotations.NotNull;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+public class PackageFileAction extends AnAction {
+  private static final SyncDateFormat TIME_FORMAT = new SyncDateFormat(new SimpleDateFormat("h:mm:ss a"));
+
+  public PackageFileAction() {
+    super(CompilerBundle.message("action.name.package.file"), CompilerBundle.message("action.description.package.file"), null);
+  }
+
+  @Override
+  public void update(AnActionEvent e) {
+    boolean visible = false;
+    final Project project = e.getData(PlatformDataKeys.PROJECT);
+    if (project != null) {
+      final List<VirtualFile> files = getFilesToPackage(e, project);
+      if (!files.isEmpty()) {
+        visible = true;
+        e.getPresentation().setText(files.size() == 1 ? CompilerBundle.message("action.name.package.file") : CompilerBundle.message("action.name.package.files"));
+      }
+    }
+
+    e.getPresentation().setVisible(visible);
+  }
+
+  @NotNull
+  private static List<VirtualFile> getFilesToPackage(@NotNull AnActionEvent e, @NotNull Project project) {
+    final VirtualFile[] files = e.getData(PlatformDataKeys.VIRTUAL_FILE_ARRAY);
+    if (files == null) return Collections.emptyList();
+
+    List<VirtualFile> result = new ArrayList<VirtualFile>();
+    ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
+    final CompilerManager compilerManager = CompilerManager.getInstance(project);
+    for (VirtualFile file : files) {
+      if (file == null || file.isDirectory() ||
+          fileIndex.isInSourceContent(file) && compilerManager.isCompilableFileType(file.getFileType())) {
+        return Collections.emptyList();
+      }
+      final Collection<? extends Artifact> artifacts = ArtifactBySourceFileFinder.getInstance(project).findArtifacts(file);
+      for (Artifact artifact : artifacts) {
+        if (!StringUtil.isEmpty(artifact.getOutputPath())) {
+          result.add(file);
+          break;
+        }
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public void actionPerformed(AnActionEvent event) {
+    final Project project = event.getData(PlatformDataKeys.PROJECT);
+    if (project == null) return;
+
+    FileDocumentManager.getInstance().saveAllDocuments();
+    final List<VirtualFile> files = getFilesToPackage(event, project);
+    Artifact[] allArtifacts = ArtifactManager.getInstance(project).getArtifacts();
+    PackageFileWorker.startPackagingFiles(project, files, allArtifacts, new Runnable() {
+      public void run() {
+        setStatusText(project, files);
+      }
+    });
+  }
+
+  private static void setStatusText(Project project, List<VirtualFile> files) {
+    if (!files.isEmpty()) {
+      StringBuilder fileNames = new StringBuilder();
+      for (VirtualFile file : files) {
+        if (fileNames.length() != 0) fileNames.append(", ");
+        fileNames.append("'").append(file.getName()).append("'");
+      }
+      String time = TIME_FORMAT.format(Clock.getTime());
+      final String statusText = CompilerBundle.message("status.text.file.has.been.packaged", files.size(), fileNames, time);
+      WindowManager.getInstance().getStatusBar(project).setInfo(statusText);
+    }
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/actions/PackageFileWorker.java b/java/compiler/impl/src/com/intellij/packaging/impl/ui/actions/PackageFileWorker.java
new file mode 100644
index 0000000..6be07f0
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/actions/PackageFileWorker.java
@@ -0,0 +1,183 @@
+/*
+ * 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 com.intellij.packaging.impl.ui.actions;
+
+import com.intellij.notification.Notification;
+import com.intellij.notification.NotificationType;
+import com.intellij.notification.Notifications;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ReadAction;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.deployment.DeploymentUtil;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Trinity;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VfsUtil;
+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.impl.artifacts.ArtifactUtil;
+import com.intellij.packaging.impl.artifacts.PackagingElementPath;
+import com.intellij.packaging.impl.elements.ArchivePackagingElement;
+import com.intellij.util.PathUtil;
+import com.intellij.util.io.zip.JBZipEntry;
+import com.intellij.util.io.zip.JBZipFile;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class PackageFileWorker {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.packaging.impl.ui.actions.PackageFileWorker");
+  private final File myFile;
+  private final String myRelativeOutputPath;
+
+  private PackageFileWorker(File file, String relativeOutputPath) {
+    myFile = file;
+    myRelativeOutputPath = relativeOutputPath;
+  }
+
+  public static void startPackagingFiles(final Project project, final List<VirtualFile> files,
+                                         final Artifact[] artifacts, final Runnable onFinished) {
+    ProgressManager.getInstance().run(new Task.Backgroundable(project, "Packaging Files") {
+      @Override
+      public void run(@NotNull ProgressIndicator indicator) {
+        try {
+          for (final VirtualFile file : files) {
+            indicator.checkCanceled();
+            new ReadAction() {
+              protected void run(final Result result) {
+                try {
+                  packageFile(file, project, artifacts);
+                }
+                catch (IOException e) {
+                  String message = CompilerBundle.message("message.tect.package.file.io.error", e.toString());
+                  Notifications.Bus.notify(new Notification("Package File", "Cannot package file", message, NotificationType.ERROR));
+                }
+              }
+            }.execute();
+          }
+        }
+        finally {
+          ApplicationManager.getApplication().invokeLater(onFinished);
+        }
+      }
+    });
+  }
+
+  public static void packageFile(@NotNull VirtualFile file, @NotNull Project project, final Artifact[] artifacts) throws IOException {
+    LOG.debug("Start packaging file: " + file.getPath());
+    final Collection<Trinity<Artifact, PackagingElementPath, String>> items = ArtifactUtil.findContainingArtifactsWithOutputPaths(file, project, artifacts);
+    File ioFile = VfsUtil.virtualToIoFile(file);
+    for (Trinity<Artifact, PackagingElementPath, String> item : items) {
+      final Artifact artifact = item.getFirst();
+      final String outputPath = artifact.getOutputPath();
+      if (!StringUtil.isEmpty(outputPath)) {
+        PackageFileWorker worker = new PackageFileWorker(ioFile, item.getThird());
+        LOG.debug(" package to " + outputPath);
+        worker.packageFile(outputPath, item.getSecond().getParents());
+      }
+    }
+  }
+
+  private void packageFile(String outputPath, List<CompositePackagingElement<?>> parents) throws IOException {
+    List<CompositePackagingElement<?>> parentsList = new ArrayList<CompositePackagingElement<?>>(parents);
+    Collections.reverse(parentsList);
+    if (!parentsList.isEmpty() && parentsList.get(0) instanceof ArtifactRootElement) {
+      parentsList = parentsList.subList(1, parentsList.size());
+    }
+    copyFile(outputPath, parentsList);
+  }
+
+  private void copyFile(String outputPath, List<CompositePackagingElement<?>> parents) throws IOException {
+    if (parents.isEmpty()) {
+      final String fullOutputPath = DeploymentUtil.appendToPath(outputPath, myRelativeOutputPath);
+      LOG.debug("  copying to " + fullOutputPath);
+      FileUtil.copy(myFile, new File(FileUtil.toSystemDependentName(fullOutputPath)));
+      return;
+    }
+
+    final CompositePackagingElement<?> element = parents.get(0);
+    final String nextOutputPath = outputPath + "/" + element.getName();
+    final List<CompositePackagingElement<?>> parentsTrail = parents.subList(1, parents.size());
+    if (element instanceof ArchivePackagingElement) {
+      packFile(nextOutputPath, "", parentsTrail);
+    }
+    else {
+      copyFile(nextOutputPath, parentsTrail);
+    }
+  }
+
+  private void packFile(String archivePath, String pathInArchive, List<CompositePackagingElement<?>> parents) throws IOException {
+    final File archiveFile = new File(FileUtil.toSystemDependentName(archivePath));
+    if (parents.isEmpty()) {
+      LOG.debug("  adding to archive " + archivePath);
+      JBZipFile file = getOrCreateZipFile(archiveFile);
+      try {
+        final String fullPathInArchive = DeploymentUtil.trimForwardSlashes(DeploymentUtil.appendToPath(pathInArchive, myRelativeOutputPath));
+        final JBZipEntry entry = file.getOrCreateEntry(fullPathInArchive);
+        entry.setData(FileUtil.loadFileBytes(myFile));
+      }
+      finally {
+        file.close();
+      }
+      return;
+    }
+
+    final CompositePackagingElement<?> element = parents.get(0);
+    final String nextPathInArchive = DeploymentUtil.trimForwardSlashes(DeploymentUtil.appendToPath(pathInArchive, element.getName()));
+    final List<CompositePackagingElement<?>> parentsTrail = parents.subList(1, parents.size());
+    if (element instanceof ArchivePackagingElement) {
+      JBZipFile zipFile = getOrCreateZipFile(archiveFile);
+      try {
+        final JBZipEntry entry = zipFile.getOrCreateEntry(nextPathInArchive);
+        LOG.debug("  extracting to temp file: " + nextPathInArchive + " from " + archivePath);
+        final File tempFile = FileUtil.createTempFile("packageFile" + FileUtil.sanitizeFileName(nextPathInArchive), FileUtil.getExtension(PathUtil.getFileName(nextPathInArchive)));
+        if (entry.getSize() != -1) {
+          FileUtil.writeToFile(tempFile, entry.getData());
+        }
+        packFile(FileUtil.toSystemIndependentName(tempFile.getAbsolutePath()), "", parentsTrail);
+        entry.setData(FileUtil.loadFileBytes(tempFile));
+        FileUtil.delete(tempFile);
+      }
+      finally {
+        zipFile.close();
+      }
+    }
+    else {
+      packFile(archivePath, nextPathInArchive, parentsTrail);
+    }
+  }
+
+  private static JBZipFile getOrCreateZipFile(File archiveFile) throws IOException {
+    FileUtil.createIfDoesntExist(archiveFile);
+    return new JBZipFile(archiveFile);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/properties/ArchiveElementPropertiesPanel.java b/java/compiler/impl/src/com/intellij/packaging/impl/ui/properties/ArchiveElementPropertiesPanel.java
new file mode 100644
index 0000000..dd2902d
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/properties/ArchiveElementPropertiesPanel.java
@@ -0,0 +1,28 @@
+/*
+ * 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 com.intellij.packaging.impl.ui.properties;
+
+import com.intellij.packaging.impl.elements.ArchivePackagingElement;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+
+/**
+ * @author nik
+ */
+public class ArchiveElementPropertiesPanel extends ElementWithManifestPropertiesPanel<ArchivePackagingElement> {
+  public ArchiveElementPropertiesPanel(ArchivePackagingElement element, final ArtifactEditorContext context) {
+    super(element, context);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/properties/DirectoryElementPropertiesPanel.java b/java/compiler/impl/src/com/intellij/packaging/impl/ui/properties/DirectoryElementPropertiesPanel.java
new file mode 100644
index 0000000..804d236
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/properties/DirectoryElementPropertiesPanel.java
@@ -0,0 +1,28 @@
+/*
+ * 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 com.intellij.packaging.impl.ui.properties;
+
+import com.intellij.packaging.impl.elements.DirectoryPackagingElement;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+
+/**
+ * @author nik
+ */
+public class DirectoryElementPropertiesPanel extends ElementWithManifestPropertiesPanel<DirectoryPackagingElement> {
+  public DirectoryElementPropertiesPanel(DirectoryPackagingElement element, ArtifactEditorContext context) {
+    super(element, context);
+  }
+}
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/properties/ElementWithManifestPropertiesPanel.form b/java/compiler/impl/src/com/intellij/packaging/impl/ui/properties/ElementWithManifestPropertiesPanel.form
new file mode 100644
index 0000000..f1e8930
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/properties/ElementWithManifestPropertiesPanel.form
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.packaging.impl.ui.properties.ElementWithManifestPropertiesPanel">
+  <grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="5" left="5" bottom="5" right="5"/>
+    <constraints>
+      <xy x="20" y="20" width="500" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <vspacer id="e5716">
+        <constraints>
+          <grid row="1" 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>
+      <grid id="98ebe" binding="myPropertiesPanel" layout-manager="CardLayout" hgap="0" vgap="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>
+          <grid id="e398c" 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>
+              <card name="properties"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="9a3d6" 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>
+                  <text resource-bundle="messages/CompilerBundle" key="label.text.main.class"/>
+                </properties>
+              </component>
+              <component id="d87aa" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myMainClassField">
+                <constraints>
+                  <grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+                </constraints>
+                <properties/>
+              </component>
+              <component id="40600" 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>
+                  <text resource-bundle="messages/CompilerBundle" key="label.text.class.path"/>
+                </properties>
+              </component>
+              <component id="a5121" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myClasspathField">
+                <constraints>
+                  <grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+                </constraints>
+                <properties/>
+              </component>
+              <component id="e12c" class="javax.swing.JLabel" binding="myTitleLabel">
+                <constraints>
+                  <grid row="0" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+                </constraints>
+                <properties>
+                  <text value="element name"/>
+                </properties>
+              </component>
+              <component id="446b5" 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="aca67"/>
+                  <text value="Manifest &amp;File:"/>
+                </properties>
+              </component>
+              <component id="aca67" class="javax.swing.JTextField" binding="myManifestPathField">
+                <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>
+                  <editable value="false"/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+          <grid id="4473f" layout-manager="GridLayoutManager" row-count="3" 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>
+              <card name="buttons"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="66e62" class="javax.swing.JButton" binding="myCreateManifestButton" default-binding="true">
+                <constraints>
+                  <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+                </constraints>
+                <properties>
+                  <text value="&amp;Create Manifest..."/>
+                </properties>
+              </component>
+              <hspacer id="970f7">
+                <constraints>
+                  <grid row="1" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+                </constraints>
+              </hspacer>
+              <vspacer id="bf917">
+                <constraints>
+                  <grid row="2" 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>
+              <component id="c6af2" class="javax.swing.JButton" binding="myUseExistingManifestButton" default-binding="true">
+                <constraints>
+                  <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+                </constraints>
+                <properties>
+                  <text value="&amp;Use Existing Manifest..."/>
+                </properties>
+              </component>
+              <component id="a0a7b" class="javax.swing.JLabel" binding="myManifestNotFoundLabel">
+                <constraints>
+                  <grid row="0" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+                </constraints>
+                <properties>
+                  <text value="META-INF/MANIFEST.MF file not found"/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+        </children>
+      </grid>
+    </children>
+  </grid>
+</form>
diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/ui/properties/ElementWithManifestPropertiesPanel.java b/java/compiler/impl/src/com/intellij/packaging/impl/ui/properties/ElementWithManifestPropertiesPanel.java
new file mode 100644
index 0000000..ad58d51
--- /dev/null
+++ b/java/compiler/impl/src/com/intellij/packaging/impl/ui/properties/ElementWithManifestPropertiesPanel.java
@@ -0,0 +1,176 @@
+/*
+ * 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 com.intellij.packaging.impl.ui.properties;
+
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+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.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.impl.elements.CompositeElementWithManifest;
+import com.intellij.packaging.impl.elements.ManifestFileUtil;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.ManifestFileConfiguration;
+import com.intellij.packaging.ui.PackagingElementPropertiesPanel;
+import com.intellij.ui.DocumentAdapter;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class ElementWithManifestPropertiesPanel<E extends CompositeElementWithManifest<?>> extends PackagingElementPropertiesPanel {
+  private final E myElement;
+  private final ArtifactEditorContext myContext;
+  private JPanel myMainPanel;
+  private TextFieldWithBrowseButton myMainClassField;
+  private TextFieldWithBrowseButton myClasspathField;
+  private JLabel myTitleLabel;
+  private JButton myCreateManifestButton;
+  private JButton myUseExistingManifestButton;
+  private JPanel myPropertiesPanel;
+  private JTextField myManifestPathField;
+  private JLabel myManifestNotFoundLabel;
+  private ManifestFileConfiguration myManifestFileConfiguration;
+
+  public ElementWithManifestPropertiesPanel(E element, final ArtifactEditorContext context) {
+    myElement = element;
+    myContext = context;
+
+    ManifestFileUtil.setupMainClassField(context.getProject(), myMainClassField);
+
+    myClasspathField.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        Messages.showTextAreaDialog(myClasspathField.getTextField(), "Edit Classpath", "classpath-attribute-editor");
+      }
+    });
+    myClasspathField.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
+      @Override
+      protected void textChanged(DocumentEvent e) {
+        myContext.queueValidation();
+      }
+    });
+    myUseExistingManifestButton.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        chooseManifest();
+      }
+    });
+    myCreateManifestButton.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        createManifest();
+      }
+    });
+  }
+
+  private void createManifest() {
+    final VirtualFile file = ManifestFileUtil.showDialogAndCreateManifest(myContext, myElement);
+    if (file == null) {
+      return;
+    }
+
+    ManifestFileUtil.addManifestFileToLayout(file.getPath(), myContext, myElement);
+    updateManifest();
+    myContext.getThisArtifactEditor().updateLayoutTree();
+  }
+
+  private void chooseManifest() {
+    final FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, false, false, false) {
+      @Override
+      public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) {
+        return super.isFileVisible(file, showHiddenFiles) && (file.isDirectory() ||
+               file.getName().equalsIgnoreCase(ManifestFileUtil.MANIFEST_FILE_NAME));
+      }
+    };
+    descriptor.setTitle("Specify Path to MANIFEST.MF file");
+    final VirtualFile file = FileChooser.chooseFile(descriptor, myContext.getProject(), null);
+    if (file == null) return;
+
+    ManifestFileUtil.addManifestFileToLayout(file.getPath(), myContext, myElement);
+    updateManifest();
+    myContext.getThisArtifactEditor().updateLayoutTree();
+  }
+
+  private void updateManifest() {
+    myManifestFileConfiguration = myContext.getManifestFile(myElement, myContext.getArtifactType());
+    final String card;
+    if (myManifestFileConfiguration != null) {
+      card = "properties";
+      myManifestPathField.setText(FileUtil.toSystemDependentName(myManifestFileConfiguration.getManifestFilePath()));
+      myMainClassField.setText(StringUtil.notNullize(myManifestFileConfiguration.getMainClass()));
+      myMainClassField.setEnabled(myManifestFileConfiguration.isWritable());
+      myClasspathField.setText(StringUtil.join(myManifestFileConfiguration.getClasspath(), " "));
+      myClasspathField.setEnabled(myManifestFileConfiguration.isWritable());
+    }
+    else {
+      card = "buttons";
+      myManifestPathField.setText("");
+    }
+    ((CardLayout)myPropertiesPanel.getLayout()).show(myPropertiesPanel, card);
+  }
+
+  public void reset() {
+    myTitleLabel.setText("'" + myElement.getName() + "' manifest properties:");
+    myManifestNotFoundLabel.setText("META-INF/MANIFEST.MF file not found in '" + myElement.getName() + "'");
+    updateManifest();
+  }
+
+  public boolean isModified() {
+    return myManifestFileConfiguration != null && (!myManifestFileConfiguration.getClasspath().equals(getConfiguredClasspath())
+           || !Comparing.equal(myManifestFileConfiguration.getMainClass(), getConfiguredMainClass())
+           || !Comparing.equal(myManifestFileConfiguration.getManifestFilePath(), getConfiguredManifestPath()));
+  }
+
+  @Nullable
+  private String getConfiguredManifestPath() {
+    final String path = myManifestPathField.getText();
+    return path.length() != 0 ? FileUtil.toSystemIndependentName(path) : null;
+  }
+
+  @Override
+  public void apply() {
+    if (myManifestFileConfiguration != null) {
+      myManifestFileConfiguration.setMainClass(getConfiguredMainClass());
+      myManifestFileConfiguration.setClasspath(getConfiguredClasspath());
+      myManifestFileConfiguration.setManifestFilePath(getConfiguredManifestPath());
+    }
+  }
+
+  private List<String> getConfiguredClasspath() {
+    return StringUtil.split(myClasspathField.getText(), " ");
+  }
+
+  @NotNull
+  public JComponent createComponent() {
+    return myMainPanel;
+  }
+
+  @Nullable
+  private String getConfiguredMainClass() {
+    final String className = myMainClassField.getText();
+    return className.length() != 0 ? className : null;
+  }
+
+}
diff --git a/java/compiler/impl/testSrc/com/intellij/compiler/BaseCompilerTestCase.java b/java/compiler/impl/testSrc/com/intellij/compiler/BaseCompilerTestCase.java
new file mode 100644
index 0000000..ebd49e4
--- /dev/null
+++ b/java/compiler/impl/testSrc/com/intellij/compiler/BaseCompilerTestCase.java
@@ -0,0 +1,443 @@
+package com.intellij.compiler;
+
+import com.intellij.compiler.impl.CompileDriver;
+import com.intellij.compiler.impl.ExitStatus;
+import com.intellij.ide.highlighter.ModuleFileType;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.application.ex.PathManagerEx;
+import com.intellij.openapi.compiler.*;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
+import com.intellij.openapi.roots.CompilerModuleExtension;
+import com.intellij.openapi.roots.CompilerProjectExtension;
+import com.intellij.openapi.roots.ModuleRootModificationUtil;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactManager;
+import com.intellij.packaging.impl.compiler.ArtifactCompileScope;
+import com.intellij.testFramework.ModuleTestCase;
+import com.intellij.testFramework.PlatformTestCase;
+import com.intellij.testFramework.PsiTestUtil;
+import com.intellij.testFramework.VfsTestUtil;
+import com.intellij.util.ParameterizedRunnable;
+import com.intellij.util.concurrency.Semaphore;
+import com.intellij.util.io.TestFileSystemBuilder;
+import com.intellij.util.ui.UIUtil;
+import gnu.trove.THashSet;
+import junit.framework.Assert;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.util.JpsPathUtil;
+
+import javax.swing.*;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author nik
+ */
+public abstract class BaseCompilerTestCase extends ModuleTestCase {
+  protected boolean useExternalCompiler() {
+    return false;
+  }
+
+  @Override
+  protected void setUpModule() {
+  }
+
+  @Override
+  protected void setUp() throws Exception {
+    super.setUp();
+    if (useExternalCompiler()) {
+      CompilerTestUtil.enableExternalCompiler(myProject);
+    }
+    else {
+      CompilerTestUtil.disableExternalCompiler(myProject);
+    }
+  }
+
+  @Override
+  protected Sdk getTestProjectJdk() {
+    if (useExternalCompiler()) {
+      return JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk();
+    }
+    return super.getTestProjectJdk();
+  }
+
+  @Override
+  protected void tearDown() throws Exception {
+    for (Artifact artifact : getArtifactManager().getArtifacts()) {
+      final String outputPath = artifact.getOutputPath();
+      if (!StringUtil.isEmpty(outputPath)) {
+        FileUtil.delete(new File(FileUtil.toSystemDependentName(outputPath)));
+      }
+    }
+    if (useExternalCompiler()) {
+      CompilerTestUtil.disableExternalCompiler(myProject);
+    }
+
+    super.tearDown();
+  }
+
+  protected ArtifactManager getArtifactManager() {
+    return ArtifactManager.getInstance(myProject);
+  }
+
+  protected String getProjectBasePath() {
+    return getBaseDir().getPath();
+  }
+
+  protected VirtualFile getBaseDir() {
+    final VirtualFile baseDir = myProject.getBaseDir();
+    Assert.assertNotNull(baseDir);
+    return baseDir;
+  }
+
+  protected void copyToProject(String relativePath) {
+    File dir = PathManagerEx.findFileUnderProjectHome(relativePath, getClass());
+    final File target = new File(FileUtil.toSystemDependentName(getProjectBasePath()));
+    try {
+      FileUtil.copyDir(dir, target);
+    }
+    catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+    new WriteAction() {
+      protected void run(final Result result) {
+        VirtualFile virtualDir = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(target);
+        assertNotNull(target.getAbsolutePath() + " not found", virtualDir);
+        virtualDir.refresh(false, true);
+      }
+    }.execute();
+  }
+
+  protected Module addModule(final String moduleName, final @Nullable VirtualFile sourceRoot) {
+    return new WriteAction<Module>() {
+      @Override
+      protected void run(final Result<Module> result) {
+        final Module module = createModule(moduleName);
+        if (sourceRoot != null) {
+          PsiTestUtil.addSourceContentToRoots(module, sourceRoot);
+        }
+        ModuleRootModificationUtil.setModuleSdk(module, getTestProjectJdk());
+        result.setResult(module);
+      }
+    }.execute().getResultObject();
+  }
+
+  protected VirtualFile createFile(final String path) {
+    return createFile(path, "");
+  }
+
+  protected VirtualFile createFile(final String path, final String text) {
+    return VfsTestUtil.createFile(getBaseDir(), path, text);
+  }
+
+  protected CompilationLog make(final Artifact... artifacts) {
+    final CompileScope scope = ArtifactCompileScope.createArtifactsScope(myProject, Arrays.asList(artifacts));
+    return make(scope, CompilerFilter.ALL);
+  }
+
+  protected CompilationLog recompile(final Artifact... artifacts) {
+    final CompileScope scope = ArtifactCompileScope.createArtifactsScope(myProject, Arrays.asList(artifacts));
+    return compile(scope, CompilerFilter.ALL, true);
+  }
+
+  protected CompilationLog make(Module... modules) {
+    return make(getCompilerManager().createModulesCompileScope(modules, false), CompilerFilter.ALL);
+  }
+
+  protected CompilationLog recompile(Module... modules) {
+    return compile(getCompilerManager().createModulesCompileScope(modules, false), CompilerFilter.ALL, true);
+  }
+
+  protected CompilerManager getCompilerManager() {
+    return CompilerManager.getInstance(myProject);
+  }
+
+  protected void assertModulesUpToDate() {
+    boolean upToDate = getCompilerManager().isUpToDate(getCompilerManager().createProjectCompileScope(myProject));
+    assertTrue("Modules are not up to date", upToDate);
+  }
+
+  protected CompilationLog compile(boolean force, VirtualFile... files) {
+    return compile(getCompilerManager().createFilesCompileScope(files), CompilerFilter.ALL, force);
+  }
+
+  protected CompilationLog make(final CompileScope scope, final CompilerFilter filter) {
+    return compile(scope, filter, false);
+  }
+
+  protected CompilationLog compile(final CompileScope scope, final CompilerFilter filter, final boolean forceCompile) {
+    return compile(scope, filter, forceCompile, false);
+  }
+
+  protected CompilationLog compile(final CompileScope scope, final CompilerFilter filter, final boolean forceCompile,
+                                   final boolean errorsExpected) {
+    return compile(errorsExpected, new ParameterizedRunnable<CompileStatusNotification>() {
+      @Override
+      public void run(CompileStatusNotification callback) {
+        final CompilerManager compilerManager = getCompilerManager();
+        if (forceCompile) {
+          Assert.assertSame("Only 'ALL' filter is supported for forced compilation", CompilerFilter.ALL, filter);
+          compilerManager.compile(scope, callback);
+        }
+        else {
+          compilerManager.make(scope, filter, callback);
+        }
+      }
+    });
+  }
+
+  protected CompilationLog rebuild() {
+    return compile(false, new ParameterizedRunnable<CompileStatusNotification>() {
+      @Override
+      public void run(CompileStatusNotification compileStatusNotification) {
+        getCompilerManager().rebuild(compileStatusNotification);
+      }
+    });
+  }
+
+  protected CompilationLog compile(final boolean errorsExpected, final ParameterizedRunnable<CompileStatusNotification> action) {
+    CompilationLog log = compile(action);
+    if (errorsExpected && log.myErrors.length == 0) {
+      Assert.fail("compilation finished without errors");
+    }
+    else if (!errorsExpected && log.myErrors.length > 0) {
+      Assert.fail("compilation finished with errors: " + Arrays.toString(log.myErrors));
+    }
+    return log;
+  }
+
+  private CompilationLog compile(final ParameterizedRunnable<CompileStatusNotification> action) {
+    final Ref<CompilationLog> result = Ref.create(null);
+    final Semaphore semaphore = new Semaphore();
+    semaphore.down();
+    UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+      @Override
+      public void run() {
+
+        CompilerManagerImpl.testSetup();
+        final CompileStatusNotification callback = new CompileStatusNotification() {
+          @Override
+          public void finished(boolean aborted, int errors, int warnings, CompileContext compileContext) {
+            try {
+              if (aborted) {
+                Assert.fail("compilation aborted");
+              }
+              ExitStatus status = CompileDriver.getExternalBuildExitStatus(compileContext);
+              result.set(new CompilationLog(status == ExitStatus.UP_TO_DATE,
+                                            CompilerManagerImpl.getPathsToRecompile(), CompilerManagerImpl.getPathsToDelete(),
+                                            compileContext.getMessages(CompilerMessageCategory.ERROR),
+                                            compileContext.getMessages(CompilerMessageCategory.WARNING)));
+            }
+            finally {
+              semaphore.up();
+            }
+          }
+        };
+        if (useExternalCompiler()) {
+          myProject.save();
+          CompilerTestUtil.saveSdkTable();
+          CompilerTestUtil.scanSourceRootsToRecompile(myProject);
+        }
+        action.run(callback);
+      }
+    });
+
+    final long start = System.currentTimeMillis();
+    while (!semaphore.waitFor(10)) {
+      if (System.currentTimeMillis() - start > 60 * 1000) {
+        throw new RuntimeException("timeout");
+      }
+      if (SwingUtilities.isEventDispatchThread()) {
+        UIUtil.dispatchAllInvocationEvents();
+      }
+    }
+    if (SwingUtilities.isEventDispatchThread()) {
+      UIUtil.dispatchAllInvocationEvents();
+    }
+
+    return result.get();
+  }
+
+  private Set<String> getRelativePaths(String[] paths) {
+    final Set<String> set = new THashSet<String>();
+    final String basePath = myProject.getBaseDir().getPath();
+    for (String path : paths) {
+      set.add(StringUtil.trimStart(StringUtil.trimStart(FileUtil.toSystemIndependentName(path), basePath), "/"));
+    }
+    return set;
+  }
+
+  protected void changeFile(VirtualFile file) {
+    changeFile(file, null);
+  }
+
+  protected void changeFile(VirtualFile file, final String newText) {
+    try {
+      if (newText != null) {
+        VfsUtil.saveText(file, newText);
+      }
+      ((NewVirtualFile)file).setTimeStamp(file.getTimeStamp() + 10);
+    }
+    catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  protected void deleteFile(final VirtualFile file) {
+    new WriteAction() {
+      @Override
+      protected void run(final Result result) {
+        try {
+          file.delete(this);
+        }
+        catch (IOException e) {
+          throw new AssertionError(e);
+        }
+      }
+    }.execute();
+  }
+
+  @Override
+  protected void setUpProject() throws Exception {
+    super.setUpProject();
+    final String baseUrl = myProject.getBaseDir().getUrl();
+    CompilerProjectExtension.getInstance(myProject).setCompilerOutputUrl(baseUrl + "/out");
+  }
+
+  @Override
+  protected File getIprFile() throws IOException {
+    File iprFile = super.getIprFile();
+    if (useExternalCompiler()) {
+      FileUtil.delete(iprFile);
+    }
+    return iprFile;
+  }
+
+  @Override
+  protected Module doCreateRealModule(String moduleName) {
+    if (useExternalCompiler()) {
+      //todo[nik] reuse code from PlatformTestCase
+      final VirtualFile baseDir = myProject.getBaseDir();
+      Assert.assertNotNull(baseDir);
+      final File moduleFile = new File(baseDir.getPath().replace('/', File.separatorChar), moduleName + ModuleFileType.DOT_DEFAULT_EXTENSION);
+      PlatformTestCase.myFilesToDelete.add(moduleFile);
+      return new WriteAction<Module>() {
+        @Override
+        protected void run(Result<Module> result) throws Throwable {
+          Module module = ModuleManager.getInstance(myProject)
+            .newModule(FileUtil.toSystemIndependentName(moduleFile.getAbsolutePath()), getModuleType().getId());
+          module.getModuleFile();
+          result.setResult(module);
+        }
+      }.execute().getResultObject();
+    }
+    return super.doCreateRealModule(moduleName);
+  }
+
+  @Override
+  protected boolean isRunInWriteAction() {
+    return false;
+  }
+
+  protected static void assertOutput(Module module, TestFileSystemBuilder item) {
+    File outputDir = getOutputDir(module);
+    Assert.assertTrue("Output directory " + outputDir.getAbsolutePath() + " doesn't exist", outputDir.exists());
+    item.build().assertDirectoryEqual(outputDir);
+  }
+
+  protected static void assertNoOutput(Module module) {
+    File dir = getOutputDir(module);
+    Assert.assertFalse("Output directory " + dir.getAbsolutePath() + " does exist", dir.exists());
+  }
+
+  protected static File getOutputDir(Module module) {
+    CompilerModuleExtension extension = CompilerModuleExtension.getInstance(module);
+    Assert.assertNotNull(extension);
+    String outputUrl = extension.getCompilerOutputUrl();
+    Assert.assertNotNull("Output directory for module '" + module.getName() + "' isn't specified", outputUrl);
+    return JpsPathUtil.urlToFile(outputUrl);
+  }
+
+  protected class CompilationLog {
+    private final Set<String> myRecompiledPaths;
+    private final Set<String> myDeletedPaths;
+    private final boolean myExternalBuildUpToDate;
+    private final CompilerMessage[] myErrors;
+    private final CompilerMessage[] myWarnings;
+
+    public CompilationLog(boolean externalBuildUpToDate, String[] recompiledPaths, String[] deletedPaths, CompilerMessage[] errors, CompilerMessage[] warnings) {
+      myExternalBuildUpToDate = externalBuildUpToDate;
+      myErrors = errors;
+      myWarnings = warnings;
+      myRecompiledPaths = getRelativePaths(recompiledPaths);
+      myDeletedPaths = getRelativePaths(deletedPaths);
+    }
+
+    public void assertUpToDate() {
+      if (useExternalCompiler()) {
+        assertTrue(myExternalBuildUpToDate);
+      }
+      else {
+        checkRecompiled();
+        checkDeleted();
+      }
+    }
+
+    public void assertRecompiled(String... expected) {
+      checkRecompiled(expected);
+      checkDeleted();
+    }
+
+    public void assertDeleted(String... expected) {
+      checkRecompiled();
+      checkDeleted(expected);
+    }
+
+    public void assertRecompiledAndDeleted(String[] recompiled, String... deleted) {
+      checkRecompiled(recompiled);
+      checkDeleted(deleted);
+    }
+
+    private void checkRecompiled(String... expected) {
+      assertSet("recompiled", myRecompiledPaths, expected);
+    }
+
+    private void checkDeleted(String... expected) {
+      assertSet("deleted", myDeletedPaths, expected);
+    }
+
+    public CompilerMessage[] getErrors() {
+      return myErrors;
+    }
+
+    public CompilerMessage[] getWarnings() {
+      return myWarnings;
+    }
+
+    private void assertSet(String name, Set<String> actual, String[] expected) {
+      if (useExternalCompiler()) return;
+      for (String path : expected) {
+        if (!actual.remove(path)) {
+          Assert.fail("'" + path + "' is not " + name + ". " + name + ": " + new HashSet<String>(actual));
+        }
+      }
+      if (!actual.isEmpty()) {
+        Assert.fail("'" + actual.iterator().next() + "' must not be " + name);
+      }
+    }
+  }
+}
diff --git a/java/compiler/impl/testSrc/com/intellij/compiler/CompilerTestUtil.java b/java/compiler/impl/testSrc/com/intellij/compiler/CompilerTestUtil.java
new file mode 100644
index 0000000..69cb63a
--- /dev/null
+++ b/java/compiler/impl/testSrc/com/intellij/compiler/CompilerTestUtil.java
@@ -0,0 +1,87 @@
+package com.intellij.compiler;
+
+import com.intellij.compiler.impl.TranslatingCompilerFilesMonitor;
+import com.intellij.compiler.impl.javaCompiler.javac.JavacConfiguration;
+import com.intellij.compiler.server.BuildManager;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.application.ex.ApplicationManagerEx;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.ProjectJdkTable;
+import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
+import com.intellij.openapi.projectRoots.impl.ProjectJdkTableImpl;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.JDOMUtil;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.SystemProperties;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerOptions;
+import org.jetbrains.jps.model.serialization.JDomSerializationUtil;
+import org.jetbrains.jps.model.serialization.JpsGlobalLoader;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public class CompilerTestUtil {
+  private CompilerTestUtil() {
+  }
+
+  public static void setupJavacForTests(Project project) {
+    CompilerConfigurationImpl compilerConfiguration = (CompilerConfigurationImpl)CompilerConfiguration.getInstance(project);
+    compilerConfiguration.projectOpened();
+    compilerConfiguration.setDefaultCompiler(compilerConfiguration.getJavacCompiler());
+
+    JpsJavaCompilerOptions javacSettings = JavacConfiguration.getOptions(project, JavacConfiguration.class);
+    javacSettings.setTestsUseExternalCompiler(true);
+  }
+
+  public static void scanSourceRootsToRecompile(Project project) {
+    // need this to emulate project opening
+    final List<VirtualFile> roots = Arrays.asList(ProjectRootManager.getInstance(project).getContentSourceRoots());
+    TranslatingCompilerFilesMonitor.getInstance().scanSourceContent(new TranslatingCompilerFilesMonitor.ProjectRef(project), roots, roots.size(), true);
+  }
+
+  public static void saveSdkTable() {
+    try {
+      ProjectJdkTableImpl table = (ProjectJdkTableImpl)ProjectJdkTable.getInstance();
+      File sdkFile = table.getExportFiles()[0];
+      FileUtil.createParentDirs(sdkFile);
+      Element root = new Element("application");
+      root.addContent(JDomSerializationUtil.createComponentElement(JpsGlobalLoader.SDK_TABLE_COMPONENT_NAME).addContent(table.getState().cloneContent()));
+      JDOMUtil.writeDocument(new Document(root), sdkFile, SystemProperties.getLineSeparator());
+    }
+    catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public static void enableExternalCompiler(final Project project) {
+    new WriteAction() {
+      protected void run(final Result result) {
+        CompilerWorkspaceConfiguration.getInstance(project).USE_COMPILE_SERVER = true;
+        ApplicationManagerEx.getApplicationEx().doNotSave(false);
+        JavaAwareProjectJdkTableImpl table = JavaAwareProjectJdkTableImpl.getInstanceEx();
+        table.addJdk(table.getInternalJdk());
+      }
+    }.execute();
+  }
+
+  public static void disableExternalCompiler(final Project project) {
+    new WriteAction() {
+      protected void run(final Result result) {
+        CompilerWorkspaceConfiguration.getInstance(project).USE_COMPILE_SERVER = false;
+        ApplicationManagerEx.getApplicationEx().doNotSave(true);
+        JavaAwareProjectJdkTableImpl table = JavaAwareProjectJdkTableImpl.getInstanceEx();
+        table.removeJdk(table.getInternalJdk());
+        BuildManager.getInstance().stopWatchingProject(project);
+      }
+    }.execute();
+  }
+}
diff --git a/java/compiler/impl/testSrc/com/intellij/compiler/artifacts/ArtifactCompilerTestCase.java b/java/compiler/impl/testSrc/com/intellij/compiler/artifacts/ArtifactCompilerTestCase.java
new file mode 100644
index 0000000..ad03189
--- /dev/null
+++ b/java/compiler/impl/testSrc/com/intellij/compiler/artifacts/ArtifactCompilerTestCase.java
@@ -0,0 +1,116 @@
+package com.intellij.compiler.artifacts;
+
+import com.intellij.compiler.BaseCompilerTestCase;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.DependencyScope;
+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.LocalFileSystem;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.artifacts.ModifiableArtifactModel;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.impl.artifacts.PlainArtifactType;
+import com.intellij.util.io.TestFileSystemBuilder;
+import com.intellij.util.io.TestFileSystemItem;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+
+import static com.intellij.compiler.artifacts.ArtifactsTestCase.commitModel;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactCompilerTestCase extends BaseCompilerTestCase {
+
+  protected void deleteArtifact(final Artifact artifact) {
+    final ModifiableArtifactModel model = getArtifactManager().createModifiableModel();
+    model.removeArtifact(artifact);
+    commitModel(model);
+  }
+
+  protected Artifact addArtifact(TestPackagingElementBuilder builder) {
+    return addArtifact("a", builder);
+  }
+
+  protected Artifact addArtifact(final String name, TestPackagingElementBuilder builder) {
+    return addArtifact(name, builder.build());
+  }
+
+  protected Artifact addArtifact(String name, final CompositePackagingElement<?> root) {
+    return addArtifact(name, PlainArtifactType.getInstance(), root);
+  }
+
+  protected Artifact addArtifact(final String name, final ArtifactType type, final CompositePackagingElement<?> root) {
+    return getArtifactManager().addArtifact(name, type, root);
+  }
+
+  protected Library addProjectLibrary(final @Nullable Module module, final String name, final VirtualFile... jars) {
+    return addProjectLibrary(module, name, DependencyScope.COMPILE, jars);
+  }
+
+  protected Library addProjectLibrary(final @Nullable Module module, final String name, final DependencyScope scope,
+                                      final VirtualFile... jars) {
+    return PackagingElementsTestCase.addProjectLibrary(myProject, module, name, scope, jars);
+  }
+
+  protected TestPackagingElementBuilder root() {
+    return TestPackagingElementBuilder.root(myProject);
+  }
+
+  protected TestPackagingElementBuilder archive(String name) {
+    return TestPackagingElementBuilder.archive(myProject, name);
+  }
+
+  protected CompilationLog compileProject() {
+    return make(getArtifactManager().getArtifacts());
+  }
+
+  protected void changeFileInJar(String jarPath, String pathInJar) throws Exception {
+    final VirtualFile jarFile = LocalFileSystem.getInstance().findFileByPath(jarPath);
+    assertNotNull(jarFile);
+    final VirtualFile jarRoot = JarFileSystem.getInstance().getJarRootForLocalFile(jarFile);
+    assertNotNull(jarRoot);
+    VirtualFile jarEntry = jarRoot.findFileByRelativePath(pathInJar);
+    assertNotNull(jarEntry);
+    assertNotNull(jarFile);
+    changeFile(jarFile);
+    jarFile.refresh(false, false);
+
+    jarEntry = jarRoot.findFileByRelativePath(pathInJar);
+    assertNotNull(jarEntry);
+  }
+
+  protected static TestFileSystemBuilder fs() {
+    return TestFileSystemItem.fs();
+  }
+
+  public static void assertNoOutput(Artifact artifact) {
+    final String outputPath = artifact.getOutputPath();
+    assertNotNull("output path not specified for " + artifact.getName(), outputPath);
+    assertFalse(new File(FileUtil.toSystemDependentName(outputPath)).exists());
+  }
+
+  public static void assertEmptyOutput(Artifact a1) throws IOException {
+    assertOutput(a1, ArtifactCompilerTestCase.fs());
+  }
+
+  public static void assertOutput(Artifact artifact, TestFileSystemBuilder item) {
+    final VirtualFile outputFile = getOutputDir(artifact);
+    outputFile.refresh(false, true);
+    item.build().assertDirectoryEqual(VfsUtil.virtualToIoFile(outputFile));
+  }
+
+  protected static VirtualFile getOutputDir(Artifact artifact) {
+    final String output = artifact.getOutputPath();
+    assertNotNull("output path not specified for " + artifact.getName(), output);
+    final VirtualFile outputFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(output);
+    assertNotNull("output file not found " + output, outputFile);
+    return outputFile;
+  }
+}
diff --git a/java/compiler/impl/testSrc/com/intellij/compiler/artifacts/ArtifactsTestCase.java b/java/compiler/impl/testSrc/com/intellij/compiler/artifacts/ArtifactsTestCase.java
new file mode 100644
index 0000000..9c60d0c
--- /dev/null
+++ b/java/compiler/impl/testSrc/com/intellij/compiler/artifacts/ArtifactsTestCase.java
@@ -0,0 +1,291 @@
+package com.intellij.compiler.artifacts;
+
+import com.intellij.facet.Facet;
+import com.intellij.facet.impl.DefaultFacetsProvider;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.module.ModifiableModuleModel;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.ModuleRootModificationUtil;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.DefaultModulesProvider;
+import com.intellij.openapi.roots.ui.configuration.FacetsProvider;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.openapi.roots.ui.configuration.artifacts.*;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.*;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.ManifestFileProvider;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.impl.artifacts.PlainArtifactType;
+import com.intellij.packaging.impl.elements.ManifestFileUtil;
+import com.intellij.packaging.ui.ArtifactEditor;
+import com.intellij.packaging.ui.ManifestFileConfiguration;
+import com.intellij.testFramework.IdeaTestCase;
+import com.intellij.testFramework.PsiTestUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactsTestCase extends IdeaTestCase {
+  protected boolean mySetupModule;
+
+  protected ArtifactManager getArtifactManager() {
+    return ArtifactManager.getInstance(myProject);
+  }
+
+  @Override
+  protected void setUpModule() {
+    if (mySetupModule) {
+      super.setUpModule();
+    }
+  }
+
+  protected void deleteArtifact(final Artifact artifact) {
+    final ModifiableArtifactModel model = getArtifactManager().createModifiableModel();
+    model.removeArtifact(artifact);
+    commitModel(model);
+  }
+
+  protected static void commitModel(final ModifiableArtifactModel model) {
+    new WriteAction() {
+      @Override
+      protected void run(final Result result) {
+        model.commit();
+      }
+    }.execute();
+  }
+
+  protected Artifact rename(Artifact artifact, String newName) {
+    final ModifiableArtifactModel model = getArtifactManager().createModifiableModel();
+    model.getOrCreateModifiableArtifact(artifact).setName(newName);
+    commitModel(model);
+    return artifact;
+  }
+
+  protected Artifact addArtifact(String name) {
+    return addArtifact(name, null);
+  }
+
+  protected Artifact addArtifact(String name, final CompositePackagingElement<?> root) {
+    return addArtifact(name, PlainArtifactType.getInstance(), root);
+  }
+
+  protected Artifact addArtifact(final String name, final ArtifactType type, final CompositePackagingElement<?> root) {
+    return getArtifactManager().addArtifact(name, type, root);
+  }
+
+  protected PackagingElementResolvingContext getContext() {
+    return ArtifactManager.getInstance(myProject).getResolvingContext();
+  }
+
+  public static void renameFile(final VirtualFile file, final String newName) {
+    new WriteAction() {
+      @Override
+      protected void run(final Result result) {
+        try {
+          file.rename(IdeaTestCase.class, newName);
+        }
+        catch (IOException e) {
+          throw new RuntimeException(e);
+        }
+      }
+    }.execute();
+  }
+
+  protected Module addModule(final String moduleName, final @Nullable VirtualFile sourceRoot) {
+    return new WriteAction<Module>() {
+      @Override
+      protected void run(final Result<Module> result) {
+        final Module module = createModule(moduleName);
+        if (sourceRoot != null) {
+          PsiTestUtil.addSourceContentToRoots(module, sourceRoot);
+        }
+        ModuleRootModificationUtil.setModuleSdk(module, getTestProjectJdk());
+        result.setResult(module);
+      }
+    }.execute().getResultObject();
+  }
+
+  public static class MockPackagingEditorContext extends ArtifactEditorContextImpl {
+    public MockPackagingEditorContext(ArtifactsStructureConfigurableContext parent, final ArtifactEditorEx editor) {
+      super(parent, editor);
+    }
+
+    @Override
+    public void selectArtifact(@NotNull Artifact artifact) {
+    }
+
+    @Override
+    public void selectFacet(@NotNull Facet<?> facet) {
+    }
+
+    @Override
+    public void selectModule(@NotNull Module module) {
+    }
+
+    @Override
+    public void selectLibrary(@NotNull Library library) {
+    }
+
+    @Override
+    public void queueValidation() {
+    }
+
+    @Override
+    public List<Artifact> chooseArtifacts(List<? extends Artifact> artifacts, String title) {
+      return new ArrayList<Artifact>(artifacts);
+    }
+
+    @Override
+    public List<Module> chooseModules(List<Module> modules, String title) {
+      return modules;
+    }
+
+    @Override
+    public List<Library> chooseLibraries(String title) {
+      return Collections.emptyList();
+    }
+  }
+
+  public class MockArtifactsStructureConfigurableContext implements ArtifactsStructureConfigurableContext {
+    private ModifiableArtifactModel myModifiableModel;
+    private final Map<Module, ModifiableRootModel> myModifiableRootModels = new HashMap<Module, ModifiableRootModel>();
+    private final Map<CompositePackagingElement<?>, ManifestFileConfiguration> myManifestFiles =
+      new HashMap<CompositePackagingElement<?>, ManifestFileConfiguration>();
+    private final ArtifactEditorManifestFileProvider myManifestFileProvider = new ArtifactEditorManifestFileProvider(this);
+
+    @Override
+    @NotNull
+    public ModifiableArtifactModel getOrCreateModifiableArtifactModel() {
+      if (myModifiableModel == null) {
+        myModifiableModel = ArtifactManager.getInstance(myProject).createModifiableModel();
+      }
+      return myModifiableModel;
+    }
+
+    @Override
+    public ModifiableModuleModel getModifiableModuleModel() {
+      return null;
+    }
+
+    @Override
+    @NotNull
+    public ModifiableRootModel getOrCreateModifiableRootModel(@NotNull Module module) {
+      ModifiableRootModel model = myModifiableRootModels.get(module);
+      if (model == null) {
+        model = ModuleRootManager.getInstance(module).getModifiableModel();
+        myModifiableRootModels.put(module, model);
+      }
+      return model;
+    }
+
+    @Override
+    public ArtifactEditorSettings getDefaultSettings() {
+      return new ArtifactEditorSettings();
+    }
+
+    @Override
+    @NotNull
+    public Project getProject() {
+      return myProject;
+    }
+
+    @Override
+    @NotNull
+    public ArtifactModel getArtifactModel() {
+      if (myModifiableModel != null) {
+        return myModifiableModel;
+      }
+      return ArtifactManager.getInstance(myProject);
+    }
+
+    public void commitModel() {
+      if (myModifiableModel != null) {
+        myModifiableModel.commit();
+      }
+    }
+
+    @Override
+    @NotNull
+    public ModulesProvider getModulesProvider() {
+      return new DefaultModulesProvider(myProject);
+    }
+
+    @Override
+    @NotNull
+    public FacetsProvider getFacetsProvider() {
+      return DefaultFacetsProvider.INSTANCE;
+    }
+
+    @Override
+    public Library findLibrary(@NotNull String level, @NotNull String libraryName) {
+      return ArtifactManager.getInstance(myProject).getResolvingContext().findLibrary(level, libraryName);
+    }
+
+    @NotNull
+    @Override
+    public ManifestFileProvider getManifestFileProvider() {
+      return myManifestFileProvider;
+    }
+
+    @Override
+    public ManifestFileConfiguration getManifestFile(CompositePackagingElement<?> element, ArtifactType artifactType) {
+      final VirtualFile manifestFile = ManifestFileUtil.findManifestFile(element, this, PlainArtifactType.getInstance());
+      if (manifestFile == null) {
+        return null;
+      }
+
+      ManifestFileConfiguration configuration = myManifestFiles.get(element);
+      if (configuration == null) {
+        configuration = ManifestFileUtil.createManifestFileConfiguration(manifestFile);
+        myManifestFiles.put(element, configuration);
+      }
+      return configuration;
+    }
+
+    @Override
+    public CompositePackagingElement<?> getRootElement(@NotNull Artifact artifact) {
+      return artifact.getRootElement();
+    }
+
+    @Override
+    public void editLayout(@NotNull Artifact artifact, Runnable action) {
+      final ModifiableArtifact modifiableArtifact = getOrCreateModifiableArtifactModel().getOrCreateModifiableArtifact(artifact);
+      modifiableArtifact.setRootElement(artifact.getRootElement());
+      action.run();
+    }
+
+    @Override
+    public ArtifactEditor getOrCreateEditor(Artifact artifact) {
+      throw new UnsupportedOperationException("'getOrCreateEditor' not implemented in " + getClass().getName());
+    }
+
+    @Override
+    @NotNull
+    public Artifact getOriginalArtifact(@NotNull Artifact artifact) {
+      if (myModifiableModel != null) {
+        return myModifiableModel.getOriginalArtifact(artifact);
+      }
+      return artifact;
+    }
+
+    @Override
+    public void queueValidation(Artifact artifact) {
+    }
+
+    @Override
+    @NotNull
+    public ArtifactProjectStructureElement getOrCreateArtifactElement(@NotNull Artifact artifact) {
+      throw new UnsupportedOperationException("'getOrCreateArtifactElement' not implemented in " + getClass().getName());
+    }
+  }
+}
diff --git a/java/compiler/impl/testSrc/com/intellij/compiler/artifacts/ArtifactsTestUtil.java b/java/compiler/impl/testSrc/com/intellij/compiler/artifacts/ArtifactsTestUtil.java
new file mode 100644
index 0000000..3eb811f
--- /dev/null
+++ b/java/compiler/impl/testSrc/com/intellij/compiler/artifacts/ArtifactsTestUtil.java
@@ -0,0 +1,133 @@
+package com.intellij.compiler.artifacts;
+
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactManager;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.artifacts.ModifiableArtifactModel;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementFactory;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.impl.elements.ArchivePackagingElement;
+import com.intellij.packaging.impl.elements.DirectoryPackagingElement;
+import com.intellij.packaging.impl.elements.ManifestFileUtil;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+
+/**
+ * @author nik
+ */
+public class ArtifactsTestUtil {
+  public static String printToString(PackagingElement element, int level) {
+    StringBuilder builder = new StringBuilder(StringUtil.repeatSymbol(' ', level));
+    if (element instanceof ArchivePackagingElement) {
+      builder.append(((ArchivePackagingElement)element).getArchiveFileName());
+    }
+    else if (element instanceof DirectoryPackagingElement) {
+      builder.append(((DirectoryPackagingElement)element).getDirectoryName()).append("/");
+    }
+    else {
+      builder.append(element.toString());
+    }
+    builder.append("\n");
+    if (element instanceof CompositePackagingElement) {
+      for (PackagingElement<?> child : ((CompositePackagingElement<?>)element).getChildren()) {
+        builder.append(printToString(child, level + 1));
+      }
+    }
+    return builder.toString();
+  }
+
+  public static void assertLayout(PackagingElement element, String expected) {
+    assertEquals(adjustMultiLine(expected), printToString(element, 0));
+  }
+
+  private static String adjustMultiLine(String expected) {
+    final List<String> strings = StringUtil.split(StringUtil.trimStart(expected, "\n"), "\n");
+    int min = Integer.MAX_VALUE;
+    for (String s : strings) {
+      int k = 0;
+      while (k < s.length() && s.charAt(k) == ' ') {
+        k++;
+      }
+      min = Math.min(min, k);
+    }
+    List<String> lines = new ArrayList<String>();
+    for (String s : strings) {
+      lines.add(s.substring(min));
+    }
+    return StringUtil.join(lines, "\n") + "\n";      
+  }
+
+  public static void assertLayout(Project project, String artifactName, String expected) {
+    assertLayout(findArtifact(project, artifactName).getRootElement(), expected);
+  }
+
+  public static void assertOutputPath(Project project, String artifactName, String expected) {
+    assertEquals(expected, findArtifact(project, artifactName).getOutputPath());
+  }
+
+  public static void assertOutputFileName(Project project, String artifactName, String expected) {
+    assertEquals(expected, findArtifact(project, artifactName).getRootElement().getName());
+  }
+
+  public static void setOutput(final Project project, final String artifactName, final String outputPath) {
+    new WriteAction() {
+      @Override
+      protected void run(final Result result) {
+        final ModifiableArtifactModel model = ArtifactManager.getInstance(project).createModifiableModel();
+        model.getOrCreateModifiableArtifact(findArtifact(project, artifactName)).setOutputPath(outputPath);
+        model.commit();
+      }
+    }.execute();
+  }
+
+  public static void addArtifactToLayout(final Project project, final Artifact parent, final Artifact toAdd) {
+    new WriteAction() {
+      @Override
+      protected void run(final Result result) {
+        final ModifiableArtifactModel model = ArtifactManager.getInstance(project).createModifiableModel();
+        final PackagingElement<?> artifactElement = PackagingElementFactory.getInstance().createArtifactElement(toAdd, project);
+        model.getOrCreateModifiableArtifact(parent).getRootElement().addOrFindChild(artifactElement);
+        model.commit();
+      }
+    }.execute();
+  }
+
+  public static Artifact findArtifact(Project project, String artifactName) {
+    final ArtifactManager manager = ArtifactManager.getInstance(project);
+    final Artifact artifact = manager.findArtifact(artifactName);
+    assertNotNull("'" + artifactName + "' artifact not found", artifact);
+    return artifact;
+  }
+
+  public static void assertManifest(Artifact artifact, PackagingElementResolvingContext context, @Nullable String mainClass, @Nullable String classpath) {
+    final CompositePackagingElement<?> rootElement = artifact.getRootElement();
+    final ArtifactType type = artifact.getArtifactType();
+    assertManifest(rootElement, context, type, mainClass, classpath);
+  }
+
+  public static void assertManifest(CompositePackagingElement<?> rootElement,
+                                     PackagingElementResolvingContext context,
+                                     ArtifactType type,
+                                     @Nullable String mainClass, @Nullable String classpath) {
+    final VirtualFile file = ManifestFileUtil.findManifestFile(rootElement, context, type);
+    assertNotNull(file);
+    final Manifest manifest = ManifestFileUtil.readManifest(file);
+    assertEquals(mainClass, manifest.getMainAttributes().getValue(Attributes.Name.MAIN_CLASS));
+    assertEquals(classpath, manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH));
+  }
+
+}
diff --git a/java/compiler/impl/testSrc/com/intellij/compiler/artifacts/PackagingElementsTestCase.java b/java/compiler/impl/testSrc/com/intellij/compiler/artifacts/PackagingElementsTestCase.java
new file mode 100644
index 0000000..a6a667a
--- /dev/null
+++ b/java/compiler/impl/testSrc/com/intellij/compiler/artifacts/PackagingElementsTestCase.java
@@ -0,0 +1,130 @@
+package com.intellij.compiler.artifacts;
+
+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.project.Project;
+import com.intellij.openapi.roots.DependencyScope;
+import com.intellij.openapi.roots.ModuleRootModificationUtil;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.testFramework.VfsTestUtil;
+import com.intellij.util.PathUtil;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+
+/**
+ * @author nik
+ */
+public abstract class PackagingElementsTestCase extends ArtifactsTestCase {
+  protected Artifact addArtifact(TestPackagingElementBuilder builder) {
+    return addArtifact("a", builder);
+  }
+
+  protected Artifact addArtifact(final String name, TestPackagingElementBuilder builder) {
+    return addArtifact(name, builder.build());
+  }
+
+  protected static void assertLayout(Artifact artifact, String expected) {
+    assertLayout(artifact.getRootElement(), expected);
+  }
+
+  protected static void assertLayout(PackagingElement element, String expected) {
+    ArtifactsTestUtil.assertLayout(element, expected);
+  }
+
+  protected String getProjectBasePath() {
+    return getBaseDir().getPath();
+  }
+
+  protected VirtualFile getBaseDir() {
+    final VirtualFile baseDir = myProject.getBaseDir();
+    assertNotNull(baseDir);
+    return baseDir;
+  }
+
+  protected TestPackagingElementBuilder root() {
+    return TestPackagingElementBuilder.root(myProject);
+  }
+
+  protected TestPackagingElementBuilder archive(String name) {
+    return TestPackagingElementBuilder.archive(myProject, name);
+  }
+
+  protected VirtualFile createFile(final String path) {
+    return createFile(path, "");
+  }
+
+  protected VirtualFile createFile(final String path, final String text) {
+    return VfsTestUtil.createFile(getBaseDir(), path, text);
+  }
+
+  protected VirtualFile createDir(final String path) {
+    return VfsTestUtil.createDir(getBaseDir(), path);
+  }
+
+  protected static VirtualFile getJDomJar() {
+    return getJarFromLibDirectory("jdom.jar");
+  }
+
+  protected static String getLocalJarPath(VirtualFile jarEntry) {
+    return PathUtil.getLocalFile(jarEntry).getPath();
+  }
+
+  protected static String getJUnitJarPath() {
+    return getLocalJarPath(getJarFromLibDirectory("junit.jar"));
+  }
+
+  private static VirtualFile getJarFromLibDirectory(final String relativePath) {
+    final File file = PathManager.findFileInLibDirectory(relativePath);
+    final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByIoFile(file);
+    assertNotNull(file.getAbsolutePath() + " not found", virtualFile);
+    final VirtualFile jarRoot = JarFileSystem.getInstance().getJarRootForLocalFile(virtualFile);
+    assertNotNull(jarRoot);
+    return jarRoot;
+  }
+
+  protected Library addProjectLibrary(final @Nullable Module module, final String name, final VirtualFile... jars) {
+    return addProjectLibrary(module, name, DependencyScope.COMPILE, jars);
+  }
+
+  protected Library addProjectLibrary(final @Nullable Module module, final String name, final DependencyScope scope,
+                                      final VirtualFile... jars) {
+    return addProjectLibrary(myProject, module, name, scope, jars);
+  }
+
+  static Library addProjectLibrary(final Project project, final @Nullable Module module, final String name, final DependencyScope scope,
+                                   final VirtualFile[] jars) {
+    return new WriteAction<Library>() {
+      @Override
+      protected void run(final Result<Library> result) {
+        final Library library = LibraryTablesRegistrar.getInstance().getLibraryTable(project).createLibrary(name);
+        final Library.ModifiableModel libraryModel = library.getModifiableModel();
+        for (VirtualFile jar : jars) {
+          libraryModel.addRoot(jar, OrderRootType.CLASSES);
+        }
+        libraryModel.commit();
+        if (module != null) {
+          ModuleRootModificationUtil.addDependency(module, library, scope, false);
+        }
+        result.setResult(library);
+      }
+    }.execute().getResultObject();
+  }
+
+  protected static void addModuleLibrary(final Module module, final VirtualFile jar) {
+    ModuleRootModificationUtil.addModuleLibrary(module, jar.getUrl());
+  }
+
+  protected static void addModuleDependency(final Module module, final Module dependency) {
+    ModuleRootModificationUtil.addDependency(module, dependency);
+  }
+}
diff --git a/java/compiler/impl/testSrc/com/intellij/compiler/artifacts/TestPackagingElementBuilder.java b/java/compiler/impl/testSrc/com/intellij/compiler/artifacts/TestPackagingElementBuilder.java
new file mode 100644
index 0000000..3c1f6376
--- /dev/null
+++ b/java/compiler/impl/testSrc/com/intellij/compiler/artifacts/TestPackagingElementBuilder.java
@@ -0,0 +1,101 @@
+package com.intellij.compiler.artifacts;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementFactory;
+
+/**
+* @author nik
+*/
+public class TestPackagingElementBuilder {
+  private final CompositePackagingElement<?> myElement;
+  private final TestPackagingElementBuilder myParent;
+  private final Project myProject;
+
+  private TestPackagingElementBuilder(Project project, CompositePackagingElement<?> element, TestPackagingElementBuilder parent) {
+    myElement = element;
+    myParent = parent;
+    myProject = project;
+  }
+
+  public static TestPackagingElementBuilder root(Project project) {
+    return new TestPackagingElementBuilder(project, PackagingElementFactory.getInstance().createArtifactRootElement(), null);
+  }
+
+  public static TestPackagingElementBuilder archive(final Project project, String name) {
+    return new TestPackagingElementBuilder(project, PackagingElementFactory.getInstance().createArchive(name), null);
+  }
+
+  public CompositePackagingElement<?> build() {
+    TestPackagingElementBuilder builder = this;
+    while (builder.myParent != null) {
+      builder = builder.myParent;
+    }
+    return builder.myElement;
+  }
+
+  public TestPackagingElementBuilder file(VirtualFile file) {
+    return file(file.getPath());
+  }
+
+  public TestPackagingElementBuilder file(String path) {
+    myElement.addOrFindChild(getFactory().createFileCopyWithParentDirectories(path, "/"));
+    return this;
+  }
+
+  private static PackagingElementFactory getFactory() {
+    return PackagingElementFactory.getInstance();
+  }
+
+  public TestPackagingElementBuilder dirCopy(VirtualFile dir) {
+    return dirCopy(dir.getPath());
+  }
+
+  public TestPackagingElementBuilder dirCopy(String path) {
+    myElement.addOrFindChild(getFactory().createDirectoryCopyWithParentDirectories(path, "/"));
+    return this;
+  }
+
+  public TestPackagingElementBuilder extractedDir(String jarPath, String pathInJar) {
+    myElement.addOrFindChild(getFactory().createExtractedDirectoryWithParentDirectories(jarPath, pathInJar, "/"));
+    return this;
+  }
+
+  public TestPackagingElementBuilder module(Module module) {
+    myElement.addOrFindChild(getFactory().createModuleOutput(module));
+    return this;
+  }
+
+  public TestPackagingElementBuilder lib(Library library) {
+    myElement.addOrFindChildren(getFactory().createLibraryElements(library));
+    return this;
+  }
+
+  public TestPackagingElementBuilder artifact(Artifact artifact) {
+    myElement.addOrFindChild(getFactory().createArtifactElement(artifact, myProject));
+    return this;
+  }
+
+  public TestPackagingElementBuilder archive(String name) {
+    final CompositePackagingElement<?> archive = getFactory().createArchive(name);
+    return new TestPackagingElementBuilder(myProject, myElement.addOrFindChild(archive), this);
+  }
+
+  public TestPackagingElementBuilder dir(String name) {
+    return new TestPackagingElementBuilder(myProject, myElement.addOrFindChild(getFactory().createDirectory(name)), this);
+  }
+
+  public TestPackagingElementBuilder add(PackagingElement<?> element) {
+    myElement.addOrFindChild(element);
+    return this;
+  }
+
+  public TestPackagingElementBuilder end() {
+    return myParent;
+  }
+}
diff --git a/java/compiler/instrumentation-util/instrumentation-util.iml b/java/compiler/instrumentation-util/instrumentation-util.iml
new file mode 100644
index 0000000..60d62d3
--- /dev/null
+++ b/java/compiler/instrumentation-util/instrumentation-util.iml
@@ -0,0 +1,13 @@
+<?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$/src" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="asm4" level="project" />
+  </component>
+</module>
+
diff --git a/java/compiler/instrumentation-util/src/com/intellij/compiler/instrumentation/InstrumentationClassFinder.java b/java/compiler/instrumentation-util/src/com/intellij/compiler/instrumentation/InstrumentationClassFinder.java
new file mode 100644
index 0000000..abde877
--- /dev/null
+++ b/java/compiler/instrumentation-util/src/com/intellij/compiler/instrumentation/InstrumentationClassFinder.java
@@ -0,0 +1,765 @@
+package com.intellij.compiler.instrumentation;
+
+import org.jetbrains.asm4.ClassReader;
+import org.jetbrains.asm4.ClassVisitor;
+import org.jetbrains.asm4.MethodVisitor;
+import org.jetbrains.asm4.Opcodes;
+import sun.misc.Resource;
+
+import java.io.*;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.*;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: 2/16/12
+ */
+public class InstrumentationClassFinder {
+  private static final PseudoClass[] EMPTY_PSEUDOCLASS_ARRAY = new PseudoClass[0];
+  private static final String CLASS_RESOURCE_EXTENSION = ".class";
+  private static final URL[] URL_EMPTY_ARRAY = new URL[0];
+  private final Map<String, PseudoClass> myLoaded = new HashMap<String, PseudoClass>(); // className -> class object
+  private final ClassFinderClasspath myPlatformClasspath;
+  private final ClassFinderClasspath myClasspath;
+  private final URL[] myPlatformUrls;
+  private final URL[] myClasspathUrls;
+  private ClassLoader myLoader;
+  private byte[] myBuffer;
+
+  public InstrumentationClassFinder(final URL[] cp) {
+    this(URL_EMPTY_ARRAY, cp);
+  }
+
+  public InstrumentationClassFinder(final URL[] platformUrls, final URL[] classpathUrls) {
+    myPlatformUrls = platformUrls;
+    myClasspathUrls = classpathUrls;
+    myPlatformClasspath = new ClassFinderClasspath(platformUrls);
+    myClasspath = new ClassFinderClasspath(classpathUrls);
+  }
+
+  // compatibility with legacy code requiring ClassLoader
+  public ClassLoader getLoader() {
+    ClassLoader loader = myLoader;
+    if (loader != null) {
+      return loader;
+    }
+    final URLClassLoader platformLoader = myPlatformUrls.length > 0 ? new URLClassLoader(myPlatformUrls, null) : null;
+    final ClassLoader cpLoader = new URLClassLoader(myClasspathUrls, platformLoader);
+    loader = new ClassLoader(cpLoader) {
+
+      public InputStream getResourceAsStream(String name) {
+        InputStream is = null;
+        is = super.getResourceAsStream(name);
+        if (is == null) {
+          try {
+            is = InstrumentationClassFinder.this.getResourceAsStream(name);
+          }
+          catch (IOException ignored) {
+          }
+        }
+        return is;
+      }
+
+      protected Class findClass(String name) throws ClassNotFoundException {
+        final InputStream is = lookupClassBeforeClasspath(name.replace('.', '/'));
+        if (is == null) {
+          throw new ClassNotFoundException("Class not found: " + name.replace('/', '.'));  // ensure presentable class name in error message
+        }
+        try {
+          final byte[] bytes = loadBytes(is);
+          return defineClass(name, bytes, 0, bytes.length);
+        }
+        finally {
+          try {
+            is.close();
+          }
+          catch (IOException ignored) {
+          }
+        }
+      }
+    };
+    myLoader = loader;
+    return loader;
+  }
+
+  public void releaseResources() {
+    myPlatformClasspath.releaseResources();
+    myClasspath.releaseResources();
+    myLoaded.clear();
+    myBuffer = null;
+    myLoader = null;
+  }
+
+  public PseudoClass loadClass(final String name) throws IOException, ClassNotFoundException{
+    final String internalName = name.replace('.', '/'); // normalize
+    final PseudoClass aClass = myLoaded.get(internalName);
+    if (aClass != null) {
+      return aClass;
+    }
+
+    final InputStream is = getClassBytesAsStream(internalName);
+
+    if (is == null) {
+      throw new ClassNotFoundException("Class not found: " + name.replace('/', '.')); // ensure presentable class name in error message
+    }
+
+    try {
+      final PseudoClass result = loadPseudoClass(is);
+      myLoaded.put(internalName, result);
+      return result;
+    }
+    finally {
+      is.close();
+    }
+  }
+
+  public void cleanCachedData(String className) {
+    myLoaded.remove(className.replace('.', '/'));
+  }
+
+  public InputStream getClassBytesAsStream(String className) throws IOException {
+    final String internalName = className.replace('.', '/'); // normalize
+    InputStream is = null;
+    // first look into platformCp
+    final String resourceName = internalName + CLASS_RESOURCE_EXTENSION;
+    Resource resource = myPlatformClasspath.getResource(resourceName, false);
+    if (resource != null) {
+      is = resource.getInputStream();
+    }
+    // second look into memory and classspath
+    if (is == null) {
+      is = lookupClassBeforeClasspath(internalName);
+    }
+
+    if (is == null) {
+      resource = myClasspath.getResource(resourceName, false);
+      if (resource != null) {
+        is = resource.getInputStream();
+      }
+    }
+
+    if (is == null) {
+      is = lookupClassAfterClasspath(internalName);
+    }
+    return is;
+  }
+
+  public InputStream getResourceAsStream(String resourceName) throws IOException {
+    InputStream is = null;
+
+    Resource resource = myPlatformClasspath.getResource(resourceName, false);
+    if (resource != null) {
+      is = resource.getInputStream();
+    }
+
+    if (is == null) {
+      resource = myClasspath.getResource(resourceName, false);
+      if (resource != null) {
+        is = resource.getInputStream();
+      }
+    }
+
+    return is;
+  }
+
+  protected InputStream lookupClassBeforeClasspath(final String internalClassName) {
+    return null;
+  }
+
+  protected InputStream lookupClassAfterClasspath(final String internalClassName) {
+    return null;
+  }
+
+  private PseudoClass loadPseudoClass(InputStream is) throws IOException {
+    final ClassReader reader = new ClassReader(is);
+    final V visitor = new V();
+
+    reader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+
+    return new PseudoClass(visitor.myName, visitor.mySuperclassName, visitor.myInterfaces, visitor.myModifiers, visitor.myMethods);
+  }
+  
+  public final class PseudoClass {
+    private final String myName;
+    private final String mySuperClass;
+    private final String[] myInterfaces;
+    private final int myModifiers;
+    private final List<PseudoMethod> myMethods;
+
+    private PseudoClass(final String name, final String superClass, final String[] interfaces, final int modifiers, List<PseudoMethod> methods) {
+      myName = name;
+      mySuperClass = superClass;
+      myInterfaces = interfaces;
+      myModifiers = modifiers;
+      myMethods = methods;
+    }
+
+    public int getModifiers() {
+      return myModifiers;
+    }
+
+    public boolean isInterface() {
+      return (myModifiers & Opcodes.ACC_INTERFACE) > 0;
+    }
+
+    public String getName() {
+      return myName;
+    }
+
+    public List<PseudoMethod> getMethods() {
+      return myMethods;
+    }
+
+    public List<PseudoMethod> findMethods(String name) {
+      final List<PseudoMethod> result = new ArrayList<PseudoMethod>();
+      for (PseudoMethod method : myMethods) {
+        if (method.getName().equals(name)){
+          result.add(method);
+        }
+      }
+      return result;
+    }
+
+    public PseudoMethod findMethod(String name, String descriptor) {
+      for (PseudoMethod method : myMethods) {
+        if (method.getName().equals(name) && method.getSignature().equals(descriptor)){
+          return method;
+        }
+      }
+      return null;
+    }
+
+    public InstrumentationClassFinder getFinder() {
+      return InstrumentationClassFinder.this;
+    }
+
+    public PseudoClass getSuperClass() throws IOException, ClassNotFoundException {
+      final String superClass = mySuperClass;
+      return superClass != null? loadClass(superClass) : null;
+    }
+
+    public PseudoClass[] getInterfaces() throws IOException, ClassNotFoundException {
+      if (myInterfaces == null) {
+        return EMPTY_PSEUDOCLASS_ARRAY;
+      }
+
+      final PseudoClass[] result = new PseudoClass[myInterfaces.length];
+
+      for (int i = 0; i < result.length; i++) {
+        result[i] = loadClass(myInterfaces[i]);
+      }
+
+      return result;
+    }
+
+    public boolean equals (final Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      return getName().equals(((PseudoClass)o).getName());
+    }
+
+    private boolean isSubclassOf(final PseudoClass x) throws IOException, ClassNotFoundException {
+      for (PseudoClass c = this; c != null; c = c.getSuperClass()) {
+        final PseudoClass superClass = c.getSuperClass();
+
+        if (superClass != null && superClass.equals(x)) {
+          return true;
+        }
+      }
+
+      return false;
+    }
+
+    private boolean implementsInterface(final PseudoClass x) throws IOException, ClassNotFoundException {
+      for (PseudoClass c = this; c != null; c = c.getSuperClass()) {
+        final PseudoClass[] tis = c.getInterfaces();
+        for (final PseudoClass ti : tis) {
+          if (ti.equals(x) || ti.implementsInterface(x)) {
+            return true;
+          }
+        }
+      }
+      return false;
+    }
+
+    public boolean isAssignableFrom(final PseudoClass x) throws IOException, ClassNotFoundException {
+      if (this.equals(x)) {
+        return true;
+      }
+      if (x.isSubclassOf(this)) {
+        return true;
+      }
+      if (x.implementsInterface(this)) {
+        return true;
+      }
+      if (x.isInterface() && "java/lang/Object".equals(getName())) {
+        return true;
+      }
+      return false;
+    }
+
+    public boolean hasDefaultPublicConstructor() {
+      for (PseudoMethod method : myMethods) {
+        if ("<init>".equals(method.getName()) && "()V".equals(method.getSignature())) {
+          return true;
+        }
+      }
+      return false;
+    }
+
+    public String getDescriptor() {
+      return "L" + myName + ";";
+    }
+  }
+
+  public static final class PseudoMethod {
+    private final int myAccess;
+    private final String myName;
+    private final String mySignature;
+
+    public PseudoMethod(int access, String name, String signature) {
+      myAccess = access;
+      myName = name;
+      mySignature = signature;
+    }
+
+    public int getModifiers() {
+      return myAccess;
+    }
+
+    public String getName() {
+      return myName;
+    }
+
+    public String getSignature() {
+      return mySignature;
+    }
+  }
+
+  private static class V extends ClassVisitor {
+    public String mySuperclassName = null;
+    public String[] myInterfaces = null;
+    public String myName = null;
+    public int myModifiers;
+    private final List<PseudoMethod> myMethods = new ArrayList<PseudoMethod>();
+
+    private V() {
+      super(Opcodes.ASM4);
+    }
+
+    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+      if ((access & Opcodes.ACC_PUBLIC) > 0) {
+        myMethods.add(new PseudoMethod(access, name, desc));
+      }
+      return super.visitMethod(access, name, desc, signature, exceptions);
+    }
+
+    public void visit(int version, int access, String pName, String signature, String pSuperName, String[] pInterfaces) {
+      mySuperclassName = pSuperName;
+      myInterfaces = pInterfaces;
+      myName = pName;
+      myModifiers = access;
+    }
+  }
+
+  static class ClassFinderClasspath {
+    private static final String FILE_PROTOCOL = "file";
+
+    private final Stack<URL> myUrls = new Stack<URL>();
+    private final List<Loader> myLoaders = new ArrayList<Loader>();
+    private final Map<URL,Loader> myLoadersMap = new HashMap<URL, Loader>();
+
+    public ClassFinderClasspath(URL[] urls) {
+      if (urls.length > 0) {
+        for (int i = urls.length - 1; i >= 0; i--) {
+          myUrls.push(urls[i]);
+        }
+      }
+    }
+
+    public Resource getResource(String s, boolean flag) {
+      int i = 0;
+      for (Loader loader; (loader = getLoader(i)) != null; i++) {
+        Resource resource = loader.getResource(s, flag);
+        if (resource != null) {
+          return resource;
+        }
+      }
+
+      return null;
+    }
+
+    public void releaseResources() {
+      for (Loader loader : myLoaders) {
+        loader.releaseResources();
+      }
+      myLoaders.clear();
+      myLoadersMap.clear();
+    }
+
+    private synchronized Loader getLoader(int i) {
+      while (myLoaders.size() < i + 1) {
+        URL url;
+        synchronized (myUrls) {
+          if (myUrls.empty()) {
+            return null;
+          }
+          url = myUrls.pop();
+        }
+
+        if (myLoadersMap.containsKey(url)) {
+          continue;
+        }
+
+        Loader loader;
+        try {
+          loader = getLoader(url, myLoaders.size());
+          if (loader == null) {
+            continue;
+          }
+        }
+        catch (IOException ioexception) {
+          continue;
+        }
+
+        myLoaders.add(loader);
+        myLoadersMap.put(url, loader);
+      }
+
+      return myLoaders.get(i);
+    }
+
+    private Loader getLoader(final URL url, int index) throws IOException {
+      String s;
+      try {
+        s = url.toURI().getSchemeSpecificPart();
+      }
+      catch (URISyntaxException thisShouldNotHappen) {
+        thisShouldNotHappen.printStackTrace();
+        s = url.getFile();
+      }
+
+      Loader loader = null;
+      if (s != null  && new File(s).isDirectory()) {
+        if (FILE_PROTOCOL.equals(url.getProtocol())) {
+          loader = new FileLoader(url, index);
+        }
+      }
+      else {
+        loader = new JarLoader(url, index);
+      }
+
+      return loader;
+    }
+
+
+    private abstract static class Loader {
+      protected static final String JAR_PROTOCOL = "jar";
+      protected static final String FILE_PROTOCOL = "file";
+
+      private final URL myURL;
+      private final int myIndex;
+
+      protected Loader(URL url, int index) {
+        myURL = url;
+        myIndex = index;
+      }
+
+
+      protected URL getBaseURL() {
+        return myURL;
+      }
+
+      public abstract Resource getResource(final String name, boolean flag);
+
+      public abstract void releaseResources();
+
+      public int getIndex() {
+        return myIndex;
+      }
+    }
+
+    private static class FileLoader extends Loader {
+      private final File myRootDir;
+
+      @SuppressWarnings({"HardCodedStringLiteral"})
+      FileLoader(URL url, int index) throws IOException {
+        super(url, index);
+        if (!FILE_PROTOCOL.equals(url.getProtocol())) {
+          throw new IllegalArgumentException("url");
+        }
+        else {
+          final String s = unescapePercentSequences(url.getFile().replace('/', File.separatorChar));
+          myRootDir = new File(s);
+        }
+      }
+
+      public void releaseResources() {
+      }
+
+      public Resource getResource(final String name, boolean check) {
+        URL url = null;
+        File file = null;
+
+        try {
+          url = new URL(getBaseURL(), name);
+          if (!url.getFile().startsWith(getBaseURL().getFile())) {
+            return null;
+          }
+
+          file = new File(myRootDir, name.replace('/', File.separatorChar));
+          if (!check || file.exists()) {     // check means we load or process resource so we check its existence via old way
+            return new FileResource(name, url, file, !check);
+          }
+        }
+        catch (Exception exception) {
+          if (!check && file != null && file.exists()) {
+            try {   // we can not open the file if it is directory, Resource still can be created
+              return new FileResource(name, url, file, false);
+            }
+            catch (IOException ex) {
+            }
+          }
+        }
+        return null;
+      }
+
+      private class FileResource extends Resource {
+        private final String myName;
+        private final URL myUrl;
+        private final File myFile;
+
+        public FileResource(String name, URL url, File file, boolean willLoadBytes) throws IOException {
+          myName = name;
+          myUrl = url;
+          myFile = file;
+          if (willLoadBytes) getByteBuffer(); // check for existence by creating cached file input stream
+        }
+
+        public String getName() {
+          return myName;
+        }
+
+        public URL getURL() {
+          return myUrl;
+        }
+
+        public URL getCodeSourceURL() {
+          return getBaseURL();
+        }
+
+        public InputStream getInputStream() throws IOException {
+          return new BufferedInputStream(new FileInputStream(myFile));
+        }
+
+        public int getContentLength() throws IOException {
+          return -1;
+        }
+
+        public String toString() {
+          return myFile.getAbsolutePath();
+        }
+      }
+
+      public String toString() {
+        return "FileLoader [" + myRootDir + "]";
+      }
+    }
+
+    private class JarLoader extends Loader {
+      private final URL myURL;
+      private ZipFile myZipFile;
+
+      JarLoader(URL url, int index) throws IOException {
+        super(new URL(JAR_PROTOCOL, "", -1, url + "!/"), index);
+        myURL = url;
+      }
+
+      public void releaseResources() {
+        final ZipFile zipFile = myZipFile;
+        if (zipFile != null) {
+          myZipFile = null;
+          try {
+            zipFile.close();
+          }
+          catch (IOException e) {
+            throw new RuntimeException();
+          }
+        }
+      }
+
+      private ZipFile acquireZipFile() throws IOException {
+        ZipFile zipFile = myZipFile;
+        if (zipFile == null) {
+          zipFile = doGetZipFile();
+          myZipFile = zipFile;
+        }
+        return zipFile;
+      }
+
+      private ZipFile doGetZipFile() throws IOException {
+        if (FILE_PROTOCOL.equals(myURL.getProtocol())) {
+          String s = unescapePercentSequences(myURL.getFile().replace('/', File.separatorChar));
+          if (!new File(s).exists()) {
+            throw new FileNotFoundException(s);
+          }
+          else {
+            return new ZipFile(s);
+          }
+        }
+
+        return null;
+      }
+
+      public Resource getResource(String name, boolean flag) {
+        try {
+          final ZipFile file = acquireZipFile();
+          if (file != null) {
+            final ZipEntry entry = file.getEntry(name);
+            if (entry != null) {
+              return new JarResource(entry, new URL(getBaseURL(), name));
+            }
+          }
+        }
+        catch (Exception e) {
+          return null;
+        }
+        return null;
+      }
+
+      private class JarResource extends Resource {
+        private final ZipEntry myEntry;
+        private final URL myUrl;
+
+        public JarResource(ZipEntry name, URL url) {
+          myEntry = name;
+          myUrl = url;
+        }
+
+        public String getName() {
+          return myEntry.getName();
+        }
+
+        public URL getURL() {
+          return myUrl;
+        }
+
+        public URL getCodeSourceURL() {
+          return myURL;
+        }
+
+        public InputStream getInputStream() throws IOException {
+          try {
+            final ZipFile file = acquireZipFile();
+            if (file == null) {
+              return null;
+            }
+
+            final InputStream inputStream = file.getInputStream(myEntry);
+            if (inputStream == null) {
+              return null; // if entry was not found
+            }
+            return new FilterInputStream(inputStream) {};
+          }
+          catch (IOException e) {
+            e.printStackTrace();
+            return null;
+          }
+        }
+
+        public int getContentLength() {
+          return (int)myEntry.getSize();
+        }
+      }
+
+      public String toString() {
+        return "JarLoader [" + myURL + "]";
+      }
+    }
+  }
+
+
+  private static String unescapePercentSequences(String s) {
+    if (s.indexOf('%') == -1) {
+      return s;
+    }
+    StringBuilder decoded = new StringBuilder();
+    final int len = s.length();
+    int i = 0;
+    while (i < len) {
+      char c = s.charAt(i);
+      if (c == '%') {
+        List<Integer> bytes = new ArrayList<Integer>();
+        while (i + 2 < len && s.charAt(i) == '%') {
+          final int d1 = decode(s.charAt(i + 1));
+          final int d2 = decode(s.charAt(i + 2));
+          if (d1 != -1 && d2 != -1) {
+            bytes.add(((d1 & 0xf) << 4 | d2 & 0xf));
+            i += 3;
+          }
+          else {
+            break;
+          }
+        }
+        if (!bytes.isEmpty()) {
+          final byte[] bytesArray = new byte[bytes.size()];
+          for (int j = 0; j < bytes.size(); j++) {
+            bytesArray[j] = (byte)bytes.get(j).intValue();
+          }
+          try {
+            decoded.append(new String(bytesArray, "UTF-8"));
+            continue;
+          }
+          catch (UnsupportedEncodingException ignored) {
+          }
+        }
+      }
+
+      decoded.append(c);
+      i++;
+    }
+    return decoded.toString();
+  }
+
+  private static int decode(char c) {
+    if ((c >= '0') && (c <= '9')){
+      return c - '0';
+    }
+    if ((c >= 'a') && (c <= 'f')){
+      return c - 'a' + 10;
+    }
+    if ((c >= 'A') && (c <= 'F')){
+      return c - 'A' + 10;
+    }
+    return -1;
+  }
+
+  public byte[] loadBytes(InputStream stream) {
+    byte[] buf = myBuffer;
+    if (buf == null) {
+      buf = new byte[512];
+      myBuffer = buf;
+    }
+
+    final ByteArrayOutputStream result = new ByteArrayOutputStream();
+    try {
+      while (true) {
+        int n = stream.read(buf, 0, buf.length);
+        if (n <= 0) {
+          break;
+        }
+        result.write(buf, 0, n);
+      }
+      result.close();
+    }
+    catch (IOException ignored) {
+    }
+    return result.toByteArray();
+  }
+
+}
diff --git a/java/compiler/instrumentation-util/src/com/intellij/compiler/instrumentation/InstrumenterClassWriter.java b/java/compiler/instrumentation-util/src/com/intellij/compiler/instrumentation/InstrumenterClassWriter.java
new file mode 100644
index 0000000..b2f93c5
--- /dev/null
+++ b/java/compiler/instrumentation-util/src/com/intellij/compiler/instrumentation/InstrumenterClassWriter.java
@@ -0,0 +1,43 @@
+package com.intellij.compiler.instrumentation;
+
+import org.jetbrains.asm4.ClassWriter;
+
+/**
+* @author Eugene Zhuravlev
+*         Date: 3/27/12
+*/
+public class InstrumenterClassWriter extends ClassWriter {
+  private final InstrumentationClassFinder myFinder;
+
+  public InstrumenterClassWriter(int flags, final InstrumentationClassFinder finder) {
+    super(flags);
+    myFinder = finder;
+  }
+
+  protected String getCommonSuperClass(final String type1, final String type2) {
+    try {
+      final InstrumentationClassFinder.PseudoClass cls1 = myFinder.loadClass(type1);
+      final InstrumentationClassFinder.PseudoClass cls2 = myFinder.loadClass(type2);
+      if (cls1.isAssignableFrom(cls2)) {
+        return cls1.getName();
+      }
+      if (cls2.isAssignableFrom(cls1)) {
+        return cls2.getName();
+      }
+      if (cls1.isInterface() || cls2.isInterface()) {
+        return "java/lang/Object";
+      }
+      else {
+        InstrumentationClassFinder.PseudoClass c = cls1;
+        do {
+          c = c.getSuperClass();
+        }
+        while (!c.isAssignableFrom(cls2));
+        return c.getName();
+      }
+    }
+    catch (Exception e) {
+      throw new RuntimeException(e.toString(), e);
+    }
+  }
+}
diff --git a/java/compiler/instrumentation-util/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java b/java/compiler/instrumentation-util/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java
new file mode 100644
index 0000000..77bc538
--- /dev/null
+++ b/java/compiler/instrumentation-util/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java
@@ -0,0 +1,182 @@
+/*
+ * 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 com.intellij.compiler.notNullVerification;
+
+import org.jetbrains.asm4.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author ven
+ */
+public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcodes {
+  private static final String NOT_NULL_CLASS_NAME = "org/jetbrains/annotations/NotNull";
+  private static final String NOT_NULL_TYPE = "L"+ NOT_NULL_CLASS_NAME + ";";
+  private static final String SYNTHETIC_CLASS_NAME = "java/lang/Synthetic";
+  private static final String SYNTHETIC_TYPE = "L" + SYNTHETIC_CLASS_NAME + ";";
+  private static final String IAE_CLASS_NAME = "java/lang/IllegalArgumentException";
+  private static final String ISE_CLASS_NAME = "java/lang/IllegalStateException";
+  private static final String STRING_CLASS_NAME = "java/lang/String";
+  private static final String CONSTRUCTOR_NAME = "<init>";
+  private static final String EXCEPTION_INIT_SIGNATURE = "(L" + STRING_CLASS_NAME + ";)V";
+
+  private static final String NULL_ARG_MESSAGE = "Argument %d for @NotNull parameter of %s.%s must not be null";
+  private static final String NULL_RESULT_MESSAGE = "@NotNull method %s.%s must not return null";
+
+  private String myClassName;
+  private boolean myIsModification = false;
+  private RuntimeException myPostponedError;
+
+  public NotNullVerifyingInstrumenter(final ClassVisitor classVisitor) {
+    super(Opcodes.ASM4, classVisitor);
+  }
+
+  public boolean isModification() {
+    return myIsModification;
+  }
+
+  @Override
+  public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+    super.visit(version, access, name, signature, superName, interfaces);
+    myClassName = name;
+  }
+
+  @Override
+  public MethodVisitor visitMethod(final int access, final String name, String desc, String signature, String[] exceptions) {
+    final Type[] args = Type.getArgumentTypes(desc);
+    final Type returnType = Type.getReturnType(desc);
+    final MethodVisitor v = cv.visitMethod(access, name, desc, signature, exceptions);
+    return new MethodVisitor(Opcodes.ASM4, v) {
+
+      private final List<Integer> myNotNullParams = new ArrayList<Integer>();
+      private int mySyntheticCount = 0;
+      private boolean myIsNotNull = false;
+      private Label myStartGeneratedCodeLabel;
+
+      public AnnotationVisitor visitParameterAnnotation(final int parameter, final String anno, final boolean visible) {
+        final AnnotationVisitor av = mv.visitParameterAnnotation(parameter, anno, visible);
+        if (isReferenceType(args[parameter]) && anno.equals(NOT_NULL_TYPE)) {
+          myNotNullParams.add(new Integer(parameter));
+        }
+        else if (anno.equals(SYNTHETIC_TYPE)) {
+          // see http://forge.ow2.org/tracker/?aid=307392&group_id=23&atid=100023&func=detail
+          mySyntheticCount++;
+        }
+        return av;
+      }
+
+      @Override
+      public AnnotationVisitor visitAnnotation(String anno, boolean isRuntime) {
+        final AnnotationVisitor av = mv.visitAnnotation(anno, isRuntime);
+        if (isReferenceType(returnType) && anno.equals(NOT_NULL_TYPE)) {
+          myIsNotNull = true;
+        }
+
+        return av;
+      }
+
+      @Override
+      public void visitCode() {
+        if (myNotNullParams.size() > 0) {
+          myStartGeneratedCodeLabel = new Label();
+          mv.visitLabel(myStartGeneratedCodeLabel);
+        }
+        for (Integer param : myNotNullParams) {
+          int var = ((access & ACC_STATIC) == 0) ? 1 : 0;
+          for (int i = 0; i < param; ++i) {
+            var += args[i].getSize();
+          }
+          mv.visitVarInsn(ALOAD, var);
+
+          Label end = new Label();
+          mv.visitJumpInsn(IFNONNULL, end);
+
+          generateThrow(IAE_CLASS_NAME, String.format(NULL_ARG_MESSAGE, param - mySyntheticCount, myClassName, name), end);
+        }
+      }
+
+      @Override
+      public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
+        final boolean isStatic = (access & ACC_STATIC) != 0;
+        final boolean isParameter = isStatic ? index < args.length : index <= args.length;
+        final Label label = (isParameter && myStartGeneratedCodeLabel != null) ? myStartGeneratedCodeLabel : start;
+        mv.visitLocalVariable(name, desc, signature, label, end, index);
+      }
+
+      @Override
+      public void visitInsn(int opcode) {
+        if (opcode == ARETURN) {
+          if (myIsNotNull) {
+            mv.visitInsn(DUP);
+            final Label skipLabel = new Label();
+            mv.visitJumpInsn(IFNONNULL, skipLabel);
+            generateThrow(ISE_CLASS_NAME, String.format(NULL_RESULT_MESSAGE, myClassName, name), skipLabel);
+          }
+        }
+
+        mv.visitInsn(opcode);
+      }
+
+      private void generateThrow(final String exceptionClass, final String descr, final Label end) {
+        mv.visitTypeInsn(NEW, exceptionClass);
+        mv.visitInsn(DUP);
+        mv.visitLdcInsn(descr);
+        mv.visitMethodInsn(INVOKESPECIAL, exceptionClass, CONSTRUCTOR_NAME, EXCEPTION_INIT_SIGNATURE);
+        mv.visitInsn(ATHROW);
+        mv.visitLabel(end);
+
+        myIsModification = true;
+        processPostponedErrors();
+      }
+
+      @Override
+      public void visitMaxs(final int maxStack, final int maxLocals) {
+        try {
+          super.visitMaxs(maxStack, maxLocals);
+        }
+        catch (Throwable e) {
+          registerError(name, "visitMaxs", e);
+        }
+      }
+    };
+  }
+
+  private static boolean isReferenceType(final Type type) {
+    return type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY;
+  }
+
+  private void registerError(String methodName, String operationName, Throwable e) {
+    if (myPostponedError == null) {
+      // throw the first error that occurred
+      Throwable err = e.getCause();
+      if (err == null) {
+        err = e;
+      }
+      myPostponedError = new RuntimeException("Operation '" + operationName + "' failed for " + myClassName + "." + methodName + "(): " + err.getMessage(), err);
+    }
+    if (myIsModification) {
+      processPostponedErrors();
+    }
+  }
+
+  private void processPostponedErrors() {
+    final RuntimeException error = myPostponedError;
+    if (error != null) {
+      throw error;
+    }
+  }
+}
diff --git a/java/compiler/javac2/javac2.iml b/java/compiler/javac2/javac2.iml
new file mode 100644
index 0000000..c44096e
--- /dev/null
+++ b/java/compiler/javac2/javac2.iml
@@ -0,0 +1,169 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module relativePaths="false" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_5" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="Ant" level="project" />
+    <orderEntry type="module" module-name="forms-compiler" />
+    <orderEntry type="module" module-name="forms_rt" />
+    <orderEntry type="library" name="asm4" level="project" />
+    <orderEntry type="module" module-name="instrumentation-util" />
+  </component>
+  <component name="copyright">
+    <Base>
+      <setting name="state" value="0" />
+    </Base>
+    <LanguageOptions name="HTML">
+      <option name="templateOptions">
+        <value>
+          <option name="block" value="true" />
+          <option name="separateBefore" value="false" />
+          <option name="separateAfter" value="false" />
+          <option name="prefixLines" value="true" />
+          <option name="lenBefore" value="80" />
+          <option name="lenAfter" value="80" />
+          <option name="box" value="false" />
+          <option name="filler" value=" " />
+        </value>
+      </option>
+      <option name="notice" value="Copyright (c) &amp;#36;today.year, Your Corporation. All Rights Reserved." />
+      <option name="keyword" value="Copyright" />
+      <option name="fileTypeOverride" value="2" />
+      <option name="relativeBefore" value="true" />
+      <option name="addBlankAfter" value="true" />
+      <option name="fileLocation" value="1" />
+      <option name="useAlternate" value="false" />
+    </LanguageOptions>
+    <LanguageOptions name="JAVA">
+      <option name="templateOptions">
+        <value>
+          <option name="block" value="true" />
+          <option name="separateBefore" value="false" />
+          <option name="separateAfter" value="false" />
+          <option name="prefixLines" value="true" />
+          <option name="lenBefore" value="80" />
+          <option name="lenAfter" value="80" />
+          <option name="box" value="false" />
+          <option name="filler" value=" " />
+        </value>
+      </option>
+      <option name="notice" value="/*&#10; * Copyright 2000-2005 JetBrains s.r.o.&#10; * &#10; * Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);&#10; * you may not use this file except in compliance with the License.&#10; * You may obtain a copy of the License at&#10; * &#10; * http://www.apache.org/licenses/LICENSE-2.0&#10; * &#10; * Unless required by applicable law or agreed to in writing, software&#10; * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,&#10; * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#10; * See the License for the specific language governing permissions and&#10; * limitations under the License.&#10; */" />
+      <option name="keyword" value="Copyright" />
+      <option name="fileTypeOverride" value="2" />
+      <option name="relativeBefore" value="true" />
+      <option name="addBlankAfter" value="true" />
+      <option name="fileLocation" value="1" />
+      <option name="useAlternate" value="false" />
+    </LanguageOptions>
+    <LanguageOptions name="JSP">
+      <option name="templateOptions">
+        <value>
+          <option name="block" value="true" />
+          <option name="separateBefore" value="false" />
+          <option name="separateAfter" value="false" />
+          <option name="prefixLines" value="true" />
+          <option name="lenBefore" value="80" />
+          <option name="lenAfter" value="80" />
+          <option name="box" value="false" />
+          <option name="filler" value=" " />
+        </value>
+      </option>
+      <option name="notice" value="Copyright (c) &amp;#36;today.year, Your Corporation. All Rights Reserved." />
+      <option name="keyword" value="Copyright" />
+      <option name="fileTypeOverride" value="2" />
+      <option name="relativeBefore" value="true" />
+      <option name="addBlankAfter" value="true" />
+      <option name="fileLocation" value="1" />
+      <option name="useAlternate" value="false" />
+    </LanguageOptions>
+    <LanguageOptions name="JavaScript">
+      <option name="templateOptions">
+        <value>
+          <option name="block" value="true" />
+          <option name="separateBefore" value="false" />
+          <option name="separateAfter" value="false" />
+          <option name="prefixLines" value="true" />
+          <option name="lenBefore" value="80" />
+          <option name="lenAfter" value="80" />
+          <option name="box" value="false" />
+          <option name="filler" value=" " />
+        </value>
+      </option>
+      <option name="notice" value="Copyright (c) &amp;#36;today.year, Your Corporation. All Rights Reserved." />
+      <option name="keyword" value="Copyright" />
+      <option name="fileTypeOverride" value="2" />
+      <option name="relativeBefore" value="true" />
+      <option name="addBlankAfter" value="true" />
+      <option name="fileLocation" value="1" />
+      <option name="useAlternate" value="false" />
+    </LanguageOptions>
+    <LanguageOptions name="Properties">
+      <option name="templateOptions">
+        <value>
+          <option name="block" value="true" />
+          <option name="separateBefore" value="false" />
+          <option name="separateAfter" value="false" />
+          <option name="prefixLines" value="true" />
+          <option name="lenBefore" value="80" />
+          <option name="lenAfter" value="80" />
+          <option name="box" value="false" />
+          <option name="filler" value=" " />
+        </value>
+      </option>
+      <option name="notice" value="Copyright (c) &amp;#36;today.year, Your Corporation. All Rights Reserved." />
+      <option name="keyword" value="Copyright" />
+      <option name="fileTypeOverride" value="2" />
+      <option name="relativeBefore" value="true" />
+      <option name="addBlankAfter" value="true" />
+      <option name="fileLocation" value="1" />
+      <option name="useAlternate" value="false" />
+    </LanguageOptions>
+    <LanguageOptions name="XML">
+      <option name="templateOptions">
+        <value>
+          <option name="block" value="true" />
+          <option name="separateBefore" value="false" />
+          <option name="separateAfter" value="false" />
+          <option name="prefixLines" value="true" />
+          <option name="lenBefore" value="80" />
+          <option name="lenAfter" value="80" />
+          <option name="box" value="false" />
+          <option name="filler" value=" " />
+        </value>
+      </option>
+      <option name="notice" value="Copyright (c) &amp;#36;today.year, Your Corporation. All Rights Reserved." />
+      <option name="keyword" value="Copyright" />
+      <option name="fileTypeOverride" value="2" />
+      <option name="relativeBefore" value="true" />
+      <option name="addBlankAfter" value="true" />
+      <option name="fileLocation" value="1" />
+      <option name="useAlternate" value="false" />
+    </LanguageOptions>
+    <LanguageOptions name="__TEMPLATE__">
+      <option name="templateOptions">
+        <value>
+          <option name="block" value="true" />
+          <option name="separateBefore" value="false" />
+          <option name="separateAfter" value="false" />
+          <option name="prefixLines" value="true" />
+          <option name="lenBefore" value="80" />
+          <option name="lenAfter" value="80" />
+          <option name="box" value="false" />
+          <option name="filler" value=" " />
+        </value>
+      </option>
+      <option name="notice" value="Copyright 2000-&amp;#36;{today.year} JetBrains s.r.o.&#10;&#10;Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);&#10;you may not use this file except in compliance with the License.&#10;You may obtain a copy of the License at&#10;&#10;http://www.apache.org/licenses/LICENSE-2.0&#10;&#10;Unless required by applicable law or agreed to in writing, software&#10;distributed under the License is distributed on an &quot;AS IS&quot; BASIS,&#10;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#10;See the License for the specific language governing permissions and&#10;limitations under the License." />
+      <option name="keyword" value="Copyright" />
+      <option name="fileTypeOverride" value="4" />
+      <option name="relativeBefore" value="true" />
+      <option name="addBlankAfter" value="true" />
+      <option name="fileLocation" value="1" />
+      <option name="useAlternate" value="false" />
+    </LanguageOptions>
+  </component>
+</module>
+
diff --git a/java/compiler/javac2/src/com/intellij/ant/InstrumentIdeaExtensions.java b/java/compiler/javac2/src/com/intellij/ant/InstrumentIdeaExtensions.java
new file mode 100644
index 0000000..0c07341
--- /dev/null
+++ b/java/compiler/javac2/src/com/intellij/ant/InstrumentIdeaExtensions.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 com.intellij.ant;
+
+/**
+ * This ant task is used to instrument compiled classes with idea
+ * extensions without compiling java sources.
+ */
+public class InstrumentIdeaExtensions extends Javac2 {
+    /**
+     * Customize behavior of {@link Javac2} disabling compilation of java classes.
+     *
+     * @return false, meaning that java classes are not compiled
+     * @see com.intellij.ant.Javac2#areJavaClassesCompiled()
+     */
+    protected boolean areJavaClassesCompiled() {
+        return false;
+    }
+}
diff --git a/java/compiler/javac2/src/com/intellij/ant/Javac2.java b/java/compiler/javac2/src/com/intellij/ant/Javac2.java
new file mode 100644
index 0000000..3676261
--- /dev/null
+++ b/java/compiler/javac2/src/com/intellij/ant/Javac2.java
@@ -0,0 +1,554 @@
+/*
+ * 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 com.intellij.ant;
+
+import com.intellij.compiler.instrumentation.InstrumentationClassFinder;
+import com.intellij.compiler.instrumentation.InstrumenterClassWriter;
+import com.intellij.compiler.notNullVerification.NotNullVerifyingInstrumenter;
+import com.intellij.uiDesigner.compiler.*;
+import com.intellij.uiDesigner.lw.CompiledClassPropertiesProvider;
+import com.intellij.uiDesigner.lw.LwRootContainer;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Javac;
+import org.apache.tools.ant.types.Path;
+import org.jetbrains.asm4.ClassReader;
+import org.jetbrains.asm4.ClassVisitor;
+import org.jetbrains.asm4.ClassWriter;
+import org.jetbrains.asm4.Opcodes;
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+
+public class Javac2 extends Javac {
+    private ArrayList myFormFiles;
+    private List myNestedFormPathList;
+
+    public Javac2() {
+    }
+
+    /**
+     * Check if Java classes should be actually compiled by the task. This method is overridden by
+     * {@link com.intellij.ant.InstrumentIdeaExtensions} task in order to suppress actual compilation
+     * of the java sources.
+     *
+     * @return true if the java classes are compiled, false if just instrumentation is performed.
+     */
+    protected boolean areJavaClassesCompiled() {
+        return true;
+    }
+
+    /**
+     * This method is called when option that supported only for the case when java sources are compiled
+     * and it is not supported for the case when only instrumentation is performed.
+     *
+     * @param optionName the option name to warn about.
+     */
+    private void unsupportedOptionMessage(final String optionName) {
+        if (!areJavaClassesCompiled()) {
+            log("The option " + optionName + " is not supported by InstrumentIdeaExtensions task", Project.MSG_ERR);
+        }
+    }
+
+    /**
+     * The overridden setter method that warns about unsupported option.
+     *
+     * @param v the option value
+     */
+    public void setDebugLevel(String v) {
+        unsupportedOptionMessage("debugLevel");
+        super.setDebugLevel(v);
+    }
+
+    /**
+     * The overridden setter method that warns about unsupported option.
+     *
+     * @param list the option value
+     */
+    public void setListfiles(boolean list) {
+        unsupportedOptionMessage("listFiles");
+        super.setListfiles(list);
+    }
+
+    /**
+     * The overridden setter method that warns about unsupported option.
+     *
+     * @param memoryInitialSize the option value
+     */
+    public void setMemoryInitialSize(String memoryInitialSize) {
+        unsupportedOptionMessage("memoryInitialSize");
+        super.setMemoryInitialSize(memoryInitialSize);
+    }
+
+    /**
+     * The overridden setter method that warns about unsupported option.
+     *
+     * @param memoryMaximumSize the option value
+     */
+    public void setMemoryMaximumSize(String memoryMaximumSize) {
+        unsupportedOptionMessage("memoryMaximumSize");
+        super.setMemoryMaximumSize(memoryMaximumSize);
+    }
+
+    /**
+     * The overridden setter method that warns about unsupported option.
+     *
+     * @param encoding the option value
+     */
+    public void setEncoding(String encoding) {
+        unsupportedOptionMessage("encoding");
+        super.setEncoding(encoding);
+    }
+
+    /**
+     * The overridden setter method that warns about unsupported option.
+     *
+     * @param optimize the option value
+     */
+    public void setOptimize(boolean optimize) {
+        unsupportedOptionMessage("optimize");
+        super.setOptimize(optimize);
+    }
+
+    /**
+     * The overridden setter method that warns about unsupported option.
+     *
+     * @param depend the option value
+     */
+    public void setDepend(boolean depend) {
+        unsupportedOptionMessage("depend");
+        super.setDepend(depend);
+    }
+
+    /**
+     * The overridden setter method that warns about unsupported option.
+     *
+     * @param f the option value
+     */
+    public void setFork(boolean f) {
+        unsupportedOptionMessage("fork");
+        super.setFork(f);
+    }
+
+    /**
+     * The overridden setter method that warns about unsupported option.
+     *
+     * @param forkExec the option value
+     */
+    public void setExecutable(String forkExec) {
+        unsupportedOptionMessage("executable");
+        super.setExecutable(forkExec);
+    }
+
+    /**
+     * The overridden setter method that warns about unsupported option.
+     *
+     * @param compiler the option value
+     */
+    public void setCompiler(String compiler) {
+        unsupportedOptionMessage("compiler");
+        super.setCompiler(compiler);
+    }
+
+    /**
+     * Sets the nested form directories that will be used during the
+     * compilation.
+     * @param nestedformdirs a list of {@link PrefixedPath}
+     */
+    public void setNestedformdirs(List nestedformdirs) {
+        myNestedFormPathList = nestedformdirs;
+    }
+
+    /**
+     * Gets the nested form directories that will be used during the
+     * compilation.
+     * @return the extension directories as a list of {@link PrefixedPath}
+     */
+    public List getNestedformdirs() {
+        return myNestedFormPathList;
+    }
+
+    /**
+     * Adds a path to nested form directories.
+     * @return a path to be configured
+     */
+    public PrefixedPath createNestedformdirs() {
+        PrefixedPath p = new PrefixedPath(getProject());
+        if (myNestedFormPathList == null) {
+            myNestedFormPathList = new ArrayList();
+        }
+        myNestedFormPathList.add(p);
+        return p;
+    }
+
+    /**
+     * The overridden compile method that does not actually compiles java sources but only instruments
+     * class files.
+     */
+    protected void compile() {
+      // compile java
+      if (areJavaClassesCompiled()) {
+        super.compile();
+      }
+
+      InstrumentationClassFinder finder = buildClasspathClassLoader();
+      if (finder == null) {
+        return;
+      }
+      try {
+        instrumentForms(finder);
+
+        //NotNull instrumentation
+        final int instrumented = instrumentNotNull(getDestdir(), finder);
+
+        log("Added @NotNull assertions to " + instrumented + " files", Project.MSG_INFO);
+      }
+      finally {
+        finder.releaseResources();
+      }
+    }
+
+  /**
+     * Instrument forms
+     *
+     * @param finder a classloader to use
+     */
+    private void instrumentForms(final InstrumentationClassFinder finder) {
+        // we instrument every file, because we cannot find which files should not be instrumented without dependency storage
+        final ArrayList formsToInstrument = myFormFiles;
+
+        if (formsToInstrument.size() == 0) {
+            log("No forms to instrument found", Project.MSG_VERBOSE);
+            return;
+        }
+
+        final HashMap class2form = new HashMap();
+
+        for (int i = 0; i < formsToInstrument.size(); i++) {
+            final File formFile = (File)formsToInstrument.get(i);
+
+            log("compiling form " + formFile.getAbsolutePath(), Project.MSG_VERBOSE);
+            final LwRootContainer rootContainer;
+            try {
+                rootContainer = Utils.getRootContainer(formFile.toURI().toURL(), new CompiledClassPropertiesProvider(finder.getLoader()));
+            }
+            catch (AlienFormFileException e) {
+                // ignore non-IDEA forms
+                continue;
+            }
+            catch (Exception e) {
+                fireError("Cannot process form file " + formFile.getAbsolutePath() + ". Reason: " + e);
+                continue;
+            }
+
+            final String classToBind = rootContainer.getClassToBind();
+            if (classToBind == null) {
+                continue;
+            }
+
+            String name = classToBind.replace('.', '/');
+            File classFile = getClassFile(name);
+            if (classFile == null) {
+                log(formFile.getAbsolutePath() + ": Class to bind does not exist: " + classToBind, Project.MSG_WARN);
+                continue;
+            }
+
+            final File alreadyProcessedForm = (File)class2form.get(classToBind);
+            if (alreadyProcessedForm != null) {
+                fireError(formFile.getAbsolutePath() +
+                          ": " +
+                          "The form is bound to the class " +
+                          classToBind +
+                          ".\n" +
+                          "Another form " +
+                          alreadyProcessedForm.getAbsolutePath() +
+                          " is also bound to this class.");
+                continue;
+            }
+            class2form.put(classToBind, formFile);
+
+            try {
+                int version;
+                InputStream stream = new FileInputStream(classFile);
+                try {
+                    version = getClassFileVersion(new ClassReader(stream));
+                }
+                finally {
+                    stream.close();
+                }
+                AntNestedFormLoader formLoader = new AntNestedFormLoader(finder.getLoader(), myNestedFormPathList);
+                InstrumenterClassWriter classWriter = new InstrumenterClassWriter(getAsmClassWriterFlags(version), finder);
+                final AsmCodeGenerator codeGenerator = new AsmCodeGenerator(rootContainer, finder, formLoader, false, classWriter);
+                codeGenerator.patchFile(classFile);
+                final FormErrorInfo[] warnings = codeGenerator.getWarnings();
+
+                for (int j = 0; j < warnings.length; j++) {
+                    log(formFile.getAbsolutePath() + ": " + warnings[j].getErrorMessage(), Project.MSG_WARN);
+                }
+                final FormErrorInfo[] errors = codeGenerator.getErrors();
+                if (errors.length > 0) {
+                    StringBuffer message = new StringBuffer();
+                    for (int j = 0; j < errors.length; j++) {
+                        if (message.length() > 0) {
+                            message.append("\n");
+                        }
+                        message.append(formFile.getAbsolutePath()).append(": ").append(errors[j].getErrorMessage());
+                    }
+                    fireError(message.toString());
+                }
+            }
+            catch (Exception e) {
+                fireError("Forms instrumentation failed for " + formFile.getAbsolutePath() + ": " + e.toString());
+            }
+        }
+    }
+
+    /**
+     * @return the flags for class writer
+     */
+    private static int getAsmClassWriterFlags(int version) {
+        return version >= Opcodes.V1_6 && version != Opcodes.V1_1 ? ClassWriter.COMPUTE_FRAMES : ClassWriter.COMPUTE_MAXS;
+    }
+
+    /**
+     * Create class loader based on classpath, bootclasspath, and sourcepath.
+     *
+     * @return a URL classloader
+     */
+    private InstrumentationClassFinder buildClasspathClassLoader() {
+        final StringBuffer classPathBuffer = new StringBuffer();
+        final Path cp = new Path(getProject());
+        appendPath(cp, getBootclasspath());
+        cp.setLocation(getDestdir().getAbsoluteFile());
+        appendPath(cp, getClasspath());
+        appendPath(cp, getSourcepath());
+        appendPath(cp, getSrcdir());
+        if (getIncludeantruntime()) {
+            cp.addExisting(cp.concatSystemClasspath("last"));
+        }
+        if (getIncludejavaruntime()) {
+            cp.addJavaRuntime();
+        }
+        cp.addExtdirs(getExtdirs());
+
+        final String[] pathElements = cp.list();
+        for (int i = 0; i < pathElements.length; i++) {
+            final String pathElement = pathElements[i];
+            classPathBuffer.append(File.pathSeparator);
+            classPathBuffer.append(pathElement);
+        }
+
+        final String classPath = classPathBuffer.toString();
+        log("classpath=" + classPath, Project.MSG_VERBOSE);
+
+        try {
+            return createInstrumentationClassFinder(classPath);
+        }
+        catch (MalformedURLException e) {
+            fireError(e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * Append path to class path if the appened path is not empty and is not null
+     *
+     * @param cp the path to modify
+     * @param p  the path to append
+     */
+    private void appendPath(Path cp, final Path p) {
+        if (p != null && p.size() > 0) {
+            cp.append(p);
+        }
+    }
+
+    /**
+     * Instrument classes with NotNull annotations
+     *
+     * @param dir    the directory with classes to instrument (the directory is processed recursively)
+     * @param finder the classloader to use
+     * @return the amount of classes actually affected by instrumentation
+     */
+    private int instrumentNotNull(File dir, final InstrumentationClassFinder finder) {
+        int instrumented = 0;
+        final File[] files = dir.listFiles();
+        for (int i = 0; i < files.length; i++) {
+            File file = files[i];
+            final String name = file.getName();
+            if (name.endsWith(".class")) {
+                final String path = file.getPath();
+                log("Adding @NotNull assertions to " + path, Project.MSG_VERBOSE);
+                try {
+                    final FileInputStream inputStream = new FileInputStream(file);
+                    try {
+                        ClassReader reader = new ClassReader(inputStream);
+
+                        int version = getClassFileVersion(reader);
+
+                        if (version >= Opcodes.V1_5) {
+                            ClassWriter writer = new InstrumenterClassWriter(getAsmClassWriterFlags(version), finder);
+
+                            final NotNullVerifyingInstrumenter instrumenter = new NotNullVerifyingInstrumenter(writer);
+                            reader.accept(instrumenter, 0);
+                            if (instrumenter.isModification()) {
+                                final FileOutputStream fileOutputStream = new FileOutputStream(path);
+                                try {
+                                    fileOutputStream.write(writer.toByteArray());
+                                    instrumented++;
+                                }
+                                finally {
+                                    fileOutputStream.close();
+                                }
+                            }
+                        }
+                    }
+                    finally {
+                        inputStream.close();
+                    }
+                }
+                catch (IOException e) {
+                    log("Failed to instrument @NotNull assertion for " + path + ": " + e.getMessage(), Project.MSG_WARN);
+                }
+                catch (Exception e) {
+                    fireError("@NotNull instrumentation failed for " + path + ": " + e.toString());
+                }
+            }
+            else if (file.isDirectory()) {
+                instrumented += instrumentNotNull(file, finder);
+            }
+        }
+
+        return instrumented;
+    }
+
+    private static int getClassFileVersion(ClassReader reader) {
+        final int[] classfileVersion = new int[1];
+        reader.accept(new ClassVisitor(Opcodes.ASM4) {
+            public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+                classfileVersion[0] = version;
+            }
+        }, 0);
+
+        return classfileVersion[0];
+    }
+
+    private void fireError(final String message) {
+        if (failOnError) {
+            throw new BuildException(message, getLocation());
+        }
+        else {
+            log(message, Project.MSG_ERR);
+        }
+    }
+
+    private File getClassFile(String className) {
+        final String classOrInnerName = getClassOrInnerName(className);
+        if (classOrInnerName == null) return null;
+        return new File(getDestdir().getAbsolutePath(), classOrInnerName + ".class");
+    }
+
+    private String getClassOrInnerName(String className) {
+        File classFile = new File(getDestdir().getAbsolutePath(), className + ".class");
+        if (classFile.exists()) return className;
+        int position = className.lastIndexOf('/');
+        if (position == -1) return null;
+        return getClassOrInnerName(className.substring(0, position) + '$' + className.substring(position + 1));
+    }
+
+    protected void resetFileLists() {
+        super.resetFileLists();
+        myFormFiles = new ArrayList();
+    }
+
+    protected void scanDir(final File srcDir, final File destDir, final String[] files) {
+        super.scanDir(srcDir, destDir, files);
+        for (int i = 0; i < files.length; i++) {
+            final String file = files[i];
+            if (file.endsWith(".form")) {
+                log("Found form file " + file, Project.MSG_VERBOSE);
+                myFormFiles.add(new File(srcDir, file));
+            }
+        }
+    }
+
+  private static InstrumentationClassFinder createInstrumentationClassFinder(final String classPath) throws MalformedURLException {
+    final ArrayList urls = new ArrayList();
+    for (StringTokenizer tokenizer = new StringTokenizer(classPath, File.pathSeparator); tokenizer.hasMoreTokens();) {
+      final String s = tokenizer.nextToken();
+      urls.add(new File(s).toURI().toURL());
+    }
+    final URL[] urlsArr = (URL[])urls.toArray(new URL[urls.size()]);
+    return new InstrumentationClassFinder(urlsArr);
+  }
+
+  private class AntNestedFormLoader implements NestedFormLoader {
+        private final ClassLoader myLoader;
+        private final List myNestedFormPathList;
+        private final HashMap myFormCache = new HashMap();
+
+        public AntNestedFormLoader(final ClassLoader loader, List nestedFormPathList) {
+            myLoader = loader;
+            myNestedFormPathList = nestedFormPathList;
+        }
+
+        public LwRootContainer loadForm(String formFilePath) throws Exception {
+            if (myFormCache.containsKey(formFilePath)) {
+                return (LwRootContainer)myFormCache.get(formFilePath);
+            }
+
+            String lowerFormFilePath = formFilePath.toLowerCase();
+            log("Searching for form " + lowerFormFilePath, Project.MSG_VERBOSE);
+            for (Iterator iterator = myFormFiles.iterator(); iterator.hasNext();) {
+                File file = (File)iterator.next();
+                String name = file.getAbsolutePath().replace(File.separatorChar, '/').toLowerCase();
+                log("Comparing with " + name, Project.MSG_VERBOSE);
+                if (name.endsWith(lowerFormFilePath)) {
+                  return loadForm(formFilePath, new FileInputStream(file));
+                }
+            }
+
+            if (myNestedFormPathList != null) {
+                for (int i = 0; i < myNestedFormPathList.size(); i++) {
+                    PrefixedPath path = (PrefixedPath)myNestedFormPathList.get(i);
+                    File formFile = path.findFile(formFilePath);
+                    if (formFile != null) {
+                        return loadForm(formFilePath, new FileInputStream(formFile));
+                    }
+                }
+            }
+            InputStream resourceStream = myLoader.getResourceAsStream(formFilePath);
+            if (resourceStream != null) {
+                return loadForm(formFilePath, resourceStream);
+            }
+            throw new Exception("Cannot find nested form file " + formFilePath);
+        }
+
+        private LwRootContainer loadForm(String formFileName, InputStream resourceStream) throws Exception {
+            final LwRootContainer container = Utils.getRootContainer(resourceStream, null);
+            myFormCache.put(formFileName, container);
+            return container;
+        }
+
+        public String getClassToBindName(LwRootContainer container) {
+            final String className = container.getClassToBind();
+            String result = getClassOrInnerName(className.replace('.', '/'));
+            if (result != null) return result.replace('/', '.');
+            return className;
+        }
+    }
+}
diff --git a/java/compiler/javac2/src/com/intellij/ant/PrefixedPath.java b/java/compiler/javac2/src/com/intellij/ant/PrefixedPath.java
new file mode 100644
index 0000000..c7af4eb
--- /dev/null
+++ b/java/compiler/javac2/src/com/intellij/ant/PrefixedPath.java
@@ -0,0 +1,79 @@
+/*
+ * 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 com.intellij.ant;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Path;
+
+import java.io.File;
+
+/**
+ * Allows to specify relative output prefix for Path.
+ * Used to support searching for nested form files under source roots with package prefixes.
+ *
+ * @author nik
+ */
+public class PrefixedPath extends Path {
+  private String myPrefix;
+
+  public PrefixedPath(Project project) {
+    super(project);
+  }
+
+  public PrefixedPath(Project p, String path) {
+    super(p, path);
+  }
+
+  public String getPrefix() {
+    return myPrefix;
+  }
+
+  public void setPrefix(String prefix) {
+    myPrefix = prefix;
+  }
+
+  public File findFile(String relativePath) {
+    relativePath = trimStartSlash(relativePath);
+    String prefix = myPrefix;
+    if (prefix != null) {
+      prefix = trimStartSlash(ensureEndsWithSlash(prefix));
+      if (!relativePath.toLowerCase().startsWith(prefix.toLowerCase())) {
+        return null;
+      }
+      relativePath = relativePath.substring(prefix.length());
+    }
+
+    String[] dirsList = list();
+    for (int j = 0, listLength = dirsList.length; j < listLength; j++) {
+      String fullPath = ensureEndsWithSlash(dirsList[j]) + relativePath;
+      File file = new File(fullPath.replace('/', File.separatorChar));
+      if (file.isFile()) {
+        return file;
+      }
+    }
+    return null;
+  }
+
+  private static String trimStartSlash(String path) {
+    if (path.startsWith("/")) return path.substring(1);
+    return path;
+  }
+
+  private static String ensureEndsWithSlash(String path) {
+    if (!path.endsWith("/")) return path + "/";
+    return path;
+  }
+}
diff --git a/java/compiler/javac2/src/com/intellij/uiDesigner/ant/Javac2.java b/java/compiler/javac2/src/com/intellij/uiDesigner/ant/Javac2.java
new file mode 100644
index 0000000..d5a0f4e
--- /dev/null
+++ b/java/compiler/javac2/src/com/intellij/uiDesigner/ant/Javac2.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+/*
+ * Created by IntelliJ IDEA.
+ * User: yole
+ * Date: 25.07.2006
+ * Time: 17:59:03
+ */
+package com.intellij.uiDesigner.ant;
+
+/**
+ * @noinspection ClassNameSameAsAncestorName
+ * @deprecated
+ */
+public class Javac2 extends com.intellij.ant.Javac2 {
+}
diff --git a/java/compiler/openapi/compiler-openapi.iml b/java/compiler/openapi/compiler-openapi.iml
new file mode 100644
index 0000000..9fb5c93
--- /dev/null
+++ b/java/compiler/openapi/compiler-openapi.iml
@@ -0,0 +1,165 @@
+<?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$/src" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="openapi" />
+  </component>
+  <component name="copyright">
+    <Base>
+      <setting name="state" value="0" />
+    </Base>
+    <LanguageOptions name="HTML">
+      <option name="templateOptions">
+        <value>
+          <option name="block" value="true" />
+          <option name="separateBefore" value="false" />
+          <option name="separateAfter" value="false" />
+          <option name="prefixLines" value="true" />
+          <option name="lenBefore" value="80" />
+          <option name="lenAfter" value="80" />
+          <option name="box" value="false" />
+          <option name="filler" value=" " />
+        </value>
+      </option>
+      <option name="notice" value="Copyright (c) &amp;#36;today.year, Your Corporation. All Rights Reserved." />
+      <option name="keyword" value="Copyright" />
+      <option name="fileTypeOverride" value="2" />
+      <option name="relativeBefore" value="true" />
+      <option name="addBlankAfter" value="true" />
+      <option name="fileLocation" value="1" />
+      <option name="useAlternate" value="false" />
+    </LanguageOptions>
+    <LanguageOptions name="JAVA">
+      <option name="templateOptions">
+        <value>
+          <option name="block" value="true" />
+          <option name="separateBefore" value="false" />
+          <option name="separateAfter" value="false" />
+          <option name="prefixLines" value="true" />
+          <option name="lenBefore" value="80" />
+          <option name="lenAfter" value="80" />
+          <option name="box" value="false" />
+          <option name="filler" value=" " />
+        </value>
+      </option>
+      <option name="notice" value="/*&#10; * Copyright (c) &amp;#36;today.year Your Corporation. All Rights Reserved.&#10; */" />
+      <option name="keyword" value="Copyright" />
+      <option name="fileTypeOverride" value="2" />
+      <option name="relativeBefore" value="true" />
+      <option name="addBlankAfter" value="true" />
+      <option name="fileLocation" value="1" />
+      <option name="useAlternate" value="false" />
+    </LanguageOptions>
+    <LanguageOptions name="JSP">
+      <option name="templateOptions">
+        <value>
+          <option name="block" value="true" />
+          <option name="separateBefore" value="false" />
+          <option name="separateAfter" value="false" />
+          <option name="prefixLines" value="true" />
+          <option name="lenBefore" value="80" />
+          <option name="lenAfter" value="80" />
+          <option name="box" value="false" />
+          <option name="filler" value=" " />
+        </value>
+      </option>
+      <option name="notice" value="Copyright (c) &amp;#36;today.year, Your Corporation. All Rights Reserved." />
+      <option name="keyword" value="Copyright" />
+      <option name="fileTypeOverride" value="2" />
+      <option name="relativeBefore" value="true" />
+      <option name="addBlankAfter" value="true" />
+      <option name="fileLocation" value="1" />
+      <option name="useAlternate" value="false" />
+    </LanguageOptions>
+    <LanguageOptions name="JavaScript">
+      <option name="templateOptions">
+        <value>
+          <option name="block" value="true" />
+          <option name="separateBefore" value="false" />
+          <option name="separateAfter" value="false" />
+          <option name="prefixLines" value="true" />
+          <option name="lenBefore" value="80" />
+          <option name="lenAfter" value="80" />
+          <option name="box" value="false" />
+          <option name="filler" value=" " />
+        </value>
+      </option>
+      <option name="notice" value="Copyright (c) &amp;#36;today.year, Your Corporation. All Rights Reserved." />
+      <option name="keyword" value="Copyright" />
+      <option name="fileTypeOverride" value="2" />
+      <option name="relativeBefore" value="true" />
+      <option name="addBlankAfter" value="true" />
+      <option name="fileLocation" value="1" />
+      <option name="useAlternate" value="false" />
+    </LanguageOptions>
+    <LanguageOptions name="Properties">
+      <option name="templateOptions">
+        <value>
+          <option name="block" value="true" />
+          <option name="separateBefore" value="false" />
+          <option name="separateAfter" value="false" />
+          <option name="prefixLines" value="true" />
+          <option name="lenBefore" value="80" />
+          <option name="lenAfter" value="80" />
+          <option name="box" value="false" />
+          <option name="filler" value=" " />
+        </value>
+      </option>
+      <option name="notice" value="Copyright (c) &amp;#36;today.year, Your Corporation. All Rights Reserved." />
+      <option name="keyword" value="Copyright" />
+      <option name="fileTypeOverride" value="2" />
+      <option name="relativeBefore" value="true" />
+      <option name="addBlankAfter" value="true" />
+      <option name="fileLocation" value="1" />
+      <option name="useAlternate" value="false" />
+    </LanguageOptions>
+    <LanguageOptions name="XML">
+      <option name="templateOptions">
+        <value>
+          <option name="block" value="true" />
+          <option name="separateBefore" value="false" />
+          <option name="separateAfter" value="false" />
+          <option name="prefixLines" value="true" />
+          <option name="lenBefore" value="80" />
+          <option name="lenAfter" value="80" />
+          <option name="box" value="false" />
+          <option name="filler" value=" " />
+        </value>
+      </option>
+      <option name="notice" value="Copyright (c) &amp;#36;today.year, Your Corporation. All Rights Reserved." />
+      <option name="keyword" value="Copyright" />
+      <option name="fileTypeOverride" value="2" />
+      <option name="relativeBefore" value="true" />
+      <option name="addBlankAfter" value="true" />
+      <option name="fileLocation" value="1" />
+      <option name="useAlternate" value="false" />
+    </LanguageOptions>
+    <LanguageOptions name="__TEMPLATE__">
+      <option name="templateOptions">
+        <value>
+          <option name="block" value="true" />
+          <option name="separateBefore" value="false" />
+          <option name="separateAfter" value="false" />
+          <option name="prefixLines" value="true" />
+          <option name="lenBefore" value="80" />
+          <option name="lenAfter" value="80" />
+          <option name="box" value="false" />
+          <option name="filler" value=" " />
+        </value>
+      </option>
+      <option name="notice" value="Copyright 2000-&amp;#36;{today.year} JetBrains s.r.o.&#10;&#10;Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);&#10;you may not use this file except in compliance with the License.&#10;You may obtain a copy of the License at&#10;&#10;http://www.apache.org/licenses/LICENSE-2.0&#10;&#10;Unless required by applicable law or agreed to in writing, software&#10;distributed under the License is distributed on an &quot;AS IS&quot; BASIS,&#10;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#10;See the License for the specific language governing permissions and&#10;limitations under the License." />
+      <option name="keyword" value="Copyright" />
+      <option name="fileTypeOverride" value="4" />
+      <option name="relativeBefore" value="true" />
+      <option name="addBlankAfter" value="true" />
+      <option name="fileLocation" value="1" />
+      <option name="useAlternate" value="false" />
+    </LanguageOptions>
+  </component>
+</module>
+
diff --git a/java/compiler/openapi/src/com/intellij/compiler/CompilerConfiguration.java b/java/compiler/openapi/src/com/intellij/compiler/CompilerConfiguration.java
new file mode 100644
index 0000000..9aa97cf
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/CompilerConfiguration.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 com.intellij.compiler;
+
+import com.intellij.openapi.compiler.options.ExcludedEntriesConfiguration;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.model.java.compiler.AnnotationProcessingConfiguration;
+
+public abstract class CompilerConfiguration {
+  // need this flag for profiling purposes. In production code is always set to 'true'
+  public static final boolean MAKE_ENABLED = true;
+
+  @Nullable
+  public abstract String getProjectBytecodeTarget();
+
+  @Nullable
+  public abstract String getBytecodeTargetLevel(Module module);
+
+  public abstract void setBytecodeTargetLevel(Module module, String level);
+
+  @NotNull
+  public abstract AnnotationProcessingConfiguration getAnnotationProcessingConfiguration(Module module);
+
+  /**
+   * @return true if exists at least one enabled annotation processing profile
+   */
+  public abstract boolean isAnnotationProcessorsEnabled();
+
+  public static CompilerConfiguration getInstance(Project project) {
+    return project.getComponent(CompilerConfiguration.class);
+  }
+
+  public abstract boolean isExcludedFromCompilation(VirtualFile virtualFile);
+
+  public abstract boolean isResourceFile(VirtualFile virtualFile);
+
+  public abstract boolean isResourceFile(String path);
+
+  public abstract void addResourceFilePattern(String namePattern) throws MalformedPatternException;
+
+  public abstract boolean isAddNotNullAssertions();
+
+  public abstract void setAddNotNullAssertions(boolean enabled);
+
+  public abstract ExcludedEntriesConfiguration getExcludedEntriesConfiguration();
+}
\ No newline at end of file
diff --git a/java/compiler/openapi/src/com/intellij/compiler/CompilerSettingsFactory.java b/java/compiler/openapi/src/com/intellij/compiler/CompilerSettingsFactory.java
new file mode 100644
index 0000000..c2e85ef
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/CompilerSettingsFactory.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 com.intellij.compiler;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.project.Project;
+
+/**
+ * Please use {@link com.intellij.openapi.options.ConfigurableEP#parentId} to put your configurable under Compiler Settings
+ * @author Eugene Zhuravlev
+ *         Date: Sep 17, 2008
+ */
+@Deprecated
+public interface CompilerSettingsFactory {
+  ExtensionPointName<CompilerSettingsFactory> EP_NAME = ExtensionPointName.create("com.intellij.compilerSettingsFactory");
+
+  Configurable create(Project project);
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/CompilerWorkspaceConfiguration.java b/java/compiler/openapi/src/com/intellij/compiler/CompilerWorkspaceConfiguration.java
new file mode 100644
index 0000000..98250d9
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/CompilerWorkspaceConfiguration.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+/*
+ * @author Eugene Zhuravlev
+ */
+package com.intellij.compiler;
+
+import com.intellij.openapi.components.*;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+
+@State(
+  name = "CompilerWorkspaceConfiguration",
+  storages = {
+    @Storage(
+      file = StoragePathMacros.WORKSPACE_FILE
+    )}
+)
+public class CompilerWorkspaceConfiguration implements PersistentStateComponent<CompilerWorkspaceConfiguration> {
+  public static final int DEFAULT_COMPILE_PROCESS_HEAP_SIZE = 700;
+  public static final String DEFAULT_COMPILE_PROCESS_VM_OPTIONS = "-ea";
+
+  public boolean AUTO_SHOW_ERRORS_IN_EDITOR = true;
+  @Deprecated public boolean CLOSE_MESSAGE_VIEW_IF_SUCCESS = true;
+  public boolean CLEAR_OUTPUT_DIRECTORY = true;
+  public boolean USE_COMPILE_SERVER = true;
+  public boolean MAKE_PROJECT_ON_SAVE = true;
+  public boolean PARALLEL_COMPILATION = false;
+  public int COMPILER_PROCESS_HEAP_SIZE = DEFAULT_COMPILE_PROCESS_HEAP_SIZE;
+  public String COMPILER_PROCESS_ADDITIONAL_VM_OPTIONS = DEFAULT_COMPILE_PROCESS_VM_OPTIONS;
+
+  public static CompilerWorkspaceConfiguration getInstance(Project project) {
+    return ServiceManager.getService(project, CompilerWorkspaceConfiguration.class);
+  }
+
+  public CompilerWorkspaceConfiguration getState() {
+    return this;
+  }
+
+  public void loadState(CompilerWorkspaceConfiguration state) {
+    XmlSerializerUtil.copyBean(state, this);
+  }
+
+  public boolean useOutOfProcessBuild() {
+    return USE_COMPILE_SERVER;
+  }
+
+  public boolean allowAutoMakeWhileRunningApplication() {
+    return false;/*ALLOW_AUTOMAKE_WHILE_RUNNING_APPLICATION*/
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/MalformedPatternException.java b/java/compiler/openapi/src/com/intellij/compiler/MalformedPatternException.java
new file mode 100644
index 0000000..af7636c
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/MalformedPatternException.java
@@ -0,0 +1,27 @@
+/*
+ * 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 com.intellij.compiler;
+
+public class MalformedPatternException extends RuntimeException {
+  public MalformedPatternException(Throwable cause) {
+    super(cause);
+  }
+
+  @Override
+  public String getLocalizedMessage() {
+    return getCause().getLocalizedMessage();
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ModuleCompilerUtil.java b/java/compiler/openapi/src/com/intellij/compiler/ModuleCompilerUtil.java
new file mode 100644
index 0000000..44b7b2a
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ModuleCompilerUtil.java
@@ -0,0 +1,173 @@
+/*
+ * 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 com.intellij.compiler;
+
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.ModuleRootModel;
+import com.intellij.openapi.util.Pair;
+import com.intellij.util.Chunk;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.graph.*;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * @author dsl
+ */
+public final class ModuleCompilerUtil {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.ModuleCompilerUtil");
+  private ModuleCompilerUtil() { }
+
+  public static Module[] getDependencies(Module module) {
+    return ModuleRootManager.getInstance(module).getDependencies();
+  }
+
+  public static Graph<Module> createModuleGraph(final Module[] modules) {
+    return GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph<Module>() {
+      public Collection<Module> getNodes() {
+        return Arrays.asList(modules);
+      }
+
+      public Iterator<Module> getIn(Module module) {
+        return Arrays.asList(getDependencies(module)).iterator();
+      }
+    }));
+  }
+
+  public static List<Chunk<Module>> getSortedModuleChunks(Project project, List<Module> modules) {
+    final Module[] allModules = ModuleManager.getInstance(project).getModules();
+    final List<Chunk<Module>> chunks = getSortedChunks(createModuleGraph(allModules));
+
+    final Set<Module> modulesSet = new HashSet<Module>(modules);
+    // leave only those chunks that contain at least one module from modules
+    for (Iterator<Chunk<Module>> it = chunks.iterator(); it.hasNext();) {
+      final Chunk<Module> chunk = it.next();
+      if (!ContainerUtil.intersects(chunk.getNodes(), modulesSet)) {
+        it.remove();
+      }
+    }
+    return chunks;
+  }
+
+  public static <Node> List<Chunk<Node>> getSortedChunks(final Graph<Node> graph) {
+    final Graph<Chunk<Node>> chunkGraph = toChunkGraph(graph);
+    final List<Chunk<Node>> chunks = new ArrayList<Chunk<Node>>(chunkGraph.getNodes().size());
+    for (final Chunk<Node> chunk : chunkGraph.getNodes()) {
+      chunks.add(chunk);
+    }
+    DFSTBuilder<Chunk<Node>> builder = new DFSTBuilder<Chunk<Node>>(chunkGraph);
+    if (!builder.isAcyclic()) {
+      LOG.error("Acyclic graph expected");
+      return null;
+    }
+
+    Collections.sort(chunks, builder.comparator());
+    return chunks;
+  }
+  
+  public static <Node> Graph<Chunk<Node>> toChunkGraph(final Graph<Node> graph) {
+    return GraphAlgorithms.getInstance().computeSCCGraph(graph);
+  }
+
+  public static void sortModules(final Project project, final List<Module> modules) {
+    final Application application = ApplicationManager.getApplication();
+    Runnable sort = new Runnable() {
+      public void run() {
+        Comparator<Module> comparator = ModuleManager.getInstance(project).moduleDependencyComparator();
+        Collections.sort(modules, comparator);
+      }
+    };
+    if (application.isDispatchThread()) {
+      sort.run();
+    }
+    else {
+      application.runReadAction(sort);
+    }
+  }
+
+
+  public static <T extends ModuleRootModel> GraphGenerator<T> createGraphGenerator(final Map<Module, T> models) {
+    return GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph<T>() {
+      public Collection<T> getNodes() {
+        return models.values();
+      }
+
+      public Iterator<T> getIn(final ModuleRootModel model) {
+        final Module[] modules = model.getModuleDependencies();
+        final List<T> dependencies = new ArrayList<T>();
+        for (Module module : modules) {
+          T depModel = models.get(module);
+          if (depModel != null) {
+            dependencies.add(depModel);
+          }
+        }
+        return dependencies.iterator();
+      }
+    }));
+  }
+
+  /**
+   * @return pair of modules which become circular after adding dependency, or null if all remains OK
+   */
+  @Nullable
+  public static Pair<Module, Module> addingDependencyFormsCircularity(final Module currentModule, Module toDependOn) {
+    assert currentModule != toDependOn;
+    // whatsa lotsa of @&#^%$ codes-a!
+
+    final Map<Module, ModifiableRootModel> models = new LinkedHashMap<Module, ModifiableRootModel>();
+    Project project = currentModule.getProject();
+    for (Module module : ModuleManager.getInstance(project).getModules()) {
+      ModifiableRootModel model = ModuleRootManager.getInstance(module).getModifiableModel();
+      models.put(module, model);
+    }
+    ModifiableRootModel currentModel = models.get(currentModule);
+    ModifiableRootModel toDependOnModel = models.get(toDependOn);
+    Collection<Chunk<ModifiableRootModel>> nodesBefore = buildChunks(models);
+    for (Chunk<ModifiableRootModel> chunk : nodesBefore) {
+      if (chunk.containsNode(toDependOnModel) && chunk.containsNode(currentModel)) return null; // they circular already
+    }
+
+    try {
+      currentModel.addModuleOrderEntry(toDependOn);
+      Collection<Chunk<ModifiableRootModel>> nodesAfter = buildChunks(models);
+      for (Chunk<ModifiableRootModel> chunk : nodesAfter) {
+        if (chunk.containsNode(toDependOnModel) && chunk.containsNode(currentModel)) {
+          Iterator<ModifiableRootModel> nodes = chunk.getNodes().iterator();
+          return Pair.create(nodes.next().getModule(), nodes.next().getModule());
+        }
+      }
+    }
+    finally {
+      for (ModifiableRootModel model : models.values()) {
+        model.dispose();
+      }
+    }
+    return null;
+  }
+
+  public static <T extends ModuleRootModel> Collection<Chunk<T>> buildChunks(final Map<Module, T> models) {
+    return toChunkGraph(createGraphGenerator(models)).getNodes();
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/BuildProperties.java b/java/compiler/openapi/src/com/intellij/compiler/ant/BuildProperties.java
new file mode 100644
index 0000000..51ee7d1
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/BuildProperties.java
@@ -0,0 +1,271 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NonNls;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+public abstract class BuildProperties extends CompositeGenerator {
+  public static final @NonNls String TARGET_ALL = "all";
+  public static final @NonNls String TARGET_BUILD_MODULES = "build.modules";
+  public static final @NonNls String TARGET_CLEAN = "clean";
+  public static final @NonNls String TARGET_INIT = "init";
+  public static final @NonNls String TARGET_REGISTER_CUSTOM_COMPILERS = "register.custom.compilers";
+  public static final @NonNls String DEFAULT_TARGET = TARGET_ALL;
+  public static final @NonNls String PROPERTY_COMPILER_NAME = "compiler.name";
+  public static final @NonNls String PROPERTY_COMPILER_ADDITIONAL_ARGS = "compiler.args";
+  public static final @NonNls String PROPERTY_COMPILER_MAX_MEMORY = "compiler.max.memory";
+  public static final @NonNls String PROPERTY_COMPILER_EXCLUDES = "compiler.excluded";
+  public static final @NonNls String PROPERTY_COMPILER_RESOURCE_PATTERNS = "compiler.resources";
+  public static final @NonNls String PROPERTY_IGNORED_FILES = "ignored.files";
+  public static final @NonNls String PROPERTY_COMPILER_GENERATE_DEBUG_INFO = "compiler.debug";
+  public static final @NonNls String PROPERTY_COMPILER_GENERATE_NO_WARNINGS = "compiler.generate.no.warnings";
+  public static final @NonNls String PROPERTY_PROJECT_JDK_HOME = "project.jdk.home";
+  public static final @NonNls String PROPERTY_PROJECT_JDK_BIN = "project.jdk.bin";
+  public static final @NonNls String PROPERTY_PROJECT_JDK_CLASSPATH = "project.jdk.classpath";
+  public static final @NonNls String PROPERTY_SKIP_TESTS = "skip.tests";
+  public static final @NonNls String PROPERTY_LIBRARIES_PATTERNS = "library.patterns";
+  public static final @NonNls String PROPERTY_IDEA_HOME = "idea.home";
+  public static final @NonNls String PROPERTY_JAVAC2_HOME = "javac2.home";
+  public static final @NonNls String PROPERTY_JAVAC2_CLASSPATH_ID = "javac2.classpath";
+
+  protected abstract void createJdkGenerators(Project project);
+
+  public static Sdk[] getUsedJdks(Project project) {
+    final Set<Sdk> jdks = new HashSet<Sdk>();
+    Module[] modules = ModuleManager.getInstance(project).getModules();
+    for (Module module : modules) {
+      Sdk jdk = ModuleRootManager.getInstance(module).getSdk();
+      if (jdk != null) {
+        jdks.add(jdk);
+      }
+    }
+    return jdks.toArray(new Sdk[jdks.size()]);
+  }
+
+  @NonNls
+  public static String getJdkPathId(@NonNls final String jdkName) {
+    return "jdk.classpath." + convertName(jdkName);
+  }
+
+  @NonNls
+  public static String getModuleChunkJdkClasspathProperty(@NonNls final String moduleChunkName) {
+    return "module.jdk.classpath." + convertName(moduleChunkName);
+  }
+
+  @NonNls
+  public static String getModuleChunkJdkHomeProperty(@NonNls final String moduleChunkName) {
+    return "module.jdk.home." + convertName(moduleChunkName);
+  }
+
+  @NonNls
+  public static String getModuleChunkJdkBinProperty(@NonNls final String moduleChunkName) {
+    return "module.jdk.bin." + convertName(moduleChunkName);
+  }
+
+  @NonNls
+  public static String getModuleChunkCompilerArgsProperty(@NonNls final String moduleName) {
+    return "compiler.args." + convertName(moduleName);
+  }
+
+  @NonNls
+  public static String getLibraryPathId(@NonNls final String libraryName) {
+    return "library." + convertName(libraryName) + ".classpath";
+  }
+
+  @NonNls
+  public static String getJdkHomeProperty(@NonNls final String jdkName) {
+    return "jdk.home." + convertName(jdkName);
+  }
+
+  @NonNls
+  public static String getJdkBinProperty(@NonNls final String jdkName) {
+    return "jdk.bin." + convertName(jdkName);
+  }
+
+  @NonNls
+  public static String getCompileTargetName(@NonNls String moduleName) {
+    return "compile.module." + convertName(moduleName);
+  }
+
+  @NonNls
+  public static String getOutputPathProperty(@NonNls String moduleName) {
+    return convertName(moduleName) + ".output.dir";
+  }
+
+  @NonNls
+  public static String getOutputPathForTestsProperty(@NonNls String moduleName) {
+    return convertName(moduleName) + ".testoutput.dir";
+  }
+
+  @NonNls
+  public static String getClasspathProperty(@NonNls String moduleName) {
+    return convertName(moduleName) + ".module.production.classpath";
+  }
+
+  @NonNls
+  public static String getTestClasspathProperty(@NonNls String moduleName) {
+    return convertName(moduleName) + ".module.classpath";
+  }
+
+  @NonNls
+  public static String getRuntimeClasspathProperty(@NonNls String moduleName) {
+    return convertName(moduleName) + ".runtime.production.module.classpath";
+  }
+
+  @NonNls
+  public static String getTestRuntimeClasspathProperty(@NonNls String moduleName) {
+    return convertName(moduleName) + ".runtime.module.classpath";
+  }
+
+  @NonNls
+  public static String getBootClasspathProperty(@NonNls String moduleName) {
+    return convertName(moduleName) + ".module.bootclasspath";
+  }
+
+  @NonNls
+  public static String getSourcepathProperty(@NonNls String moduleName) {
+    return convertName(moduleName) + ".module.sourcepath";
+  }
+
+  @NonNls
+  public static String getTestSourcepathProperty(@NonNls String moduleName) {
+    return convertName(moduleName) + ".module.test.sourcepath";
+  }
+
+  @NonNls
+  public static String getExcludedFromModuleProperty(@NonNls String moduleName) {
+    return "excluded.from.module." + convertName(moduleName);
+  }
+
+  @NonNls
+  public static String getExcludedFromCompilationProperty(@NonNls String moduleName) {
+    return "excluded.from.compilation." + convertName(moduleName);
+  }
+
+  @NonNls
+  public static String getProjectBuildFileName(Project project) {
+    return convertName(project.getName());
+  }
+
+  @NonNls
+  public static String getModuleChunkBuildFileName(final ModuleChunk chunk) {
+    return "module_" + convertName(chunk.getName());
+  }
+
+  @NonNls
+  public static String getModuleCleanTargetName(@NonNls String moduleName) {
+    return "clean.module." + convertName(moduleName);
+  }
+
+  @NonNls
+  public static String getModuleChunkBasedirProperty(ModuleChunk chunk) {
+    return "module." + convertName(chunk.getName()) + ".basedir";
+  }
+
+  /**
+   * left for compatibility
+   *
+   * @param module the module to get property for
+   * @return name of the property
+   */
+  @NonNls
+  public static String getModuleBasedirProperty(Module module) {
+    return "module." + convertName(module.getName()) + ".basedir";
+  }
+
+  @NonNls
+  public static String getProjectBaseDirProperty() {
+    return "basedir";
+  }
+
+  public static File getModuleChunkBaseDir(ModuleChunk chunk) {
+    return chunk.getBaseDir();
+  }
+
+  public static File getProjectBaseDir(final Project project) {
+    final VirtualFile baseDir = project.getBaseDir();
+    assert baseDir != null;
+    return VfsUtil.virtualToIoFile(baseDir);
+  }
+
+  /**
+   * Convert name. All double quotes are removed and spaces are replaced with underscore.
+   *
+   * @param name a name to convert
+   * @return a converted name
+   */
+  @NonNls
+  public static String convertName(@NonNls final String name) {
+    //noinspection HardCodedStringLiteral
+    return name.replaceAll("\"", "").replaceAll("\\s+", "_").toLowerCase();
+  }
+
+  @NonNls
+  public static String getPathMacroProperty(@NonNls String pathMacro) {
+    return "path.variable." + convertName(pathMacro);
+  }
+
+  @NonNls
+  public static String propertyRef(@NonNls String propertyName) {
+    return "${" + propertyName + "}";
+  }
+
+  /**
+   * Construct path relative to the specified property
+   *
+   * @param propertyName the property name
+   * @param relativePath the relative path
+   * @return the path relative to the property
+   */
+  @NonNls
+  public static String propertyRelativePath(@NonNls String propertyName, @NonNls String relativePath) {
+    return "${" + propertyName + "}/" + relativePath;
+  }
+
+
+  public static File toCanonicalFile(final File file) {
+    File canonicalFile;
+    try {
+      canonicalFile = file.getCanonicalFile();
+    }
+    catch (IOException e) {
+      canonicalFile = file;
+    }
+    return canonicalFile;
+  }
+
+  @NonNls
+  public static String getTempDirForModuleProperty(@NonNls String moduleName) {
+    return "tmp.dir." + convertName(moduleName);
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/BuildTargetsFactory.java b/java/compiler/openapi/src/com/intellij/compiler/ant/BuildTargetsFactory.java
new file mode 100644
index 0000000..65a7587
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/BuildTargetsFactory.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.
+ */
+
+/*
+ * User: anna
+ * Date: 19-Dec-2006
+ */
+package com.intellij.compiler.ant;
+
+import com.intellij.compiler.ant.taskdefs.Target;
+import com.intellij.openapi.compiler.make.BuildRecipe;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+public abstract class BuildTargetsFactory {
+  public static BuildTargetsFactory getInstance() {
+    return ServiceManager.getService(BuildTargetsFactory.class);
+  }
+
+  public abstract Generator createComment(String comment);
+
+  //for test
+  public abstract GenerationOptions getDefaultOptions(Project project);
+}
\ No newline at end of file
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/ChunkBuildExtension.java b/java/compiler/openapi/src/com/intellij/compiler/ant/ChunkBuildExtension.java
new file mode 100644
index 0000000..adb1cb0
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/ChunkBuildExtension.java
@@ -0,0 +1,85 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.ExtensionPoints;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.project.Project;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.elements.ArtifactAntGenerationContext;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public abstract class ChunkBuildExtension {
+  public static final ExtensionPointName<ChunkBuildExtension> EP_NAME = ExtensionPointName.create(ExtensionPoints.ANT_BUILD_GEN);
+
+  @NotNull
+  @NonNls
+  public abstract String[] getTargets(final ModuleChunk chunk);
+
+  public abstract void process(Project project, ModuleChunk chunk, GenerationOptions genOptions, CompositeGenerator generator);
+
+  public void generateProjectTargets(Project project, GenerationOptions genOptions, CompositeGenerator generator) {
+  }
+
+  public void generateProperties(final PropertyFileGenerator generator, final Project project, final GenerationOptions options) {
+  }
+
+  public void generateTasksForArtifact(Artifact artifact, boolean preprocessing, ArtifactAntGenerationContext context,
+                                       CompositeGenerator generator) {
+  }
+
+  public List<String> getCleanTargetNames(Project project, GenerationOptions genOptions) {
+    return Collections.emptyList();
+  }
+
+  public static String[] getAllTargets(ModuleChunk chunk) {
+    List<String> allTargets = new ArrayList<String>();
+    final ChunkBuildExtension[] extensions = Extensions.getRootArea().getExtensionPoint(EP_NAME).getExtensions();
+    for (ChunkBuildExtension extension : extensions) {
+      ContainerUtil.addAll(allTargets, extension.getTargets(chunk));
+    }
+    if (allTargets.isEmpty()) {
+      allTargets.add(BuildProperties.getCompileTargetName(chunk.getName()));
+    }
+    return ArrayUtil.toStringArray(allTargets);
+  }
+
+  public static void process(CompositeGenerator generator, ModuleChunk chunk, GenerationOptions genOptions) {
+    final Project project = chunk.getProject();
+    final ChunkBuildExtension[] extensions = Extensions.getRootArea().getExtensionPoint(EP_NAME).getExtensions();
+    for (ChunkBuildExtension extension : extensions) {
+      extension.process(project, chunk, genOptions, generator);
+    }
+  }
+
+  public static void generateAllProperties(final PropertyFileGenerator propertyFileGenerator,
+                                           final Project project,
+                                           final GenerationOptions genOptions) {
+    ChunkBuildExtension[] extensions = Extensions.getRootArea().getExtensionPoint(EP_NAME).getExtensions();
+    for (ChunkBuildExtension extension : extensions) {
+      extension.generateProperties(propertyFileGenerator, project, genOptions);
+    }
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/ChunkCustomCompilerExtension.java b/java/compiler/openapi/src/com/intellij/compiler/ant/ChunkCustomCompilerExtension.java
new file mode 100644
index 0000000..2356389
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/ChunkCustomCompilerExtension.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 com.intellij.compiler.ant;
+
+import com.intellij.ExtensionPoints;
+import com.intellij.compiler.ant.taskdefs.PatternSetRef;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.project.Project;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.LinkedList;
+
+/**
+ * The extenstion point for the custom compilers
+ */
+public abstract class ChunkCustomCompilerExtension {
+  /**
+   * Extension point name
+   */
+  public static final ExtensionPointName<ChunkCustomCompilerExtension> EP_NAME =
+    ExtensionPointName.create(ExtensionPoints.ANT_CUSTOM_COMPILER);
+  /**
+   * Comparator that compares extensions using names. It is used for make order of elements predictable.
+   */
+  protected static final Comparator<ChunkCustomCompilerExtension> COMPARATOR = new Comparator<ChunkCustomCompilerExtension>() {
+    public int compare(ChunkCustomCompilerExtension o1, ChunkCustomCompilerExtension o2) {
+      return o1.getClass().getName().compareTo(o2.getClass().getName());
+    }
+  };
+
+  /**
+   * Generate custom compile task inside compile target. Note that if more
+   * than one extension requested custom compilation, all of them are included into ant
+   * build and fail task is added to the compile target.
+   *
+   * @param project          the context project
+   * @param chunk            the chunk to compile
+   * @param genOptions       an options to compile
+   * @param generator        a generator where custom compilation tasks will be added.
+   * @param compileTests     if true, tests are compiled
+   * @param compilerArgs     the javac compilier arguements
+   * @param bootclasspathTag the boot classpath element for the javac compiler
+   * @param classpathTag     the classpath tag for the javac compiler
+   * @param compilerExcludes the compiler excluded tag
+   * @param srcTag           the soruce tag
+   * @param outputPathRef    the output path references
+   */
+  @SuppressWarnings({"UnusedDeclaration"})
+  public abstract void generateCustomCompile(Project project,
+                                             ModuleChunk chunk,
+                                             GenerationOptions genOptions,
+                                             boolean compileTests,
+                                             CompositeGenerator generator,
+                                             Tag compilerArgs,
+                                             Tag bootclasspathTag,
+                                             Tag classpathTag,
+                                             PatternSetRef compilerExcludes,
+                                             Tag srcTag,
+                                             String outputPathRef);
+
+  /**
+   * Generate task registration for custom compiler if needed.
+   *
+   * @param project    the context project
+   * @param genOptions an options to compile
+   * @param generator  a generator where custom compilation tasks will be added.
+   */
+  @SuppressWarnings({"UnusedDeclaration"})
+  public abstract void generateCustomCompilerTaskRegistration(Project project, GenerationOptions genOptions, CompositeGenerator generator);
+
+  /**
+   * Allows to check if the custom compilation task is required to compile module sources.
+   * Such task should be able to process standard java sources as well.
+   *
+   * @param chunk a chunk to check
+   * @return true if the facet requires custom comiplation.
+   */
+  @SuppressWarnings({"UnusedDeclaration", "MethodMayBeStatic"})
+  public abstract boolean hasCustomCompile(final ModuleChunk chunk);
+
+  /**
+   * Get custom compilation providers for module chunk
+   *
+   * @param chunk a chunk to examine
+   * @return a list of custom compilators
+   */
+  public static ChunkCustomCompilerExtension[] getCustomCompile(ModuleChunk chunk) {
+    final ChunkCustomCompilerExtension[] extensions = Extensions.getRootArea().getExtensionPoint(EP_NAME).getExtensions();
+    LinkedList<ChunkCustomCompilerExtension> compilers = new LinkedList<ChunkCustomCompilerExtension>();
+    for (ChunkCustomCompilerExtension extension : extensions) {
+      if (extension.hasCustomCompile(chunk)) {
+        compilers.add(extension);
+      }
+    }
+    final ChunkCustomCompilerExtension[] rc = compilers.toArray(new ChunkCustomCompilerExtension[compilers.size()]);
+    Arrays.sort(rc, ChunkCustomCompilerExtension.COMPARATOR);
+    return rc;
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/CompositeGenerator.java b/java/compiler/openapi/src/com/intellij/compiler/ant/CompositeGenerator.java
new file mode 100644
index 0000000..884e125
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/CompositeGenerator.java
@@ -0,0 +1,122 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.openapi.util.Pair;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A composite generator
+ *
+ * @author Eugene Zhuravlev
+ *         Date: Mar 25, 2004
+ */
+public class CompositeGenerator extends Generator {
+  /**
+   * child generators
+   */
+  private final List<Pair<Generator, Integer>> myGenerators = new ArrayList<Pair<Generator, Integer>>();
+  /**
+   * New line property
+   */
+  private boolean hasLeadingNewline = true;
+
+  /**
+   * A constructor
+   */
+  public CompositeGenerator() {
+  }
+
+  /**
+   * A constructor that adds two elements
+   *
+   * @param generator1      the first generator
+   * @param generator2      the second generator
+   * @param emptyLinesCount the amount of new lines between two generators
+   */
+  public CompositeGenerator(Generator generator1, Generator generator2, int emptyLinesCount) {
+    add(generator1);
+    add(generator2, emptyLinesCount);
+  }
+
+  /**
+   * By default, the composite generator generates an empty newline before generating nested eleemnts.
+   * Setting the property to the false, allows suppressing it.
+   *
+   * @param value the new value of the property
+   */
+  public void setHasLeadingNewline(boolean value) {
+    hasLeadingNewline = value;
+  }
+
+  /**
+   * Add child generator with no emtpy lines before it
+   *
+   * @param generator the generator to add
+   */
+  public final void add(Generator generator) {
+    add(generator, 0);
+  }
+
+  /**
+   * Add child generator with the specified amount of empty lines before it
+   *
+   * @param generator       a generator
+   * @param emptyLinesCount amount of empty lines
+   */
+  public final void add(Generator generator, int emptyLinesCount) {
+    myGenerators.add(new Pair<Generator, Integer>(generator, new Integer(emptyLinesCount)));
+  }
+
+  /**
+   * Generate content.
+   *
+   * @param out output stream
+   * @throws IOException in case of IO propblem
+   * @see #setHasLeadingNewline(boolean)
+   */
+  public void generate(PrintWriter out) throws IOException {
+    boolean first = true;
+    for (final Pair<Generator, Integer> pair : myGenerators) {
+      if (first) {
+        if (hasLeadingNewline) {
+          crlf(out);
+        }
+        first = false;
+      }
+      else {
+        crlf(out);
+      }
+      final int emptyLinesCount = pair.getSecond().intValue();
+      for (int idx = 0; idx < emptyLinesCount; idx++) {
+        crlf(out);
+      }
+      pair.getFirst().generate(out);
+    }
+  }
+
+  /**
+   * @return amount of the child generators
+   */
+  public final int getGeneratorCount() {
+    return myGenerators.size();
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/GenerationOptions.java b/java/compiler/openapi/src/com/intellij/compiler/ant/GenerationOptions.java
new file mode 100644
index 0000000..fc286ee
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/GenerationOptions.java
@@ -0,0 +1,152 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.openapi.module.Module;
+
+/**
+ * Ant file generation options. This object is availalbe during construction of ant object tree.
+ *
+ * @author anna
+ */
+public abstract class GenerationOptions {
+  /**
+     * This option specifies whether mulitfile or single file ant script is created.
+     */
+    public final boolean generateSingleFile;
+    /**
+     * This option specifies whehter standard javac or javac2 task is used
+     */
+    public final boolean enableFormCompiler;
+    /**
+     * This option speciries whether files are backed up before generation
+     */
+    public final boolean backupPreviouslyGeneratedFiles;
+    /**
+     * This option specifies whether target JDK is forced during compilation or default ant JDK is used.
+     */
+    public final boolean forceTargetJdk;
+    /**
+     * if true, the runtime classpath is inlined
+     */
+    public final boolean inlineRuntimeClasspath;
+    /**
+     * if true, the runtime classpath is inlined
+     */
+    public final boolean expandJarDirectories;
+
+
+    /**
+     * A constructor
+     *
+     * @param forceTargetJdk                 a value of corresponding option
+     * @param generateSingleFile             a value of corresponding option
+     * @param enableFormCompiler             a value of corresponding option
+     * @param backupPreviouslyGeneratedFiles a value of corresponding option
+     * @param inlineRuntimeClasspath         if true, a runtiem classpaths are inlined
+     * @param expandJarDirectories           if true, jar directories are expaned
+     */
+    public GenerationOptions(boolean forceTargetJdk,
+                             boolean generateSingleFile,
+                             boolean enableFormCompiler,
+                             boolean backupPreviouslyGeneratedFiles,
+                             boolean inlineRuntimeClasspath,
+                             boolean expandJarDirectories) {
+        this.forceTargetJdk = forceTargetJdk;
+        this.generateSingleFile = generateSingleFile;
+        this.enableFormCompiler = enableFormCompiler;
+        this.backupPreviouslyGeneratedFiles = backupPreviouslyGeneratedFiles;
+        this.inlineRuntimeClasspath = inlineRuntimeClasspath;
+        this.expandJarDirectories = expandJarDirectories;
+    }
+
+    /**
+     * A constructor
+     *
+     * @param forceTargetJdk                 a value of corresponding option
+     * @param generateSingleFile             a value of corresponding option
+     * @param enableFormCompiler             a value of corresponding option
+     * @param backupPreviouslyGeneratedFiles a value of corresponding option
+     * @param inlineRuntimeClasspath         if true a runtiem classpaths are inlined
+     */
+    public GenerationOptions(boolean forceTargetJdk,
+                             boolean generateSingleFile,
+                             boolean enableFormCompiler,
+                             boolean backupPreviouslyGeneratedFiles,
+                             boolean inlineRuntimeClasspath) {
+        this(forceTargetJdk, generateSingleFile, enableFormCompiler, backupPreviouslyGeneratedFiles, inlineRuntimeClasspath, false);
+    }
+
+    /**
+     * A constructor
+     *
+     * @param forceTargetJdk                 a value of corresponding option
+     * @param generateSingleFile             a value of corresponding option
+     * @param enableFormCompiler             a value of corresponding option
+     * @param backupPreviouslyGeneratedFiles a value of corresponding option
+     */
+    @Deprecated
+    public GenerationOptions(boolean forceTargetJdk,
+                             boolean generateSingleFile,
+                             boolean enableFormCompiler,
+                             boolean backupPreviouslyGeneratedFiles) {
+        this(forceTargetJdk, generateSingleFile, enableFormCompiler, backupPreviouslyGeneratedFiles, false);
+    }
+
+    /**
+     * Substitute path prefix with macro reference if it matches some macro.
+     *
+     * @param path a path to update
+     * @return an updated path or argument
+     */
+    public abstract String subsitutePathWithMacros(String path);
+
+    /**
+     * Get property reference for the specified url of module output directory
+     *
+     * @param url an URL to map
+     * @return the property reference in the form ${..}
+     */
+    public abstract String getPropertyRefForUrl(String url);
+
+    /**
+     * @return an array of module chunks. an array must not be modified by the clients.
+     */
+    public abstract ModuleChunk[] getModuleChunks();
+
+    /**
+     * Get the chunk that contains the specified module.
+     *
+     * @param module the module to find
+     * @return the chunk that contains specifid module
+     */
+    public abstract ModuleChunk getChunkByModule(Module module);
+
+    /**
+     * @return a set of custom compilers, each compiler is used at least once in some chunk.
+     */
+    public abstract ChunkCustomCompilerExtension[] getCustomCompilers();
+
+    /**
+     * @return true if the idea home property must be generated
+     */
+    public abstract boolean isIdeaHomeGenerated();
+
+
+  public abstract String getBuildFileName();
+  
+  public abstract String getPropertiesFileName();
+}
\ No newline at end of file
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/GenerationUtils.java b/java/compiler/openapi/src/com/intellij/compiler/ant/GenerationUtils.java
new file mode 100644
index 0000000..527d913
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/GenerationUtils.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.
+ */
+
+package com.intellij.compiler.ant;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.PathUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class GenerationUtils {
+    private GenerationUtils() {
+    }
+
+    /**
+     * Get relative file
+     *
+     * @param file       a valid file (must be either belong to {@link com.intellij.openapi.vfs.LocalFileSystem}  or to point to the root entry on
+     *                   {@link com.intellij.openapi.vfs.JarFileSystem}.
+     * @param chunk      a module chunk.
+     * @param genOptions generation options
+     * @return a relative path
+     */
+    @Nullable
+    public static String toRelativePath(final VirtualFile file, final ModuleChunk chunk, final GenerationOptions genOptions) {
+        final Module module = chunk.getModules()[0];
+        final File moduleBaseDir = chunk.getBaseDir();
+        return toRelativePath(file, moduleBaseDir, BuildProperties.getModuleBasedirProperty(module), genOptions);
+    }
+
+    public static String toRelativePath(final String file, final File baseDir, final Module module, final GenerationOptions genOptions) {
+        return toRelativePath(file, baseDir, BuildProperties.getModuleBasedirProperty(module), genOptions);
+    }
+
+    public static String toRelativePath(final String path, final ModuleChunk chunk, final GenerationOptions genOptions) {
+        return toRelativePath(path, chunk.getBaseDir(), BuildProperties.getModuleChunkBasedirProperty(chunk), genOptions);
+    }
+
+    /**
+     * Get relative file
+     *
+     * @param file                          a valid file (must be either belong to {@link com.intellij.openapi.vfs.LocalFileSystem}  or to point to the root entry on
+     *                                      {@link com.intellij.openapi.vfs.JarFileSystem}.
+     * @param baseDir                       base director for relative path calculation
+     * @param baseDirPropertyName           property name for the base directory
+     * @param genOptions                    generation options
+     * @return a relative path
+     */
+    @Nullable
+    public static String toRelativePath(final VirtualFile file,
+                                        final File baseDir,
+                                        final String baseDirPropertyName,
+                                        final GenerationOptions genOptions) {
+        final String localPath = PathUtil.getLocalPath(file);
+        if (localPath == null) {
+            return null;
+        }
+        return toRelativePath(localPath, baseDir, baseDirPropertyName, genOptions);
+    }
+
+    public static String toRelativePath(String path,
+                                        File baseDir,
+                                        @NonNls final String baseDirPropertyName,
+                                        GenerationOptions genOptions) {
+        path = normalizePath(path);
+        if(path.length() == 0) {
+            return path;
+        }
+        final String substitutedPath = genOptions.subsitutePathWithMacros(path);
+        if (!substitutedPath.equals(path)) {
+            // path variable substitution has highest priority
+            return substitutedPath;
+        }
+        if (baseDir != null) {
+            File base;
+            try {
+                // use canonical paths in order to resolve symlinks
+                base = baseDir.getCanonicalFile();
+            }
+            catch (IOException e) {
+                base = baseDir;
+            }
+            final String relativepath = FileUtil.getRelativePath(base, new File(path));
+            if (relativepath != null) {
+              final String _relativePath = relativepath.replace(File.separatorChar, '/');
+              final String root = BuildProperties.propertyRef(baseDirPropertyName);
+              return ".".equals(_relativePath) ? root : root + "/" + _relativePath;
+            }
+        }
+        return substitutedPath;
+    }
+
+    /**
+     * Normalize path by ensuring that only "/" is used as file name separator.
+     *
+     * @param path the path to normalize
+     * @return the normalized path
+     */
+    public static String normalizePath(String path) {
+        return path.replace(File.separatorChar, '/');
+    }
+
+    public static String trimJarSeparator(final String path) {
+        return path.endsWith(JarFileSystem.JAR_SEPARATOR) ? path.substring(0, path.length() - JarFileSystem.JAR_SEPARATOR.length()) : path;
+    }
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/Generator.java b/java/compiler/openapi/src/com/intellij/compiler/ant/Generator.java
new file mode 100644
index 0000000..40c143b
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/Generator.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 com.intellij.compiler.ant;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 16, 2004
+ */
+public abstract class Generator {
+  private static int ourIndent = 0;
+  private static final int INDENT_SHIFT = 2;
+
+  public abstract void generate(PrintWriter out) throws IOException;
+
+  protected static void crlf(PrintWriter out) throws IOException {
+    out.println();
+    indent(out);
+  }
+
+  protected static void shiftIndent() {
+    ourIndent += INDENT_SHIFT;
+  }
+
+  protected static void unshiftIndent() {
+    ourIndent -= INDENT_SHIFT;
+  }
+
+  protected static void indent(PrintWriter out) throws IOException {
+    for (int idx = 0; idx < ourIndent; idx++) {
+      out.print(" ");
+    }
+  }
+
+  /**
+   * Write XML header
+   * @param out a writer
+   * @throws IOException in case of IO problem
+   */
+  protected static void writeXmlHeader(final PrintWriter out) throws IOException {
+    //noinspection HardCodedStringLiteral
+    out.print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+    crlf(out);
+  }
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/ModuleChunk.java b/java/compiler/openapi/src/com/intellij/compiler/ant/ModuleChunk.java
new file mode 100644
index 0000000..0665dd2
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/ModuleChunk.java
@@ -0,0 +1,134 @@
+/*
+ * 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 com.intellij.compiler.ant;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.CompilerModuleExtension;
+import com.intellij.openapi.roots.ModuleRootManager;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Module chunk consists of interdependent modules.
+ *
+ * @author Eugene Zhuravlev
+ *         Date: Nov 19, 2004
+ */
+public class ModuleChunk {
+  /**
+   * Modules in the chunk
+   */
+  private final Module[] myModules;
+  /**
+   * A array of custom compilation providers.
+   */
+  private final ChunkCustomCompilerExtension[] myCustomCompilers;
+  /**
+   * The main module in the chunck (guessed by heuristic or selected by user)
+   */
+  private Module myMainModule;
+  /**
+   * Chucnk dependendencies
+   */
+  private ModuleChunk[] myDependentChunks;
+  private File myBaseDir = null;
+
+  public ModuleChunk(Module[] modules) {
+    myModules = modules;
+    Arrays.sort(myModules, new Comparator<Module>() {
+      public int compare(final Module o1, final Module o2) {
+        return o1.getName().compareToIgnoreCase(o2.getName());
+      }
+    });
+    myMainModule = myModules[0];
+    myCustomCompilers = ChunkCustomCompilerExtension.getCustomCompile(this);
+  }
+
+  public String getName() {
+    return myMainModule.getName();
+  }
+
+  /**
+   * @return an array of custom compilers for the module chunk
+   */
+  public ChunkCustomCompilerExtension[] getCustomCompilers() {
+    return myCustomCompilers;
+  }
+
+  public Module[] getModules() {
+    return myModules;
+  }
+
+  @Nullable
+  public String getOutputDirUrl() {
+    return CompilerModuleExtension.getInstance(myMainModule).getCompilerOutputUrl();
+  }
+
+  @Nullable
+  public String getTestsOutputDirUrl() {
+    return CompilerModuleExtension.getInstance(myMainModule).getCompilerOutputUrlForTests();
+  }
+
+  public boolean isJdkInherited() {
+    return ModuleRootManager.getInstance(myMainModule).isSdkInherited();
+  }
+
+  @Nullable
+  public Sdk getJdk() {
+    return ModuleRootManager.getInstance(myMainModule).getSdk();
+  }
+
+  public ModuleChunk[] getDependentChunks() {
+    return myDependentChunks;
+  }
+
+  public void setDependentChunks(ModuleChunk[] dependentChunks) {
+    myDependentChunks = dependentChunks;
+  }
+
+  public File getBaseDir() {
+    if (myBaseDir != null) {
+      return myBaseDir;
+    }
+    return new File(myMainModule.getModuleFilePath()).getParentFile();
+  }
+
+  public void setBaseDir(File baseDir) {
+    myBaseDir = baseDir;
+  }
+
+  public void setMainModule(Module module) {
+    myMainModule = module;
+  }
+
+  public Project getProject() {
+    return myMainModule.getProject();
+  }
+
+  public boolean contains(final Module module) {
+    for (Module chunkModule : myModules) {
+      if (chunkModule.equals(module)) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/PropertyFileGenerator.java b/java/compiler/openapi/src/com/intellij/compiler/ant/PropertyFileGenerator.java
new file mode 100644
index 0000000..86c65d7
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/PropertyFileGenerator.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 com.intellij.compiler.ant;
+
+/**
+ * @author nik
+ */
+public abstract class PropertyFileGenerator extends Generator {
+  /**
+   * Add property. Note that property name and value
+   * are automatically escaped when the property file
+   * is generated.
+   *
+   * @param name a property name
+   * @param value a property value
+   */
+  public abstract void addProperty(String name, String value);
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/Tag.java b/java/compiler/openapi/src/com/intellij/compiler/ant/Tag.java
new file mode 100644
index 0000000..aa08438
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/Tag.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 com.intellij.compiler.ant;
+
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class Tag extends CompositeGenerator {
+  public static final Tag[] EMPTY_ARRAY = new Tag[0];
+  private final String myTagName;
+  private final Pair[] myTagOptions;
+
+  public Tag(@NonNls String tagName, Pair... tagOptions) {
+    myTagName = tagName;
+    myTagOptions = tagOptions;
+  }
+
+  public void generate(PrintWriter out) throws IOException {
+    out.print("<");
+    out.print(myTagName);
+    if (myTagOptions != null && myTagOptions.length > 0) {
+      out.print(" ");
+      int generated = 0;
+      for (final Pair option : myTagOptions) {
+        if (option == null) {
+          continue;
+        }
+        if (generated > 0) {
+          out.print(" ");
+        }
+        out.print((String)option.getFirst());
+        out.print("=\"");
+        out.print(StringUtil.escapeXml((String)option.getSecond()));
+        out.print("\"");
+        generated += 1;
+      }
+    }
+    if (getGeneratorCount() > 0) {
+      out.print(">");
+      shiftIndent();
+      try {
+        super.generate(out);
+      }
+      finally {
+        unshiftIndent();
+      }
+      crlf(out);
+      out.print("</");
+      out.print(myTagName);
+      out.print(">");
+    }
+    else {
+      out.print("/>");
+    }
+  }
+
+  @Nullable
+  protected static Pair<String, String> pair(@NonNls String v1, @Nullable @NonNls String v2) {
+    if (v2 == null) {
+      return null;
+    }
+    return new Pair<String, String>(v1, v2);
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/AntCall.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/AntCall.java
new file mode 100644
index 0000000..4768d5f
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/AntCall.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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.util.Pair;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+@SuppressWarnings({"HardCodedStringLiteral"})
+public class AntCall extends Tag{
+
+  public AntCall(final String target) {
+    super("antcall", new Pair[] {new Pair<String, String>("target", target)});
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/AntProject.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/AntProject.java
new file mode 100644
index 0000000..3aa3eee
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/AntProject.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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 25, 2004
+ */
+public class AntProject extends Tag {
+  public AntProject(@NonNls String name, @NonNls String defaultTarget) {
+    //noinspection HardCodedStringLiteral
+    super("project", new Pair[]{new Pair<String, String>("name", name), new Pair<String, String>("default", defaultTarget)});
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Attribute.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Attribute.java
new file mode 100644
index 0000000..d6098db
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Attribute.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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class Attribute extends Tag{
+  public Attribute(@NonNls String name, String value) {
+    //noinspection HardCodedStringLiteral
+    super("attribute", new Pair[] {Pair.create("name", name),Pair.create("value", value)});
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Copy.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Copy.java
new file mode 100644
index 0000000..c95348b
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Copy.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.
+ */
+
+package com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 23, 2004
+ */
+public class Copy extends Tag {
+  public Copy(@NonNls String toDir) {
+    //noinspection HardCodedStringLiteral
+    super("copy", new Pair[] {new Pair<String, String>("todir", toDir)});
+  }
+  public Copy(@NonNls String file, @NonNls String toFile) {
+    //noinspection HardCodedStringLiteral
+    super("copy", new Pair[] {new Pair<String, String>("file", file), new Pair<String, String>("tofile", toFile)});
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Delete.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Delete.java
new file mode 100644
index 0000000..2dfe0d3
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Delete.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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class Delete extends Tag{
+  public Delete(@NonNls String dir) {
+    //noinspection HardCodedStringLiteral
+    super("delete", new Pair[] {Pair.create("dir", dir)});
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/DirSet.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/DirSet.java
new file mode 100644
index 0000000..b4cba08
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/DirSet.java
@@ -0,0 +1,33 @@
+/*
+ * 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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class DirSet extends Tag{
+
+  public DirSet(@NonNls final String dir) {
+    //noinspection HardCodedStringLiteral
+    super("dirset", new Pair[] {new Pair<String, String>("dir", dir)});
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Dirname.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Dirname.java
new file mode 100644
index 0000000..f833f58
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Dirname.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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 25, 2004
+ */
+public class Dirname extends Tag{
+  public Dirname(@NonNls String property, @NonNls String file) {
+    //noinspection HardCodedStringLiteral
+    super("dirname", new Pair[] {new Pair<String, String>("property", property), new Pair<String, String>("file", file)});
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Exclude.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Exclude.java
new file mode 100644
index 0000000..42e7a6a
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Exclude.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.
+ */
+
+package com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class Exclude extends Tag {
+
+  public Exclude(@NonNls final String name) {
+    //noinspection HardCodedStringLiteral
+    super("exclude", new Pair[] {new Pair<String, String>("name", name)});
+  }
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/FileSet.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/FileSet.java
new file mode 100644
index 0000000..5db6e0d
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/FileSet.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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class FileSet extends Tag{
+
+  public FileSet(@NonNls final String dir) {
+    super("fileset", pair("dir", dir));
+  }
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Import.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Import.java
new file mode 100644
index 0000000..2c6e23c
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Import.java
@@ -0,0 +1,37 @@
+/*
+ * 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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 24, 2004
+ */
+public class Import extends Tag{
+  public Import(@NonNls String file, boolean optional) {
+    //noinspection HardCodedStringLiteral
+    super("import", new Pair[] {new Pair<String, String>("file", file), new Pair<String, String>("optional", optional? "true" : "false")});
+  }
+
+  public Import(@NonNls String file) {
+    //noinspection HardCodedStringLiteral
+    super("import", new Pair[] {new Pair<String, String>("file", file)});
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Include.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Include.java
new file mode 100644
index 0000000..73352a2
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Include.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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class Include extends Tag {
+
+  public Include(@NonNls final String name) {
+    super("include", pair("name", name));
+  }
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Jar.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Jar.java
new file mode 100644
index 0000000..423faa9
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Jar.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.
+ */
+
+package com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class Jar extends Tag {
+  public Jar(@NonNls final String destFile, @NonNls String duplicate) {
+    this(destFile, duplicate, false);
+  }
+
+  public Jar(@NonNls final String destFile, @NonNls String duplicate, final boolean useManifestFromFileSets) {
+    super("jar", pair("destfile", destFile), pair("duplicate", duplicate), useManifestFromFileSets ? pair("filesetmanifest", "mergewithoutmain") : null);
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Javac.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Javac.java
new file mode 100644
index 0000000..c64884e
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Javac.java
@@ -0,0 +1,70 @@
+/*
+ * 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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.BuildProperties;
+import com.intellij.compiler.ant.GenerationOptions;
+import com.intellij.compiler.ant.ModuleChunk;
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 16, 2004
+ */
+public class Javac extends Tag {
+
+    public Javac(GenerationOptions genOptions, ModuleChunk moduleChunk, final String outputDir) {
+        super(getTagName(genOptions, moduleChunk), getAttributes(genOptions, outputDir, moduleChunk));
+    }
+
+    private static String getTagName(GenerationOptions genOptions, ModuleChunk moduleChunk) {
+        if (moduleChunk.getCustomCompilers().length > 0) {
+            return "instrumentIdeaExtensions";
+        }
+        return genOptions.enableFormCompiler ? "javac2" : "javac";
+    }
+
+    private static Pair[] getAttributes(GenerationOptions genOptions, String outputDir, ModuleChunk moduleChunk) {
+        final List<Pair> pairs = new ArrayList<Pair>();
+        pairs.add(pair("destdir", outputDir));
+        if (moduleChunk.getCustomCompilers().length == 0) {
+            pairs.add(pair("debug", BuildProperties.propertyRef(BuildProperties.PROPERTY_COMPILER_GENERATE_DEBUG_INFO)));
+            pairs.add(pair("nowarn", BuildProperties.propertyRef(BuildProperties.PROPERTY_COMPILER_GENERATE_NO_WARNINGS)));
+            pairs.add(pair("memorymaximumsize", BuildProperties.propertyRef(BuildProperties.PROPERTY_COMPILER_MAX_MEMORY)));
+            pairs.add(pair("fork", "true"));
+            if (genOptions.forceTargetJdk) {
+                pairs.add(pair("executable", getExecutable(moduleChunk.getName())));
+            }
+        }
+        return pairs.toArray(new Pair[pairs.size()]);
+    }
+
+    @Nullable
+    @NonNls
+    private static String getExecutable(String moduleName) {
+        if (moduleName == null) {
+            return null;
+        }
+        return BuildProperties.propertyRef(BuildProperties.getModuleChunkJdkBinProperty(moduleName)) + "/javac";
+    }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Manifest.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Manifest.java
new file mode 100644
index 0000000..da0ac1c
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Manifest.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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.compiler.make.ManifestBuilder;
+import com.intellij.openapi.util.Pair;
+
+import java.util.jar.Attributes;
+import java.util.*;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class Manifest extends Tag{
+  public Manifest() {
+    super("manifest", new Pair[] {});
+  }
+
+  public void applyAttributes(final java.util.jar.Manifest manifest) {
+    ManifestBuilder.setGlobalAttributes(manifest.getMainAttributes());
+    final Attributes mainAttributes = manifest.getMainAttributes();
+
+    List<Object> keys = new ArrayList<Object>(mainAttributes.keySet());
+    Collections.sort(keys, new Comparator<Object>() {
+      public int compare(final Object o1, final Object o2) {
+        Attributes.Name name1 = (Attributes.Name)o1;
+        Attributes.Name name2 = (Attributes.Name)o2;
+        return name1.toString().compareTo(name2.toString());
+      }
+    });
+    for (final Object o : keys) {
+      Attributes.Name name = (Attributes.Name)o;
+      String value = (String)mainAttributes.get(name);
+      add(new Attribute(name.toString(), value));
+    }
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Mkdir.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Mkdir.java
new file mode 100644
index 0000000..ef15e5c
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Mkdir.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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 17, 2004
+ */
+public class Mkdir extends Tag {
+  public Mkdir(@NonNls String directory) {
+    //noinspection HardCodedStringLiteral
+    super("mkdir", new Pair[] {new Pair<String, String>("dir", directory)});
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Param.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Param.java
new file mode 100644
index 0000000..19649e0
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Param.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.
+ */
+
+package com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class Param extends Tag {
+  public Param(@NonNls final String name, final String value) {
+    //noinspection HardCodedStringLiteral
+    super("param", new Pair[] {
+      new Pair<String, String>("name", name),
+      new Pair<String, String>("value", value)
+    });
+  }
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Path.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Path.java
new file mode 100644
index 0000000..925c187
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Path.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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class Path extends Tag {
+
+    public Path(@NonNls final String id) {
+        super("path", pair("id", id));
+    }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/PathElement.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/PathElement.java
new file mode 100644
index 0000000..f48c1a5
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/PathElement.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.
+ */
+
+package com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class PathElement extends Tag {
+
+    public PathElement(@NonNls String dir) {
+        //noinspection HardCodedStringLiteral
+        super("pathelement", pair("location", dir));
+    }
+
+}
+
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/PathRef.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/PathRef.java
new file mode 100644
index 0000000..1f8d658
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/PathRef.java
@@ -0,0 +1,33 @@
+/*
+ * 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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class PathRef extends Tag{
+
+  public PathRef(@NonNls final String refid) {
+    super("path", new Pair[] {pair("refid", refid)});
+  }
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/PatternSet.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/PatternSet.java
new file mode 100644
index 0000000..6a1d237
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/PatternSet.java
@@ -0,0 +1,30 @@
+/*
+ * 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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class PatternSet extends Tag{
+  public PatternSet(@NonNls final String id) {
+    super("patternset", pair("id", id));
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/PatternSetRef.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/PatternSetRef.java
new file mode 100644
index 0000000..1055016
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/PatternSetRef.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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class PatternSetRef extends Tag{
+  public PatternSetRef(@NonNls final String refid) {
+    //noinspection HardCodedStringLiteral
+    super("patternset", new Pair[] {new Pair<String, String>("refid", refid)});
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Property.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Property.java
new file mode 100644
index 0000000..f28d127
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Property.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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class Property extends Tag {
+
+  public Property(@NonNls final String name, final String value) {
+    //noinspection HardCodedStringLiteral
+    super("property", new Pair[] {
+      new Pair<String, String>("name", name),
+      new Pair<String, String>("value", value)
+    });
+  }
+
+  public Property(@NonNls final String filePath) {
+    //noinspection HardCodedStringLiteral
+    super("property", new Pair[] {
+      new Pair<String, String>("file", filePath),
+    });
+  }
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Target.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Target.java
new file mode 100644
index 0000000..26f35bd
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Target.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.
+ */
+
+package com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class Target extends Tag{
+  public Target(@NonNls String name, @Nullable String depends, @Nullable String description, @Nullable String unlessCondition) {
+    super("target", getOptions(name, depends, description, unlessCondition));
+  }
+
+  @SuppressWarnings({"HardCodedStringLiteral"})
+  private static Pair[] getOptions(@NonNls String name, @Nullable @NonNls String depends, @Nullable String description, @Nullable @NonNls String unlessCondition) {
+    final List<Pair> options = new ArrayList<Pair>();
+    options.add(new Pair<String, String>("name", name));
+    if (depends != null && depends.length() > 0) {
+      options.add(new Pair<String, String>("depends", depends));
+    }
+    if (description != null && description.length() > 0) {
+      options.add(new Pair<String, String>("description", description));
+    }
+    if (unlessCondition != null && unlessCondition.length() > 0) {
+      options.add(new Pair<String, String>("unless", unlessCondition));
+    }
+    return options.toArray(new Pair[options.size()]);
+  }
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Unzip.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Unzip.java
new file mode 100644
index 0000000..8e3a4e7
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Unzip.java
@@ -0,0 +1,27 @@
+/*
+ * 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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+
+/**
+ * @author nik
+ */
+public class Unzip extends Tag {
+  public Unzip(String archivePath, String dest) {
+    super("unzip", pair("src", archivePath), pair("dest", dest));
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Zip.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Zip.java
new file mode 100644
index 0000000..a79ee96
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/Zip.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.
+ */
+
+/*
+ * User: anna
+ * Date: 19-Dec-2006
+ */
+package com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NonNls;
+
+public class Zip extends Tag {
+  public Zip(@NonNls final String destFile) {
+    //noinspection HardCodedStringLiteral
+    super("zip", new Pair[] {Pair.create("destfile", destFile)});
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/ZipFileSet.java b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/ZipFileSet.java
new file mode 100644
index 0000000..a11b951
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/compiler/ant/taskdefs/ZipFileSet.java
@@ -0,0 +1,68 @@
+/*
+ * 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 com.intellij.compiler.ant.taskdefs;
+
+import com.intellij.compiler.ant.Tag;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Mar 19, 2004
+ */
+public class ZipFileSet extends Tag{
+  public static final ZipFileSet[] EMPTY_ARRAY = new ZipFileSet[0];
+
+  private ZipFileSet(@NonNls String tagName, Pair... tagOptions) {
+    super(tagName, tagOptions);
+  }
+
+  public ZipFileSet(@NonNls String fileOrDir, @NonNls final String relativePath, boolean isDir) {
+    super("zipfileset",
+          pair(isDir ? "dir" : "file", fileOrDir),
+          pair("prefix", prefix(isDir, relativePath)));
+  }
+
+  public static ZipFileSet createUnpackedSet(@NonNls String zipFilePath, @NotNull String relativePath, final boolean isDir) {
+    return new ZipFileSet("zipfileset",
+                          pair("src", zipFilePath),
+                          pair("prefix", prefix(isDir, relativePath)));
+  }
+
+  @Nullable
+  private static String prefix(final boolean isDir, final String relativePath) {
+    String path;
+    if (isDir) {
+      path = relativePath;
+    }
+    else {
+      final String parent = new File(relativePath).getParent();
+      path = parent == null ? "" : FileUtil.toSystemIndependentName(parent);
+    }
+    if (path != null) {
+      path = StringUtil.trimStart(path, "/");
+    }
+    return !StringUtil.isEmpty(path) ? path : null;
+  }
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/ClassInstrumentingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/ClassInstrumentingCompiler.java
new file mode 100644
index 0000000..cfcf337
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/ClassInstrumentingCompiler.java
@@ -0,0 +1,26 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+
+/**
+ * A tag interface indicating that the compiler will instrument java classes.
+ * This affects the order of compiler calls:
+ * The sequence in which compilers are called:
+ * SourceGeneratingCompiler -> SourceInstrumentingCompiler -> TranslatingCompiler ->  ClassInstrumentingCompiler -> ClassPostProcessingCompiler -> PackagingCompiler -> Validator
+ */
+public interface ClassInstrumentingCompiler extends FileProcessingCompiler {
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/ClassPostProcessingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/ClassPostProcessingCompiler.java
new file mode 100644
index 0000000..203facd
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/ClassPostProcessingCompiler.java
@@ -0,0 +1,26 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+
+/**
+ * A tag interface indicating that the compiler will take Java classes and perform some activities on them.
+ * This affects the order of compiler calls:
+ * The sequence in which compilers are called:
+ * SourceGeneratingCompiler -> SourceInstrumentingCompiler -> TranslatingCompiler ->  ClassInstrumentingCompiler -> ClassPostProcessingCompiler -> PackagingCompiler -> Validator
+ */
+public interface ClassPostProcessingCompiler extends FileProcessingCompiler {
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilationStatusAdapter.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilationStatusAdapter.java
new file mode 100644
index 0000000..2639194
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilationStatusAdapter.java
@@ -0,0 +1,24 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+public class CompilationStatusAdapter implements CompilationStatusListener {
+  public void compilationFinished(boolean aborted, int errors, int warnings, final CompileContext compileContext) {
+  }
+
+  public void fileGenerated(String outputRoot, String relativePath) {
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilationStatusListener.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilationStatusListener.java
new file mode 100644
index 0000000..0675098
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilationStatusListener.java
@@ -0,0 +1,37 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+import java.util.EventListener;
+
+/**
+ * A listener for compiler events.
+ *
+ * @see CompilerManager#addCompilationStatusListener(CompilationStatusListener)
+ */
+public interface CompilationStatusListener extends EventListener {
+  /**
+   * Invoked in a Swing dispatch thread after the compilation is finished.
+   *
+   * @param aborted  true if compilatioin has been cancelled
+   * @param errors   error count
+   * @param warnings warning count
+   * @param compileContext context for the finished compilation
+   */
+  void compilationFinished(boolean aborted, int errors, int warnings, final CompileContext compileContext);
+
+  void fileGenerated(String outputRoot, String relativePath);
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompileContext.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompileContext.java
new file mode 100644
index 0000000..0f93063
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompileContext.java
@@ -0,0 +1,157 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.UserDataHolder;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.pom.Navigatable;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * An interface allowing access and modification of the data associated with the current compile session.
+ */
+public interface CompileContext extends UserDataHolder {
+  /**
+   * Allows to add a message to be shown in Compiler message view.
+   * If correct url, line and column numbers are supplied, the navigation to the specified file is available from the view.
+   *
+   * @param category  the category of a message (information, error, warning).
+   * @param message   the text of the message.
+   * @param url       a url to the file to which the message applies, null if not available.
+   * @param lineNum   a line number, -1 if not available.
+   * @param columnNum a column number, -1 if not available.
+   */
+  void addMessage(CompilerMessageCategory category, String message, @Nullable String url, int lineNum, int columnNum);
+
+  /**
+   * Allows to add a message to be shown in Compiler message view, with a specified Navigatable
+   * that is used to navigate to the error location.
+   *
+   * @param category    the category of a message (information, error, warning).
+   * @param message     the text of the message.
+   * @param url         a url to the file to which the message applies, null if not available.
+   * @param lineNum     a line number, -1 if not available.
+   * @param columnNum   a column number, -1 if not available.
+   * @param navigatable the navigatable pointing to the error location.
+   * @since 6.0
+   */
+  void addMessage(CompilerMessageCategory category, String message, @Nullable String url, int lineNum, int columnNum,
+                  Navigatable navigatable);
+
+  /**
+   * Returns all messages of the specified category added during the current compile session.
+   *
+   * @param category the category for which messages are requested.
+   * @return all compiler messages of the specified category
+   */
+  CompilerMessage[] getMessages(CompilerMessageCategory category);
+
+  /**
+   * Returns the count of messages of the specified category added during the current compile session.
+   *
+   * @param category the category for which messages are requested.
+   * @return the number of messages of the specified category
+   */
+  int getMessageCount(CompilerMessageCategory category);
+
+  /**
+   * Returns the progress indicator of the compilation process.
+   *
+   * @return the progress indicator instance.
+   */
+  @NotNull
+  ProgressIndicator getProgressIndicator();
+
+  /**
+   * Returns the current compile scope.
+   *
+   * @return current compile scope
+   */
+  CompileScope getCompileScope();
+
+  /**
+   * Returns the compile scope which would be used if the entire project was rebuilt.
+   * {@link #getCompileScope()} may return the scope, that is more narrow than ProjectCompileScope.
+   *
+   * @return project-wide compile scope.
+   */
+  CompileScope getProjectCompileScope();
+
+  /**
+   * A compiler may call this method in order to request complete project rebuild.
+   * This may be necessary, for example, when compiler caches are corrupted.
+   */
+  void requestRebuildNextTime(String message);
+
+  /**
+   * Returns the module to which the specified file belongs. This method is aware of the file->module mapping
+   * for generated files.
+   *
+   * @param file the file to check.
+   * @return the module to which the file belongs
+   */
+  Module getModuleByFile(VirtualFile file);
+
+  /**
+   * Returns the source roots for the specified module.
+   *
+   * @return module's source roots as well as source roots for generated sources that are attributed to the module
+   */
+  VirtualFile[] getSourceRoots(Module module);
+
+  /**
+   * Returns the list of all output directories.
+   *
+   * @return a list of all configured output directories from all modules (including output directories for tests)
+   */
+  VirtualFile[] getAllOutputDirectories();
+
+  /**
+   * Returns the output directory for the specified module.
+   *
+   * @param module the module to check.
+   * @return the output directory for the module specified, null if corresponding VirtualFile is not valid or directory not specified
+   */
+  @Nullable
+  VirtualFile getModuleOutputDirectory(Module module);
+
+  /**
+   * Returns the test output directory for the specified module.
+   *
+   * @param module the module to check.
+   * @return the tests output directory the module specified, null if corresponding VirtualFile is not valid. If in Paths settings
+   *         output directory for tests is not configured explicitly, but the output path is present, the output path will be returned.
+   */
+  @Nullable
+  VirtualFile getModuleOutputDirectoryForTests(Module module);
+
+  /**
+   * Checks if the compilation is incremental, i.e. triggered by one of "Make" actions.
+   *
+   * @return true if compilation is incremental. 
+   */
+  boolean isMake();
+
+  boolean isRebuild();
+
+  Project getProject();
+
+  boolean isAnnotationProcessorsEnabled();
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompileScope.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompileScope.java
new file mode 100644
index 0000000..5e1c304
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompileScope.java
@@ -0,0 +1,59 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Interface describing the current compilation scope.
+ * Only sources that belong to the scope are compiled.
+ *
+ * @see CompilerManager#compile(CompileScope, CompileStatusNotification)
+ */
+public interface CompileScope extends ExportableUserDataHolder {
+  CompileScope[] EMPTY_ARRAY = new CompileScope[0];
+  /**
+   * Returns the list of files within the scope.
+   *
+   * @param fileType     the type of the files. Null should be passed if all available files are needed.
+   * @param inSourceOnly if true, files are searched only in directories within the scope that are marked as "sources" or "test sources" in module settings.
+   *                     Otherwise files are searched in all directories that belong to the scope.
+   * @return a list of files of given type that belong to this scope.
+   */
+  @NotNull
+  VirtualFile[] getFiles(@Nullable FileType fileType, boolean inSourceOnly);
+
+  /**
+   * Checks if the file with the specified URL belongs to the scope.
+   *
+   * @param url an VFS url. Note that actual file may not exist on the disk.
+   * @return true if the url specified belongs to the scope, false otherwise.
+   *         Note: the method may be time-consuming.
+   */
+  boolean belongs(String url);
+
+  /**
+   * Returns the list of modules files in which belong to the scope.
+   *
+   * @return a list of modules this scope affects.
+   */
+  @NotNull
+  Module[] getAffectedModules();
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompileStatusNotification.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompileStatusNotification.java
new file mode 100644
index 0000000..9a8d7f0
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompileStatusNotification.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.
+ */
+package com.intellij.openapi.compiler;
+
+/**
+ * A callback interface passed to ComplerManager methods. Provides notification similar to
+ * {@link CompilationStatusListener}.
+ *
+ * @see CompilerManager#compile(CompileScope, CompileStatusNotification)
+ */
+public interface CompileStatusNotification {
+  /**
+   * Invoked in a Swing dispatch thread after the compilation is finished.
+   *
+   * @param aborted  true if compilation has been cancelled.
+   * @param errors   error count
+   * @param warnings warning count
+   * @param compileContext context for the finished compilation
+   */
+  void finished(boolean aborted, int errors, int warnings, final CompileContext compileContext);
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompileTask.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompileTask.java
new file mode 100644
index 0000000..e3bd1df
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompileTask.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.
+ */
+package com.intellij.openapi.compiler;
+
+/**
+ * Describes a task to be executed before or after compilation.
+ *
+ * @see CompilerManager#addAfterTask(CompileTask)
+ * @see CompilerManager#addBeforeTask(CompileTask)
+ */
+public interface CompileTask {
+  /**
+   * Executes the task.
+   *
+   * @param context current compile context
+   * @return true if execution succeeded, false otherwise. If the task returns false, the compilation
+   *         is aborted, and it's expected that the task adds a message defining the reason for the failure
+   *         to the compile context.
+   */
+  boolean execute(CompileContext context);
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/Compiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/Compiler.java
new file mode 100644
index 0000000..77f77f4
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/Compiler.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.
+ */
+package com.intellij.openapi.compiler;
+
+import org.jetbrains.annotations.NotNull;
+import com.intellij.openapi.extensions.ExtensionPointName;
+
+/**
+ * Base interface for a custom compiler which participates in the IDEA build process.
+ *
+ * @see CompilerManager#addCompiler(Compiler)
+ * @see CompilerManager#removeCompiler(Compiler)
+ */
+public interface Compiler {
+  ExtensionPointName<Compiler> EP_NAME = ExtensionPointName.create("com.intellij.compiler");
+  
+  /**
+   * Returns the description of the compiler. All registered compilers should have unique description.
+   *
+   * @return the description string.
+   */
+  @NotNull
+  String getDescription();
+
+  /**
+   * Called before compilation starts. If at least one of registered compilers returned false, compilation won't start.
+   * 
+   * @param scope the scope on which the compilation is started.
+   * @return true if everything is ok, false otherwise
+   */
+  boolean validateConfiguration(CompileScope scope);
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerBundle.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerBundle.java
new file mode 100644
index 0000000..eac8c80
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerBundle.java
@@ -0,0 +1,56 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+import com.intellij.CommonBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.PropertyKey;
+
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.util.ResourceBundle;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Sep 9, 2005
+ */
+public class CompilerBundle {
+  private static Reference<ResourceBundle> ourBundle;
+
+  @NonNls private static final String BUNDLE = "messages.CompilerBundle";
+
+  private CompilerBundle() {
+  }
+
+  public static String jdkHomeNotFoundMessage(final Sdk jdk) {
+    return message("javac.error.jdk.home.missing", jdk.getName(), jdk.getHomePath());
+  }
+
+  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/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerFactory.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerFactory.java
new file mode 100644
index 0000000..8147e0b
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerFactory.java
@@ -0,0 +1,28 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author yole
+ */
+public interface CompilerFactory {
+  ExtensionPointName<CompilerFactory> EP_NAME = ExtensionPointName.create("com.intellij.compilerFactory");
+  
+  Compiler[] createCompilers(@NotNull CompilerManager compilerManager);
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerFilter.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerFilter.java
new file mode 100644
index 0000000..c7a9f7f
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerFilter.java
@@ -0,0 +1,33 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: Jul 9, 2009
+ */
+public interface CompilerFilter {
+  CompilerFilter ALL = new CompilerFilter() {
+    public boolean acceptCompiler(Compiler compiler) {
+      return true;
+    }
+  };
+  /**
+   * @param compiler
+   * @return true if this compiler can be executed
+   */
+  boolean acceptCompiler(Compiler compiler);
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerManager.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerManager.java
new file mode 100644
index 0000000..4456665
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerManager.java
@@ -0,0 +1,294 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+import com.intellij.notification.NotificationGroup;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Set;
+
+/**
+ * A "root" class in compiler subsystem - allows one to register a custom compiler or a compilation task, register/unregister a compilation listener
+ * and invoke various types of compilations (make, compile, rebuild)
+ */
+public abstract class CompilerManager {
+  public static final Key<Key> CONTENT_ID_KEY = Key.create("COMPILATION_CONTENT_ID_CUSTOM_KEY");
+  public static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.logOnlyGroup("Compiler");
+
+  /**
+   * Returns the compiler manager instance for the specified project.
+   *
+   * @param project the project for which the manager is requested.
+   * @return the manager instance.
+   */
+  public static CompilerManager getInstance(Project project) {
+    return ServiceManager.getService(project, CompilerManager.class);
+  }
+  
+  public abstract boolean isCompilationActive();
+  
+  /**
+   * Registers a custom compiler.
+   *
+   * @param compiler the compiler to register.
+   */
+  public abstract void addCompiler(@NotNull Compiler compiler);
+
+  /**
+   * Registers a custom translating compiler. Input and output filetype sets allow compiler manager
+   * to sort translating compilers so that output of one compiler will be used as input for another one
+   * 
+   * @param compiler compiler implementation 
+   * @param inputTypes a set of filetypes that compiler accepts as input
+   * @param outputTypes a set of filetypes that compiler can generate
+   */
+  public abstract void addTranslatingCompiler(@NotNull TranslatingCompiler compiler, Set<FileType> inputTypes, Set<FileType> outputTypes);
+
+  @NotNull
+  public abstract Set<FileType> getRegisteredInputTypes(@NotNull TranslatingCompiler compiler);
+  
+  @NotNull
+  public abstract Set<FileType> getRegisteredOutputTypes(@NotNull TranslatingCompiler compiler);
+  
+  /**
+   * Unregisters a custom compiler.
+   *
+   * @param compiler the compiler to unregister.
+   */
+  public abstract void removeCompiler(@NotNull Compiler compiler);
+
+  /**
+   * Returns all registered compilers of the specified class.
+   *
+   * @param compilerClass the class for which the compilers should be returned.
+   * @return all registered compilers of the specified class.
+   */
+  @NotNull
+  public abstract <T  extends Compiler> T[] getCompilers(@NotNull Class<T> compilerClass);
+
+  /**
+   * Returns all registered compilers of the specified class that the filter accepts
+   *
+   * @param compilerClass the class for which the compilers should be returned.
+   * @param filter additional filter to restrict compiler instances
+   * @return all registered compilers of the specified class.
+   */
+  @NotNull
+  public abstract <T  extends Compiler> T[] getCompilers(@NotNull Class<T> compilerClass, CompilerFilter filter);
+
+  /**
+   * Registers the type as a compilable type so that Compile action will be enabled on files of this type.
+   *
+   * @param type the type for which the Compile action is enabled.
+   */
+  public abstract void addCompilableFileType(@NotNull FileType type);
+
+  /**
+   * Unregisters the type as a compilable type so that Compile action will be disabled on files of this type.
+   *
+   * @param type the type for which the Compile action is disabled.
+   */
+  public abstract void removeCompilableFileType(@NotNull FileType type);
+
+  /**
+   * Checks if files of the specified type can be compiled by one of registered compilers.
+   * If the compiler can process files of certain type, it should register this file type within
+   * the CompilerManager as a compilable file type.
+   *
+   * @param type the type to check.
+   * @return true if the file type is compilable, false otherwise.
+   * @see com.intellij.openapi.compiler.CompilerManager#addCompilableFileType(FileType)
+   */
+  public abstract boolean isCompilableFileType(@NotNull FileType type);
+
+  /**
+   * Registers a compiler task that will be executed before the compilation.
+   *
+   * @param task the task to register.
+   */
+  public abstract void addBeforeTask(@NotNull CompileTask task);
+
+  /**
+   * Registers a compiler task  that will be executed after the compilation.
+   *
+   * @param task the task to register.
+   */
+  public abstract void addAfterTask(@NotNull CompileTask task);
+
+  /**
+   * Returns the list of all tasks to be executed before compilation.
+   *
+   * @return all tasks to be executed before compilation.
+   */
+  @NotNull
+  public abstract CompileTask[] getBeforeTasks();
+
+  /**
+   * Returns the list of all tasks to be executed after compilation.
+   *
+   * @return all tasks to be executed after compilation.
+   */
+  @NotNull
+  public abstract CompileTask[] getAfterTasks();
+
+  /**
+   * Compile a set of files.
+   *
+   * @param files             a list of files to compile. If a VirtualFile is a directory, all containing files are processed.
+   *                          Compiler excludes are not honored.
+   * @param callback          a notification callback, or null if no notifications needed.
+   */
+  public abstract void compile(@NotNull VirtualFile[] files, @Nullable CompileStatusNotification callback);
+
+  /**
+   * Compile all sources (including test sources) from the module. Compiler excludes are not honored.
+   *
+   * @param module            a module which sources are to be compiled
+   * @param callback          a notification callback, or null if no notifications needed
+   */
+  public abstract void compile(@NotNull Module module, @Nullable CompileStatusNotification callback);
+
+  /**
+   * Compile all files from the scope given.  Compiler excludes are not honored.
+   *
+   * @param scope             a scope to be compiled
+   * @param callback          a notification callback, or null if no notifications needed
+   */
+  public abstract void compile(@NotNull CompileScope scope, @Nullable CompileStatusNotification callback);
+
+  /**
+   * Compile all modified files and all files that depend on them all over the project.
+   * Files are compiled according to dependencies between the modules they belong to. Compiler excludes are honored.
+   *
+   * @param callback a notification callback, or null if no notifications needed
+   */
+  public abstract void make(@Nullable CompileStatusNotification callback);
+
+  /**
+   * Compile all modified files and all files that depend on them from the given module and all modules this module depends on recursively.
+   * Files are compiled according to dependencies between the modules they belong to. Compiler excludes are honored.
+   *
+   * @param module   a module which sources are to be compiled.
+   * @param callback a notification callback, or null if no notifications needed.
+   */
+  public abstract void make(@NotNull Module module, @Nullable CompileStatusNotification callback);
+
+  /**
+   * Compile all modified files and all files that depend on them from the modules and all modules these modules depend on recursively.
+   * Files are compiled according to dependencies between the modules they belong to. Compiler excludes are honored. All modules must belong to the same project.
+   *
+   * @param project  a project modules belong to
+   * @param modules  modules to compile
+   * @param callback a notification callback, or null if no notifications needed.
+   */
+  public abstract void make(@NotNull Project project, @NotNull Module[] modules, @Nullable CompileStatusNotification callback);
+
+  /**
+   * Compile all modified files and all files that depend on them from the scope given.
+   * Files are compiled according to dependencies between the modules they belong to. Compiler excludes are honored. All modules must belong to the same project
+   *
+   * @param scope    a scope to be compiled
+   * @param callback a notification callback, or null if no notifications needed
+   */
+  public abstract void make(@NotNull CompileScope scope, @Nullable CompileStatusNotification callback);
+
+  /**
+   * Compile all modified files and all files that depend on them from the scope given.
+   * Files are compiled according to dependencies between the modules they belong to. Compiler excludes are honored. All modules must belong to the same project
+   *
+   * @param scope    a scope to be compiled
+   * @param filter filter allowing choose what compilers should be executed
+   * @param callback a notification callback, or null if no notifications needed
+   */
+  public abstract void make(@NotNull CompileScope scope, CompilerFilter filter, @Nullable CompileStatusNotification callback);
+
+  /**
+   * Checks if compile scope given is up-to-date
+   * @param scope
+   * @return true if make on the scope specified wouldn't do anything or false if something is to be compiled or deleted 
+   */
+  public abstract boolean isUpToDate(@NotNull CompileScope scope);
+  /**
+   * Rebuild the whole project from scratch. Compiler excludes are honored.
+   *
+   * @param callback a notification callback, or null if no notifications needed
+   */
+  public abstract void rebuild(@Nullable CompileStatusNotification callback);
+
+  /**
+   * Execute a custom compile task.
+   *
+   * @param task           the task to execute.
+   * @param scope          compile scope for which the task is executed.
+   * @param contentName    the name of a tab in message view where the execution results will be displayed.
+   * @param onTaskFinished a runnable to be executed when the task finishes, null if nothing should be executed.
+   */
+  public abstract void executeTask(@NotNull CompileTask task, @NotNull CompileScope scope, String contentName,
+                                   @Nullable Runnable onTaskFinished);
+
+  /**
+   * Register a listener to track compilation events.
+   *
+   * @param listener the listener to be registered.
+   */
+  public abstract void addCompilationStatusListener(@NotNull CompilationStatusListener listener);
+  public abstract void addCompilationStatusListener(@NotNull CompilationStatusListener listener, @NotNull Disposable parentDisposable);
+
+  /**
+   * Unregister a compilation listener.
+   *
+   * @param listener the listener to be unregistered.
+   */
+  public abstract void removeCompilationStatusListener(@NotNull CompilationStatusListener listener);
+
+  /**
+   * Checks if the specified file is excluded from compilation.
+   *
+   * @param file the file to check.
+   * @return true if the file is excluded from compilation, false otherwise
+   */
+  public abstract boolean isExcludedFromCompilation(@NotNull VirtualFile file);
+
+  @NotNull
+  public abstract OutputToSourceMapping getJavaCompilerOutputMapping();
+
+  /*
+   * Convetience methods for creating frequently-used compile scopes
+   */
+  @NotNull
+  public abstract CompileScope createFilesCompileScope(@NotNull VirtualFile[] files);
+  @NotNull
+  public abstract CompileScope createModuleCompileScope(@NotNull Module module, final boolean includeDependentModules);
+  @NotNull
+  public abstract CompileScope createModulesCompileScope(@NotNull Module[] modules, final boolean includeDependentModules);
+  @NotNull
+  public abstract CompileScope createModuleGroupCompileScope(@NotNull Project project, @NotNull Module[] modules, final boolean includeDependentModules);
+  @NotNull
+  public abstract CompileScope createProjectCompileScope(@NotNull Project project);
+
+  public abstract void setValidationEnabled(ModuleType moduleType, boolean enabled);
+
+  public abstract boolean isValidationEnabled(Module moduleType);
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerMessage.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerMessage.java
new file mode 100644
index 0000000..3afd6a6
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerMessage.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.
+ */
+package com.intellij.openapi.compiler;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.pom.Navigatable;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Describes a single compiler message that is shown in compiler message view.
+ *
+ * @see CompileContext#addMessage(CompilerMessageCategory, String, String, int, int)
+ */
+public interface CompilerMessage {
+  /**
+   * An empty array of compiler messages which can be reused to avoid unnecessary allocations.
+   */
+  CompilerMessage[] EMPTY_ARRAY = new CompilerMessage[0];
+
+  /**
+   * Returns the category of the message.
+   *
+   * @return a category this message belongs to (error, warning, information).
+   */
+  CompilerMessageCategory getCategory();
+
+  /**
+   * Returs the message text.
+   *
+   * @return message text
+   */
+  String getMessage();
+
+  /**
+   * Returns the navigatable object allowing to navigate to the message source.
+   *
+   * @return the instance.
+   */
+  @Nullable
+  Navigatable getNavigatable();
+
+  /**
+   * Returns the file to which the message applies.
+   *
+   * @return the file to which the message applies.
+   */
+  VirtualFile getVirtualFile();
+
+  /**
+   * Returns the location prefix prepended to message while exporting compilation results to text.
+   *
+   * @return location prefix prepended to message while exporting compilation results to text.
+   */
+  String getExportTextPrefix();
+
+  /**
+   * Returns the location prefix prepended to message while exporting compilation results to text.
+   *
+   * @return location prefix prepended to message while rendering compilation results in UI.
+   */
+  String getRenderTextPrefix();
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerMessageCategory.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerMessageCategory.java
new file mode 100644
index 0000000..708184f
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerMessageCategory.java
@@ -0,0 +1,59 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+/**
+ * A set of constants describing possible message categories.
+ *
+ * @see CompilerMessage#getCategory()
+ * @see CompileContext#addMessage(CompilerMessageCategory, String, String, int, int) 
+ */
+public enum CompilerMessageCategory {
+  ERROR {
+    public String toString() {
+      return CompilerBundle.message("message.category.error");
+    }
+    public String getPresentableText() {
+      return toString();
+    }
+  },
+  WARNING {
+    public String toString() {
+      return CompilerBundle.message("message.category.warning");
+    }
+    public String getPresentableText() {
+      return toString();
+    }
+  },
+  INFORMATION {
+    public String toString() {
+      return CompilerBundle.message("message.category.information");
+    }
+    public String getPresentableText() {
+      return toString();
+    }
+  },
+  STATISTICS {
+    public String toString() {
+      return CompilerBundle.message("message.category.statistics");
+    }
+    public String getPresentableText() {
+      return toString();
+    }
+  };
+
+  public abstract String getPresentableText();
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerPaths.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerPaths.java
new file mode 100644
index 0000000..f02820f
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerPaths.java
@@ -0,0 +1,245 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.ide.highlighter.ProjectFileType;
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.CompilerModuleExtension;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.util.PathUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jps.model.java.compiler.AnnotationProcessingConfiguration;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Locale;
+
+/**
+ * A set of utility methods for working with paths
+ */
+public class CompilerPaths {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.compiler.CompilerPaths");
+  private static volatile String ourSystemPath;
+  private static final Comparator<String> URLS_COMPARATOR = new Comparator<String>() {
+    public int compare(String o1, String o2) {
+      return o1.compareTo(o2);
+    }
+  };
+  private static final String DEFAULT_GENERATED_DIR_NAME = "generated";
+
+  /**
+   * Returns a directory
+   * @param project
+   * @param compiler
+   * @return a directory where compiler may generate files. All generated files are not deleted when the application exits
+   */
+  public static File getGeneratedDataDirectory(Project project, Compiler compiler) {
+    //noinspection HardCodedStringLiteral
+    return new File(getGeneratedDataDirectory(project), compiler.getDescription().replaceAll("\\s+", "_"));
+  }
+
+  /**
+   * @param project
+   * @return a root directory where generated files for various compilers are stored
+   */
+  public static File getGeneratedDataDirectory(Project project) {
+    //noinspection HardCodedStringLiteral
+    return new File(getCompilerSystemDirectory(project), ".generated");
+  }
+
+  /**
+   * @param project
+   * @return a root directory where compiler caches for the given project are stored
+   */
+  public static File getCacheStoreDirectory(final Project project) {
+    //noinspection HardCodedStringLiteral
+    return new File(getCompilerSystemDirectory(project), ".caches");
+  }
+
+  public static File getCacheStoreDirectory(String compilerProjectDirName) {
+    //noinspection HardCodedStringLiteral
+    return new File(getCompilerSystemDirectory(compilerProjectDirName), ".caches");
+  }
+
+  public static File getRebuildMarkerFile(Project project) {
+    return new File(getCompilerSystemDirectory(project), "rebuild_required");
+  }
+
+  /**
+   * @param project
+   * @return a directory under IDEA "system" directory where all files related to compiler subsystem are stored (such as compiler caches or generated files)
+   */
+  public static File getCompilerSystemDirectory(Project project) {
+    return getCompilerSystemDirectory(getCompilerSystemDirectoryName(project));
+  }
+
+  public static File getCompilerSystemDirectory(String compilerProjectDirName) {
+    return new File(getCompilerSystemDirectory(), compilerProjectDirName);
+  }
+
+  public static String getCompilerSystemDirectoryName(Project project) {
+    return getPresentableName(project) + "." + project.getLocationHash();
+  }
+
+  @Nullable
+  private static String getPresentableName(final Project project) {
+    if (project.isDefault()) {
+      return project.getName();
+    }
+
+    String location = project.getPresentableUrl();
+    if (location == null) {
+      return null;
+    }
+
+    String projectName = FileUtil.toSystemIndependentName(location);
+    if (projectName.endsWith("/")) {
+      projectName = projectName.substring(0, projectName.length() - 1);
+    }
+
+    final int lastSlash = projectName.lastIndexOf('/');
+    if (lastSlash >= 0 && lastSlash + 1 < projectName.length()) {
+      projectName = projectName.substring(lastSlash + 1);
+    }
+
+    if (StringUtil.endsWithIgnoreCase(projectName, ProjectFileType.DOT_DEFAULT_EXTENSION)) {
+      projectName = projectName.substring(0, projectName.length() - ProjectFileType.DOT_DEFAULT_EXTENSION.length());
+    }
+    
+    projectName = projectName.toLowerCase(Locale.US).replace(':', '_'); // replace ':' from windows drive names
+    return projectName;
+  }
+
+  public static File getCompilerSystemDirectory() {
+    //noinspection HardCodedStringLiteral
+    final String systemPath = ourSystemPath != null? ourSystemPath : (ourSystemPath = PathUtil.getCanonicalPath(PathManager.getSystemPath()));
+    return new File(systemPath, "compiler");
+  }
+
+  /**
+   * @param module
+   * @param forTestClasses true if directory for test sources, false - for sources.
+   * @return a directory to which the sources (or test sources depending on the second partameter) should be compiled.
+   * Null is returned if output directory is not specified or is not valid
+   */
+  @Nullable
+  public static VirtualFile getModuleOutputDirectory(final Module module, boolean forTestClasses) {
+    final CompilerModuleExtension compilerModuleExtension = CompilerModuleExtension.getInstance(module);
+    VirtualFile outPath;
+    if (forTestClasses) {
+      final VirtualFile path = compilerModuleExtension.getCompilerOutputPathForTests();
+      if (path != null) {
+        outPath = path;
+      }
+      else {
+        outPath = compilerModuleExtension.getCompilerOutputPath();
+      }
+    }
+    else {
+      outPath = compilerModuleExtension.getCompilerOutputPath();
+    }
+    if (outPath == null) {
+      return null;
+    }
+    if (!outPath.isValid()) {
+      LOG.info("Requested output path for module " + module.getName() + " is not valid");
+      return null;
+    }
+    return outPath;
+  }
+
+  /**
+   * The same as {@link #getModuleOutputDirectory} but returns String.
+   * The method still returns a non-null value if the output path is specified in Settings but does not exist on disk.
+   */
+  @Nullable
+  public static String getModuleOutputPath(final Module module, boolean forTestClasses) {
+    final String outPathUrl;
+    final Application application = ApplicationManager.getApplication();
+    final CompilerModuleExtension extension = CompilerModuleExtension.getInstance(module);
+    if (forTestClasses) {
+      if (application.isDispatchThread()) {
+        final String url = extension.getCompilerOutputUrlForTests();
+        outPathUrl = url != null ? url : extension.getCompilerOutputUrl();
+      }
+      else {
+        outPathUrl = application.runReadAction(new Computable<String>() {
+          public String compute() {
+            final String url = extension.getCompilerOutputUrlForTests();
+            return url != null ? url : extension.getCompilerOutputUrl();
+          }
+        });
+      }
+    }
+    else { // for ordinary classes
+      if (application.isDispatchThread()) {
+        outPathUrl = extension.getCompilerOutputUrl();
+      }
+      else {
+        outPathUrl = application.runReadAction(new Computable<String>() {
+          public String compute() {
+            return extension.getCompilerOutputUrl();
+          }
+        });
+      }
+    }
+    return outPathUrl != null? VirtualFileManager.extractPath(outPathUrl) : null;
+  }
+
+  @Nullable
+  public static String getAnnotationProcessorsGenerationPath(Module module) {
+    final AnnotationProcessingConfiguration config = CompilerConfiguration.getInstance(module.getProject()).getAnnotationProcessingConfiguration(module);
+    final String sourceDirName = config.getGeneratedSourcesDirectoryName(false);
+    if (config.isOutputRelativeToContentRoot()) {
+      final String[] roots = ModuleRootManager.getInstance(module).getContentRootUrls();
+      if (roots.length == 0) {
+        return null;
+      }
+      if (roots.length > 1) {
+        Arrays.sort(roots, URLS_COMPARATOR);
+      }
+      return StringUtil.isEmpty(sourceDirName)? VirtualFileManager.extractPath(roots[0]): VirtualFileManager.extractPath(roots[0]) + "/" + sourceDirName;
+    }
+
+
+    final String path = getModuleOutputPath(module, false);
+    if (path == null) {
+      return null;
+    }
+    return StringUtil.isEmpty(sourceDirName)? path : path + "/" + sourceDirName;
+  }
+  
+  @NonNls
+  public static String getGenerationOutputPath(IntermediateOutputCompiler compiler, Module module, final boolean forTestSources) {
+    final String generatedCompilerDirectoryPath = getGeneratedDataDirectory(module.getProject(), compiler).getPath();
+    //noinspection HardCodedStringLiteral
+    final String moduleDir = module.getName().replaceAll("\\s+", "_") + "." + Integer.toHexString(module.getModuleFilePath().hashCode());
+    return generatedCompilerDirectoryPath.replace(File.separatorChar, '/') + "/" + moduleDir + "/" + (forTestSources? "test" : "production");
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerTopics.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerTopics.java
new file mode 100644
index 0000000..a3b8c46
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerTopics.java
@@ -0,0 +1,28 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+import com.intellij.util.messages.Topic;
+
+/**
+ * @author yole
+ */
+public class CompilerTopics {
+  public static final Topic<CompilationStatusListener> COMPILATION_STATUS = new Topic<CompilationStatusListener>("compilation status", CompilationStatusListener.class);
+
+  private CompilerTopics() {
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CopyingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CopyingCompiler.java
new file mode 100644
index 0000000..7d80c21
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CopyingCompiler.java
@@ -0,0 +1,151 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.io.IOUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Compiler which copies the compiled files to a different directory.
+ */
+public abstract class CopyingCompiler implements PackagingCompiler{
+  public abstract VirtualFile[] getFilesToCopy(CompileContext context);
+  public abstract String getDestinationPath(VirtualFile sourceFile);
+
+  public final void processOutdatedItem(CompileContext context, String url, @Nullable ValidityState state) {
+    if (state != null) {
+      final String destinationPath = ((DestinationFileInfo)state).getDestinationPath();
+      new File(destinationPath).delete();
+    }
+  }
+
+  @NotNull
+  public final ProcessingItem[] getProcessingItems(final CompileContext context) {
+    return ApplicationManager.getApplication().runReadAction(new Computable<ProcessingItem[]>() {
+      public ProcessingItem[] compute() {
+        final VirtualFile[] filesToCopy = getFilesToCopy(context);
+        final ProcessingItem[] items = new ProcessingItem[filesToCopy.length];
+        for (int idx = 0; idx < filesToCopy.length; idx++) {
+          final VirtualFile file = filesToCopy[idx];
+          items[idx] = new CopyItem(file, getDestinationPath(file));
+        }
+        return items;
+      }
+    });
+  }
+
+  public ProcessingItem[] process(CompileContext context, ProcessingItem[] items) {
+    final List<ProcessingItem> successfullyProcessed = new ArrayList<ProcessingItem>(items.length);
+    for (ProcessingItem item : items) {
+      final CopyItem copyItem = (CopyItem)item;
+      final String fromPath = copyItem.getSourcePath();
+      final String toPath = copyItem.getDestinationPath();
+      try {
+        FileUtil.copy(new File(fromPath), new File(toPath));
+        successfullyProcessed.add(copyItem);
+      }
+      catch (IOException e) {
+        context.addMessage(
+          CompilerMessageCategory.ERROR,
+          CompilerBundle.message("error.copying", fromPath, toPath, e.getMessage()),
+          null, -1, -1
+        );
+      }
+    }
+    return successfullyProcessed.toArray(new ProcessingItem[successfullyProcessed.size()]);
+  }
+
+  @NotNull
+  public String getDescription() {
+    return CompilerBundle.message("file.copying.compiler.description");
+  }
+
+  public boolean validateConfiguration(CompileScope scope) {
+    return true;
+  }
+
+  public ValidityState createValidityState(DataInput in) throws IOException {
+    return new DestinationFileInfo(IOUtil.readString(in), true);
+  }
+
+  private static class CopyItem implements FileProcessingCompiler.ProcessingItem {
+    private final VirtualFile myFile;
+    private final DestinationFileInfo myInfo;
+    private final String mySourcePath;
+
+    public CopyItem(VirtualFile file, String destinationPath) {
+      myFile = file;
+      mySourcePath = file.getPath().replace('/', File.separatorChar);
+      myInfo = new DestinationFileInfo(destinationPath, new File(destinationPath).exists());
+    }
+
+    @NotNull
+    public VirtualFile getFile() {
+      return myFile;
+    }
+
+    public ValidityState getValidityState() {
+      return myInfo;
+    }
+
+    public String getSourcePath() {
+      return mySourcePath;
+    }
+
+    public String getDestinationPath() {
+      return myInfo.getDestinationPath();
+    }
+  }
+
+  private static class DestinationFileInfo implements ValidityState {
+    private final String destinationPath;
+    private final boolean myFileExists;
+
+    public DestinationFileInfo(String destinationPath, boolean fileExists) {
+      this.destinationPath = destinationPath;
+      myFileExists = fileExists;
+    }
+
+    public boolean equalsTo(ValidityState otherState) {
+      if (!(otherState instanceof DestinationFileInfo)) {
+        return false;
+      }
+      DestinationFileInfo destinationFileInfo = (DestinationFileInfo)otherState;
+      return (myFileExists == destinationFileInfo.myFileExists) && (destinationPath.equals(destinationFileInfo.destinationPath));
+    }
+
+    public void save(DataOutput out) throws IOException {
+      IOUtil.writeString(destinationPath, out);
+    }
+
+    public String getDestinationPath() {
+      return destinationPath;
+    }
+  }
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/DummyCompileContext.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/DummyCompileContext.java
new file mode 100644
index 0000000..85b0156
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/DummyCompileContext.java
@@ -0,0 +1,121 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.CompilerModuleExtension;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.pom.Navigatable;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class DummyCompileContext implements CompileContext {
+  protected DummyCompileContext() {
+  }
+
+  private static final DummyCompileContext OUR_INSTANCE = new DummyCompileContext();
+
+  public static DummyCompileContext getInstance() {
+    return OUR_INSTANCE;
+  }
+
+  public Project getProject() {
+    return null;
+  }
+
+  public void addMessage(CompilerMessageCategory category, String message, String url, int lineNum, int columnNum) {
+  }
+
+
+  public void addMessage(CompilerMessageCategory category,
+                         String message,
+                         @Nullable String url,
+                         int lineNum,
+                         int columnNum,
+                         Navigatable navigatable) {
+  }
+
+  public CompilerMessage[] getMessages(CompilerMessageCategory category) {
+    return CompilerMessage.EMPTY_ARRAY;
+  }
+
+  public int getMessageCount(CompilerMessageCategory category) {
+    return 0;
+  }
+
+  public ProgressIndicator getProgressIndicator() {
+    return null;
+  }
+
+  public CompileScope getCompileScope() {
+    return null;
+  }
+
+  public CompileScope getProjectCompileScope() {
+    return null;
+  }
+
+  public void requestRebuildNextTime(String message) {
+  }
+
+  public Module getModuleByFile(VirtualFile file) {
+    return null;
+  }
+
+  public boolean isAnnotationProcessorsEnabled() {
+    return false;
+  }
+
+  public VirtualFile[] getSourceRoots(Module module) {
+    return VirtualFile.EMPTY_ARRAY;
+  }
+
+  public VirtualFile[] getAllOutputDirectories() {
+    return VirtualFile.EMPTY_ARRAY;
+  }
+
+  public VirtualFile getModuleOutputDirectory(final Module module) {
+    return ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile>() {
+      public VirtualFile compute() {
+        return CompilerModuleExtension.getInstance(module).getCompilerOutputPath();
+      }
+    });
+  }
+
+  public VirtualFile getModuleOutputDirectoryForTests(Module module) {
+    return null;
+  }
+
+  public <T> T getUserData(@NotNull Key<T> key) {
+    return null;
+  }
+
+  public <T> void putUserData(@NotNull Key<T> key, T value) {
+  }
+
+  public boolean isMake() {
+    return false; // stub implementation
+  }
+
+  public boolean isRebuild() {
+    return false;
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/EmptyValidityState.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/EmptyValidityState.java
new file mode 100644
index 0000000..fdcf405
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/EmptyValidityState.java
@@ -0,0 +1,42 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+import java.io.DataOutput;
+import java.io.IOException;
+
+/**
+ * Empty validity state for force recompilation
+ *
+ * @author Konstantin Bulenkov
+ */
+public final class EmptyValidityState implements ValidityState {
+  /**
+   * In most cases this method returns false to force recompile
+   *
+   * @param otherState the state to compare with.
+   * @return true if and only if otherState == this
+   */
+  public boolean equalsTo(ValidityState otherState) {
+      return otherState == this;
+  }
+
+  /**
+   * Do nothing here
+   */
+  public void save(DataOutput dataOutput) throws IOException {
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/ExportableUserDataHolder.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/ExportableUserDataHolder.java
new file mode 100644
index 0000000..1dda010
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/ExportableUserDataHolder.java
@@ -0,0 +1,33 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.UserDataHolder;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Map;
+
+/**
+ * @author Eugene Zhuravlev
+ *         Date: 2/21/12
+ */
+public interface ExportableUserDataHolder extends UserDataHolder{
+
+  @NotNull
+  Map<Key, Object> exportUserData();
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/ExportableUserDataHolderBase.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/ExportableUserDataHolderBase.java
new file mode 100644
index 0000000..4f4e20d
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/ExportableUserDataHolderBase.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 com.intellij.openapi.compiler;
+
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.UserDataHolderBase;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+public class ExportableUserDataHolderBase extends UserDataHolderBase implements ExportableUserDataHolder{
+  private final Set<Key> myKeys = Collections.synchronizedSet(new HashSet<Key>());
+
+  @NotNull
+  public final Map<Key, Object> exportUserData() {
+    final Map<Key, Object> result = new HashMap<Key, Object>();
+    synchronized (myKeys) {
+      for (Key<?> k : myKeys) {
+        final Object data = getUserData(k);
+        if (data != null) {
+          result.put(k, data);
+        }
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public final <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
+    if (value != null) {
+      myKeys.add(key);
+    }
+    else {
+      myKeys.remove(key);
+    }
+    super.putUserData(key, value);
+  }
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/FileProcessingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/FileProcessingCompiler.java
new file mode 100644
index 0000000..733514c
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/FileProcessingCompiler.java
@@ -0,0 +1,77 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A base interface describing shared functionality for various types of file processing compilers.
+ * Actual compiler implementation should implement one of these:
+ * {@link ClassInstrumentingCompiler}, {@link ClassPostProcessingCompiler},
+ * {@link SourceInstrumentingCompiler}.
+ */
+public interface FileProcessingCompiler extends Compiler, ValidityStateFactory {
+  /**
+   * Describes a processing unit for this compiler - a virtual file with associated state.
+   */
+  interface ProcessingItem {
+    /**
+     * A utility constant used to return empty arrays of ProcessingItem objects
+     */
+    ProcessingItem[] EMPTY_ARRAY = new ProcessingItem[0];
+
+    /**
+     * Returns the file to be processed.
+     *
+     * @return a file to be processed; cannot be null
+     */
+    @NotNull
+    VirtualFile getFile();
+
+    /**
+     * @return an object describing dependencies of the instrumented file (can be null).
+     *         For example, if the file "A" should be processed whenever file "B" or file "C" is changed, the ValidityState object can
+     *         be composed of a pair [timestamp("B"), timestamp("C")]. Thus, whenever a timestamp of any of these files is changed,
+     *         the current ValidityState won't be equal to the stored ValidityState and the item will be picked up by the make for recompilation.
+     */
+    @Nullable
+    ValidityState getValidityState();
+  }
+
+  /**
+   * Returns the items which will be processed in the current compile operation.
+   * The method is called before the call to {@link #process} method
+   *
+   * @param context the current compilation context.
+   * @return a non-null array of all items that potentially can be processed at the moment of method call. Even if
+   *         the file is not changed, it should be returned if it _can_ be processed by the compiler implementing the interface.
+   */
+  @NotNull
+  ProcessingItem[] getProcessingItems(CompileContext context);
+
+  /**
+   * Compiles the specified items.
+   *
+   * @param context the current compilation context.
+   * @param items items to be processed, selected by make subsystem. The items are selected from the list returned by the
+   *              {@link #getProcessingItems} method.
+   * @return successfully processed items.
+   */
+  ProcessingItem[] process(CompileContext context, ProcessingItem[] items);
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/GeneratingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/GeneratingCompiler.java
new file mode 100644
index 0000000..962bcb2
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/GeneratingCompiler.java
@@ -0,0 +1,79 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.vfs.VirtualFile;
+
+/**
+ * A base interface for all compilers that generate new files. The generated files may be processed by other compilers.
+ * Actual implementation should implement one of its subinterfaces. Currently only {@link SourceGeneratingCompiler} is available.
+ */
+public interface GeneratingCompiler extends Compiler, ValidityStateFactory, IntermediateOutputCompiler {
+  /**
+   * Represents a single item generated by the compiler.
+   */
+  interface GenerationItem {
+    /**
+     * Returns the path of the generated file.
+     *
+     * @return path of a generated file, relative to output directory.
+     */
+    String getPath();
+
+    /**
+     * Returns the object describing dependencies of the generated file.
+     *
+     * @return a serializable object describing dependencies of the generated file.
+     */
+    ValidityState getValidityState();
+
+    /**
+     * Returns the module to which the generated item belongs. This affects the sequence
+     * of compiling the generated files.
+     *
+     * @return the module to which the generated item belongs.
+     */
+    Module getModule();
+
+    /**
+     * @return true if the generated item is supposed to be located in test sources, false otherwise
+     */
+    boolean isTestSource();
+  }
+
+  /**
+   * Returns the list of all the files this compiler can generate.
+   *
+   * @param context the current compile context.
+   * @return items describing all the files this compiler can generate.
+   */
+  GenerationItem[] getGenerationItems(CompileContext context);
+
+  /**
+   * Invokes the generation process.
+   *
+   * @param context             the current compile context.
+   * @param items               what items to generate.
+   * @param outputRootDirectory the root directory under which the items are generated (the paths
+   *                            in {@link GenerationItem#getPath()} are relative to that directory).
+   *                            All files generated by the compiler must be placed in that directory or
+   *                            its subdirectories, otherwise they will not be compiled properly on
+   *                            subsequent build steps.
+   * @return successfully generated items
+   */
+  GenerationItem[] generate(CompileContext context, GenerationItem[] items, VirtualFile outputRootDirectory);
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/IntermediateOutputCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/IntermediateOutputCompiler.java
new file mode 100644
index 0000000..a289393
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/IntermediateOutputCompiler.java
@@ -0,0 +1,23 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+/**
+ * A tag interface denoting that compiler's output should go into 'intermediate' directory 
+ * and can be used as input to another compiler 
+ */
+public interface IntermediateOutputCompiler extends Compiler{
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/JavaSourceTransformingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/JavaSourceTransformingCompiler.java
new file mode 100644
index 0000000..27a3975
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/JavaSourceTransformingCompiler.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 com.intellij.openapi.compiler;
+
+import com.intellij.openapi.vfs.VirtualFile;
+
+/**
+ * This compiler is called right before the java sources compiler.
+ */
+public interface JavaSourceTransformingCompiler extends Compiler {
+
+  /**
+   * Checks if the file can be transformed by the compiler.
+   *
+   * @param file an original file that is about to be compiled with java compiler
+   * @return true if compiler would like to transform the file, false otherwise.
+   *         If true is returned, a copy of original file will be made and {@link #transform(CompileContext,com.intellij.openapi.vfs.VirtualFile,com.intellij.openapi.vfs.VirtualFile)}
+   *         method will be called on it. If transformation succeeded, the transformed copy will be passed to java compiler instead of original file.
+   */
+  boolean isTransformable(VirtualFile file);
+
+  /**
+   * Transforms the specified file.
+   *
+   * @param context      the current compile context.
+   * @param file         a copy of original file to be transformed. If there are more than one transformer registered, this copy may already contain transformations made by other transformers which were called before this one
+   * @param originalFile an original file. Since the copy that is supposed to be modified is located outside the project, it is not possible to use PSI for analysis.
+   *                     So the original file is provided. Note that it is passed for reference purposes only. It MUST NOT be transformed or changed in any way.
+   *                     For example, it is possible to obtain a PsiFile for the original file:<br><br>
+   *                     <code>PsiJavaFile originalPsiJavaFile = (PsiJavaFile)PsiManager.getInstance(project).findFile(originalFile)</code>;<br><br>
+   *                     The obtained originalPsiJavaFile can be analysed, searched etc. For transforming the file by the means of PSI, there should be created a copy of the originalPsiJavaFile:<br><br>
+   *                     <code>PsiJavaFile psiFileCopy = (PsiJavaFile)originalPsiJavaFile.copy();</code><br><br>
+   *                     The psiFileCopy can then be transformed, and its text saved to the first "file" argument:<br><br>
+   *                     <code>String text = psiFileCopy.getText();</code><br><br>
+   *                     <p/>
+   *                     <b>Note that transforming files by the means of PSI may considerably slow down the overall make performance.</b>
+   * @return true if transform succeeded, false otherwise.
+   */
+  boolean transform(CompileContext context, VirtualFile file, VirtualFile originalFile);
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/OutputToSourceMapping.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/OutputToSourceMapping.java
new file mode 100644
index 0000000..0e1afed
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/OutputToSourceMapping.java
@@ -0,0 +1,23 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+import org.jetbrains.annotations.Nullable;
+
+public interface OutputToSourceMapping {
+  @Nullable
+  String getSourcePath(String outputPath);
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/PackagingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/PackagingCompiler.java
new file mode 100644
index 0000000..e7e1350
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/PackagingCompiler.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 com.intellij.openapi.compiler;
+
+import org.jetbrains.annotations.Nullable;
+
+
+/**
+ * A tag interface indicating that the compiler will package the compiled Java classes.
+ * This affects the order of compiler calls.
+ * The sequence in which compilers are called:
+ * SourceGeneratingCompiler -> SourceInstrumentingCompiler -> TranslatingCompiler ->  ClassInstrumentingCompiler -> ClassPostProcessingCompiler -> PackagingCompiler -> Validator
+ */
+public interface PackagingCompiler extends FileProcessingCompiler{
+  /**
+   * Called when the compiler detects that an item in the output directory is outdated
+   * and will be recompiled. Note that this method will be called before, and independently from,
+   * subsequent calls to {@link #process}.
+   *
+   * @param context the current compile context.
+   * @param url     the URL of a file in the output directory which will be recompiled.
+   * @param state   the validity state of the file specified by <code>url</code>.
+   */
+  void processOutdatedItem(CompileContext context, String url, @Nullable ValidityState state);
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/SourceGeneratingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/SourceGeneratingCompiler.java
new file mode 100644
index 0000000..4c0b646
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/SourceGeneratingCompiler.java
@@ -0,0 +1,42 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.vfs.VirtualFile;
+
+/**
+ * A tag interface indicating that the compiler will generate Java sources.
+ * This affects the order of compiler calls.
+ * The sequence in which compilers are called:
+ * SourceGeneratingCompiler -> SourceInstrumentingCompiler -> TranslatingCompiler ->  ClassInstrumentingCompiler -> ClassPostProcessingCompiler -> PackagingCompiler -> Validator
+ */
+public interface SourceGeneratingCompiler extends GeneratingCompiler {
+  
+  /**
+   * Used by make subsystem to obtain the file that should be opened in the editor instead of generated file if there were errors found 
+   * while compiling the generated file
+   * 
+   *
+   * @param context current compile context
+   * @param module the module to which the generated file was attributed
+   * @param outputRoot the compiler output root 
+   * @param generatedFile - the file that was generated by this compiler
+   * @return substituting file that should be used for navigation in UI or null if no such substitutor is available 
+   *
+   */
+  VirtualFile getPresentableFile(CompileContext context, Module module, VirtualFile outputRoot, VirtualFile generatedFile);
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/SourceInstrumentingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/SourceInstrumentingCompiler.java
new file mode 100644
index 0000000..e8d07f1
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/SourceInstrumentingCompiler.java
@@ -0,0 +1,25 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+/**
+ * A tag interface indicating that the compiler will instrument java sources.
+ * This affects the order of compiler calls:
+ * The sequence in which compilers are called:
+ * SourceGeneratingCompiler -> SourceInstrumentingCompiler -> TranslatingCompiler ->  ClassInstrumentingCompiler -> ClassPostProcessingCompiler -> Validator
+ */
+public interface SourceInstrumentingCompiler extends FileProcessingCompiler {
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/SourceProcessingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/SourceProcessingCompiler.java
new file mode 100644
index 0000000..8392fdc
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/SourceProcessingCompiler.java
@@ -0,0 +1,19 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+public interface SourceProcessingCompiler extends FileProcessingCompiler, IntermediateOutputCompiler {
+}
\ No newline at end of file
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/TimestampValidityState.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/TimestampValidityState.java
new file mode 100644
index 0000000..d5841ba
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/TimestampValidityState.java
@@ -0,0 +1,65 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+/**
+ * A simple implementation of ValidityState that is enough for most cases.
+ * The file is considered modified if its timestamp is changed.
+ */
+public final class TimestampValidityState implements ValidityState {
+  private final long myTimestamp;
+
+  /**
+   * Creates a validity state with the specified timestamp.
+   *
+   * @param timestamp the timestamp for the validity state.
+   */
+  public TimestampValidityState(long timestamp) {
+    myTimestamp = timestamp;
+  }
+
+  public boolean equalsTo(ValidityState otherState) {
+    if (!(otherState instanceof TimestampValidityState)) {
+      return false;
+    }
+    return myTimestamp == ((TimestampValidityState)otherState).myTimestamp;
+  }
+
+  /**
+   * Saves the validity state to the specified stream.
+   *
+   * @param out
+   * @throws IOException if the stream write fails.
+   */
+  public void save(DataOutput out) throws IOException {
+    out.writeLong(myTimestamp);
+  }
+
+  /**
+   * Loads the validity state from the specified stream.
+   *
+   * @param is the stream to load the validity state from.
+   * @throws IOException if the stream read fails.
+   * @since 5.0.2
+   */
+  public static TimestampValidityState load(DataInput is) throws IOException {
+    return new TimestampValidityState(is.readLong());
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/TranslatingCompiler.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/TranslatingCompiler.java
new file mode 100644
index 0000000..3c56f8d
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/TranslatingCompiler.java
@@ -0,0 +1,81 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.Chunk;
+
+import java.util.Collection;
+
+/**
+ * A tag interface indicating that the compiler will translate one type of files into another (e.g. .java -> .class).
+ * This affects the order of compiler calls.
+ * The sequence in which compilers are called:
+ * SourceGeneratingCompiler -> SourceInstrumentingCompiler -> TranslatingCompiler ->  ClassInstrumentingCompiler -> ClassPostProcessingCompiler -> PackagingCompiler -> Validator
+ */
+public interface TranslatingCompiler extends Compiler {
+
+  /**
+   * Defines a single file compiled by the compiler.
+   */
+  interface OutputItem {
+    /**
+     * Returns the path to the output file.
+     *
+     * @return absolute path of the output file ('/' slashes used)
+     */
+    String getOutputPath();
+
+    /**
+     * Returns the path to the source file.
+     *
+     * @return the source file to be compiled
+     */
+    VirtualFile getSourceFile();
+  }
+
+  interface OutputSink {
+    /**
+     *
+     * @param outputRoot output directory
+     * @param items output items that were successfully compiled.
+     * @param filesToRecompile virtual files that should be considered as "modified" next time compilation is invoked.
+     */
+    void add(String outputRoot, Collection<OutputItem> items, VirtualFile[] filesToRecompile);
+  }
+
+
+  /**
+   * Checks if the compiler can compile the specified file.
+   *
+   * @param file    the file to check.
+   * @param context the context for the current compile operation.
+   * @return true if can compile the file, false otherwise. If the method returns false, <code>file</code>
+   *         will not be included in the list of files passed to {@link #compile(CompileContext,Chunk<Module>,com.intellij.openapi.vfs.VirtualFile[], com.intellij.openapi.compiler.TranslatingCompiler.OutputSink)}.
+   */
+  boolean isCompilableFile(VirtualFile file, CompileContext context);
+
+  /**
+   * Compiles the specified files.
+   *
+   * @param context the context for the current compile operation.
+   * @param moduleChunk contains modules that form a cycle. If project module graph has no cycles, a chunk corresponds to a single module
+   * @param files   the source files to compile that correspond to the module chunk
+   * @param sink storage that accepts compiler output results
+   */
+  void compile(CompileContext context, Chunk<Module> moduleChunk, VirtualFile[] files, OutputSink sink);
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/Validator.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/Validator.java
new file mode 100644
index 0000000..560ef8a
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/Validator.java
@@ -0,0 +1,26 @@
+/*
+ * 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 com.intellij.openapi.compiler;
+
+/**
+ * A tag interface indicating that the compiler will perform any validation on the files in given compile scope.
+ * Error/Warning messages should be added to the CompileContext object.
+ * This affects the order of compiler calls.
+ * The sequence in which compilers are called:
+ * SourceGeneratingCompiler -> SourceInstrumentingCompiler -> TranslatingCompiler ->  ClassInstrumentingCompiler -> ClassPostProcessingCompiler -> PackagingCompiler -> Validator
+ */
+public interface Validator extends FileProcessingCompiler {
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/ValidityState.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/ValidityState.java
new file mode 100644
index 0000000..65d12e6
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/ValidityState.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 com.intellij.openapi.compiler;
+
+import java.io.DataOutput;
+import java.io.IOException;
+
+/**
+ * Instances of this class are associated with the files processed by compilers.
+ * A file is considered modified if currently associated ValidityState differs from the previously stored ValiditySate for this file.
+ */
+public interface ValidityState {
+  /**
+   * Compares this validity state to other ValidityState.
+   *
+   * @param otherState the state to compare with.
+   * @return true if states can be considered equal, false otherwise.
+   */
+  boolean equalsTo(ValidityState otherState);
+  /**
+   * Invoked by make subsystem in order to store the state.
+   *
+   * @param out the output to which the state should be stored.
+   * @throws IOException if the save operation failed because of an I/O error.
+   * @see TimestampValidityState#load(java.io.DataInputStream)
+   */
+  void save(DataOutput out) throws IOException;
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/ValidityStateFactory.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/ValidityStateFactory.java
new file mode 100644
index 0000000..626f8db
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/ValidityStateFactory.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 com.intellij.openapi.compiler;
+
+import java.io.DataInput;
+import java.io.IOException;
+
+/**
+ * A factory for creating {@link ValidityState} objects.
+ */
+public interface ValidityStateFactory {
+  /**
+   * Used for deserialization of ValidityState objects from compiler internal caches.
+   * @see ValidityState
+   * @param in
+   */ 
+  ValidityState createValidityState(DataInput in) throws IOException;
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/ex/CompilerPathsEx.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/ex/CompilerPathsEx.java
new file mode 100644
index 0000000..0f40216
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/ex/CompilerPathsEx.java
@@ -0,0 +1,125 @@
+/*
+ * 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 com.intellij.openapi.compiler.ex;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.compiler.CompilerPaths;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.CompilerModuleExtension;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.util.containers.OrderedSet;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Set;
+
+public class CompilerPathsEx extends CompilerPaths {
+  public static final Key<Boolean> CLEAR_ALL_OUTPUTS_KEY = Key.create("_should_clear_all_outputs_");
+
+  public static File getZippedOutputPath(Project project, String outputDirectoryPath) {
+    final File outputDir = new File(outputDirectoryPath);
+    return new File(getZipStoreDirectory(project), "_" + outputDir.getName() + Integer.toHexString(outputDirectoryPath.hashCode()) + ".zip");
+  }
+
+  public static File getZipStoreDirectory(Project project) {
+    return new File(getCompilerSystemDirectory(project), ".zip");
+  }
+
+  public static class FileVisitor {
+    protected void accept(final VirtualFile file, final String fileRoot, final String filePath) {
+      if (file.isDirectory()) {
+        acceptDirectory(file, fileRoot, filePath);
+      }
+      else {
+        acceptFile(file, fileRoot, filePath);
+      }
+    }
+
+    protected void acceptFile(VirtualFile file, String fileRoot, String filePath) {
+    }
+
+    protected void acceptDirectory(final VirtualFile file, final String fileRoot, final String filePath) {
+      ProgressManager.checkCanceled();
+      final VirtualFile[] children = file.getChildren();
+      for (final VirtualFile child : children) {
+        final String name = child.getName();
+        final String _filePath;
+        final StringBuilder buf = StringBuilderSpinAllocator.alloc();
+        try {
+          buf.append(filePath).append("/").append(name);
+          _filePath = buf.toString();
+        }
+        finally {
+          StringBuilderSpinAllocator.dispose(buf);
+        }
+        accept(child, fileRoot, _filePath);
+      }
+    }
+  }
+
+  public static void visitFiles(final Collection<VirtualFile> directories, final FileVisitor visitor) {
+    for (final VirtualFile outputDir : directories) {
+      ApplicationManager.getApplication().runReadAction(new Runnable() {
+        public void run() {
+          final String path = outputDir.getPath();
+          visitor.accept(outputDir, path, path);
+        }
+      });
+    }
+  }
+
+  public static String[] getOutputPaths(Module[] modules) {
+    final Set<String> outputPaths = new OrderedSet<String>();
+    for (Module module : modules) {
+      final CompilerModuleExtension compilerModuleExtension = CompilerModuleExtension.getInstance(module);
+      if (compilerModuleExtension == null) {
+        continue;
+      }
+      String outputPathUrl = compilerModuleExtension.getCompilerOutputUrl();
+      if (outputPathUrl != null) {
+        outputPaths.add(VirtualFileManager.extractPath(outputPathUrl).replace('/', File.separatorChar));
+      }
+
+      String outputPathForTestsUrl = compilerModuleExtension.getCompilerOutputUrlForTests();
+      if (outputPathForTestsUrl != null) {
+        outputPaths.add(VirtualFileManager.extractPath(outputPathForTestsUrl).replace('/', File.separatorChar));
+      }
+    }
+    return ArrayUtil.toStringArray(outputPaths);
+  }
+
+  public static VirtualFile[] getOutputDirectories(final Module[] modules) {
+    final Set<VirtualFile> dirs = new OrderedSet<VirtualFile>();
+    for (Module module : modules) {
+      final VirtualFile outputDir = getModuleOutputDirectory(module, false);
+      if (outputDir != null) {
+        dirs.add(outputDir);
+      }
+      VirtualFile testsOutputDir = getModuleOutputDirectory(module, true);
+      if (testsOutputDir != null) {
+        dirs.add(testsOutputDir);
+      }
+    }
+    return VfsUtil.toVirtualFileArray(dirs);
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildInstruction.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildInstruction.java
new file mode 100644
index 0000000..148e152
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildInstruction.java
@@ -0,0 +1,22 @@
+/*
+ * 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 com.intellij.openapi.compiler.make;
+
+public interface BuildInstruction {
+  String getOutputRelativePath();
+
+  boolean accept(BuildInstructionVisitor visitor) throws Exception;
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildInstructionVisitor.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildInstructionVisitor.java
new file mode 100644
index 0000000..6ae9b33
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildInstructionVisitor.java
@@ -0,0 +1,26 @@
+/*
+ * 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 com.intellij.openapi.compiler.make;
+
+
+public abstract class BuildInstructionVisitor {
+  public boolean visitInstruction(BuildInstruction instruction) throws Exception {
+    return true;
+  }
+  public boolean visitFileCopyInstruction(FileCopyInstruction instruction) throws Exception {
+    return visitInstruction(instruction);
+  }
+}
\ No newline at end of file
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildParticipant.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildParticipant.java
new file mode 100644
index 0000000..48022b6
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildParticipant.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 com.intellij.openapi.compiler.make;
+
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.packaging.artifacts.Artifact;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @deprecated use interfaces from {@link com.intellij.openapi.compiler.Compiler}'s hierarchy instead
+ */
+public abstract class BuildParticipant {
+  public static final BuildParticipant[] EMPTY_ARRAY = new BuildParticipant[0];
+
+  @Nullable
+  public abstract Artifact createArtifact(CompileContext context);
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildParticipantProvider.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildParticipantProvider.java
new file mode 100644
index 0000000..a0f46da
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildParticipantProvider.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 com.intellij.openapi.compiler.make;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.module.Module;
+
+import java.util.Collection;
+
+/**
+ * @author nik
+ *
+ * @deprecated use interfaces from {@link com.intellij.openapi.compiler.Compiler}'s hierarchy instead
+ */
+public abstract class BuildParticipantProvider {
+  public static final ExtensionPointName<BuildParticipantProvider> EXTENSION_POINT_NAME = ExtensionPointName.create("com.intellij.compiler.buildParticipantProvider");
+
+
+  public abstract Collection<? extends BuildParticipant> getParticipants(Module module);
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildRecipe.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildRecipe.java
new file mode 100644
index 0000000..ad30291
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/BuildRecipe.java
@@ -0,0 +1,30 @@
+/*
+ * 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 com.intellij.openapi.compiler.make;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+
+@Deprecated
+public interface BuildRecipe {
+  void addInstruction(BuildInstruction instruction);
+
+  boolean visitInstructions(BuildInstructionVisitor visitor, boolean reverseOrder);
+  boolean visitInstructionsWithExceptions(BuildInstructionVisitor visitor, boolean reverseOrder) throws Exception;
+
+  void addFileCopyInstruction(@NotNull File file, boolean isDirectory, String outputRelativePath);
+}
\ No newline at end of file
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/make/FileCopyInstruction.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/FileCopyInstruction.java
new file mode 100644
index 0000000..4109410
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/FileCopyInstruction.java
@@ -0,0 +1,27 @@
+/*
+ * 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 com.intellij.openapi.compiler.make;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+
+public interface FileCopyInstruction extends BuildInstruction {
+  File getFile();
+
+  boolean isDirectory();
+
+}
\ No newline at end of file
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/make/ManifestBuilder.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/ManifestBuilder.java
new file mode 100644
index 0000000..3f645d0
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/ManifestBuilder.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 com.intellij.openapi.compiler.make;
+
+import com.intellij.openapi.application.ApplicationNamesInfo;
+import org.jetbrains.annotations.NonNls;
+
+import java.util.jar.Attributes;
+
+public class ManifestBuilder {
+  @NonNls private static final String NAME = "Created-By";
+  private static final Attributes.Name CREATED_BY = new Attributes.Name(NAME);
+
+  private ManifestBuilder() {
+  }
+
+  public static void setGlobalAttributes(Attributes mainAttributes) {
+    setVersionAttribute(mainAttributes);
+    setIfNone(mainAttributes, CREATED_BY, ApplicationNamesInfo.getInstance().getFullProductName());
+  }
+
+  public static void setVersionAttribute(Attributes mainAttributes) {
+    setIfNone(mainAttributes, Attributes.Name.MANIFEST_VERSION, "1.0");
+  }
+
+  private static void setIfNone(Attributes mainAttributes, Attributes.Name attrName, String value) {
+    if (mainAttributes.getValue(attrName) == null) {
+      mainAttributes.put(attrName, value);
+    }
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/make/package.html b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/package.html
new file mode 100644
index 0000000..839258a
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/make/package.html
@@ -0,0 +1,20 @@
+<!--
+  ~ Copyright 2000-2007 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.
+  -->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html><body bgcolor="white">
+Provides interfaces for extending the build process.
+</body></html>
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/options/ExcludeEntryDescription.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/options/ExcludeEntryDescription.java
new file mode 100644
index 0000000..b752e8e
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/options/ExcludeEntryDescription.java
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+/**
+ * created at Jan 3, 2002
+ * @author Jeka
+ */
+package com.intellij.openapi.compiler.options;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.io.FileUtil;
+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 org.jetbrains.annotations.Nullable;
+
+public class ExcludeEntryDescription implements Disposable {
+  private boolean myIsFile;
+  private boolean myIncludeSubdirectories;
+  private VirtualFilePointer myFilePointer;
+  private final Disposable myParentDisposable;
+
+  public ExcludeEntryDescription(String url, boolean includeSubdirectories, boolean isFile, Disposable parent) {
+    myParentDisposable = parent;
+    myFilePointer = VirtualFilePointerManager.getInstance().create(url, parent, null);
+    myIncludeSubdirectories = includeSubdirectories;
+    myIsFile = isFile;
+  }
+
+  public ExcludeEntryDescription(VirtualFile virtualFile, boolean includeSubdirectories, boolean isFile, Disposable parent) {
+    this(virtualFile.getUrl(), includeSubdirectories, isFile, parent);
+  }
+
+  public ExcludeEntryDescription copy(Disposable parent) {
+    return new ExcludeEntryDescription(getUrl(), myIncludeSubdirectories, myIsFile,parent);
+  }
+
+  public void setPresentableUrl(String newUrl) {
+    myFilePointer = VirtualFilePointerManager.getInstance().create(VfsUtil.pathToUrl(FileUtil.toSystemIndependentName(newUrl)), myParentDisposable, null);
+    final VirtualFile file = getVirtualFile();
+    if (file != null) {
+      myIsFile = !file.isDirectory();
+    }
+  }
+
+  public boolean isFile() {
+    return myIsFile;
+  }
+
+  public String getUrl() {
+    return myFilePointer.getUrl();
+  }
+
+  public String getPresentableUrl() {
+    return myFilePointer.getPresentableUrl();
+  }
+
+  public boolean isIncludeSubdirectories() {
+    return myIncludeSubdirectories;
+  }
+
+  public void setIncludeSubdirectories(boolean includeSubdirectories) {
+    myIncludeSubdirectories = includeSubdirectories;
+  }
+
+  @Nullable
+  public VirtualFile getVirtualFile() {
+    return myFilePointer.getFile();
+  }
+
+  public boolean isValid() {
+    return myFilePointer.isValid();
+  }
+
+  public boolean equals(Object obj) {
+    if(!(obj instanceof ExcludeEntryDescription)) {
+      return false;
+    }
+    ExcludeEntryDescription entryDescription = (ExcludeEntryDescription)obj;
+    if(entryDescription.myIsFile != myIsFile) {
+      return false;
+    }
+    if(entryDescription.myIncludeSubdirectories != myIncludeSubdirectories) {
+      return false;
+    }
+    return Comparing.equal(entryDescription.getUrl(), getUrl());
+  }
+
+  public int hashCode() {
+    int result = (myIsFile ? 1 : 0);
+    result = 31 * result + (myIncludeSubdirectories ? 1 : 0);
+    result = 31 * result + getUrl().hashCode();
+    return result;
+  }
+
+  public void dispose() {
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/options/ExcludedEntriesConfigurable.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/options/ExcludedEntriesConfigurable.java
new file mode 100644
index 0000000..6a2cd87
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/options/ExcludedEntriesConfigurable.java
@@ -0,0 +1,385 @@
+/*
+ * 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 com.intellij.openapi.compiler.options;
+
+import com.intellij.compiler.CompilerConfiguration;
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.compiler.CompilerBundle;
+import com.intellij.openapi.fileChooser.FileChooser;
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.options.UnnamedConfigurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.vcs.FileStatusManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.*;
+import com.intellij.ui.table.JBTable;
+
+import javax.swing.*;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class ExcludedEntriesConfigurable implements UnnamedConfigurable {
+  private final Project myProject;
+  private final ArrayList<ExcludeEntryDescription> myExcludeEntryDescriptions = new ArrayList<ExcludeEntryDescription>();
+  private final FileChooserDescriptor myDescriptor;
+  private final ExcludedEntriesConfiguration myConfiguration;
+  private ExcludedEntriesPanel myExcludedEntriesPanel;
+
+  public ExcludedEntriesConfigurable(Project project) {
+    this(project, new FileChooserDescriptor(true, true, false, false, false, true),
+         CompilerConfiguration.getInstance(project).getExcludedEntriesConfiguration());
+  }
+
+  public ExcludedEntriesConfigurable(Project project, FileChooserDescriptor descriptor, final ExcludedEntriesConfiguration configuration) {
+    myDescriptor = descriptor;
+    myConfiguration = configuration;
+    myProject = project;
+  }
+
+  public void reset() {
+    ExcludeEntryDescription[] descriptions = myConfiguration.getExcludeEntryDescriptions();
+    disposeMyDescriptions();
+    for (ExcludeEntryDescription description : descriptions) {
+      myExcludeEntryDescriptions.add(description.copy(myProject));
+    }
+    ((AbstractTableModel)myExcludedEntriesPanel.myExcludedTable.getModel()).fireTableDataChanged();
+  }
+
+  public void addEntry(ExcludeEntryDescription description) {
+    myExcludeEntryDescriptions.add(description);
+    ((AbstractTableModel)myExcludedEntriesPanel.myExcludedTable.getModel()).fireTableDataChanged();
+  }
+
+  private void disposeMyDescriptions() {
+    for (ExcludeEntryDescription description : myExcludeEntryDescriptions) {
+      Disposer.dispose(description);
+    }
+    myExcludeEntryDescriptions.clear();
+  }
+
+  public void apply() {
+    myConfiguration.removeAllExcludeEntryDescriptions();
+    for (ExcludeEntryDescription description : myExcludeEntryDescriptions) {
+      myConfiguration.addExcludeEntryDescription(description.copy(myProject));
+    }
+    FileStatusManager.getInstance(myProject).fileStatusesChanged(); // refresh exclude from compile status
+  }
+
+  public boolean isModified() {
+    ExcludeEntryDescription[] excludeEntryDescriptions = myConfiguration.getExcludeEntryDescriptions();
+    if(excludeEntryDescriptions.length != myExcludeEntryDescriptions.size()) {
+      return true;
+    }
+    for(int i = 0; i < excludeEntryDescriptions.length; i++) {
+      ExcludeEntryDescription description = excludeEntryDescriptions[i];
+      if(!Comparing.equal(description, myExcludeEntryDescriptions.get(i))) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public JComponent createComponent() {
+    if (myExcludedEntriesPanel == null) {
+      myExcludedEntriesPanel = new ExcludedEntriesPanel();
+    }
+    return myExcludedEntriesPanel;
+  }
+
+  public void disposeUIResources() {
+    myExcludedEntriesPanel = null;
+  }
+
+  private class ExcludedEntriesPanel extends PanelWithButtons {
+    private JButton myRemoveButton;
+    private JBTable myExcludedTable;
+
+    public ExcludedEntriesPanel() {
+      initPanel();
+    }
+
+    protected String getLabelText(){
+      return null;
+    }
+
+    protected JButton[] createButtons(){
+      final JButton addButton = new JButton(IdeBundle.message("button.add"));
+      addButton.addActionListener(
+        new ActionListener() {
+          public void actionPerformed(ActionEvent e){
+            addPath(myDescriptor);
+          }
+        }
+      );
+
+      myRemoveButton = new JButton(IdeBundle.message("button.remove"));
+      myRemoveButton.addActionListener(
+        new ActionListener(){
+          public void actionPerformed(ActionEvent e){
+            removePaths();
+          }
+        }
+      );
+      myRemoveButton.setEnabled(false);
+      myRemoveButton.getModel().addChangeListener(new ChangeListener() {
+        public void stateChanged(ChangeEvent e) {
+          if (myExcludedTable.getSelectedRow() == -1) {
+            myRemoveButton.setEnabled(false);
+          }
+        }
+      });
+
+      return new JButton[]{/*addButton, myRemoveButton*/};
+    }
+
+    private void addPath(FileChooserDescriptor descriptor) {
+      int selected = -1 /*myExcludedTable.getSelectedRow() + 1*/;
+      if(selected < 0) {
+        selected = myExcludeEntryDescriptions.size();
+      }
+      int savedSelected = selected;
+      VirtualFile[] chosen = FileChooser.chooseFiles(descriptor, myProject, null);
+      for (final VirtualFile chosenFile : chosen) {
+        if (isFileExcluded(chosenFile)) {
+          continue;
+        }
+        ExcludeEntryDescription description;
+        if (chosenFile.isDirectory()) {
+          description = new ExcludeEntryDescription(chosenFile, true, false, myProject);
+        }
+        else {
+          description = new ExcludeEntryDescription(chosenFile, false, true, myProject);
+        }
+        myExcludeEntryDescriptions.add(selected, description);
+        selected++;
+      }
+      if (selected > savedSelected) { // actually added something
+        AbstractTableModel model = (AbstractTableModel)myExcludedTable.getModel();
+        model.fireTableRowsInserted(savedSelected, selected-1);
+        myExcludedTable.setRowSelectionInterval(savedSelected, selected - 1);
+      }
+    }
+
+    private boolean isFileExcluded(VirtualFile file) {
+      for (final ExcludeEntryDescription description : myExcludeEntryDescriptions) {
+        final VirtualFile descriptionFile = description.getVirtualFile();
+        if (descriptionFile == null) {
+          continue;
+        }
+        if (file.equals(descriptionFile)) {
+          return true;
+        }
+      }
+      return false;
+    }
+
+    private void removePaths() {
+      int[] selected = myExcludedTable.getSelectedRows();
+      if(selected == null || selected.length <= 0) {
+        return;
+      }
+      if(myExcludedTable.isEditing()) {
+        TableCellEditor editor = myExcludedTable.getCellEditor();
+        if (editor != null) {
+          editor.stopCellEditing();
+        }
+      }
+      AbstractTableModel model = (AbstractTableModel)myExcludedTable.getModel();
+      Arrays.sort(selected);
+      int indexToSelect = selected[selected.length - 1];
+      int removedCount = 0;
+      for (int indexToRemove : selected) {
+        final int row = indexToRemove - removedCount;
+        ExcludeEntryDescription description = myExcludeEntryDescriptions.get(row);
+        Disposer.dispose(description);
+        myExcludeEntryDescriptions.remove(row);
+        model.fireTableRowsDeleted(row, row);
+        removedCount += 1;
+      }
+      if(indexToSelect >= myExcludeEntryDescriptions.size()) {
+        indexToSelect = myExcludeEntryDescriptions.size() - 1;
+      }
+      if(indexToSelect >= 0) {
+        myExcludedTable.setRowSelectionInterval(indexToSelect, indexToSelect);
+      }
+      myExcludedTable.requestFocus();
+    }
+
+    protected JComponent createMainComponent(){
+      final String[] names = {
+        CompilerBundle.message("exclude.from.compile.table.path.column.name"),
+        CompilerBundle.message("exclude.from.compile.table.recursively.column.name")
+      };
+      // Create a model of the data.
+      TableModel dataModel = new AbstractTableModel() {
+        public int getColumnCount() {
+          return names.length;
+        }
+
+        public int getRowCount() {
+          return myExcludeEntryDescriptions.size();
+        }
+
+        public Object getValueAt(int row, int col) {
+          ExcludeEntryDescription description = myExcludeEntryDescriptions.get(row);
+          if(col == 0) {
+            return description.getPresentableUrl();
+          }
+          if(col == 1) {
+            if(!description.isFile()) {
+              return description.isIncludeSubdirectories() ? Boolean.TRUE : Boolean.FALSE;
+            }
+            else {
+              return null;
+            }
+          }
+          return null;
+        }
+
+        public String getColumnName(int column) {
+          return names[column];
+        }
+
+        public Class getColumnClass(int c) {
+          if(c == 0) {
+            return String.class;
+          }
+          if(c == 1) {
+            return Boolean.class;
+          }
+          return null;
+        }
+
+        public boolean isCellEditable(int row, int col) {
+          if(col == 1) {
+            ExcludeEntryDescription description = myExcludeEntryDescriptions.get(row);
+            return !description.isFile();
+          }
+          return true;
+        }
+
+        public void setValueAt(Object aValue, int row, int col) {
+          ExcludeEntryDescription description = myExcludeEntryDescriptions.get(row);
+          if (col == 1) {
+            description.setIncludeSubdirectories(aValue.equals(Boolean.TRUE));
+          } else {
+            final String path = (String)aValue;
+            description.setPresentableUrl(path);
+          }
+        }
+      };
+
+      myExcludedTable = new JBTable(dataModel);
+      myExcludedTable.setEnableAntialiasing(true);
+
+      myExcludedTable.getEmptyText().setText(CompilerBundle.message("no.excludes"));
+      myExcludedTable.setPreferredScrollableViewportSize(new Dimension(300, myExcludedTable.getRowHeight() * 6));
+      myExcludedTable.setDefaultRenderer(Boolean.class, new BooleanRenderer());
+      myExcludedTable.setDefaultRenderer(Object.class, new MyObjectRenderer());
+      myExcludedTable.getColumn(names[0]).setPreferredWidth(350);
+      final int cbWidth = 15 + myExcludedTable.getTableHeader().getFontMetrics(myExcludedTable.getTableHeader().getFont()).stringWidth(names[1]);
+      final TableColumn cbColumn = myExcludedTable.getColumn(names[1]);
+      cbColumn.setPreferredWidth(cbWidth);
+      cbColumn.setMaxWidth(cbWidth);
+      myExcludedTable.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+      myExcludedTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
+        public void valueChanged(ListSelectionEvent e) {
+          myRemoveButton.setEnabled(myExcludedTable.getSelectedRow() >= 0);
+        }
+      });
+      TableCellEditor editor = myExcludedTable.getDefaultEditor(String.class);
+      if(editor instanceof DefaultCellEditor) {
+        ((DefaultCellEditor)editor).setClickCountToStart(1);
+      }
+
+      return ToolbarDecorator.createDecorator(myExcludedTable)
+        .disableUpAction()
+        .disableDownAction()
+        .setAddAction(new AnActionButtonRunnable() {
+          @Override
+          public void run(AnActionButton anActionButton) {
+            addPath(myDescriptor);
+          }
+        })
+        .setRemoveAction(new AnActionButtonRunnable() {
+          @Override
+          public void run(AnActionButton anActionButton) {
+            removePaths();
+          }
+        }).createPanel();
+
+
+      //return ScrollPaneFactory.createScrollPane(myExcludedTable);
+    }
+  }
+
+  private static class BooleanRenderer extends JCheckBox implements TableCellRenderer {
+    private final JPanel myPanel = new JPanel();
+
+    public BooleanRenderer() {
+      setHorizontalAlignment(CENTER);
+    }
+
+    public Component getTableCellRendererComponent(JTable table, Object value,
+                                                   boolean isSelected, boolean hasFocus,
+                                                   int row, int column) {
+      if(value == null) {
+        if(isSelected) {
+          myPanel.setBackground(table.getSelectionBackground());
+        }
+        else {
+          myPanel.setBackground(table.getBackground());
+        }
+        return myPanel;
+      }
+      if(isSelected) {
+        setForeground(table.getSelectionForeground());
+        super.setBackground(table.getSelectionBackground());
+      }
+      else {
+        setForeground(table.getForeground());
+        setBackground(table.getBackground());
+      }
+      setSelected(((Boolean)value).booleanValue());
+      return this;
+    }
+  }
+
+  private class MyObjectRenderer extends DefaultTableCellRenderer {
+    public MyObjectRenderer() {
+      setUI(new RightAlignedLabelUI());
+    }
+
+    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+      final Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+      final ExcludeEntryDescription description = myExcludeEntryDescriptions.get(row);
+      component.setForeground(!description.isValid() ? JBColor.RED : isSelected ? table.getSelectionForeground() : table.getForeground());
+      component.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
+      return component;
+    }
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/options/ExcludedEntriesConfiguration.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/options/ExcludedEntriesConfiguration.java
new file mode 100644
index 0000000..67e8745
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/options/ExcludedEntriesConfiguration.java
@@ -0,0 +1,145 @@
+/*
+ * 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 com.intellij.openapi.compiler.options;
+
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.JDOMExternalizable;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+
+/**
+ * @author nik
+ */
+public class ExcludedEntriesConfiguration implements PersistentStateComponent<ExcludedEntriesConfiguration>, JDOMExternalizable, Disposable {
+  @NonNls private static final String FILE = "file";
+  @NonNls private static final String DIRECTORY = "directory";
+  @NonNls private static final String URL = "url";
+  @NonNls private static final String INCLUDE_SUBDIRECTORIES = "includeSubdirectories";
+  private final Collection<ExcludeEntryDescription> myExcludeEntryDescriptions = new LinkedHashSet<ExcludeEntryDescription>();
+  private ExcludeEntryDescription[] myCachedDescriptions = null;
+
+  public synchronized ExcludeEntryDescription[] getExcludeEntryDescriptions() {
+    if (myCachedDescriptions == null) {
+      myCachedDescriptions = myExcludeEntryDescriptions.toArray(new ExcludeEntryDescription[myExcludeEntryDescriptions.size()]);
+    }
+    return myCachedDescriptions;
+  }
+
+  public synchronized void addExcludeEntryDescription(ExcludeEntryDescription description) {
+    myExcludeEntryDescriptions.add(description);
+    myCachedDescriptions = null;
+  }
+
+  public synchronized void removeExcludeEntryDescription(ExcludeEntryDescription description) {
+    myExcludeEntryDescriptions.remove(description);
+    myCachedDescriptions = null;
+  }
+
+  public synchronized void removeAllExcludeEntryDescriptions() {
+    myExcludeEntryDescriptions.clear();
+    myCachedDescriptions = null;
+  }
+
+  public synchronized boolean containsExcludeEntryDescription(ExcludeEntryDescription description) {
+    return myExcludeEntryDescriptions.contains(description);
+  }
+
+  public void readExternal(final Element node) {
+    for (final Object o : node.getChildren()) {
+      Element element = (Element)o;
+      String url = element.getAttributeValue(URL);
+      if (url == null) continue;
+      if (FILE.equals(element.getName())) {
+        ExcludeEntryDescription excludeEntryDescription = new ExcludeEntryDescription(url, false, true, this);
+        addExcludeEntryDescription(excludeEntryDescription);
+      }
+      if (DIRECTORY.equals(element.getName())) {
+        boolean includeSubdirectories = Boolean.parseBoolean(element.getAttributeValue(INCLUDE_SUBDIRECTORIES));
+        ExcludeEntryDescription excludeEntryDescription = new ExcludeEntryDescription(url, includeSubdirectories, false,this);
+        addExcludeEntryDescription(excludeEntryDescription);
+      }
+    }
+  }
+
+  public void writeExternal(final Element element) {
+    for (final ExcludeEntryDescription description : getExcludeEntryDescriptions()) {
+      if (description.isFile()) {
+        Element entry = new Element(FILE);
+        entry.setAttribute(URL, description.getUrl());
+        element.addContent(entry);
+      }
+      else {
+        Element entry = new Element(DIRECTORY);
+        entry.setAttribute(URL, description.getUrl());
+        entry.setAttribute(INCLUDE_SUBDIRECTORIES, Boolean.toString(description.isIncludeSubdirectories()));
+        element.addContent(entry);
+      }
+    }
+  }
+
+  public boolean isExcluded(VirtualFile virtualFile) {
+    for (final ExcludeEntryDescription entryDescription : getExcludeEntryDescriptions()) {
+      VirtualFile descriptionFile = entryDescription.getVirtualFile();
+      if (descriptionFile == null) {
+        continue;
+      }
+      if (entryDescription.isFile()) {
+        if (descriptionFile.equals(virtualFile)) {
+          return true;
+        }
+      }
+      else if (entryDescription.isIncludeSubdirectories()) {
+        if (VfsUtil.isAncestor(descriptionFile, virtualFile, false)) {
+          return true;
+        }
+      }
+      else {
+        if (virtualFile.isDirectory()) {
+          continue;
+        }
+        if (descriptionFile.equals(virtualFile.getParent())) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  public void dispose() {
+    for (ExcludeEntryDescription description : myExcludeEntryDescriptions) {
+      Disposer.dispose(description);
+    }
+  }
+
+  public ExcludedEntriesConfiguration getState() {
+    return this;
+  }
+
+  public void loadState(final ExcludedEntriesConfiguration state) {
+    for (ExcludeEntryDescription description : state.getExcludeEntryDescriptions()) {
+      addExcludeEntryDescription(description.copy(this));
+    }
+    Disposer.dispose(state);
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/package.html b/java/compiler/openapi/src/com/intellij/openapi/compiler/package.html
new file mode 100644
index 0000000..b938f8e
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/package.html
@@ -0,0 +1,21 @@
+<!--
+  ~ Copyright 2000-2007 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.
+  -->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html><body bgcolor="white">
+Provides interfaces for invoking the compiler, listening to compilation events and implementing
+custom compilers or compilation steps.
+</body></html>
diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/util/InspectionValidator.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/util/InspectionValidator.java
new file mode 100644
index 0000000..64088c8
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/util/InspectionValidator.java
@@ -0,0 +1,95 @@
+/*
+ * 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 com.intellij.openapi.compiler.util;
+
+import com.intellij.codeHighlighting.HighlightDisplayLevel;
+import com.intellij.codeInspection.InspectionToolProvider;
+import com.intellij.codeInspection.LocalInspectionTool;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompileScope;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * @author peter
+ */
+public abstract class InspectionValidator {
+  public static final ExtensionPointName<InspectionValidator> EP_NAME = ExtensionPointName.create("com.intellij.compiler.inspectionValidator");
+  private final String myDescription;
+  private final String myProgressIndicatorText;
+  private final Class<? extends LocalInspectionTool>[] myInspectionToolClasses;
+
+  protected InspectionValidator(@NotNull final String description, @NotNull final String progressIndicatorText, final Class<? extends LocalInspectionTool>... inspectionToolClasses) {
+    myDescription = description;
+    myProgressIndicatorText = progressIndicatorText;
+    myInspectionToolClasses = inspectionToolClasses;
+  }
+
+  protected InspectionValidator(@NotNull final String description, @NotNull final String progressIndicatorText, final InspectionToolProvider provider) {
+    //noinspection unchecked
+    this(description, progressIndicatorText, provider.getInspectionClasses());
+  }
+
+  protected InspectionValidator(@NotNull final String description, @NotNull final String progressIndicatorText, final Class<? extends InspectionToolProvider> providerClass)
+    throws IllegalAccessException, InstantiationException {
+    this(description, progressIndicatorText, providerClass.newInstance());
+  }
+
+
+  public abstract boolean isAvailableOnScope(@NotNull CompileScope scope);
+
+  public abstract Collection<VirtualFile> getFilesToProcess(final Project project, final CompileContext context);
+
+  @NotNull
+  public Collection<? extends PsiElement> getDependencies(final PsiFile psiFile) {
+    return Collections.emptyList();
+  }
+
+  @NotNull
+  public Class<? extends LocalInspectionTool>[] getInspectionToolClasses(final CompileContext context) {
+    return myInspectionToolClasses;
+  }
+
+  public final String getDescription() {
+    return myDescription;
+  }
+
+  public final String getProgressIndicatorText() {
+    return myProgressIndicatorText;
+  }
+
+  public CompilerMessageCategory getCategoryByHighlightDisplayLevel(@NotNull final HighlightDisplayLevel severity, @NotNull final VirtualFile virtualFile, @NotNull final CompileContext context) {
+    if (severity == HighlightDisplayLevel.ERROR) return CompilerMessageCategory.ERROR;
+    if (severity == HighlightDisplayLevel.WARNING) return CompilerMessageCategory.WARNING;
+    return CompilerMessageCategory.INFORMATION;
+  }
+
+  @NotNull
+  public Map<ProblemDescriptor, HighlightDisplayLevel> checkAdditionally(PsiFile file) {
+    return Collections.emptyMap();
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/openapi/deployment/DeploymentUtil.java b/java/compiler/openapi/src/com/intellij/openapi/deployment/DeploymentUtil.java
new file mode 100644
index 0000000..dc1ff57
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/openapi/deployment/DeploymentUtil.java
@@ -0,0 +1,131 @@
+/*
+ * 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 com.intellij.openapi.deployment;
+
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.make.BuildRecipe;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.util.descriptors.ConfigFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.util.Set;
+
+public abstract class DeploymentUtil {
+  public static DeploymentUtil getInstance() {
+    return ServiceManager.getService(DeploymentUtil.class);
+  }
+
+  public abstract void copyFile(@NotNull File fromFile,
+                                @NotNull File toFile,
+                                @NotNull CompileContext context,
+                                @Nullable Set<String> writtenPaths,
+                                @Nullable FileFilter fileFilter) throws IOException;
+
+  @Deprecated
+  public abstract boolean addItemsRecursively(@NotNull BuildRecipe items, @NotNull File root, String outputRelativePath);
+
+  public static String trimForwardSlashes(@NotNull String path) {
+    while (path.length() != 0 && (path.charAt(0) == '/' || path.charAt(0) == File.separatorChar)) {
+      path = path.substring(1);
+    }
+    return path;
+  }
+
+  public abstract void reportDeploymentDescriptorDoesNotExists(ConfigFile descriptor, CompileContext context, Module module);
+
+  public static String concatPaths(String... paths) {
+    final StringBuilder builder = new StringBuilder();
+    for (String path : paths) {
+      if (path.length() == 0) continue;
+
+      final int len = builder.length();
+      if (len > 0 && builder.charAt(len - 1) != '/' && builder.charAt(len - 1) != File.separatorChar) {
+        builder.append('/');
+      }
+      builder.append(len != 0 ? trimForwardSlashes(path) : path);
+    }
+    return builder.toString();
+  }
+
+  public static String appendToPath(@NotNull String basePath, @NotNull String relativePath) {
+    final boolean endsWithSlash = StringUtil.endsWithChar(basePath, '/') || StringUtil.endsWithChar(basePath, '\\');
+    final boolean startsWithSlash = StringUtil.startsWithChar(relativePath, '/') || StringUtil.startsWithChar(relativePath, '\\');
+    String tail;
+    if (endsWithSlash && startsWithSlash) {
+      tail = trimForwardSlashes(relativePath);
+    }
+    else if (!endsWithSlash && !startsWithSlash && basePath.length() > 0 && relativePath.length() > 0) {
+      tail = "/" + relativePath;
+    }
+    else {
+      tail = relativePath;
+    }
+    return basePath + tail;
+  }
+
+  @Deprecated
+  public abstract BuildRecipe createBuildRecipe();
+
+  @Nullable
+  public abstract String getConfigFileErrorMessage(ConfigFile configFile);
+
+  /**
+   * @deprecated use {@link com.intellij.openapi.util.io.FileUtil#getRelativePath}
+   */
+  @Nullable
+  public static String getRelativePath(@NotNull String basePath, @NotNull final String filePath) {
+    if (basePath.equals(filePath)) return "";
+    if (!basePath.endsWith(File.separator)) basePath += File.separatorChar;
+
+    int len = 0;
+    int lastSeparatorIndex = 0; // need this for cases like this: base="/temp/abcde/baseDir" and file="/temp/ab"
+    while (len < filePath.length() && len < basePath.length() && filePath.charAt(len) == basePath.charAt(len)) {
+      if (basePath.charAt(len) == File.separatorChar) {
+        lastSeparatorIndex = len;
+      }
+      len++;
+    }
+
+    if (len == 0) {
+      return null;
+    }
+    final StringBuilder relativePath = StringBuilderSpinAllocator.alloc();
+    try {
+      for (int i=len; i < basePath.length(); i++) {
+        if (basePath.charAt(i) == File.separatorChar) {
+          relativePath.append("..");
+          relativePath.append(File.separatorChar);
+        }
+      }
+      relativePath.append(filePath.substring(lastSeparatorIndex + 1));
+
+      return relativePath.toString();
+    }
+    finally {
+      StringBuilderSpinAllocator.dispose(relativePath);
+    }
+  }
+
+  public abstract void checkConfigFile(final ConfigFile descriptor, final CompileContext compileContext, final Module module);
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/artifacts/Artifact.java b/java/compiler/openapi/src/com/intellij/packaging/artifacts/Artifact.java
new file mode 100644
index 0000000..8155cf0
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/artifacts/Artifact.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.
+ */
+package com.intellij.packaging.artifacts;
+
+import com.intellij.openapi.util.UserDataHolder;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+
+/**
+ * @author nik
+ */
+public interface Artifact extends UserDataHolder {
+  @NotNull
+  ArtifactType getArtifactType();
+
+  String getName();
+
+  boolean isBuildOnMake();
+
+  @NotNull
+  CompositePackagingElement<?> getRootElement();
+
+  @Nullable
+  String getOutputPath();
+
+  Collection<? extends ArtifactPropertiesProvider> getPropertiesProviders();
+
+  ArtifactProperties<?> getProperties(@NotNull ArtifactPropertiesProvider propertiesProvider);
+
+  @Nullable
+  VirtualFile getOutputFile();
+
+  @Nullable
+  String getOutputFilePath();
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactAdapter.java b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactAdapter.java
new file mode 100644
index 0000000..34b91f0
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactAdapter.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 com.intellij.packaging.artifacts;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public class ArtifactAdapter implements ArtifactListener {
+  public void artifactAdded(@NotNull Artifact artifact) {
+  }
+
+  public void artifactRemoved(@NotNull Artifact artifact) {
+  }
+
+  public void artifactChanged(@NotNull Artifact artifact, @NotNull String oldName) {
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactListener.java b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactListener.java
new file mode 100644
index 0000000..88dddea
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactListener.java
@@ -0,0 +1,33 @@
+/*
+ * 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 com.intellij.packaging.artifacts;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.EventListener;
+
+/**
+ * @author nik
+ */
+public interface ArtifactListener extends EventListener {
+
+  void artifactAdded(@NotNull Artifact artifact);
+
+  void artifactRemoved(@NotNull Artifact artifact);
+
+  void artifactChanged(@NotNull Artifact artifact, @NotNull String oldName);
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactManager.java b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactManager.java
new file mode 100644
index 0000000..9610bfa
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactManager.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 com.intellij.packaging.artifacts;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.ModificationTracker;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.util.messages.Topic;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.Comparator;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactManager implements ArtifactModel {
+  public static final Topic<ArtifactListener> TOPIC = Topic.create("artifacts changes", ArtifactListener.class);
+  public static final Comparator<Artifact> ARTIFACT_COMPARATOR = new Comparator<Artifact>() {
+    public int compare(Artifact o1, Artifact o2) {
+      return o1.getName().compareToIgnoreCase(o2.getName());
+    }
+  };
+
+  public static ArtifactManager getInstance(@NotNull Project project) {
+    return project.getComponent(ArtifactManager.class);
+  }
+
+  public abstract Artifact[] getSortedArtifacts();
+
+  public abstract ModifiableArtifactModel createModifiableModel();
+
+  public abstract PackagingElementResolvingContext getResolvingContext();
+
+  @NotNull
+  public abstract Artifact addArtifact(@NonNls @NotNull String name, @NotNull ArtifactType type, @Nullable CompositePackagingElement<?> root);
+
+  public abstract void addElementsToDirectory(@NotNull Artifact artifact, @NotNull String relativePath,
+                                              @NotNull Collection<? extends PackagingElement<?>> elements);
+
+  public abstract void addElementsToDirectory(@NotNull Artifact artifact, @NotNull String relativePath,
+                                              @NotNull PackagingElement<?> element);
+
+  public abstract ModificationTracker getModificationTracker();
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactModel.java b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactModel.java
new file mode 100644
index 0000000..91f3ae7
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactModel.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 com.intellij.packaging.artifacts;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public interface ArtifactModel {
+  @NotNull
+  Artifact[] getArtifacts();
+
+  @Nullable
+  Artifact findArtifact(@NotNull String name);
+
+  @NotNull
+  Artifact getArtifactByOriginal(@NotNull Artifact artifact);
+
+  @NotNull
+  Artifact getOriginalArtifact(@NotNull Artifact artifact);
+
+  Collection<? extends Artifact> getArtifactsByType(@NotNull ArtifactType type);
+
+  List<? extends Artifact> getAllArtifactsIncludingInvalid();
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactPointer.java b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactPointer.java
new file mode 100644
index 0000000..ee1ed1c
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactPointer.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 com.intellij.packaging.artifacts;
+
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public interface ArtifactPointer {
+
+  @NotNull
+  String getArtifactName();
+
+  @Nullable
+  Artifact getArtifact();
+
+  @NotNull
+  String getArtifactName(@NotNull ArtifactModel artifactModel);
+
+  @Nullable
+  Artifact findArtifact(@NotNull ArtifactModel artifactModel);
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactPointerManager.java b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactPointerManager.java
new file mode 100644
index 0000000..c5f32f4
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactPointerManager.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 com.intellij.packaging.artifacts;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.components.ServiceManager;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactPointerManager {
+  public static ArtifactPointerManager getInstance(@NotNull Project project) {
+    return ServiceManager.getService(project, ArtifactPointerManager.class);
+  }
+
+  public abstract ArtifactPointer createPointer(@NotNull String name);
+
+  public abstract ArtifactPointer createPointer(@NotNull Artifact artifact);
+
+  public abstract ArtifactPointer createPointer(@NotNull Artifact artifact, @NotNull ArtifactModel artifactModel);
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactProperties.java b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactProperties.java
new file mode 100644
index 0000000..65d590b
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactProperties.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.
+ */
+package com.intellij.packaging.artifacts;
+
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.ArtifactPropertiesEditor;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactProperties<S> implements PersistentStateComponent<S> {
+
+  public void onBuildStarted(@NotNull Artifact artifact, @NotNull CompileContext compileContext) {
+  }
+
+  public void onBuildFinished(@NotNull Artifact artifact, @NotNull CompileContext compileContext) {
+  }
+
+  public abstract ArtifactPropertiesEditor createEditor(@NotNull ArtifactEditorContext context);
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactPropertiesProvider.java b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactPropertiesProvider.java
new file mode 100644
index 0000000..bb3c162
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactPropertiesProvider.java
@@ -0,0 +1,59 @@
+/*
+ * 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 com.intellij.packaging.artifacts;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.extensions.Extensions;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactPropertiesProvider {
+  public static final ExtensionPointName<ArtifactPropertiesProvider> EP_NAME = ExtensionPointName.create("com.intellij.packaging.artifactPropertiesProvider");
+  private final String myId;
+
+  protected ArtifactPropertiesProvider(@NotNull @NonNls String id) {
+    myId = id;
+  }
+
+  public final String getId() {
+    return myId;
+  }
+
+  public boolean isAvailableFor(@NotNull ArtifactType type) {
+    return true;
+  } 
+
+  @NotNull 
+  public abstract ArtifactProperties<?> createProperties(@NotNull ArtifactType artifactType);
+
+  public static ArtifactPropertiesProvider[] getProviders() {
+    return Extensions.getExtensions(EP_NAME);
+  }
+
+  @Nullable
+  public static ArtifactPropertiesProvider findById(@NotNull @NonNls String id) {
+    for (ArtifactPropertiesProvider provider : getProviders()) {
+      if (provider.getId().equals(id)) {
+        return provider;
+      }
+    }
+    return null;
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactTemplate.java b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactTemplate.java
new file mode 100644
index 0000000..f1b8827
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactTemplate.java
@@ -0,0 +1,78 @@
+/*
+ * 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 com.intellij.packaging.artifacts;
+
+import com.intellij.packaging.elements.CompositePackagingElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactTemplate {
+
+  public abstract String getPresentableName();
+
+  /**
+   * @deprecated override {@link #createArtifact()} instead
+   */
+  @Deprecated
+  public CompositePackagingElement<?> createRootElement(@NotNull String artifactName) {
+    return null;
+  }
+
+  /**
+   * @deprecated override {@link #createArtifact()} instead
+   */
+  @Deprecated
+  @NotNull
+  public String suggestArtifactName() {
+    return "unnamed";
+  }
+
+  @Nullable
+  public NewArtifactConfiguration createArtifact() {
+    final String name = suggestArtifactName();
+    return new NewArtifactConfiguration(createRootElement(name), name, null);
+  }
+
+  public void setUpArtifact(@NotNull Artifact artifact, @NotNull NewArtifactConfiguration configuration) {
+  }
+
+  public static class NewArtifactConfiguration {
+    private final CompositePackagingElement<?> myRootElement;
+    private final String myArtifactName;
+    private final ArtifactType myArtifactType;
+
+    public NewArtifactConfiguration(CompositePackagingElement<?> rootElement, String artifactName, ArtifactType artifactType) {
+      myRootElement = rootElement;
+      myArtifactName = artifactName;
+      myArtifactType = artifactType;
+    }
+
+    public CompositePackagingElement<?> getRootElement() {
+      return myRootElement;
+    }
+
+    public String getArtifactName() {
+      return myArtifactName;
+    }
+
+    public ArtifactType getArtifactType() {
+      return myArtifactType;
+    }
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactType.java b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactType.java
new file mode 100644
index 0000000..130d0e2
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ArtifactType.java
@@ -0,0 +1,100 @@
+/*
+ * 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 com.intellij.packaging.artifacts;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementOutputKind;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import com.intellij.packaging.ui.ArtifactProblemsHolder;
+import com.intellij.packaging.ui.PackagingSourceItem;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactType {
+  public static final ExtensionPointName<ArtifactType> EP_NAME = ExtensionPointName.create("com.intellij.packaging.artifactType");
+  private final String myId;
+  private final String myTitle;
+
+  protected ArtifactType(@NonNls String id, String title) {
+    myId = id;
+    myTitle = title;
+  }
+
+  public final String getId() {
+    return myId;
+  }
+
+  public String getPresentableName() {
+    return myTitle;
+  }                                
+
+  @NotNull
+  public abstract Icon getIcon();
+
+  @Nullable
+  public String getDefaultPathFor(@NotNull PackagingSourceItem sourceItem) {
+    return getDefaultPathFor(sourceItem.getKindOfProducedElements());
+  }
+
+  @Nullable
+  public abstract String getDefaultPathFor(@NotNull PackagingElementOutputKind kind);
+
+  public boolean isSuitableItem(@NotNull PackagingSourceItem sourceItem) {
+    return true;
+  }
+
+  public static ArtifactType[] getAllTypes() {
+    return Extensions.getExtensions(EP_NAME);
+  }
+
+  @Nullable
+  public static ArtifactType findById(@NotNull @NonNls String id) {
+    for (ArtifactType type : getAllTypes()) {
+      if (id.equals(type.getId())) {
+        return type;
+      }
+    }
+    return null;
+  }
+
+  @NotNull
+  public abstract CompositePackagingElement<?> createRootElement(@NotNull String artifactName);
+
+  @NotNull
+  public List<? extends ArtifactTemplate> getNewArtifactTemplates(@NotNull PackagingElementResolvingContext context) {
+    return Collections.emptyList();
+  }
+
+  public void checkRootElement(@NotNull CompositePackagingElement<?> rootElement, @NotNull Artifact artifact, @NotNull ArtifactProblemsHolder manager) {
+  }
+
+  @Nullable
+  public List<? extends PackagingElement<?>> getSubstitution(@NotNull Artifact artifact, @NotNull PackagingElementResolvingContext context,
+                                                             @NotNull ArtifactType parentType) {
+    return null;
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/artifacts/ModifiableArtifact.java b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ModifiableArtifact.java
new file mode 100644
index 0000000..0b3f245
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ModifiableArtifact.java
@@ -0,0 +1,37 @@
+/*
+ * 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 com.intellij.packaging.artifacts;
+
+import com.intellij.packaging.elements.CompositePackagingElement;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public interface ModifiableArtifact extends Artifact {
+
+  void setBuildOnMake(boolean enabled);
+
+  void setOutputPath(String outputPath);
+
+  void setName(@NotNull String name);
+
+  void setRootElement(CompositePackagingElement<?> root);
+
+  void setProperties(ArtifactPropertiesProvider provider, ArtifactProperties<?> properties);
+
+  void setArtifactType(@NotNull ArtifactType selected);
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/artifacts/ModifiableArtifactModel.java b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ModifiableArtifactModel.java
new file mode 100644
index 0000000..5e6172f
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/artifacts/ModifiableArtifactModel.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 com.intellij.packaging.artifacts;
+
+import com.intellij.packaging.elements.CompositePackagingElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public interface ModifiableArtifactModel extends ArtifactModel {
+
+  @NotNull
+  ModifiableArtifact addArtifact(final @NotNull String name, @NotNull ArtifactType artifactType);
+
+  @NotNull
+  ModifiableArtifact addArtifact(final @NotNull String name, @NotNull ArtifactType artifactType, CompositePackagingElement<?> rootElement);
+
+  void removeArtifact(@NotNull Artifact artifact);
+
+  @NotNull
+  ModifiableArtifact getOrCreateModifiableArtifact(@NotNull Artifact artifact);
+
+  @Nullable
+  Artifact getModifiableCopy(Artifact artifact);
+
+  void addListener(@NotNull ArtifactListener listener);
+
+  void removeListener(@NotNull ArtifactListener listener);
+
+
+  boolean isModified();
+
+  void commit();
+
+  void dispose();
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/AntCopyInstructionCreator.java b/java/compiler/openapi/src/com/intellij/packaging/elements/AntCopyInstructionCreator.java
new file mode 100644
index 0000000..fc0eb03
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/elements/AntCopyInstructionCreator.java
@@ -0,0 +1,42 @@
+/*
+ * 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 com.intellij.packaging.elements;
+
+import com.intellij.compiler.ant.Generator;
+import com.intellij.compiler.ant.Tag;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public interface AntCopyInstructionCreator {
+
+  @NotNull
+  Tag createDirectoryContentCopyInstruction(@NotNull String dirPath);
+
+  @NotNull
+  Tag createFileCopyInstruction(@NotNull String filePath, String outputFileName);
+
+  @NotNull
+  AntCopyInstructionCreator subFolder(@NotNull String directoryName);
+
+  @Nullable
+  Generator createSubFolderCommand(@NotNull String directoryName);
+
+  @NotNull
+  Generator createExtractedDirectoryInstruction(@NotNull String jarPath);
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/ArtifactAntGenerationContext.java b/java/compiler/openapi/src/com/intellij/packaging/elements/ArtifactAntGenerationContext.java
new file mode 100644
index 0000000..46864f3
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/elements/ArtifactAntGenerationContext.java
@@ -0,0 +1,49 @@
+/*
+ * 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 com.intellij.packaging.elements;
+
+import com.intellij.compiler.ant.GenerationOptions;
+import com.intellij.compiler.ant.Generator;
+import com.intellij.openapi.project.Project;
+import com.intellij.packaging.artifacts.Artifact;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author nik
+ */
+public interface ArtifactAntGenerationContext {
+
+  void runBeforeCurrentArtifact(Generator generator);
+
+  void runBeforeBuild(Generator generator);
+
+  void runAfterBuild(Generator generator);
+
+  String createNewTempFileProperty(@NonNls String basePropertyName, @NonNls String fileName);
+
+  String getModuleOutputPath(@NonNls String moduleName);
+
+  String getModuleTestOutputPath(@NonNls String moduleName);
+
+  String getSubstitutedPath(@NonNls String path);
+
+  String getArtifactOutputProperty(@NotNull Artifact artifact);
+
+  Project getProject();
+
+  GenerationOptions getGenerationOptions();
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/ArtifactIncrementalCompilerContext.java b/java/compiler/openapi/src/com/intellij/packaging/elements/ArtifactIncrementalCompilerContext.java
new file mode 100644
index 0000000..6ee65f5
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/elements/ArtifactIncrementalCompilerContext.java
@@ -0,0 +1,22 @@
+/*
+ * 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 com.intellij.packaging.elements;
+
+/**
+ * @author nik
+ */
+public interface ArtifactIncrementalCompilerContext {
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/ArtifactRootElement.java b/java/compiler/openapi/src/com/intellij/packaging/elements/ArtifactRootElement.java
new file mode 100644
index 0000000..37da8ba
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/elements/ArtifactRootElement.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 com.intellij.packaging.elements;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactRootElement<S> extends CompositePackagingElement<S> {
+  protected ArtifactRootElement(PackagingElementType type) {
+    super(type);
+  }
+
+  @Override
+  public boolean isEqualTo(@NotNull PackagingElement<?> element) {
+    return false;
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/ComplexPackagingElement.java b/java/compiler/openapi/src/com/intellij/packaging/elements/ComplexPackagingElement.java
new file mode 100644
index 0000000..5a09569
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/elements/ComplexPackagingElement.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 com.intellij.packaging.elements;
+
+import com.intellij.compiler.ant.Generator;
+import com.intellij.packaging.artifacts.ArtifactType;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * @author nik
+ */
+public abstract class ComplexPackagingElement<S> extends PackagingElement<S> {
+  protected ComplexPackagingElement(PackagingElementType type) {
+    super(type);
+  }
+
+  @Override
+  public List<? extends Generator> computeAntInstructions(@NotNull PackagingElementResolvingContext resolvingContext, @NotNull AntCopyInstructionCreator creator,
+                                                          @NotNull ArtifactAntGenerationContext generationContext,
+                                                          @NotNull ArtifactType artifactType) {
+    final List<? extends PackagingElement<?>> substitution = getSubstitution(resolvingContext, artifactType);
+    if (substitution == null) {
+      return Collections.emptyList();
+    }
+
+    final List<Generator> fileSets = new ArrayList<Generator>();
+    for (PackagingElement<?> element : substitution) {
+      fileSets.addAll(element.computeAntInstructions(resolvingContext, creator, generationContext, artifactType));
+    }
+    return fileSets;
+  }
+
+  @Override
+  public void computeIncrementalCompilerInstructions(@NotNull IncrementalCompilerInstructionCreator creator,
+                                                     @NotNull PackagingElementResolvingContext resolvingContext,
+                                                     @NotNull ArtifactIncrementalCompilerContext compilerContext, @NotNull ArtifactType artifactType) {
+    final List<? extends PackagingElement<?>> substitution = getSubstitution(resolvingContext, artifactType);
+    if (substitution == null) return;
+
+    for (PackagingElement<?> element : substitution) {
+      element.computeIncrementalCompilerInstructions(creator, resolvingContext, compilerContext,
+                                                     getArtifactTypeForSubstitutedElements(resolvingContext, artifactType));
+    }
+  }
+
+  protected ArtifactType getArtifactTypeForSubstitutedElements(PackagingElementResolvingContext resolvingContext, ArtifactType artifactType) {
+    return artifactType;
+  }
+
+
+  @Nullable
+  public abstract List<? extends PackagingElement<?>> getSubstitution(@NotNull PackagingElementResolvingContext context, @NotNull ArtifactType artifactType);
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/ComplexPackagingElementType.java b/java/compiler/openapi/src/com/intellij/packaging/elements/ComplexPackagingElementType.java
new file mode 100644
index 0000000..7fd3d99
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/elements/ComplexPackagingElementType.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 com.intellij.packaging.elements;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.ModificationTracker;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public abstract class ComplexPackagingElementType<E extends ComplexPackagingElement<?>> extends PackagingElementType<E> {
+  protected ComplexPackagingElementType(@NotNull @NonNls String id, @NotNull String presentableName) {
+    super(id, presentableName);
+  }
+
+  public abstract String getShowContentActionText();
+
+  @Nullable
+  public ModificationTracker getAllSubstitutionsModificationTracker(@NotNull Project project) {
+    return null;
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/CompositePackagingElement.java b/java/compiler/openapi/src/com/intellij/packaging/elements/CompositePackagingElement.java
new file mode 100644
index 0000000..fbfa5e0
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/elements/CompositePackagingElement.java
@@ -0,0 +1,140 @@
+/*
+ * 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 com.intellij.packaging.elements;
+
+import com.intellij.compiler.ant.Generator;
+import com.intellij.packaging.artifacts.ArtifactType;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class CompositePackagingElement<S> extends PackagingElement<S> implements RenameablePackagingElement {
+  private final List<PackagingElement<?>> myChildren = new ArrayList<PackagingElement<?>>();
+  private List<PackagingElement<?>> myUnmodifiableChildren;
+
+  protected CompositePackagingElement(PackagingElementType type) {
+    super(type);
+  }
+
+  public <T extends PackagingElement<?>> T addOrFindChild(@NotNull T child) {
+    for (PackagingElement<?> element : myChildren) {
+      if (element.isEqualTo(child)) {
+        if (element instanceof CompositePackagingElement) {
+          final List<PackagingElement<?>> children = ((CompositePackagingElement<?>)child).getChildren();
+          ((CompositePackagingElement<?>)element).addOrFindChildren(children);
+        }
+        //noinspection unchecked
+        return (T) element;
+      }
+    }
+    myChildren.add(child);
+    return child;
+  }
+
+  public void addFirstChild(@NotNull PackagingElement<?> child) {
+    myChildren.add(0, child);
+    for (int i = 1; i < myChildren.size(); i++) {
+      PackagingElement<?> element = myChildren.get(i);
+      if (element.isEqualTo(child)) {
+        if (element instanceof CompositePackagingElement<?>) {
+          ((CompositePackagingElement<?>)child).addOrFindChildren(((CompositePackagingElement<?>)element).getChildren());
+        }
+        myChildren.remove(i);
+        break;
+      }
+    }
+  }
+
+  public List<? extends PackagingElement<?>> addOrFindChildren(Collection<? extends PackagingElement<?>> children) {
+    List<PackagingElement<?>> added = new ArrayList<PackagingElement<?>>();
+    for (PackagingElement<?> child : children) {
+      added.add(addOrFindChild(child));
+    }
+    return added;
+  }
+
+  @Nullable
+  public PackagingElement<?> moveChild(int index, int direction) {
+    int target = index + direction;
+    if (0 <= index && index < myChildren.size() && 0 <= target && target < myChildren.size()) {
+      final PackagingElement<?> element1 = myChildren.get(index);
+      final PackagingElement<?> element2 = myChildren.get(target);
+      myChildren.set(index, element2);
+      myChildren.set(target, element1);
+      return element1;
+    }
+    return null;
+  }
+
+  public void removeChild(@NotNull PackagingElement<?> child) {
+    myChildren.remove(child);
+  }
+
+  public void removeChildren(@NotNull Collection<? extends PackagingElement<?>> children) {
+    myChildren.removeAll(children);
+  }
+
+  @NotNull
+  public List<PackagingElement<?>> getChildren() {
+    if (myUnmodifiableChildren == null) {
+      myUnmodifiableChildren = Collections.unmodifiableList(myChildren);
+    }
+    return myUnmodifiableChildren;
+  }
+
+  public boolean canBeRenamed() {
+    return true;
+  }
+
+  protected List<? extends Generator> computeChildrenGenerators(PackagingElementResolvingContext resolvingContext,
+                                                                final AntCopyInstructionCreator copyInstructionCreator,
+                                                                final ArtifactAntGenerationContext generationContext, ArtifactType artifactType) {
+    final List<Generator> generators = new ArrayList<Generator>();
+    for (PackagingElement<?> child : myChildren) {
+      generators.addAll(child.computeAntInstructions(resolvingContext, copyInstructionCreator, generationContext, artifactType));
+    }
+    return generators;
+  }
+
+  protected void computeChildrenInstructions(@NotNull IncrementalCompilerInstructionCreator creator,
+                                             @NotNull PackagingElementResolvingContext resolvingContext,
+                                             @NotNull ArtifactIncrementalCompilerContext compilerContext, ArtifactType artifactType) {
+    for (PackagingElement<?> child : myChildren) {
+      child.computeIncrementalCompilerInstructions(creator, resolvingContext, compilerContext, artifactType);
+    }
+  }
+
+  public void removeAllChildren() {
+    myChildren.clear();
+  }
+
+  @Nullable
+  public CompositePackagingElement<?> findCompositeChild(@NotNull String name) {
+    for (PackagingElement<?> child : myChildren) {
+      if (child instanceof CompositePackagingElement && name.equals(((CompositePackagingElement)child).getName())) {
+        return (CompositePackagingElement)child;
+      }
+    }
+    return null;
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/CompositePackagingElementType.java b/java/compiler/openapi/src/com/intellij/packaging/elements/CompositePackagingElementType.java
new file mode 100644
index 0000000..92af59d
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/elements/CompositePackagingElementType.java
@@ -0,0 +1,49 @@
+/*
+ * 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 com.intellij.packaging.elements;
+
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class CompositePackagingElementType<E extends CompositePackagingElement<?>> extends PackagingElementType<E> {
+  protected CompositePackagingElementType(@NotNull @NonNls String id, @NotNull String presentableName) {
+    super(id, presentableName);
+  }
+
+  @Override
+  public boolean canCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact) {
+    return true;
+  }
+
+
+  @Nullable
+  public abstract CompositePackagingElement<?> createComposite(CompositePackagingElement<?> parent, @Nullable String baseName, @NotNull ArtifactEditorContext context);
+
+  @NotNull
+  public List<? extends PackagingElement<?>> chooseAndCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact, @NotNull CompositePackagingElement<?> parent) {
+    final PackagingElement<?> composite = createComposite(parent, null, context);
+    return composite != null ? Collections.singletonList(composite) : Collections.<E>emptyList();
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/IncrementalCompilerInstructionCreator.java b/java/compiler/openapi/src/com/intellij/packaging/elements/IncrementalCompilerInstructionCreator.java
new file mode 100644
index 0000000..7c1f1d0
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/elements/IncrementalCompilerInstructionCreator.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 com.intellij.packaging.elements;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public interface IncrementalCompilerInstructionCreator {
+
+  void addFileCopyInstruction(@NotNull VirtualFile file, @NotNull String outputFileName);
+
+  void addDirectoryCopyInstructions(@NotNull VirtualFile directory);
+
+  void addDirectoryCopyInstructions(@NotNull VirtualFile directory, @Nullable PackagingFileFilter filter);
+
+  IncrementalCompilerInstructionCreator subFolder(@NotNull String directoryName);
+
+  IncrementalCompilerInstructionCreator archive(@NotNull String archiveFileName);
+
+  IncrementalCompilerInstructionCreator subFolderByRelativePath(@NotNull String relativeDirectoryPath);
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/ManifestFileProvider.java b/java/compiler/openapi/src/com/intellij/packaging/elements/ManifestFileProvider.java
new file mode 100644
index 0000000..599c07f
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/elements/ManifestFileProvider.java
@@ -0,0 +1,31 @@
+/*
+ * 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 com.intellij.packaging.elements;
+
+import com.intellij.packaging.artifacts.ArtifactType;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public interface ManifestFileProvider {
+  @Nullable
+  List<String> getClasspathFromManifest(@NotNull CompositePackagingElement<?> archiveRoot, @NotNull ArtifactType artifactType);
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElement.java b/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElement.java
new file mode 100644
index 0000000..377616b
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElement.java
@@ -0,0 +1,57 @@
+/*
+ * 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 com.intellij.packaging.elements;
+
+import com.intellij.compiler.ant.Generator;
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingElementPresentation;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class PackagingElement<S> implements PersistentStateComponent<S> {
+  private final PackagingElementType myType;
+
+  protected PackagingElement(PackagingElementType type) {
+    myType = type;
+  }
+
+  public abstract PackagingElementPresentation createPresentation(@NotNull ArtifactEditorContext context);
+
+  public final PackagingElementType getType() {
+    return myType;
+  }
+
+  public abstract List<? extends Generator> computeAntInstructions(@NotNull PackagingElementResolvingContext resolvingContext, @NotNull AntCopyInstructionCreator creator,
+                                                                   @NotNull ArtifactAntGenerationContext generationContext,
+                                                                   @NotNull ArtifactType artifactType);
+
+  public abstract void computeIncrementalCompilerInstructions(@NotNull IncrementalCompilerInstructionCreator creator, @NotNull PackagingElementResolvingContext resolvingContext,
+                                                              @NotNull ArtifactIncrementalCompilerContext compilerContext,
+                                                              @NotNull ArtifactType artifactType);
+
+  public abstract boolean isEqualTo(@NotNull PackagingElement<?> element);
+
+  @NotNull
+  public PackagingElementOutputKind getFilesKind(PackagingElementResolvingContext context) {
+    return PackagingElementOutputKind.OTHER;
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElementFactory.java b/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElementFactory.java
new file mode 100644
index 0000000..d6574fa
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElementFactory.java
@@ -0,0 +1,124 @@
+/*
+ * 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 com.intellij.packaging.elements;
+
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactPointer;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class PackagingElementFactory {
+
+  public static PackagingElementFactory getInstance() {
+    return ServiceManager.getService(PackagingElementFactory.class);
+  }
+
+  @NotNull
+  public abstract ArtifactRootElement<?> createArtifactRootElement();
+
+  @NotNull
+  public abstract CompositePackagingElement<?> createDirectory(@NotNull @NonNls String directoryName);
+
+  @NotNull
+  public abstract CompositePackagingElement<?> createArchive(@NotNull @NonNls String archiveFileName);
+
+  public abstract PackagingElement<?> createFileCopy(@NotNull String filePath, @Nullable String outputFileName);
+
+  @NotNull
+  public abstract PackagingElement<?> createModuleOutput(@NotNull String moduleName, @NotNull Project project);
+
+  @NotNull
+  public abstract PackagingElement<?> createModuleOutput(@NotNull Module module);
+
+  @NotNull
+  public abstract PackagingElement<?> createTestModuleOutput(@NotNull Module module);
+
+  @NotNull
+  public abstract List<? extends PackagingElement<?>> createLibraryElements(@NotNull Library library);
+
+  @NotNull
+  public abstract PackagingElement<?> createArtifactElement(@NotNull ArtifactPointer artifactPointer, @NotNull Project project);
+
+  @NotNull
+  public abstract PackagingElement<?> createArtifactElement(@NotNull Artifact artifact, @NotNull Project project);
+
+  @NotNull
+  public abstract PackagingElement<?> createLibraryFiles(@NotNull String libraryName, @NotNull String level, String moduleName);
+
+
+  @NotNull
+  public abstract PackagingElement<?> createDirectoryCopyWithParentDirectories(@NotNull String filePath, @NotNull String relativeOutputPath);
+
+  @NotNull
+  public abstract PackagingElement<?> createExtractedDirectoryWithParentDirectories(@NotNull String jarPath, @NotNull String pathInJar,
+                                                                                    @NotNull String relativeOutputPath);
+
+  @NotNull
+  public abstract PackagingElement<?> createExtractedDirectory(@NotNull VirtualFile jarEntry);
+
+  @NotNull
+  public abstract PackagingElement<?> createFileCopyWithParentDirectories(@NotNull String filePath, @NotNull String relativeOutputPath,
+                                                                          @Nullable String outputFileName);
+  
+  @NotNull
+  public abstract PackagingElement<?> createFileCopyWithParentDirectories(@NotNull String filePath, @NotNull String relativeOutputPath);
+  
+
+  @NotNull
+  public abstract CompositePackagingElement<?> getOrCreateDirectory(@NotNull CompositePackagingElement<?> parent, @NotNull String relativePath);
+
+  @NotNull
+  public abstract CompositePackagingElement<?> getOrCreateArchive(@NotNull CompositePackagingElement<?> parent, @NotNull String relativePath);
+
+  public abstract void addFileCopy(@NotNull CompositePackagingElement<?> root, @NotNull String outputDirectoryPath, @NotNull String sourceFilePath,
+                                   final String outputFileName);
+
+  public abstract void addFileCopy(@NotNull CompositePackagingElement<?> root, @NotNull String outputDirectoryPath, @NotNull String sourceFilePath);
+
+  @NotNull
+  public abstract PackagingElement<?> createParentDirectories(@NotNull String relativeOutputPath, @NotNull PackagingElement<?> element);
+
+
+  @NotNull
+  public abstract List<? extends PackagingElement<?>> createParentDirectories(@NotNull String relativeOutputPath, @NotNull List<? extends PackagingElement<?>> elements);
+
+  @NotNull
+  
+  public abstract CompositePackagingElementType<?>[] getCompositeElementTypes();
+
+  @Nullable
+  public abstract PackagingElementType<?> findElementType(String id);
+
+  @NotNull
+  public abstract PackagingElementType<?>[] getNonCompositeElementTypes();
+
+  @NotNull
+  public abstract PackagingElementType[] getAllElementTypes();
+
+  @NotNull
+  public abstract ComplexPackagingElementType<?>[] getComplexElementTypes();
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElementOutputKind.java b/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElementOutputKind.java
new file mode 100644
index 0000000..4ba42df
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElementOutputKind.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 com.intellij.packaging.elements;
+
+/**
+ * @author nik
+ */
+public class PackagingElementOutputKind {
+  public static final PackagingElementOutputKind DIRECTORIES_WITH_CLASSES = new PackagingElementOutputKind(true, false);
+  public static final PackagingElementOutputKind JAR_FILES = new PackagingElementOutputKind(false, true);
+  public static final PackagingElementOutputKind OTHER = new PackagingElementOutputKind(false, false);
+  private final boolean myContainsDirectoriesWithClasses;
+  private final boolean myContainsJarFiles;
+
+  public PackagingElementOutputKind(boolean containsDirectoriesWithClasses, boolean containsJarFiles) {
+    myContainsDirectoriesWithClasses = containsDirectoriesWithClasses;
+    myContainsJarFiles = containsJarFiles;
+  }
+
+  public boolean containsDirectoriesWithClasses() {
+    return myContainsDirectoriesWithClasses;
+  }
+
+  public boolean containsJarFiles() {
+    return myContainsJarFiles;
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElementResolvingContext.java b/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElementResolvingContext.java
new file mode 100644
index 0000000..a033d3c
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElementResolvingContext.java
@@ -0,0 +1,47 @@
+/*
+ * 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 com.intellij.packaging.elements;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.FacetsProvider;
+import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
+import com.intellij.packaging.artifacts.ArtifactModel;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author nik
+ */
+public interface PackagingElementResolvingContext {
+  @NotNull
+  Project getProject();
+
+  @NotNull
+  ArtifactModel getArtifactModel();
+
+  @NotNull
+  ModulesProvider getModulesProvider();
+
+  @NotNull
+  FacetsProvider getFacetsProvider();
+
+  @Nullable
+  Library findLibrary(@NotNull String level, @NotNull String libraryName);
+
+  @NotNull
+  ManifestFileProvider getManifestFileProvider();
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElementType.java b/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElementType.java
new file mode 100644
index 0000000..48cc0c2
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingElementType.java
@@ -0,0 +1,79 @@
+/*
+ * 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 com.intellij.packaging.elements;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.project.Project;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.ui.ArtifactEditorContext;
+import com.intellij.packaging.ui.PackagingElementPropertiesPanel;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class PackagingElementType<E extends PackagingElement<?>> {
+  public static final ExtensionPointName<PackagingElementType> EP_NAME = ExtensionPointName.create("com.intellij.packaging.elementType");
+  private final String myId;
+  private final String myPresentableName;
+
+  protected PackagingElementType(@NotNull @NonNls String id, @NotNull String presentableName) {
+    myId = id;
+    myPresentableName = presentableName;
+  }
+
+  public final String getId() {
+    return myId;
+  }
+
+  public String getPresentableName() {
+    return myPresentableName;
+  }
+
+  @Nullable
+  public Icon getCreateElementIcon() {
+    return null;
+  }
+
+  public abstract boolean canCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact);
+
+  @NotNull 
+  public abstract List<? extends PackagingElement<?>> chooseAndCreate(@NotNull ArtifactEditorContext context, @NotNull Artifact artifact,
+                                                                      @NotNull CompositePackagingElement<?> parent);
+
+  @NotNull
+  public abstract E createEmpty(@NotNull Project project);
+
+  protected static <T extends PackagingElementType<?>> T getInstance(final Class<T> aClass) {
+    for (PackagingElementType type : Extensions.getExtensions(EP_NAME)) {
+      if (aClass.isInstance(type)) {
+        return aClass.cast(type);
+      }
+    }
+    throw new AssertionError();
+  }
+
+  @Nullable
+  public PackagingElementPropertiesPanel createElementPropertiesPanel(@NotNull E element, @NotNull ArtifactEditorContext context) {
+    return null;
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingFileFilter.java b/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingFileFilter.java
new file mode 100644
index 0000000..363b391
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/elements/PackagingFileFilter.java
@@ -0,0 +1,30 @@
+/*
+ * 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 com.intellij.packaging.elements;
+
+import org.jetbrains.annotations.NotNull;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.compiler.CompileContext;
+
+/**
+ * @author nik
+ */
+public abstract class PackagingFileFilter {
+
+  public abstract boolean accept(@NotNull VirtualFile virtualFile, @NotNull CompileContext context);
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/elements/RenameablePackagingElement.java b/java/compiler/openapi/src/com/intellij/packaging/elements/RenameablePackagingElement.java
new file mode 100644
index 0000000..d34fb89
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/elements/RenameablePackagingElement.java
@@ -0,0 +1,29 @@
+/*
+ * 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 com.intellij.packaging.elements;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author nik
+ */
+public interface RenameablePackagingElement {
+  String getName();
+
+  boolean canBeRenamed();
+
+  void rename(@NotNull String newName);
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/ui/ArtifactEditor.java b/java/compiler/openapi/src/com/intellij/packaging/ui/ArtifactEditor.java
new file mode 100644
index 0000000..144bda2
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/ui/ArtifactEditor.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 com.intellij.packaging.ui;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public interface ArtifactEditor {
+  void updateLayoutTree();
+
+  void putLibraryIntoDefaultLocation(@NotNull Library library);
+
+  void putModuleIntoDefaultLocation(@NotNull Module module);
+
+  void addToClasspath(CompositePackagingElement<?> element, List<String> classpath);
+
+  boolean isDisposed();
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/ui/ArtifactEditorContext.java b/java/compiler/openapi/src/com/intellij/packaging/ui/ArtifactEditorContext.java
new file mode 100644
index 0000000..7ede5be
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/ui/ArtifactEditorContext.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 com.intellij.packaging.ui;
+
+import com.intellij.facet.Facet;
+import com.intellij.openapi.module.ModifiableModuleModel;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.packaging.artifacts.ArtifactType;
+import com.intellij.packaging.artifacts.ModifiableArtifactModel;
+import com.intellij.packaging.elements.CompositePackagingElement;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public interface ArtifactEditorContext extends PackagingElementResolvingContext {
+
+  void queueValidation();
+
+  @NotNull
+  ArtifactType getArtifactType();
+
+  @NotNull
+  ModifiableArtifactModel getOrCreateModifiableArtifactModel();
+
+  @Nullable
+  ModifiableModuleModel getModifiableModuleModel();
+
+  @NotNull
+  ModifiableRootModel getOrCreateModifiableRootModel(@NotNull Module module);
+
+  @Nullable
+  ManifestFileConfiguration getManifestFile(CompositePackagingElement<?> element, ArtifactType artifactType);
+
+
+  CompositePackagingElement<?> getRootElement(@NotNull Artifact artifact);
+
+  void editLayout(@NotNull Artifact artifact, Runnable runnable);
+
+  ArtifactEditor getOrCreateEditor(Artifact originalArtifact);
+
+  ArtifactEditor getThisArtifactEditor();
+
+  void selectArtifact(@NotNull Artifact artifact);
+
+  void selectFacet(@NotNull Facet<?> facet);
+
+  void selectModule(@NotNull Module module);
+
+  void selectLibrary(@NotNull Library library);
+
+
+  List<Artifact> chooseArtifacts(List<? extends Artifact> artifacts, String title);
+
+  List<Module> chooseModules(List<Module> modules, final String title);
+
+  List<Library> chooseLibraries(String title);
+
+  Artifact getArtifact();
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/ui/ArtifactProblemQuickFix.java b/java/compiler/openapi/src/com/intellij/packaging/ui/ArtifactProblemQuickFix.java
new file mode 100644
index 0000000..851ce79
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/ui/ArtifactProblemQuickFix.java
@@ -0,0 +1,37 @@
+/*
+ * 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 com.intellij.packaging.ui;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactProblemQuickFix {
+  private String myActionName;
+
+  protected ArtifactProblemQuickFix() {
+    this("Fix");
+  }
+
+  protected ArtifactProblemQuickFix(String actionName) {
+    myActionName = actionName;
+  }
+
+  public String getActionName() {
+    return myActionName;
+  }
+
+  public abstract void performFix(ArtifactEditorContext artifactEditorContext);
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/ui/ArtifactProblemsHolder.java b/java/compiler/openapi/src/com/intellij/packaging/ui/ArtifactProblemsHolder.java
new file mode 100644
index 0000000..abe8229
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/ui/ArtifactProblemsHolder.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 com.intellij.packaging.ui;
+
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementResolvingContext;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public interface ArtifactProblemsHolder {
+  @NotNull
+  PackagingElementResolvingContext getContext();
+
+  void registerError(@NotNull String message, @NotNull String problemTypeId);
+
+  void registerError(@NotNull String message, @NotNull String problemTypeId, @Nullable List<PackagingElement<?>> pathToPlace,
+                     @NotNull ArtifactProblemQuickFix... quickFixes);
+
+  void registerWarning(@NotNull String message, @NotNull String problemTypeId, @Nullable List<PackagingElement<?>> pathToPlace,
+                       @NotNull ArtifactProblemQuickFix... quickFixes);
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/ui/ArtifactPropertiesEditor.java b/java/compiler/openapi/src/com/intellij/packaging/ui/ArtifactPropertiesEditor.java
new file mode 100644
index 0000000..220b079
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/ui/ArtifactPropertiesEditor.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 com.intellij.packaging.ui;
+
+import com.intellij.openapi.options.UnnamedConfigurable;
+
+/**
+ * @author nik
+ */
+public abstract class ArtifactPropertiesEditor implements UnnamedConfigurable {
+  public static final String VALIDATION_TAB = "Validation";  
+  public static final String POST_PROCESSING_TAB = "Post-processing";
+  public static final String PRE_PROCESSING_TAB = "Pre-processing";
+
+  public abstract String getTabName();
+
+  public abstract void apply();
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/ui/ManifestFileConfiguration.java b/java/compiler/openapi/src/com/intellij/packaging/ui/ManifestFileConfiguration.java
new file mode 100644
index 0000000..116d68d
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/ui/ManifestFileConfiguration.java
@@ -0,0 +1,103 @@
+/*
+ * 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 com.intellij.packaging.ui;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author nik
+ */
+public class ManifestFileConfiguration {
+  private final boolean myWritable;
+  private List<String> myClasspath = new ArrayList<String>();
+  private String myMainClass;
+  private String myManifestFilePath;
+
+  public ManifestFileConfiguration(@NotNull ManifestFileConfiguration configuration) {
+    myWritable = configuration.isWritable();
+    myClasspath.addAll(configuration.getClasspath());
+    myMainClass = configuration.getMainClass();
+    myManifestFilePath = configuration.getManifestFilePath();
+  }
+
+  public ManifestFileConfiguration(@NotNull String manifestFilePath, @Nullable List<String> classpath, @Nullable String mainClass, boolean isWritable) {
+    myWritable = isWritable;
+    if (classpath != null) {
+      myClasspath.addAll(classpath);
+    }
+    myMainClass = mainClass;
+    myManifestFilePath = manifestFilePath;
+  }
+
+  public List<String> getClasspath() {
+    return myClasspath;
+  }
+
+  public boolean isWritable() {
+    return myWritable;
+  }
+
+  public void setClasspath(List<String> classpath) {
+    myClasspath = classpath;
+  }
+
+  public String getMainClass() {
+    return myMainClass;
+  }
+
+  public void setMainClass(String mainClass) {
+    myMainClass = mainClass;
+  }
+
+  public String getManifestFilePath() {
+    return myManifestFilePath;
+  }
+
+  public void setManifestFilePath(String manifestFilePath) {
+    myManifestFilePath = manifestFilePath;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (!(o instanceof ManifestFileConfiguration)) return false;
+
+    ManifestFileConfiguration that = (ManifestFileConfiguration)o;
+
+    if (!myClasspath.equals(that.myClasspath)) return false;
+    if (myMainClass != null ? !myMainClass.equals(that.myMainClass) : that.myMainClass != null) return false;
+    if (myManifestFilePath != null ? !myManifestFilePath.equals(that.myManifestFilePath) : that.myManifestFilePath != null) return false;
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    throw new UnsupportedOperationException();
+  }
+
+  public void addToClasspath(List<String> classpath) {
+    for (String path : classpath) {
+      if (!myClasspath.contains(path)) {
+        myClasspath.add(path);
+      }
+    }
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingElementPresentation.java b/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingElementPresentation.java
new file mode 100644
index 0000000..d26375c
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingElementPresentation.java
@@ -0,0 +1,23 @@
+/*
+ * 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 com.intellij.packaging.ui;
+
+/**
+ * @author nik
+ */
+public abstract class PackagingElementPresentation extends TreeNodePresentation {
+
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingElementPropertiesPanel.java b/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingElementPropertiesPanel.java
new file mode 100644
index 0000000..739677e
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingElementPropertiesPanel.java
@@ -0,0 +1,29 @@
+/*
+ * 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 com.intellij.packaging.ui;
+
+import com.intellij.openapi.options.UnnamedConfigurable;
+
+/**
+ * @author nik
+ */
+public abstract class PackagingElementPropertiesPanel implements UnnamedConfigurable {
+
+  public abstract void apply();
+
+  public void disposeUIResources() {
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingElementWeights.java b/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingElementWeights.java
new file mode 100644
index 0000000..f193e9b
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingElementWeights.java
@@ -0,0 +1,33 @@
+/*
+ * 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 com.intellij.packaging.ui;
+
+/**
+ * @author nik
+ */
+public class PackagingElementWeights {
+  public static final int ARTIFACT = 100;
+  public static final int DIRECTORY = 50;
+  public static final int DIRECTORY_COPY = 40;
+  public static final int EXTRACTED_DIRECTORY = 39;
+  public static final int LIBRARY = 30;
+  public static final int MODULE = 20;
+  public static final int FACET = 10;
+  public static final int FILE_COPY = 0;
+
+  private PackagingElementWeights() {
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingSourceItem.java b/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingSourceItem.java
new file mode 100644
index 0000000..d222876
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingSourceItem.java
@@ -0,0 +1,57 @@
+/*
+ * 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 com.intellij.packaging.ui;
+
+import com.intellij.packaging.elements.PackagingElement;
+import com.intellij.packaging.elements.PackagingElementOutputKind;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author nik
+ */
+public abstract class PackagingSourceItem {
+  private boolean myProvideElements;
+
+  protected PackagingSourceItem() {
+    this(true);
+  }
+
+  protected PackagingSourceItem(boolean provideElements) {
+    myProvideElements = provideElements;
+  }
+
+  @Override
+  public abstract boolean equals(Object obj);
+
+  @Override
+  public abstract int hashCode();
+
+  public abstract SourceItemPresentation createPresentation(@NotNull ArtifactEditorContext context);
+
+  @NotNull
+  public abstract List<? extends PackagingElement<?>> createElements(@NotNull ArtifactEditorContext context);
+
+  public boolean isProvideElements() {
+    return myProvideElements;
+  }
+
+  @NotNull
+  public PackagingElementOutputKind getKindOfProducedElements() {
+    return PackagingElementOutputKind.OTHER;
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingSourceItemsProvider.java b/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingSourceItemsProvider.java
new file mode 100644
index 0000000..3b62092
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/ui/PackagingSourceItemsProvider.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.
+ */
+package com.intellij.packaging.ui;
+
+import com.intellij.packaging.artifacts.Artifact;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+
+/**
+ * @author nik
+ */
+public abstract class PackagingSourceItemsProvider {
+  public static final ExtensionPointName<PackagingSourceItemsProvider> EP_NAME = ExtensionPointName.create("com.intellij.packaging.sourceItemProvider");
+
+  @NotNull
+  public abstract Collection<? extends PackagingSourceItem> getSourceItems(@NotNull ArtifactEditorContext editorContext, @NotNull Artifact artifact,
+                                                                                 @Nullable PackagingSourceItem parent);
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/ui/SourceItemPresentation.java b/java/compiler/openapi/src/com/intellij/packaging/ui/SourceItemPresentation.java
new file mode 100644
index 0000000..42de0ae
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/ui/SourceItemPresentation.java
@@ -0,0 +1,22 @@
+/*
+ * 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 com.intellij.packaging.ui;
+
+/**
+ * @author nik
+ */
+public abstract class SourceItemPresentation extends TreeNodePresentation {
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/ui/SourceItemWeights.java b/java/compiler/openapi/src/com/intellij/packaging/ui/SourceItemWeights.java
new file mode 100644
index 0000000..70e05f0
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/ui/SourceItemWeights.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 com.intellij.packaging.ui;
+
+/**
+ * @author nik
+ */
+public class SourceItemWeights {
+  public static final int ARTIFACTS_GROUP_WEIGHT = 200;
+  public static final int ARTIFACT_WEIGHT = 150;
+  public static final int MODULE_GROUP_WEIGHT = 100;
+  public static final int MODULE_WEIGHT = 50;
+  public static final int MODULE_OUTPUT_WEIGHT = 30;
+  public static final int LIBRARY_WEIGHT = 10;
+
+  private SourceItemWeights() {
+  }
+}
diff --git a/java/compiler/openapi/src/com/intellij/packaging/ui/TreeNodePresentation.java b/java/compiler/openapi/src/com/intellij/packaging/ui/TreeNodePresentation.java
new file mode 100644
index 0000000..96fe6ec
--- /dev/null
+++ b/java/compiler/openapi/src/com/intellij/packaging/ui/TreeNodePresentation.java
@@ -0,0 +1,57 @@
+/*
+ * 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 com.intellij.packaging.ui;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import com.intellij.ide.projectView.PresentationData;
+import com.intellij.ui.SimpleTextAttributes;
+
+/**
+ * @author nik
+ */
+public abstract class TreeNodePresentation {
+  public abstract String getPresentableName();
+
+  public String getSearchName() {
+    return getPresentableName();
+  }
+
+  public abstract void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes,
+                              SimpleTextAttributes commentAttributes);
+
+  @Nullable
+  public String getTooltipText() {
+    return null;
+  }
+
+  public boolean canNavigateToSource() {
+    return false;
+  }
+
+  public void navigateToSource() {
+  }
+
+  /**
+   * @deprecated this method is not used and will be removed in the future
+   */
+  @Nullable
+  public Object getSourceObject() {
+    return null;
+  }
+
+  public abstract int getWeight();
+}