Merge remote-tracking branch 'origin/master'
authorAlexander Lobas <Alexander.Lobas@jetbrains.com>
Mon, 30 Jan 2012 13:55:45 +0000 (17:55 +0400)
committerAlexander Lobas <Alexander.Lobas@jetbrains.com>
Mon, 30 Jan 2012 13:55:45 +0000 (17:55 +0400)
71 files changed:
java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java
java/java-impl/src/com/intellij/psi/impl/JavaCodeBlockModificationListener.java
java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/ResolveClassUtil.java
java/java-psi-impl/src/com/intellij/psi/scope/util/PsiScopesUtil.java
jps/jps-builders/jps-builders.iml
jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java
jps/jps-builders/testSrc/org/jetbrains/ether/IncrementalTestCase.java
lib/dev/hamcrest-api-1.0.jar [deleted file]
lib/dev/hamcrest-core-1.1.jar [new file with mode: 0644]
lib/dev/hamcrest-library-1.0.jar [deleted file]
lib/dev/hamcrest-library-1.1.jar [new file with mode: 0644]
lib/dev/jmock-2.5.1.jar [moved from lib/dev/jmock-2.3.0-RC2.jar with 54% similarity]
lib/dev/jmock-junit4-2.3.0-RC2.jar [deleted file]
lib/dev/jmock-junit4-2.5.1.jar [new file with mode: 0644]
lib/dev/jmock-legacy-2.5.1.jar [new file with mode: 0644]
platform/lang-impl/src/com/intellij/execution/ui/layout/actions/CloseViewAction.java
platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerContentUi.java
platform/lang-impl/src/com/intellij/ide/actions/ViewStructureAction.java
platform/lang-impl/src/com/intellij/ide/util/FileStructurePopup.java
platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java
platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java
platform/platform-api/src/com/intellij/ui/treeStructure/filtered/FilteringTreeBuilder.java
platform/platform-impl/src/com/intellij/internal/statistic/persistence/ApplicationStatisticsPersistenceComponent.java
platform/testFramework/src/com/intellij/testFramework/FileStructureTestBase.java [new file with mode: 0644]
platform/util/src/com/intellij/util/containers/ConcurrentHashSet.java
platform/util/src/com/intellij/util/io/PagedFileStorage.java
platform/util/src/com/intellij/util/io/PersistentEnumeratorDelegate.java
platform/vcs-api/src/com/intellij/openapi/vcs/persistent/SmallMapSerializer.java
plugins/InspectionGadgets/src/com/siyeh/ig/style/StringBufferReplaceableByStringInspection.java
plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/SimpleStringBuffer.after.java [new file with mode: 0644]
plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/SimpleStringBuffer.java [new file with mode: 0644]
plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/StartsWithPrimitive.after.java [new file with mode: 0644]
plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/StartsWithPrimitive.java [new file with mode: 0644]
plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/StringBufferVariable.after.java [new file with mode: 0644]
plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/StringBufferVariable.java [new file with mode: 0644]
plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/StringBuilderAppend.after.java [new file with mode: 0644]
plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/StringBuilderAppend.java [new file with mode: 0644]
plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/style/StringBufferReplaceableWithStringFixTest.java [new file with mode: 0644]
plugins/git4idea/src/git4idea/branch/GitBranchOperation.java
plugins/git4idea/src/git4idea/branch/GitBranchOperationsProcessor.java
plugins/git4idea/src/git4idea/branch/GitCheckoutNewBranchOperation.java
plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java
plugins/git4idea/src/git4idea/branch/GitDeleteBranchOperation.java
plugins/git4idea/src/git4idea/commands/GitHandler.java
plugins/git4idea/src/git4idea/history/browser/CherryPicker.java
plugins/git4idea/src/git4idea/push/GitPushRejectedDetector.java
plugins/git4idea/src/git4idea/repo/GitRepositoryManager.java
plugins/git4idea/src/git4idea/ui/GitCommitListWithDiffPanel.java
plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java
plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java
plugins/git4idea/src/git4idea/ui/branch/GitMultiRootBranchConfig.java
plugins/git4idea/tests/git4idea/branch/GitBranchOperationsTest.java
plugins/gradle/resources/icons/gradle.png
plugins/gradle/src/META-INF/plugin.xml
plugins/gradle/src/org/jetbrains/plugins/gradle/bootstrap/GradleBootstrap.java
plugins/gradle/src/org/jetbrains/plugins/gradle/diff/GradleDiffUtil.java
plugins/gradle/src/org/jetbrains/plugins/gradle/diff/GradleLibraryDependencyStructureChangesCalculator.java
plugins/gradle/src/org/jetbrains/plugins/gradle/diff/GradleModuleStructureChangesCalculator.java
plugins/gradle/src/org/jetbrains/plugins/gradle/diff/GradleProjectStructureChangesCalculator.java
plugins/gradle/src/org/jetbrains/plugins/gradle/diff/GradleProjectStructureHelper.java [new file with mode: 0644]
plugins/gradle/src/org/jetbrains/plugins/gradle/diff/GradleProjectStructureHelperImpl.java [new file with mode: 0644]
plugins/gradle/src/org/jetbrains/plugins/gradle/diff/GradleStructureChangesCalculator.java
plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureChangeListener.java
plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureChangesModel.java
plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureChangesPanel.java
plugins/gradle/src/org/jetbrains/plugins/gradle/task/GradleResolveProjectTask.java
plugins/gradle/testSources/org/jetbrains/plugins/gradle/sync/GradleProjectStructureChangesModelTest.groovy [new file with mode: 0644]
plugins/gradle/testSources/org/jetbrains/plugins/gradle/testutil/AbstractProjectBuilder.groovy [new file with mode: 0644]
plugins/gradle/testSources/org/jetbrains/plugins/gradle/testutil/ChangeBuilder.groovy [new file with mode: 0644]
plugins/gradle/testSources/org/jetbrains/plugins/gradle/testutil/GradleProjectBuilder.groovy [new file with mode: 0644]
plugins/gradle/testSources/org/jetbrains/plugins/gradle/testutil/IntellijProjectBuilder.groovy [new file with mode: 0644]

index dccdb06284941e4a1dbd50999ece086d83e58cbe..ee4b5eae23a9e8df723bc9e909439aa566833913 100644 (file)
@@ -186,7 +186,7 @@ public abstract class ImportClassFixBase<T extends PsiElement & PsiReference> im
         && (JspPsiUtil.isInJspFile(psiFile) ?
             CodeInsightSettings.getInstance().JSP_ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY :
             CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY)
-        && codeAnalyzer.canChangeFileSilently(psiFile)
+        && (ApplicationManager.getApplication().isUnitTestMode() || codeAnalyzer.canChangeFileSilently(psiFile))
         && !autoImportWillInsertUnexpectedCharacters(classes[0])
       ) {
       CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
index d3aed97675032f7c142a21506c118d3c0e38b85d..ba8fffebe15cc48f4b041920da558bbca8cafb49 100644 (file)
@@ -68,7 +68,9 @@ public class JavaCodeBlockModificationListener implements PsiTreeChangePreproces
   private void processChange(final PsiElement parent, final PsiElement child1, final PsiElement child2) {
     try {
       if (!isInsideCodeBlock(parent)) {
-        if (parent != null && isClassOwner(parent.getContainingFile()) || isClassOwner(child1) || isClassOwner(child2) || isSourceDir(parent)) {
+        if (parent != null && isClassOwner(parent.getContainingFile()) ||
+            isClassOwner(child1) || isClassOwner(child2) || isSourceDir(parent) ||
+            (parent != null && isClassOwner(parent.getParent()))) {
           myModificationTracker.incCounter();
         }
         else {
index 1e3be2dfc81f85b082fcd10c9ae41548dbd75322..1ced09018224ba9c19ff38f72ea594af1f0f6c40 100644 (file)
@@ -21,10 +21,12 @@ import com.intellij.psi.*;
 import com.intellij.psi.impl.source.PsiJavaCodeReferenceElementImpl;
 import com.intellij.psi.scope.util.PsiScopesUtil;
 import com.intellij.psi.util.PsiUtil;
+import org.jetbrains.annotations.Nullable;
 
 public class ResolveClassUtil {
   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.resolve.ResolveClassUtil");
 
+  @Nullable
   public static PsiClass resolveClass(PsiJavaCodeReferenceElement ref) {
     if (ref instanceof PsiJavaCodeReferenceElementImpl && ((PsiJavaCodeReferenceElementImpl)ref).getKind() == PsiJavaCodeReferenceElementImpl.CLASS_IN_QUALIFIED_NEW_KIND){
       PsiElement parent = ref.getParent();
index 016e9782e9d7186bf54f5e86ff98a26af2e7fbba..d44097d038d40adb445382f24899dddca8e77266 100644 (file)
@@ -138,7 +138,7 @@ public class PsiScopesUtil {
     }
   }
 
-  public static boolean resolveAndWalk(PsiScopeProcessor processor, PsiJavaCodeReferenceElement ref, PsiElement maxScope) {
+  public static boolean resolveAndWalk(PsiScopeProcessor processor, PsiJavaCodeReferenceElement ref, @Nullable PsiElement maxScope) {
     return resolveAndWalk(processor, ref, maxScope, false);
   }
 
index bcbc6d67133d4c82cd75b5b83f07cc6f576e0d35..2a02bef74b73cb370eab7d2356522f1cb1eaae17 100644 (file)
@@ -34,7 +34,7 @@
     <orderEntry type="module" module-name="java-runtime" scope="RUNTIME" />
     <orderEntry type="library" name="Log4J" level="project" />
     <orderEntry type="library" name="JUnit4" level="project" />
-    <orderEntry type="module-library">
+    <orderEntry type="module-library" scope="TEST">
       <library>
         <CLASSES>
           <root url="jar://$MODULE_DIR$/../lib/junit-addons-1.4.jar!/" />
index cfba0719cf258562872691ab5f1abf8e0034c982..931bc55ca63613d6ad01e71de22f98d0e4efa464 100644 (file)
@@ -390,6 +390,8 @@ public class IncProjectBuilder {
     final BuildDataManager dataManager = context.getDataManager();
     final boolean compilingTests = context.isCompilingTests();
     try {
+      final Collection<String> allOutputs = new LinkedList<String>();
+
       context.processFilesToRecompile(chunk, new FileProcessor() {
         private final Map<Module, SourceToOutputMapping> storageMap = new HashMap<Module, SourceToOutputMapping>();
 
@@ -403,24 +405,11 @@ public class IncProjectBuilder {
           final String srcPath = FileUtil.toSystemIndependentName(file.getPath());
           final Collection<String> outputs = srcToOut.getState(srcPath);
 
-          if (LOG.isDebugEnabled()) {
-            if (outputs != null && context.isMake()) {
-              LOG.info("Cleaning output files:");
-              final String[] buffer = new String[outputs.size()];
-              int i = 0;
-              for (String output : outputs) {
-                buffer[i++] = output;
-              }
-              Arrays.sort(buffer);
-              for (String output : buffer) {
-                LOG.info(output);
-              }
-              LOG.info("End of files");
-            }
-          }
-
           if (outputs != null) {
             for (String output : outputs) {
+              if (LOG.isDebugEnabled()) {
+                allOutputs.add(output);
+              }
               FileUtil.delete(new File(output));
             }
             srcToOut.remove(srcPath);
@@ -428,6 +417,22 @@ public class IncProjectBuilder {
           return true;
         }
       });
+
+      if (LOG.isDebugEnabled()) {
+        if (context.isMake() && allOutputs.size() > 0) {
+          LOG.info("Cleaning output files:");
+          final String[] buffer = new String[allOutputs.size()];
+          int i = 0;
+          for (String output : allOutputs) {
+            buffer[i++] = output;
+          }
+          Arrays.sort(buffer);
+          for (String output : buffer) {
+            LOG.info(output);
+          }
+          LOG.info("End of files");
+        }
+      }
     }
     catch (Exception e) {
       throw new ProjectBuildException(e);
index d2e63772a39e6869fd36419d90ca5e6926c0c716..d0ea121136254fa283adfa037d45493d3f66d801 100644 (file)
@@ -59,7 +59,61 @@ public abstract class IncrementalTestCase extends TestCase {
       return s;
     }
   } 
-  
+
+  static {
+    Logger.setFactory(new Logger.Factory() {
+          @Override
+          public Logger getLoggerInstance(String category) {
+            final org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(category);
+
+            final boolean affectedLogger = category.equals("#org.jetbrains.jps.incremental.java.JavaBuilder") ||
+                                           category.equals("#org.jetbrains.jps.incremental.IncProjectBuilder");
+
+            return new Logger() {
+              @Override
+              public boolean isDebugEnabled() {
+                return affectedLogger;
+              }
+
+              @Override
+              public void debug(@NonNls String message) {
+              }
+
+              @Override
+              public void debug(@Nullable Throwable t) {
+              }
+
+              @Override
+              public void debug(@NonNls String message, @Nullable Throwable t) {
+              }
+
+              @Override
+              public void error(@NonNls String message, @Nullable Throwable t, @NonNls String... details) {
+              }
+
+              @Override
+              public void info(@NonNls String message) {
+                if (affectedLogger) {
+                  logger.info(stripper.strip(message));
+                }
+              }
+
+              @Override
+              public void info(@NonNls String message, @Nullable Throwable t) {
+              }
+
+              @Override
+              public void warn(@NonNls String message, @Nullable Throwable t) {
+              }
+
+              @Override
+              public void setLevel(Level level) {
+              }
+            };
+          }
+        });
+  }
+
   private static RootStripper stripper = new RootStripper();
   
   private final String groupName;
@@ -198,61 +252,6 @@ public abstract class IncrementalTestCase extends TestCase {
     properties.setProperty("log4j.appender.A1.layout.ConversionPattern", "%m%n");
 
     PropertyConfigurator.configure(properties);
-
-    Logger.setFactory(new Logger.Factory() {
-      @Override
-      public Logger getLoggerInstance(String category) {
-        final org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(category);
-
-        final boolean affectedLogger = category.equals("#org.jetbrains.jps.incremental.java.JavaBuilder") ||
-                                       category.equals("#org.jetbrains.jps.incremental.IncProjectBuilder");
-        
-        final String root = getWorkDir() + File.separator;
-        final int pos = root.length();
-
-        return new Logger() {
-          @Override
-          public boolean isDebugEnabled() {
-            return affectedLogger;
-          }
-
-          @Override
-          public void debug(@NonNls String message) {
-          }
-
-          @Override
-          public void debug(@Nullable Throwable t) {
-          }
-
-          @Override
-          public void debug(@NonNls String message, @Nullable Throwable t) {
-          }
-
-          @Override
-          public void error(@NonNls String message, @Nullable Throwable t, @NonNls String... details) {
-          }
-
-          @Override
-          public void info(@NonNls String message) {
-            if (affectedLogger) {
-              logger.info(stripper.strip(message));
-            }
-          }
-
-          @Override
-          public void info(@NonNls String message, @Nullable Throwable t) {
-          }
-
-          @Override
-          public void warn(@NonNls String message, @Nullable Throwable t) {
-          }
-
-          @Override
-          public void setLevel(Level level) {
-          }
-        };
-      }
-    });
   }
 
   public void doTest() throws Exception {
diff --git a/lib/dev/hamcrest-api-1.0.jar b/lib/dev/hamcrest-api-1.0.jar
deleted file mode 100644 (file)
index 5df3f19..0000000
Binary files a/lib/dev/hamcrest-api-1.0.jar and /dev/null differ
diff --git a/lib/dev/hamcrest-core-1.1.jar b/lib/dev/hamcrest-core-1.1.jar
new file mode 100644 (file)
index 0000000..5f1d5ce
Binary files /dev/null and b/lib/dev/hamcrest-core-1.1.jar differ
diff --git a/lib/dev/hamcrest-library-1.0.jar b/lib/dev/hamcrest-library-1.0.jar
deleted file mode 100644 (file)
index e0b275f..0000000
Binary files a/lib/dev/hamcrest-library-1.0.jar and /dev/null differ
diff --git a/lib/dev/hamcrest-library-1.1.jar b/lib/dev/hamcrest-library-1.1.jar
new file mode 100644 (file)
index 0000000..40610c9
Binary files /dev/null and b/lib/dev/hamcrest-library-1.1.jar differ
similarity index 54%
rename from lib/dev/jmock-2.3.0-RC2.jar
rename to lib/dev/jmock-2.5.1.jar
index 77aef4b1915683700dc38a8c0eb20ceb0428e577..4415dfbc94f8515bef4495976bcdb0fa5e6b5981 100644 (file)
Binary files a/lib/dev/jmock-2.3.0-RC2.jar and b/lib/dev/jmock-2.5.1.jar differ
diff --git a/lib/dev/jmock-junit4-2.3.0-RC2.jar b/lib/dev/jmock-junit4-2.3.0-RC2.jar
deleted file mode 100644 (file)
index cfb2fbd..0000000
Binary files a/lib/dev/jmock-junit4-2.3.0-RC2.jar and /dev/null differ
diff --git a/lib/dev/jmock-junit4-2.5.1.jar b/lib/dev/jmock-junit4-2.5.1.jar
new file mode 100644 (file)
index 0000000..fb3697a
Binary files /dev/null and b/lib/dev/jmock-junit4-2.5.1.jar differ
diff --git a/lib/dev/jmock-legacy-2.5.1.jar b/lib/dev/jmock-legacy-2.5.1.jar
new file mode 100644 (file)
index 0000000..c26cc0d
Binary files /dev/null and b/lib/dev/jmock-legacy-2.5.1.jar differ
index cb18ab9d58400f59fd0a78b363925dc5d88d3369..54ccf41abb3bd7891f05d8e270857e3766ca49e6 100644 (file)
@@ -29,16 +29,20 @@ public class CloseViewAction extends BaseViewAction {
   private static final Icon HOVERED_ICON = IconLoader.getIcon("/actions/closeNewHovered.png");
 
   protected void update(final AnActionEvent e, final ViewContext context, final Content[] content) {
-    setEnabled(e, isEnabled(context, content, e.getPlace()));
+    setEnabled(e, isEnabled(content));
     e.getPresentation().setIcon(ICON);
     e.getPresentation().setHoveredIcon(HOVERED_ICON);
   }
 
   protected void actionPerformed(final AnActionEvent e, final ViewContext context, final Content[] content) {
-    context.getContentManager().removeContent(content[0], context.isToDisposeRemovedContent());
+    perform(context, content[0]);
   }
 
-  public static boolean isEnabled(ViewContext context, Content[] content, String place) {
+  public static boolean perform(ViewContext context, Content content) {
+    return context.getContentManager().removeContent(content, context.isToDisposeRemovedContent());
+  }
+
+  public static boolean isEnabled(Content[] content) {
     return content.length == 1 && content[0].isCloseable();
   }
   
index 5846a58bd543bca221331cb478ec7041fdd3a6c3..988721d50064e11f3ea560df3cf1a711eab71a20 100644 (file)
@@ -18,6 +18,7 @@ package com.intellij.execution.ui.layout.impl;
 
 import com.intellij.execution.ui.RunnerLayoutUi;
 import com.intellij.execution.ui.layout.*;
+import com.intellij.execution.ui.layout.actions.CloseViewAction;
 import com.intellij.execution.ui.layout.actions.RestoreViewAction;
 import com.intellij.ide.DataManager;
 import com.intellij.openapi.Disposable;
@@ -56,6 +57,7 @@ import org.jetbrains.annotations.Nullable;
 import javax.swing.*;
 import javax.swing.border.EmptyBorder;
 import java.awt.*;
+import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
@@ -236,6 +238,19 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac
         }
       }
     });
+    myTabs.addTabMouseListener(new MouseAdapter() {
+      @Override
+      public void mousePressed(MouseEvent e) {
+        if (UIUtil.isCloseClick(e)) {
+          final TabInfo tabInfo = myTabs.findInfo(e);
+          final GridImpl grid = getGridFor(tabInfo);
+          final Content[] contents = grid != null ? CONTENT_KEY.getData(grid) : null;
+          if (contents != null && CloseViewAction.isEnabled(contents)) {
+            CloseViewAction.perform(RunnerContentUi.this, contents[0]);
+          }
+        }
+      }
+    });
 
     if (myOriginal != null) {
       final ContentManager manager = ContentFactory.SERVICE.getInstance().createContentManager(this, false, myProject);
index a1e115a7f0141278774eeafdd43d4a1fdb905360..4e64b7c3983c3079cfd0e542d083c8ec06e945dd 100644 (file)
@@ -90,7 +90,7 @@ public class ViewStructureAction extends AnAction {
   }
   
   @Nullable
-  private static FileStructurePopup createPopup(final Editor editor, Project project, Navigatable navigatable, final FileEditor fileEditor) {
+  public static FileStructurePopup createPopup(final Editor editor, Project project, @Nullable Navigatable navigatable, final FileEditor fileEditor) {
     final StructureViewBuilder structureViewBuilder = fileEditor.getStructureViewBuilder();
     if (structureViewBuilder == null) return null;
     StructureView structureView = structureViewBuilder.createStructureView(fileEditor, project);
index 2285504fe806780f4143970c7227ef3c95b9a55b..9d885b1e49d8444373a55afe5be95360ddb19f78 100644 (file)
@@ -149,6 +149,7 @@ public class FileStructurePopup implements Disposable {
     myTree = new JBTreeWithHintProvider(new DefaultMutableTreeNode(myTreeStructure.getRootElement())) {
       @Override
       protected PsiElement getPsiElementForHint(Object selectedValue) {
+        //noinspection ConstantConditions
         return getPsi((FilteringTreeStructure.FilteringNode)((DefaultMutableTreeNode)selectedValue).getUserObject());
       }
     };
@@ -218,12 +219,13 @@ public class FileStructurePopup implements Disposable {
         return current.isEmpty() ? null : findClosestTo(myInitialPsiElement, current);
       }
 
+      @Nullable
       private Object findClosestTo(PsiElement path, ArrayList<ObjectWithWeight> paths) {
         if (path == null || myInitialPsiElement == null) {
           return paths.get(0).node;
         }
         final Set<PsiElement> parents = getAllParents(myInitialPsiElement);
-        Object cur = paths.get(0).node;
+        ArrayList<TreePath> cur = new ArrayList<TreePath>();
         int max = -1;
         for (ObjectWithWeight p : paths) {
           final Object last = ((TreePath)p.node).getLastPathComponent();
@@ -238,14 +240,21 @@ public class FileStructurePopup implements Disposable {
             final int size = ContainerUtil.intersection(parents, elements).size();
             if (size > max) {
               max = size;
-              cur = p.node;
-            } else if (size == max && size == parents.size()) {
-              cur = p.node;
+              cur.clear();
+              cur.add((TreePath)p.node);
+            } else if (size == max) {
+              cur.add((TreePath)p.node);
             }
           }
         }
 
-        return cur;
+        Collections.sort(cur, new Comparator<TreePath>() {
+          @Override
+          public int compare(TreePath o1, TreePath o2) {
+            return o2.getPathCount() - o1.getPathCount();
+          }
+        });
+        return cur.isEmpty() ? null : cur.get(0);
       }
 
       class ObjectWithWeight {
@@ -285,7 +294,7 @@ public class FileStructurePopup implements Disposable {
     mySpeedSearch.setComparator(new SpeedSearchComparator(false, true));
 
     final FileStructurePopupFilter filter = new FileStructurePopupFilter();
-    myFilteringStructure = new FilteringTreeStructure(filter, myTreeStructure, false);
+    myFilteringStructure = new FilteringTreeStructure(filter, myTreeStructure, ApplicationManager.getApplication().isUnitTestMode());
     myAbstractTreeBuilder = new FilteringTreeBuilder(myTree, filter, myFilteringStructure, null) {
       @Override
       protected boolean validateNode(Object child) {
@@ -385,35 +394,37 @@ public class FileStructurePopup implements Disposable {
         });
       }
     });
-    final Alarm alarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD, myPopup);
-    alarm.addRequest(new Runnable() {
-      String filter = "";
+    if (!ApplicationManager.getApplication().isUnitTestMode()) {
+      final Alarm alarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD, myPopup);
+      alarm.addRequest(new Runnable() {
+        String filter = "";
 
-      @Override
-      public void run() {
-        alarm.cancelAllRequests();
-        String prefix = mySpeedSearch.getEnteredPrefix();
-        myTree.getEmptyText().setText(StringUtil.isEmpty(prefix) ? "Nothing to show" : "Can't find '" + prefix + "'");
-        if (prefix == null) prefix = "";
-
-        if (!filter.equals(prefix)) {
-          filter = prefix;
-          myAbstractTreeBuilder.refilter(null, false, false).doWhenProcessed(new Runnable() {
-            @Override
-            public void run() {
-              myTree.repaint();
-              //if (mySpeedSearch.isPopupActive()) {
-              //  mySpeedSearch.refreshSelection();
-              //}
-            }
-          });
+        @Override
+        public void run() {
+          alarm.cancelAllRequests();
+          String prefix = mySpeedSearch.getEnteredPrefix();
+          myTree.getEmptyText().setText(StringUtil.isEmpty(prefix) ? "Nothing to show" : "Can't find '" + prefix + "'");
+          if (prefix == null) prefix = "";
+
+          if (!filter.equals(prefix)) {
+            filter = prefix;
+            myAbstractTreeBuilder.refilter(null, false, false).doWhenProcessed(new Runnable() {
+              @Override
+              public void run() {
+                myTree.repaint();
+                //if (mySpeedSearch.isPopupActive()) {
+                //  mySpeedSearch.refreshSelection();
+                //}
+              }
+            });
+          }
+          alarm.addRequest(this, 300);
         }
-        alarm.addRequest(this, 300);
-      }
-    }, 300);
+      }, 300);
+    }
   }
 
-  private void selectPsiElement(PsiElement element) {
+  public void selectPsiElement(PsiElement element) {
     Set<PsiElement> parents = getAllParents(element);
 
     FilteringTreeStructure.FilteringNode node = (FilteringTreeStructure.FilteringNode)myAbstractTreeBuilder.getRootElement();
@@ -478,7 +489,7 @@ public class FileStructurePopup implements Disposable {
   }
 
   @Nullable
-  protected PsiElement getCurrentElement(@Nullable final PsiFile psiFile) {
+  public PsiElement getCurrentElement(@Nullable final PsiFile psiFile) {
     if (psiFile == null) return null;
 
     PsiDocumentManager.getInstance(myProject).commitAllDocuments();
@@ -718,6 +729,18 @@ public class FileStructurePopup implements Disposable {
     myTitle = title;
   }
 
+  public Tree getTree() {
+    return myTree;
+  }
+
+  public TreeSpeedSearch getSpeedSearch() {
+    return mySpeedSearch;
+  }
+
+  public FilteringTreeBuilder getTreeBuilder() {
+    return myAbstractTreeBuilder;
+  }
+
   private class FileStructurePopupFilter implements ElementFilter {
     private String myLastFilter = null;
     private HashSet<Object> myVisibleParents = new HashSet<Object>();
index 050f1efad0693c6a6cd73d7ce7dafac887307f66..7baf631f9661108d6a1d4f792ff52d27d5d6a986 100644 (file)
@@ -132,6 +132,8 @@ public class InjectedLanguageUtil {
       if (file == null || !file.isPhysical() && file.getOriginalFile() == file) return;
     }
 
+    if (containingFile.getViewProvider() instanceof InjectedFileViewProvider) return; // no injection inside injection
+
     PsiElement inTree = loadTree(host, containingFile);
     if (inTree != host) {
       host = inTree;
index ced3ab62d05d315966c51a478ef3688562919e0a..43423a3599326a448abfe5bbde0bc7795a08f4d9 100644 (file)
@@ -2362,7 +2362,7 @@ public class AbstractTreeUi {
 
     final ActionCallback done = new ActionCallback();
 
-    invokeLaterIfNeeded(new Runnable() {
+    final Runnable cancelUpdate = new Runnable() {
       public void run() {
         if (isReleased()) {
           done.setRejected();
@@ -2371,21 +2371,29 @@ public class AbstractTreeUi {
 
         if (myResettingToReadyNow.get()) {
           _getReady().notify(done);
-        } else if (isReady()) {
+        }
+        else if (isReady()) {
           resetToReadyNow();
           done.setDone();
-        } else {
+        }
+        else {
           if (isIdle() && hasPendingWork()) {
             resetToReadyNow();
             done.setDone();
-          } else {
+          }
+          else {
             _getReady().notify(done);
           }
         }
 
         maybeReady();
       }
-    }, false);
+    };
+    if (ApplicationManager.getApplication().isUnitTestMode()) {
+      cancelUpdate.run();
+    } else {
+      invokeLaterIfNeeded(cancelUpdate, false);
+    }
 
     if (isEdt() || isPassthroughMode()) {
       maybeReady();
index 941dafef44eb61aca12372b84131fd2199cf600a..71f152e5fc58af4845f139fe97598e0927522bbf 100644 (file)
@@ -18,6 +18,7 @@ package com.intellij.ui.treeStructure.filtered;
 import com.intellij.ide.util.treeView.AbstractTreeBuilder;
 import com.intellij.ide.util.treeView.AbstractTreeStructure;
 import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.util.ActionCallback;
 import com.intellij.openapi.util.Disposer;
 import com.intellij.ui.speedSearch.ElementFilter;
@@ -120,7 +121,7 @@ public class FilteringTreeBuilder extends AbstractTreeBuilder {
       myRefilterQueue.cancelAllUpdates();
     }
     final ActionCallback callback = new ActionCallback();
-    getUi().cancelUpdate().doWhenProcessed(new Runnable() {
+    final Runnable afterCancelUpdate = new Runnable() {
       @Override
       public void run() {
         if (myRefilterQueue == null || now) {
@@ -145,7 +146,12 @@ public class FilteringTreeBuilder extends AbstractTreeBuilder {
           });
         }
       }
-    });
+    };
+    if (!ApplicationManager.getApplication().isUnitTestMode()) {
+      getUi().cancelUpdate().doWhenProcessed(afterCancelUpdate);
+    } else {
+      afterCancelUpdate.run();
+    }
 
     return callback;
   }
@@ -155,7 +161,7 @@ public class FilteringTreeBuilder extends AbstractTreeBuilder {
     final ActionCallback selectionDone = new ActionCallback();
 
     getFilteredStructure().refilter();
-    queueUpdate().doWhenProcessed(new Runnable() {
+    final Runnable selectionRunnable = new Runnable() {
       public void run() {
         revalidateTree();
 
@@ -173,25 +179,36 @@ public class FilteringTreeBuilder extends AbstractTreeBuilder {
                 selectionDone.setDone();
               }
             });
-          } else {
+          }
+          else {
             TreeUtil.ensureSelection(myTree);
             selectionDone.setDone();
           }
-        } else {
+        }
+        else {
           selectionDone.setDone();
         }
       }
-    });
+    };
+    if (!ApplicationManager.getApplication().isUnitTestMode()) {
+      queueUpdate().doWhenProcessed(selectionRunnable);
+    } else {
+      selectionRunnable.run();
+    }
 
     final ActionCallback result = new ActionCallback();
 
     selectionDone.doWhenDone(new Runnable() {
       public void run() {
-        scrollSelectionToVisible(new Runnable() {
-          public void run() {
-            getReady(this).notify(result);
-          }
-        }, false);
+        if (!ApplicationManager.getApplication().isUnitTestMode()) {
+          scrollSelectionToVisible(new Runnable() {
+            public void run() {
+              getReady(this).notify(result);
+            }
+          }, false);
+        } else {
+          result.setDone();
+        }
       }
     }).doWhenRejected(new Runnable() {
       @Override
index 5521c228621709d23d52aba7edfb1d49f702fc5d..319cd705803a3621783bb4ec6f009f7600e788b4 100644 (file)
@@ -19,7 +19,6 @@ package com.intellij.internal.statistic.persistence;
 import com.intellij.ide.AppLifecycleListener;
 import com.intellij.internal.statistic.AbstractApplicationUsagesCollector;
 import com.intellij.internal.statistic.UsagesCollector;
-import com.intellij.internal.statistic.beans.ConvertUsagesUtil;
 import com.intellij.internal.statistic.beans.GroupDescriptor;
 import com.intellij.internal.statistic.beans.UsageDescriptor;
 import com.intellij.openapi.application.ApplicationManager;
@@ -29,6 +28,7 @@ import com.intellij.openapi.components.PersistentStateComponent;
 import com.intellij.openapi.components.State;
 import com.intellij.openapi.components.Storage;
 import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.project.DumbService;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.project.ProjectManager;
 import com.intellij.openapi.project.ProjectManagerListener;
@@ -218,6 +218,7 @@ public class ApplicationStatisticsPersistenceComponent extends ApplicationStatis
   }
 
   private static void doPersistProjectUsages(@NotNull Project project) {
+    if (DumbService.isDumb(project)) return;
     for (UsagesCollector usagesCollector : Extensions.getExtensions(UsagesCollector.EP_NAME)) {
       if (usagesCollector instanceof AbstractApplicationUsagesCollector) {
         ((AbstractApplicationUsagesCollector)usagesCollector).persistProjectUsages(project);
diff --git a/platform/testFramework/src/com/intellij/testFramework/FileStructureTestBase.java b/platform/testFramework/src/com/intellij/testFramework/FileStructureTestBase.java
new file mode 100644 (file)
index 0000000..3f991b0
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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.testFramework;
+
+import com.intellij.ide.actions.ViewStructureAction;
+import com.intellij.ide.util.FileStructurePopup;
+import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase;
+import com.intellij.ui.TreeSpeedSearch;
+import com.intellij.ui.treeStructure.Tree;
+import com.intellij.ui.treeStructure.filtered.FilteringTreeBuilder;
+import com.intellij.ui.treeStructure.filtered.FilteringTreeStructure;
+import com.intellij.util.ui.tree.TreeUtil;
+import junit.framework.Assert;
+import org.junit.Before;
+
+import java.io.File;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public abstract class FileStructureTestBase extends CodeInsightFixtureTestCase {
+  FileStructurePopup myPopup;
+
+  @Before
+  public void setUp() throws Exception {
+    super.setUp();
+    myFixture.configureByFile(getFileName(getFileExtension()));
+    myPopup = ViewStructureAction.createPopup(myFixture.getEditor(),
+                                              myFixture.getProject(),
+                                              null,
+                                              TextEditorProvider.getInstance().getTextEditor(myFixture.getEditor()));
+    update();
+  }
+
+  protected abstract String getFileExtension();
+
+  @Override
+  public void tearDown() throws Exception {
+    Disposer.dispose(myPopup);
+    super.tearDown();
+  }
+
+  private String getFileName(String ext) {
+    return getTestName(false) + (StringUtil.isEmpty(ext) ? "" : "." + ext);
+  }
+
+  protected String getTreeFileName() {
+    return getFileName("tree");
+  }
+
+  protected void checkTree() throws Exception {
+    final String expected = FileUtil.loadFile(new File(getTestDataPath() + "/" + getTreeFileName()));
+    Assert.assertEquals(expected, PlatformTestUtil.print(getTree(), true));
+  }
+
+
+  private void update() throws InterruptedException {
+    myPopup.getTreeBuilder().refilter().doWhenProcessed(new Runnable() {
+      @Override
+      public void run() {
+        getStructure().rebuild();
+        updateTree();
+        TreeUtil.expandAll(getTree());
+        myPopup.selectPsiElement(getFile());
+      }
+    });
+  }
+
+  protected Tree getTree() {
+    return myPopup.getTree();
+  }
+
+  protected FilteringTreeBuilder getBuilder() {
+    return myPopup.getTreeBuilder();
+  }
+
+  protected TreeSpeedSearch getSpeedSearch() {
+    return myPopup.getSpeedSearch();
+  }
+
+
+  protected void updateTree() {
+    updateRecursively(getRootNode());
+  }
+
+  protected FilteringTreeStructure getStructure() {
+    final FilteringTreeStructure structure = (FilteringTreeStructure)getBuilder().getTreeStructure();
+    assert structure != null;
+    return structure;
+  }
+
+  protected FilteringTreeStructure.FilteringNode getRootNode() {
+    return (FilteringTreeStructure.FilteringNode)getStructure().getRootElement();
+  }
+
+  protected void updateRecursively(final FilteringTreeStructure.FilteringNode node) {
+    node.update();
+    for (FilteringTreeStructure.FilteringNode child : node.children()) {
+      updateRecursively(child);
+    }
+  }
+}
index a792361c4cf267d19047f29e646312d7d66d321f..3db9efffae82f44622a433853939ede0be183c32 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * 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.
@@ -92,5 +92,10 @@ public class ConcurrentHashSet<K> implements Set<K> {
   public void clear() {
     map.clear();
   }
+
+  @Override
+  public String toString() {
+    return map.keySet().toString();
+  }
 }
 
index a8acc9b212193496344886661dd41a9642726ed9..4cf6dfbde294a4ffc09725b987bd94e2b0acdd4c 100644 (file)
@@ -49,7 +49,7 @@ public class PagedFileStorage implements Forceable {
 
   static {
     final int lower = 100;
-    final int upper = SystemInfo.is64Bit ? 500 : 200;
+    final int upper = SystemInfo.is64Bit && !PersistentEnumeratorDelegate.useBtree() ? 500 : 200;
 
     BUFFER_SIZE = Math.max(1, SystemInfo.getIntProperty("idea.paged.storage.page.size", 10)) * MB;
     if (ByteBufferWrapper.NO_MMAP) {
index c7c689eedae58177959a1ab67db7294722d7ea2b..7dc5f0a802dae24923bc5ee9d86f327c3e039687 100644 (file)
@@ -16,7 +16,6 @@
 package com.intellij.util.io;
 
 import com.intellij.openapi.Forceable;
-import com.intellij.openapi.util.SystemInfo;
 import org.jetbrains.annotations.Nullable;
 
 import java.io.Closeable;
@@ -32,7 +31,7 @@ public class PersistentEnumeratorDelegate<Data> implements Closeable, Forceable
                    new PersistentEnumerator<Data>(file, dataDescriptor, initialSize);
   }
 
-  private boolean useBtree() {
+  static boolean useBtree() {
     String property = System.getProperty("idea.use.btree");
     return !"false".equals(property);
   }
index 004dee277a1b954651c40912edd036944d83e383..41d28da4acb89918a28d58058f981bcfc7d2699f 100644 (file)
@@ -20,7 +20,9 @@ import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream;
 import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.util.io.DataExternalizer;
+import com.intellij.util.io.DataOutputStream;
 import com.intellij.util.io.KeyDescriptor;
+import com.intellij.util.io.UnsyncByteArrayInputStream;
 import org.jetbrains.annotations.NotNull;
 
 import java.io.*;
@@ -51,7 +53,7 @@ public class SmallMapSerializer<K,V> implements Forceable {
   private void init() {
     try {
       final byte[] bytes = FileUtil.loadFileBytes(myFile);
-      final DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes));
+      final DataInputStream dis = new DataInputStream(new UnsyncByteArrayInputStream(bytes));
       final int size = dis.readInt();
       for (int i = 0; i < size; i++) {
         final KeyWrapper<K> keyWrapper = new KeyWrapper<K>(myKeyDescriptor, myKeyDescriptor.read(dis));
index 38eed03f36cd78c5fba3ecb229e206bc480c6f14..a7e13174cc6f99b9923132eed6861d5d07e00fac 100644 (file)
@@ -66,7 +66,8 @@ public class StringBufferReplaceableByStringInspection extends BaseInspection {
     public String getName() {
       if (isStringBuilder) {
         return InspectionGadgetsBundle.message("string.builder.replaceable.by.string.quickfix");
-      } else {
+      }
+      else {
         return InspectionGadgetsBundle.message("string.buffer.replaceable.by.string.quickfix");
       }
     }
@@ -110,20 +111,16 @@ public class StringBufferReplaceableByStringInspection extends BaseInspection {
         final PsiNewExpression newExpression = (PsiNewExpression)initializer;
         final PsiExpressionList argumentList = newExpression.getArgumentList();
         final PsiExpression[] arguments = argumentList.getExpressions();
-        if (arguments.length == 0) {
-          result.append("\"\"");
-        } else if (arguments.length == 1) {
+        if (arguments.length == 1) {
           final PsiExpression argument = arguments[0];
           final PsiType type = argument.getType();
-          if (PsiType.INT.equals(type)) {
-            result.append("\"\"");
-          } else {
+          if (!PsiType.INT.equals(type)) {
             result.append(argument.getText());
           }
-        } else {
-          return null;
         }
-      } else if (initializer instanceof PsiMethodCallExpression) {
+        return result;
+      }
+      else if (initializer instanceof PsiMethodCallExpression) {
         final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)initializer;
         final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
         final PsiExpression qualifier = methodExpression.getQualifierExpression();
@@ -131,20 +128,34 @@ public class StringBufferReplaceableByStringInspection extends BaseInspection {
         if (result == null) {
           return null;
         }
-        if (!"toString".equals(methodExpression.getReferenceName())) {
+        if ("toString".equals(methodExpression.getReferenceName())) {
+          if (result.length() == 0) {
+            result.append("\"\"");
+          }
+        }
+        else {
           final PsiExpressionList argumentList = methodCallExpression.getArgumentList();
           final PsiExpression[] arguments = argumentList.getExpressions();
           if (arguments.length != 1) {
             return null;
           }
           final PsiExpression argument = arguments[0];
-          result.append('+');
-          result.append(argument.getText());
+          if (result.length() != 0) {
+            result.append('+').append(argument.getText());
+          }
+          else {
+            final PsiType type = argument.getType();
+            if (type instanceof PsiPrimitiveType) {
+              result.append("String.valueOf(").append(argument.getText()).append(")");
+            }
+            else {
+              result.append(argument.getText());
+            }
+          }
         }
-      } else {
-        return null;
+        return result;
       }
-      return result;
+      return null;
     }
   }
 
@@ -261,7 +272,7 @@ public class StringBufferReplaceableByStringInspection extends BaseInspection {
       }
       completeExpression = grandParent;
       if (found) {
-        return (PsiExpression) completeExpression;
+        return (PsiExpression)completeExpression;
       }
     }
     return null;
diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/SimpleStringBuffer.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/SimpleStringBuffer.after.java
new file mode 100644 (file)
index 0000000..f948a96
--- /dev/null
@@ -0,0 +1,7 @@
+package com.siyeh.igfixes.style.replace_with_string;
+
+public class SimpleStringBuffer {
+  String foo() {
+    return "";
+  }
+}
diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/SimpleStringBuffer.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/SimpleStringBuffer.java
new file mode 100644 (file)
index 0000000..77902d4
--- /dev/null
@@ -0,0 +1,7 @@
+package com.siyeh.igfixes.style.replace_with_string;
+
+public class SimpleStringBuffer {
+  String foo() {
+    return new <caret>StringBuffer().toString();
+  }
+}
diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/StartsWithPrimitive.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/StartsWithPrimitive.after.java
new file mode 100644 (file)
index 0000000..89721d3
--- /dev/null
@@ -0,0 +1,6 @@
+class StartsWithPrimitive {
+
+  String foo(int i) {
+    return String.valueOf(i);
+  }
+}
\ No newline at end of file
diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/StartsWithPrimitive.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/StartsWithPrimitive.java
new file mode 100644 (file)
index 0000000..c0f4d87
--- /dev/null
@@ -0,0 +1,6 @@
+class StartsWithPrimitive {
+
+  String foo(int i) {
+    return new Stri<caret>ngBuffer().append(i).toString();
+  }
+}
\ No newline at end of file
diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/StringBufferVariable.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/StringBufferVariable.after.java
new file mode 100644 (file)
index 0000000..9f7b9e2
--- /dev/null
@@ -0,0 +1,6 @@
+class StringBufferVariable {
+  void foo() {
+    String sb = "asdf" + "asdf";
+    System.out.println(sb.toString());
+  }
+}
\ No newline at end of file
diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/StringBufferVariable.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/StringBufferVariable.java
new file mode 100644 (file)
index 0000000..27b16ee
--- /dev/null
@@ -0,0 +1,6 @@
+class StringBufferVariable {
+  void foo() {
+    StringBuffer <caret>sb = new StringBuffer("asdf").append("asdf");
+    System.out.println(sb.toString());
+  }
+}
\ No newline at end of file
diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/StringBuilderAppend.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/StringBuilderAppend.after.java
new file mode 100644 (file)
index 0000000..8282bb2
--- /dev/null
@@ -0,0 +1,5 @@
+class StringBuilderAppend {
+  String foo(int i) {
+    return "test: " + i;
+  }
+}
\ No newline at end of file
diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/StringBuilderAppend.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/replace_with_string/StringBuilderAppend.java
new file mode 100644 (file)
index 0000000..ff584cf
--- /dev/null
@@ -0,0 +1,5 @@
+class StringBuilderAppend {
+  String foo(int i) {
+    return new Strin<caret>gBuilder().append("test: ").append(i).toString();
+  }
+}
\ No newline at end of file
diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/style/StringBufferReplaceableWithStringFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/style/StringBufferReplaceableWithStringFixTest.java
new file mode 100644 (file)
index 0000000..0d03db2
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2012 Bas Leijdekkers
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.siyeh.ig.fixes.style;
+
+import com.siyeh.InspectionGadgetsBundle;
+import com.siyeh.ig.IGQuickFixesTestCase;
+import com.siyeh.ig.style.StringBufferReplaceableByStringInspection;
+
+public class StringBufferReplaceableWithStringFixTest extends IGQuickFixesTestCase {
+
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+    myFixture.enableInspections(new StringBufferReplaceableByStringInspection());
+    myRelativePath = "style/replace_with_string";
+    myDefaultHint = InspectionGadgetsBundle.message("string.buffer.replaceable.by.string.quickfix");
+  }
+
+  public void testSimpleStringBuffer() { doTest(); }
+  public void testStringBuilderAppend() { doTest("StringBuilderAppend", InspectionGadgetsBundle.message("string.builder.replaceable.by.string.quickfix")); }
+  public void testStringBufferVariable() { doTest(); }
+  public void testStartsWithPrimitive() { doTest(); }
+}
index 3a4177a529d2a1491bd9439d599f915ef5bf0519..1116915687cd3989ab8b0ca05bbcb52405617a1d 100644 (file)
@@ -37,6 +37,8 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import static com.intellij.openapi.util.text.StringUtil.pluralize;
+
 /**
  * Common class for Git operations with branches aware of multi-root configuration,
  * which means showing combined error information, proposing to rollback, etc.
@@ -154,7 +156,7 @@ abstract class GitBranchOperation {
     UIUtil.invokeAndWaitIfNeeded(new Runnable() {
       @Override
       public void run() {
-        String description = message + getRollbackProposal();
+        String description = "<html>" + message + ".<br/>" + getRollbackProposal() + "</html>";
         ok.set(Messages.OK ==
                MessageManager.showYesNoDialog(myProject, description, title, "Rollback", "Don't rollback", Messages.getErrorIcon()));
       }
@@ -190,11 +192,16 @@ abstract class GitBranchOperation {
     }
   }
 
+  @NotNull
+  protected String repositories() {
+    return pluralize("repository", getSuccessfulRepositories().size());
+  }
+
   private void showUnmergedFilesDialogWithRollback() {
     final AtomicBoolean ok = new AtomicBoolean();
     UIUtil.invokeAndWaitIfNeeded(new Runnable() {
       @Override public void run() {
-        String description = "You have to resolve all merge conflicts before checkout.<br/>" + getRollbackProposal();
+        String description = "<html>You have to resolve all merge conflicts before checkout.<br/>" + getRollbackProposal() + "</html>";
         // suppressing: this message looks ugly if capitalized by words
         //noinspection DialogTitleCapitalization
         ok.set(Messages.OK == MessageManager.showYesNoDialog(myProject, description, UNMERGED_FILES_ERROR_TITLE, "Rollback", "Don't rollback", Messages.getErrorIcon()));
index 392554efbcf8ec123252bfd056d3d6829a66592f..df1989fdc3de51152928663829e5294465519c01 100644 (file)
@@ -38,7 +38,6 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
@@ -52,7 +51,7 @@ public final class GitBranchOperationsProcessor {
   private static final Logger LOG = Logger.getInstance(GitBranchOperationsProcessor.class);
 
   private final Project myProject;
-  private final Collection<GitRepository> myRepositories;
+  private final List<GitRepository> myRepositories;
   @Nullable private final Runnable myCallInAwtAfterExecution;
   private final GitRepository mySelectedRepository;
 
@@ -61,16 +60,16 @@ public final class GitBranchOperationsProcessor {
   }
   
   public GitBranchOperationsProcessor(@NotNull GitRepository repository, @Nullable Runnable callInAwtAfterExecution) {
-    this(repository.getProject(), Collections.singleton(repository), repository, callInAwtAfterExecution);
+    this(repository.getProject(), Collections.singletonList(repository), repository, callInAwtAfterExecution);
   }
 
-  public GitBranchOperationsProcessor(@NotNull Project project, @NotNull Collection<GitRepository> repositories,
+  public GitBranchOperationsProcessor(@NotNull Project project, @NotNull List<GitRepository> repositories,
                                       @NotNull GitRepository selectedRepository) {
     this(project, repositories, selectedRepository, null);
   }
 
   public GitBranchOperationsProcessor(@NotNull Project project,
-                                      @NotNull Collection<GitRepository> repositories,
+                                      @NotNull List<GitRepository> repositories,
                                       @NotNull GitRepository selectedRepository,
                                       @Nullable Runnable callInAwtAfterExecution) {
     myProject = project;
@@ -205,7 +204,7 @@ public final class GitBranchOperationsProcessor {
     }.runInBackground();
   }
 
-  private GitCommitCompareInfo loadCommitsToCompare(Collection<GitRepository> repositories, String branchName) {
+  private GitCommitCompareInfo loadCommitsToCompare(List<GitRepository> repositories, String branchName) {
     GitCommitCompareInfo compareInfo = new GitCommitCompareInfo();
     for (GitRepository repository : repositories) {
       compareInfo.put(repository, loadCommitsToCompare(repository, branchName));
index fb6b50305e38b0def9ef27fdc102a3548173ba15..28396fdde88c72d3ea4ef20072af30ba4e720c96 100644 (file)
@@ -93,7 +93,7 @@ public class GitCheckoutNewBranchOperation extends GitBranchOperation {
   @NotNull
   @Override
   protected String getRollbackProposal() {
-    return "However checkout has succeeded for the following repositories:<br/>" +
+    return "However checkout has succeeded for the following " + repositories() + ":<br/>" +
            successfulRepositoriesJoined() +
            "<br/>You may rollback (checkout back to " + myPreviousBranch + ") not to let branches diverge.";
   }
index 966e26487b5d6adc523f0d5e83285032bdc16035..9be5f1e1f0e9696e7ad5fc6080c8f42a5aa26bc7 100644 (file)
@@ -23,7 +23,6 @@ import com.intellij.openapi.ui.VerticalFlowLayout;
 import com.intellij.openapi.util.Clock;
 import com.intellij.openapi.util.Condition;
 import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vcs.VcsException;
 import com.intellij.openapi.vcs.changes.Change;
 import com.intellij.openapi.vcs.changes.ChangeListManager;
@@ -55,6 +54,7 @@ import javax.swing.*;
 import java.util.*;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import static com.intellij.openapi.util.text.StringUtil.*;
 import static git4idea.commands.GitMessageWithFilesDetector.Event.LOCAL_CHANGES_OVERWRITTEN_BY_CHECKOUT;
 import static git4idea.commands.GitMessageWithFilesDetector.Event.UNTRACKED_FILES_OVERWRITTEN_BY;
 import static git4idea.util.GitUIUtil.code;
@@ -203,7 +203,7 @@ public class GitCheckoutOperation extends GitBranchOperation {
     String description = UntrackedFilesNotifier.createUntrackedFilesOverwrittenDescription("checkout", true);
 
     final SelectFilesDialog dialog = new UntrackedFilesDialog(myProject, new ArrayList<VirtualFile>(untrackedFiles),
-                                                              StringUtil.stripHtml(description, true));
+                                                              stripHtml(description, true));
     dialog.setTitle(title);
     UIUtil.invokeAndWaitIfNeeded(new Runnable() {
       @Override
@@ -238,7 +238,7 @@ public class GitCheckoutOperation extends GitBranchOperation {
   @NotNull
   @Override
   protected String getRollbackProposal() {
-    return "However checkout has succeeded for the following repositories:<br/>" +
+    return "However checkout has succeeded for the following " + repositories() + ":<br/>" +
            successfulRepositoriesJoined() +
            "<br/>You may rollback (checkout back to " + myPreviousBranch + ") not to let branches diverge.";
   }
@@ -376,7 +376,7 @@ public class GitCheckoutOperation extends GitBranchOperation {
       LOG.info("Couldn't save local changes", e);
       notifyError("Couldn't save uncommitted changes.",
                   String.format("Tried to save uncommitted changes in %s before checkout, but failed with an error.<br/>%s",
-                                saver.getSaverName(), StringUtil.join(e.getMessages())));
+                                saver.getSaverName(), join(e.getMessages())));
       return false;
     }
   }
index 6c22234e051fc76789e7f33ace539a32973d79fc..dd75f9342d9666b2f66ccce5dd750870df1ee25f 100644 (file)
@@ -137,7 +137,7 @@ class GitDeleteBranchOperation extends GitBranchOperation {
   @NotNull
   @Override
   protected String getRollbackProposal() {
-    return "However branch deletion has succeeded for the following repositories.:<br/>" +
+    return "However branch deletion has succeeded for the following " + repositories() + ":<br/>" +
            successfulRepositoriesJoined() +
            "<br/>You may rollback (recreate " + myBranchName + " in these roots) not to let branches diverge.";
   }
index 79e0779f14d86859f9399602bf8b7447e746f40d..dd7ac999af35f2afd909a37f481e8d1e698e0988 100644 (file)
@@ -502,8 +502,8 @@ public abstract class GitHandler {
   public void setSilent(final boolean silent) {
     checkNotStarted();
     mySilent = silent;
-    setStderrSuppressed(true);
-    setStdoutSuppressed(true);
+    setStderrSuppressed(silent);
+    setStdoutSuppressed(silent);
   }
 
   /**
index 9ce3a4615a401d3c231fcf5ac008a366c3773360..f76af153ddaf3dbcab8411756e8d73be222249fa 100644 (file)
@@ -148,8 +148,7 @@ public class CherryPicker {
 
     final Collection<FilePath> paths = ChangesUtil.getPaths(changes);
     String message = ce.getDefaultMessageFor(paths.toArray(new FilePath[paths.size()]));
-    message = (message == null) ? new StringBuilder().append(commit.getDescription()).append("(cherry picked from commit ")
-      .append(commit.getShortHash()).append(")").toString() : message;
+    message = (message == null) ? commit.getDescription() + " (cherry picked from commit " + commit.getShortHash() + ")" : message;
 
     myMessagesInOrder.add(message);
     myFilesToMove.put(message, paths);
index 0dc47f2cdeb86064cb1bb0a3d2b2d7f6fc6b30f2..ca96475cfeecdff017e7b49335d52f241b1c6c70 100644 (file)
@@ -28,7 +28,7 @@ import java.util.regex.Pattern;
  */
 class GitPushRejectedDetector implements GitLineHandlerListener {
 
-  private static final Pattern REJECTED_PATTERN = Pattern.compile("\\s+! \\[rejected\\]\\s+(\\w+) -> (\\w+) .*");
+  private static final Pattern REJECTED_PATTERN = Pattern.compile("\\s+! \\[rejected\\]\\s+(\\S+) -> (\\S+) .*");
   
   private final Collection<RejectedRef> myRejectedRefs = new ArrayList<RejectedRef>();
 
index fe5fc065c808a0bd8b05e0339ff2c9da1b01c065..eb6276183333cb7d91502ff7a2b6f2ab6fe61848 100644 (file)
@@ -33,6 +33,8 @@ import org.jetbrains.annotations.Nullable;
 import java.util.*;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import static git4idea.GitUtil.sortRepositories;
+
 /**
  * GitRepositoryManager initializes and stores {@link GitRepository GitRepositories} for Git roots defined in the project.
  * @author Kirill Likhodedov
@@ -152,10 +154,10 @@ public final class GitRepositoryManager extends AbstractProjectComponent impleme
    * @return all repositories tracked by the manager.
    */
   @NotNull
-  public Collection<GitRepository> getRepositories() {
+  public List<GitRepository> getRepositories() {
     try {
       REPO_LOCK.readLock().lock();
-      return myRepositories.values();
+      return sortRepositories(myRepositories.values());
     }
     finally {
       REPO_LOCK.readLock().unlock();
index 9616c7740db8862ee0e9d740f3363645f959c618..098c21c8302f30ac9354973311ffdf5d3ab0d6cd 100644 (file)
@@ -24,6 +24,7 @@ import git4idea.history.browser.GitCommit;
 import org.jetbrains.annotations.NotNull;
 
 import javax.swing.*;
+import java.awt.*;
 import java.util.Collections;
 import java.util.List;
 
@@ -35,24 +36,24 @@ import java.util.List;
  */
 public class GitCommitListWithDiffPanel extends JPanel {
 
-  private final Project myProject;
   private final ChangesBrowser myChangesBrowser;
   private final GitCommitListPanel myCommitListPanel;
 
-  public GitCommitListWithDiffPanel(Project project, List<GitCommit> commits) {
-    myProject = project;
+  public GitCommitListWithDiffPanel(@NotNull Project project, @NotNull List<GitCommit> commits) {
+    super(new BorderLayout());
 
-    myCommitListPanel = new GitCommitListPanel(myProject, commits);
+    myCommitListPanel = new GitCommitListPanel(project, commits);
     myCommitListPanel.addListSelectionListener(new Consumer<GitCommit>() {
       @Override public void consume(GitCommit commit) {
         myChangesBrowser.setChangesToDisplay(commit.getChanges());
       }
     });
 
-    myChangesBrowser = new ChangesBrowser(myProject, null, Collections.<Change>emptyList(), null, false, true, null, ChangesBrowser.MyUseCase.LOCAL_CHANGES, null);
+    myChangesBrowser = new ChangesBrowser(project, null, Collections.<Change>emptyList(), null, false, true, null, ChangesBrowser.MyUseCase.LOCAL_CHANGES, null);
     myCommitListPanel.registerDiffAction(myChangesBrowser.getDiffAction());
 
     Splitter splitter = new Splitter(false, 0.7f);
+    splitter.setHonorComponentsMinimumSize(false);
     splitter.setFirstComponent(myCommitListPanel);
     splitter.setSecondComponent(myChangesBrowser);
     add(splitter);
index 2e6309a2ed9b3f6479f38a1bb3ca7f5e56484a2c..c31352bbd3a27f85675dbb78930c5691465f8944 100644 (file)
@@ -39,7 +39,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.event.HyperlinkEvent;
-import java.util.Collection;
+import java.util.List;
 
 /**
  * <p>
@@ -65,12 +65,9 @@ class GitBranchPopup  {
   }
 
   /**
-   *
-   * @param project
    * @param currentRepository Current repository, which means the repository of the currently open or selected file.
    *                          In the case of synchronized branch operations current repository matter much less, but sometimes is used,
    *                          for example, it is preselected in the repositories combobox in the compare branches dialog.
-   * @return
    */
   static GitBranchPopup getInstance(@NotNull Project project, @NotNull GitRepository currentRepository) {
     return new GitBranchPopup(project, currentRepository);
@@ -157,7 +154,7 @@ class GitBranchPopup  {
   }
 
   private void fillWithCommonRepositoryActions(DefaultActionGroup popupGroup, GitRepositoryManager repositoryManager) {
-    Collection<GitRepository> repositories = repositoryManager.getRepositories();
+    List<GitRepository> repositories = repositoryManager.getRepositories();
     String currentBranch = myMultiRootBranchConfig.getCurrentBranch();
     assert currentBranch != null : "Current branch can't be null if branches have not diverged";
     popupGroup.add(new GitBranchPopupActions.CurrentBranchAction(currentBranch, " in all roots"));
index 42be940eaf37e29bd35fc83efa62710e1b7ec734..8f8bcb176f4fb8cfdfbb399e56c4d0205fa45429 100644 (file)
@@ -33,7 +33,6 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
@@ -56,7 +55,7 @@ class GitBranchPopupActions {
     DefaultActionGroup popupGroup = new DefaultActionGroup(null, false);
 
     popupGroup.addAction(new CurrentBranchAction(GitBranchUiUtil.getDisplayableBranchText(myRepository), "in root " + GitUIUtil.getShortRepositoryName(myRepository)));
-    popupGroup.addAction(new NewBranchAction(myProject, Collections.singleton(myRepository), myRepository));
+    popupGroup.addAction(new NewBranchAction(myProject, Collections.singletonList(myRepository), myRepository));
     popupGroup.addAction(new CheckoutRevisionActions(myProject, myRepository));
 
     if (toInsert != null) {
@@ -68,7 +67,7 @@ class GitBranchPopupActions {
     Collections.sort(localBranches);
     for (GitBranch localBranch : localBranches) {
       if (!localBranch.equals(myRepository.getCurrentBranch())) { // don't show current branch in the list
-        popupGroup.add(new LocalBranchActions(myProject, Collections.singleton(myRepository), localBranch.getName(), myRepository));
+        popupGroup.add(new LocalBranchActions(myProject, Collections.singletonList(myRepository), localBranch.getName(), myRepository));
       }
     }
 
@@ -76,7 +75,7 @@ class GitBranchPopupActions {
     List<GitBranch> remoteBranches = new ArrayList<GitBranch>(myRepository.getBranches().getRemoteBranches());
     Collections.sort(remoteBranches);
     for (GitBranch remoteBranch : remoteBranches) {
-      popupGroup.add(new RemoteBranchActions(myProject, Collections.singleton(myRepository), remoteBranch.getName(), myRepository));
+      popupGroup.add(new RemoteBranchActions(myProject, Collections.singletonList(myRepository), remoteBranch.getName(), myRepository));
     }
     
     return popupGroup;
@@ -101,10 +100,10 @@ class GitBranchPopupActions {
 
   static class NewBranchAction extends DumbAwareAction {
     private final Project myProject;
-    private final Collection<GitRepository> myRepositories;
+    private final List<GitRepository> myRepositories;
     @NotNull private final GitRepository mySelectedRepository;
 
-    NewBranchAction(@NotNull Project project, @NotNull Collection<GitRepository> repositories, @NotNull GitRepository selectedRepository) {
+    NewBranchAction(@NotNull Project project, @NotNull List<GitRepository> repositories, @NotNull GitRepository selectedRepository) {
       super("New Branch", "Create and checkout new branch", IconLoader.getIcon("/general/add.png"));
       myProject = project;
       myRepositories = repositories;
@@ -175,11 +174,11 @@ class GitBranchPopupActions {
   static class LocalBranchActions extends ActionGroup {
 
     private final Project myProject;
-    private final Collection<GitRepository> myRepositories;
+    private final List<GitRepository> myRepositories;
     private String myBranchName;
     @NotNull private final GitRepository mySelectedRepository;
 
-    LocalBranchActions(@NotNull Project project, @NotNull Collection<GitRepository> repositories, @NotNull String branchName,
+    LocalBranchActions(@NotNull Project project, @NotNull List<GitRepository> repositories, @NotNull String branchName,
                        @NotNull GitRepository selectedRepository) {
       super("", true);
       myProject = project;
@@ -202,11 +201,11 @@ class GitBranchPopupActions {
 
     private static class CheckoutAction extends DumbAwareAction {
       private final Project myProject;
-      private final Collection<GitRepository> myRepositories;
+      private final List<GitRepository> myRepositories;
       private final String myBranchName;
       @NotNull private final GitRepository mySelectedRepository;
 
-      CheckoutAction(@NotNull Project project, @NotNull Collection<GitRepository> repositories, @NotNull String branchName,
+      CheckoutAction(@NotNull Project project, @NotNull List<GitRepository> repositories, @NotNull String branchName,
                      @NotNull GitRepository selectedRepository) {
         super("Checkout");
         myProject = project;
@@ -224,11 +223,11 @@ class GitBranchPopupActions {
 
     private static class CheckoutAsNewBranch extends DumbAwareAction {
       private final Project myProject;
-      private final Collection<GitRepository> myRepositories;
+      private final List<GitRepository> myRepositories;
       private final String myBranchName;
       @NotNull private final GitRepository mySelectedRepository;
 
-      CheckoutAsNewBranch(@NotNull Project project, @NotNull Collection<GitRepository> repositories, @NotNull String branchName,
+      CheckoutAsNewBranch(@NotNull Project project, @NotNull List<GitRepository> repositories, @NotNull String branchName,
                           @NotNull GitRepository selectedRepository) {
         super("Checkout as new branch");
         myProject = project;
@@ -254,11 +253,11 @@ class GitBranchPopupActions {
      */
     private static class DeleteAction extends DumbAwareAction {
       private final Project myProject;
-      private final Collection<GitRepository> myRepositories;
+      private final List<GitRepository> myRepositories;
       private final String myBranchName;
       private final GitRepository mySelectedRepository;
 
-      DeleteAction(Project project, Collection<GitRepository> repositories, String branchName, GitRepository selectedRepository) {
+      DeleteAction(Project project, List<GitRepository> repositories, String branchName, GitRepository selectedRepository) {
         super("Delete");
         myProject = project;
         myRepositories = repositories;
@@ -279,11 +278,11 @@ class GitBranchPopupActions {
   static class RemoteBranchActions extends ActionGroup {
 
     private final Project myProject;
-    private final Collection<GitRepository> myRepositories;
+    private final List<GitRepository> myRepositories;
     private String myBranchName;
     @NotNull private final GitRepository mySelectedRepository;
 
-    RemoteBranchActions(@NotNull Project project, @NotNull Collection<GitRepository> repositories, @NotNull String branchName,
+    RemoteBranchActions(@NotNull Project project, @NotNull List<GitRepository> repositories, @NotNull String branchName,
                         @NotNull GitRepository selectedRepository) {
       super("", true);
       myProject = project;
@@ -304,11 +303,11 @@ class GitBranchPopupActions {
 
     private static class CheckoutRemoteBranchAction extends DumbAwareAction {
       private final Project myProject;
-      private final Collection<GitRepository> myRepositories;
+      private final List<GitRepository> myRepositories;
       private final String myRemoteBranchName;
       @NotNull private final GitRepository mySelectedRepository;
 
-      public CheckoutRemoteBranchAction(@NotNull Project project, @NotNull Collection<GitRepository> repositories,
+      public CheckoutRemoteBranchAction(@NotNull Project project, @NotNull List<GitRepository> repositories,
                                         @NotNull String remoteBranchName, @NotNull GitRepository selectedRepository) {
         super("Checkout as new local branch");
         myProject = project;
@@ -339,12 +338,12 @@ class GitBranchPopupActions {
   private static class CompareAction extends DumbAwareAction {
 
     private final Project myProject;
-    private final Collection<GitRepository> myRepositories;
+    private final List<GitRepository> myRepositories;
     private final String myBranchName;
     private final GitRepository mySelectedRepository;
 
     public CompareAction(@NotNull Project project,
-                         @NotNull Collection<GitRepository> repositories,
+                         @NotNull List<GitRepository> repositories,
                          @NotNull String branchName,
                          GitRepository selectedRepository) {
       super("Compare");
index 4e45aed7a5b4f2456c0c6195802c5b32d99f685a..c45b886bd1f2280723d3ae8c8a7581607ac48232 100644 (file)
@@ -24,15 +24,16 @@ import org.jetbrains.annotations.Nullable;
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 
 /**
  * @author Kirill Likhodedov
  */
 public class GitMultiRootBranchConfig {
   
-  private final Collection<GitRepository> myRepositories;
+  private final List<GitRepository> myRepositories;
 
-  public GitMultiRootBranchConfig(@NotNull Collection<GitRepository> repositories) {
+  public GitMultiRootBranchConfig(@NotNull List<GitRepository> repositories) {
     myRepositories = repositories;
   }
 
index d10e26f0f00011131fc0a204087c95b57ff2f56e..2209fdc834d9cccc28fd0617e039fb8a51ac86eb 100644 (file)
@@ -36,7 +36,7 @@ import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Arrays;
-import java.util.Collection;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import static git4idea.test.GitExec.*;
@@ -51,7 +51,7 @@ public class GitBranchOperationsTest extends AbstractVcsTestCase  {
   private static final String NEW_BRANCH = "new_branch";
   private static final String MASTER = "master";
 
-  private Collection<GitRepository> myRepositories;
+  private List<GitRepository> myRepositories;
   private GitRepository myUltimate;
   private GitRepository myCommunity;
   private GitRepository myContrib;
index b76b4ba5b8a8af9d9e526a708370b1c5d845e8bd..af8da119630b6f705720ff360e36dc2f7296d8b5 100644 (file)
Binary files a/plugins/gradle/resources/icons/gradle.png and b/plugins/gradle/resources/icons/gradle.png differ
index 1487b063dbc56fd1553707eda26e0806b638da1e..36d93560b32e92128894a848c43327471e4423da 100644 (file)
 
     <applicationService serviceImplementation="org.jetbrains.plugins.gradle.remote.GradleApiFacadeManager"/>
     <applicationService serviceImplementation="org.jetbrains.plugins.gradle.util.GradleLibraryManager"/>
-    <applicationService serviceImplementation="org.jetbrains.plugins.gradle.diff.GradleProjectStructureChangesCalculator"/>
+    <applicationService serviceInterface="org.jetbrains.plugins.gradle.diff.GradleStructureChangesCalculator"
+                        serviceImplementation="org.jetbrains.plugins.gradle.diff.GradleProjectStructureChangesCalculator"/>
+    <applicationService serviceImplementation="org.jetbrains.plugins.gradle.diff.GradleModuleStructureChangesCalculator"/>
+    <applicationService serviceImplementation="org.jetbrains.plugins.gradle.diff.GradleLibraryDependencyStructureChangesCalculator"/>
+    <applicationService serviceInterface="org.jetbrains.plugins.gradle.diff.GradleProjectStructureHelper"
+                        serviceImplementation="org.jetbrains.plugins.gradle.diff.GradleProjectStructureHelperImpl"/>
     <applicationService serviceInterface="org.jetbrains.plugins.gradle.notification.GradleProgressNotificationManager"
                         serviceImplementation="org.jetbrains.plugins.gradle.notification.GradleProgressNotificationManagerImpl"/>
 
index 762c18a47488c5a7ec26c856b00388769e009742..254145d26a659580e0cbd82a649f5d139411cfb4 100644 (file)
@@ -15,7 +15,7 @@ import org.jetbrains.plugins.gradle.ui.GradleIcons;
 import org.jetbrains.plugins.gradle.util.GradleBundle;
 
 /**
- * // TODO den add doc
+ * Encapsulates initialisation routine of the gradle integration.
  * 
  * @author Denis Zhdanov
  * @since 11/3/11 4:01 PM
@@ -42,10 +42,6 @@ public class GradleBootstrap extends AbstractProjectComponent {
   }
 
   private void initToolWindow() {
-    // TODO den don't show tool window if no gradle project is associated with the current project.
-    if (!Boolean.getBoolean("gradle.show.tool.window")) {
-      return;
-    }
     final ToolWindowManagerEx manager = ToolWindowManagerEx.getInstanceEx(myProject);
     ToolWindow toolWindow = manager.registerToolWindow(GRADLE_TOOL_WINDOW_ID, false, ToolWindowAnchor.RIGHT);
     toolWindow.setIcon(GradleIcons.GRADLE_ICON);
index bbafe4cd653a8585d7750f4112487a90b0aa632a..a6b51a7b820481f549c77660777c7773ada0a306 100644 (file)
@@ -25,11 +25,10 @@ public class GradleDiffUtil {
    * Example: particular module has been added at the gradle side. We want to mark that module, its content root(s), dependencies etc
    * as gradle-local changes.
    * 
-   * @param entity  target gradle-local entity
-   * @return        collection of gradle-local changes for the given entity and its interested sub-entities
+   * @param entity          target gradle-local entity
+   * @param currentChanges  holder for the changes built during the current call
    */
-  public static Set<GradleProjectStructureChange> buildLocalChanges(@NotNull GradleEntity entity) {
-    final Set<GradleProjectStructureChange> result = new HashSet<GradleProjectStructureChange>();
+  public static void buildLocalChanges(@NotNull GradleEntity entity, @NotNull final Set<GradleProjectStructureChange> currentChanges) {
     entity.invite(new GradleEntityVisitor() {
       @Override
       public void visit(@NotNull GradleProject project) {
@@ -38,7 +37,7 @@ public class GradleDiffUtil {
 
       @Override
       public void visit(@NotNull GradleModule module) {
-        result.add(new GradleModulePresenceChange(module, null));
+        currentChanges.add(new GradleModulePresenceChange(module, null));
         for (GradleDependency dependency : module.getDependencies()) {
           dependency.invite(this);
         }
@@ -61,79 +60,52 @@ public class GradleDiffUtil {
 
       @Override
       public void visit(@NotNull GradleLibraryDependency dependency) {
-        result.add(new GradleLibraryDependencyPresenceChange(dependency, null));
+        currentChanges.add(new GradleLibraryDependencyPresenceChange(dependency, null));
       }
     });
-    return result;
   }
 
   /**
    * Analogues to {@link #buildLocalChanges} but targets intellij entity.
    *
-   * @param module  target intellij-local module that doesn't present at the gradle side
-   * @return        collection of intellij-local changes for the given entity and its interested sub-entities
+   * @param module          target intellij-local module that doesn't present at the gradle side
+   * @param currentChanges  holder for the changes built during the current call
    */
-  public static Set<? extends GradleProjectStructureChange> buildLocalChanges(@NotNull Module module) {
-    Set<GradleProjectStructureChange> result = new HashSet<GradleProjectStructureChange>();
-    result.add(new GradleModulePresenceChange(null, module));
+  public static void buildLocalChanges(@NotNull Module module, @NotNull Set<GradleProjectStructureChange> currentChanges) {
+    currentChanges.add(new GradleModulePresenceChange(null, module));
     // TODO den process module sub-entities here (content roots and dependencies).
-    return result;
   }
 
   /**
    * Analogues to {@link #buildLocalChanges} but targets intellij entity.
    * 
    * @param libraryDependency  target intellij-local library dependency that doesn't present at the gradle side
-   * @return                   collection of intellij-local changes for the given entity and its interested sub-entities
+   * @param currentChanges     holder for the changes built during the current call
    */
-  public static Set<? extends GradleProjectStructureChange> buildLocalChanges(@NotNull LibraryOrderEntry libraryDependency) {
-    return Collections.singleton(new GradleLibraryDependencyPresenceChange(null, libraryDependency));
+  public static void buildLocalChanges(@NotNull LibraryOrderEntry libraryDependency,
+                                       @NotNull Set<GradleProjectStructureChange> currentChanges)
+  {
+    currentChanges.add(new GradleLibraryDependencyPresenceChange(null, libraryDependency));
   }
 
   /**
    * Performs argument type-based dispatch and delegates to one of strongly typed <code>'buildLocalChanges()'</code> methods.
    *
-   * @param entity  target intellij-local entity that doesn't present at the gradle side
-   * @return        collection of intellij-local changes for the given entity and its interested sub-entities
+   * @param entity          target intellij-local entity that doesn't present at the gradle side
+   * @param currentChanges  holder for the changes built during the current call
    */
-  @NotNull
-  public static Set<? extends GradleProjectStructureChange> buildLocalChanges(@NotNull Object entity) {
+  public static void buildLocalChanges(@NotNull Object entity,
+                                       @NotNull Set<GradleProjectStructureChange> currentChanges)
+  {
     if (entity instanceof GradleEntity) {
-      return buildLocalChanges((GradleEntity)entity);
+      buildLocalChanges((GradleEntity)entity, currentChanges);
     }
     else if (entity instanceof Module) {
-      return buildLocalChanges((Module)entity);
+      buildLocalChanges((Module)entity, currentChanges);
     }
     else if (entity instanceof LibraryOrderEntry) {
-      return buildLocalChanges((LibraryOrderEntry)entity);
-    }
-    else {
-      return Collections.emptySet();
-    }
-  }
-
-  /**
-   * Concatenates given entities into the single collection and returns it.
-   * <p/>
-   * The main idea behind this method is that most of the time we don't expect changes at all, hence, corresponding changes calculators
-   * can use {@link Collections#emptySet()}. However, if some sub-nodes do have changes, attempt
-   * to {@link Collection#addAll(Collection) merge} them within the empty set mentioned above would cause an exception.
-   * <p/>
-   * That's why we provide dedicated method for creating new collection as a merge result.
-   * 
-   * @param collections  collections to merge
-   * @return             merge result
-   */
-  @NotNull
-  public static Set<GradleProjectStructureChange> concatenate(Collection<? extends GradleProjectStructureChange>... collections) {
-    Set<GradleProjectStructureChange> result = null;
-    for (Collection<? extends GradleProjectStructureChange> collection : collections) {
-      if (result == null) {
-        result = new HashSet<GradleProjectStructureChange>();
-      }
-      result.addAll(collection);
+      buildLocalChanges((LibraryOrderEntry)entity, currentChanges);
     }
-    return result == null ? Collections.<GradleProjectStructureChange>emptySet() : result;
   }
 
   /**
@@ -146,39 +118,34 @@ public class GradleDiffUtil {
    * @param gradleEntities    entities available at the gradle side
    * @param intellijEntities  entities available at the intellij side
    * @param knownChanges      collection that contains known changes about the entities
+   * @param currentChanges    holder for the changes discovered during the current call
    * @param <I>               target intellij entity type
    * @param <G>               target gradle entity type
-   * @return                  set of changes between the given entity collections
    */
-  @NotNull
-  public static <I, G extends GradleEntity> Set<GradleProjectStructureChange> calculate(
+  public static <I, G extends GradleEntity> void calculate(
     @NotNull GradleStructureChangesCalculator<G, I> calculator,
     @NotNull Iterable<? extends G> gradleEntities,
     @NotNull Iterable<? extends I> intellijEntities,
-    @NotNull Set<GradleProjectStructureChange> knownChanges)
+    @NotNull Set<GradleProjectStructureChange> knownChanges,
+    @NotNull Set<GradleProjectStructureChange> currentChanges)
   {
-    Set<GradleProjectStructureChange> result = Collections.emptySet();
     Map<Object, I> intellijEntitiesByKeys = new HashMap<Object, I>();
     for (I entity : intellijEntities) {
-      final I previous = intellijEntitiesByKeys.put(calculator.getIntellijKey(entity, knownChanges), entity);
+      final I previous = intellijEntitiesByKeys.put(calculator.getIntellijKey(entity), entity);
       assert previous == null;
     }
     for (G gradleEntity: gradleEntities) {
       I intellijEntity = intellijEntitiesByKeys.remove(calculator.getGradleKey(gradleEntity, knownChanges));
-      Set<GradleProjectStructureChange> changesToMerge;
       if (intellijEntity == null) {
-        changesToMerge = buildLocalChanges(gradleEntity);
+        buildLocalChanges(gradleEntity, currentChanges);
       }
       else {
-        changesToMerge = calculator.calculate(gradleEntity, intellijEntity, knownChanges);
+        calculator.calculate(gradleEntity, intellijEntity, knownChanges, currentChanges);
       }
-      result = concatenate(result, changesToMerge);
     }
 
     for (I entity : intellijEntitiesByKeys.values()) {
-      result = concatenate(result, buildLocalChanges(entity));
+      buildLocalChanges(entity, currentChanges);
     }
-    
-    return result;
   }
 }
index 4c2490a48af3048f94e8cbd2851033f035d2f96d..077f58b88e90e4615adac734c9d0d2e09cb6827c 100644 (file)
@@ -4,7 +4,6 @@ import com.intellij.openapi.roots.LibraryOrderEntry;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.plugins.gradle.model.GradleLibraryDependency;
 
-import java.util.Collections;
 import java.util.Set;
 
 /**
@@ -14,20 +13,19 @@ import java.util.Set;
 public class GradleLibraryDependencyStructureChangesCalculator
   implements GradleStructureChangesCalculator<GradleLibraryDependency, LibraryOrderEntry>
 {
-  @NotNull
+
   @Override
-  public Set<GradleProjectStructureChange> calculate(@NotNull GradleLibraryDependency gradleEntity,
-                                                     @NotNull LibraryOrderEntry intellijEntity,
-                                                     @NotNull Set<GradleProjectStructureChange> knownChanges)
+  public void calculate(@NotNull GradleLibraryDependency gradleEntity,
+                        @NotNull LibraryOrderEntry intellijEntity,
+                        @NotNull Set<GradleProjectStructureChange> knownChanges,
+                        @NotNull Set<GradleProjectStructureChange> currentChanges)
   {
     // TODO den implement 
-    return Collections.emptySet();
   }
 
   @NotNull
   @Override
-  public Object getIntellijKey(@NotNull LibraryOrderEntry entity, @NotNull Set<GradleProjectStructureChange> knownChanges) {
-    // TODO den consider the known changes
+  public Object getIntellijKey(@NotNull LibraryOrderEntry entity) {
     final String result = entity.getLibraryName();
     return result == null ? "" : result;
   }
index 84c62ec4ff4216c32296f6821eeb6b3ba007329a..28f5ec482cba3da58ee1b6eb9af7e95c3708e774 100644 (file)
@@ -34,24 +34,30 @@ import java.util.Set;
  */
 public class GradleModuleStructureChangesCalculator implements GradleStructureChangesCalculator<GradleModule, Module> {
   
-  private final GradleLibraryDependencyStructureChangesCalculator myLibraryDependencyCalculator
-    = new GradleLibraryDependencyStructureChangesCalculator();
-  
-  @NotNull
+  private final GradleLibraryDependencyStructureChangesCalculator myLibraryDependencyCalculator;
+  private final GradleProjectStructureHelper myStructureHelper;
+
+  public GradleModuleStructureChangesCalculator(@NotNull GradleLibraryDependencyStructureChangesCalculator libraryDependencyCalculator,
+                                                @NotNull GradleProjectStructureHelper structureHelper)
+  {
+    myLibraryDependencyCalculator = libraryDependencyCalculator;
+    myStructureHelper = structureHelper;
+  }
+
   @Override
-  public Set<GradleProjectStructureChange> calculate(@NotNull GradleModule gradleEntity,
-                                                     @NotNull Module intellijEntity,
-                                                     @NotNull Set<GradleProjectStructureChange> knownChanges)
+  public void calculate(@NotNull GradleModule gradleEntity,
+                        @NotNull Module intellijEntity,
+                        @NotNull Set<GradleProjectStructureChange> knownChanges,
+                        @NotNull Set<GradleProjectStructureChange> currentChanges)
   {
     //TODO den process module-local settings
-    //TODO den process content roots 
-    return checkDependencies(gradleEntity, intellijEntity, knownChanges);
+    //TODO den process content roots
+    checkDependencies(gradleEntity, intellijEntity, knownChanges, currentChanges); 
   }
 
   @NotNull
   @Override
-  public Object getIntellijKey(@NotNull Module entity, @NotNull Set<GradleProjectStructureChange> knownChanges) {
-    // TODO den consider the known changes
+  public Object getIntellijKey(@NotNull Module entity) {
     return entity.getName();
   }
 
@@ -62,9 +68,10 @@ public class GradleModuleStructureChangesCalculator implements GradleStructureCh
     return entity.getName();
   }
 
-  private Set<GradleProjectStructureChange> checkDependencies(@NotNull GradleModule gradleModule,
-                                                              @NotNull Module intellijModule,
-                                                              @NotNull Set<GradleProjectStructureChange> knownChanges)
+  private void checkDependencies(@NotNull GradleModule gradleModule,
+                                 @NotNull Module intellijModule,
+                                 @NotNull Set<GradleProjectStructureChange> knownChanges,
+                                 @NotNull Set<GradleProjectStructureChange> currentChanges)
   {
     // Prepare intellij part.
     final List<ModuleOrderEntry> intellijModuleDependencies = new ArrayList<ModuleOrderEntry>();
@@ -82,11 +89,10 @@ public class GradleModuleStructureChangesCalculator implements GradleStructureCh
         return libraryOrderEntry;
       }
     };
-    final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(intellijModule);
-    for (OrderEntry orderEntry : moduleRootManager.getOrderEntries()) {
+    for (OrderEntry orderEntry : myStructureHelper.getOrderEntries(intellijModule)) {
       orderEntry.accept(policy, null);
     }
-    
+
     // Prepare gradle part.
     final List<GradleModuleDependency> gradleModuleDependencies = new ArrayList<GradleModuleDependency>();
     final List<GradleLibraryDependency> gradleLibraryDependencies = new ArrayList<GradleLibraryDependency>();
@@ -104,12 +110,10 @@ public class GradleModuleStructureChangesCalculator implements GradleStructureCh
     for (GradleDependency dependency : gradleModule.getDependencies()) {
       dependency.invite(visitor);
     }
-    
+
     // Calculate changes.
     // TODO den process module dependencies here as well.
-    final Set<GradleProjectStructureChange> libraryChanges
-      = GradleDiffUtil.calculate(myLibraryDependencyCalculator, gradleLibraryDependencies, intellijLibraryDependencies, knownChanges);
-    return libraryChanges;
-    
+    GradleDiffUtil.calculate(myLibraryDependencyCalculator, gradleLibraryDependencies, intellijLibraryDependencies,
+                             knownChanges, currentChanges);
   }
 }
index 9c099280cecfc28d5623bc1b65d6cc160ee0fb9e..ad9bc2827854c32f9e9f8534391e7797f617e2a5 100644 (file)
@@ -1,21 +1,15 @@
 package org.jetbrains.plugins.gradle.diff;
 
 import com.intellij.openapi.module.Module;
-import com.intellij.openapi.module.ModuleManager;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.LanguageLevelProjectExtension;
 import com.intellij.pom.java.LanguageLevel;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.plugins.gradle.model.GradleModule;
 import org.jetbrains.plugins.gradle.model.GradleProject;
 
-import java.util.Collections;
-import java.util.List;
+import java.util.Collection;
 import java.util.Set;
 
-import static java.util.Arrays.asList;
-import static org.jetbrains.plugins.gradle.diff.GradleDiffUtil.concatenate;
-
 /**
  * Encapsulates functionality of calculating changes between Gradle and IntelliJ IDEA project hierarchies.
  * <p/>
@@ -26,25 +20,31 @@ import static org.jetbrains.plugins.gradle.diff.GradleDiffUtil.concatenate;
  */
 public class GradleProjectStructureChangesCalculator implements GradleStructureChangesCalculator<GradleProject, Project> {
 
-  private final GradleModuleStructureChangesCalculator myModuleChangesCalculator = new GradleModuleStructureChangesCalculator();
+  private final GradleModuleStructureChangesCalculator myModuleChangesCalculator;
+  private final GradleProjectStructureHelper           myStructureHelper;
+
+  public GradleProjectStructureChangesCalculator(@NotNull GradleModuleStructureChangesCalculator moduleCalculator,
+                                                 @NotNull GradleProjectStructureHelper structureHelper) {
+    myModuleChangesCalculator = moduleCalculator;
+    myStructureHelper = structureHelper;
+  }
 
-  @NotNull
   @Override
-  public Set<GradleProjectStructureChange> calculate(@NotNull GradleProject gradleEntity,
-                                                     @NotNull Project intellijEntity,
-                                                     @NotNull Set<GradleProjectStructureChange> knownChanges)
+  public void calculate(@NotNull GradleProject gradleEntity,
+                        @NotNull Project intellijEntity,
+                        @NotNull Set<GradleProjectStructureChange> knownChanges,
+                        @NotNull Set<GradleProjectStructureChange> currentChanges)
   {
-    final Set<GradleProjectStructureChange> result = calculateProjectChanges(gradleEntity, intellijEntity, knownChanges);
+    calculateProjectChanges(gradleEntity, intellijEntity, currentChanges);
 
     final Set<? extends GradleModule> gradleSubEntities = gradleEntity.getModules();
-    final List<Module> intellijSubEntities = asList(ModuleManager.getInstance(intellijEntity).getModules());
-    return concatenate(result, GradleDiffUtil.calculate(myModuleChangesCalculator, gradleSubEntities, intellijSubEntities, knownChanges));
+    final Collection<Module> intellijSubEntities = myStructureHelper.getModules(intellijEntity);
+    GradleDiffUtil.calculate(myModuleChangesCalculator, gradleSubEntities, intellijSubEntities, knownChanges, currentChanges);
   }
 
   @NotNull
   @Override
-  public Object getIntellijKey(@NotNull Project entity, @NotNull Set<GradleProjectStructureChange> knownChanges) {
-    // TODO den consider the known changes
+  public Object getIntellijKey(@NotNull Project entity) {
     return entity.getName();
   }
 
@@ -55,43 +55,33 @@ public class GradleProjectStructureChangesCalculator implements GradleStructureC
     return entity.getName();
   }
 
-  @NotNull
-  private static Set<GradleProjectStructureChange> calculateProjectChanges(@NotNull GradleProject gradleProject,
-                                                                           @NotNull Project intellijProject,
-                                                                           @NotNull Set<GradleProjectStructureChange> knownChanges)
+  private void calculateProjectChanges(@NotNull GradleProject gradleProject,
+                                       @NotNull Project intellijProject,
+                                       @NotNull Set<GradleProjectStructureChange> currentChanges)
   {
-    final Set<GradleProjectStructureChange> nameChanges = checkName(gradleProject, intellijProject, knownChanges);
-    final Set<GradleProjectStructureChange> levelChanges = checkLanguageLevel(gradleProject, intellijProject, knownChanges);
-    return concatenate(nameChanges, levelChanges);
+    checkName(gradleProject, intellijProject, currentChanges);
+    checkLanguageLevel(gradleProject, intellijProject, currentChanges);
   }
 
-  @NotNull
-  private static Set<GradleProjectStructureChange> checkName(@NotNull GradleProject gradleProject,
-                                                             @NotNull Project intellijProject,
-                                                             @NotNull Set<GradleProjectStructureChange> knownChanges)
+  private static void checkName(@NotNull GradleProject gradleProject,
+                                @NotNull Project intellijProject,
+                                @NotNull Set<GradleProjectStructureChange> currentChanges)
   {
     String gradleName = gradleProject.getName();
     String intellijName = intellijProject.getName();
-    if (gradleName.equals(intellijName)) {
-      return Collections.emptySet();
+    if (!gradleName.equals(intellijName)) {
+      currentChanges.add(new GradleRenameChange(GradleRenameChange.Entity.PROJECT, gradleName, intellijName));
     }
-    final GradleRenameChange change = new GradleRenameChange(GradleRenameChange.Entity.PROJECT, gradleName, intellijName);
-    return knownChanges.contains(change) ? Collections.<GradleProjectStructureChange>emptySet()
-                                         : Collections.<GradleProjectStructureChange>singleton(change);
   }
 
-  @NotNull
-  private static Set<GradleProjectStructureChange> checkLanguageLevel(@NotNull GradleProject gradleProject,
-                                                                      @NotNull Project intellijProject,
-                                                                      @NotNull Set<GradleProjectStructureChange> knownChanges)
+  private void checkLanguageLevel(@NotNull GradleProject gradleProject,
+                                                               @NotNull Project intellijProject,
+                                                               @NotNull Set<GradleProjectStructureChange> currentChanges)
   {
     LanguageLevel gradleLevel = gradleProject.getLanguageLevel();
-    LanguageLevel intellijLevel = LanguageLevelProjectExtension.getInstance(intellijProject).getLanguageLevel();
-    if (gradleLevel == intellijLevel) {
-      return Collections.emptySet();
+    LanguageLevel intellijLevel = myStructureHelper.getLanguageLevel(intellijProject);
+    if (gradleLevel != intellijLevel) {
+      currentChanges.add(new GradleLanguageLevelChange(gradleLevel, intellijLevel));
     }
-    final GradleLanguageLevelChange change = new GradleLanguageLevelChange(gradleLevel, intellijLevel);
-    return knownChanges.contains(change) ? Collections.<GradleProjectStructureChange>emptySet()
-                                         : Collections.<GradleProjectStructureChange>singleton(change);
   }
 }
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/diff/GradleProjectStructureHelper.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/diff/GradleProjectStructureHelper.java
new file mode 100644 (file)
index 0000000..f0c81df
--- /dev/null
@@ -0,0 +1,37 @@
+package org.jetbrains.plugins.gradle.diff;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.pom.java.LanguageLevel;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+
+/**
+ * IntelliJ code provides a lot of statical bindings to the interested pieces of data. For example we need to execute code
+ * like below to get list of modules for the target project:
+ * <pre>
+ *   ModuleManager.getInstance(project).getModules()
+ * </pre>
+ * That means that it's not possible to test target classes in isolation if corresponding infrastructure is not set up.
+ * However, we don't want to set it up if we execute a simple standalone test.
+ * <p/>
+ * This interface is intended to encapsulate access to the underlying project infrastructure.
+ * <p/>
+ * Implementations of this interface are expected to be thread-safe.
+ * 
+ * @author Denis Zhdanov
+ * @since 1/26/12 11:32 AM
+ */
+public interface GradleProjectStructureHelper {
+
+  @NotNull
+  LanguageLevel getLanguageLevel(@NotNull Project project);
+
+  @NotNull
+  Collection<Module> getModules(@NotNull Project project);
+
+  @NotNull
+  Collection<OrderEntry> getOrderEntries(@NotNull Module module);
+}
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/diff/GradleProjectStructureHelperImpl.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/diff/GradleProjectStructureHelperImpl.java
new file mode 100644 (file)
index 0000000..6b30b59
--- /dev/null
@@ -0,0 +1,38 @@
+package org.jetbrains.plugins.gradle.diff;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.LanguageLevelProjectExtension;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.pom.java.LanguageLevel;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ * @author Denis Zhdanov
+ * @since 1/26/12 11:54 AM
+ */
+public class GradleProjectStructureHelperImpl implements GradleProjectStructureHelper {
+
+  @NotNull
+  @Override
+  public LanguageLevel getLanguageLevel(@NotNull Project project) {
+    return LanguageLevelProjectExtension.getInstance(project).getLanguageLevel();
+  }
+
+  @NotNull
+  @Override
+  public Collection<Module> getModules(@NotNull Project project) {
+    return Arrays.asList(ModuleManager.getInstance(project).getModules());
+  }
+
+  @NotNull
+  @Override
+  public Collection<OrderEntry> getOrderEntries(@NotNull Module module) {
+    return Arrays.asList(ModuleRootManager.getInstance(module).getOrderEntries());
+  }
+}
index 778d4cc1fdbd6a5c372dd18e0ea4837f37c3f32e..362e0cf18fb763462a72667952f54e87fd7e03a3 100644 (file)
@@ -18,17 +18,60 @@ import java.util.Set;
  */
 public interface GradleStructureChangesCalculator<G extends GradleEntity, I> {
 
-  // TODO den add doc
-  @NotNull
-  Set<GradleProjectStructureChange> calculate(@NotNull G gradleEntity,
-                                              @NotNull I intellijEntity,
-                                              @NotNull Set<GradleProjectStructureChange> knownChanges);
+  /**
+   * Calculates changes between the given entities.
+   * 
+   * @param gradleEntity    target gradle entity
+   * @param intellijEntity  target intellij entity
+   * @param knownChanges    changes between the gradle and intellij project structure that has been known up until now
+   * @param currentChanges  holder for the changes between the given entities discovered by the current call. Note that
+   *                        it must contain the change objects that have been known (contained at the <code>'knownChanges'</code>)
+   *                        but are still in place
+   */
+  void calculate(@NotNull G gradleEntity,
+                 @NotNull I intellijEntity,
+                 @NotNull Set<GradleProjectStructureChange> knownChanges,
+                 @NotNull Set<GradleProjectStructureChange> currentChanges);
 
-  // TODO den add doc
+  /**
+   * There are three possible situations when we compare a set of gradle entities with a set of intellij entities:
+   * <pre>
+   * <ul>
+   *   <li>particular entity presents only at the gradle side;</li>
+   *   <li>particular entity presents only at the intellij side;</li>
+   *   <li>particular gradle entity is matched to particular intellij entity (they may have difference in their settings though);</li>
+   * </ul>
+   * </pre>
+   * <p/>
+   * The general idea is to map evey item at the given sets of gradle and intellij entities to particular key (both gradle and
+   * intellij keys are expected to belong to the same class) and then compare them. Matched keys shows that corresponding
+   * entities should be {@link #calculate(GradleEntity, Object, Set) compared to each other}; non-matched indicate that corresponding
+   * entities are gradle- or intellij-local.
+   * <p/>
+   * This method allows to match intellij entity to the target key.
+   * 
+   * @param entity  intellij entity to match
+   * @return        key for the given entity
+   * @see #getGradleKey(GradleEntity, Set)
+   */
   @NotNull
-  Object getIntellijKey(@NotNull I entity, @NotNull Set<GradleProjectStructureChange> knownChanges);
-  
-  // TODO den add doc
+  Object getIntellijKey(@NotNull I entity);
+
+  /**
+   * Serves the same purpose as {@link #getIntellijKey(Object)} but targets gradle entities.
+   * <p/>
+   * There is a possible case that two corresponding gradle and intellij entities differ from each other by the setting that
+   * affects the resulting key (e.g. we may use module name as a key for 'module' entities and intellij module name differs from
+   * the name of the corresponding gradle module). We need to match only in one direction then (e.g. consider a situation when
+   * particular module is named differently at gradle and intellij. We shouldn't consider that change during both
+   * {@code intellij-entity -> key} and {@code gradle-entity -> key} mappings because that would produce two different keys). 
+   * So, we take into consideration the known changes only during {@code gradle-entity -> key} processing.
+   * 
+   * @param entity        target gradle entity that should be mapped to a key
+   * @param knownChanges  known changes between the gradle and intellij structures
+   * @return              key for the given entity
+   * @see #getIntellijKey(Object)
+   */
   @NotNull
   Object getGradleKey(@NotNull G entity, @NotNull Set<GradleProjectStructureChange> knownChanges);
 }
index fe167530b145723baddfba8429d18420ed8f7aac..1a60653391691fd1a0ef5060e08f1c9ee002ee3f 100644 (file)
@@ -18,7 +18,9 @@ public interface GradleProjectStructureChangeListener {
   /**
    * Notifies current listener on the newly discovered changes between the gradle and intellij project models.
    * 
-   * @param changes  newly discovered changes between the gradle and intellij project models.
+   * @param oldChanges      changes between the gradle and intellij project models that had been known prior to the current update
+   * @param currentChanges  the most up-to-date changes between the gradle and intellij project models
    */
-  void onChanges(@NotNull Collection<GradleProjectStructureChange> changes);
+  void onChanges(@NotNull Collection<GradleProjectStructureChange> oldChanges,
+                 @NotNull Collection<GradleProjectStructureChange> currentChanges);
 }
index 7825821034eea61f4b8d99536fb133e6ab8e298e..f1ee0022e65deee195ed15bc65cc251d23e024f6 100644 (file)
@@ -2,15 +2,15 @@ package org.jetbrains.plugins.gradle.sync;
 
 import com.intellij.openapi.components.AbstractProjectComponent;
 import com.intellij.openapi.project.Project;
-import com.intellij.util.containers.ConcurrentHashSet;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.plugins.gradle.diff.GradleProjectStructureChange;
-import org.jetbrains.plugins.gradle.diff.GradleProjectStructureChangesCalculator;
+import org.jetbrains.plugins.gradle.diff.GradleStructureChangesCalculator;
 import org.jetbrains.plugins.gradle.model.GradleProject;
 
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * // TODO den add doc
@@ -23,11 +23,14 @@ import java.util.concurrent.CopyOnWriteArraySet;
 public class GradleProjectStructureChangesModel extends AbstractProjectComponent {
 
   private final Set<GradleProjectStructureChangeListener> myListeners = new CopyOnWriteArraySet<GradleProjectStructureChangeListener>();
-  private final Set<GradleProjectStructureChange>         myChanges   = new ConcurrentHashSet<GradleProjectStructureChange>();
+  private final AtomicReference<Set<GradleProjectStructureChange>> myChanges
+    = new AtomicReference<Set<GradleProjectStructureChange>>(new HashSet<GradleProjectStructureChange>());
 
-  private final GradleProjectStructureChangesCalculator myChangesCalculator;
+  private final GradleStructureChangesCalculator<GradleProject, Project> myChangesCalculator;
 
-  public GradleProjectStructureChangesModel(@NotNull Project project, @NotNull GradleProjectStructureChangesCalculator changesCalculator) {
+  public GradleProjectStructureChangesModel(@NotNull Project project,
+                                            @NotNull GradleStructureChangesCalculator<GradleProject, Project> changesCalculator)
+  {
     super(project);
     myChangesCalculator = changesCalculator;
   }
@@ -44,15 +47,22 @@ public class GradleProjectStructureChangesModel extends AbstractProjectComponent
    *  </li>
    *  <li>{@link #addListener(GradleProjectStructureChangeListener) Registered listeners} are notified if any new change is detected;</li>
    * </ol>
+   * <p/>
+   * <b>Note:</b> it's very important that the listeners are notified <b>after</b> the actual state change, i.e. {@link #getChanges()}
+   * during the update returns up-to-date data.
    *
    * @param gradleProject  gradle project to sync with
    */
   public void update(@NotNull GradleProject gradleProject) {
-    Set<GradleProjectStructureChange> knownChanges = new HashSet<GradleProjectStructureChange>(myChanges);
-    final Set<GradleProjectStructureChange> newChanges = myChangesCalculator.calculate(gradleProject, myProject, knownChanges);
-    myChanges.addAll(newChanges);
+    Set<GradleProjectStructureChange> knownChanges = new HashSet<GradleProjectStructureChange>(myChanges.get());
+    Set<GradleProjectStructureChange> currentChanges = new HashSet<GradleProjectStructureChange>();
+    myChangesCalculator.calculate(gradleProject, myProject, knownChanges, currentChanges);
+    if (currentChanges.equals(knownChanges)) {
+      return;
+    }
+    myChanges.set(currentChanges);
     for (GradleProjectStructureChangeListener listener : myListeners) {
-      listener.onChanges(newChanges);
+      listener.onChanges(knownChanges, currentChanges);
     }
   }
 
@@ -72,6 +82,6 @@ public class GradleProjectStructureChangesModel extends AbstractProjectComponent
    */
   @NotNull
   public Set<GradleProjectStructureChange> getChanges() {
-    return myChanges;
+    return myChanges.get();
   }
 }
index 27fc80804c02ecf8a023b507b46d00636bba0a9d..b2d0a5ce81eb79bb76713140190f0d61fdb759c2 100644 (file)
@@ -8,6 +8,7 @@ import com.intellij.openapi.roots.ModuleRootManager;
 import com.intellij.openapi.roots.OrderEntry;
 import com.intellij.openapi.roots.RootPolicy;
 import com.intellij.ui.treeStructure.Tree;
+import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.hash.HashMap;
 import com.intellij.util.ui.UIUtil;
 import org.jetbrains.annotations.NotNull;
@@ -25,6 +26,8 @@ import org.jetbrains.plugins.gradle.util.GradleConstants;
 import javax.swing.*;
 import javax.swing.tree.DefaultMutableTreeNode;
 import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.MutableTreeNode;
+import javax.swing.tree.TreeNode;
 import java.awt.*;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -51,6 +54,9 @@ public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel {
    */
   private final Map<String, DefaultMutableTreeNode> myModuleDependencies = new HashMap<String, DefaultMutableTreeNode>();
   private final Map<String, DefaultMutableTreeNode> myModules            = new HashMap<String, DefaultMutableTreeNode>();
+
+  private final TreeNode[] myNodeHolder  = new TreeNode[1];
+  private final int[]      myIndexHolder = new int[1];
   
   private final GradleProjectStructureChangesModel myChangesModel;
 
@@ -62,11 +68,15 @@ public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel {
     myChangesModel = model;
     myChangesModel.addListener(new GradleProjectStructureChangeListener() {
       @Override
-      public void onChanges(@NotNull final Collection<GradleProjectStructureChange> changes) {
+      public void onChanges(@NotNull final Collection<GradleProjectStructureChange> oldChanges,
+                            @NotNull final Collection<GradleProjectStructureChange> currentChanges)
+      {
         UIUtil.invokeLaterIfNeeded(new Runnable() {
           @Override
           public void run() {
-            updateTree(changes); 
+            updateTree(currentChanges);
+            processObsoleteChanges(ContainerUtil.subtract(oldChanges, currentChanges));    
+
           }
         });
       }
@@ -84,6 +94,7 @@ public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel {
     constraints.fill = GridBagConstraints.BOTH;
     constraints.weightx = constraints.weighty = 1;
     myContent.add(tree, constraints);
+    myContent.setBackground(tree.getBackground());
     return treeModel;
   }
 
@@ -237,4 +248,83 @@ public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel {
       });
     }
   }
+
+  /**
+   * Updates the tree state considering that the given changes are obsolete.
+   * <p/>
+   * Example:
+   * <pre>
+   * <ol>
+   *   <li>There is a particular intellij-local library (change from the gradle project structure);</li>
+   *   <li>Corresponding node is shown at the current UI;</li>
+   *   <li>The library is removed, i.e. corresponding change has become obsolete;</li>
+   *   <li>This method is notified within the obsolete change and is expected to remove the corresponding node;</li>
+   * </ol>
+   * </pre>
+   */
+  private void processObsoleteChanges(Collection<GradleProjectStructureChange> changes) {
+    for (GradleProjectStructureChange change : changes) {
+      change.invite(new GradleProjectStructureChangeVisitor() {
+        @Override
+        public void visit(@NotNull GradleRenameChange change) {
+          // TODO den implement 
+        }
+
+        @Override
+        public void visit(@NotNull GradleProjectStructureChange change) {
+          // TODO den implement 
+        }
+
+        @Override
+        public void visit(@NotNull GradleModulePresenceChange change) {
+          // TODO den implement 
+        }
+
+        @Override
+        public void visit(@NotNull GradleLibraryDependencyPresenceChange change) {
+          // We need to remove the corresponding node then.
+          String moduleName;
+          Object library;
+          final GradleLibraryDependency gradleEntity = change.getGradleEntity();
+          final LibraryOrderEntry intellijEntity = change.getIntellijEntity();
+          assert gradleEntity != null || intellijEntity != null;
+          if (gradleEntity == null) {
+            moduleName = intellijEntity.getOwnerModule().getName();
+            library = intellijEntity;
+          }
+          else {
+            moduleName = gradleEntity.getOwnerModule().getName();
+            library = gradleEntity;
+          }
+          final DefaultMutableTreeNode holder = myModuleDependencies.get(moduleName);
+          if (holder == null) {
+            return;
+          }
+          for (DefaultMutableTreeNode node = holder.getFirstLeaf(); node != null; node = node.getNextSibling()) {
+            GradleProjectStructureNodeDescriptor<?> descriptor = (GradleProjectStructureNodeDescriptor<?>)node.getUserObject();
+            if (descriptor.getElement().equals(library)) {
+              removeNode(node);
+              return;
+            }
+          }
+        }
+      });
+    }
+  }
+
+  private void removeNode(@NotNull TreeNode node) {
+    final MutableTreeNode parent = (MutableTreeNode)node.getParent();
+    if (parent == null) {
+      return;
+    }
+    int i = parent.getIndex(node);
+    if (i <= 0) {
+      assert false : node;
+      return;
+    }
+    parent.remove(i);
+    myIndexHolder[0] = i;
+    myNodeHolder[0] = node;
+    myTreeModel.nodesWereRemoved(parent, myIndexHolder, myNodeHolder);
+  }
 }
index a8d7bd868f8e8b46ed94de5e65705de51d5742d1..ff8f163eb147bf0fc9e5675f52288a3a69ae6851 100644 (file)
@@ -43,7 +43,12 @@ public class GradleResolveProjectTask extends AbstractGradleTask {
       return;
     }
     final GradleProjectStructureChangesModel model = myIntellijProject.getComponent(GradleProjectStructureChangesModel.class);
-    model.update(project);
+    if (model != null) {
+      // This task may be called during the 'import from gradle' processing, hence, no project-level IoC is up.
+      // Model update is necessary for the correct tool window project structure diff showing but we don't have
+      // gradle tool window on this stage.
+      model.update(project);
+    }
   }
 
   @Nullable
diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/sync/GradleProjectStructureChangesModelTest.groovy b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/sync/GradleProjectStructureChangesModelTest.groovy
new file mode 100644 (file)
index 0000000..abe61ee
--- /dev/null
@@ -0,0 +1,76 @@
+package org.jetbrains.plugins.gradle.sync;
+
+
+import com.intellij.openapi.project.Project
+import org.jetbrains.plugins.gradle.testutil.ChangeBuilder
+import org.jetbrains.plugins.gradle.testutil.GradleProjectBuilder
+import org.jetbrains.plugins.gradle.testutil.IntellijProjectBuilder
+import org.junit.Before
+import org.junit.Test
+import org.picocontainer.defaults.DefaultPicoContainer
+import org.jetbrains.plugins.gradle.diff.*
+import static org.junit.Assert.assertEquals
+
+/**
+ * @author Denis Zhdanov
+ * @since 01/25/2012
+ */
+public class GradleProjectStructureChangesModelTest {
+
+  private GradleProjectStructureChangesModel myModel;
+  def gradle;
+  def intellij;
+  def changes;
+  
+  @Before
+  public void setUp() {
+    gradle = new GradleProjectBuilder()
+    intellij = new IntellijProjectBuilder()
+    changes = new ChangeBuilder()
+    def container = new DefaultPicoContainer()
+    container.registerComponentInstance(Project, intellij.project)
+    container.registerComponentInstance(GradleProjectStructureHelper, intellij.projectStructureHelper as GradleProjectStructureHelper)
+    container.registerComponentImplementation(GradleProjectStructureChangesModel)
+    container.registerComponentImplementation(GradleStructureChangesCalculator, GradleProjectStructureChangesCalculator)
+    container.registerComponentImplementation(GradleModuleStructureChangesCalculator)
+    container.registerComponentImplementation(GradleLibraryDependencyStructureChangesCalculator)
+
+    myModel = container.getComponentInstance(GradleProjectStructureChangesModel.class) as GradleProjectStructureChangesModel
+  }
+  
+  @Test
+  public void mergeGradleLocalToIntellij() {
+    gradle {
+      module {
+        dependencies {
+          lib(name: "lib1")
+          lib(name: "lib2")
+    } } }
+    
+    intellij {
+      module {
+        dependencies {
+          lib(name: "lib1")
+    } } }
+    
+    myModel.update(gradle.project)
+    checkChanges {
+      presence {
+        lib(gradle: gradle.modules.dependencies.flatten().findAll { it.name == "lib2" })
+      }
+    }
+
+    gradle {
+      module {
+        dependencies {
+          lib(name: "lib1")
+    } } }
+    myModel.update(gradle.project)
+    assertEquals([].toSet(), myModel.changes)
+  }
+
+  private def checkChanges(c) {
+    c.delegate = changes
+    assertEquals(c(), myModel.changes)
+  }
+}
diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/testutil/AbstractProjectBuilder.groovy b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/testutil/AbstractProjectBuilder.groovy
new file mode 100644 (file)
index 0000000..6e4d1f3
--- /dev/null
@@ -0,0 +1,68 @@
+package org.jetbrains.plugins.gradle.testutil
+
+import com.intellij.pom.java.LanguageLevel;
+
+/**
+ * @author Denis Zhdanov
+ * @since 1/25/12 4:06 PM
+ */
+public abstract class AbstractProjectBuilder extends BuilderSupport {
+  
+  private static final def SAME_TOKEN = "same"
+  private static int COUNTER
+  
+  def project
+  def modules = []
+  def libraries = [:].withDefault { createLibrary(it.name?: same, it.paths?: [:]) }
+  def dependencies = [:].withDefault {[]}
+
+  @Override
+  protected void setParent(Object parent, Object child) {
+  }
+
+  @Override
+  protected Object createNode(Object name) {
+    switch (name) {
+      case "call": return createNode("project", [name: same])
+      case "module": return createNode(name, [name: same])
+      case "dependencies": return getCurrent() // Assuming that 'current' is a module object
+    }
+  }
+
+  @Override
+  protected Object createNode(Object name, Object value) {
+    return null
+  }
+
+  @Override
+  protected Object createNode(Object name, Map attributes) {
+    switch (name) {
+      case "project":
+        reset()
+        return project = createProject(attributes.name?: same, attributes.langLevel?: LanguageLevel.JDK_1_6)
+      case "module": def module = createModule(attributes.name?: same); modules << module; return module
+      case "lib":
+        def module = getCurrent()
+        def dep = createLibraryDependency(module, libraries[attributes])
+        dependencies[module] << dep
+        return dep
+    }
+  }
+
+  @Override
+  protected Object createNode(Object name, Map attributes, Object value) {
+    return null
+  }
+  
+  protected abstract def createProject(String name, LanguageLevel languageLevel)
+  protected abstract def createModule(String name)
+  protected abstract def createLibrary(String name, Map paths)
+  protected abstract def createLibraryDependency(module, library)
+
+  protected String getUnique() { "./${COUNTER++}" }
+  protected String getSame() { SAME_TOKEN }
+  
+  private def reset() {
+    [modules, dependencies, libraries]*.clear()
+  }
+}
diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/testutil/ChangeBuilder.groovy b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/testutil/ChangeBuilder.groovy
new file mode 100644 (file)
index 0000000..700bcac
--- /dev/null
@@ -0,0 +1,47 @@
+package org.jetbrains.plugins.gradle.testutil;
+
+
+import org.jetbrains.plugins.gradle.diff.GradleLibraryDependencyPresenceChange
+
+/**
+ * @author Denis Zhdanov
+ * @since 1/26/12 3:25 PM
+ */
+public class ChangeBuilder extends BuilderSupport {
+  
+  def changes = []
+  
+  @Override
+  protected void setParent(Object parent, Object child) {
+  }
+
+  @Override
+  protected Object createNode(Object name) {
+    if (current == null) {
+      changes = []
+    }
+    changes
+  }
+
+  @Override
+  protected Object createNode(Object name, Object value) { changes }
+
+  @Override
+  protected Object createNode(Object name, Map attributes) {
+    switch (name) {
+      case "presence": return changes
+      case "lib":
+        changes.addAll attributes.gradle.collect { new GradleLibraryDependencyPresenceChange(it, null)}
+        changes.addAll attributes.intellij.collect { new GradleLibraryDependencyPresenceChange(null, it)}
+        return
+    }
+  }
+
+  @Override
+  protected Object createNode(Object name, Map attributes, Object value) { changes }
+
+  @Override
+  protected Object postNodeCompletion(Object parent, Object node) {
+    parent == null ? changes.toSet() : node
+  }
+}
diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/testutil/GradleProjectBuilder.groovy b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/testutil/GradleProjectBuilder.groovy
new file mode 100644 (file)
index 0000000..e889543
--- /dev/null
@@ -0,0 +1,46 @@
+package org.jetbrains.plugins.gradle.testutil
+
+import org.jetbrains.plugins.gradle.model.GradleProject
+import org.jetbrains.plugins.gradle.model.GradleModule
+import org.jetbrains.plugins.gradle.model.GradleLibraryDependency
+import org.jetbrains.plugins.gradle.model.GradleLibrary
+import org.jetbrains.plugins.gradle.model.LibraryPathType
+import com.intellij.pom.java.LanguageLevel
+
+/** 
+ * @author Denis Zhdanov
+ * @since 1/25/12 1:29 PM
+ */
+class GradleProjectBuilder extends AbstractProjectBuilder {
+
+  @Override
+  protected createProject(String name, LanguageLevel languageLevel) {
+    def result = new GradleProject(same, same)
+    result.name = name
+    result.languageLevel = languageLevel
+    result
+  }
+
+  @Override
+  protected createModule(String name) {
+    def result = new GradleModule(name, unique)
+    project.addModule(result)
+    result
+  }
+
+  @Override
+  protected createLibrary(String name, Map paths) {
+    def result = new GradleLibrary(name)
+    (paths.bin?: [same]).each { result.addPath(LibraryPathType.BINARY, it) }
+    (paths.src?: [same]).each { result.addPath(LibraryPathType.SOURCE, it) }
+    (paths.doc?: [same]).each { result.addPath(LibraryPathType.DOC, it) }
+    result
+  }
+
+  @Override
+  protected createLibraryDependency(module, library) {
+    def result = new GradleLibraryDependency(module, library)
+    module.addDependency(result)
+    result
+  }
+}
diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/testutil/IntellijProjectBuilder.groovy b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/testutil/IntellijProjectBuilder.groovy
new file mode 100644 (file)
index 0000000..92fad07
--- /dev/null
@@ -0,0 +1,49 @@
+package org.jetbrains.plugins.gradle.testutil
+
+import com.intellij.pom.java.LanguageLevel
+import com.intellij.openapi.module.Module
+import com.intellij.openapi.roots.RootPolicy
+import com.intellij.openapi.roots.libraries.Library
+import com.intellij.openapi.roots.LibraryOrderEntry
+import groovy.mock.interceptor.StubFor
+import com.intellij.openapi.project.Project
+
+/** 
+ * @author Denis Zhdanov
+ * @since 1/25/12 3:09 PM
+ */
+class IntellijProjectBuilder extends AbstractProjectBuilder {
+  
+  def projectStub = [:]
+  def project = projectStub as Project
+  def projectStructureHelper = [
+    getModules: { modules },
+    getOrderEntries: { dependencies[it] }
+  ]
+
+  @Override
+  protected createProject(String name, LanguageLevel languageLevel) {
+    projectStub.getName = { name }
+    projectStructureHelper.getLanguageLevel = { languageLevel }
+    project
+  }
+
+  @Override
+  protected createModule(String name) {
+    [ getName: { name } ] as Module
+  }
+
+  @Override
+  protected createLibrary(String name, Map paths) {
+    [ getName: { name } ] as Library
+  }
+
+  @Override
+  protected createLibraryDependency(module, library) {
+    def stub = [:]
+    def result = stub as LibraryOrderEntry
+    stub.accept = { policy, defaultValue -> policy.visitLibraryOrderEntry(result, defaultValue) }
+    stub.getLibraryName = { library.name }
+    result
+  }
+}