diff --git a/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaRearranger.java b/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaRearranger.java index 1d5bc8561a98..21489bafd61f 100644 --- a/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaRearranger.java +++ b/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaRearranger.java @@ -16,11 +16,13 @@ package com.intellij.psi.codeStyle.arrangement; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType; import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier; -import com.intellij.psi.codeStyle.arrangement.settings.ArrangementMatcherSettings; +import com.intellij.psi.codeStyle.arrangement.model.*; +import com.intellij.psi.codeStyle.arrangement.settings.ArrangementSettingsGrouper; import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware; import com.intellij.psi.codeStyle.arrangement.sort.ArrangementEntrySortType; import org.jetbrains.annotations.NotNull; @@ -32,7 +34,9 @@ import java.util.*; * @author Denis Zhdanov * @since 7/20/12 2:31 PM */ -public class JavaRearranger implements Rearranger, ArrangementStandardSettingsAware { +public class JavaRearranger implements Rearranger, ArrangementStandardSettingsAware, + ArrangementSettingsGrouper +{ // Type @NotNull private static final Set SUPPORTED_TYPES = EnumSet.of( @@ -80,35 +84,87 @@ public class JavaRearranger implements Rearranger, } @Override - public boolean isNameFilterEnabled(@Nullable ArrangementMatcherSettings settings) { + public boolean isNameFilterEnabled(@Nullable ArrangementSettingsNode current) { // TODO den implement return true; } @Override - public boolean isEnabled(@NotNull ArrangementEntryType type, @Nullable ArrangementMatcherSettings settings) { + public boolean isEnabled(@NotNull ArrangementEntryType type, @Nullable ArrangementSettingsNode current) { return SUPPORTED_TYPES.contains(type); } @Override - public boolean isEnabled(@NotNull ArrangementModifier modifier, @Nullable ArrangementMatcherSettings settings) { - if (settings == null) { + public boolean isEnabled(@NotNull ArrangementModifier modifier, @Nullable ArrangementSettingsNode current) { + if (current == null) { return SUPPORTED_MODIFIERS.contains(modifier); } - Object key = NO_TYPE; - for (ArrangementEntryType type : ArrangementEntryType.values()) { - if (settings.hasCondition(type)) { - key = type; - break; + + final Ref typeRef = new Ref(); + current.invite(new ArrangementSettingsNodeVisitor() { + @Override + public void visit(@NotNull ArrangementSettingsAtomNode node) { + if (node.getType() == ArrangementSettingType.TYPE) { + typeRef.set(node.getValue()); + } } - } + + @Override + public void visit(@NotNull ArrangementSettingsCompositeNode node) { + for (ArrangementSettingsNode n : node.getOperands()) { + if (typeRef.get() != null) { + return; + } + n.invite(this); + } + } + }); + Object key = typeRef.get() == null ? NO_TYPE : typeRef.get(); Set modifiers = MODIFIERS_BY_TYPE.get(key); return modifiers != null && modifiers.contains(modifier); } @Override - public boolean isEnabled(@NotNull ArrangementEntrySortType type, @Nullable ArrangementMatcherSettings settings) { + public boolean isEnabled(@NotNull ArrangementEntrySortType type, @Nullable ArrangementSettingsNode current) { // TODO den implement return true; } + + @NotNull + @Override + public HierarchicalArrangementSettingsNode group(@NotNull ArrangementSettingsNode node) { + final Ref result = new Ref(); + node.invite(new ArrangementSettingsNodeVisitor() { + @Override + public void visit(@NotNull ArrangementSettingsAtomNode node) { + result.set(new HierarchicalArrangementSettingsNode(node)); + } + + @Override + public void visit(@NotNull ArrangementSettingsCompositeNode node) { + ArrangementSettingsNode typeNode = null; + for (ArrangementSettingsNode n : node.getOperands()) { + if (n instanceof ArrangementSettingsAtomNode && ((ArrangementSettingsAtomNode)n).getType() == ArrangementSettingType.TYPE) { + typeNode = n; + break; + } + } + if (typeNode == null) { + result.set(new HierarchicalArrangementSettingsNode(node)); + } + else { + HierarchicalArrangementSettingsNode parent = new HierarchicalArrangementSettingsNode(typeNode); + ArrangementSettingsCompositeNode compositeWithoutType = new ArrangementSettingsCompositeNode(node.getOperator()); + for (ArrangementSettingsNode n : node.getOperands()) { + if (n != typeNode) { + compositeWithoutType.addOperand(n); + } + } + parent.addChild(new HierarchicalArrangementSettingsNode(compositeWithoutType)); + result.set(parent); + } + } + }); + return result.get(); + } } diff --git a/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelBuilderTest.java b/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelBuilderTest.java new file mode 100644 index 000000000000..509aa3ba2aa9 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelBuilderTest.java @@ -0,0 +1,79 @@ +/* + * 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.ArrangementUtil; +import com.intellij.psi.codeStyle.arrangement.JavaRearranger; +import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType; +import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsAtomNode; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode; +import gnu.trove.TIntObjectHashMap; +import org.jetbrains.annotations.NotNull; +import org.junit.Before; +import org.junit.Test; + +import javax.swing.tree.DefaultMutableTreeNode; + +import static com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier.PUBLIC; +import static com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier.STATIC; +import static org.junit.Assert.assertEquals; + +/** + * @author Denis Zhdanov + * @since 08/15/2012 + */ +public class ArrangementRuleEditingModelBuilderTest { + + @NotNull private ArrangementRuleEditingModelBuilder myBuilder; + @NotNull private DefaultMutableTreeNode myRoot; + @NotNull private TIntObjectHashMap myRowMappings; + @NotNull private JavaRearranger myGrouper; + + @Before + public void setUp() { + myBuilder = new ArrangementRuleEditingModelBuilder(); + myRoot = new DefaultMutableTreeNode(); + myRowMappings = new TIntObjectHashMap(); + myGrouper = new JavaRearranger(); + } + + @Test + public void mapToTheSameLayer() { + build(ArrangementUtil.and(atom(PUBLIC), atom(STATIC))); + // TODO den uncomment + //assertEquals(1, myRowMappings.size()); + } + + private void build(@NotNull ArrangementSettingsNode node) { + myBuilder.build(node, myRoot, myGrouper, myRowMappings); + } + + private static ArrangementSettingsAtomNode atom(@NotNull Object condition) { + final ArrangementSettingType type; + if (condition instanceof ArrangementEntryType) { + type = ArrangementSettingType.TYPE; + } + else if (condition instanceof ArrangementModifier) { + type = ArrangementSettingType.MODIFIER; + } + else { + throw new IllegalArgumentException(String.format("Unexpected condition of class %s: %s", condition.getClass(), condition)); + } + return new ArrangementSettingsAtomNode(type, condition); + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/ArrangementUtil.java b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/ArrangementUtil.java index 520407ba89ba..f2da39c54bf4 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/ArrangementUtil.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/ArrangementUtil.java @@ -16,10 +16,15 @@ package com.intellij.psi.codeStyle.arrangement; import com.intellij.lang.Language; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryMatcher; import com.intellij.psi.codeStyle.arrangement.match.CompositeArrangementEntryMatcher; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsAtomNode; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsCompositeNode; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNodeVisitor; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -108,17 +113,48 @@ public class ArrangementUtil { } //endregion - //region Matchers composition + //region Rule composition + + @NotNull + public static ArrangementSettingsNode and(@NotNull ArrangementSettingsNode... nodes) { + final ArrangementSettingsCompositeNode result = new ArrangementSettingsCompositeNode(ArrangementSettingsCompositeNode.Operator.AND); + final ArrangementSettingsNodeVisitor visitor = new ArrangementSettingsNodeVisitor() { + @Override + public void visit(@NotNull ArrangementSettingsAtomNode node) { + result.addOperand(node); + } + + @Override + public void visit(@NotNull ArrangementSettingsCompositeNode node) { + if (node.getOperator() == ArrangementSettingsCompositeNode.Operator.AND) { + for (ArrangementSettingsNode operand : node.getOperands()) { + operand.invite(this); + } + } + else { + result.addOperand(node); + } + } + }; + for (ArrangementSettingsNode node : nodes) { + node.invite(visitor); + } + return result; + } + + // TODO den remove @NotNull public static ArrangementEntryMatcher or(@NotNull ArrangementEntryMatcher... matchers) { return combine(CompositeArrangementEntryMatcher.Operator.OR, matchers); } + // TODO den remove @NotNull public static ArrangementEntryMatcher and(@NotNull ArrangementEntryMatcher... matchers) { return combine(CompositeArrangementEntryMatcher.Operator.AND, matchers); } + // TODO den remove @NotNull private static ArrangementEntryMatcher combine(@NotNull CompositeArrangementEntryMatcher.Operator operator, @NotNull ArrangementEntryMatcher... matchers) diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/ArrangementSettingsAtomNode.java b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/ArrangementSettingsAtomNode.java index 95751e6ffb8b..22a75c58888e 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/ArrangementSettingsAtomNode.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/ArrangementSettingsAtomNode.java @@ -90,6 +90,7 @@ public class ArrangementSettingsAtomNode implements ArrangementSettingsNode { return true; } + @NotNull @Override public ArrangementSettingsAtomNode clone() { ArrangementSettingsAtomNode result = new ArrangementSettingsAtomNode(myType, myValue); diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/ArrangementSettingsCompositeNode.java b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/ArrangementSettingsCompositeNode.java index 5144eeb985b8..4e36aeae7d27 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/ArrangementSettingsCompositeNode.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/ArrangementSettingsCompositeNode.java @@ -16,11 +16,10 @@ package com.intellij.psi.codeStyle.arrangement.model; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.containers.HashSet; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.Set; /** * // TODO den add doc @@ -30,7 +29,7 @@ import java.util.List; */ public class ArrangementSettingsCompositeNode implements ArrangementSettingsNode { - @NotNull private final List myOperands = new ArrayList(); + @NotNull private final Set myOperands = new HashSet(); @NotNull private final Operator myOperator; public ArrangementSettingsCompositeNode(@NotNull Operator operator) { @@ -38,7 +37,7 @@ public class ArrangementSettingsCompositeNode implements ArrangementSettingsNode } @NotNull - public List getOperands() { + public Set getOperands() { return myOperands; } @@ -57,6 +56,7 @@ public class ArrangementSettingsCompositeNode implements ArrangementSettingsNode visitor.visit(this); } + @NotNull @Override public ArrangementSettingsCompositeNode clone() { ArrangementSettingsCompositeNode result = new ArrangementSettingsCompositeNode(myOperator); @@ -70,7 +70,7 @@ public class ArrangementSettingsCompositeNode implements ArrangementSettingsNode public String toString() { return StringUtil.join(myOperands, myOperator == Operator.AND ? " and " : " or "); } - + public enum Operator { AND, OR } diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/ArrangementSettingsNode.java b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/ArrangementSettingsNode.java index 3759a39877d8..a8b55607966d 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/ArrangementSettingsNode.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/ArrangementSettingsNode.java @@ -27,5 +27,6 @@ public interface ArrangementSettingsNode extends Cloneable { void invite(@NotNull ArrangementSettingsNodeVisitor visitor); + @NotNull ArrangementSettingsNode clone(); } diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/settings/ArrangementSettingsGrouper.java b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/settings/ArrangementSettingsGrouper.java new file mode 100644 index 000000000000..33c2c671c7b7 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/settings/ArrangementSettingsGrouper.java @@ -0,0 +1,50 @@ +/* + * 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.settings; + +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode; +import com.intellij.psi.codeStyle.arrangement.model.HierarchicalArrangementSettingsNode; +import org.jetbrains.annotations.NotNull; + +/** + * Strategy which hints on how arrangement settings should be grouped at the UI. + *

+ * Implementations of this interface are expected to be thread-safe. + * + * @author Denis Zhdanov + * @since 8/15/12 4:21 PM + */ +public interface ArrangementSettingsGrouper { + + /** + * Allows to answer if and how should be grouped given settings. + *

+ * Example: given settings node is a composite 'AND' node with the following conditions: + * 'type: field; modifier: public; modifier: static; modifier: final'. We might want to show it like below: + *

+   *   field
+   *     |
+   *      ---public---static---final
+   * 
+ * That means that we'll return a {@link HierarchicalArrangementSettingsNode} with condition 'type: field' and + * a single child node with composite 'AND' condition 'modifier: public; modifier: static; modifier: final'. + * + * @param node settings node which conditions should be grouped + * @return grouping-aware node + */ + @NotNull + HierarchicalArrangementSettingsNode group(@NotNull ArrangementSettingsNode node); +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/settings/ArrangementStandardSettingsAware.java b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/settings/ArrangementStandardSettingsAware.java index 5daabda09c0b..ea02bbd5b155 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/settings/ArrangementStandardSettingsAware.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/settings/ArrangementStandardSettingsAware.java @@ -17,6 +17,7 @@ package com.intellij.psi.codeStyle.arrangement.settings; import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType; import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode; import com.intellij.psi.codeStyle.arrangement.sort.ArrangementEntrySortType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,14 +34,14 @@ import java.util.List; public interface ArrangementStandardSettingsAware { // TODO den add doc - boolean isNameFilterEnabled(@Nullable ArrangementMatcherSettings current); + boolean isNameFilterEnabled(@Nullable ArrangementSettingsNode current); // TODO den add doc - boolean isEnabled(@NotNull ArrangementEntryType type, @Nullable ArrangementMatcherSettings current); + boolean isEnabled(@NotNull ArrangementEntryType type, @Nullable ArrangementSettingsNode current); // TODO den add doc - boolean isEnabled(@NotNull ArrangementModifier modifier, @Nullable ArrangementMatcherSettings current); + boolean isEnabled(@NotNull ArrangementModifier modifier, @Nullable ArrangementSettingsNode current); // TODO den add doc - boolean isEnabled(@NotNull ArrangementEntrySortType type, @Nullable ArrangementMatcherSettings current); + boolean isEnabled(@NotNull ArrangementEntrySortType type, @Nullable ArrangementSettingsNode current); } diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/settings/DefaultArrangementSettingsGrouper.java b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/settings/DefaultArrangementSettingsGrouper.java new file mode 100644 index 000000000000..a7aa312165e7 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/settings/DefaultArrangementSettingsGrouper.java @@ -0,0 +1,36 @@ +/* + * 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.settings; + +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode; +import com.intellij.psi.codeStyle.arrangement.model.HierarchicalArrangementSettingsNode; +import org.jetbrains.annotations.NotNull; + +/** + * @author Denis Zhdanov + * @since 8/15/12 4:23 PM + */ +public class DefaultArrangementSettingsGrouper implements ArrangementSettingsGrouper { + + @NotNull public static final DefaultArrangementSettingsGrouper INSTANCE = new DefaultArrangementSettingsGrouper(); + + @NotNull + @Override + public HierarchicalArrangementSettingsNode group(@NotNull ArrangementSettingsNode node) { + // Don't group. + return new HierarchicalArrangementSettingsNode(node); + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/settings/group/ArrangementSettingsNodeGrouper.java b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/settings/group/ArrangementSettingsNodeGrouper.java deleted file mode 100644 index 18a70476651d..000000000000 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/settings/group/ArrangementSettingsNodeGrouper.java +++ /dev/null @@ -1,25 +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.settings.group; - -/** - * // TODO den add doc - * - * @author Denis Zhdanov - * @since 8/8/12 12:29 PM - */ -public interface ArrangementSettingsNodeGrouper { -} diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementAndNodeComponent.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementAndNodeComponent.java index 268fce33c73b..182bc092ba4e 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementAndNodeComponent.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementAndNodeComponent.java @@ -135,7 +135,7 @@ public class ArrangementAndNodeComponent extends JPanel implements ArrangementNo @Override public void paint(Graphics g) { - Point point = ArrangementSettingsUtil.getLocationOnScreen(this); + Point point = ArrangementConfigUtil.getLocationOnScreen(this); if (point != null) { Rectangle bounds = getBounds(); myScreenBounds = new Rectangle(point.x, point.y, bounds.width, bounds.height); diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementAtomNodeComponent.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementAtomNodeComponent.java index 9c8e714a61ed..1e2b1cf43702 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementAtomNodeComponent.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementAtomNodeComponent.java @@ -16,7 +16,6 @@ package com.intellij.application.options.codeStyle.arrangement; import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsAtomNode; -import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode; import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.awt.RelativePoint; import com.intellij.util.ui.GridBag; @@ -40,7 +39,7 @@ public class ArrangementAtomNodeComponent implements ArrangementNodeComponent { @NotNull private final JPanel myRenderer = new JPanel(new GridBagLayout()) { @Override public void paint(Graphics g) { - Point point = ArrangementSettingsUtil.getLocationOnScreen(this); + Point point = ArrangementConfigUtil.getLocationOnScreen(this); if (point != null) { Rectangle bounds = myRenderer.getBounds(); myScreenBounds = new Rectangle(point.x, point.y, bounds.width, bounds.height); @@ -114,7 +113,7 @@ public class ArrangementAtomNodeComponent implements ArrangementNodeComponent { @NotNull @Override - public ArrangementSettingsNode getSettingsNode() { + public ArrangementSettingsAtomNode getSettingsNode() { return mySettingsNode; } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementConfigUtil.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementConfigUtil.java new file mode 100644 index 000000000000..3fb162408e1e --- /dev/null +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementConfigUtil.java @@ -0,0 +1,116 @@ +/* + * 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.ArrangementEntryType; +import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode; +import com.intellij.psi.codeStyle.arrangement.settings.ArrangementMatcherSettings; +import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.*; +import java.util.List; + +/** + * Contains various utility methods to be used during showing arrangement settings. + *

+ * Thread-safe. + * + * @author Denis Zhdanov + * @since 8/8/12 9:14 AM + */ +public class ArrangementConfigUtil { + + private ArrangementConfigUtil() { + } + + /** + * Allows to answer what new settings are available for a particular {@link ArrangementMatcherSettings arrangement matcher rules}. + * + * @param filter filter to use + * @param settings object that encapsulates information about current arrangement matcher settings + * @return map which contains information on what new new settings are available at the current situation + */ + @NotNull + public static Map> buildAvailableOptions(@NotNull ArrangementStandardSettingsAware filter, + @Nullable ArrangementSettingsNode settings) + { + Map> result = new EnumMap>(ArrangementSettingType.class); + processData(filter, settings, result, ArrangementSettingType.TYPE, ArrangementEntryType.values()); + processData(filter, settings, result, ArrangementSettingType.MODIFIER, ArrangementModifier.values()); + return result; + } + + private static void processData(@NotNull ArrangementStandardSettingsAware filter, + @Nullable ArrangementSettingsNode settings, + @NotNull Map> result, + @NotNull ArrangementSettingType type, + @NotNull T[] values) + { + List data = null; + for (T v : values) { + if (!isEnabled(v, filter, settings)) { + continue; + } + if (data == null) { + data = new ArrayList(); + } + data.add(v); + } + if (data != null) { + result.put(type, data); + } + } + + public static boolean isEnabled(@NotNull Object conditionId, + @NotNull ArrangementStandardSettingsAware filter, + @Nullable ArrangementSettingsNode settings) + { + if (conditionId instanceof ArrangementEntryType) { + return filter.isEnabled((ArrangementEntryType)conditionId, settings); + } + else if (conditionId instanceof ArrangementModifier) { + return filter.isEnabled((ArrangementModifier)conditionId, settings); + } + else { + return false; + } + } + + @Nullable + public static Point getLocationOnScreen(@NotNull JComponent component) { + int dx = 0; + int dy = 0; + for (Container c = component; c != null; c = c.getParent()) { + if (c.isShowing()) { + Point locationOnScreen = c.getLocationOnScreen(); + locationOnScreen.translate(dx, dy); + return locationOnScreen; + } + else { + Point location = c.getLocation(); + dx += location.x; + dy += location.y; + } + } + return null; + } +} diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementMatcherRuleEditor.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementMatcherRuleEditor.java index 77add416e8db..7744f149a1ad 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementMatcherRuleEditor.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementMatcherRuleEditor.java @@ -16,11 +16,8 @@ package com.intellij.application.options.codeStyle.arrangement; import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryMatcher; -import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType; -import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier; import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType; import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsAtomNode; -import com.intellij.psi.codeStyle.arrangement.settings.ArrangementMatcherSettings; import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware; import com.intellij.util.ui.GridBag; import com.intellij.util.ui.MultiRowFlowPanel; @@ -29,6 +26,8 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.*; import java.util.List; @@ -47,18 +46,25 @@ public class ArrangementMatcherRuleEditor extends JPanel { new HashMap(); @NotNull private final ArrangementStandardSettingsAware myFilter; + @Nullable private ArrangementRuleEditingModel myModel; public ArrangementMatcherRuleEditor(@NotNull ArrangementStandardSettingsAware filter, @NotNull ArrangementNodeDisplayManager displayManager) { myFilter = filter; init(displayManager); + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + onMouseClicked(e); + } + }); } private void init(@NotNull ArrangementNodeDisplayManager displayManager) { setLayout(new GridBagLayout()); - Map> supportedSettings = ArrangementSettingsUtil.buildAvailableOptions(myFilter, null); + Map> supportedSettings = ArrangementConfigUtil.buildAvailableOptions(myFilter, null); addRowIfPossible(ArrangementSettingType.TYPE, supportedSettings, displayManager); addRowIfPossible(ArrangementSettingType.MODIFIER, supportedSettings, displayManager); } @@ -78,7 +84,7 @@ public class ArrangementMatcherRuleEditor extends JPanel { myComponents.put(value, component); valuesPanel.add(component.getUiComponent()); } - + int top = ArrangementAtomNodeComponent.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()); @@ -89,52 +95,68 @@ public class ArrangementMatcherRuleEditor 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 settings current rule settings if defined; null as an indication that no settings should be active + * @param model current rule settings model if defined; null as an indication that no settings should be active */ - public void updateState(@Nullable ArrangementMatcherSettings settings) { - for (ArrangementEntryType type : ArrangementEntryType.values()) { - ArrangementAtomNodeComponent component = myComponents.get(type); - if (component == null) { - continue; - } - boolean enabled; - boolean selected; - if (settings == null) { - enabled = false; - selected = false; - } - else { - enabled = myFilter.isEnabled(type, settings); - selected = settings.hasCondition(type); - } - component.setEnabled(enabled); - component.setSelected(selected); + public void updateState(@Nullable ArrangementRuleEditingModel model) { + myModel = model; + + // Reset state. + for (ArrangementAtomNodeComponent component : myComponents.values()) { + component.setEnabled(false); + component.setSelected(false); } - for (ArrangementModifier modifier : ArrangementModifier.values()) { - ArrangementAtomNodeComponent component = myComponents.get(modifier); - if (component == null) { - continue; + + if (model == null) { + return; + } + + Map> available = ArrangementConfigUtil.buildAvailableOptions(myFilter, model.getSettingsNode()); + for (Collection ids : available.values()) { + for (Object id : ids) { + ArrangementAtomNodeComponent component = myComponents.get(id); + if (component != null) { + component.setEnabled(true); + component.setSelected(model.hasCondition(id)); + } } - boolean enabled; - boolean selected; - if (settings == null) { - enabled = false; - selected = false; - } - else { - enabled = myFilter.isEnabled(modifier, settings); - selected = settings.hasCondition(modifier); - } - component.setEnabled(enabled); - component.setSelected(selected); } repaint(); } - + public void applyBackground(@NotNull Color color) { setBackground(color); for (JComponent component : myColoredComponents) { component.setBackground(color); } } + + private void onMouseClicked(@NotNull MouseEvent e) { + if (myModel == null) { + return; + } + ArrangementAtomNodeComponent component = getNodeComponentAt(e.getLocationOnScreen()); + if (component == null) { + return; + } + ArrangementSettingsAtomNode settingsNode = component.getSettingsNode(); + boolean remove = myModel.hasCondition(settingsNode.getValue()); + component.setSelected(!remove); + if (remove) { + myModel.removeAndCondition(settingsNode); + } + else { + myModel.addAndCondition(settingsNode); + } + } + + @Nullable + private ArrangementAtomNodeComponent getNodeComponentAt(@NotNull Point screenPoint) { + for (ArrangementAtomNodeComponent component : myComponents.values()) { + Rectangle screenBounds = component.getScreenBounds(); + if (screenBounds != null && screenBounds.contains(screenPoint)) { + return component; + } + } + return null; + } } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementNodeDisplayManager.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementNodeDisplayManager.java index 2f7322ddf6d5..e1cc76fa682e 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementNodeDisplayManager.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementNodeDisplayManager.java @@ -49,7 +49,7 @@ public class ArrangementNodeDisplayManager { @NotNull ArrangementStandardSettingsRepresentationAware representationManager) { myRepresentationManager = representationManager; - Map> map = ArrangementSettingsUtil.buildAvailableOptions(filter, null); + Map> map = ArrangementConfigUtil.buildAvailableOptions(filter, null); for (Map.Entry> entry : map.entrySet()) { myMaxWidths.put(entry.getKey(), maxWidth(entry.getValue())); } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementMatcherEditingListener.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingListener.java similarity index 79% rename from platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementMatcherEditingListener.java rename to platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingListener.java index 83a6dcd6da53..2993e37eb9f0 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementMatcherEditingListener.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingListener.java @@ -15,16 +15,15 @@ */ package com.intellij.application.options.codeStyle.arrangement; -import com.intellij.psi.codeStyle.arrangement.settings.ArrangementMatcherSettings; import org.jetbrains.annotations.NotNull; /** * @author Denis Zhdanov * @since 8/15/12 9:14 AM */ -public interface ArrangementMatcherEditingListener { +public interface ArrangementRuleEditingListener { - void startEditing(@NotNull ArrangementMatcherSettings settings); + void startEditing(@NotNull ArrangementRuleEditingModel settings); void stopEditing(); } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModel.java new file mode 100644 index 000000000000..2bb5749ca681 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModel.java @@ -0,0 +1,51 @@ +/* + * 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.ArrangementEntryType; +import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsAtomNode; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode; +import org.jetbrains.annotations.NotNull; + +/** + * Combines and encapsulates information about arrangement matcher rules representation (tree nodes) and + * underlying {@link ArrangementSettingsNode data model}. + *

+ * 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 + ArrangementSettingsNode getSettingsNode(); + + /** + * 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 true if current model has a registered mapping for the given key; + * false otherwise + */ + boolean hasCondition(@NotNull Object key); + + void addAndCondition(@NotNull ArrangementSettingsAtomNode node); + + void removeAndCondition(@NotNull ArrangementSettingsNode node); +} diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelBuilder.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelBuilder.java new file mode 100644 index 000000000000..d982ff8f8889 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelBuilder.java @@ -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.application.options.codeStyle.arrangement; + +import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode; +import com.intellij.psi.codeStyle.arrangement.model.HierarchicalArrangementSettingsNode; +import com.intellij.psi.codeStyle.arrangement.settings.ArrangementSettingsGrouper; +import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsRepresentationAware; +import gnu.trove.TIntObjectHashMap; +import org.jetbrains.annotations.NotNull; + +import javax.swing.tree.DefaultMutableTreeNode; + +/** + * Holds glue logic between arrangement settings and their representation - + * '{@link ArrangementSettingsNode} -> {@link ArrangementRuleEditingModel}' + *

+ * Thread-safe. + * + * @author Denis Zhdanov + * @since 8/15/12 2:50 PM + */ +public class ArrangementRuleEditingModelBuilder { + + /** + * Does the following: + *

+   * 
    + *
  1. + * {@link HierarchicalArrangementSettingsNode Groups} given {@link ArrangementSettingsNode settings} using + * the given {@link ArrangementStandardSettingsRepresentationAware#group(ArrangementSettingsNode) strategy}; + *
  2. + *
  3. + * Build {@link DefaultMutableTreeNode tree nodes} for the {@link HierarchicalArrangementSettingsNode groiping-aware nodes} + * and register them within the target tree structure (denoted by the given settings root element); + *
  4. + *
  5. + * Build necessary {@link ArrangementRuleEditingModel editing models} and store them at the given container (a key is a node row); + *
  6. + *
+ *
+ * + * @param setting target settings to process + * @param root UI tree settings root to use + * @param grouper strategy that knows how to + * {@link ArrangementStandardSettingsRepresentationAware#getDisplayValue(ArrangementModifier) group} setting + * nodes for UI representation + * @param rowMappings container to hold built {@link ArrangementRuleEditingModel editing models} (UI tree row numbers are used as keys) + */ + public void build(@NotNull ArrangementSettingsNode setting, + @NotNull DefaultMutableTreeNode root, + @NotNull ArrangementSettingsGrouper grouper, + @NotNull TIntObjectHashMap rowMappings) + { + HierarchicalArrangementSettingsNode grouped = grouper.group(setting); + } +} diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImpl.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImpl.java new file mode 100644 index 000000000000..650309c8a40e --- /dev/null +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImpl.java @@ -0,0 +1,56 @@ +/* + * 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.ArrangementSettingsAtomNode; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode; +import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware; +import org.jetbrains.annotations.NotNull; + +/** + * @author Denis Zhdanov + * @since 8/15/12 2:40 PM + */ +public class ArrangementRuleEditingModelImpl implements ArrangementRuleEditingModel { + + @NotNull private ArrangementSettingsNode mySettingsNode; + + public ArrangementRuleEditingModelImpl(@NotNull ArrangementSettingsNode node) { + mySettingsNode = node; + } + + @NotNull + @Override + public ArrangementSettingsNode getSettingsNode() { + return mySettingsNode; + } + + @Override + public boolean hasCondition(@NotNull Object key) { + // TODO den implement + return false; + } + + @Override + public void addAndCondition(@NotNull ArrangementSettingsAtomNode node) { + // TODO den implement + } + + @Override + public void removeAndCondition(@NotNull ArrangementSettingsNode node) { + // TODO den implement + } +} diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleTree.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleTree.java index 6a9d9e26123d..c2f9452132ec 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleTree.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleTree.java @@ -17,8 +17,11 @@ package com.intellij.application.options.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 com.intellij.psi.codeStyle.arrangement.settings.ArrangementMatcherSettings; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsAtomNode; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsCompositeNode; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode; +import com.intellij.psi.codeStyle.arrangement.settings.ArrangementSettingsGrouper; import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.treeStructure.Tree; import gnu.trove.TIntObjectHashMap; @@ -43,18 +46,20 @@ import java.util.List; */ public class ArrangementRuleTree { - @NotNull private final List myListeners = new ArrayList(); - @NotNull private final TreeSelectionModel mySelectionModel = new MySelectionModel(); - @NotNull private final TIntObjectHashMap myRenderers = new TIntObjectHashMap(); - @NotNull private final TIntObjectHashMap mySettings = - new TIntObjectHashMap(); + @NotNull private final List myListeners = new ArrayList(); + @NotNull private final TreeSelectionModel mySelectionModel = new MySelectionModel(); - @NotNull private final Tree myTree; - @NotNull private final ArrangementNodeComponentFactory myFactory; + @NotNull private final TIntObjectHashMap myRenderers = + new TIntObjectHashMap(); + @NotNull private final TIntObjectHashMap myModels = + new TIntObjectHashMap(); + + @NotNull private final Tree myTree; + @NotNull private final ArrangementNodeComponentFactory myFactory; private boolean mySkipSelectionChange; - public ArrangementRuleTree(@NotNull ArrangementNodeDisplayManager displayManager) { + public ArrangementRuleTree(@NotNull ArrangementSettingsGrouper grouper, @NotNull ArrangementNodeDisplayManager displayManager) { myFactory = new ArrangementNodeComponentFactory(displayManager); DefaultMutableTreeNode root = new DefaultMutableTreeNode(); DefaultTreeModel treeModel = new DefaultTreeModel(root); @@ -102,32 +107,23 @@ public class ArrangementRuleTree { onMouseClicked(e); } }); - - ArrangementSettingsCompositeNode constants = new ArrangementSettingsCompositeNode(ArrangementSettingsCompositeNode.Operator.AND); - constants.addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.PUBLIC)); - constants.addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.STATIC)); - constants.addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.FINAL)); - - ArrangementSettingsCompositeNode privateFields = new ArrangementSettingsCompositeNode(ArrangementSettingsCompositeNode.Operator.AND); - privateFields.addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.PRIVATE)); - - HierarchicalArrangementSettingsNode fields = new HierarchicalArrangementSettingsNode(new ArrangementSettingsAtomNode( - ArrangementSettingType.TYPE, ArrangementEntryType.FIELD - )); - fields.addChild(new HierarchicalArrangementSettingsNode(constants)); - fields.addChild(new HierarchicalArrangementSettingsNode(privateFields)); - int row = map(root, fields, null, 0); - - HierarchicalArrangementSettingsNode methods = new HierarchicalArrangementSettingsNode(new ArrangementSettingsAtomNode( - ArrangementSettingType.TYPE, ArrangementEntryType.METHOD - )); - methods.addChild(new HierarchicalArrangementSettingsNode(new ArrangementSettingsAtomNode( - ArrangementSettingType.MODIFIER, ArrangementModifier.PUBLIC - ))); - methods.addChild(new HierarchicalArrangementSettingsNode(new ArrangementSettingsAtomNode( - ArrangementSettingType.MODIFIER, ArrangementModifier.PRIVATE - ))); - map(root, methods, null, row); + + List rules = new ArrayList(); + rules.add(new ArrangementSettingsCompositeNode(ArrangementSettingsCompositeNode.Operator.AND) + .addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.TYPE, ArrangementEntryType.FIELD)) + .addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.PUBLIC)) + .addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.STATIC)) + .addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.FINAL))); + rules.add(new ArrangementSettingsCompositeNode(ArrangementSettingsCompositeNode.Operator.AND) + .addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.TYPE, ArrangementEntryType.FIELD)) + .addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.PRIVATE))); + rules.add(new ArrangementSettingsCompositeNode(ArrangementSettingsCompositeNode.Operator.AND) + .addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.TYPE, ArrangementEntryType.METHOD)) + .addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.PUBLIC))); + rules.add(new ArrangementSettingsCompositeNode(ArrangementSettingsCompositeNode.Operator.AND) + .addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.TYPE, ArrangementEntryType.METHOD)) + .addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.PRIVATE))); + map(root, rules, grouper); expandAll(myTree, new TreePath(root)); myTree.setRootVisible(false); @@ -168,58 +164,37 @@ public class ArrangementRuleTree { tree.expandPath(parent); } - private int map(@NotNull DefaultMutableTreeNode parentTreeNode, - @NotNull HierarchicalArrangementSettingsNode settingsNode, - @Nullable ArrangementSettingsCompositeNode template, - int row) + private void map(@NotNull DefaultMutableTreeNode root, + @NotNull List settings, + @NotNull ArrangementSettingsGrouper grouper) { - DefaultMutableTreeNode childTreeNode = new DefaultMutableTreeNode(settingsNode.getCurrent()); - parentTreeNode.add(childTreeNode); - List children = settingsNode.getChildren(); - if (children.isEmpty()) { - ArrangementMatcherSettings settings = new ArrangementMatcherSettings(); - ArrangementSettingsNode condition; - if (template == null) { - condition = settingsNode.getCurrent(); - } - else { - condition = template.clone().addOperand(settingsNode.getCurrent()); - } - settings.addCondition(condition); - mySettings.put(row, settings); - return row + 1; - } - else { - row++; - ArrangementSettingsCompositeNode newTemplate = - template == null ? new ArrangementSettingsCompositeNode(ArrangementSettingsCompositeNode.Operator.AND) - : template.clone(); - newTemplate.addOperand(settingsNode.getCurrent()); - for (HierarchicalArrangementSettingsNode node : children) { - row = map(childTreeNode, node, newTemplate, row); - } - return row; + ArrangementRuleEditingModelBuilder builder = new ArrangementRuleEditingModelBuilder(); + for (ArrangementSettingsNode setting : settings) { + builder.build(setting, root, grouper, myModels); } } - public void addEditingListener(@NotNull ArrangementMatcherEditingListener listener) { + public void addEditingListener(@NotNull ArrangementRuleEditingListener listener) { myListeners.add(listener); } /** - * @return matcher settings for the selected tree row(s) if any; null otherwise + * @return matcher model for the selected tree row(s) if any; null otherwise */ @Nullable - public ArrangementMatcherSettings getActiveSettings() { + public ArrangementRuleEditingModel getActiveModel() { TreePath[] paths = mySelectionModel.getSelectionPaths(); if (paths == null) { return null; } + + // There is a possible case that particular settings node is represented on multiple rows and that non-leaf nodes are served + // for more than one rule. No model is registered for them then and we want just to skip them. for (int i = paths.length - 1; i >= 0; i--) { int row = myTree.getRowForPath(paths[i]); - ArrangementMatcherSettings settings = mySettings.get(row); - if (settings != null) { - return settings; + ArrangementRuleEditingModel model = myModels.get(row); + if (model != null) { + return model; } } return null; @@ -296,13 +271,13 @@ public class ArrangementRuleTree { } } - private void notifyEditingListeners(@Nullable ArrangementMatcherSettings settings) { - for (ArrangementMatcherEditingListener listener : myListeners) { - if (settings == null) { + private void notifyEditingListeners(@Nullable ArrangementRuleEditingModel model) { + for (ArrangementRuleEditingListener listener : myListeners) { + if (model == null) { listener.stopEditing(); } else { - listener.startEditing(settings); + listener.startEditing(model); } } } @@ -328,7 +303,7 @@ public class ArrangementRuleTree { public void setSelectionPath(TreePath path) { if (!mySkipSelectionChange) { super.setSelectionPath(path); - notifyEditingListeners(getActiveSettings()); + notifyEditingListeners(getActiveModel()); } } } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementSettingsPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementSettingsPanel.java index 1e963eb3cc41..a29343689220 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementSettingsPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementSettingsPanel.java @@ -22,10 +22,7 @@ import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.util.Ref; import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.arrangement.settings.ArrangementMatcherSettings; -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.psi.codeStyle.arrangement.settings.*; import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.treeStructure.Tree; import com.intellij.util.ui.GridBag; @@ -54,8 +51,12 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel { representationManager = (ArrangementStandardSettingsRepresentationAware)filter; } ArrangementNodeDisplayManager displayManager = new ArrangementNodeDisplayManager(filter, representationManager); + ArrangementSettingsGrouper grouper = DefaultArrangementSettingsGrouper.INSTANCE; + if (filter instanceof ArrangementSettingsGrouper) { + grouper = (ArrangementSettingsGrouper)filter; + } - final ArrangementRuleTree ruleTree = new ArrangementRuleTree(displayManager); + final ArrangementRuleTree ruleTree = new ArrangementRuleTree(grouper, displayManager); Tree component = ruleTree.getTreeComponent(); myContent.add(new JBScrollPane(component), new GridBag().weightx(1).weighty(1).fillCell().coverLine()); CustomizationUtil.installPopupHandler( @@ -78,10 +79,10 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel { } } }); - ruleTree.addEditingListener(new ArrangementMatcherEditingListener() { + ruleTree.addEditingListener(new ArrangementRuleEditingListener() { @Override - public void startEditing(@NotNull ArrangementMatcherSettings settings) { - ruleEditor.updateState(settings); + public void startEditing(@NotNull ArrangementRuleEditingModel model) { + ruleEditor.updateState(model); resetEditor.set(Boolean.FALSE); try { editorPane.setCollapsed(false); diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementSettingsUtil.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementSettingsUtil.java deleted file mode 100644 index 82f4b89a4c81..000000000000 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementSettingsUtil.java +++ /dev/null @@ -1,177 +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.actionSystem.DataContext; -import com.intellij.openapi.actionSystem.DataKey; -import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType; -import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier; -import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType; -import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode; -import com.intellij.psi.codeStyle.arrangement.model.HierarchicalArrangementSettingsNode; -import com.intellij.psi.codeStyle.arrangement.settings.ArrangementMatcherSettings; -import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.awt.*; -import java.util.*; -import java.util.List; - -/** - * // TODO den add doc - * - * @author Denis Zhdanov - * @since 8/8/12 9:14 AM - */ -public class ArrangementSettingsUtil { - - public static final DataKey NODE_COMPONENT = DataKey.create("Arrangement.Rule.Editor.Node.Component"); - public static final DataKey FILTER = DataKey.create("Arrangement.Rule.Editor.Settings.Filter"); - public static final DataKey DISPLAY_MANAGER = DataKey.create("Arrangement.Rule.Editor.Display.Manager"); - public static final DataKey SETTINGS = DataKey.create("Arrangement.Rule.Editor.Matcher.Settings"); - public static final DataKey TREE = DataKey.create("Arrangement.Rule.Editor.Tree"); - - private static final EntryTypeHelper ENTRY_TYPE_HELPER = new EntryTypeHelper(); - private static final ModifierHelper MODIFIER_HELPER = new ModifierHelper(); - - private ArrangementSettingsUtil() { - } - - @Nullable - public static ArrangementSettingsNode getSettingsNode(@NotNull DataContext context) { - ArrangementNodeComponent nodeComponent = NODE_COMPONENT.getData(context); - return nodeComponent == null ? null : nodeComponent.getSettingsNode(); - } - - @Nullable - public static HierarchicalArrangementSettingsNode buildTreeStructure(@NotNull ArrangementSettingsNode modelNode) { - // TODO den implement - return new HierarchicalArrangementSettingsNode(modelNode); - } - - /** - * Serves for the same purposes as {@link #buildAvailableOptions(ArrangementStandardSettingsAware, ArrangementMatcherSettings)} but - * retrieves necessary information from the given context. - * - * @param context target information holder - * @return map which contains information on what new new settings are available at the current situation - */ - @NotNull - public static Map> buildAvailableOptions(@NotNull DataContext context) { - ArrangementStandardSettingsAware filter = FILTER.getData(context); - if (filter == null) { - return Collections.emptyMap(); - } - - return buildAvailableOptions(filter, SETTINGS.getData(context)); - } - - /** - * Allows to answer what new settings are available for a particular {@link ArrangementMatcherSettings arrangement matcher rules}. - * - * @param filter filter to use - * @param settings object that encapsulates information about current arrangement matcher settings - * @return map which contains information on what new new settings are available at the current situation - */ - @NotNull - public static Map> buildAvailableOptions(@NotNull ArrangementStandardSettingsAware filter, - @Nullable ArrangementMatcherSettings settings) - { - Map> result = new EnumMap>(ArrangementSettingType.class); - processData(filter, settings, result, ArrangementEntryType.values(), ENTRY_TYPE_HELPER); - processData(filter, settings, result, ArrangementModifier.values(), MODIFIER_HELPER); - return result; - } - - private static void processData(@NotNull ArrangementStandardSettingsAware filter, - @Nullable ArrangementMatcherSettings settings, - Map> result, - @NotNull T[] values, - @NotNull Helper helper) - { - List data = null; - for (T v : values) { - if (!helper.isEnabled(v, filter, settings)) { - continue; - } - if (data == null) { - data = new ArrayList(); - } - data.add(v); - } - if (data != null) { - result.put(helper.getType(), data); - } - } - - @Nullable - public static Point getLocationOnScreen(@NotNull JComponent component) { - int dx = 0; - int dy = 0; - for (Container c = component; c != null; c = c.getParent()) { - if (c.isShowing()) { - Point locationOnScreen = c.getLocationOnScreen(); - locationOnScreen.translate(dx, dy); - return locationOnScreen; - } - else { - Point location = c.getLocation(); - dx += location.x; - dy += location.y; - } - } - return null; - } - - private interface Helper { - @NotNull ArrangementSettingType getType(); - boolean isEnabled(@NotNull T data, @NotNull ArrangementStandardSettingsAware filter, @Nullable ArrangementMatcherSettings settings); - } - - private static class EntryTypeHelper implements Helper { - @NotNull - @Override - public ArrangementSettingType getType() { - return ArrangementSettingType.TYPE; - } - - @Override - public boolean isEnabled(@NotNull ArrangementEntryType data, - @NotNull ArrangementStandardSettingsAware filter, - @Nullable ArrangementMatcherSettings settings) - { - return filter.isEnabled(data, settings); - } - } - - private static class ModifierHelper implements Helper { - @NotNull - @Override - public ArrangementSettingType getType() { - return ArrangementSettingType.MODIFIER; - } - - @Override - public boolean isEnabled(@NotNull ArrangementModifier data, - @NotNull ArrangementStandardSettingsAware filter, - @Nullable ArrangementMatcherSettings settings) - { - return filter.isEnabled(data, settings); - } - } -} diff --git a/platform/lang-impl/src/com/intellij/ide/impl/StructureViewWrapperImpl.java b/platform/lang-impl/src/com/intellij/ide/impl/StructureViewWrapperImpl.java index 0b84ae387d91..930f39e25499 100644 --- a/platform/lang-impl/src/com/intellij/ide/impl/StructureViewWrapperImpl.java +++ b/platform/lang-impl/src/com/intellij/ide/impl/StructureViewWrapperImpl.java @@ -173,7 +173,8 @@ public class StructureViewWrapperImpl implements StructureViewWrapper, Disposabl // ------------------------------------------------------------------------- public void dispose() { - rebuild(); + //we don't really need it + //rebuild(); } public boolean selectCurrentElement(final FileEditor fileEditor, final VirtualFile file, final boolean requestFocus) { diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index 949af09c749f..12fdf629ee91 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -415,10 +415,10 @@ checkbox.indent.absolute.label.indent=Absolute label indent checkbox.do.not.indent.top.level.class.members=Do not indent top level class members checkbox.use.relative.indents=Use indents relative to expression start tooltip.indent.must.be.multiple.of.tab.size.for.smart.tabs.to.operate=Indent must be multiple of tab size for smart tabs to operate -combobox.crlf.system.dependent=System Dependent -combobox.crlf.unix=Unix \n -combobox.crlf.windows=Windows \r\n -combobox.crlf.mac=Mac \r +combobox.crlf.system.dependent=System-Dependent +combobox.crlf.unix=Unix and OS X (\\n) +combobox.crlf.windows=Windows (\\r\\n) +combobox.crlf.mac=Classic Mac (\\r) group.tabs.and.indents=Tabs and Indents checkbox.indent.use.same.settings.for.all.file.types=Use default indent options (General) tab.indent.other=Other @@ -554,4 +554,4 @@ arrangement.title.settings.tab=Arrangement arrangement.title.editor=Edit rule arrangement.text.type=Type arrangement.text.modifier=Modifier -arrangement.text.negate=Negate \ No newline at end of file +arrangement.text.negate=Negate diff --git a/plugins/android/src/META-INF/plugin.xml b/plugins/android/src/META-INF/plugin.xml index 2dd3255775ed..69e6dbe3d537 100644 --- a/plugins/android/src/META-INF/plugin.xml +++ b/plugins/android/src/META-INF/plugin.xml @@ -216,7 +216,9 @@ serviceImplementation="org.jetbrains.android.exportSignedPackage.GenerateSignedApkSettings"/> + + diff --git a/plugins/android/src/org/jetbrains/android/dom/AndroidXmlDocumentationProvider.java b/plugins/android/src/org/jetbrains/android/dom/AndroidXmlDocumentationProvider.java new file mode 100644 index 000000000000..688491876b32 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/dom/AndroidXmlDocumentationProvider.java @@ -0,0 +1,44 @@ +package org.jetbrains.android.dom; + +import com.intellij.lang.documentation.DocumentationProvider; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiManager; +import org.jetbrains.android.dom.wrappers.LazyValueResourceElementWrapper; +import org.jetbrains.android.resourceManagers.ValueResourceInfo; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidXmlDocumentationProvider implements DocumentationProvider { + @Override + public String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) { + if (element instanceof LazyValueResourceElementWrapper) { + final ValueResourceInfo info = ((LazyValueResourceElementWrapper)element).getResourceInfo(); + return "value resource '" + info.getName() + "' [" + info.getContainingFile().getName() + "]"; + } + return null; + } + + @Override + public List getUrlFor(PsiElement element, PsiElement originalElement) { + return null; + } + + @Override + public String generateDoc(PsiElement element, @Nullable PsiElement originalElement) { + return null; + } + + @Override + public PsiElement getDocumentationElementForLookupItem(PsiManager psiManager, Object object, PsiElement element) { + return null; + } + + @Override + public PsiElement getDocumentationElementForLink(PsiManager psiManager, String link, PsiElement context) { + return null; + } +} diff --git a/plugins/android/src/org/jetbrains/android/uipreview/AndroidLayoutPreviewToolWindowManager.java b/plugins/android/src/org/jetbrains/android/uipreview/AndroidLayoutPreviewToolWindowManager.java index 5c875c55713e..ba3e47d69164 100644 --- a/plugins/android/src/org/jetbrains/android/uipreview/AndroidLayoutPreviewToolWindowManager.java +++ b/plugins/android/src/org/jetbrains/android/uipreview/AndroidLayoutPreviewToolWindowManager.java @@ -425,14 +425,18 @@ public class AndroidLayoutPreviewToolWindowManager implements ProjectComponent { catch (RenderingException e) { LOG.debug(e); String message = e.getPresentableMessage(); - message = message != null ? message : AndroidBundle.message("android.layout.preview.default.error.message"); final Throwable[] causes = e.getCauses(); + message = message != null ? message : AndroidBundle.message("android.layout.preview.default.error.message"); errorMessage = causes.length > 0 ? new FixableIssueMessage(message + ' ', "Details", "", new Runnable() { @Override public void run() { showStackStace(causes); } }) : new FixableIssueMessage(message); + + if (causes.length == 0) { + warnMessages.addAll(e.getWarnMessages()); + } } catch (IOException e) { LOG.info(e); diff --git a/plugins/android/src/org/jetbrains/android/uipreview/ProjectCallback.java b/plugins/android/src/org/jetbrains/android/uipreview/ProjectCallback.java index 7ce87cc0f88c..0d1ff6d734c8 100644 --- a/plugins/android/src/org/jetbrains/android/uipreview/ProjectCallback.java +++ b/plugins/android/src/org/jetbrains/android/uipreview/ProjectCallback.java @@ -23,11 +23,16 @@ import com.android.ide.common.resources.IntArrayWrapper; import com.android.resources.ResourceType; import com.android.sdklib.SdkConstants; import com.android.util.Pair; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; +import com.intellij.openapi.util.Computable; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiClass; import com.intellij.util.containers.HashSet; import gnu.trove.TIntObjectHashMap; import gnu.trove.TObjectIntHashMap; +import org.jetbrains.android.util.AndroidUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -50,6 +55,7 @@ class ProjectCallback extends LegacyCallback implements IProjectCallback { private final Set myClassesWithIncorrectFormat = new HashSet(); private final Map> myLoadedClasses = new HashMap>(); + private boolean myHasProjectLoadedClasses = false; private ProjectClassLoader myProjectClassLoader = null; private final ClassLoader myParentClassLoader; @@ -104,14 +110,14 @@ class ProjectCallback extends LegacyCallback implements IProjectCallback { @SuppressWarnings("ThrowableResultOfMethodCallIgnored") @Nullable public Object loadView(String className, Class[] constructorSignature, Object[] constructorArgs) - throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException { + throws ClassNotFoundException { Class aClass = myLoadedClasses.get(className); - if (aClass != null) { - return createNewInstance(aClass, constructorSignature, constructorArgs); - } try { + if (aClass != null) { + return createNewInstance(aClass, constructorSignature, constructorArgs); + } aClass = loadClass(className); if (aClass != null) { @@ -152,6 +158,11 @@ class ProjectCallback extends LegacyCallback implements IProjectCallback { } try { + final Object o = createViewFromSuperclass(className, constructorSignature, constructorArgs); + + if (o != null) { + return o; + } return createMockView(className, constructorSignature, constructorArgs); } catch (ClassNotFoundException e) { @@ -195,6 +206,56 @@ class ProjectCallback extends LegacyCallback implements IProjectCallback { return myClassesWithIncorrectFormat.size() > 0; } + @Nullable + private Object createViewFromSuperclass(final String className, final Class[] constructorSignature, final Object[] constructorArgs) { + return ApplicationManager.getApplication().runReadAction(new Computable() { + @Nullable + @Override + public Object compute() { + final JavaPsiFacade facade = JavaPsiFacade.getInstance(myModule.getProject()); + PsiClass psiClass = facade.findClass(className, myModule.getModuleWithDependenciesAndLibrariesScope(false)); + + if (psiClass == null) { + return null; + } + psiClass = psiClass.getSuperClass(); + final Set visited = new HashSet(); + + while (psiClass != null) { + final String qName = psiClass.getQualifiedName(); + + if (qName == null || !visited.add(qName)) { + break; + } + + if (!AndroidUtils.isAbstract(psiClass)) { + try { + Class aClass = myLoadedClasses.get(qName); + if (aClass == null) { + aClass = myParentClassLoader.loadClass(qName); + if (aClass != null) { + myLoadedClasses.put(qName, aClass); + } + } + if (aClass != null) { + final Object instance = createNewInstance(aClass, constructorSignature, constructorArgs); + + if (instance != null) { + return instance; + } + } + } + catch (Exception e) { + LOG.debug(e); + } + } + psiClass = psiClass.getSuperClass(); + } + return null; + } + }); + } + private Object createMockView(String className, Class[] constructorSignature, Object[] constructorArgs) throws ClassNotFoundException, @@ -345,7 +406,7 @@ class ProjectCallback extends LegacyCallback implements IProjectCallback { } public boolean hasLoadedClasses() { - return myLoadedClasses.size() > 0; + return myHasProjectLoadedClasses; } @NotNull @@ -371,6 +432,7 @@ class ProjectCallback extends LegacyCallback implements IProjectCallback { if (aClass != null) { myLoadedClasses.put(className, aClass); + myHasProjectLoadedClasses = true; } } diff --git a/plugins/android/src/org/jetbrains/android/uipreview/RenderUtil.java b/plugins/android/src/org/jetbrains/android/uipreview/RenderUtil.java index 4ab024c41292..d790b9b62084 100644 --- a/plugins/android/src/org/jetbrains/android/uipreview/RenderUtil.java +++ b/plugins/android/src/org/jetbrains/android/uipreview/RenderUtil.java @@ -179,8 +179,7 @@ public class RenderUtil { if (exception != null) { final List exceptionsFromWarnings = getNonNullValues(callback.getBrokenClasses()); - if (exceptionsFromWarnings.size() > 0 && - exception instanceof ClassCastException && + if (exception instanceof ClassCastException && (SdkConstants.CLASS_MOCK_VIEW + " cannot be cast to " + SdkConstants.CLASS_VIEWGROUP) .equalsIgnoreCase(exception.getMessage())) { throw new RenderingException(exceptionsFromWarnings.toArray(new Throwable[exceptionsFromWarnings.size()])) diff --git a/plugins/android/src/org/jetbrains/android/uipreview/RenderingException.java b/plugins/android/src/org/jetbrains/android/uipreview/RenderingException.java index b2b9338be2f6..a01b086f0d6b 100644 --- a/plugins/android/src/org/jetbrains/android/uipreview/RenderingException.java +++ b/plugins/android/src/org/jetbrains/android/uipreview/RenderingException.java @@ -4,6 +4,7 @@ import org.jetbrains.annotations.NotNull; import java.io.PrintStream; import java.io.PrintWriter; +import java.util.Collections; import java.util.List; /** @@ -12,7 +13,7 @@ import java.util.List; public class RenderingException extends Exception { private final String myPresentableMessage; private final Throwable[] myCauses; - private List myWarnMessages; + private List myWarnMessages = Collections.emptyList(); public RenderingException() { super(); diff --git a/plugins/android/testData/documentation/strings.xml b/plugins/android/testData/documentation/strings.xml new file mode 100644 index 000000000000..a32234c7861c --- /dev/null +++ b/plugins/android/testData/documentation/strings.xml @@ -0,0 +1,3 @@ + + Hello + \ No newline at end of file diff --git a/plugins/android/testData/documentation/valueResourceReferenceQuickDoc.xml b/plugins/android/testData/documentation/valueResourceReferenceQuickDoc.xml new file mode 100644 index 000000000000..68fdf63a6302 --- /dev/null +++ b/plugins/android/testData/documentation/valueResourceReferenceQuickDoc.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/android/testSrc/org/jetbrains/android/AndroidDocumentationTest.java b/plugins/android/testSrc/org/jetbrains/android/AndroidDocumentationTest.java new file mode 100644 index 000000000000..742486942349 --- /dev/null +++ b/plugins/android/testSrc/org/jetbrains/android/AndroidDocumentationTest.java @@ -0,0 +1,21 @@ +package org.jetbrains.android; + +import com.intellij.codeInsight.navigation.CtrlMouseHandler; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiReference; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidDocumentationTest extends AndroidTestCase { + private static final String BASE_PATH = "documentation/"; + + public void testValueResourceReferenceQuickDoc() { + myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml"); + final VirtualFile f = myFixture.copyFileToProject(BASE_PATH + getTestName(true) + ".xml", "res/layout/test.xml"); + myFixture.configureFromExistingVirtualFile(f); + final PsiReference ref = myFixture.getFile().findReferenceAt(myFixture.getEditor().getCaretModel().getOffset()); + assert ref != null; + assertEquals("value resource 'myString' [strings.xml]", CtrlMouseHandler.getInfo(ref.resolve(), ref.getElement())); + } +} diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/AbstractDomChildDescriptionImpl.java b/xml/dom-impl/src/com/intellij/util/xml/impl/AbstractDomChildDescriptionImpl.java index 186e810c2fe7..0c43f399491b 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/AbstractDomChildDescriptionImpl.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/AbstractDomChildDescriptionImpl.java @@ -71,7 +71,7 @@ public abstract class AbstractDomChildDescriptionImpl implements AbstractDomChil if (myCustomAnnotations != null ? !myCustomAnnotations.equals(that.myCustomAnnotations) : that.myCustomAnnotations != null) return false; - if (!myType.equals(that.myType)) return false; + if (!getType().equals(that.getType())) return false; if (myUserMap != null ? !myUserMap.equals(that.myUserMap) : that.myUserMap != null) return false; return true; @@ -79,7 +79,7 @@ public abstract class AbstractDomChildDescriptionImpl implements AbstractDomChil @Override public int hashCode() { - int result = myType.hashCode(); + int result = getType().hashCode(); result = 31 * result + (myCustomAnnotations != null ? myCustomAnnotations.hashCode() : 0); result = 31 * result + (myUserMap != null ? myUserMap.hashCode() : 0); return result; @@ -126,7 +126,7 @@ public abstract class AbstractDomChildDescriptionImpl implements AbstractDomChil @NotNull public DomNameStrategy getDomNameStrategy(@NotNull DomElement parent) { - final DomNameStrategy strategy = DomImplUtil.getDomNameStrategy(ReflectionUtil.getRawType(myType), false); + final DomNameStrategy strategy = DomImplUtil.getDomNameStrategy(ReflectionUtil.getRawType(getType()), false); return strategy == null ? parent.getNameStrategy() : strategy; } diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/AttributeChildDescriptionImpl.java b/xml/dom-impl/src/com/intellij/util/xml/impl/AttributeChildDescriptionImpl.java index a8e2def1b106..cdba46160aa6 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/AttributeChildDescriptionImpl.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/AttributeChildDescriptionImpl.java @@ -23,7 +23,7 @@ public class AttributeChildDescriptionImpl extends DomChildDescriptionImpl imple myGetterMethod = getter; } - protected AttributeChildDescriptionImpl(final XmlName attributeName, @NotNull Type type) { + public AttributeChildDescriptionImpl(final XmlName attributeName, @NotNull Type type) { super(attributeName, type); myGetterMethod = null; } diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/CustomDomChildrenDescriptionImpl.java b/xml/dom-impl/src/com/intellij/util/xml/impl/CustomDomChildrenDescriptionImpl.java index be2c90094140..b99eda0c7a09 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/CustomDomChildrenDescriptionImpl.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/CustomDomChildrenDescriptionImpl.java @@ -21,6 +21,7 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.NotNullFunction; import com.intellij.util.xml.*; import com.intellij.util.xml.reflect.CustomDomChildrenDescription; +import com.intellij.util.xml.reflect.DomExtensionImpl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,16 +40,27 @@ public class CustomDomChildrenDescriptionImpl extends AbstractDomChildDescriptio return DomImplUtil.getCustomSubTags(handler, handler.getXmlTag().getSubTags(), handler.getFile()); } }; + private final TagNameDescriptor myTagNameDescriptor; + private final AttributeDescriptor myAttributeDescriptor; + public CustomDomChildrenDescriptionImpl(@NotNull final JavaMethod getter) { - this(getter, DomReflectionUtil.extractCollectionElementType(getter.getGenericReturnType()), TagNameDescriptor.EMPTY); + this(getter, DomReflectionUtil.extractCollectionElementType(getter.getGenericReturnType()), + TagNameDescriptor.EMPTY, TagNameDescriptor.EMPTY); } - public CustomDomChildrenDescriptionImpl(@Nullable final JavaMethod getter, @NotNull Type type, @NotNull TagNameDescriptor descriptor) { + public CustomDomChildrenDescriptionImpl(DomExtensionImpl custom) { + this(null, custom.getType(), custom.getTagNameDescriptor(), custom.getAttributesDescriptor()); + } + + private CustomDomChildrenDescriptionImpl(@Nullable final JavaMethod getter, @NotNull Type type, + @Nullable TagNameDescriptor descriptor, + @Nullable AttributeDescriptor attributesDescriptor) { super(type); myGetter = getter; myTagNameDescriptor = descriptor; + myAttributeDescriptor = attributesDescriptor; } @Nullable public JavaMethod getGetterMethod() { @@ -84,12 +96,16 @@ public class CustomDomChildrenDescriptionImpl extends AbstractDomChildDescriptio return new DummyEvaluatedXmlName(childTag.getLocalName(), childTag.getNamespace()); } - @NotNull @Override public TagNameDescriptor getTagNameDescriptor() { return myTagNameDescriptor; } + @Override + public AttributeDescriptor getCustomAttributeDescriptor() { + return myAttributeDescriptor; + } + @Override public boolean equals(Object obj) { return obj instanceof CustomDomChildrenDescriptionImpl; diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DomFileElementImpl.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DomFileElementImpl.java index 57de7a1da85a..e6dc26567e48 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/DomFileElementImpl.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DomFileElementImpl.java @@ -42,9 +42,9 @@ public class DomFileElementImpl implements DomFileElement< return null; } - @Nullable - public CustomDomChildrenDescription getCustomNameChildrenDescription() { - return null; + @NotNull + public List getCustomNameChildrenDescription() { + return Collections.emptyList(); } @Nullable diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DomGenericInfoEx.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DomGenericInfoEx.java index 6f1e1b487a19..64ffab046f8a 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/DomGenericInfoEx.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DomGenericInfoEx.java @@ -21,6 +21,7 @@ import com.intellij.util.xml.EvaluatedXmlName; import com.intellij.util.xml.JavaMethod; import com.intellij.util.xml.XmlName; import com.intellij.util.xml.reflect.AbstractDomChildrenDescription; +import com.intellij.util.xml.reflect.CustomDomChildrenDescription; import com.intellij.util.xml.reflect.DomGenericInfo; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -55,7 +56,17 @@ public abstract class DomGenericInfoEx implements DomGenericInfo { } } } - return attribute ? null : getCustomNameChildrenDescription(); + + List list = getCustomNameChildrenDescription(); + for (CustomDomChildrenDescription description : list) { + if (attribute) { + // todo + } + else if (description.getTagNameDescriptor() != null) { + return description; + } + } + return null; } public abstract boolean processAttributeChildrenDescriptions(Processor processor); diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DomSemContributor.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DomSemContributor.java index 69ec7501f127..b51f43e7bdaf 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/DomSemContributor.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DomSemContributor.java @@ -42,10 +42,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; +import java.util.*; import static com.intellij.patterns.XmlPatterns.*; @@ -160,8 +157,8 @@ public class DomSemContributor extends SemContributor { if (parent == null) return null; DomGenericInfoEx info = parent.getGenericInfo(); - final CustomDomChildrenDescription customDescription = info.getCustomNameChildrenDescription(); - if (customDescription == null) return null; + final List customs = info.getCustomNameChildrenDescription(); + if (customs.isEmpty()) return null; if (mySemService.getSemElement(DomManagerImpl.DOM_INDEXED_HANDLER_KEY, tag) == null && mySemService.getSemElement(DomManagerImpl.DOM_COLLECTION_HANDLER_KEY, tag) == null) { @@ -174,10 +171,13 @@ public class DomSemContributor extends SemContributor { return null; } } - - AbstractCollectionChildDescription desc = (AbstractCollectionChildDescription)customDescription; - Type type = customDescription.getType(); - return new CollectionElementInvocationHandler(type, tag, desc, parent, null); + for (CustomDomChildrenDescription description : customs) { + if (description.getTagNameDescriptor() != null) { + AbstractCollectionChildDescription desc = (AbstractCollectionChildDescription)description; + Type type = description.getType(); + return new CollectionElementInvocationHandler(type, tag, desc, parent, null); + } + } } return null; diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DynamicGenericInfo.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DynamicGenericInfo.java index 53738a25f2ab..7ed28403a095 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/DynamicGenericInfo.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DynamicGenericInfo.java @@ -24,6 +24,7 @@ import com.intellij.openapi.util.RecursionManager; import com.intellij.psi.xml.XmlElement; import com.intellij.psi.xml.XmlFile; import com.intellij.util.ArrayUtil; +import com.intellij.util.Function; import com.intellij.util.Processor; import com.intellij.util.containers.ConcurrentWeakHashMap; import com.intellij.util.containers.ContainerUtil; @@ -53,7 +54,7 @@ public class DynamicGenericInfo extends DomGenericInfoEx { private volatile ChildrenDescriptionsHolder myAttributes; private volatile ChildrenDescriptionsHolder myFixeds; private volatile ChildrenDescriptionsHolder myCollections; - private volatile CustomDomChildrenDescriptionImpl myCustomChildren; + private volatile List myCustomChildren; public DynamicGenericInfo(@NotNull final DomInvocationHandler handler, final StaticGenericInfo staticGenericInfo) { myInvocationHandler = handler; @@ -126,10 +127,13 @@ public class DynamicGenericInfo extends DomGenericInfoEx { myCollections = internChildrenHolder(file, newCollections); } - final DomExtensionImpl extension = registrar.getCustomChildrenType(); - if (extension != null) { - myCustomChildren = new CustomDomChildrenDescriptionImpl(null, extension.getType(), extension.getTagNameDescriptor()); - } + final List customs = registrar.getCustoms(); + myCustomChildren = customs.isEmpty() ? null : ContainerUtil.map(customs, new Function() { + @Override + public CustomDomChildrenDescriptionImpl fun(DomExtensionImpl extension) { + return new CustomDomChildrenDescriptionImpl(extension); + } + }); } private static ChildrenDescriptionsHolder internChildrenHolder(XmlFile file, ChildrenDescriptionsHolder holder) { @@ -177,8 +181,8 @@ public class DynamicGenericInfo extends DomGenericInfoEx { return myStaticGenericInfo.getNameDomElement(element); } - @Nullable - public CustomDomChildrenDescriptionImpl getCustomNameChildrenDescription() { + @NotNull + public List getCustomNameChildrenDescription() { checkInitialized(); if (myCustomChildren != null) return myCustomChildren; return myStaticGenericInfo.getCustomNameChildrenDescription(); @@ -195,7 +199,7 @@ public class DynamicGenericInfo extends DomGenericInfoEx { myAttributes.dumpDescriptions(list); myFixeds.dumpDescriptions(list); myCollections.dumpDescriptions(list); - ContainerUtil.addIfNotNull(myStaticGenericInfo.getCustomNameChildrenDescription(), list); + list.addAll(myStaticGenericInfo.getCustomNameChildrenDescription()); return list; } diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/StaticGenericInfo.java b/xml/dom-impl/src/com/intellij/util/xml/impl/StaticGenericInfo.java index a5e50666d2af..17e2e29755d0 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/StaticGenericInfo.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/StaticGenericInfo.java @@ -215,9 +215,9 @@ public class StaticGenericInfo extends DomGenericInfoEx { return o instanceof GenericDomValue ? (GenericDomValue)o : null; } - @Nullable - public CustomDomChildrenDescriptionImpl getCustomNameChildrenDescription() { - return myCustomDescription; + @NotNull + public List getCustomNameChildrenDescription() { + return myCustomDescription == null ? Collections.emptyList() : Collections.singletonList(myCustomDescription); } @Nullable @@ -239,7 +239,7 @@ public class StaticGenericInfo extends DomGenericInfoEx { myAttributes.dumpDescriptions(list); myFixed.dumpDescriptions(list); myCollections.dumpDescriptions(list); - ContainerUtil.addIfNotNull(getCustomNameChildrenDescription(), list); + list.addAll(getCustomNameChildrenDescription()); return list; } diff --git a/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtensionImpl.java b/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtensionImpl.java index 641a0585b126..4009ba74fbd1 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtensionImpl.java +++ b/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtensionImpl.java @@ -50,6 +50,7 @@ public class DomExtensionImpl implements DomExtension { private int myCount = 1; private Map myUserMap; private CustomDomChildrenDescription.TagNameDescriptor myTagNameDescriptor = CustomDomChildrenDescription.TagNameDescriptor.EMPTY; + private CustomDomChildrenDescription.AttributeDescriptor myAttributesDescriptor; public DomExtensionImpl(final Type type, final XmlName xmlName) { myType = type; @@ -64,6 +65,14 @@ public class DomExtensionImpl implements DomExtension { return myTagNameDescriptor; } + public CustomDomChildrenDescription.AttributeDescriptor getAttributesDescriptor() { + return myAttributesDescriptor; + } + + public void setAttributesDescriptor(CustomDomChildrenDescription.AttributeDescriptor attributesDescriptor) { + myAttributesDescriptor = attributesDescriptor; + } + @NotNull public XmlName getXmlName() { return myXmlName; diff --git a/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtensionsRegistrarImpl.java b/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtensionsRegistrarImpl.java index e5e95aedf984..415b0f667c98 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtensionsRegistrarImpl.java +++ b/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtensionsRegistrarImpl.java @@ -23,6 +23,7 @@ import com.intellij.util.xml.GenericAttributeValue; import com.intellij.util.xml.XmlName; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.lang.reflect.Type; import java.util.List; @@ -36,7 +37,7 @@ public class DomExtensionsRegistrarImpl implements DomExtensionsRegistrar { private final List myFixeds = new SmartList(); private final List myCollections = new SmartList(); private final Set myDependencies = new THashSet(); - private DomExtensionImpl myCustomChildrenType; + private final List myCustoms = new SmartList(); public List getAttributes() { return myAttributes; @@ -49,8 +50,8 @@ public class DomExtensionsRegistrarImpl implements DomExtensionsRegistrar { return myCollections; } - public DomExtensionImpl getCustomChildrenType() { - return myCustomChildrenType; + public List getCustoms() { + return myCustoms; } @NotNull @@ -89,13 +90,22 @@ public class DomExtensionsRegistrarImpl implements DomExtensionsRegistrar { @Override public DomExtension registerCustomChildrenExtension(@NotNull Type type, @NotNull CustomDomChildrenDescription.TagNameDescriptor descriptor) { - assert myCustomChildrenType == null; - myCustomChildrenType = new DomExtensionImpl(type, null); - myCustomChildrenType.setTagNameDescriptor(descriptor); - return myCustomChildrenType; + DomExtensionImpl extension = addExtension(myCustoms, null, type); + extension.setTagNameDescriptor(descriptor); + return extension; } - private static DomExtensionImpl addExtension(final List list, final XmlName name, final Type type) { + @NotNull + @Override + public DomExtension registerCustomChildrenExtension(@NotNull Type type, + @NotNull CustomDomChildrenDescription.AttributeDescriptor attributeDescriptor) { + + DomExtensionImpl extension = addExtension(myCustoms, null, type); + extension.setAttributesDescriptor(attributeDescriptor); + return extension; + } + + private static DomExtensionImpl addExtension(final List list, @Nullable final XmlName name, final Type type) { final DomExtensionImpl extension = new DomExtensionImpl(type, name); list.add(extension); return extension; diff --git a/xml/dom-impl/src/com/intellij/xml/impl/dom/AbstractDomChildrenDescriptor.java b/xml/dom-impl/src/com/intellij/xml/impl/dom/AbstractDomChildrenDescriptor.java index 2d701555987f..b8c59cae20ed 100644 --- a/xml/dom-impl/src/com/intellij/xml/impl/dom/AbstractDomChildrenDescriptor.java +++ b/xml/dom-impl/src/com/intellij/xml/impl/dom/AbstractDomChildrenDescriptor.java @@ -10,6 +10,7 @@ import com.intellij.psi.xml.XmlTag; import com.intellij.util.xml.DomElement; import com.intellij.util.xml.DomManager; import com.intellij.util.xml.EvaluatedXmlName; +import com.intellij.util.xml.impl.AttributeChildDescriptionImpl; import com.intellij.util.xml.reflect.*; import com.intellij.xml.XmlAttributeDescriptor; import com.intellij.xml.XmlElementDescriptor; @@ -47,14 +48,15 @@ public abstract class AbstractDomChildrenDescriptor implements XmlElementDescrip xmlElementDescriptors.add(new DomElementXmlDescriptor(childrenDescription, myManager)); } - final CustomDomChildrenDescription customDescription = domElement.getGenericInfo().getCustomNameChildrenDescription(); - if (customDescription != null) { + final List customs = domElement.getGenericInfo().getCustomNameChildrenDescription(); + + for (final CustomDomChildrenDescription custom : customs) { final XmlTag xmlTag = domElement.getXmlTag(); - for (final EvaluatedXmlName name : customDescription.getTagNameDescriptor().getCompletionVariants(domElement)) { - xmlElementDescriptors.add(new AbstractDomChildrenDescriptor(myManager) { + for (final EvaluatedXmlName name : custom.getTagNameDescriptor().getCompletionVariants(domElement)) { + AbstractDomChildrenDescriptor descriptor = new AbstractDomChildrenDescriptor(myManager) { @Override public String getDefaultName() { - final String ns = xmlTag != null? name.getNamespace(xmlTag, (XmlFile)xmlTag.getContainingFile()) : null; + final String ns = xmlTag != null ? name.getNamespace(xmlTag, (XmlFile)xmlTag.getContainingFile()) : null; if (ns != null) { final String prefix = xmlTag.getPrefixByNamespace(ns); if (prefix != null) { @@ -67,11 +69,11 @@ public abstract class AbstractDomChildrenDescriptor implements XmlElementDescrip @Override @Nullable public PsiElement getDeclaration() { - final PomTarget target = customDescription.getTagNameDescriptor().findDeclaration(domElement, name); + final PomTarget target = custom.getTagNameDescriptor().findDeclaration(domElement, name); return target == null ? null : PomService.convertToPsi(context.getProject(), target); } - - }); + }; + xmlElementDescriptors.add(descriptor); } xmlElementDescriptors.add(new AnyXmlElementDescriptor(this, getNSDescriptor())); @@ -140,7 +142,16 @@ public abstract class AbstractDomChildrenDescriptor implements XmlElementDescrip for (DomAttributeChildDescription description : descriptions) { descriptors.add(new DomAttributeXmlDescriptor(description, myManager.getProject())); } - + List customs = domElement.getGenericInfo().getCustomNameChildrenDescription(); + for (CustomDomChildrenDescription custom : customs) { + CustomDomChildrenDescription.AttributeDescriptor list = custom.getCustomAttributeDescriptor(); + if (list != null) { + for (EvaluatedXmlName variant : list.getCompletionVariants(domElement)) { + AttributeChildDescriptionImpl childDescription = new AttributeChildDescriptionImpl(variant.getXmlName(), String.class); + descriptors.add(new DomAttributeXmlDescriptor(childDescription, myManager.getProject())); + } + } + } return descriptors.toArray(new XmlAttributeDescriptor[descriptors.size()]); } diff --git a/xml/dom-openapi/src/com/intellij/util/xml/reflect/CustomDomChildrenDescription.java b/xml/dom-openapi/src/com/intellij/util/xml/reflect/CustomDomChildrenDescription.java index 6900c27fc124..e70c982e7739 100644 --- a/xml/dom-openapi/src/com/intellij/util/xml/reflect/CustomDomChildrenDescription.java +++ b/xml/dom-openapi/src/com/intellij/util/xml/reflect/CustomDomChildrenDescription.java @@ -22,6 +22,7 @@ import com.intellij.util.xml.EvaluatedXmlName; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Type; import java.util.Collections; import java.util.Set; @@ -29,35 +30,36 @@ import java.util.Set; * @author peter */ public interface CustomDomChildrenDescription extends AbstractDomChildrenDescription { - @NotNull + @Nullable TagNameDescriptor getTagNameDescriptor(); - abstract class TagNameDescriptor { - public static final TagNameDescriptor EMPTY = new TagNameDescriptor() { - @Override - public Set getCompletionVariants(@NotNull DomElement parent) { - return Collections.emptySet(); - } + @Nullable + AttributeDescriptor getCustomAttributeDescriptor(); - @Override - public PomTarget findDeclaration(DomElement parent, @NotNull EvaluatedXmlName name) { - return null; - } + class TagNameDescriptor { - @Override - public PomTarget findDeclaration(@NotNull DomElement child) { - return child.getChildDescription(); - } - }; + public static final AttributeDescriptor EMPTY = new AttributeDescriptor(); - public abstract Set getCompletionVariants(@NotNull DomElement parent); + public Set getCompletionVariants(@NotNull DomElement parent) { + return Collections.emptySet(); + } @Nullable - public abstract PomTarget findDeclaration(DomElement parent, @NotNull EvaluatedXmlName name); + public PomTarget findDeclaration(DomElement parent, @NotNull EvaluatedXmlName name) { + return null; + } @Nullable - public abstract PomTarget findDeclaration(@NotNull DomElement child); + public PomTarget findDeclaration(@NotNull DomElement child) { + return child.getChildDescription(); + } } - + + class AttributeDescriptor extends TagNameDescriptor { + + public Type getElementType(DomElement child) { + throw new UnsupportedOperationException(); + } + } } diff --git a/xml/dom-openapi/src/com/intellij/util/xml/reflect/DomExtensionsRegistrar.java b/xml/dom-openapi/src/com/intellij/util/xml/reflect/DomExtensionsRegistrar.java index ae343f3b4793..5bd0072b79a8 100644 --- a/xml/dom-openapi/src/com/intellij/util/xml/reflect/DomExtensionsRegistrar.java +++ b/xml/dom-openapi/src/com/intellij/util/xml/reflect/DomExtensionsRegistrar.java @@ -46,6 +46,10 @@ public interface DomExtensionsRegistrar { */ @NotNull DomExtension registerCustomChildrenExtension(@NotNull final Type type); - @NotNull DomExtension registerCustomChildrenExtension(@NotNull final Type type, @NotNull CustomDomChildrenDescription.TagNameDescriptor descriptor); + @NotNull DomExtension registerCustomChildrenExtension(@NotNull final Type type, + @NotNull CustomDomChildrenDescription.TagNameDescriptor descriptor); + + @NotNull DomExtension registerCustomChildrenExtension(@NotNull final Type type, + @NotNull CustomDomChildrenDescription.AttributeDescriptor attributeDescriptor); } diff --git a/xml/dom-openapi/src/com/intellij/util/xml/reflect/DomGenericInfo.java b/xml/dom-openapi/src/com/intellij/util/xml/reflect/DomGenericInfo.java index d0b00f0f955e..3fc7b1c88d1f 100644 --- a/xml/dom-openapi/src/com/intellij/util/xml/reflect/DomGenericInfo.java +++ b/xml/dom-openapi/src/com/intellij/util/xml/reflect/DomGenericInfo.java @@ -75,6 +75,6 @@ public interface DomGenericInfo { @Nullable GenericDomValue getNameDomElement(DomElement element); - @Nullable - CustomDomChildrenDescription getCustomNameChildrenDescription(); + @NotNull + List getCustomNameChildrenDescription(); } diff --git a/xml/dom-tests/tests/com/intellij/util/xml/DomChildrenTest.java b/xml/dom-tests/tests/com/intellij/util/xml/DomChildrenTest.java index d50b878b9ad1..169c66b5aae0 100644 --- a/xml/dom-tests/tests/com/intellij/util/xml/DomChildrenTest.java +++ b/xml/dom-tests/tests/com/intellij/util/xml/DomChildrenTest.java @@ -464,7 +464,7 @@ public class DomChildrenTest extends DomTestCase { final MyElement myElement = createElement("", MyElement.class); final XmlTag tag = myElement.getXmlTag(); final List customChildren = myElement.getCustomChildren(); - assertOrderedEquals(customChildren, myElement.getGenericInfo().getCustomNameChildrenDescription().getValues(myElement)); + assertOrderedEquals(customChildren, myElement.getGenericInfo().getCustomNameChildrenDescription().get(0).getValues(myElement)); assertOrderedCollection(customChildren, new Consumer() { @Override public void consume(final MyElement element) {