diff --git a/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/AbstractArrangementRuleEditingModelTest.java b/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/AbstractArrangementRuleEditingModelTest.java index 01a5474d8fbd..16d92571c40b 100644 --- a/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/AbstractArrangementRuleEditingModelTest.java +++ b/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/AbstractArrangementRuleEditingModelTest.java @@ -39,11 +39,11 @@ import static org.junit.Assert.assertTrue; */ public abstract class AbstractArrangementRuleEditingModelTest { - @NotNull protected ArrangementRuleEditingModelBuilder myBuilder; - @NotNull protected JTree myTree; - @NotNull protected DefaultMutableTreeNode myRoot; - @NotNull protected TIntObjectHashMap myRowMappings; - @NotNull protected JavaRearranger myGrouper; + @NotNull protected ArrangementRuleEditingModelBuilder myBuilder; + @NotNull protected JTree myTree; + @NotNull protected DefaultMutableTreeNode myRoot; + @NotNull protected TIntObjectHashMap myRowMappings; + @NotNull protected JavaRearranger myGrouper; @Before public void setUp() { @@ -51,7 +51,7 @@ public abstract class AbstractArrangementRuleEditingModelTest { myRoot = new DefaultMutableTreeNode(); myTree = new Tree(myRoot); myTree.expandPath(new TreePath(myRoot)); - myRowMappings = new TIntObjectHashMap(); + myRowMappings = new TIntObjectHashMap(); myGrouper = new JavaRearranger(); } @@ -73,7 +73,7 @@ public abstract class AbstractArrangementRuleEditingModelTest { return new ArrangementSettingsAtomNode(type, condition); } - protected void checkRows(int ... rows) { + protected void checkRows(int... rows) { for (int row : rows) { assertTrue( String.format("Expected to find mappings for rows %s. Actual: %s", Arrays.toString(rows), Arrays.toString(myRowMappings.keys())), diff --git a/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImplTest.java b/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImplTest.java index 46f8e62d7aca..b41eae81a7d0 100644 --- a/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImplTest.java +++ b/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImplTest.java @@ -15,7 +15,9 @@ */ package com.intellij.application.options.codeStyle.arrangement; +import com.intellij.openapi.util.Ref; import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode; +import gnu.trove.TObjectProcedure; import org.junit.Test; import javax.swing.tree.DefaultMutableTreeNode; @@ -164,5 +166,26 @@ public class ArrangementRuleEditingModelImplTest extends AbstractArrangementRule DefaultMutableTreeNode staticNode = (DefaultMutableTreeNode)layeredFieldNode.getFirstChild(); assertNotNull(staticNode); assertEquals(atom(STATIC), staticNode.getUserObject()); + + //checkTreeNodesConsistency(); + } + + private void checkTreeNodesConsistency() { + final Ref rootRef = new Ref(); + myRowMappings.forEachValue(new TObjectProcedure() { + @Override + public boolean execute(ArrangementRuleEditingModelImpl model) { + DefaultMutableTreeNode root = ArrangementConfigUtil.getRoot(model.getTopMost()); + assertSame(root, ArrangementConfigUtil.getRoot(model.getBottomMost())); + + if (rootRef.get() == null) { + rootRef.set(root); + } + else { + assertSame(rootRef.get(), root); + } + return true; + } + }); } } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementConfigUtil.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementConfigUtil.java index fae13c34d231..cec6af4a875a 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementConfigUtil.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementConfigUtil.java @@ -343,10 +343,7 @@ public class ArrangementConfigUtil { * @param node reference to the target hierarchy */ private static void markRows(@NotNull DefaultMutableTreeNode node) { - DefaultMutableTreeNode root = node; - for (TreeNode n = root; n != null; n = n.getParent()) { - root = (DefaultMutableTreeNode)n; - } + DefaultMutableTreeNode root = getRoot(node); int row = 0; Stack nodes = new Stack(); nodes.push(root); @@ -359,6 +356,15 @@ public class ArrangementConfigUtil { } } + @NotNull + public static DefaultMutableTreeNode getRoot(DefaultMutableTreeNode node) { + DefaultMutableTreeNode root = node; + for (TreeNode n = root; n != null; n = n.getParent()) { + root = (DefaultMutableTreeNode)n; + } + return root; + } + /** * Processes hierarchy denoted by the given node assuming that every node there contains information about its initial row * (see {@link #markRows(DefaultMutableTreeNode)}). @@ -371,10 +377,7 @@ public class ArrangementConfigUtil { @NotNull private static TIntIntHashMap collectRowChangesAndUnmark(@NotNull DefaultMutableTreeNode node) { @NotNull TIntIntHashMap changes = new TIntIntHashMap(); - DefaultMutableTreeNode root = node; - for (TreeNode n = root; n != null; n = n.getParent()) { - root = (DefaultMutableTreeNode)n; - } + DefaultMutableTreeNode root = getRoot(node); int row = 0; Stack nodes = new Stack(); nodes.push(root); @@ -403,10 +406,7 @@ public class ArrangementConfigUtil { * @return given node's row at the nodes hierarchy (0-based) */ public static int getRow(@NotNull DefaultMutableTreeNode node) { - DefaultMutableTreeNode root = node; - for (TreeNode n = root; n != null; n = n.getParent()) { - root = (DefaultMutableTreeNode)n; - } + DefaultMutableTreeNode root = getRoot(node); int row = 0; Stack nodes = new Stack(); nodes.push(root); diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelBuilder.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelBuilder.java index 8e3b4892eec5..0ee3004f2932 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelBuilder.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelBuilder.java @@ -70,7 +70,7 @@ public class ArrangementRuleEditingModelBuilder { @NotNull JTree tree, @NotNull DefaultMutableTreeNode root, @NotNull ArrangementSettingsGrouper grouper, - @NotNull TIntObjectHashMap rowMappings) + @NotNull TIntObjectHashMap rowMappings) { int initialInsertRow = 0; diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImpl.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImpl.java index 2c6e48e52f5b..e3e1829ccd02 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImpl.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImpl.java @@ -40,8 +40,8 @@ public class ArrangementRuleEditingModelImpl implements ArrangementRuleEditingMo @NotNull private final Set myListeners = new HashSet(); @NotNull private final Set myConditions = new HashSet(); - @NotNull private final TIntObjectHashMap myRowMappings; - @NotNull private final ArrangementSettingsGrouper myGrouper; + @NotNull private final TIntObjectHashMap myRowMappings; + @NotNull private final ArrangementSettingsGrouper myGrouper; @NotNull private DefaultMutableTreeNode myTopMost; @NotNull private DefaultMutableTreeNode myBottomMost; @@ -64,7 +64,7 @@ public class ArrangementRuleEditingModelImpl implements ArrangementRuleEditingMo @NotNull DefaultMutableTreeNode topMost, @NotNull DefaultMutableTreeNode bottomMost, @NotNull ArrangementSettingsGrouper grouper, - @NotNull TIntObjectHashMap mappings, + @NotNull TIntObjectHashMap mappings, int row) { mySettingsNode = node; @@ -93,11 +93,40 @@ public class ArrangementRuleEditingModelImpl implements ArrangementRuleEditingMo return mySettingsNode; } + @NotNull + public DefaultMutableTreeNode getTopMost() { + return myTopMost; + } + + @NotNull + public DefaultMutableTreeNode getBottomMost() { + return myBottomMost; + } + @Override public boolean hasCondition(@NotNull Object key) { return myConditions.contains(key); } + /** + * There is a possible case that tree nodes referenced by the current model become out of date due to a tree modification. + *

+ * This method asks the model to refresh its tree nodes if necessary. + */ + public void refreshTreeNodes() { + for (DefaultMutableTreeNode node = myBottomMost; node != null; node = (DefaultMutableTreeNode)node.getParent()) { + if (node == myTopMost) { + // No refresh is necessary. + return; + } + else if (mySettingsNode.equals(node.getUserObject())) { + myTopMost = node; + return; + } + } + assert false; + } + @Override public void addAndCondition(@NotNull ArrangementSettingsAtomNode node) { doAddAndCondition(node); @@ -181,12 +210,12 @@ public class ArrangementRuleEditingModelImpl implements ArrangementRuleEditingMo return; } - final TIntObjectHashMap newMappings = new TIntObjectHashMap(); + final TIntObjectHashMap newMappings = new TIntObjectHashMap(); // Update model mappings. - myRowMappings.forEachEntry(new TIntObjectProcedure() { + myRowMappings.forEachEntry(new TIntObjectProcedure() { @Override - public boolean execute(int row, ArrangementRuleEditingModel model) { + public boolean execute(int row, ArrangementRuleEditingModelImpl model) { if (row == myRow) { return true; } @@ -196,6 +225,7 @@ public class ArrangementRuleEditingModelImpl implements ArrangementRuleEditingMo else { newMappings.put(row, model); } + model.refreshTreeNodes(); return true; } }); @@ -203,9 +233,9 @@ public class ArrangementRuleEditingModelImpl implements ArrangementRuleEditingMo newMappings.put(myRow, this); myRowMappings.clear(); - newMappings.forEachEntry(new TIntObjectProcedure() { + newMappings.forEachEntry(new TIntObjectProcedure() { @Override - public boolean execute(int row, ArrangementRuleEditingModel model) { + public boolean execute(int row, ArrangementRuleEditingModelImpl model) { myRowMappings.put(row, model); return true; } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleTree.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleTree.java index 80818c2fe2de..bf1fd834d1ac 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleTree.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleTree.java @@ -54,8 +54,8 @@ public class ArrangementRuleTree { @NotNull private final TIntObjectHashMap myRenderers = new TIntObjectHashMap(); - @NotNull private final TIntObjectHashMap myModels = - new TIntObjectHashMap(); + @NotNull private final TIntObjectHashMap myModels = + new TIntObjectHashMap(); @NotNull private final DefaultTreeModel myTreeModel; @NotNull private final Tree myTree; @@ -176,9 +176,9 @@ public class ArrangementRuleTree { for (ArrangementSettingsNode setting : settings) { builder.build(setting, myTree, root, grouper, myModels); } - myModels.forEachValue(new TObjectProcedure() { + myModels.forEachValue(new TObjectProcedure() { @Override - public boolean execute(ArrangementRuleEditingModel model) { + public boolean execute(ArrangementRuleEditingModelImpl model) { model.addListener(myModelChangeListener); return true; }