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

Re-organizing arrangement UI - tree -> list
This commit is contained in:
Denis.Zhdanov
2012-11-01 15:01:01 +04:00
parent 6cabd2fcc4
commit 49df96a495
42 changed files with 1598 additions and 3638 deletions
@@ -15,7 +15,7 @@
*/
package com.intellij.application.options;
import com.intellij.application.options.codeStyle.arrangement.ArrangementSettingsPanel;
import com.intellij.application.options.codeStyle.arrangement.newui.ArrangementSettingsPanel2;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.fileTypes.FileType;
@@ -26,7 +26,7 @@ import org.jetbrains.annotations.NotNull;
* @author Denis Zhdanov
* @since 8/8/12 8:57 AM
*/
public class JavaArrangementPanel extends ArrangementSettingsPanel {
public class JavaArrangementPanel extends ArrangementSettingsPanel2 {
public JavaArrangementPanel(@NotNull CodeStyleSettings settings) {
super(settings, JavaLanguage.INSTANCE);
@@ -154,7 +154,7 @@ public class JavaRearranger implements Rearranger<JavaElementArrangementEntry>,
return;
}
ArrangementCompositeMatchCondition composite = new ArrangementCompositeMatchCondition(ArrangementOperator.AND);
ArrangementCompositeMatchCondition composite = new ArrangementCompositeMatchCondition();
for (Object condition : conditions) {
composite.addOperand(new ArrangementAtomMatchCondition(ArrangementUtil.parseType(condition), condition));
}
@@ -1,98 +0,0 @@
/*
* 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.openapi.util.Pair;
import com.intellij.psi.codeStyle.arrangement.JavaRearranger;
import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
import com.intellij.psi.codeStyle.arrangement.match.StdArrangementEntryMatcher;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.containers.ContainerUtilRt;
import gnu.trove.TIntIntHashMap;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.NotNull;
import org.junit.Before;
import javax.swing.*;
import javax.swing.tree.TreePath;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import static org.junit.Assert.assertNotNull;
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 ArrangementTreeNode myRoot;
@NotNull protected TIntObjectHashMap<ArrangementRuleEditingModelImpl> myRowMappings;
@NotNull protected List<Set<ArrangementMatchCondition>> myGroupingRules;
@Before
public void setUp() {
myBuilder = new ArrangementRuleEditingModelBuilder();
myRoot = new ArrangementTreeNode(null);
myTree = new Tree(myRoot);
myTree.expandPath(new TreePath(myRoot));
myRowMappings = new TIntObjectHashMap<ArrangementRuleEditingModelImpl>();
myGroupingRules = ContainerUtilRt.newArrayList();
myGroupingRules.add(ContainerUtilRt.<ArrangementMatchCondition>newHashSet(
atom(ArrangementEntryType.FIELD), atom(ArrangementEntryType.METHOD)
));
}
protected void configure(@NotNull ArrangementMatchCondition matchCondition) {
Pair<ArrangementRuleEditingModelImpl, TIntIntHashMap> pair = myBuilder.build(
new StdArrangementMatchRule(new StdArrangementEntryMatcher(matchCondition)), myTree, myRoot, null, myGroupingRules
);
assertNotNull(pair);
myRowMappings.put(pair.first.getRow(), pair.first);
}
protected static ArrangementAtomMatchCondition 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 ArrangementAtomMatchCondition(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)
);
}
}
}
@@ -1,507 +0,0 @@
/*
* 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.ArrangementSettingType
import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition
import gnu.trove.TIntIntHashMap
import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable
import org.junit.Test
import javax.swing.tree.DefaultTreeModel
import static org.junit.Assert.assertEquals
/**
* @author Denis Zhdanov
* @since 8/17/12 1:12 PM
*/
@SuppressWarnings(["GroovyAssignabilityCheck", "GroovyVariableNotAssigned"])
class ArrangementConfigUtilTest {
@Test
void replace() {
// Init.
def one;
def four;
def initial = new TreeNodeBuilder().
'0' {
one = '1' {
'2' {
'3'()
four = '4'()
'5'()}
'6'()}
}
// Modify.
def replacement = new TreeNodeBuilder().
'1' {
'4'()
}
def rowMappings = doReplace(one, four, replacement)
// Check
def expected = new TreeNodeBuilder().
'0' {
'1' {
'2' {
'3'()}
'4'()
'2' {
'5'()}
'6'()}
}
assertNodesEqual(expected, initial)
checkRowMappings([5 : 6, 6 : 7], rowMappings)
}
@Test
void replaceWithMergeToNodeAbove() {
// Init.
def from;
def to;
def initial = new TreeNodeBuilder().
'0' {
'1'() {
'2'()
'3'()}
from = '4' {
to = '5'()
'6'()}
}
// Modify.
def replacement = new TreeNodeBuilder().
'1' {
'5'()
}
def rowMappings = doReplace(from, to, replacement)
// Check.
def expected = new TreeNodeBuilder().
'0' {
'1' {
'2'()
'3'()
'5'()}
'4' {
'6'()}
}
assertNodesEqual(expected, initial)
checkRowMappings([:], rowMappings)
}
@Test
void replaceWithTwoLevelMergeToNodeAbove() {
// Init.
def node;
def initial = new TreeNodeBuilder().
'0' {
'1'() {
'2'()
'3'()}
node = '4'()
'5'() {
'6'()}
}
// Modify.
def replacement = new TreeNodeBuilder().
'1' {
'4'()
}
def rowMappings = doReplace(node, node, replacement)
// Check.
def expected = new TreeNodeBuilder().
'0' {
'1' {
'2'()
'3'()
'4'()}
'5' {
'6'()}
}
assertNodesEqual(expected, initial)
checkRowMappings([:], rowMappings)
}
@Test
void replaceWithTwoLevelMergeToNodeBelow() {
// Init.
def from;
def to;
def initial = new TreeNodeBuilder().
'0' {
from = '1'() {
'2'()
to = '3'()}
'4'() {
'5'() }
}
// Modify.
def replacement = new TreeNodeBuilder().
'4' {
'3'()
}
def rowMappings = doReplace(from, to, replacement)
// Check.
def expected = new TreeNodeBuilder().
'0' {
'1' {
'2'()}
'4' {
'3'()
'5'()}
}
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(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() {
def initial = new TreeNodeBuilder().
'0' {
'1'()
'2' {
'3' {
'4'()
}
}
'5'()
}
def toAdd = new TreeNodeBuilder().
'2' {
'3' {
'6'()
}
}
def expected = new TreeNodeBuilder().
'0' {
'2' {
'3' {
'6'()
}
}
'1'()
'2' {
'3' {
'4'()
}
}
'5'()
}
doInsert(initial, 0, toAdd)
assertNodesEqual(expected, initial)
}
@Test
void addWithMergeAbove() {
def initial = new TreeNodeBuilder().
'0' {
'1'()
'2' {
'3' {
'4'()
}
}
'5'()
}
def toAdd = new TreeNodeBuilder().
'2' {
'3' {
'6'()
}
}
def expected = new TreeNodeBuilder().
'0' {
'1'()
'2' {
'3' {
'6'()
'4'()
}
}
'5'()
}
doInsert(initial, 1, toAdd)
assertNodesEqual(expected, initial)
}
@Test
void addWithMergeBelow() {
def initial = new TreeNodeBuilder().
'0' {
'1'()
'2' {
'3' {
'4'()
}
}
'5'()
}
def toAdd = new TreeNodeBuilder().
'2' {
'3' {
'6'()
}
}
def expected = new TreeNodeBuilder().
'0' {
'1'()
'2' {
'3' {
'4'()
'6'()
}
}
'5'()
}
doInsert(initial, 2, toAdd)
assertNodesEqual(expected, initial)
}
@Test
void addWithoutMergeBelow() {
def initial = new TreeNodeBuilder().
'0' {
'1'()
'2' {
'3' {
'4'()
}
}
'5'()
}
def toAdd = new TreeNodeBuilder().
'2' {
'3' {
'6'()
}
}
def expected = new TreeNodeBuilder().
'0' {
'1'()
'2' {
'3' {
'4'()
}
}
'5'()
'2' {
'3' {
'6'()
}
}
}
doInsert(initial, 3, toAdd)
assertNodesEqual(expected, initial)
}
@Test
void removeFirst() {
def from;
def to;
def initial = new TreeNodeBuilder().
'0' {
from = '1' {
to = '2'()
'3'()}
'4'()
}
def expected = new TreeNodeBuilder().
'0' {
'1' {
'3'()}
'4'()
}
def rowMappings = doRemove(from, to)
assertNodesEqual(expected, initial)
checkRowMappings([ 3 : 2, 4 : 3 ], rowMappings)
}
@Test
void removeWithMerge() {
def from;
def to;
def initial = new TreeNodeBuilder().
'0' {
'1' {
'2'()}
from = '3'() {
to = '4'()}
'1' {
'5'()}
}
def expected = new TreeNodeBuilder().
'0' {
'1' {
'2'()
'5'()}
}
def rowMappings = doRemove(from, to)
assertNodesEqual(expected, initial)
checkRowMappings([ 6 : 3 ], rowMappings)
}
@Test
void removeSingleNodeWithMerge() {
def node;
def initial = new TreeNodeBuilder().
'0' {
'1' {
'2'()}
node = '3'()
'1' {
'4'()}
}
def expected = new TreeNodeBuilder().
'0' {
'1' {
'2'()
'4'()}
}
def rowMappings = doRemove(node, node)
assertNodesEqual(expected, initial)
checkRowMappings([ 5 : 3 ], rowMappings)
}
@Test
void removeWithSingleNodeBelowMerge() {
def node;
def initial = new TreeNodeBuilder().
'0' {
'1' {
'2'()}
node = '3'()
'1'()
}
def expected = new TreeNodeBuilder().
'0' {
'1' {
'2'()}
'1'()
}
def rowMappings = doRemove(node, node)
assertNodesEqual(expected, initial)
checkRowMappings([ 4 : 3 ], rowMappings)
}
private static def doReplace(from, to, replacement) {
ArrangementConfigUtil.replace(from, to, replacement, new DefaultTreeModel(ArrangementConfigUtil.getRoot(from)), true)
}
private static def doInsert(parent, i, child) {
ArrangementConfigUtil.insert(parent, i, child, new DefaultTreeModel(ArrangementConfigUtil.getRoot(parent)))
}
private static def doRemove(from, to) {
ArrangementConfigUtil.remove(from, to, new DefaultTreeModel(ArrangementConfigUtil.getRoot(from)), true)
}
private static void assertNodesEqual(@NotNull ArrangementTreeNode expected, @NotNull ArrangementTreeNode actual) {
assertEquals(expected.userObject, actual.userObject)
assertEquals(expected.childCount, actual.childCount)
for (i in 0..<expected.childCount) {
assertNodesEqual(expected.getChildAt(i), actual.getChildAt(i))
}
}
private static void checkRowMappings(@NotNull Map<Integer, Integer> expected, @NotNull TIntIntHashMap actual) {
assertEquals(expected.size(), actual.size())
expected.each {key, value -> assertEquals(value, actual.get(key)) }
}
}
public class TreeNodeBuilder extends BuilderSupport {
@Override
protected Object createNode(Object name) {
def result = new ArrangementTreeNode(new ArrangementAtomMatchCondition(ArrangementSettingType.MODIFIER, name))
currentNode?.add(result)
result
}
@Nullable
ArrangementTreeNode getCurrentNode() {
getCurrent() as ArrangementTreeNode
}
@Override protected void setParent(Object parent, Object child) { }
@Override protected Object createNode(Object name, Object value) { throw new UnsupportedOperationException() }
@Override protected Object createNode(Object name, Map attributes) { throw new UnsupportedOperationException() }
@Override protected Object createNode(Object name, Map attributes, Object value) { throw new UnsupportedOperationException() }
}
@@ -1,77 +0,0 @@
/*
* 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 org.junit.Test;
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.*;
import static org.junit.Assert.*;
/**
* @author Denis Zhdanov
* @since 08/15/2012
*/
public class ArrangementRuleEditingModelBuilderTest extends AbstractArrangementRuleEditingModelTest {
@Test
public void mapToTheSameLayer() {
configure(and(atom(PUBLIC), atom(STATIC)));
checkRows(1);
ArrangementRuleEditingModel model = myRowMappings.get(1);
assertTrue(model.hasCondition(PUBLIC));
assertTrue(model.hasCondition(STATIC));
assertFalse(model.hasCondition(PRIVATE));
assertEquals(1, myRoot.getChildCount());
assertEquals(and(atom(PUBLIC), atom(STATIC)), myRoot.getFirstChild().getBackingCondition());
}
@Test
public void splitIntoTwoLayers() {
configure(and(atom(FIELD), atom(PUBLIC), atom(STATIC)));
checkRows(2);
ArrangementTreeNode fieldUiNode = myRoot.getFirstChild();
assertNotNull(fieldUiNode);
assertEquals(atom(FIELD), fieldUiNode.getBackingCondition());
ArrangementTreeNode modifiersUiNode = fieldUiNode.getFirstChild();
assertNotNull(modifiersUiNode);
assertEquals(and(atom(PUBLIC), atom(STATIC)), modifiersUiNode.getBackingCondition());
}
@Test
public void addToExistingLayer() {
configure(and(atom(PUBLIC), atom(STATIC), atom(FIELD)));
configure(and(atom(PRIVATE), atom(FIELD)));
checkRows(2, 3);
ArrangementTreeNode fieldUiNode = myRoot.getFirstChild();
assertNotNull(fieldUiNode);
assertEquals(atom(FIELD), fieldUiNode.getBackingCondition());
ArrangementTreeNode publicStaticUiNode = fieldUiNode.getFirstChild();
assertNotNull(publicStaticUiNode);
assertEquals(and(atom(PUBLIC), atom(STATIC)), publicStaticUiNode.getBackingCondition());
ArrangementTreeNode privateUiNode = fieldUiNode.getLastChild();
assertNotNull(privateUiNode);
assertEquals(atom(PRIVATE), privateUiNode.getBackingCondition());
}
}
@@ -1,197 +0,0 @@
/*
* 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.ArrangementMatchCondition;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
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.*;
/**
* @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));
ArrangementTreeNode child = myRoot.getFirstChild();
assertNotNull(child);
ArrangementMatchCondition expectedMatchCondition = and(atom(PUBLIC), atom(STATIC));
assertEquals(expectedMatchCondition, child.getBackingCondition());
assertEquals(expectedMatchCondition, model.getCondition());
}
@Test
public void buildNewSingleLevel() {
configure(atom(PUBLIC));
ArrangementRuleEditingModelImpl model = myRowMappings.get(1);
assertNotNull(model);
assertEquals(1, myRowMappings.size());
model.addAndCondition(atom(FIELD));
assertEquals(2, model.getRow());
assertEquals(and(atom(FIELD), atom(PUBLIC)), model.getCondition());
ArrangementTreeNode fieldNode = myRoot.getFirstChild();
assertNotNull(fieldNode);
assertEquals(atom(FIELD), fieldNode.getBackingCondition());
ArrangementTreeNode publicNode = fieldNode.getFirstChild();
assertNotNull(publicNode);
assertEquals(atom(PUBLIC), publicNode.getBackingCondition());
}
@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.getCondition());
ArrangementTreeNode fieldNode = myRoot.getFirstChild();
assertNotNull(fieldNode);
assertEquals(atom(FIELD), fieldNode.getBackingCondition());
ArrangementTreeNode modifiersNode = fieldNode.getFirstChild();
assertNotNull(modifiersNode);
assertEquals(and(atom(PUBLIC), atom(STATIC)), modifiersNode.getBackingCondition());
}
@Test
public void removeAndKeepAllLevels() {
configure(and(atom(FIELD), atom(PUBLIC), atom(STATIC)));
ArrangementRuleEditingModelImpl model = myRowMappings.get(2);
assertNotNull(model);
assertEquals(1, myRowMappings.size());
model.removeAndCondition(atom(PUBLIC));
assertEquals(1, myRowMappings.size());
assertSame(model, myRowMappings.get(2));
assertEquals(and(atom(FIELD), atom(STATIC)), model.getCondition());
ArrangementTreeNode fieldNode = myRoot.getFirstChild();
assertNotNull(fieldNode);
assertEquals(atom(FIELD), fieldNode.getBackingCondition());
ArrangementTreeNode modifiersNode = fieldNode.getFirstChild();
assertNotNull(modifiersNode);
assertEquals(atom(STATIC), modifiersNode.getBackingCondition());
checkModelTreeNodesConsistency(model);
}
@Test
public void removeLastRowCondition() {
configure(and(atom(FIELD), atom(PUBLIC)));
ArrangementRuleEditingModelImpl model = myRowMappings.get(2);
assertNotNull(model);
assertEquals(1, myRowMappings.size());
model.removeAndCondition(atom(PUBLIC));
assertEquals(1, model.getRow());
assertEquals(atom(FIELD), model.getCondition());
ArrangementTreeNode fieldNode = myRoot.getFirstChild();
assertNotNull(fieldNode);
assertEquals(atom(FIELD), fieldNode.getBackingCondition());
assertEquals(0, fieldNode.getChildCount());
}
@Test
public void removeFirstRowConditionFromMultiChildrenParent() {
configure(and(atom(FIELD), atom(PUBLIC)));
configure(and(atom(FIELD), atom(STATIC)));
ArrangementRuleEditingModelImpl modelToChange = myRowMappings.get(2);
assertNotNull(modelToChange);
ArrangementRuleEditingModelImpl siblingModel = myRowMappings.get(3);
assertNotNull(siblingModel);
assertEquals(2, myRowMappings.size());
modelToChange.removeAndCondition(atom(PUBLIC));
assertSame(3, siblingModel.getRow());
assertEquals(and(atom(FIELD), atom(STATIC)), siblingModel.getCondition());
ArrangementTreeNode fieldNode = myRoot.getFirstChild();
assertNotNull(fieldNode);
assertEquals(atom(FIELD), fieldNode.getBackingCondition());
ArrangementTreeNode staticNode = fieldNode.getFirstChild();
assertNotNull(staticNode);
assertEquals(atom(STATIC), staticNode.getBackingCondition());
assertNull(staticNode.getNextNode());
}
@Test
public void removeLastRowConditionFromMultiChildrenParent() {
configure(and(atom(FIELD), atom(PUBLIC)));
configure(and(atom(FIELD), atom(STATIC)));
assertEquals(2, myRowMappings.size());
ArrangementRuleEditingModel siblingModel = myRowMappings.get(2);
assertNotNull(siblingModel);
ArrangementRuleEditingModel modelToChange = myRowMappings.get(3);
assertNotNull(modelToChange);
modelToChange.removeAndCondition(atom(STATIC));
assertEquals(2, myRowMappings.size());
assertSame(siblingModel, myRowMappings.get(2));
assertEquals(and(atom(FIELD), atom(PUBLIC)), siblingModel.getCondition());
ArrangementTreeNode fieldNode = myRoot.getFirstChild();
assertNotNull(fieldNode);
assertEquals(atom(FIELD), fieldNode.getBackingCondition());
ArrangementTreeNode publicNode = fieldNode.getFirstChild();
assertNotNull(publicNode);
assertEquals(atom(PUBLIC), publicNode.getBackingCondition());
assertNull(publicNode.getNextNode());
}
private void checkModelTreeNodesConsistency(@NotNull ArrangementRuleEditingModelImpl model) {
model.refreshTreeNodes();
ArrangementTreeNode root = ArrangementConfigUtil.getRoot(model.getBottomMost());
assertSame(myRoot, root);
assertSame(root, ArrangementConfigUtil.getRoot(model.getTopMost()));
}
}
@@ -0,0 +1,71 @@
/*
* 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.psi.codeStyle.arrangement;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementCompositeMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
import com.intellij.util.containers.ContainerUtilRt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.Set;
/**
* @author Denis Zhdanov
* @since 10/31/12 12:30 PM
*/
public class ArrangementConditionInfo {
@NotNull private final Set<ArrangementAtomMatchCondition> myAtomConditions = ContainerUtilRt.newHashSet();
@NotNull private final Set<Object> myConditions = ContainerUtilRt.newHashSet();
public void addAtomCondition(@NotNull ArrangementAtomMatchCondition condition) {
myAtomConditions.add(condition);
myConditions.add(condition.getValue());
}
public boolean hasCondition(@NotNull Object condition) {
return myConditions.contains(condition);
}
public void removeCondition(@NotNull Object condition) {
if (!myConditions.remove(condition)) {
return;
}
for (Iterator<ArrangementAtomMatchCondition> iterator = myAtomConditions.iterator(); iterator.hasNext(); ) {
ArrangementAtomMatchCondition c = iterator.next();
if (c.getValue().equals(condition)) {
iterator.remove();
break;
}
}
}
@Nullable
public ArrangementMatchCondition buildCondition() {
if (myAtomConditions.isEmpty()) {
return null;
}
if (myAtomConditions.size() == 1) {
return myAtomConditions.iterator().next();
}
else {
return new ArrangementCompositeMatchCondition(myAtomConditions);
}
}
}
@@ -26,8 +26,6 @@ import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
@@ -63,8 +61,8 @@ public class ArrangementUtil {
//endregion
@NotNull
public static ArrangementMatchCondition and(@NotNull ArrangementMatchCondition... nodes) {
final ArrangementCompositeMatchCondition result = new ArrangementCompositeMatchCondition(ArrangementOperator.AND);
public static ArrangementMatchCondition combine(@NotNull ArrangementMatchCondition... nodes) {
final ArrangementCompositeMatchCondition result = new ArrangementCompositeMatchCondition();
final ArrangementMatchConditionVisitor visitor = new ArrangementMatchConditionVisitor() {
@Override
public void visit(@NotNull ArrangementAtomMatchCondition node) {
@@ -73,13 +71,8 @@ public class ArrangementUtil {
@Override
public void visit(@NotNull ArrangementCompositeMatchCondition node) {
if (node.getOperator() == ArrangementOperator.AND) {
for (ArrangementMatchCondition operand : node.getOperands()) {
operand.invite(this);
}
}
else {
result.addOperand(node);
for (ArrangementMatchCondition operand : node.getOperands()) {
operand.invite(this);
}
}
};
@@ -157,81 +150,6 @@ public class ArrangementUtil {
}
}
@SuppressWarnings("AssignmentToForLoopParameter")
@NotNull
public static HierarchicalArrangementConditionNode group(@NotNull ArrangementMatchCondition condition,
@NotNull List<Set<ArrangementMatchCondition>> groupingRules)
{
if (groupingRules.isEmpty()) {
// No grouping rules have been provided, use a flat structure.
return new HierarchicalArrangementConditionNode(condition);
}
final List<ArrangementMatchCondition> conditions = new ArrayList<ArrangementMatchCondition>();
condition.invite(new ArrangementMatchConditionVisitor() {
@Override
public void visit(@NotNull ArrangementAtomMatchCondition condition) {
conditions.add(condition);
}
@Override
public void visit(@NotNull ArrangementCompositeMatchCondition condition) {
if (condition.getOperator() == ArrangementOperator.AND && conditions.isEmpty() /* Don't process nested composite conditions*/) {
for (ArrangementMatchCondition operand : condition.getOperands()) {
conditions.add(operand);
}
}
else {
conditions.add(condition);
}
}
});
if (conditions.isEmpty()) {
return new HierarchicalArrangementConditionNode(condition);
}
HierarchicalArrangementConditionNode result = null;
for (Set<ArrangementMatchCondition> rules : groupingRules) {
for (ArrangementMatchCondition rule : rules) {
for (int i = 0; i < conditions.size(); i++) {
ArrangementMatchCondition c = conditions.get(i);
if (rule.equals(c)) {
conditions.remove(i--);
HierarchicalArrangementConditionNode node = new HierarchicalArrangementConditionNode(c);
if (result == null) {
result = node;
}
else {
result.setChild(node);
}
}
}
}
}
if (!conditions.isEmpty()) {
HierarchicalArrangementConditionNode node;
if (conditions.size() == 1) {
node = new HierarchicalArrangementConditionNode(conditions.get(0));
}
else {
ArrangementCompositeMatchCondition c = new ArrangementCompositeMatchCondition(ArrangementOperator.AND);
for (ArrangementMatchCondition operand : conditions) {
c.addOperand(operand);
}
node = new HierarchicalArrangementConditionNode(c);
}
if (result == null) {
result = node;
}
else {
result.setChild(node);
}
}
assert result != null;
return result;
}
public static <T> Set<T> flatten(@NotNull Iterable<? extends Iterable<T>> data) {
Set<T> result = ContainerUtilRt.newHashSet();
for (Iterable<T> i : data) {
@@ -241,4 +159,23 @@ public class ArrangementUtil {
}
return result;
}
@NotNull
public static ArrangementConditionInfo extractConditions(@NotNull ArrangementMatchCondition condition) {
final ArrangementConditionInfo result = new ArrangementConditionInfo();
condition.invite(new ArrangementMatchConditionVisitor() {
@Override
public void visit(@NotNull ArrangementAtomMatchCondition condition) {
result.addAtomCondition(condition);
}
@Override
public void visit(@NotNull ArrangementCompositeMatchCondition condition) {
for (ArrangementMatchCondition operand : condition.getOperands()) {
operand.invite(this);
}
}
});
return result;
}
}
@@ -16,7 +16,6 @@
package com.intellij.psi.codeStyle.arrangement.match;
import com.intellij.psi.codeStyle.arrangement.ArrangementEntry;
import com.intellij.psi.codeStyle.arrangement.ArrangementOperator;
import org.jetbrains.annotations.NotNull;
import java.util.*;
@@ -28,35 +27,19 @@ import java.util.*;
public class CompositeArrangementEntryMatcher implements ArrangementEntryMatcher {
@NotNull private final Set<ArrangementEntryMatcher> myMatchers = new HashSet<ArrangementEntryMatcher>();
@NotNull private final ArrangementOperator myOperator;
public CompositeArrangementEntryMatcher(@NotNull ArrangementOperator operator, @NotNull ArrangementEntryMatcher... matchers) {
myOperator = operator;
public CompositeArrangementEntryMatcher(@NotNull ArrangementEntryMatcher... matchers) {
myMatchers.addAll(Arrays.asList(matchers));
}
@Override
public boolean isMatched(@NotNull ArrangementEntry entry) {
for (ArrangementEntryMatcher matcher : myMatchers) {
boolean matched = matcher.isMatched(entry);
if (matched && myOperator == ArrangementOperator.OR) {
return true;
}
else if (!matched && myOperator == ArrangementOperator.AND) {
if (!matcher.isMatched(entry)) {
return false;
}
}
return myOperator == ArrangementOperator.AND;
}
@NotNull
public ArrangementOperator getOperator() {
return myOperator;
}
@NotNull
public Collection<ArrangementEntryMatcher> getMatchers() {
return myMatchers;
return true;
}
public void addMatcher(@NotNull ArrangementEntryMatcher rule) {
@@ -65,9 +48,7 @@ public class CompositeArrangementEntryMatcher implements ArrangementEntryMatcher
@Override
public int hashCode() {
int result = myMatchers.hashCode();
result = 31 * result + myOperator.hashCode();
return result;
return myMatchers.hashCode();
}
@Override
@@ -77,14 +58,11 @@ public class CompositeArrangementEntryMatcher implements ArrangementEntryMatcher
CompositeArrangementEntryMatcher matcher = (CompositeArrangementEntryMatcher)o;
if (!myMatchers.equals(matcher.myMatchers)) return false;
if (myOperator != matcher.myOperator) return false;
return true;
return myMatchers.equals(matcher.myMatchers);
}
@Override
public String toString() {
return String.format("%s of those: %s", myOperator == ArrangementOperator.AND ? "all" : "any", myMatchers);
return String.format("all of those: %s", myMatchers);
}
}
@@ -16,7 +16,6 @@
package com.intellij.psi.codeStyle.arrangement.match;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.codeStyle.arrangement.ArrangementOperator;
import com.intellij.psi.codeStyle.arrangement.model.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
@@ -72,14 +71,13 @@ public class DefaultArrangementEntryMatcherSerializer {
}
};
private static final Logger LOG = Logger.getInstance("#" + DefaultArrangementEntryMatcherSerializer.class.getName());
private static final Set<String> COMPOSITE_OPERATORS = new HashSet<String>();
private static final Logger LOG = Logger.getInstance("#" + DefaultArrangementEntryMatcherSerializer.class.getName());
@NotNull private static final String COMPOSITE_CONDITION_NAME = "AND";
private static final Set<String> ATOM_SETTINGS_TYPES = new HashSet<String>();
static {
for (ArrangementOperator operator : ArrangementOperator.values()) {
COMPOSITE_OPERATORS.add(operator.toString());
}
for (ArrangementSettingType type : ArrangementSettingType.values()) {
ATOM_SETTINGS_TYPES.add(type.toString());
}
@@ -115,7 +113,7 @@ public class DefaultArrangementEntryMatcherSerializer {
@Nullable
private static ArrangementMatchCondition deserializeCondition(@NotNull Element matcherElement) {
String name = matcherElement.getName();
if (!COMPOSITE_OPERATORS.contains(name)) {
if (!COMPOSITE_CONDITION_NAME.equals(name)) {
if (ATOM_SETTINGS_TYPES.contains(name)) {
return deserializeAtomCondition(matcherElement);
}
@@ -123,13 +121,13 @@ public class DefaultArrangementEntryMatcherSerializer {
LOG.warn(String.format(
"Can't deserialize an arrangement entry matcher from matchElement with name '%s'. Reason: only the following elements"
+ "are supported: %s and %s",
name, COMPOSITE_OPERATORS, ATOM_SETTINGS_TYPES
name, COMPOSITE_CONDITION_NAME, ATOM_SETTINGS_TYPES
));
return null;
}
}
else {
ArrangementCompositeMatchCondition composite = new ArrangementCompositeMatchCondition(ArrangementOperator.valueOf(name));
ArrangementCompositeMatchCondition composite = new ArrangementCompositeMatchCondition();
for (Object child : matcherElement.getChildren()) {
ArrangementMatchCondition deserialised = deserializeCondition((Element)child);
if (deserialised != null) {
@@ -175,7 +173,7 @@ public class DefaultArrangementEntryMatcherSerializer {
@Override
public void visit(@NotNull ArrangementCompositeMatchCondition condition) {
Element composite = new Element(condition.getOperator().toString());
Element composite = new Element(COMPOSITE_CONDITION_NAME);
if (result == null) {
result = composite;
}
@@ -16,7 +16,6 @@
package com.intellij.psi.codeStyle.arrangement.match;
import com.intellij.psi.codeStyle.arrangement.ArrangementEntry;
import com.intellij.psi.codeStyle.arrangement.ArrangementOperator;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementCompositeMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
@@ -93,7 +92,6 @@ public class StdArrangementEntryMatcher implements ArrangementEntryMatcher {
@NotNull private final Set<ArrangementEntryType> myTypes = EnumSet.noneOf(ArrangementEntryType.class);
@NotNull private final Set<ArrangementModifier> myModifiers = EnumSet.noneOf(ArrangementModifier.class);
private ArrangementOperator myOperator;
private boolean nestedComposite;
@Override
@@ -110,7 +108,6 @@ public class StdArrangementEntryMatcher implements ArrangementEntryMatcher {
@Override
public void visit(@NotNull ArrangementCompositeMatchCondition condition) {
if (!nestedComposite) {
myOperator = condition.getOperator();
nestedComposite = true;
for (ArrangementMatchCondition c : condition.getOperands()) {
c.invite(this);
@@ -126,7 +123,7 @@ public class StdArrangementEntryMatcher implements ArrangementEntryMatcher {
public ArrangementEntryMatcher getMatcher() {
ByTypeArrangementEntryMatcher byType = myTypes.isEmpty() ? null : new ByTypeArrangementEntryMatcher(myTypes);
ByModifierArrangementEntryMatcher byModifiers = myModifiers.isEmpty() ? null : new ByModifierArrangementEntryMatcher(myModifiers);
if (byType == null && byModifiers == null && (myOperator == null || myMatchers.isEmpty())) {
if (byType == null && byModifiers == null && myMatchers.isEmpty()) {
return ArrangementEntryMatcher.EMPTY;
}
if (myMatchers.isEmpty() && (byType == null ^ byModifiers == null)) {
@@ -136,7 +133,7 @@ public class StdArrangementEntryMatcher implements ArrangementEntryMatcher {
return myMatchers.get(0);
}
else {
CompositeArrangementEntryMatcher result = new CompositeArrangementEntryMatcher(myOperator);
CompositeArrangementEntryMatcher result = new CompositeArrangementEntryMatcher();
for (ArrangementEntryMatcher matcher : myMatchers) {
result.addMatcher(matcher);
}
@@ -16,10 +16,10 @@
package com.intellij.psi.codeStyle.arrangement.model;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.codeStyle.arrangement.ArrangementOperator;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Set;
/**
@@ -33,10 +33,12 @@ import java.util.Set;
public class ArrangementCompositeMatchCondition implements ArrangementMatchCondition {
@NotNull private final Set<ArrangementMatchCondition> myOperands = new HashSet<ArrangementMatchCondition>();
@NotNull private final ArrangementOperator myOperator;
public ArrangementCompositeMatchCondition(@NotNull ArrangementOperator operator) {
myOperator = operator;
public ArrangementCompositeMatchCondition() {
}
public ArrangementCompositeMatchCondition(@NotNull Collection<? extends ArrangementMatchCondition> conditions) {
myOperands.addAll(conditions);
}
@NotNull
@@ -54,11 +56,6 @@ public class ArrangementCompositeMatchCondition implements ArrangementMatchCondi
myOperands.remove(condition);
}
@NotNull
public ArrangementOperator getOperator() {
return myOperator;
}
@Override
public void invite(@NotNull ArrangementMatchConditionVisitor visitor) {
visitor.visit(this);
@@ -67,7 +64,7 @@ public class ArrangementCompositeMatchCondition implements ArrangementMatchCondi
@NotNull
@Override
public ArrangementCompositeMatchCondition clone() {
ArrangementCompositeMatchCondition result = new ArrangementCompositeMatchCondition(myOperator);
ArrangementCompositeMatchCondition result = new ArrangementCompositeMatchCondition();
for (ArrangementMatchCondition operand : myOperands) {
result.addOperand(operand.clone());
}
@@ -76,9 +73,7 @@ public class ArrangementCompositeMatchCondition implements ArrangementMatchCondi
@Override
public int hashCode() {
int result = myOperands.hashCode();
result = 31 * result + myOperator.hashCode();
return result;
return myOperands.hashCode();
}
@Override
@@ -92,18 +87,11 @@ public class ArrangementCompositeMatchCondition implements ArrangementMatchCondi
ArrangementCompositeMatchCondition setting = (ArrangementCompositeMatchCondition)o;
if (!myOperands.equals(setting.myOperands)) {
return false;
}
if (myOperator != setting.myOperator) {
return false;
}
return true;
return myOperands.equals(setting.myOperands);
}
@Override
public String toString() {
return String.format("(%s)", StringUtil.join(myOperands, myOperator == ArrangementOperator.AND ? " and " : " or "));
return String.format("(%s)", StringUtil.join(myOperands, " and "));
}
}
@@ -1,57 +0,0 @@
/*
* 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.psi.codeStyle.arrangement.model;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* {@link #getCurrent() Wraps} {@link ArrangementMatchCondition} 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 HierarchicalArrangementConditionNode {
@NotNull private final ArrangementMatchCondition myCurrent;
@Nullable private HierarchicalArrangementConditionNode myChild;
public HierarchicalArrangementConditionNode(@NotNull ArrangementMatchCondition current) {
myCurrent = current;
}
@NotNull
public ArrangementMatchCondition getCurrent() {
return myCurrent;
}
@Nullable
public HierarchicalArrangementConditionNode getChild() {
return myChild;
}
public void setChild(@Nullable HierarchicalArrangementConditionNode child) {
myChild = child;
}
@Override
public String toString() {
return myCurrent.toString();
}
}
@@ -1,91 +0,0 @@
/*
* 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.psi.codeStyle.arrangement;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
import com.intellij.psi.codeStyle.arrangement.model.*;
import org.junit.Test;
import java.util.Collections;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
/**
* @author Denis Zhdanov
* @since 9/12/12 8:10 PM
*/
public class ArrangementUtilTest {
@Test
public void oneLevelGroupingAndMultipleNonGroupedNodes() {
ArrangementCompositeMatchCondition condition = new ArrangementCompositeMatchCondition(ArrangementOperator.AND);
ArrangementMatchCondition abstractCondition =
new ArrangementAtomMatchCondition(ArrangementSettingType.MODIFIER, ArrangementModifier.ABSTRACT);
ArrangementMatchCondition methodCondition
= new ArrangementAtomMatchCondition(ArrangementSettingType.TYPE, ArrangementEntryType.METHOD);
ArrangementMatchCondition publicCondition =
new ArrangementAtomMatchCondition(ArrangementSettingType.MODIFIER, ArrangementModifier.PUBLIC);
condition.addOperand(abstractCondition);
condition.addOperand(methodCondition);
condition.addOperand(publicCondition);
HierarchicalArrangementConditionNode grouped =
ArrangementUtil.group(condition, Collections.singletonList(Collections.singleton(methodCondition)));
assertEquals(methodCondition, grouped.getCurrent());
HierarchicalArrangementConditionNode child = grouped.getChild();
assertNotNull(child);
ArrangementCompositeMatchCondition expectedChildCondition = new ArrangementCompositeMatchCondition(ArrangementOperator.AND);
expectedChildCondition.addOperand(abstractCondition);
expectedChildCondition.addOperand(publicCondition);
assertEquals(expectedChildCondition, child.getCurrent());
assertNull(child.getChild());
}
@Test
public void oneLevelGroupingAndSingleNonGroupedNode() {
ArrangementCompositeMatchCondition condition = new ArrangementCompositeMatchCondition(ArrangementOperator.AND);
ArrangementMatchCondition abstractCondition =
new ArrangementAtomMatchCondition(ArrangementSettingType.MODIFIER, ArrangementModifier.ABSTRACT);
ArrangementMatchCondition methodCondition
= new ArrangementAtomMatchCondition(ArrangementSettingType.TYPE, ArrangementEntryType.METHOD);
condition.addOperand(abstractCondition);
condition.addOperand(methodCondition);
HierarchicalArrangementConditionNode grouped =
ArrangementUtil.group(condition, Collections.singletonList(Collections.singleton(methodCondition)));
assertEquals(methodCondition, grouped.getCurrent());
HierarchicalArrangementConditionNode child = grouped.getChild();
assertNotNull(child);
assertEquals(abstractCondition, child.getCurrent());
assertNull(child.getChild());
}
@Test
public void groupSingleNode() {
ArrangementMatchCondition condition = new ArrangementAtomMatchCondition(ArrangementSettingType.MODIFIER, ArrangementModifier.ABSTRACT);
HierarchicalArrangementConditionNode grouped =
ArrangementUtil.group(condition, Collections.singletonList(Collections.singleton(condition)));
assertEquals(condition, grouped.getCurrent());
assertNull(grouped.getChild());
}
}
@@ -27,7 +27,6 @@ import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import static com.intellij.psi.codeStyle.arrangement.ArrangementOperator.AND;
import static com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType.CLASS;
import static com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType.FIELD;
import static com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType.METHOD;
@@ -52,12 +51,12 @@ public class DefaultArrangementEntryMatcherSerializerTest {
@Test
public void compositeMatchers() {
ArrangementCompositeMatchCondition condition = new ArrangementCompositeMatchCondition(AND);
ArrangementCompositeMatchCondition condition = new ArrangementCompositeMatchCondition();
condition.addOperand(new ArrangementAtomMatchCondition(TYPE, METHOD));
condition.addOperand(new ArrangementAtomMatchCondition(MODIFIER, SYNCHRONIZED));
doTest(condition);
condition = new ArrangementCompositeMatchCondition(AND);
condition = new ArrangementCompositeMatchCondition();
condition.addOperand(new ArrangementAtomMatchCondition(TYPE, FIELD));
condition.addOperand(new ArrangementAtomMatchCondition(MODIFIER, PUBLIC));
condition.addOperand(new ArrangementAtomMatchCondition(MODIFIER, STATIC));
@@ -67,7 +66,7 @@ public class DefaultArrangementEntryMatcherSerializerTest {
@Test
public void conditionsOrder() {
// Inspired by IDEA-91826.
ArrangementCompositeMatchCondition condition = new ArrangementCompositeMatchCondition(AND);
ArrangementCompositeMatchCondition condition = new ArrangementCompositeMatchCondition();
ArrangementEntryType typeToPreserve = FIELD;
Set<ArrangementModifier> modifiersToPreserve = EnumSet.of(PUBLIC, STATIC, FINAL);
condition.addOperand(new ArrangementAtomMatchCondition(TYPE, typeToPreserve));
@@ -18,7 +18,6 @@ package com.intellij.psi.codeStyle.arrangement.match;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import com.intellij.psi.codeStyle.arrangement.ArrangementOperator;
import com.intellij.psi.codeStyle.arrangement.ModifierAwareArrangementEntry;
import com.intellij.psi.codeStyle.arrangement.TypeAwareArrangementEntry;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition;
@@ -80,7 +79,7 @@ public class StandardArrangementEntryMatcherTest {
@Test
public void compositeAndCondition() {
ArrangementCompositeMatchCondition condition = new ArrangementCompositeMatchCondition(ArrangementOperator.AND);
ArrangementCompositeMatchCondition condition = new ArrangementCompositeMatchCondition();
condition.addOperand(new ArrangementAtomMatchCondition(ArrangementSettingType.TYPE, ArrangementEntryType.FIELD));
condition.addOperand(new ArrangementAtomMatchCondition(ArrangementSettingType.MODIFIER, ArrangementModifier.PUBLIC));
@@ -0,0 +1,31 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.application.options.codeStyle.arrangement;
import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Denis Zhdanov
* @since 10/31/12 1:06 PM
*/
public interface ArrangementChangeConditionCallback {
@Nullable
StdArrangementMatchRule onChange(@NotNull StdArrangementMatchRule rule, @Nullable ArrangementMatchCondition newCondition);
}
@@ -15,22 +15,15 @@
*/
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.ArrangementMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType;
import com.intellij.psi.codeStyle.arrangement.model.HierarchicalArrangementConditionNode;
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware;
import com.intellij.util.containers.Stack;
import gnu.trove.TIntIntHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.util.*;
import java.util.List;
@@ -119,468 +112,4 @@ public class ArrangementConfigUtil {
}
return null;
}
public static int getDepth(@NotNull HierarchicalArrangementConditionNode node) {
HierarchicalArrangementConditionNode child = node.getChild();
return child == null ? 1 : 1 + getDepth(child);
}
@NotNull
public static HierarchicalArrangementConditionNode getLast(@NotNull HierarchicalArrangementConditionNode node) {
HierarchicalArrangementConditionNode result = node;
for (HierarchicalArrangementConditionNode child = node.getChild(); child != null; child = child.getChild()) {
result = child;
}
return result;
}
@SuppressWarnings("ConstantConditions")
@NotNull
public static ArrangementTreeNode getLastBefore(@NotNull ArrangementTreeNode start, @NotNull ArrangementTreeNode stop)
throws IllegalArgumentException
{
ArrangementTreeNode result = start;
for (ArrangementTreeNode 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;
}
@NotNull
public static ArrangementTreeNode getLast(@NotNull final ArrangementTreeNode node) {
ArrangementTreeNode result = node;
int childCount = result.getChildCount();
while (childCount > 0) {
result = result.getChildAt(childCount - 1);
}
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;
* <code>null</code> as an indication that we want to create a standalone nodes hierarchy
* @param conditionNode settings node which should be represented at the UI tree denoted by the given UI tree node
* @param model tree model to use for the tree modification
* @return pair {@code (bottom-most leaf node created; number of rows created)}
*/
@NotNull
public static Pair<ArrangementTreeNode, Integer> map(@Nullable ArrangementTreeNode uiParentNode,
@NotNull HierarchicalArrangementConditionNode conditionNode,
@Nullable DefaultTreeModel model)
{
ArrangementTreeNode uiNode = null;
int rowsCreated = 0;
if (uiParentNode != null && uiParentNode.getChildCount() > 0) {
ArrangementTreeNode child = uiParentNode.getChildAt(uiParentNode.getChildCount() - 1);
if (conditionNode.getCurrent().equals(child.getBackingCondition())) {
uiNode = child;
}
}
if (uiNode == null) {
uiNode = new ArrangementTreeNode(conditionNode.getCurrent());
if (uiParentNode != null) {
if (model == null) {
uiParentNode.add(uiNode);
}
else {
model.insertNodeInto(uiNode, uiParentNode, uiParentNode.getChildCount());
}
}
rowsCreated++;
}
ArrangementTreeNode leaf = uiNode;
HierarchicalArrangementConditionNode childConditionNode = conditionNode.getChild();
if (childConditionNode != null) {
Pair<ArrangementTreeNode, Integer> pair = map(uiNode, childConditionNode, model);
leaf = pair.first;
rowsCreated += pair.second;
}
return Pair.create(leaf, rowsCreated);
}
/**
* Utility method which helps to replace node sub-hierarchy identified by the given start and end nodes (inclusive) by
* a sub-hierarchy which is denoted by the given root.
*
* @param from indicates start of the node sub-hierarchy (top-most node) to be replaced (inclusive)
* @param to indicates end of the node sub-hierarchy (bottom-most node) to be replaced (inclusive)
* @param replacement root of the node sub-hierarchy which should replace the one identified by the given 'start' and 'end' nodes
* @param treeModel model which should hold ui nodes
* @param rootVisible determines if the root should be count during rows calculations
* @return collection of row changes at the form {@code 'old row -> new row'} (all rows are zero-based)
*/
public static TIntIntHashMap replace(@NotNull ArrangementTreeNode from,
@NotNull ArrangementTreeNode to,
@NotNull ArrangementTreeNode replacement,
@NotNull DefaultTreeModel treeModel,
boolean rootVisible)
{
return doReplace(from, to, replacement, treeModel, rootVisible);
}
@SuppressWarnings("AssignmentToForLoopParameter")
@NotNull
private static TIntIntHashMap doReplace(@NotNull ArrangementTreeNode from,
@NotNull ArrangementTreeNode to,
@Nullable ArrangementTreeNode replacement,
@NotNull DefaultTreeModel treeModel,
boolean rootVisible)
{
// The algorithm looks as follows:
// 1. Cut sub-hierarchy which belongs to the given 'from' root and is located below the 'to -> from' path;
// 2. Remove 'to -> from' sub-hierarchy' by going bottom-up and stopping as soon as a current node has a child over than one
// from the sub-hierarchy to remove;
// 3. Add 'replacement' sub-hierarchy starting after the 'from' index at its parent;
// 4. Add sub-hierarchy cut at the 1) starting after the 'replacement' sub-hierarchy index;
// Example:
// Initial:
// 0
// |_1
// |_2
// | |_3
// | |_4
// | |_5
// |
// |_6
// Let's say we want to replace the sub-hierarchy '1 -> 2 -> 4' by the sub-hierarchy '1 -> 4'. The algorithm in action:
// 1. Cut bottom sub-hierarchy:
// Current: Cut:
// 0 1
// |_1 |_2
// |_2 | |_5
// |_3 |
// |_4 |_6
//
// 2. Remove target sub-hierarchy:
// Current:
// 0
// |_1
// |_2 <-- stop at this node because it has a child node '3' which doesn't belong to the '1 -> 2 -> 4'
// |_3
// 3. Add 'replacement' sub-hierarchy:
// Current:
// 0
// |_1 <-- re-use this node for '1 -> 4' addition
// |_2
// | |_3
// |
// |_4
// 4. Add 'bottom' sub-hierarchy:
// Current:
// 0
// |_1 <-- re-use this node either for '1 -> 2 -> 5' or '1 -> 6' addition
// |_2
// | |_3
// |
// |_4
// |
// |_2
// | |_5
// |
// |_6
//
// Note: we need to have a notion of 'equal nodes' for node re-usage. It's provided by comparing node user objects.
markRows(from, rootVisible);
final ArrangementTreeNode root = from.getParent();
assert root != null;
@Nullable ArrangementTreeNode cutHierarchy = cutSubHierarchy(root, treeModel, to);
int childCountBefore = root.getChildCount();
for (ArrangementTreeNode current = to; current != root;) {
ArrangementTreeNode parent = current.getParent();
treeModel.removeNodeFromParent(current);
current = parent;
if (parent == null || parent.getChildCount() > 0) {
break;
}
}
int insertionIndex = root.getChildCount() < childCountBefore ? childCountBefore - 1 : childCountBefore;
if (replacement != null) {
insert(root, insertionIndex, replacement, treeModel);
}
if (cutHierarchy != null) {
insert(root, root.getChildCount(), cutHierarchy, treeModel);
}
return collectRowChangesAndUnmark(root, rootVisible);
}
/**
* Removes all nodes which lay below the path identified by the given 'top' and 'bottom' nodes from the tree structure and returns them.
*
* @param topMost top most node marker, i.e. all cut nodes descend from this node
* @param treeModel tree model which manages the nodes
* @param bottomMost bottom most node after which all other nodes should be cut
* @return removed nodes below the target path if any; <code>null</code> otherwise
*/
@Nullable
public static ArrangementTreeNode cutSubHierarchy(@NotNull ArrangementTreeNode topMost,
@NotNull DefaultTreeModel treeModel,
@NotNull ArrangementTreeNode bottomMost)
{
ArrangementTreeNode cutHierarchy = null;
for (ArrangementTreeNode current = bottomMost; current != null && current != topMost; current = current.getParent()) {
ArrangementTreeNode parent = current.getParent();
assert parent != null;
int i = parent.getIndex(current);
int childCount = parent.getChildCount();
if (i >= childCount - 1) {
continue;
}
ArrangementTreeNode parentCopy = parent.copy();
if (parent.getChildCount() > 0) {
parentCopy.resetRow();
}
if (cutHierarchy != null) {
parentCopy.add(cutHierarchy);
}
for (int j = i + 1; j < childCount; j++) {
ArrangementTreeNode child = parent.getChildAt(i + 1);
treeModel.removeNodeFromParent(child);
parentCopy.add(child);
}
cutHierarchy = parentCopy;
}
return cutHierarchy;
}
/**
* Enriches every node at the hierarchy denoted by the given node by information about it's row.
*
* @param node reference to the target hierarchy
* @param rootVisible determines if the root should be count during rows calculations
*/
public static void markRows(@NotNull ArrangementTreeNode node, boolean rootVisible) {
ArrangementTreeNode root = getRoot(node);
int row = rootVisible ? 0 : -1;
Stack<ArrangementTreeNode> nodes = new Stack<ArrangementTreeNode>();
nodes.push(root);
while (!nodes.isEmpty()) {
ArrangementTreeNode n = nodes.pop();
n.markRow(row++);
for (int i = n.getChildCount() - 1; i >= 0; i--) {
nodes.push(n.getChildAt(i));
}
}
}
@NotNull
public static ArrangementTreeNode getRoot(@NotNull ArrangementTreeNode node) {
ArrangementTreeNode root = node;
for (ArrangementTreeNode n = root; n != null; n = n.getParent()) {
root = n;
}
return root;
}
/**
* Processes hierarchy denoted by the given node assuming that every node there contains information about its initial row
* (see {@link #markRows(ArrangementTreeNode, boolean)}).
* <p/>
* Collects all row changes and returns them. All row information is dropped from the nodes during the current method processing.
*
* @param node reference to the target nodes hierarchy
* @return collection of row changes at the form {@code 'old row -> new row'} (all rows are zero-based)
*/
@NotNull
public static TIntIntHashMap collectRowChangesAndUnmark(@NotNull ArrangementTreeNode node, boolean rootVisible) {
@NotNull TIntIntHashMap changes = new TIntIntHashMap();
ArrangementTreeNode root = getRoot(node);
int row = rootVisible ? 0 : -1;
Stack<ArrangementTreeNode> nodes = new Stack<ArrangementTreeNode>();
nodes.push(root);
while (!nodes.isEmpty()) {
ArrangementTreeNode n = nodes.pop();
if (n.isRowSet() && n.getRow() != row) {
changes.put(n.getRow(), row);
}
n.resetRow();
row++;
for (int i = n.getChildCount() - 1; i >= 0; i--) {
nodes.push(n.getChildAt(i));
}
}
return changes;
}
/**
* Allows to map given node to its row at the hierarchy.
*
* @param node target node
* @param rootVisible determines if the root should be count on rows calculation
* @return given node's row at the nodes hierarchy (0-based)
*/
public static int getRow(@NotNull ArrangementTreeNode node, boolean rootVisible) {
ArrangementTreeNode root = getRoot(node);
int row = rootVisible ? 0 : -1;
Stack<ArrangementTreeNode> nodes = new Stack<ArrangementTreeNode>();
nodes.push(root);
while (!nodes.isEmpty()) {
ArrangementTreeNode n = nodes.pop();
if (n == node) {
return row;
}
row++;
for (int i = n.getChildCount() - 1; i >= 0; i--) {
nodes.push(n.getChildAt(i));
}
}
StringBuilder buffer = new StringBuilder();
String separator = "->";
for (TreeNode n = node; n != null; n = n.getParent()) {
buffer.append(n).append(separator);
}
buffer.setLength(buffer.length() - separator.length());
throw new RuntimeException("Invalid ArrangementTreeNode detected: " + buffer.toString());
}
/**
* Inserts given child to the given parent re-using existing nodes under the parent sub-hierarchy if possible.
* <p/>
* Example:
* <pre>
* parent: 0 to-insert: 2
* |_1 |_3
* |_2 |_6
* | |_3
* | |_4
* |_5
* -------------------------------------------------------------------------------------------------
* | index: | 0 | 1 | 2 | 3 |
* |-------------------------------------------------------------------------------------------------
* | result: | 0 | 0 | 0 | 0 |
* | | |_2 | |_1 | |_1 | |_1 |
* | | | |_3 | |_2 | |_2 | |_2 |
* | | | |_6 | | |_3 | | |_3 | | |_3 |
* | | |_1 | | |_6 | | |_4 | | |_4 |
* | | |_2 | | |_4 | | |_6 | |_5 |
* | | | |_3 | |_5 | |_5 | |_2 |
* | | | |_4 | | | |_3 |
* | | |_5 | | | |_6 |
* </pre>
* <p/>
*
* @param parent parent node to insert into
* @param index insertion index to use for the given parent node
* @param child node to insert to the given parent node at the given insertion index
* @param treeModel model which should hold UI nodes
* @return <code>true</code> if given child node has been merged to the existing node; <code>false</code> otherwise
*/
public static void insert(@NotNull final ArrangementTreeNode parent,
final int index,
@NotNull final ArrangementTreeNode child,
@NotNull DefaultTreeModel treeModel)
{
ArrangementTreeNode root = getRoot(parent);
List<ArrangementTreeNode> toInsert = new ArrayList<ArrangementTreeNode>();
if (hasEqualSetting(root, child)) {
for (int i = 0; i < child.getChildCount(); i++) {
toInsert.add(child.getChildAt(i));
}
}
else {
toInsert.add(child);
}
int i = index;
for (ArrangementTreeNode node : toInsert) {
doInsert(parent, i++, node, treeModel);
}
}
private static boolean doInsert(@NotNull final ArrangementTreeNode parent,
final int index,
@NotNull final ArrangementTreeNode child,
@NotNull DefaultTreeModel treeModel)
{
if (parent.getChildCount() < index) {
treeModel.insertNodeInto(child, parent, parent.getChildCount());
return false;
}
if (child.getChildCount() <= 0) {
// Don't merge the last child.
treeModel.insertNodeInto(child, parent, index);
return false;
}
boolean anchorAbove = false;
ArrangementTreeNode mergeCandidate = null;
if (index > 0) {
mergeCandidate = parent.getChildAt(index - 1);
if (mergeCandidate.getChildCount() <= 0 /* don't merge into leaf node*/ || !hasEqualSetting(mergeCandidate, child)) {
mergeCandidate = null;
}
}
if (index < parent.getChildCount()) {
ArrangementTreeNode n = parent.getChildAt(index);
if (hasEqualSetting(n, child)) {
mergeCandidate = n;
anchorAbove = true;
}
}
if (mergeCandidate == null) {
treeModel.insertNodeInto(child, parent, index);
return false;
}
for (int i = 0, limit = child.getChildCount(); i < limit; i++) {
insert(mergeCandidate, anchorAbove ? 0 : mergeCandidate.getChildCount(), child.getChildAt(0), treeModel);
}
return true;
}
private static boolean hasEqualSetting(@NotNull ArrangementTreeNode node1, @NotNull ArrangementTreeNode node2) {
ArrangementMatchCondition matchCondition1 = node1.getBackingCondition();
ArrangementMatchCondition matchCondition2 = node2.getBackingCondition();
if (matchCondition1 == null) {
return matchCondition2 == null;
}
else {
return matchCondition1.equals(matchCondition2);
}
}
/**
* Removes target sub-hierarchy from the tree.
*
* @param from indicates start of the node sub-hierarchy (top-most node) to be replaced (inclusive)
* @param to indicates end of the node sub-hierarchy (bottom-most node) to be replaced (inclusive)
* @param treeModel model which should hold ui nodes
* @param rootVisible determines if the root should be count during rows calculations
* @return collection of row changes at the form {@code 'old row -> new row'} (all rows are zero-based)
*/
@NotNull
public static TIntIntHashMap remove(@NotNull final ArrangementTreeNode from,
@NotNull final ArrangementTreeNode to,
@NotNull DefaultTreeModel treeModel,
boolean rootVisible)
{
return doReplace(from, to, null, treeModel, rootVisible);
}
}
@@ -17,7 +17,6 @@ package com.intellij.application.options.codeStyle.arrangement;
import com.intellij.openapi.actionSystem.DataKey;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.NotNullFunction;
import org.jetbrains.annotations.NonNls;
@@ -33,8 +32,9 @@ public class ArrangementConstants {
@NonNls public static final String RULE_EDITOR_PLACE = "Arrangement.RuleEditor.Place";
@NonNls public static final String RULE_EDITOR_TOOL_WINDOW_PLACE = "Arrangement.RuleEditor.ToolWindow.Place";
@NonNls public static final String RULE_TREE_PLACE = "Arrangement.RuleTree.Place";
public static final int HORIZONTAL_PADDING = 8;
public static final int HORIZONTAL_GAP = 8;
public static final boolean LOG_RULE_MODIFICATION = Boolean.parseBoolean(System.getProperty("log.arrangement.rule.modification"));
@@ -28,7 +28,7 @@ import java.awt.*;
* @author Denis Zhdanov
* @since 10/30/12 12:10 PM
*/
public class ArrangementEditorTreeNode extends ArrangementTreeNode implements ArrangementRepresentationAwareNode {
public class ArrangementEditorTreeNode /*extends ArrangementTreeNode implements ArrangementRepresentationAwareNode*/ {
private static final int STEPS_NUMBER = 10;
@@ -41,7 +41,7 @@ public class ArrangementEditorTreeNode extends ArrangementTreeNode implements Ar
private int myStep = 1;
public ArrangementEditorTreeNode(@NotNull ArrangementRuleEditor editor, @NotNull DefaultTreeModel treeModel, int availableWidth) {
super(null);
//super(null);
myTreeModel = treeModel;
myAvailableWidth = availableWidth;
editor.applyAvailableWidth(availableWidth - ArrangementConstants.HORIZONTAL_PADDING);
@@ -99,11 +99,11 @@ public class ArrangementEditorTreeNode extends ArrangementTreeNode implements Ar
public void changeState() {
myStep++;
myTreeModel.nodeChanged(this);
//myTreeModel.nodeChanged(this);
}
@NotNull
@Override
//@Override
public JComponent getRenderer() {
return myRenderer;
}
@@ -1,152 +0,0 @@
/*
* 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.application.options.codeStyle.arrangement.node.ArrangementCheckBoxNode;
import com.intellij.application.options.codeStyle.arrangement.node.ArrangementComboBoxNode;
import com.intellij.application.options.codeStyle.arrangement.node.ArrangementSectionNode;
import com.intellij.openapi.application.ApplicationBundle;
import com.intellij.psi.codeStyle.arrangement.group.ArrangementGroupingRule;
import com.intellij.psi.codeStyle.arrangement.group.ArrangementGroupingType;
import com.intellij.psi.codeStyle.arrangement.order.ArrangementEntryOrderType;
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import java.util.ArrayList;
import java.util.List;
/**
* This class is responsible for representing {@link ArrangementGroupingRule grouping rules} at tree structure.
* <p/>
* Thread-safe.
*
* @author Denis Zhdanov
* @since 9/27/12 2:36 PM
*/
public class ArrangementGroupingRulesManager {
@NotNull public static final ArrangementGroupingRulesManager INSTANCE = new ArrangementGroupingRulesManager();
@SuppressWarnings("MethodMayBeStatic")
@Nullable
public MutableTreeNode buildAvailableRules(@NotNull ArrangementStandardSettingsAware settingsFilter,
@NotNull ArrangementNodeDisplayManager displayManager)
{
ArrangementSectionNode result = new ArrangementSectionNode(ApplicationBundle.message("arrangement.settings.section.groups"));
String orderLabel = ApplicationBundle.message("arrangement.settings.label.order") + ":";
for (ArrangementGroupingType groupingType : ArrangementGroupingType.values()) {
if (!settingsFilter.isEnabled(groupingType, null)) {
continue;
}
final ArrangementCheckBoxNode<ArrangementGroupingType> groupingNode
= new ArrangementCheckBoxNode<ArrangementGroupingType>(displayManager, groupingType);
result.add(groupingNode);
List<ArrangementEntryOrderType> supportedOrderTypes = new ArrayList<ArrangementEntryOrderType>();
for (ArrangementEntryOrderType orderType : ArrangementEntryOrderType.values()) {
if (settingsFilter.isEnabled(groupingType, orderType)) {
supportedOrderTypes.add(orderType);
}
}
if (supportedOrderTypes.isEmpty()) {
continue;
}
final ArrangementComboBoxNode<ArrangementEntryOrderType> orderTypeNode
= new ArrangementComboBoxNode<ArrangementEntryOrderType>(displayManager, orderLabel, supportedOrderTypes);
groupingNode.add(orderTypeNode);
groupingNode.setChangeCallback(new Consumer<ArrangementCheckBoxNode<ArrangementGroupingType>>() {
@Override
public void consume(ArrangementCheckBoxNode<ArrangementGroupingType> node) {
orderTypeNode.setEnabled(groupingNode.isSelected());
}
});
groupingNode.setSelected(true);
}
return result;
}
@SuppressWarnings({"MethodMayBeStatic", "unchecked"})
public void applyRules(@NotNull List<ArrangementGroupingRule> rules,
@NotNull MutableTreeNode groupsRoot,
@NotNull DefaultTreeModel model)
{
for (int i = 0; i < groupsRoot.getChildCount(); i++) {
ArrangementCheckBoxNode<?> child = (ArrangementCheckBoxNode<?>)groupsRoot.getChildAt(i);
child.setSelected(false);
}
for (int i = 0; i < rules.size(); i++) {
ArrangementGroupingRule rule = rules.get(i);
int index = getIndex(groupsRoot, rule.getRule());
if (index < 0) {
return;
}
ArrangementCheckBoxNode<?> child = (ArrangementCheckBoxNode<?>)groupsRoot.getChildAt(index);
child.setSelected(true);
if (i != index) {
model.removeNodeFromParent(child);
int insertionIndex = i < index ? i : i - 1;
model.insertNodeInto(child, groupsRoot, insertionIndex);
}
if (child.getChildCount() > 0) {
ArrangementComboBoxNode<ArrangementEntryOrderType> orderTypeNode =
(ArrangementComboBoxNode<ArrangementEntryOrderType>)child.getChildAt(0);
orderTypeNode.setSelectedValue(rule.getOrderType());
}
}
}
private static int getIndex(@NotNull TreeNode root, @NotNull Object data) {
for (int i = 0; i < root.getChildCount(); i++) {
TreeNode child = root.getChildAt(i);
if (child instanceof ArrangementCheckBoxNode<?> && data.equals(((ArrangementCheckBoxNode)child).getValue())) {
return i;
}
}
return -1;
}
@SuppressWarnings({"unchecked", "MethodMayBeStatic"})
@NotNull
public List<ArrangementGroupingRule> buildRules(@NotNull TreeNode groupsRoot) {
List<ArrangementGroupingRule> result = new ArrayList<ArrangementGroupingRule>();
for (int i = 0; i < groupsRoot.getChildCount(); i++) {
ArrangementCheckBoxNode<ArrangementGroupingType> groupNode =
(ArrangementCheckBoxNode<ArrangementGroupingType>)groupsRoot.getChildAt(i);
if (!groupNode.isSelected()) {
continue;
}
ArrangementGroupingType groupingType = groupNode.getValue();
if (groupNode.getChildCount() < 1) {
result.add(new ArrangementGroupingRule(groupingType));
continue;
}
ArrangementComboBoxNode<ArrangementEntryOrderType> orderNode =
(ArrangementComboBoxNode<ArrangementEntryOrderType>)groupNode.getChildAt(0);
ArrangementEntryOrderType orderType = orderNode.getSelectedValue();
result.add(new ArrangementGroupingRule(groupingType, orderType));
}
return result;
}
}
@@ -1,84 +0,0 @@
/*
* 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.ArrangementOperator;
import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
import com.intellij.psi.codeStyle.arrangement.match.StdArrangementEntryMatcher;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementCompositeMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
import org.jetbrains.annotations.NotNull;
/**
* Combines and encapsulates information about arrangement matcher rules representation (tree nodes) and
* underlying {@link ArrangementMatchCondition data model}.
* <p/>
* Implementations of this interface are not obliged to be thread-safe.
*
* @author Denis Zhdanov
* @since 8/15/12 1:04 PM
*/
public interface ArrangementRuleEditingModel {
@NotNull
StdArrangementMatchRule EMPTY_RULE = new StdArrangementMatchRule(new StdArrangementEntryMatcher(
new ArrangementCompositeMatchCondition(ArrangementOperator.AND)
));
@NotNull
ArrangementMatchCondition getCondition();
@NotNull
StdArrangementMatchRule getRule();
/**
* Asks current model to destroy itself.
* <p/>
* The key concern here is to perform necessary tree modification.
*/
void destroy();
/**
* Allows to answer if current model has a registered condition for the given key. A key is expected to be one of the standard
* keys, e.g. {@link ArrangementEntryType type}, {@link ArrangementModifier modifier} etc.
*
* @param key target key
* @return <code>true</code> if current model has a registered mapping for the given key;
* <code>false</code> otherwise
*/
boolean hasCondition(@NotNull Object key);
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 #getRule() 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 #getRule() underlying match condition}
*/
void replaceCondition(@NotNull ArrangementAtomMatchCondition from, @NotNull ArrangementAtomMatchCondition to)
throws IllegalArgumentException;
}
@@ -1,123 +0,0 @@
/*
* 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.openapi.util.Pair;
import com.intellij.psi.codeStyle.arrangement.ArrangementUtil;
import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.HierarchicalArrangementConditionNode;
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsRepresentationAware;
import gnu.trove.TIntIntHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import java.util.List;
import java.util.Set;
/**
* Holds glue logic between arrangement settings and their representation -
* '{@link ArrangementMatchCondition} -&gt; {@link ArrangementRuleEditingModel}'
* <p/>
* Thread-safe.
*
* @author Denis Zhdanov
* @since 8/15/12 2:50 PM
*/
public class ArrangementRuleEditingModelBuilder {
/**
* Does the following:
* <pre>
* <ol>
* <li>
* {@link HierarchicalArrangementConditionNode Groups} given {@link ArrangementMatchCondition settings} using
* the given grouping rules;
* </li>
* <li>
* Build {@link DefaultMutableTreeNode tree nodes} for the {@link HierarchicalArrangementConditionNode groiping-aware nodes}
* and register them within the target tree structure (denoted by the given settings root element);
* </li>
* <li>
* Build necessary {@link ArrangementRuleEditingModel editing models} and store them at the given container (a key is a node row);
* </li>
* </ol>
* </pre>
*
* @param rule target rule to process
* @param tree UI tree which shows arrangement matcher rules
* @param root UI tree settings root to use (may be not the same as the tree root)
* @param anchor node after which should be previous sibling for the root node of the inserted condition;
* <code>null</code> as an indication that new condition nodes should be inserted as the last 'root' child
* @param groupingRules conditions grouping rules
* {@link ArrangementStandardSettingsRepresentationAware#getDisplayValue(ArrangementModifier) group} setting
* nodes for UI representation
* @return created model and collection of row changes at the form {@code 'old row -> new row'} (all rows are zero-based);
* <code>null</code> if no model has been created
*/
@SuppressWarnings("MethodMayBeStatic")
@Nullable
public Pair<ArrangementRuleEditingModelImpl, TIntIntHashMap> build(
@NotNull StdArrangementMatchRule rule,
@NotNull JTree tree,
@NotNull ArrangementTreeNode root,
@Nullable ArrangementTreeNode anchor,
@NotNull List<Set<ArrangementMatchCondition>> groupingRules)
{
HierarchicalArrangementConditionNode grouped = ArrangementUtil.group(rule.getMatcher().getCondition(), groupingRules);
DefaultTreeModel treeModel = (DefaultTreeModel)tree.getModel();
if (grouped.getChild() == null) {
ArrangementTreeNode lastChild = null;
if (root.getChildCount() > 0) {
lastChild = root.getLastChild();
}
if (lastChild != null && grouped.getCurrent().equals(lastChild.getBackingCondition())) {
return null;
}
}
Pair<ArrangementTreeNode, Integer> pair = ArrangementConfigUtil.map(null, grouped, null);
ArrangementTreeNode topMostNode = ArrangementConfigUtil.getRoot(pair.first);
ArrangementConfigUtil.markRows(root, true);
ArrangementTreeNode bottomHierarchy = null;
if (anchor != null) {
bottomHierarchy = ArrangementConfigUtil.cutSubHierarchy(root, treeModel, anchor);
}
ArrangementConfigUtil.insert(root, root.getChildCount(), topMostNode, treeModel);
if (bottomHierarchy != null) {
ArrangementConfigUtil.insert(root, root.getChildCount(), bottomHierarchy, treeModel);
}
TIntIntHashMap rowChanges = ArrangementConfigUtil.collectRowChangesAndUnmark(root, true);
topMostNode = ArrangementConfigUtil.getLastBefore(pair.first, root);
int row = ArrangementConfigUtil.getRow(pair.first, tree.isRootVisible());
ArrangementRuleEditingModelImpl model = new ArrangementRuleEditingModelImpl(
treeModel,
rule,
topMostNode,
pair.first,
groupingRules,
row,
tree.isRootVisible()
);
return Pair.create(model, rowChanges);
}
}
@@ -1,284 +0,0 @@
/*
* 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.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.codeStyle.arrangement.ArrangementUtil;
import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule;
import com.intellij.psi.codeStyle.arrangement.match.StdArrangementEntryMatcher;
import com.intellij.psi.codeStyle.arrangement.model.*;
import com.intellij.util.containers.hash.HashSet;
import gnu.trove.TIntIntHashMap;
import org.jetbrains.annotations.NotNull;
import javax.swing.tree.DefaultTreeModel;
import java.util.List;
import java.util.Set;
/**
* Not thread-safe.
*
* @author Denis Zhdanov
* @since 8/15/12 2:40 PM
*/
public class ArrangementRuleEditingModelImpl implements ArrangementRuleEditingModel {
@NotNull private static final MyConditionsBuilder CONDITIONS_BUILDER = new MyConditionsBuilder();
@NotNull private static final Logger LOG = Logger.getInstance("#" + ArrangementRuleEditingModelImpl.class.getName());
@NotNull private final Set<Listener> myListeners = new HashSet<Listener>();
@NotNull private final Set<Object> myConditions = new HashSet<Object>();
@NotNull private final DefaultTreeModel myTreeModel;
@NotNull private final List<Set<ArrangementMatchCondition>> myGroupingRules;
private final boolean myRootVisible;
@NotNull private ArrangementTreeNode myTopMost;
@NotNull private ArrangementTreeNode myBottomMost;
@NotNull private StdArrangementMatchRule myRule;
private int myRow;
/**
* Creates new <code>ArrangementRuleEditingModelImpl</code> object.
*
* @param model tree model which holds target ui nodes. Basically, we need to perform ui nodes modification via it in order
* to generate corresponding events automatically
* @param rule backing arrangement rule
* @param topMost there is a possible case that a single settings node is shown in more than one visual line
* ({@link HierarchicalArrangementConditionNode}). 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 groupingRules grouping rules
* @param row row number for which current model is registered at the given model mappings
* @param rootVisible determines if the root should be count during rows calculations
*/
public ArrangementRuleEditingModelImpl(@NotNull DefaultTreeModel model,
@NotNull StdArrangementMatchRule rule,
@NotNull ArrangementTreeNode topMost,
@NotNull ArrangementTreeNode bottomMost,
@NotNull List<Set<ArrangementMatchCondition>> groupingRules,
int row,
boolean rootVisible)
{
myTreeModel = model;
myRule = rule;
myTopMost = topMost;
myBottomMost = bottomMost;
myGroupingRules = groupingRules;
myRow = row;
myRootVisible = rootVisible;
refreshConditions();
}
private void refreshConditions() {
myConditions.clear();
CONDITIONS_BUILDER.conditions = myConditions;
try {
getCondition().invite(CONDITIONS_BUILDER);
}
finally {
CONDITIONS_BUILDER.conditions = null;
}
}
@NotNull
@Override
public ArrangementMatchCondition getCondition() {
return myRule.getMatcher().getCondition();
}
@NotNull
@Override
public StdArrangementMatchRule getRule() {
return myRule;
}
public int getRow() {
return myRow;
}
public void setRow(int row) {
myRow = row;
}
@NotNull
public ArrangementTreeNode getTopMost() {
return myTopMost;
}
@NotNull
public ArrangementTreeNode 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.
* <p/>
* This method asks the model to refresh its tree nodes if necessary.
*/
public void refreshTreeNodes() {
for (ArrangementTreeNode node = myBottomMost; node != null; node = node.getParent()) {
if (node == myTopMost) {
// No refresh is necessary.
return;
}
ArrangementMatchCondition matchCondition = myTopMost.getBackingCondition();
if (matchCondition != null && matchCondition.equals(node.getBackingCondition())) {
myTopMost = node;
return;
}
}
assert false;
}
@Override
public void addAndCondition(@NotNull ArrangementAtomMatchCondition condition) {
if (ArrangementConstants.LOG_RULE_MODIFICATION) {
LOG.info(String.format(
"Arrangement rule modification - adding a condition '%s'. Model: '%s', row: %d", condition, myRule, myRow
));
}
ArrangementMatchCondition newCondition = ArrangementUtil.and(getCondition().clone(), condition);
applyNewCondition(newCondition);
}
@Override
public void removeAndCondition(@NotNull ArrangementMatchCondition condition) {
if (ArrangementConstants.LOG_RULE_MODIFICATION) {
LOG.info(String.format(
"Arrangement rule modification - removing a condition '%s'. Model: '%s', row: %d", condition, myRule, myRow
));
}
if (condition.equals(myBottomMost.getBackingCondition())) {
if (myTopMost != myBottomMost && myTopMost.getChildCount() > 1) {
destroy();
return;
}
}
ArrangementMatchCondition matchCondition = getCondition();
assert matchCondition instanceof ArrangementCompositeMatchCondition;
ArrangementMatchCondition newCondition = matchCondition.clone();
ArrangementCompositeMatchCondition composite = (ArrangementCompositeMatchCondition)newCondition;
composite.getOperands().remove(condition);
if (composite.getOperands().size() == 1) {
newCondition = composite.getOperands().iterator().next();
}
applyNewCondition(newCondition);
}
private void applyNewCondition(@NotNull ArrangementMatchCondition newCondition) {
myRule = new StdArrangementMatchRule(new StdArrangementEntryMatcher(newCondition));
HierarchicalArrangementConditionNode grouped = ArrangementUtil.group(newCondition, myGroupingRules);
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);
myBottomMost = newBottom;
myTopMost = newTop;
refreshTreeNodes();
int newRow = ArrangementConfigUtil.getRow(myBottomMost, myRootVisible);
if (myRow != newRow) {
rowChanges.put(myRow, newRow);
myRow = newRow;
}
refreshConditions();
for (Listener listener : myListeners) {
listener.onChanged(this, rowChanges);
}
if (ArrangementConstants.LOG_RULE_MODIFICATION) {
LOG.info(String.format("Arrangement rule is modified: '%s', row: %d", myRule, myRow));
}
}
@Override
public void replaceCondition(@NotNull ArrangementAtomMatchCondition from, @NotNull ArrangementAtomMatchCondition to) {
if (ArrangementConstants.LOG_RULE_MODIFICATION) {
LOG.info(String.format(
"Arrangement rule modification - replacing condition '%s' by '%s'. Model: '%s', row: %d", from, to, myRule, myRow
));
}
ArrangementMatchCondition newCondition;
ArrangementMatchCondition matchCondition = getCondition();
if (matchCondition.equals(from)) {
newCondition = to;
}
else {
assert matchCondition instanceof ArrangementCompositeMatchCondition;
ArrangementCompositeMatchCondition composite = (ArrangementCompositeMatchCondition)matchCondition;
ArrangementCompositeMatchCondition newComposite = composite.clone();
newComposite.getOperands().remove(from);
newComposite.getOperands().add(to);
newCondition = newComposite;
}
applyNewCondition(newCondition);
}
@Override
public void destroy() {
if (ArrangementConstants.LOG_RULE_MODIFICATION) {
LOG.info(String.format("Arrangement rule modification - destroy. Model: '%s', row: %d", myRule, myRow));
}
for (Listener listener : myListeners) {
listener.beforeModelDestroy(this);
}
TIntIntHashMap rowChanges = ArrangementConfigUtil.remove(myTopMost, myBottomMost, myTreeModel, myRootVisible);
for (Listener listener : myListeners) {
listener.afterModelDestroy(rowChanges);
}
}
public void addListener(@NotNull Listener listener) {
myListeners.add(listener);
}
@Override
public String toString() {
return "model for " + myRule;
}
private static class MyConditionsBuilder implements ArrangementMatchConditionVisitor {
@NotNull Set<Object> conditions;
@Override
public void visit(@NotNull ArrangementAtomMatchCondition setting) {
conditions.add(setting.getValue());
}
@Override
public void visit(@NotNull ArrangementCompositeMatchCondition setting) {
for (ArrangementMatchCondition operand : setting.getOperands()) {
operand.invite(this);
}
}
}
public interface Listener {
void onChanged(@NotNull ArrangementRuleEditingModelImpl model, @NotNull TIntIntHashMap rowChanges);
void beforeModelDestroy(@NotNull ArrangementRuleEditingModelImpl model);
void afterModelDestroy(@NotNull TIntIntHashMap rowChanges);
}
}
@@ -15,11 +15,15 @@
*/
package com.intellij.application.options.codeStyle.arrangement;
import com.intellij.application.options.codeStyle.arrangement.node.match.ArrangementAtomMatchNodeComponent;
import com.intellij.application.options.codeStyle.arrangement.node.match.ArrangementMatchNodeComponent;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementMatchRule;
import com.intellij.application.options.codeStyle.arrangement.node.match.ArrangementAtomMatchConditionComponent;
import com.intellij.application.options.codeStyle.arrangement.node.match.ArrangementMatchConditionComponent;
import com.intellij.psi.codeStyle.arrangement.ArrangementConditionInfo;
import com.intellij.psi.codeStyle.arrangement.ArrangementUtil;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryMatcher;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementMatchRule;
import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType;
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware;
import com.intellij.ui.IdeBorderFactory;
@@ -47,19 +51,24 @@ public class ArrangementRuleEditor extends JPanel {
@NotNull private final List<MultiRowFlowPanel> myRows = new ArrayList<MultiRowFlowPanel>();
@NotNull private final Map<Object, ArrangementAtomMatchNodeComponent> myComponents =
new HashMap<Object, ArrangementAtomMatchNodeComponent>();
@NotNull private final Map<Object, ArrangementAtomMatchConditionComponent> myComponents =
new HashMap<Object, ArrangementAtomMatchConditionComponent>();
@NotNull private final ArrangementStandardSettingsAware myFilter;
@NotNull private final ArrangementColorsProvider myColorsProvider;
@Nullable private ArrangementRuleEditingModel myModel;
@NotNull private final ArrangementStandardSettingsAware myFilter;
@NotNull private final ArrangementColorsProvider myColorsProvider;
@NotNull private final ArrangementChangeConditionCallback myChangeCallback;
@Nullable private StdArrangementMatchRule myRule;
@Nullable private ArrangementConditionInfo myConditionInfo;
public ArrangementRuleEditor(@NotNull ArrangementStandardSettingsAware filter,
@NotNull ArrangementColorsProvider provider,
@NotNull ArrangementNodeDisplayManager displayManager)
@NotNull ArrangementNodeDisplayManager displayManager,
@NotNull ArrangementChangeConditionCallback callback)
{
myFilter = filter;
myColorsProvider = provider;
myChangeCallback = callback;
init(displayManager);
addMouseListener(new MouseAdapter() {
@Override
@@ -89,13 +98,13 @@ public class ArrangementRuleEditor extends JPanel {
MultiRowFlowPanel valuesPanel = new MultiRowFlowPanel(FlowLayout.LEFT, 8, 5);
for (Object value : manager.sort(values)) {
ArrangementAtomMatchNodeComponent component =
new ArrangementAtomMatchNodeComponent(manager, myColorsProvider, new ArrangementAtomMatchCondition(key, value), null);
ArrangementAtomMatchConditionComponent component =
new ArrangementAtomMatchConditionComponent(manager, myColorsProvider, new ArrangementAtomMatchCondition(key, value), null);
myComponents.put(value, component);
valuesPanel.add(component.getUiComponent());
}
int top = ArrangementAtomMatchNodeComponent.VERTICAL_PADDING;
int top = ArrangementAtomMatchConditionComponent.VERTICAL_PADDING;
add(new JLabel(manager.getDisplayLabel(key) + ":"), new GridBag().anchor(GridBagConstraints.NORTHWEST).insets(top, 0, 0, 0));
add(valuesPanel, new GridBag().anchor(GridBagConstraints.WEST).weightx(1).fillCellHorizontally().coverLine());
myRows.add(valuesPanel);
@@ -105,36 +114,46 @@ public class ArrangementRuleEditor extends JPanel {
* Asks current editor to refresh its state in accordance with the given arguments (e.g. when new rule is selected and
* we want to show only available conditions).
*
* @param model current rule settings model if defined; null as an indication that no settings should be active
* @param rule rule which match condition should be edited (if defined);
* <code>null</code> as an indication that no settings should be active
*/
public void updateState(@Nullable ArrangementRuleEditingModel model) {
myModel = model;
public void updateState(@Nullable StdArrangementMatchRule rule) {
myRule = rule;
myConditionInfo = null;
// Reset state.
for (ArrangementAtomMatchNodeComponent component : myComponents.values()) {
for (ArrangementAtomMatchConditionComponent component : myComponents.values()) {
component.setEnabled(false);
component.setSelected(false);
}
if (model == null) {
if (rule == null) {
return;
}
Map<ArrangementSettingType, Collection<?>> available = ArrangementConfigUtil.buildAvailableConditions(
myFilter, model.getRule().getMatcher().getCondition()
);
ArrangementMatchCondition condition = rule.getMatcher().getCondition();
myConditionInfo = ArrangementUtil.extractConditions(condition);
Map<ArrangementSettingType, Collection<?>> available = ArrangementConfigUtil.buildAvailableConditions(myFilter, condition);
for (Collection<?> ids : available.values()) {
for (Object id : ids) {
ArrangementAtomMatchNodeComponent component = myComponents.get(id);
ArrangementAtomMatchConditionComponent component = myComponents.get(id);
if (component != null) {
component.setEnabled(true);
component.setSelected(model.hasCondition(id));
component.setSelected(myConditionInfo.hasCondition(id));
}
}
}
repaint();
}
private void updateState() {
assert myConditionInfo != null;
ArrangementMatchCondition newCondition = myConditionInfo.buildCondition();
myChangeCallback.onChange(myRule, newCondition);
updateState(myRule);
}
public void applyAvailableWidth(int width) {
for (MultiRowFlowPanel row : myRows) {
row.setForcedWidth(width);
@@ -150,19 +169,20 @@ public class ArrangementRuleEditor extends JPanel {
}
private void onMouseClicked(@NotNull MouseEvent e) {
if (myModel == null) {
if (myRule == null || myConditionInfo == null) {
return;
}
ArrangementAtomMatchNodeComponent clickedComponent = getNodeComponentAt(e.getLocationOnScreen());
ArrangementAtomMatchConditionComponent clickedComponent = getNodeComponentAt(e.getLocationOnScreen());
if (clickedComponent == null || !clickedComponent.isEnabled()) {
return;
}
ArrangementAtomMatchCondition chosenCondition = clickedComponent.getMatchCondition();
boolean remove = myModel.hasCondition(chosenCondition.getValue());
boolean remove = myConditionInfo.hasCondition(chosenCondition.getValue());
clickedComponent.setSelected(!remove);
repaintComponent(clickedComponent);
if (remove) {
myModel.removeAndCondition(chosenCondition);
myConditionInfo.removeCondition(chosenCondition);
updateState();
return;
}
@@ -172,30 +192,33 @@ public class ArrangementRuleEditor extends JPanel {
continue;
}
for (Object key : mutex) {
if (myModel.hasCondition(key)) {
ArrangementAtomMatchNodeComponent componentToDeselect = myComponents.get(key);
myModel.replaceCondition(componentToDeselect.getMatchCondition(), chosenCondition);
for (ArrangementAtomMatchNodeComponent componentToCheck : myComponents.values()) {
if (myConditionInfo.hasCondition(key)) {
ArrangementAtomMatchConditionComponent componentToDeselect = myComponents.get(key);
myConditionInfo.removeCondition(componentToDeselect.getMatchCondition().getValue());
myConditionInfo.addAtomCondition(chosenCondition);
ArrangementMatchCondition newCondition = myConditionInfo.buildCondition();
for (ArrangementAtomMatchConditionComponent componentToCheck : myComponents.values()) {
Object value = componentToCheck.getMatchCondition().getValue();
if (myModel.hasCondition(value) && !ArrangementConfigUtil.isEnabled(value, myFilter, myModel.getCondition())) {
myModel.removeAndCondition(componentToCheck.getMatchCondition());
if (myConditionInfo.hasCondition(value) && !ArrangementConfigUtil.isEnabled(value, myFilter, newCondition)) {
myConditionInfo.removeCondition(componentToCheck.getMatchCondition().getValue());
newCondition = myConditionInfo.buildCondition();
}
}
// There is a possible case that some conditions become unavailable, e.g. changing type from 'field' to 'method'
// makes 'volatile' condition inappropriate.
updateState(myModel);
updateState();
return;
}
}
}
myModel.addAndCondition(chosenCondition);
updateState(myModel);
myConditionInfo.addAtomCondition(chosenCondition);
updateState();
}
@Nullable
private ArrangementAtomMatchNodeComponent getNodeComponentAt(@NotNull Point screenPoint) {
for (ArrangementAtomMatchNodeComponent component : myComponents.values()) {
private ArrangementAtomMatchConditionComponent getNodeComponentAt(@NotNull Point screenPoint) {
for (ArrangementAtomMatchConditionComponent component : myComponents.values()) {
Rectangle screenBounds = component.getScreenBounds();
if (screenBounds != null && screenBounds.contains(screenPoint)) {
return component;
@@ -204,7 +227,7 @@ public class ArrangementRuleEditor extends JPanel {
return null;
}
private void repaintComponent(@NotNull ArrangementMatchNodeComponent component) {
private void repaintComponent(@NotNull ArrangementMatchConditionComponent component) {
Rectangle bounds = component.getScreenBounds();
if (bounds != null) {
Point location = bounds.getLocation();
@@ -26,7 +26,6 @@ import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationBundle;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.arrangement.Rearranger;
@@ -68,7 +67,7 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel {
@NotNull private final Language myLanguage;
@NotNull private final ArrangementStandardSettingsAware mySettingsAware;
@NotNull private final ArrangementRuleTree myRuleTree;
@NotNull private final ArrangementRuleTree myRuleTree = null;
public ArrangementSettingsPanel(@NotNull CodeStyleSettings settings, @NotNull Language language) {
super(settings);
@@ -106,8 +105,9 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel {
toolbarControl.setBorder(IdeBorderFactory.createBorder(SideBorder.LEFT | SideBorder.TOP | SideBorder.RIGHT));
myContent.add(toolbarControl, new GridBag().weightx(1).fillCellHorizontally().coverLine());
myRuleTree = new ArrangementRuleTree(getSettings(settings), groupingRules, displayManager, colorsProvider, mySettingsAware);
final Tree treeComponent = myRuleTree.getTreeComponent();
//myRuleTree = new ArrangementRuleTree(getSettings(settings), groupingRules, displayManager, colorsProvider, mySettingsAware);
//final Tree treeComponent = myRuleTree.getTreeComponent();
final Tree treeComponent = null;
actionToolbar.setTargetComponent(treeComponent);
JBScrollPane scrollPane = new JBScrollPane(treeComponent);
myContent.add(scrollPane, new GridBag().weightx(1).weighty(1).fillCell().coverLine());
@@ -138,7 +138,7 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel {
}
if (myWidth != visibleRect.width) {
myWidth = visibleRect.width;
myRuleTree.updateCanvasWidth(myWidth);
//myRuleTree.updateCanvasWidth(myWidth);
}
}
});
@@ -178,20 +178,20 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel {
}
else {
myYToRestore = -1;
List<ArrangementRuleEditingModelImpl> models = myRuleTree.getActiveModels();
if (models.size() == 1) {
Rectangle bounds = treeComponent.getPathBounds(new TreePath(models.get(0).getBottomMost().getPath()));
if (bounds != null) {
myYToRestore = bounds.y;
Rectangle viewRect = viewport.getViewRect();
if (bounds.y < viewRect.y) {
scroll(bounds.y);
}
else if (bounds.y + bounds.height >= viewRect.y + viewRect.height) {
scroll(bounds.y + bounds.height - viewRect.height);
}
}
}
//List<ArrangementRuleEditingModelImpl> models = myRuleTree.getActiveModels();
//if (models.size() == 1) {
// Rectangle bounds = treeComponent.getPathBounds(new TreePath(models.get(0).getBottomMost().getPath()));
// if (bounds != null) {
// myYToRestore = bounds.y;
// Rectangle viewRect = viewport.getViewRect();
// if (bounds.y < viewRect.y) {
// scroll(bounds.y);
// }
// else if (bounds.y + bounds.height >= viewRect.y + viewRect.height) {
// scroll(bounds.y + bounds.height - viewRect.height);
// }
// }
//}
}
}
@@ -217,7 +217,7 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel {
@Override
public void run() {
treeComponent.requestFocus();
ArrangementRuleEditingModel model = myRuleTree.newModel();
//ArrangementRuleEditingModel model = myRuleTree.newModel();
//ruleEditor.updateState(model, scrollPane.getVisibleRect().width);
resetEditor.set(Boolean.FALSE);
try {
@@ -232,10 +232,10 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel {
@Override
public void run() {
treeComponent.requestFocus();
List<ArrangementRuleEditingModelImpl> models = myRuleTree.getActiveModels();
for (ArrangementRuleEditingModelImpl model : models) {
model.destroy();
}
//List<ArrangementRuleEditingModelImpl> models = myRuleTree.getActiveModels();
//for (ArrangementRuleEditingModelImpl model : models) {
// model.destroy();
//}
}
};
final NotNullFunction<Boolean, Boolean> updateMoveFunction = new NotNullFunction<Boolean, Boolean>() {
@@ -320,17 +320,18 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel {
@Override
public boolean isModified(@NotNull CodeStyleSettings settings) {
return !Comparing.equal(getSettings(settings), myRuleTree.getSettings());
//return !Comparing.equal(getSettings(settings), myRuleTree.getSettings());
return false;
}
@Override
public void apply(@NotNull CodeStyleSettings settings) {
settings.getCommonSettings(myLanguage).setArrangementSettings(myRuleTree.getSettings());
//settings.getCommonSettings(myLanguage).setArrangementSettings(myRuleTree.getSettings());
}
@Override
protected void resetImpl(@NotNull CodeStyleSettings settings) {
myRuleTree.setSettings(getSettings(settings));
//myRuleTree.setSettings(getSettings(settings));
}
@Override
@@ -346,6 +347,6 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel {
@Override
public void dispose() {
super.dispose();
myRuleTree.disposeUI();
//myRuleTree.disposeUI();
}
}
@@ -1,121 +0,0 @@
/*
* 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.ArrangementMatchCondition;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.tree.DefaultMutableTreeNode;
/**
* 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
*/
public class ArrangementTreeNode extends DefaultMutableTreeNode {
private static final int NO_ROW = -1;
@Nullable private final ArrangementMatchCondition myCondition;
private int myRow;
public ArrangementTreeNode(@Nullable ArrangementMatchCondition condition) {
this(condition, NO_ROW);
}
public ArrangementTreeNode(@Nullable ArrangementMatchCondition condition, int row) {
myCondition = condition;
myRow = row;
}
@Nullable
public ArrangementMatchCondition getBackingCondition() {
return myCondition;
}
public boolean isRowSet() {
return myRow >= 0;
}
public int getRow() {
return myRow;
}
public void markRow(int row) {
myRow = row;
}
public void resetRow() {
myRow = NO_ROW;
}
@NotNull
public ArrangementTreeNode copy() {
// Settings are copied by-ref intentionally here.
return new ArrangementTreeNode(myCondition, myRow);
}
@Nullable
@Override
public ArrangementTreeNode getParent() {
return (ArrangementTreeNode)super.getParent();
}
@Override
public ArrangementTreeNode getChildAt(int index) {
return (ArrangementTreeNode)super.getChildAt(index);
}
@Override
public ArrangementTreeNode getFirstChild() {
return (ArrangementTreeNode)super.getFirstChild();
}
@Override
public ArrangementTreeNode getLastChild() {
return (ArrangementTreeNode)super.getLastChild();
}
@Override
public ArrangementTreeNode getNextSibling() {
return (ArrangementTreeNode)super.getNextSibling();
}
@Override
public ArrangementTreeNode getNextNode() {
return (ArrangementTreeNode)super.getNextNode();
}
@Override
public ArrangementTreeNode getPreviousNode() {
return (ArrangementTreeNode)super.getPreviousNode();
}
@Override
public ArrangementTreeNode getLastLeaf() {
return (ArrangementTreeNode)super.getLastLeaf();
}
@Override
public String toString() {
return myCondition == null ? "" : myCondition.toString() + (myRow >= 0 ? ": row=" + myRow : "");
}
}
@@ -0,0 +1,103 @@
/*
* 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.newui;
import com.intellij.application.options.codeStyle.arrangement.ArrangementColorsProvider;
import com.intellij.application.options.codeStyle.arrangement.ArrangementConstants;
import com.intellij.application.options.codeStyle.arrangement.ArrangementNodeDisplayManager;
import com.intellij.application.options.codeStyle.arrangement.node.match.ArrangementMatchConditionComponent;
import com.intellij.application.options.codeStyle.arrangement.node.match.ArrangementMatchNodeComponentFactory;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule;
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware;
import com.intellij.ui.components.JBList;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.Collections;
import java.util.List;
/**
* @author Denis Zhdanov
* @since 10/31/12 1:23 PM
*/
public class ArrangementMatchingRulesList extends JBList {
private static final Logger LOG = Logger.getInstance("#" + ArrangementMatchingRulesList.class.getName());
@NotNull private final TIntObjectHashMap<ArrangementMatchConditionComponent> myComponents
= new TIntObjectHashMap<ArrangementMatchConditionComponent>();
@NotNull private final DefaultListModel myModel = new DefaultListModel();
@NotNull private final ArrangementMatchNodeComponentFactory myFactory;
public ArrangementMatchingRulesList(@NotNull ArrangementNodeDisplayManager displayManager,
@NotNull ArrangementColorsProvider colorsProvider,
@NotNull ArrangementStandardSettingsAware settingsFilter)
{
myFactory = new ArrangementMatchNodeComponentFactory(displayManager, colorsProvider, myModel);
setModel(myModel);
setCellRenderer(new MyListCellRenderer());
}
public void setRules(@Nullable List<StdArrangementMatchRule> rules) {
myComponents.clear();
myModel.clear();
if (rules == null) {
return;
}
for (StdArrangementMatchRule rule : rules) {
myModel.addElement(rule);
}
if (ArrangementConstants.LOG_RULE_MODIFICATION) {
LOG.info("Arrangement matching rules list is refreshed. Given rules:");
for (StdArrangementMatchRule rule : rules) {
LOG.info(" " + rule.toString());
}
}
}
@NotNull
public List<StdArrangementMatchRule> getRules() {
// TODO den implement
return Collections.emptyList();
}
// TODO den remove
@Override
public void paint(Graphics g) {
super.paint(g);
}
private class MyListCellRenderer implements ListCellRenderer {
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
ArrangementMatchConditionComponent component = myComponents.get(index);
if (component == null) {
StdArrangementMatchRule rule = (StdArrangementMatchRule)value;
myComponents.put(index, component = myFactory.getComponent(rule.getMatcher().getCondition(), rule, true));
}
return component.getUiComponent();
}
}
}
@@ -0,0 +1,64 @@
/*
* 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.newui;
import com.intellij.application.options.codeStyle.arrangement.ArrangementColorsProvider;
import com.intellij.application.options.codeStyle.arrangement.ArrangementNodeDisplayManager;
import com.intellij.openapi.application.ApplicationBundle;
import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule;
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.util.ui.GridBag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.List;
/**
* @author Denis Zhdanov
* @since 10/30/12 5:28 PM
*/
public class ArrangementMatchingRulesPanel extends JPanel {
@NotNull private final ArrangementMatchingRulesList myList;
public ArrangementMatchingRulesPanel(@NotNull ArrangementNodeDisplayManager displayManager,
@NotNull ArrangementColorsProvider colorsProvider,
@NotNull ArrangementStandardSettingsAware settingsFilter)
{
myList = new ArrangementMatchingRulesList(displayManager, colorsProvider, settingsFilter);
init();
}
private void init() {
setBorder(IdeBorderFactory.createTitledBorder(ApplicationBundle.message("arrangement.settings.section.match")));
setLayout(new GridBagLayout());
JBScrollPane scrollPane = new JBScrollPane(myList);
add(scrollPane, new GridBag().fillCell().weightx(1).weighty(1));
}
@NotNull
public List<StdArrangementMatchRule> getRules() {
return myList.getRules();
}
public void setRules(@Nullable List<StdArrangementMatchRule> rules) {
myList.setRules(rules);
}
}
@@ -13,12 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.codeStyle.arrangement;
package com.intellij.application.options.codeStyle.arrangement.newui;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
/**
* @author Denis Zhdanov
* @since 8/27/12 10:30 AM
* @since 10/30/12 6:57 PM
*/
public enum ArrangementOperator {
AND, OR
public interface ArrangementRepresentationAware {
@NotNull
JComponent getRenderer();
}
@@ -0,0 +1,132 @@
/*
* 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.newui;
import com.intellij.application.options.CodeStyleAbstractPanel;
import com.intellij.application.options.codeStyle.arrangement.ArrangementColorsProvider;
import com.intellij.application.options.codeStyle.arrangement.ArrangementColorsProviderImpl;
import com.intellij.application.options.codeStyle.arrangement.ArrangementNodeDisplayManager;
import com.intellij.lang.Language;
import com.intellij.openapi.application.ApplicationBundle;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
import com.intellij.openapi.util.Comparing;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.arrangement.Rearranger;
import com.intellij.psi.codeStyle.arrangement.StdArrangementSettings;
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementColorsAware;
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware;
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsRepresentationAware;
import com.intellij.psi.codeStyle.arrangement.settings.DefaultArrangementSettingsRepresentationManager;
import com.intellij.util.ui.GridBag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
/**
* @author Denis Zhdanov
* @since 10/30/12 5:17 PM
*/
public abstract class ArrangementSettingsPanel2 extends CodeStyleAbstractPanel {
@NotNull private final JPanel myContent = new JPanel(new GridBagLayout());
@NotNull private final Language myLanguage;
@NotNull private final ArrangementStandardSettingsAware mySettingsAware;
@NotNull private final ArrangementMatchingRulesPanel myMatchingRulesPanel;
public ArrangementSettingsPanel2(@NotNull CodeStyleSettings settings, @NotNull Language language) {
super(settings);
myLanguage = language;
Rearranger<?> rearranger = Rearranger.EXTENSION.forLanguage(language);
assert rearranger instanceof ArrangementStandardSettingsAware;
mySettingsAware = (ArrangementStandardSettingsAware)rearranger;
final ArrangementColorsProvider colorsProvider;
if (rearranger instanceof ArrangementColorsAware) {
colorsProvider = new ArrangementColorsProviderImpl((ArrangementColorsAware)rearranger);
}
else {
colorsProvider = new ArrangementColorsProviderImpl(null);
}
ArrangementStandardSettingsRepresentationAware representationManager = DefaultArrangementSettingsRepresentationManager.INSTANCE;
if (mySettingsAware instanceof ArrangementStandardSettingsRepresentationAware) {
representationManager = (ArrangementStandardSettingsRepresentationAware)mySettingsAware;
}
final ArrangementNodeDisplayManager displayManager = new ArrangementNodeDisplayManager(
mySettingsAware, colorsProvider, representationManager
);
myMatchingRulesPanel = new ArrangementMatchingRulesPanel(displayManager, colorsProvider, mySettingsAware);
myContent.add(myMatchingRulesPanel, new GridBag().fillCell().weightx(1).weighty(1));
}
@Nullable
@Override
public JComponent getPanel() {
return myContent;
}
@Nullable
@Override
protected EditorHighlighter createHighlighter(EditorColorsScheme scheme) {
// TODO den implement
return null;
}
@SuppressWarnings("unchecked")
@Nullable
private StdArrangementSettings getSettings(@NotNull CodeStyleSettings settings) {
StdArrangementSettings result = (StdArrangementSettings)settings.getCommonSettings(myLanguage).getArrangementSettings();
if (result == null) {
result = mySettingsAware.getDefaultSettings();
}
return result;
}
@Override
public void apply(CodeStyleSettings settings) {
// TODO den implement
}
@Override
public boolean isModified(CodeStyleSettings settings) {
StdArrangementSettings s = new StdArrangementSettings(myMatchingRulesPanel.getRules());
// TODO den add grouping rules support
return !Comparing.equal(getSettings(settings), s);
}
@Override
protected void resetImpl(CodeStyleSettings settings) {
StdArrangementSettings s = getSettings(settings);
if (s == null) {
myMatchingRulesPanel.setRules(null);
}
else {
myMatchingRulesPanel.setRules(s.getRules());
}
}
@Override
protected String getTabTitle() {
return ApplicationBundle.message("arrangement.title.settings.tab");
}
}
@@ -16,7 +16,6 @@
package com.intellij.application.options.codeStyle.arrangement.node;
import com.intellij.application.options.codeStyle.arrangement.ArrangementNodeDisplayManager;
import com.intellij.application.options.codeStyle.arrangement.ArrangementTreeNode;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.util.Consumer;
import com.intellij.util.ui.GridBag;
@@ -33,8 +32,8 @@ import java.awt.event.ItemListener;
* @author Denis Zhdanov
* @since 9/27/12 3:10 PM
*/
public class ArrangementCheckBoxNode<T> extends ArrangementTreeNode
implements ArrangementRepresentationAwareNode, ArrangementEditableNode
public class ArrangementCheckBoxNode<T>/* extends ArrangementTreeNode
implements ArrangementRepresentationAwareNode, ArrangementEditableNode*/
{
@NotNull private final JPanel myRenderer = new JPanel(new GridBagLayout());
@NotNull private final JBCheckBox myCheckBox = new JBCheckBox();
@@ -44,7 +43,7 @@ public class ArrangementCheckBoxNode<T> extends ArrangementTreeNode
@Nullable private Consumer<ArrangementCheckBoxNode<T>> myListener;
public ArrangementCheckBoxNode(@NotNull ArrangementNodeDisplayManager displayManager, @NotNull T data) {
super(null);
//super(null);
myCheckBox.setText(displayManager.getDisplayValue(data));
myData = data;
myCheckBox.setBackground(UIUtil.getTreeBackground());
@@ -60,13 +59,13 @@ public class ArrangementCheckBoxNode<T> extends ArrangementTreeNode
}
@NotNull
@Override
//@Override
public JComponent getRenderer() {
return myRenderer;
}
@NotNull
@Override
//@Override
public JComponent getEditor() {
return myRenderer;
}
@@ -16,7 +16,6 @@
package com.intellij.application.options.codeStyle.arrangement.node;
import com.intellij.application.options.codeStyle.arrangement.ArrangementNodeDisplayManager;
import com.intellij.application.options.codeStyle.arrangement.ArrangementTreeNode;
import com.intellij.util.ui.GridBag;
import org.jetbrains.annotations.NotNull;
@@ -29,7 +28,7 @@ import java.util.List;
* @author Denis Zhdanov
* @since 9/27/12 5:01 PM
*/
public class ArrangementComboBoxNode<T> extends ArrangementTreeNode implements ArrangementRepresentationAwareNode, ArrangementEditableNode {
public class ArrangementComboBoxNode<T> /*extends ArrangementTreeNode implements ArrangementRepresentationAwareNode, ArrangementEditableNode*/ {
@NotNull private final JPanel myControl = new JPanel(new GridBagLayout());
@NotNull private final JLabel myLabel = new JLabel();
@@ -37,7 +36,7 @@ public class ArrangementComboBoxNode<T> extends ArrangementTreeNode implements A
@NotNull private final List<T> myData = new ArrayList<T>();
public ArrangementComboBoxNode(@NotNull ArrangementNodeDisplayManager displayManager, @NotNull String text, @NotNull Iterable<T> data) {
super(null);
//super(null);
for (T t : data) {
myData.add(t);
myComboBox.addItem(displayManager.getDisplayValue(t));
@@ -48,13 +47,13 @@ public class ArrangementComboBoxNode<T> extends ArrangementTreeNode implements A
}
@NotNull
@Override
//@Override
public JComponent getEditor() {
return myControl;
}
@NotNull
@Override
//@Override
public JComponent getRenderer() {
return myControl;
}
@@ -15,7 +15,6 @@
*/
package com.intellij.application.options.codeStyle.arrangement.node;
import com.intellij.application.options.codeStyle.arrangement.ArrangementTreeNode;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
@@ -24,13 +23,13 @@ import javax.swing.*;
* @author Denis Zhdanov
* @since 9/27/12 2:24 PM
*/
public class ArrangementSectionNode extends ArrangementTreeNode implements ArrangementRepresentationAwareNode {
public class ArrangementSectionNode /*extends ArrangementTreeNode implements ArrangementRepresentationAwareNode*/ {
@NotNull private final JPanel myRenderer = new JPanel();
@NotNull private final JLabel myTextLabel = new JLabel();
public ArrangementSectionNode(@NotNull String text) {
super(null);
//super(null);
myTextLabel.setText(text);
myRenderer.add(myTextLabel);
}
@@ -16,14 +16,14 @@
package com.intellij.application.options.codeStyle.arrangement.node.match;
import com.intellij.application.options.codeStyle.arrangement.ArrangementConfigUtil;
import com.intellij.application.options.codeStyle.arrangement.ArrangementConstants;
import com.intellij.application.options.codeStyle.arrangement.ArrangementNodeDisplayManager;
import com.intellij.application.options.codeStyle.arrangement.ArrangementRuleEditingModel;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementCompositeMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchConditionVisitor;
import com.intellij.ui.awt.RelativePoint;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -36,26 +36,24 @@ import java.util.List;
import java.util.Map;
/**
* {@link ArrangementMatchNodeComponent Component} for showing {@link ArrangementCompositeMatchCondition composite nodes}.
* {@link ArrangementMatchConditionComponent Component} for showing {@link ArrangementCompositeMatchCondition composite nodes}.
* <p/>
* Not thread-safe.
*
* @author Denis Zhdanov
* @since 8/8/12 10:51 AM
*/
public class ArrangementAndMatchNodeComponent extends JPanel implements ArrangementMatchNodeComponent {
public class ArrangementAndMatchConditionComponent extends JPanel implements ArrangementMatchConditionComponent {
private static final int BUBBLE_CONNECTOR_LENGTH = 5;
@NotNull private final List<ArrangementMatchNodeComponent> myComponents = new ArrayList<ArrangementMatchNodeComponent>();
@NotNull private final List<ArrangementMatchConditionComponent> myComponents = new ArrayList<ArrangementMatchConditionComponent>();
@NotNull private final ArrangementCompositeMatchCondition mySetting;
@Nullable private Rectangle myScreenBounds;
public ArrangementAndMatchNodeComponent(@NotNull ArrangementCompositeMatchCondition setting,
@NotNull ArrangementMatchNodeComponentFactory factory,
@NotNull ArrangementNodeDisplayManager manager,
@Nullable ArrangementRuleEditingModel model)
public ArrangementAndMatchConditionComponent(@NotNull StdArrangementMatchRule rule,
@NotNull ArrangementCompositeMatchCondition setting,
@NotNull ArrangementMatchNodeComponentFactory factory,
@NotNull ArrangementNodeDisplayManager manager)
{
mySetting = setting;
setOpaque(false);
@@ -81,13 +79,13 @@ public class ArrangementAndMatchNodeComponent extends JPanel implements Arrangem
for (Object key : ordered) {
ArrangementMatchCondition operand = operands.get(key);
assert operand != null;
ArrangementMatchNodeComponent component = factory.getComponent(operand, model, false);
ArrangementMatchConditionComponent component = factory.getComponent(operand, rule, true);
myComponents.add(component);
JComponent uiComponent = component.getUiComponent();
Dimension size = uiComponent.getPreferredSize();
add(uiComponent);
uiComponent.setBounds(x, 0, size.width, size.height);
x += size.width + BUBBLE_CONNECTOR_LENGTH;
x += size.width + ArrangementConstants.HORIZONTAL_GAP;
}
}
@@ -121,30 +119,11 @@ public class ArrangementAndMatchNodeComponent extends JPanel implements Arrangem
@Override
public void setSelected(boolean selected) {
for (ArrangementMatchNodeComponent component : myComponents) {
for (ArrangementMatchConditionComponent component : myComponents) {
component.setSelected(selected);
}
}
@Override
public ArrangementMatchNodeComponent getNodeComponentAt(@NotNull RelativePoint point) {
if (myScreenBounds == null) {
return null;
}
Point screenPoint = point.getScreenPoint();
if (!myScreenBounds.contains(screenPoint)) {
return null;
}
for (ArrangementMatchNodeComponent component : myComponents) {
Rectangle screenBounds = component.getScreenBounds();
if (screenBounds != null && screenBounds.contains(screenPoint)) {
return component;
}
}
return null;
}
@Override
public Dimension getMinimumSize() {
return getPreferredSize();
@@ -166,7 +145,7 @@ public class ArrangementAndMatchNodeComponent extends JPanel implements Arrangem
myHeight = Math.max(size.height, myHeight);
}
if (components.length > 1) {
myWidth += (components.length - 1) * BUBBLE_CONNECTOR_LENGTH;
myWidth += (components.length - 1) * ArrangementConstants.HORIZONTAL_GAP;
}
return new Dimension(myWidth, myHeight);
}
@@ -179,37 +158,12 @@ public class ArrangementAndMatchNodeComponent extends JPanel implements Arrangem
myScreenBounds = new Rectangle(point.x, point.y, bounds.width, bounds.height);
}
super.paint(g);
Component[] components = getComponents();
if (components.length < 2) {
return;
}
// Draw node connectors.
int x = 0;
g.setColor(UIManager.getColor("Tree.hash"));
for (int i = 0; i < components.length; i++) {
Component component = components[i];
Rectangle bounds = component.getBounds();
if (myScreenBounds != null && i < myComponents.size()) {
myComponents.get(i).setScreenBounds(new Rectangle(
myScreenBounds.x + bounds.x, myScreenBounds.y + bounds.y, bounds.width, bounds.height
));
}
int y = bounds.y + bounds.height / 2;
x += bounds.width;
if (i < components.length - 1) {
// TODO den check
//g.drawLine(x, y, x + BUBBLE_CONNECTOR_LENGTH, y);
}
x += BUBBLE_CONNECTOR_LENGTH;
}
}
@Override
public Rectangle handleMouseMove(@NotNull MouseEvent event) {
Point location = event.getLocationOnScreen();
for (ArrangementMatchNodeComponent component : myComponents) {
for (ArrangementMatchConditionComponent component : myComponents) {
Rectangle bounds = component.getScreenBounds();
if (bounds != null && bounds.contains(location)) {
return component.handleMouseMove(event);
@@ -221,7 +175,7 @@ public class ArrangementAndMatchNodeComponent extends JPanel implements Arrangem
@Override
public void handleMouseClick(@NotNull MouseEvent event) {
Point location = event.getLocationOnScreen();
for (ArrangementMatchNodeComponent component : myComponents) {
for (ArrangementMatchConditionComponent component : myComponents) {
Rectangle bounds = component.getScreenBounds();
if (bounds != null && bounds.contains(location)) {
component.handleMouseClick(event);
@@ -23,7 +23,7 @@ import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.RoundedLineBorder;
import com.intellij.ui.SimpleColoredComponent;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.Consumer;
import com.intellij.util.ui.GridBag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -33,16 +33,16 @@ import java.awt.*;
import java.awt.event.MouseEvent;
/**
* {@link ArrangementMatchNodeComponent} for {@link ArrangementAtomMatchCondition} representation.
* {@link ArrangementMatchConditionComponent} for {@link ArrangementAtomMatchCondition} representation.
* <p/>
* Not thread-safe.
*
* @author Denis Zhdanov
* @since 8/8/12 10:06 AM
*/
public class ArrangementAtomMatchNodeComponent implements ArrangementMatchNodeComponent {
public class ArrangementAtomMatchConditionComponent implements ArrangementMatchConditionComponent {
public static final int VERTICAL_PADDING = 4;
public static final int VERTICAL_PADDING = 4;
@NotNull private final JPanel myRenderer = new JPanel(new GridBagLayout()) {
@Override
@@ -76,14 +76,14 @@ public class ArrangementAtomMatchNodeComponent implements ArrangementMatchNodeCo
return myTextControlSize == null ? super.getPreferredSize() : myTextControlSize;
}
};
@NotNull private final String myText;
@NotNull private final String myText;
@NotNull private final ArrangementColorsProvider myColorsProvider;
@NotNull private final RoundedLineBorder myBorder;
@NotNull private final ArrangementAtomMatchCondition myCondition;
@Nullable private final ActionButton myCloseButton;
@Nullable private final Runnable myCloseCallback;
@Nullable private final ActionButton myCloseButton;
@Nullable private final Consumer<ArrangementAtomMatchCondition> myCloseCallback;
@NotNull private Color myBackgroundColor;
@@ -91,13 +91,12 @@ public class ArrangementAtomMatchNodeComponent implements ArrangementMatchNodeCo
@Nullable private Rectangle myScreenBounds;
private boolean myEnabled = true;
private boolean myInverted;
private boolean myCloseButtonHovered;
public ArrangementAtomMatchNodeComponent(@NotNull ArrangementNodeDisplayManager manager,
@NotNull ArrangementColorsProvider colorsProvider,
@NotNull ArrangementAtomMatchCondition condition,
@Nullable Runnable closeCallback)
public ArrangementAtomMatchConditionComponent(@NotNull ArrangementNodeDisplayManager manager,
@NotNull ArrangementColorsProvider colorsProvider,
@NotNull ArrangementAtomMatchCondition condition,
@Nullable Consumer<ArrangementAtomMatchCondition> closeCallback)
{
myColorsProvider = colorsProvider;
myCondition = condition;
@@ -117,7 +116,7 @@ public class ArrangementAtomMatchNodeComponent implements ArrangementMatchNodeCo
}
};
GridBagConstraints constraints = new GridBag().anchor(GridBagConstraints.CENTER).insets(0, 0, 0, 0);
GridBagConstraints constraints = new GridBag().anchor(GridBagConstraints.WEST).weightx(1).insets(0, 0, 0, 0);
JPanel insetsPanel = new JPanel(new GridBagLayout());
insetsPanel.add(myTextControl, constraints);
@@ -140,8 +139,8 @@ public class ArrangementAtomMatchNodeComponent implements ArrangementMatchNodeCo
super.paint(g);
}
};
roundBorderPanel.add(insetsPanel, new GridBag().fillCellHorizontally());
roundBorderPanel.add(myCloseButton, new GridBag().anchor(GridBagConstraints.CENTER).insets(VERTICAL_PADDING, 0, 0, 0));
roundBorderPanel.add(insetsPanel, new GridBag().fillCellHorizontally().anchor(GridBagConstraints.WEST));
roundBorderPanel.add(myCloseButton, new GridBag().anchor(GridBagConstraints.CENTER).weightx(1).insets(VERTICAL_PADDING, 0, 0, 0));
myBorder = IdeBorderFactory.createRoundedBorder(arcSize);
roundBorderPanel.setBorder(myBorder);
roundBorderPanel.setOpaque(false);
@@ -180,11 +179,6 @@ public class ArrangementAtomMatchNodeComponent implements ArrangementMatchNodeCo
return false;
}
@Override
public ArrangementMatchNodeComponent getNodeComponentAt(@NotNull RelativePoint point) {
return (myScreenBounds != null && myScreenBounds.contains(point.getScreenPoint())) ? this : null;
}
/**
* Instructs current component that it should {@link #getUiComponent() draw} itself according to the given 'selected' state.
*
@@ -212,17 +206,6 @@ public class ArrangementAtomMatchNodeComponent implements ArrangementMatchNodeCo
myEnabled = enabled;
}
/**
* Instructs current component that it should {@link #getUiComponent() draw} itself according to the given 'inverted' state.
* <p/>
* For example, target rule might look like 'public' and inverting it produces 'not public'.
*
* @param inverted flag that indicates if current component should be drawn as 'inverted'
*/
public void setInverted(boolean inverted) {
myInverted = inverted;
}
@Nullable
@Override
public Rectangle handleMouseMove(@NotNull MouseEvent event) {
@@ -237,8 +220,8 @@ public class ArrangementAtomMatchNodeComponent implements ArrangementMatchNodeCo
@Override
public void handleMouseClick(@NotNull MouseEvent event) {
Rectangle buttonBounds = getCloseButtonScreenLocation();
if (buttonBounds != null && buttonBounds.contains(event.getLocationOnScreen()) && myCloseCallback != null) {
myCloseCallback.run();
if (buttonBounds != null && myCloseCallback != null && buttonBounds.contains(event.getLocationOnScreen())) {
myCloseCallback.consume(getMatchCondition());
}
}
@@ -22,7 +22,6 @@ import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.actionSystem.impl.ActionButton;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
import com.intellij.ui.awt.RelativePoint;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -36,14 +35,14 @@ import java.awt.event.MouseEvent;
* @author Denis Zhdanov
* @since 10/29/12 11:53 AM
*/
public class ArrangementEditIconMatchNodeComponent extends JPanel implements ArrangementMatchNodeComponent {
public class ArrangementEditIconMatchConditionComponent extends JPanel implements ArrangementMatchConditionComponent {
@NotNull private final ArrangementMatchNodeComponent myDelegate;
@NotNull private final Icon myActiveEditIcon;
@NotNull private final Icon myInactiveEditIcon;
@NotNull private final ActionButton myEditButton;
@NotNull private final ArrangementMatchConditionComponent myDelegate;
@NotNull private final Icon myActiveEditIcon;
@NotNull private final Icon myInactiveEditIcon;
@NotNull private final ActionButton myEditButton;
public ArrangementEditIconMatchNodeComponent(@NotNull ArrangementMatchNodeComponent delegate) {
public ArrangementEditIconMatchConditionComponent(@NotNull ArrangementMatchConditionComponent delegate) {
myDelegate = delegate;
setLayout(null);
@@ -93,12 +92,6 @@ public class ArrangementEditIconMatchNodeComponent extends JPanel implements Arr
return this;
}
@Override
@Nullable
public ArrangementMatchNodeComponent getNodeComponentAt(@NotNull RelativePoint point) {
return myDelegate.getNodeComponentAt(point);
}
@Override
@Nullable
public Rectangle getScreenBounds() {
@@ -20,7 +20,6 @@ import com.intellij.application.options.codeStyle.arrangement.ArrangementNodeDis
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.ui.GridBag;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
@@ -34,7 +33,7 @@ import java.awt.event.MouseEvent;
* @author Denis Zhdanov
* @since 9/12/12 5:39 PM
*/
public class ArrangementGroupingMatchNodeComponent extends JPanel implements ArrangementMatchNodeComponent {
public class ArrangementGroupingMatchConditionComponent extends JPanel implements ArrangementMatchConditionComponent {
private static final int TOP_INSET = 3;
@@ -43,8 +42,8 @@ public class ArrangementGroupingMatchNodeComponent extends JPanel implements Arr
@Nullable private Rectangle myScreenBounds;
@NotNull private Dimension myPreferredSize;
public ArrangementGroupingMatchNodeComponent(@NotNull ArrangementNodeDisplayManager manager,
@NotNull ArrangementAtomMatchCondition condition)
public ArrangementGroupingMatchConditionComponent(@NotNull ArrangementNodeDisplayManager manager,
@NotNull ArrangementAtomMatchCondition condition)
{
myCondition = condition;
String text = StringUtil.capitalize(StringUtil.pluralize(manager.getDisplayValue(myCondition.getValue())));
@@ -66,12 +65,6 @@ public class ArrangementGroupingMatchNodeComponent extends JPanel implements Arr
return this;
}
@Nullable
@Override
public ArrangementMatchNodeComponent getNodeComponentAt(@NotNull RelativePoint point) {
return (myScreenBounds != null && myScreenBounds.contains(point.getScreenPoint())) ? this : null;
}
@Nullable
@Override
public Rectangle getScreenBounds() {
@@ -16,7 +16,6 @@
package com.intellij.application.options.codeStyle.arrangement.node.match;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
import com.intellij.ui.awt.RelativePoint;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -25,25 +24,25 @@ import java.awt.*;
import java.awt.event.MouseEvent;
/**
* // TODO den add doc
* Component which manages {@link ArrangementMatchCondition match condition} UI representation.
*
* @author Denis Zhdanov
* @since 8/10/12 11:22 AM
*/
public interface ArrangementMatchNodeComponent {
public interface ArrangementMatchConditionComponent {
@NotNull
ArrangementMatchCondition getMatchCondition();
// TODO den add doc
/**
* @return UI component for the {@link #getMatchCondition() target match condition}
*/
@NotNull
JComponent getUiComponent();
// TODO den add doc
@Nullable
ArrangementMatchNodeComponent getNodeComponentAt(@NotNull RelativePoint point);
// TODO den add doc
/**
* @return screen bounds for the {@link #getUiComponent() target UI component} (if known)
*/
@Nullable
Rectangle getScreenBounds();
@@ -52,7 +51,7 @@ public interface ArrangementMatchNodeComponent {
/**
* Notifies current component that canvas (container where current component is painted) width has been changed.
* <p/>
* The intended usage is to allow component to draw something up to/at the right screen size.
* The intended usage is to allow component to draw something up to/at the right edge.
*
* @param width new canvas width
* @return <code>true</code> if current component's representation has been changed;
@@ -17,17 +17,17 @@ package com.intellij.application.options.codeStyle.arrangement.node.match;
import com.intellij.application.options.codeStyle.arrangement.ArrangementColorsProvider;
import com.intellij.application.options.codeStyle.arrangement.ArrangementNodeDisplayManager;
import com.intellij.application.options.codeStyle.arrangement.ArrangementRuleEditingModel;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.codeStyle.arrangement.match.StdArrangementEntryMatcher;
import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementCompositeMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchConditionVisitor;
import com.intellij.util.containers.ContainerUtilRt;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import javax.swing.*;
import java.util.Set;
/**
@@ -36,78 +36,90 @@ import java.util.Set;
*/
public class ArrangementMatchNodeComponentFactory {
@NotNull private final Set<ArrangementMatchCondition> myGroupingConditions = ContainerUtilRt.newHashSet();
@NotNull private final ArrangementNodeDisplayManager myDisplayManager;
@NotNull private final ArrangementColorsProvider myColorsProvider;
@Nullable private final Runnable myRemoveConditionCallback;
@NotNull private final DefaultListModel myListModel;
public ArrangementMatchNodeComponentFactory(@NotNull ArrangementNodeDisplayManager manager,
@NotNull ArrangementColorsProvider provider,
@Nullable Runnable removeConditionCallback,
@NotNull List<Set<ArrangementMatchCondition>> groupingRules)
@NotNull final DefaultListModel model)
{
myDisplayManager = manager;
myColorsProvider = provider;
myRemoveConditionCallback = removeConditionCallback;
for (Set<ArrangementMatchCondition> rules : groupingRules) {
myGroupingConditions.addAll(rules);
myListModel = model;
}
private static void removeCondition(@NotNull DefaultListModel model,
@NotNull StdArrangementMatchRule rule,
@NotNull ArrangementAtomMatchCondition condition)
{
int i = model.indexOf(rule);
if (i < 0) {
return;
}
ArrangementMatchCondition existingCondition = rule.getMatcher().getCondition();
if (existingCondition.equals(condition)) {
model.remove(i);
return;
}
assert existingCondition instanceof ArrangementCompositeMatchCondition;
Set<ArrangementMatchCondition> operands = ((ArrangementCompositeMatchCondition)existingCondition).getOperands();
operands.remove(condition);
if (operands.isEmpty()) {
model.remove(i);
}
else if (operands.size() == 1) {
model.set(i, new StdArrangementMatchRule(new StdArrangementEntryMatcher(operands.iterator().next()), rule.getOrderType()));
}
}
/**
* Allows to build UI component for the given model.
*
* @param rendererTarget target model element for which UI component should be built
* @param rule rule which contains given 'renderer target' condition and serves as
* a data entry for the target list model
* @param allowModification flag which indicates whether given model can be changed at future
* @return renderer for the given model
*/
@NotNull
public ArrangementMatchNodeComponent getComponent(@NotNull final ArrangementMatchCondition node,
@Nullable final ArrangementRuleEditingModel model,
boolean showEditIcon)
public ArrangementMatchConditionComponent getComponent(@NotNull final ArrangementMatchCondition rendererTarget,
@NotNull final StdArrangementMatchRule rule,
final boolean allowModification)
{
final Ref<ArrangementMatchNodeComponent> ref = new Ref<ArrangementMatchNodeComponent>();
node.invite(new ArrangementMatchConditionVisitor() {
final Ref<ArrangementMatchConditionComponent> ref = new Ref<ArrangementMatchConditionComponent>();
rendererTarget.invite(new ArrangementMatchConditionVisitor() {
@Override
public void visit(@NotNull ArrangementAtomMatchCondition condition) {
ArrangementMatchNodeComponent component;
if (myGroupingConditions.contains(condition)) {
component = new ArrangementGroupingMatchNodeComponent(myDisplayManager, condition);
}
else {
component = new ArrangementAtomMatchNodeComponent(
myDisplayManager, myColorsProvider, condition, prepareRemoveCallback(condition, model)
);
}
RemoveAtomConditionCallback callback = allowModification ? new RemoveAtomConditionCallback(rule) : null;
ArrangementMatchConditionComponent component = new ArrangementAtomMatchConditionComponent(
myDisplayManager, myColorsProvider, condition, callback
);
ref.set(component);
}
@Override
public void visit(@NotNull ArrangementCompositeMatchCondition condition) {
switch (condition.getOperator()) {
case AND:
ref.set(new ArrangementAndMatchNodeComponent(condition, ArrangementMatchNodeComponentFactory.this, myDisplayManager, model));
break;
case OR: // TODO den implement
}
ref.set(new ArrangementAndMatchConditionComponent(rule, condition, ArrangementMatchNodeComponentFactory.this, myDisplayManager));
}
});
if (showEditIcon) {
return new ArrangementEditIconMatchNodeComponent(ref.get());
}
else {
return ref.get();
}
return ref.get();
}
@Nullable
private Runnable prepareRemoveCallback(@NotNull final ArrangementMatchCondition condition,
@Nullable final ArrangementRuleEditingModel model)
{
if (model == null) {
return null;
private class RemoveAtomConditionCallback implements Consumer<ArrangementAtomMatchCondition> {
@NotNull private final StdArrangementMatchRule myRule;
RemoveAtomConditionCallback(@NotNull StdArrangementMatchRule rule) {
myRule = rule;
}
@Override
public void consume(ArrangementAtomMatchCondition condition) {
removeCondition(myListModel, myRule, condition);
}
return new Runnable() {
@Override
public void run() {
model.removeAndCondition(condition);
if (myRemoveConditionCallback != null) {
myRemoveConditionCallback.run();
}
}
};
}
}
@@ -75,7 +75,7 @@ abstract class AbstractRearrangerTest extends LightPlatformCodeInsightFixtureTes
condition = atom(conditions[0])
}
else {
condition = ArrangementUtil.and(conditions.collect { atom(it) } as ArrangementMatchCondition[])
condition = ArrangementUtil.combine(conditions.collect { atom(it) } as ArrangementMatchCondition[])
}
new StdArrangementMatchRule(new StdArrangementEntryMatcher(condition))