Snapshot of commit d5ec1d5018ed24f1b4f32b1d09df6dbd7e2fc425

from branch master of git://git.jetbrains.org/idea/community.git
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;
+  }
+}