IDEA-19061 Integrate the Rearranger-plugin into core-IDE

'Replace condition' operation is provided (e.g. for type change like 'from field to method')
This commit is contained in:
Denis.Zhdanov
2012-08-23 19:37:26 +04:00
parent b3084293d8
commit 62f2a68cd0
11 changed files with 192 additions and 99 deletions
@@ -141,7 +141,7 @@ node = '4'()
@Test
void replaceWithTwoLevelMergeToNodeBelow() {
// Init.
// Init.
def from;
def to;
def initial = new TreeNodeBuilder().
@@ -172,6 +172,43 @@ to = '3'()}
assertNodesEqual(expected, initial)
checkRowMappings([:], rowMappings)
}
@Test
void replaceFirstChildWithMergeBelow() {
// Init.
def from;
def to;
def initial = new TreeNodeBuilder().
'0' {
from = '1'() {
to = '2'()}
'3'() {
'4'() }
'1' {
'5'()
'6'()}
}
// Modify.
def replacement = new TreeNodeBuilder().
'3' {
'2'()
}
def rowMappings = doReplace(initial, from, to, replacement)
// Check.
def expected = new TreeNodeBuilder().
'0' {
'3' {
'2'()
'4'()}
'1' {
'5'()
'6'()}
}
assertNodesEqual(expected, initial)
checkRowMappings([ 4 : 3, 5 : 4, 6 : 5, 7 : 6 ], rowMappings)
}
@Test
void addWithoutMergeAbove() {
@@ -39,7 +39,7 @@ public class ArrangementRuleEditingModelBuilderTest extends AbstractArrangementR
assertTrue(model.hasCondition(STATIC));
assertFalse(model.hasCondition(PRIVATE));
assertEquals(1, myRoot.getChildCount());
assertEquals(matchCondition, myRoot.getFirstChild().getBackingSetting());
assertEquals(matchCondition, myRoot.getFirstChild().getBackingCondition());
}
@Test
@@ -51,11 +51,11 @@ public class ArrangementRuleEditingModelBuilderTest extends AbstractArrangementR
ArrangementTreeNode fieldUiNode = myRoot.getFirstChild();
assertNotNull(fieldUiNode);
assertEquals(atom(FIELD), fieldUiNode.getBackingSetting());
assertEquals(atom(FIELD), fieldUiNode.getBackingCondition());
ArrangementTreeNode modifiersUiNode = fieldUiNode.getFirstChild();
assertNotNull(modifiersUiNode);
assertEquals(and(atom(PUBLIC), atom(STATIC)), modifiersUiNode.getBackingSetting());
assertEquals(and(atom(PUBLIC), atom(STATIC)), modifiersUiNode.getBackingCondition());
}
@Test
@@ -67,14 +67,14 @@ public class ArrangementRuleEditingModelBuilderTest extends AbstractArrangementR
ArrangementTreeNode fieldUiNode = myRoot.getFirstChild();
assertNotNull(fieldUiNode);
assertEquals(atom(FIELD), fieldUiNode.getBackingSetting());
assertEquals(atom(FIELD), fieldUiNode.getBackingCondition());
ArrangementTreeNode publicStaticUiNode = fieldUiNode.getFirstChild();
assertNotNull(publicStaticUiNode);
assertEquals(and(atom(PUBLIC), atom(STATIC)), publicStaticUiNode.getBackingSetting());
assertEquals(and(atom(PUBLIC), atom(STATIC)), publicStaticUiNode.getBackingCondition());
ArrangementTreeNode privateUiNode = fieldUiNode.getLastChild();
assertNotNull(privateUiNode);
assertEquals(atom(PRIVATE), privateUiNode.getBackingSetting());
assertEquals(atom(PRIVATE), privateUiNode.getBackingCondition());
}
}
@@ -40,7 +40,7 @@ public class ArrangementRuleEditingModelImplTest extends AbstractArrangementRule
ArrangementTreeNode child = myRoot.getFirstChild();
assertNotNull(child);
ArrangementMatchCondition expectedMatchCondition = and(atom(PUBLIC), atom(STATIC));
assertEquals(expectedMatchCondition, child.getBackingSetting());
assertEquals(expectedMatchCondition, child.getBackingCondition());
assertEquals(expectedMatchCondition, model.getMatchCondition());
}
@@ -58,11 +58,11 @@ public class ArrangementRuleEditingModelImplTest extends AbstractArrangementRule
ArrangementTreeNode fieldNode = myRoot.getFirstChild();
assertNotNull(fieldNode);
assertEquals(atom(FIELD), fieldNode.getBackingSetting());
assertEquals(atom(FIELD), fieldNode.getBackingCondition());
ArrangementTreeNode publicNode = fieldNode.getFirstChild();
assertNotNull(publicNode);
assertEquals(atom(PUBLIC), publicNode.getBackingSetting());
assertEquals(atom(PUBLIC), publicNode.getBackingCondition());
}
@Test
@@ -80,11 +80,11 @@ public class ArrangementRuleEditingModelImplTest extends AbstractArrangementRule
ArrangementTreeNode fieldNode = myRoot.getFirstChild();
assertNotNull(fieldNode);
assertEquals(atom(FIELD), fieldNode.getBackingSetting());
assertEquals(atom(FIELD), fieldNode.getBackingCondition());
ArrangementTreeNode modifiersNode = fieldNode.getFirstChild();
assertNotNull(modifiersNode);
assertEquals(and(atom(PUBLIC), atom(STATIC)), modifiersNode.getBackingSetting());
assertEquals(and(atom(PUBLIC), atom(STATIC)), modifiersNode.getBackingCondition());
}
@Test
@@ -102,11 +102,11 @@ public class ArrangementRuleEditingModelImplTest extends AbstractArrangementRule
ArrangementTreeNode fieldNode = myRoot.getFirstChild();
assertNotNull(fieldNode);
assertEquals(atom(FIELD), fieldNode.getBackingSetting());
assertEquals(atom(FIELD), fieldNode.getBackingCondition());
ArrangementTreeNode modifiersNode = fieldNode.getFirstChild();
assertNotNull(modifiersNode);
assertEquals(atom(STATIC), modifiersNode.getBackingSetting());
assertEquals(atom(STATIC), modifiersNode.getBackingCondition());
}
@Test
@@ -123,7 +123,7 @@ public class ArrangementRuleEditingModelImplTest extends AbstractArrangementRule
ArrangementTreeNode fieldNode = myRoot.getFirstChild();
assertNotNull(fieldNode);
assertEquals(atom(FIELD), fieldNode.getBackingSetting());
assertEquals(atom(FIELD), fieldNode.getBackingCondition());
assertEquals(0, fieldNode.getChildCount());
}
@@ -150,15 +150,15 @@ public class ArrangementRuleEditingModelImplTest extends AbstractArrangementRule
ArrangementTreeNode atomFieldNode = myRoot.getFirstChild();
assertNotNull(atomFieldNode);
assertEquals(atom(FIELD), atomFieldNode.getBackingSetting());
assertEquals(atom(FIELD), atomFieldNode.getBackingCondition());
ArrangementTreeNode layeredFieldNode = atomFieldNode.getNextSibling();
assertNotNull(atomFieldNode);
assertEquals(atom(FIELD), atomFieldNode.getBackingSetting());
assertEquals(atom(FIELD), atomFieldNode.getBackingCondition());
ArrangementTreeNode staticNode = layeredFieldNode.getFirstChild();
assertNotNull(staticNode);
assertEquals(atom(STATIC), staticNode.getBackingSetting());
assertEquals(atom(STATIC), staticNode.getBackingCondition());
}
@Test
@@ -184,14 +184,14 @@ public class ArrangementRuleEditingModelImplTest extends AbstractArrangementRule
ArrangementTreeNode compositeFieldNode = myRoot.getFirstChild();
assertNotNull(compositeFieldNode);
assertEquals(atom(FIELD), compositeFieldNode.getBackingSetting());
assertEquals(atom(FIELD), compositeFieldNode.getBackingCondition());
ArrangementTreeNode publicNode = compositeFieldNode.getFirstChild();
assertNotNull(publicNode);
assertEquals(atom(PUBLIC), publicNode.getBackingSetting());
assertEquals(atom(PUBLIC), publicNode.getBackingCondition());
ArrangementTreeNode atomFieldNode = compositeFieldNode.getNextSibling();
assertNotNull(atomFieldNode);
assertEquals(atom(FIELD), atomFieldNode.getBackingSetting());
assertEquals(atom(FIELD), atomFieldNode.getBackingCondition());
}
}
@@ -24,7 +24,7 @@ import java.util.Set;
/**
* Encapsulates composite match condition, e.g. "an entry has type 'field' and modifier 'static'".
* <p/>
* Thread-safe.
* Not thread-safe.
*
* @author Denis Zhdanov
* @since 8/8/12 1:18 PM
@@ -188,7 +188,7 @@ public class ArrangementConfigUtil {
int rowsCreated = 0;
if (uiParentNode != null && uiParentNode.getChildCount() > 0) {
ArrangementTreeNode child = uiParentNode.getChildAt(uiParentNode.getChildCount() - 1);
if (conditionNode.getCurrent().equals(child.getBackingSetting())) {
if (conditionNode.getCurrent().equals(child.getBackingCondition())) {
uiNode = child;
}
}
@@ -316,7 +316,7 @@ public class ArrangementConfigUtil {
parentCopy.add(cutHierarchy);
}
for (int j = i + 1; j < childCount; j++) {
ArrangementTreeNode child = parent.getChildAt(j);
ArrangementTreeNode child = parent.getChildAt(i + 1);
treeModel.removeNodeFromParent(child);
parentCopy.add(child);
}
@@ -527,8 +527,8 @@ public class ArrangementConfigUtil {
}
private static boolean hasEqualSetting(@NotNull ArrangementTreeNode node1, @NotNull ArrangementTreeNode node2) {
ArrangementMatchCondition matchCondition1 = node1.getBackingSetting();
ArrangementMatchCondition matchCondition2 = node2.getBackingSetting();
ArrangementMatchCondition matchCondition1 = node1.getBackingCondition();
ArrangementMatchCondition matchCondition2 = node2.getBackingCondition();
if (matchCondition1 == null) {
return matchCondition2 == null;
}
@@ -39,7 +39,7 @@ import java.util.List;
* @author Denis Zhdanov
* @since 8/14/12 9:54 AM
*/
public class ArrangementMatcherRuleEditor extends JPanel {
public class ArrangementMatchConditionEditor extends JPanel {
@NotNull private final List<JComponent> myColoredComponents = new ArrayList<JComponent>();
@NotNull private final Map<Object, ArrangementAtomNodeComponent> myComponents =
@@ -48,8 +48,8 @@ public class ArrangementMatcherRuleEditor extends JPanel {
@NotNull private final ArrangementStandardSettingsAware myFilter;
@Nullable private ArrangementRuleEditingModel myModel;
public ArrangementMatcherRuleEditor(@NotNull ArrangementStandardSettingsAware filter,
@NotNull ArrangementNodeDisplayManager displayManager)
public ArrangementMatchConditionEditor(@NotNull ArrangementStandardSettingsAware filter,
@NotNull ArrangementNodeDisplayManager displayManager)
{
myFilter = filter;
init(displayManager);
@@ -138,30 +138,33 @@ public class ArrangementMatcherRuleEditor extends JPanel {
if (component == null) {
return;
}
ArrangementAtomMatchCondition setting = component.getMatchCondition();
boolean remove = myModel.hasCondition(setting.getValue());
ArrangementAtomMatchCondition chosenCondition = component.getMatchCondition();
boolean remove = myModel.hasCondition(chosenCondition.getValue());
component.setSelected(!remove);
repaintComponent(component);
if (remove) {
myModel.removeAndCondition(setting);
myModel.removeAndCondition(chosenCondition);
return;
}
Collection<Set<?>> mutexes = myFilter.getMutexes();
for (Set<?> mutex : mutexes) {
if (!mutex.contains(setting.getValue())) {
if (!mutex.contains(chosenCondition.getValue())) {
continue;
}
for (Object key : mutex) {
if (myModel.hasCondition(key)) {
ArrangementAtomNodeComponent componentToDeselect = myComponents.get(key);
componentToDeselect.setSelected(false);
myModel.removeAndCondition(componentToDeselect.getMatchCondition());
repaintComponent(componentToDeselect);
myModel.replaceCondition(componentToDeselect.getMatchCondition(), chosenCondition);
// There is a possible case that some conditions become unavailable, e.g. changing type from 'field' to 'method'
// makes 'volatile' condition inappropriate.
updateState(myModel);
return;
}
}
}
myModel.addAndCondition(setting);
myModel.addAndCondition(chosenCondition);
}
@Nullable
@@ -48,4 +48,18 @@ public interface ArrangementRuleEditingModel {
void addAndCondition(@NotNull ArrangementAtomMatchCondition condition);
void removeAndCondition(@NotNull ArrangementMatchCondition condition);
/**
* We need to be able to replace one condition by another. Most of the time it can be simulated by
* {@link #removeAndCondition(ArrangementMatchCondition) 'remove old'} and
* {@link #addAndCondition(ArrangementAtomMatchCondition) 'add new'} actions sequence but that doesn't work when
* {@link #getMatchCondition() underlying condition} has the only atom condition. Removing it eliminates the condition at all.
*
* @param from condition which should be replaced
* @param to replacement condition
* @throws IllegalArgumentException when given 'from' condition is not a part of the
* {@link #getMatchCondition() underlying match condition}
*/
void replaceCondition(@NotNull ArrangementAtomMatchCondition from, @NotNull ArrangementAtomMatchCondition to)
throws IllegalArgumentException;
}
@@ -34,8 +34,6 @@ import java.util.Set;
*/
public class ArrangementRuleEditingModelImpl implements ArrangementRuleEditingModel {
private static final TIntIntHashMap EMPTY_CHANGES = new TIntIntHashMap();
@NotNull private static final MyConditionsBuilder CONDITIONS_BUILDER = new MyConditionsBuilder();
@NotNull private final Set<Listener> myListeners = new HashSet<Listener>();
@@ -129,8 +127,8 @@ public class ArrangementRuleEditingModelImpl implements ArrangementRuleEditingMo
// No refresh is necessary.
return;
}
ArrangementMatchCondition matchCondition = myTopMost.getBackingSetting();
if (matchCondition != null && matchCondition.equals(node.getBackingSetting())) {
ArrangementMatchCondition matchCondition = myTopMost.getBackingCondition();
if (matchCondition != null && matchCondition.equals(node.getBackingCondition())) {
myTopMost = node;
return;
}
@@ -140,62 +138,78 @@ public class ArrangementRuleEditingModelImpl implements ArrangementRuleEditingMo
@Override
public void addAndCondition(@NotNull ArrangementAtomMatchCondition condition) {
TIntIntHashMap rowChanges = doAddAndCondition(condition);
refreshConditions();
notifyListeners(rowChanges);
ArrangementMatchCondition newCondition = ArrangementUtil.and(myMatchCondition.clone(), condition);
applyNewCondition(newCondition);
}
@NotNull
private TIntIntHashMap doAddAndCondition(@NotNull ArrangementAtomMatchCondition condition) {
ArrangementMatchCondition newNode = ArrangementUtil.and(myMatchCondition.clone(), condition);
return applyNewCondition(newNode);
}
@Override
public void removeAndCondition(@NotNull ArrangementMatchCondition condition) {
TIntIntHashMap rowChanges = doRemoveAndCondition(condition);
refreshConditions();
notifyListeners(rowChanges);
if (!(myMatchCondition instanceof ArrangementCompositeMatchCondition)) {
// TODO den implement
return;
}
ArrangementMatchCondition newCondition = myMatchCondition.clone();
ArrangementCompositeMatchCondition composite = (ArrangementCompositeMatchCondition)newCondition;
composite.getOperands().remove(condition);
if (composite.getOperands().size() == 1) {
newCondition = composite.getOperands().iterator().next();
}
applyNewCondition(newCondition);
}
@NotNull
private TIntIntHashMap doRemoveAndCondition(@NotNull ArrangementMatchCondition node) {
if (!(myMatchCondition instanceof ArrangementCompositeMatchCondition)) {
return EMPTY_CHANGES;
}
ArrangementMatchCondition newNode = myMatchCondition.clone();
ArrangementCompositeMatchCondition composite = (ArrangementCompositeMatchCondition)newNode;
composite.getOperands().remove(node);
if (composite.getOperands().size() == 1) {
newNode = composite.getOperands().iterator().next();
}
return applyNewCondition(newNode);
}
@NotNull
private TIntIntHashMap applyNewCondition(@NotNull ArrangementMatchCondition newNode) {
private void applyNewCondition(@NotNull ArrangementMatchCondition newNode) {
myMatchCondition = newNode;
HierarchicalArrangementConditionNode grouped = myGrouper.group(newNode);
int newDepth = ArrangementConfigUtil.getDepth(grouped);
int oldDepth = ArrangementConfigUtil.distance(myTopMost, myBottomMost);
if (oldDepth == newDepth) {
myBottomMost.setSettings(ArrangementConfigUtil.getLast(grouped).getCurrent());
return EMPTY_CHANGES;
}
Pair<ArrangementTreeNode, Integer> replacement = ArrangementConfigUtil.map(null, grouped, null);
ArrangementTreeNode newBottom = replacement.first;
ArrangementTreeNode newTop = ArrangementConfigUtil.getRoot(newBottom);
final TIntIntHashMap rowChanges = ArrangementConfigUtil.replace(myTopMost, myBottomMost, newTop, myTreeModel, myRootVisible);
myTopMost = newTop;
myBottomMost = newBottom;
for (ArrangementTreeNode node = myBottomMost.getParent(); node != null; node = node.getParent()) {
// There is a possible case that top condition is merged into existing one, hence, we need to refresh it.
ArrangementMatchCondition condition = node.getBackingCondition();
if (condition != null && condition.equals(newTop.getBackingCondition())) {
newTop = node;
}
}
myTopMost = newTop;
rowChanges.remove(myRow);
int newRow = ArrangementConfigUtil.getRow(myBottomMost, myRootVisible);
rowChanges.put(myRow, newRow);
myRow = newRow;
return rowChanges;
refreshConditions();
notifyListeners(rowChanges);
}
@Override
public void replaceCondition(@NotNull ArrangementAtomMatchCondition from, @NotNull ArrangementAtomMatchCondition to)
throws IllegalArgumentException
{
for (ArrangementTreeNode node = myBottomMost; node != null; node = node.getParent()) {
if (from.equals(node.getBackingCondition())) {
ArrangementMatchCondition newCondition;
if (myMatchCondition.equals(from)) {
newCondition = to;
}
else {
assert myMatchCondition instanceof ArrangementCompositeMatchCondition;
ArrangementCompositeMatchCondition composite = (ArrangementCompositeMatchCondition)myMatchCondition;
ArrangementCompositeMatchCondition newComposite = composite.clone();
newComposite.getOperands().remove(from);
newComposite.getOperands().add(to);
newCondition = newComposite;
}
applyNewCondition(newCondition);
return;
}
if (node == myTopMost) {
throw new IllegalArgumentException(String.format(
"Can't perform arrangement match condition modification ('%s' -> '%s'). Reason: target condition doesn't have "
+ "'%s' condition - %s",
from, to, from, myMatchCondition));
}
}
}
public void addListener(@NotNull Listener listener) {
@@ -434,6 +434,8 @@ public class ArrangementRuleTree {
}
private void onModelChange(@NotNull ArrangementRuleEditingModelImpl model, @NotNull final TIntIntHashMap rowChanges) {
expandAll(myTree, new TreePath(myTreeModel.getRoot()));
// Refresh models.
myModels.forEachValue(myModelNodesRefresher);
@@ -455,14 +457,36 @@ public class ArrangementRuleTree {
}
return true;
}
});
putAll(changedModelMappings, myModels);
putAll(changedRendererMappings, myRenderers);
// Drop JTree visual caches.
rowChanges.forEachEntry(new TIntIntProcedure() {
@Override
public boolean execute(int oldRow, int newRow) {
refreshTreeNode(oldRow);
refreshTreeNode(newRow);
return true;
}
private void refreshTreeNode(int row) {
TreePath path = myTree.getPathForRow(row);
if (path == null) {
return;
}
TreeNode node = (TreeNode)path.getLastPathComponent();
if (node == null) {
return;
}
myTreeModel.nodeStructureChanged(node);
}
});
// Perform necessary actions for the changed model.
ArrangementTreeNode topMost = model.getTopMost();
ArrangementTreeNode bottomMost = model.getBottomMost();
expandAll(myTree, new TreePath(myTreeModel.getRoot()));
doClearSelection();
myExplicitSelectionChange = true;
try {
@@ -470,9 +494,10 @@ public class ArrangementRuleTree {
TreePath path = new TreePath(node.getPath());
int row = myTree.getRowForPath(path);
myRenderers.remove(row);
mySelectionModel.addSelectionPath(path);
myTreeModel.nodeChanged(node);
mySelectionModel.addSelectionPath(path);
ArrangementMatchCondition matchCondition = node.getBackingSetting();
ArrangementMatchCondition matchCondition = node.getBackingCondition();
if (matchCondition != null) {
getNodeComponentAt(row, matchCondition).setSelected(true);
}
@@ -506,7 +531,7 @@ public class ArrangementRuleTree {
int row,
boolean hasFocus)
{
ArrangementMatchCondition node = ((ArrangementTreeNode)value).getBackingSetting();
ArrangementMatchCondition node = ((ArrangementTreeNode)value).getBackingCondition();
if (node == null) {
return EMPTY_RENDERER;
}
@@ -64,7 +64,7 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel {
);
final JXTaskPane editorPane = new JXTaskPane(ApplicationBundle.message("arrangement.title.editor"));
final ArrangementMatcherRuleEditor ruleEditor = new ArrangementMatcherRuleEditor(filter, displayManager);
final ArrangementMatchConditionEditor ruleEditor = new ArrangementMatchConditionEditor(filter, displayManager);
ruleEditor.applyBackground(component.getBackground());
editorPane.getContentPane().setBackground(component.getBackground());
editorPane.add(ruleEditor);
@@ -22,7 +22,10 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.tree.DefaultMutableTreeNode;
/**
* // TODO den add doc
* JTree node for arrangement rule tree.
* <p/>
* The general idea is to provide two additional properties - {@link #getBackingCondition() backing condition} and {@link #getRow() row}
* and encapsulate class casts.
*
* @author Denis Zhdanov
* @since 8/20/12 10:53 PM
@@ -31,25 +34,21 @@ public class ArrangementTreeNode extends DefaultMutableTreeNode {
private static final int NO_ROW = -1;
@Nullable private ArrangementMatchCondition mySettings;
private int myRow;
@Nullable private final ArrangementMatchCondition myCondition;
private int myRow;
public ArrangementTreeNode(@Nullable ArrangementMatchCondition settings) {
this(settings, NO_ROW);
public ArrangementTreeNode(@Nullable ArrangementMatchCondition condition) {
this(condition, NO_ROW);
}
public ArrangementTreeNode(@Nullable ArrangementMatchCondition settings, int row) {
mySettings = settings;
public ArrangementTreeNode(@Nullable ArrangementMatchCondition condition, int row) {
myCondition = condition;
myRow = row;
}
@Nullable
public ArrangementMatchCondition getBackingSetting() {
return mySettings;
}
public void setSettings(@Nullable ArrangementMatchCondition settings) {
mySettings = settings;
public ArrangementMatchCondition getBackingCondition() {
return myCondition;
}
public boolean isRowSet() {
@@ -71,9 +70,10 @@ public class ArrangementTreeNode extends DefaultMutableTreeNode {
@NotNull
public ArrangementTreeNode copy() {
// Settings are copied by-ref intentionally here.
return new ArrangementTreeNode(mySettings, myRow);
return new ArrangementTreeNode(myCondition, myRow);
}
@Nullable
@Override
public ArrangementTreeNode getParent() {
return (ArrangementTreeNode)super.getParent();
@@ -111,6 +111,6 @@ public class ArrangementTreeNode extends DefaultMutableTreeNode {
@Override
public String toString() {
return mySettings == null ? "" : mySettings.toString() + (myRow >= 0 ? ": row=" + myRow : "");
return myCondition == null ? "" : myCondition.toString() + (myRow >= 0 ? ": row=" + myRow : "");
}
}