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

Added ability to add simple conditions
This commit is contained in:
Denis.Zhdanov
2012-08-16 15:11:20 +04:00
parent 4aa841bd55
commit 3470a4beff
15 changed files with 508 additions and 143 deletions
@@ -161,10 +161,10 @@ public class JavaRearranger implements Rearranger<JavaElementArrangementEntry>,
}
}
if (compositeWithoutType.getOperands().size() == 1) {
parent.addChild(new HierarchicalArrangementSettingsNode(compositeWithoutType.getOperands().iterator().next()));
parent.setChild(new HierarchicalArrangementSettingsNode(compositeWithoutType.getOperands().iterator().next()));
}
else {
parent.addChild(new HierarchicalArrangementSettingsNode(compositeWithoutType));
parent.setChild(new HierarchicalArrangementSettingsNode(compositeWithoutType));
}
result.set(parent);
}
@@ -0,0 +1,84 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.application.options.codeStyle.arrangement;
import com.intellij.psi.codeStyle.arrangement.JavaRearranger;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsAtomNode;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
import com.intellij.ui.treeStructure.Tree;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.NotNull;
import org.junit.Before;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import java.util.Arrays;
import static org.junit.Assert.assertTrue;
/**
* @author Denis Zhdanov
* @since 8/16/12 11:04 AM
*/
public abstract class AbstractArrangementRuleEditingModelTest {
@NotNull protected ArrangementRuleEditingModelBuilder myBuilder;
@NotNull protected JTree myTree;
@NotNull protected DefaultMutableTreeNode myRoot;
@NotNull protected TIntObjectHashMap<ArrangementRuleEditingModel> myRowMappings;
@NotNull protected JavaRearranger myGrouper;
@Before
public void setUp() {
myBuilder = new ArrangementRuleEditingModelBuilder();
myRoot = new DefaultMutableTreeNode();
myTree = new Tree(myRoot);
myTree.expandPath(new TreePath(myRoot));
myRowMappings = new TIntObjectHashMap<ArrangementRuleEditingModel>();
myGrouper = new JavaRearranger();
}
protected void configure(@NotNull ArrangementSettingsNode settingsNode) {
myBuilder.build(settingsNode, myTree, myRoot, myGrouper, myRowMappings);
}
protected static ArrangementSettingsAtomNode atom(@NotNull Object condition) {
final ArrangementSettingType type;
if (condition instanceof ArrangementEntryType) {
type = ArrangementSettingType.TYPE;
}
else if (condition instanceof ArrangementModifier) {
type = ArrangementSettingType.MODIFIER;
}
else {
throw new IllegalArgumentException(String.format("Unexpected condition of class %s: %s", condition.getClass(), condition));
}
return new ArrangementSettingsAtomNode(type, condition);
}
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())),
myRowMappings.containsKey(row)
);
}
}
}
@@ -15,23 +15,10 @@
*/
package com.intellij.application.options.codeStyle.arrangement;
import com.intellij.psi.codeStyle.arrangement.JavaRearranger;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsAtomNode;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
import com.intellij.ui.treeStructure.Tree;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.NotNull;
import org.junit.Before;
import org.junit.Test;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import java.util.Arrays;
import static com.intellij.psi.codeStyle.arrangement.ArrangementUtil.and;
import static com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType.FIELD;
@@ -42,23 +29,7 @@ import static org.junit.Assert.*;
* @author Denis Zhdanov
* @since 08/15/2012
*/
public class ArrangementRuleEditingModelBuilderTest {
@NotNull private ArrangementRuleEditingModelBuilder myBuilder;
@NotNull private JTree myTree;
@NotNull private DefaultMutableTreeNode myRoot;
@NotNull private TIntObjectHashMap<ArrangementRuleEditingModel> myRowMappings;
@NotNull private JavaRearranger myGrouper;
@Before
public void setUp() {
myBuilder = new ArrangementRuleEditingModelBuilder();
myRoot = new DefaultMutableTreeNode();
myTree = new Tree(myRoot);
myTree.expandPath(new TreePath(myRoot));
myRowMappings = new TIntObjectHashMap<ArrangementRuleEditingModel>();
myGrouper = new JavaRearranger();
}
public class ArrangementRuleEditingModelBuilderTest extends AbstractArrangementRuleEditingModelTest {
@Test
public void mapToTheSameLayer() {
@@ -108,27 +79,4 @@ public class ArrangementRuleEditingModelBuilderTest {
assertNotNull(privateUiNode);
assertEquals(atom(PRIVATE), privateUiNode.getUserObject());
}
private 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())),
myRowMappings.containsKey(row)
);
}
}
private static ArrangementSettingsAtomNode atom(@NotNull Object condition) {
final ArrangementSettingType type;
if (condition instanceof ArrangementEntryType) {
type = ArrangementSettingType.TYPE;
}
else if (condition instanceof ArrangementModifier) {
type = ArrangementSettingType.MODIFIER;
}
else {
throw new IllegalArgumentException(String.format("Unexpected condition of class %s: %s", condition.getClass(), condition));
}
return new ArrangementSettingsAtomNode(type, condition);
}
}
@@ -0,0 +1,94 @@
/*
* 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.application.options.codeStyle.arrangement;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
import org.junit.Test;
import javax.swing.tree.DefaultMutableTreeNode;
import static com.intellij.psi.codeStyle.arrangement.ArrangementUtil.and;
import static com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType.FIELD;
import static com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier.PUBLIC;
import static com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier.STATIC;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
/**
* @author Denis Zhdanov
* @since 8/16/12 10:07 AM
*/
public class ArrangementRuleEditingModelImplTest extends AbstractArrangementRuleEditingModelTest {
@Test
public void addConditionToSameLevel() {
configure(atom(PUBLIC));
ArrangementRuleEditingModel model = myRowMappings.get(1);
assertNotNull(model);
model.addAndCondition(atom(STATIC));
DefaultMutableTreeNode child = (DefaultMutableTreeNode)myRoot.getFirstChild();
assertNotNull(child);
ArrangementSettingsNode expectedSettingsNode = and(atom(PUBLIC), atom(STATIC));
assertEquals(expectedSettingsNode, child.getUserObject());
assertEquals(expectedSettingsNode, model.getSettingsNode());
}
@Test
public void buildNewSingleLevel() {
configure(atom(PUBLIC));
ArrangementRuleEditingModel model = myRowMappings.get(1);
assertNotNull(model);
assertEquals(1, myRowMappings.size());
model.addAndCondition(atom(FIELD));
assertEquals(1, myRowMappings.size());
assertSame(model, myRowMappings.get(2));
assertEquals(and(atom(FIELD), atom(PUBLIC)), model.getSettingsNode());
DefaultMutableTreeNode fieldNode = (DefaultMutableTreeNode)myRoot.getFirstChild();
assertNotNull(fieldNode);
assertEquals(atom(FIELD), fieldNode.getUserObject());
DefaultMutableTreeNode publicNode = fieldNode.getFirstLeaf();
assertNotNull(publicNode);
assertEquals(atom(PUBLIC), publicNode.getUserObject());
}
@Test
public void addConditionToSameNestedLevel() {
configure(and(atom(FIELD), atom(PUBLIC)));
ArrangementRuleEditingModel model = myRowMappings.get(2);
assertNotNull(model);
assertEquals(1, myRowMappings.size());
model.addAndCondition(atom(STATIC));
assertEquals(1, myRowMappings.size());
assertSame(model, myRowMappings.get(2));
assertEquals(and(atom(FIELD), atom(PUBLIC), atom(STATIC)), model.getSettingsNode());
DefaultMutableTreeNode fieldNode = (DefaultMutableTreeNode)myRoot.getFirstChild();
assertNotNull(fieldNode);
assertEquals(atom(FIELD), fieldNode.getUserObject());
DefaultMutableTreeNode modifiersNode = fieldNode.getFirstLeaf();
assertNotNull(modifiersNode);
assertEquals(and(atom(PUBLIC), atom(STATIC)), modifiersNode.getUserObject());
}
}
@@ -96,7 +96,7 @@ public class ArrangementSettingsCompositeNode implements ArrangementSettingsNode
@Override
public String toString() {
return StringUtil.join(myOperands, myOperator == Operator.AND ? " and " : " or ");
return String.format("(%s)", StringUtil.join(myOperands, myOperator == Operator.AND ? " and " : " or "));
}
public enum Operator {
@@ -16,21 +16,21 @@
package com.intellij.psi.codeStyle.arrangement.model;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import org.jetbrains.annotations.Nullable;
/**
* // TODO den add doc
* {@link #getCurrent() Wraps} {@link ArrangementSettingsNode} in order to allow to build {@link #getChild() hierarchy}
* from a plain sequence of them
* <p/>
* Not thread-safe.
*
* @author Denis Zhdanov
* @since 8/8/12 1:23 PM
*/
public class HierarchicalArrangementSettingsNode {
@NotNull private final List<HierarchicalArrangementSettingsNode> myChildren = new ArrayList<HierarchicalArrangementSettingsNode>();
@NotNull private final ArrangementSettingsNode myCurrent;
@NotNull private final ArrangementSettingsNode myCurrent;
@Nullable private HierarchicalArrangementSettingsNode myChild;
public HierarchicalArrangementSettingsNode(@NotNull ArrangementSettingsNode current) {
myCurrent = current;
@@ -41,15 +41,15 @@ public class HierarchicalArrangementSettingsNode {
return myCurrent;
}
@NotNull
public List<HierarchicalArrangementSettingsNode> getChildren() {
return myChildren;
@Nullable
public HierarchicalArrangementSettingsNode getChild() {
return myChild;
}
public void addChild(@NotNull HierarchicalArrangementSettingsNode child) {
myChildren.add(child);
public void setChild(@Nullable HierarchicalArrangementSettingsNode child) {
myChild = child;
}
@Override
public String toString() {
return myCurrent.toString();
@@ -15,8 +15,11 @@
*/
package com.intellij.application.options.codeStyle.arrangement;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsAtomNode;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsCompositeNode;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNodeVisitor;
import com.intellij.ui.awt.RelativePoint;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -24,10 +27,12 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* // TODO den add doc
* {@link ArrangementNodeComponent Component} for showing {@link ArrangementSettingsCompositeNode composite nodes}.
* <p/>
* Not thread-safe.
*
@@ -43,11 +48,33 @@ public class ArrangementAndNodeComponent extends JPanel implements ArrangementNo
@NotNull private final ArrangementSettingsCompositeNode mySettingsNode;
@Nullable private Rectangle myScreenBounds;
public ArrangementAndNodeComponent(@NotNull ArrangementSettingsCompositeNode node, @NotNull ArrangementNodeComponentFactory factory) {
public ArrangementAndNodeComponent(@NotNull ArrangementSettingsCompositeNode node,
@NotNull ArrangementNodeComponentFactory factory,
@NotNull ArrangementNodeDisplayManager manager)
{
mySettingsNode = node;
setLayout(null);
int x = 0;
final Map<Object, ArrangementSettingsNode> operands = new HashMap<Object, ArrangementSettingsNode>();
ArrangementSettingsNodeVisitor visitor = new ArrangementSettingsNodeVisitor() {
@Override
public void visit(@NotNull ArrangementSettingsAtomNode node) {
operands.put(node.getValue(), node);
}
@Override
public void visit(@NotNull ArrangementSettingsCompositeNode node) {
operands.put(node, node);
}
};
for (ArrangementSettingsNode operand : node.getOperands()) {
operand.invite(visitor);
}
List<Object> ordered = manager.sort(operands.keySet());
for (Object key : ordered) {
ArrangementSettingsNode operand = operands.get(key);
assert operand != null;
ArrangementNodeComponent component = factory.getComponent(operand);
myComponents.add(component);
JComponent uiComponent = component.getUiComponent();
@@ -159,13 +186,16 @@ public class ArrangementAndNodeComponent extends JPanel implements ArrangementNo
));
}
int y = bounds.y + bounds.height / 2;
// TODO den shift y for the component
x += bounds.width;
// TODO den use right 'y'
if (i < components.length - 1) {
g.drawLine(x, y, x + BUBBLE_CONNECTOR_LENGTH, y);
}
x += BUBBLE_CONNECTOR_LENGTH;
}
}
@Override
public String toString() {
return String.format("(%s)", StringUtil.join(myComponents, " and "));
}
}
@@ -15,16 +15,21 @@
*/
package com.intellij.application.options.codeStyle.arrangement;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
import com.intellij.psi.codeStyle.arrangement.model.HierarchicalArrangementSettingsNode;
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementMatcherSettings;
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.util.*;
import java.util.List;
@@ -113,4 +118,76 @@ public class ArrangementConfigUtil {
}
return null;
}
public static int getDepth(@NotNull HierarchicalArrangementSettingsNode node) {
HierarchicalArrangementSettingsNode child = node.getChild();
return child == null ? 1 : 1 + getDepth(child);
}
@NotNull
public static HierarchicalArrangementSettingsNode getLast(@NotNull HierarchicalArrangementSettingsNode node) {
HierarchicalArrangementSettingsNode result = node;
for (HierarchicalArrangementSettingsNode child = node.getChild(); child != null; child = child.getChild()) {
result = child;
}
return result;
}
@NotNull
public static TreeNode getLastBefore(@NotNull TreeNode start, @NotNull TreeNode stop) throws IllegalArgumentException {
TreeNode result = start;
for (TreeNode n = start.getParent(); n != stop; n = n.getParent()) {
if (n == null) {
throw new IllegalArgumentException(String.format(
"Non-crossing paths detected - start: %s, stop: %s", new TreePath(start), new TreePath(stop)
));
}
result = n;
}
return result;
}
public static int distance(@NotNull TreeNode parent, @NotNull TreeNode child) {
if (parent == child) {
return 1;
}
int result = 1;
for (TreeNode n = child; n != null && n != parent; n = n.getParent()) {
result++;
}
return result;
}
/**
* @param uiParentNode UI tree node which should hold UI nodes created for representing given settings node
* @param settingsNode settings node which should be represented at the UI tree denoted by the given UI tree node
* @return pair {@code (bottom-most leaf node created; number of rows created)}
*/
@NotNull
public static Pair<DefaultMutableTreeNode, Integer> map(@NotNull DefaultMutableTreeNode uiParentNode,
@NotNull HierarchicalArrangementSettingsNode settingsNode)
{
DefaultMutableTreeNode uiNode = null;
int rowsCreated = 0;
for (int i = uiParentNode.getChildCount() - 1; i >= 0; i--) {
DefaultMutableTreeNode child = (DefaultMutableTreeNode)uiParentNode.getChildAt(i);
if (settingsNode.getCurrent().equals(child.getUserObject())) {
uiNode = child;
break;
}
}
if (uiNode == null) {
uiNode = new DefaultMutableTreeNode(settingsNode.getCurrent());
uiParentNode.add(uiNode);
rowsCreated++;
}
DefaultMutableTreeNode leaf = uiNode;
HierarchicalArrangementSettingsNode childSettingsNode = settingsNode.getChild();
if (childSettingsNode != null) {
Pair<DefaultMutableTreeNode, Integer> pair = map(uiNode, childSettingsNode);
leaf = pair.first;
rowsCreated += pair.second;
}
return Pair.create(leaf, rowsCreated);
}
}
@@ -43,7 +43,7 @@ public class ArrangementNodeComponentFactory {
@Override
public void visit(@NotNull ArrangementSettingsCompositeNode node) {
switch (node.getOperator()) {
case AND: ref.set(new ArrangementAndNodeComponent(node, ArrangementNodeComponentFactory.this)); break;
case AND: ref.set(new ArrangementAndNodeComponent(node, ArrangementNodeComponentFactory.this, myDisplayManager)); break;
case OR: // TODO den implement
}
}
@@ -21,6 +21,8 @@ import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsAtomNode;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
import org.jetbrains.annotations.NotNull;
import javax.swing.tree.TreeNode;
/**
* Combines and encapsulates information about arrangement matcher rules representation (tree nodes) and
* underlying {@link ArrangementSettingsNode data model}.
@@ -48,4 +50,10 @@ public interface ArrangementRuleEditingModel {
void addAndCondition(@NotNull ArrangementSettingsAtomNode node);
void removeAndCondition(@NotNull ArrangementSettingsNode node);
void addListener(@NotNull Listener listener);
interface Listener {
void onChanged(@NotNull TreeNode topMost, @NotNull TreeNode bottomMost);
}
}
@@ -15,6 +15,7 @@
*/
package com.intellij.application.options.codeStyle.arrangement;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
import com.intellij.psi.codeStyle.arrangement.model.HierarchicalArrangementSettingsNode;
@@ -91,9 +92,12 @@ public class ArrangementRuleEditingModelBuilder {
}
HierarchicalArrangementSettingsNode grouped = grouper.group(setting);
int rowsCreated = map(root, grouped);
ArrangementRuleEditingModelImpl model = new ArrangementRuleEditingModelImpl(setting);
rowMappings.put(initialInsertRow + rowsCreated - 1, model);
Pair<DefaultMutableTreeNode, Integer> pair = ArrangementConfigUtil.map(root, grouped);
DefaultMutableTreeNode topMostNode = (DefaultMutableTreeNode)ArrangementConfigUtil.getLastBefore(pair.first, root);
int row = initialInsertRow + pair.second - 1;
ArrangementRuleEditingModelImpl model
= new ArrangementRuleEditingModelImpl(setting, topMostNode, pair.first, grouper, rowMappings, row);
rowMappings.put(row, model);
}
private static int calculateRowsCount(@NotNull TreeNode node) {
@@ -103,30 +107,4 @@ public class ArrangementRuleEditingModelBuilder {
}
return result;
}
/**
* @param uiParentNode UI tree node which should hold UI nodes created for representing given settings node
* @param settingsNode settings node which should be represented at the UI tree denoted by the given UI tree node
* @return number of rows created
*/
private static int map(@NotNull DefaultMutableTreeNode uiParentNode, @NotNull HierarchicalArrangementSettingsNode settingsNode) {
DefaultMutableTreeNode uiNode = null;
int result = 0;
for (int i = uiParentNode.getChildCount() - 1; i >= 0; i--) {
DefaultMutableTreeNode child = (DefaultMutableTreeNode)uiParentNode.getChildAt(i);
if (settingsNode.getCurrent().equals(child.getUserObject())) {
uiNode = child;
break;
}
}
if (uiNode == null) {
uiNode = new DefaultMutableTreeNode(settingsNode.getCurrent());
uiParentNode.add(uiNode);
result++;
}
for (HierarchicalArrangementSettingsNode childSettingsNode : settingsNode.getChildren()) {
result += map(uiNode, childSettingsNode);
}
return result;
}
}
@@ -15,40 +15,76 @@
*/
package com.intellij.application.options.codeStyle.arrangement;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsAtomNode;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsCompositeNode;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNodeVisitor;
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.codeStyle.arrangement.ArrangementUtil;
import com.intellij.psi.codeStyle.arrangement.model.*;
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementSettingsGrouper;
import com.intellij.util.containers.hash.HashSet;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.NotNull;
import javax.swing.tree.DefaultMutableTreeNode;
import java.util.Arrays;
import java.util.Set;
/**
* Not thread-safe.
*
* @author Denis Zhdanov
* @since 8/15/12 2:40 PM
*/
public class ArrangementRuleEditingModelImpl implements ArrangementRuleEditingModel {
@NotNull private final Set<Object> myConditions = new HashSet<Object>();
@NotNull private static final MyConditionsBuilder CONDITIONS_BUILDER = new MyConditionsBuilder();
@NotNull private final Set<Listener> myListeners = new HashSet<Listener>();
@NotNull private final Set<Object> myConditions = new HashSet<Object>();
@NotNull private final TIntObjectHashMap<ArrangementRuleEditingModel> myRowMappings;
@NotNull private final ArrangementSettingsGrouper myGrouper;
@NotNull private DefaultMutableTreeNode myTopMost;
@NotNull private DefaultMutableTreeNode myBottomMost;
@NotNull private ArrangementSettingsNode mySettingsNode;
private int myRow;
public ArrangementRuleEditingModelImpl(@NotNull ArrangementSettingsNode node) {
/**
* Creates new <code>ArrangementRuleEditingModelImpl</code> object.
*
* @param node backing settings node
* @param topMost there is a possible case that a single settings node is shown in more than one visual line
* ({@link HierarchicalArrangementSettingsNode}). This argument is the top-most UI node used for the
* settings node representation
* @param bottomMost bottom-most UI node used for the given settings node representation
* @param grouper strategy that encapsulates information on how settings node should be displayed
* @param mappings {@code 'row -> model'} mappings
* @param row row number for which current model is registered at the given model mappings
*/
public ArrangementRuleEditingModelImpl(@NotNull ArrangementSettingsNode node,
@NotNull DefaultMutableTreeNode topMost,
@NotNull DefaultMutableTreeNode bottomMost,
@NotNull ArrangementSettingsGrouper grouper,
@NotNull TIntObjectHashMap<ArrangementRuleEditingModel> mappings,
int row)
{
mySettingsNode = node;
node.invite(new ArrangementSettingsNodeVisitor() {
@Override
public void visit(@NotNull ArrangementSettingsAtomNode node) {
myConditions.add(node.getValue());
}
myTopMost = topMost;
myBottomMost = bottomMost;
myGrouper = grouper;
myRowMappings = mappings;
myRow = row;
extractConditions(node);
}
@Override
public void visit(@NotNull ArrangementSettingsCompositeNode node) {
for (ArrangementSettingsNode operand : node.getOperands()) {
operand.invite(this);
}
}
});
private void extractConditions(@NotNull ArrangementSettingsNode node) {
myConditions.clear();
CONDITIONS_BUILDER.conditions = myConditions;
try {
node.invite(CONDITIONS_BUILDER);
}
finally {
CONDITIONS_BUILDER.conditions = null;
}
}
@NotNull
@@ -64,11 +100,74 @@ public class ArrangementRuleEditingModelImpl implements ArrangementRuleEditingMo
@Override
public void addAndCondition(@NotNull ArrangementSettingsAtomNode node) {
// TODO den implement
doAddAndCondition(node);
extractConditions(mySettingsNode);
notifyListeners();
}
private void doAddAndCondition(@NotNull ArrangementSettingsAtomNode node) {
ArrangementSettingsNode newNode = ArrangementUtil.and(mySettingsNode.clone(), node);
HierarchicalArrangementSettingsNode grouped = myGrouper.group(newNode);
int newDepth = ArrangementConfigUtil.getDepth(grouped);
int oldDepth = ArrangementConfigUtil.distance(myTopMost, myBottomMost);
if (newDepth == oldDepth) {
mySettingsNode = newNode;
myBottomMost.setUserObject(ArrangementConfigUtil.getLast(grouped).getCurrent());
return;
}
mySettingsNode = newNode;
DefaultMutableTreeNode parent = (DefaultMutableTreeNode)myTopMost.getParent();
parent.remove(myTopMost);
Pair<DefaultMutableTreeNode,Integer> pair = ArrangementConfigUtil.map(parent, grouped);
myTopMost = (DefaultMutableTreeNode)ArrangementConfigUtil.getLastBefore(pair.first, parent);
myBottomMost = pair.first;
int depthShift = newDepth - oldDepth;
int[] rows = myRowMappings.keys();
Arrays.sort(rows);
for (int i = rows.length - 1; i >= 0; i--) {
int row = rows[i];
if (row >= myRow) {
myRowMappings.put(row + depthShift, myRowMappings.get(row));
myRowMappings.remove(row);
}
else {
break;
}
}
myRow += depthShift;
}
@Override
public void removeAndCondition(@NotNull ArrangementSettingsNode node) {
// TODO den implement
}
@Override
public void addListener(@NotNull Listener listener) {
myListeners.add(listener);
}
private void notifyListeners() {
for (Listener listener : myListeners) {
listener.onChanged(myTopMost, myBottomMost);
}
}
private static class MyConditionsBuilder implements ArrangementSettingsNodeVisitor {
@NotNull Set<Object> conditions;
@Override
public void visit(@NotNull ArrangementSettingsAtomNode node) {
conditions.add(node.getValue());
}
@Override
public void visit(@NotNull ArrangementSettingsCompositeNode node) {
for (ArrangementSettingsNode operand : node.getOperands()) {
operand.invite(this);
}
}
}
}
@@ -21,9 +21,9 @@ import org.jetbrains.annotations.NotNull;
* @author Denis Zhdanov
* @since 8/15/12 9:14 AM
*/
public interface ArrangementRuleEditingListener {
public interface ArrangementRuleSelectionListener {
void startEditing(@NotNull ArrangementRuleEditingModel settings);
void onSelected(@NotNull ArrangementRuleEditingModel settings);
void stopEditing();
void selectionRemoved();
}
@@ -46,14 +46,18 @@ import java.util.List;
*/
public class ArrangementRuleTree {
@NotNull private final List<ArrangementRuleEditingListener> myListeners = new ArrayList<ArrangementRuleEditingListener>();
@NotNull private final TreeSelectionModel mySelectionModel = new MySelectionModel();
@NotNull private static final JLabel EMPTY_RENDERER = new JLabel("");
@NotNull private final List<ArrangementRuleSelectionListener> myListeners = new ArrayList<ArrangementRuleSelectionListener>();
@NotNull private final TreeSelectionModel mySelectionModel = new MySelectionModel();
@NotNull private final MyModelChangeListener myModelChangeListener = new MyModelChangeListener();
@NotNull private final TIntObjectHashMap<ArrangementNodeComponent> myRenderers =
new TIntObjectHashMap<ArrangementNodeComponent>();
@NotNull private final TIntObjectHashMap<ArrangementRuleEditingModel> myModels =
new TIntObjectHashMap<ArrangementRuleEditingModel>();
@NotNull private final DefaultTreeModel myTreeModel;
@NotNull private final Tree myTree;
@NotNull private final ArrangementNodeComponentFactory myFactory;
@@ -62,8 +66,8 @@ public class ArrangementRuleTree {
public ArrangementRuleTree(@NotNull ArrangementSettingsGrouper grouper, @NotNull ArrangementNodeDisplayManager displayManager) {
myFactory = new ArrangementNodeComponentFactory(displayManager);
DefaultMutableTreeNode root = new DefaultMutableTreeNode();
DefaultTreeModel treeModel = new DefaultTreeModel(root);
myTree = new Tree(treeModel) {
myTreeModel = new DefaultTreeModel(root);
myTree = new Tree(myTreeModel) {
@Override
protected void setExpandedState(TreePath path, boolean state) {
// Don't allow node collapse
@@ -85,7 +89,7 @@ public class ArrangementRuleTree {
try {
super.processMouseEvent(e);
if (mySkipSelectionChange) {
notifyEditingListeners(null);
notifySelectionListeners(null);
}
}
finally {
@@ -172,9 +176,16 @@ public class ArrangementRuleTree {
for (ArrangementSettingsNode setting : settings) {
builder.build(setting, myTree, root, grouper, myModels);
}
myModels.forEachValue(new TObjectProcedure<ArrangementRuleEditingModel>() {
@Override
public boolean execute(ArrangementRuleEditingModel model) {
model.addListener(myModelChangeListener);
return true;
}
});
}
public void addEditingListener(@NotNull ArrangementRuleEditingListener listener) {
public void addEditingListener(@NotNull ArrangementRuleSelectionListener listener) {
myListeners.add(listener);
}
@@ -271,17 +282,37 @@ public class ArrangementRuleTree {
}
}
private void notifyEditingListeners(@Nullable ArrangementRuleEditingModel model) {
for (ArrangementRuleEditingListener listener : myListeners) {
private void notifySelectionListeners(@Nullable ArrangementRuleEditingModel model) {
for (ArrangementRuleSelectionListener listener : myListeners) {
if (model == null) {
listener.stopEditing();
listener.selectionRemoved();
}
else {
listener.startEditing(model);
listener.onSelected(model);
}
}
}
private void onModelChange(@NotNull TreeNode topMost, @NotNull TreeNode bottomMost) {
mySkipSelectionChange = true;
try {
for (DefaultMutableTreeNode node = (DefaultMutableTreeNode)bottomMost; node != null; node = (DefaultMutableTreeNode)node.getParent()) {
TreePath path = new TreePath(node.getPath());
int row = myTree.getRowForPath(path);
myRenderers.remove(row);
myTreeModel.nodeChanged(node);
mySelectionModel.addSelectionPath(path);
getNodeComponentAt(row, (ArrangementSettingsNode)node.getUserObject()).setSelected(true);
if (node == topMost) {
break;
}
}
}
finally {
mySkipSelectionChange = false;
}
}
private class MyCellRenderer implements TreeCellRenderer {
@Override
public Component getTreeCellRendererComponent(JTree tree,
@@ -290,8 +321,10 @@ public class ArrangementRuleTree {
boolean expanded,
boolean leaf,
int row,
boolean hasFocus)
{
boolean hasFocus) {
if (row < 0) {
return EMPTY_RENDERER;
}
ArrangementSettingsNode node = (ArrangementSettingsNode)((DefaultMutableTreeNode)value).getUserObject();
return getNodeComponentAt(row, node).getUiComponent();
}
@@ -299,12 +332,26 @@ public class ArrangementRuleTree {
private class MySelectionModel extends DefaultTreeSelectionModel {
@Override
public void addSelectionPath(TreePath path) {
if (!mySkipSelectionChange) {
super.addSelectionPath(path);
}
}
@Override
public void setSelectionPath(TreePath path) {
if (!mySkipSelectionChange) {
super.setSelectionPath(path);
notifyEditingListeners(getActiveModel());
notifySelectionListeners(getActiveModel());
}
}
}
private class MyModelChangeListener implements ArrangementRuleEditingModel.Listener {
@Override
public void onChanged(@NotNull TreeNode topMost, @NotNull TreeNode bottomMost) {
onModelChange(topMost, bottomMost);
}
}
}
@@ -79,9 +79,9 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel {
}
}
});
ruleTree.addEditingListener(new ArrangementRuleEditingListener() {
ruleTree.addEditingListener(new ArrangementRuleSelectionListener() {
@Override
public void startEditing(@NotNull ArrangementRuleEditingModel model) {
public void onSelected(@NotNull ArrangementRuleEditingModel model) {
ruleEditor.updateState(model);
resetEditor.set(Boolean.FALSE);
try {
@@ -93,7 +93,7 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel {
}
@Override
public void stopEditing() {
public void selectionRemoved() {
editorPane.setCollapsed(true);
}
});