diff options
Diffstat (limited to 'xml/xml-psi-impl/src/com/intellij')
7 files changed, 208 insertions, 88 deletions
diff --git a/xml/xml-psi-impl/src/com/intellij/psi/impl/source/html/dtd/HtmlAttributeDescriptorImpl.java b/xml/xml-psi-impl/src/com/intellij/psi/impl/source/html/dtd/HtmlAttributeDescriptorImpl.java index 445a5503a855..d6bbd3ba3722 100644 --- a/xml/xml-psi-impl/src/com/intellij/psi/impl/source/html/dtd/HtmlAttributeDescriptorImpl.java +++ b/xml/xml-psi-impl/src/com/intellij/psi/impl/source/html/dtd/HtmlAttributeDescriptorImpl.java @@ -16,14 +16,16 @@ package com.intellij.psi.impl.source.html.dtd; import com.intellij.psi.PsiElement; +import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlElement; import com.intellij.util.ArrayUtil; import com.intellij.xml.XmlAttributeDescriptor; +import com.intellij.xml.impl.BasicXmlAttributeDescriptor; /** * @author Maxim.Mossienko */ -public class HtmlAttributeDescriptorImpl implements XmlAttributeDescriptor { +public class HtmlAttributeDescriptorImpl extends BasicXmlAttributeDescriptor { private final XmlAttributeDescriptor delegate; private final boolean myCaseSensitive; @@ -90,4 +92,9 @@ public class HtmlAttributeDescriptorImpl implements XmlAttributeDescriptor { public String toString() { return delegate.toString(); } + + @Override + protected PsiElement getEnumeratedValueDeclaration(XmlAttributeValue attributeValue, String value) { + return super.getEnumeratedValueDeclaration(attributeValue, myCaseSensitive ? value : value.toLowerCase()); + } } diff --git a/xml/xml-psi-impl/src/com/intellij/xml/impl/BasicXmlAttributeDescriptor.java b/xml/xml-psi-impl/src/com/intellij/xml/impl/BasicXmlAttributeDescriptor.java index 1aa72c3f7ffc..83cf70f5ff37 100644 --- a/xml/xml-psi-impl/src/com/intellij/xml/impl/BasicXmlAttributeDescriptor.java +++ b/xml/xml-psi-impl/src/com/intellij/xml/impl/BasicXmlAttributeDescriptor.java @@ -24,40 +24,18 @@ */ package com.intellij.xml.impl; -import com.intellij.codeInsight.daemon.XmlErrorMessages; +import com.intellij.openapi.util.Comparing; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlElement; +import com.intellij.util.ArrayUtilRt; +import com.intellij.xml.util.XmlAttributeValueReference; import com.intellij.xml.XmlAttributeDescriptor; -import com.intellij.xml.util.XmlUtil; import org.jetbrains.annotations.Nullable; public abstract class BasicXmlAttributeDescriptor implements XmlAttributeDescriptor { public String validateValue(XmlElement context, String value) { - if (isFixed()) { - String defaultValue = getDefaultValue(); - - if (defaultValue != null && !defaultValue.equals(value)) { - return XmlErrorMessages.message("attribute.should.have.fixed.value", getName(), defaultValue); - } - } - - if (isEnumerated(context) && XmlUtil.isSimpleXmlAttributeValue(value, (XmlAttributeValue)context)) { - String[] values = getEnumeratedValues(context); - boolean valueWasFound = false; - - for (String enumValue : values) { - if (enumValue.equals(value)) { - valueWasFound = true; - break; - } - } - - if (!valueWasFound) { - return XmlErrorMessages.message("wrong.attribute.value"); - } - } - return null; } @@ -78,4 +56,26 @@ public abstract class BasicXmlAttributeDescriptor implements XmlAttributeDescrip public String toString() { return getName(); } + + public PsiElement getValueDeclaration(XmlAttributeValue attributeValue, String value) { + String defaultValue = getDefaultValue(); + if (Comparing.equal(defaultValue, value)) { + return getDefaultValueDeclaration(); + } + return isFixed() ? null : getEnumeratedValueDeclaration(attributeValue, value); + } + + protected PsiElement getEnumeratedValueDeclaration(XmlAttributeValue attributeValue, String value) { + String[] values = getEnumeratedValues(); + if (values == null || values.length == 0) return getDeclaration(); + return ArrayUtilRt.find(values, value) != -1 ? getDeclaration() : null; + } + + protected PsiElement getDefaultValueDeclaration() { + return getDeclaration(); + } + + public PsiReference[] getValueReferences(XmlAttributeValue value) { + return new PsiReference[] { new XmlAttributeValueReference(value, this)}; + } } diff --git a/xml/xml-psi-impl/src/com/intellij/xml/impl/schema/XmlAttributeDescriptorImpl.java b/xml/xml-psi-impl/src/com/intellij/xml/impl/schema/XmlAttributeDescriptorImpl.java index 195449b9ef02..2248f003ab98 100644 --- a/xml/xml-psi-impl/src/com/intellij/xml/impl/schema/XmlAttributeDescriptorImpl.java +++ b/xml/xml-psi-impl/src/com/intellij/xml/impl/schema/XmlAttributeDescriptorImpl.java @@ -15,22 +15,19 @@ */ package com.intellij.xml.impl.schema; +import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiElement; -import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.meta.PsiWritableMetaData; -import com.intellij.psi.xml.XmlElement; -import com.intellij.psi.xml.XmlFile; -import com.intellij.psi.xml.XmlTag; -import com.intellij.util.ArrayUtil; -import com.intellij.util.IncorrectOperationException; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.*; +import com.intellij.util.*; import com.intellij.xml.XmlElementDescriptor; import com.intellij.xml.impl.BasicXmlAttributeDescriptor; import com.intellij.xml.util.XmlUtil; import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.HashSet; +import java.util.List; /** * @author Mike @@ -38,6 +35,8 @@ import java.util.HashSet; public class XmlAttributeDescriptorImpl extends BasicXmlAttributeDescriptor implements PsiWritableMetaData { private XmlTag myTag; String myUse; + private boolean myExhaustiveEnum; + @NonNls public static final String REQUIRED_ATTR_VALUE = "required"; @NonNls @@ -115,18 +114,7 @@ public class XmlAttributeDescriptorImpl extends BasicXmlAttributeDescriptor impl } public boolean isEnumerated(@Nullable XmlElement context) { - final XmlElementDescriptorImpl elementDescriptor = (XmlElementDescriptorImpl)XmlUtil.findXmlDescriptorByType( - myTag, - context != null ?PsiTreeUtil.getContextOfType(context, XmlTag.class, true):null - ); - - if (elementDescriptor != null && - elementDescriptor.getType() instanceof ComplexTypeDescriptor) { - final EnumerationData data = getEnumeratedValuesImpl(((ComplexTypeDescriptor)elementDescriptor.getType()).getDeclaration()); - return data != null && data.exaustive; - } - - return false; + return processEnumeration(context, PairProcessor.TRUE); } public boolean isEnumerated() { @@ -138,19 +126,27 @@ public class XmlAttributeDescriptorImpl extends BasicXmlAttributeDescriptor impl } public String[] getEnumeratedValues(XmlElement context) { - final XmlElementDescriptorImpl elementDescriptor = (XmlElementDescriptorImpl)XmlUtil.findXmlDescriptorByType( - myTag, - context != null ?PsiTreeUtil.getContextOfType(context, XmlTag.class, true):null - ); + final List<String> list = new SmartList<String>(); + processEnumeration(context, new PairProcessor<PsiElement, String>() { + @Override + public boolean process(PsiElement element, String s) { + list.add(s); + return true; + } + }); + String defaultValue = getDefaultValue(); + if (defaultValue != null) { + list.add(defaultValue); + } + return ArrayUtil.toStringArray(list); + } - if (elementDescriptor!=null && elementDescriptor.getType() instanceof ComplexTypeDescriptor) { - final EnumerationData data = getEnumeratedValuesImpl(((ComplexTypeDescriptor)elementDescriptor.getType()).getDeclaration()); - final String s = getDefaultValue(); + private boolean processEnumeration(XmlElement context, PairProcessor<PsiElement, String> processor) { + XmlTag contextTag = context != null ? PsiTreeUtil.getContextOfType(context, XmlTag.class, true) : null; + final XmlElementDescriptorImpl elementDescriptor = (XmlElementDescriptorImpl)XmlUtil.findXmlDescriptorByType(myTag, contextTag); - if (s != null && s.length() > 0 && data == null) { - return new String[] {s}; - } - return data != null? data.enumeratedValues:ArrayUtil.EMPTY_STRING_ARRAY; + if (elementDescriptor!=null && elementDescriptor.getType() instanceof ComplexTypeDescriptor) { + return processEnumerationImpl(((ComplexTypeDescriptor)elementDescriptor.getType()).getDeclaration(), processor); } final String namespacePrefix = myTag.getNamespacePrefix(); @@ -159,35 +155,42 @@ public class XmlAttributeDescriptorImpl extends BasicXmlAttributeDescriptor impl ); if (type != null) { - final EnumerationData data = getEnumeratedValuesImpl(type); - return data != null? data.enumeratedValues:ArrayUtil.EMPTY_STRING_ARRAY; + return processEnumerationImpl(type, processor); } - return ArrayUtil.EMPTY_STRING_ARRAY; - } - - static class EnumerationData { - final String[] enumeratedValues; - final boolean exaustive; - - EnumerationData(@NotNull String[] _values, boolean _exaustive) { - enumeratedValues = _values; - exaustive = _exaustive; - } + return false; } - private static EnumerationData getEnumeratedValuesImpl(final XmlTag declaration) { + private boolean processEnumerationImpl(final XmlTag declaration, final PairProcessor<PsiElement, String> pairProcessor) { if ("boolean".equals(declaration.getAttributeValue("name"))) { - return new EnumerationData(new String[] {"true", "false"}, true); + XmlAttributeValue valueElement = declaration.getAttribute("name").getValueElement(); + pairProcessor.process(valueElement, "true"); + pairProcessor.process(valueElement, "false"); + myExhaustiveEnum = true; + return true; } - final HashSet<String> variants = new HashSet<String>(); - final boolean exaustive = XmlUtil.collectEnumerationValues(declaration, variants); + else { + final Ref<Boolean> found = new Ref<Boolean>(Boolean.FALSE); + myExhaustiveEnum = XmlUtil.processEnumerationValues(declaration, new Processor<XmlTag>() { + @Override + public boolean process(XmlTag tag) { + found.set(Boolean.TRUE); + XmlAttribute name = tag.getAttribute("value"); + return name == null || pairProcessor.process(name.getValueElement(), name.getValue()); + } + }); + return found.get(); + } + } - if (variants.size() > 0) { - return new EnumerationData(ArrayUtil.toStringArray(variants), exaustive); + @Override + public PsiElement getValueDeclaration(XmlAttributeValue attributeValue, String value) { + PsiElement declaration = super.getValueDeclaration(attributeValue, value); + if (declaration == null && !myExhaustiveEnum) { + return getDeclaration(); } - return null; + return declaration; } public String getName(PsiElement context) { @@ -243,4 +246,20 @@ public class XmlAttributeDescriptorImpl extends BasicXmlAttributeDescriptor impl public void setName(String name) throws IncorrectOperationException { NamedObjectDescriptor.setName(myTag, name); } + + @Override + protected PsiElement getEnumeratedValueDeclaration(XmlAttributeValue attributeValue, final String value) { + final Ref<PsiElement> result = new Ref<PsiElement>(); + processEnumeration(myTag, new PairProcessor<PsiElement, String>() { + @Override + public boolean process(PsiElement element, String s) { + if (value.equals(s)) { + result.set(element); + return false; + } + return true; + } + }); + return result.get(); + } } diff --git a/xml/xml-psi-impl/src/com/intellij/xml/impl/schema/XmlNSDescriptorImpl.java b/xml/xml-psi-impl/src/com/intellij/xml/impl/schema/XmlNSDescriptorImpl.java index 8b4c20a76c2c..4e5e37eaf0d4 100644 --- a/xml/xml-psi-impl/src/com/intellij/xml/impl/schema/XmlNSDescriptorImpl.java +++ b/xml/xml-psi-impl/src/com/intellij/xml/impl/schema/XmlNSDescriptorImpl.java @@ -757,7 +757,7 @@ public class XmlNSDescriptorImpl implements XmlNSDescriptorEx,Validator<XmlDocum return true; } - protected static boolean equalsToSchemaName(XmlTag tag, @NonNls String schemaName) { + protected static boolean equalsToSchemaName(@NotNull XmlTag tag, @NonNls String schemaName) { return schemaName.equals(tag.getLocalName()) && checkSchemaNamespace(tag); } diff --git a/xml/xml-psi-impl/src/com/intellij/xml/impl/schema/XmlSchemaTagsProcessor.java b/xml/xml-psi-impl/src/com/intellij/xml/impl/schema/XmlSchemaTagsProcessor.java index b884ced15bf6..98cb0b4923d6 100644 --- a/xml/xml-psi-impl/src/com/intellij/xml/impl/schema/XmlSchemaTagsProcessor.java +++ b/xml/xml-psi-impl/src/com/intellij/xml/impl/schema/XmlSchemaTagsProcessor.java @@ -31,6 +31,8 @@ import java.util.Set; */ public abstract class XmlSchemaTagsProcessor { + public final static ThreadLocal<Boolean> PROCESSING_FLAG = new ThreadLocal<Boolean>(); + private final Set<XmlTag> myVisited = new HashSet<XmlTag>(); protected final XmlNSDescriptorImpl myNsDescriptor; private final String[] myTagsToIgnore; @@ -41,10 +43,16 @@ public abstract class XmlSchemaTagsProcessor { } public final void startProcessing(XmlTag tag) { - processTag(tag, null); + try { + PROCESSING_FLAG.set(Boolean.TRUE); + processTag(tag, null); + } + finally { + PROCESSING_FLAG.set(null); + } } - public void processTag(XmlTag tag, @Nullable XmlTag context) { + private void processTag(XmlTag tag, @Nullable XmlTag context) { if (myVisited.contains(tag)) return; myVisited.add(tag); @@ -80,6 +88,7 @@ public abstract class XmlSchemaTagsProcessor { if (ref == null) return; XmlTag group; XmlTag parentTag = tag.getParentTag(); + assert parentTag != null; if (XmlNSDescriptorImpl.equalsToSchemaName(parentTag, "attributeGroup") && ref.equals(parentTag.getAttributeValue("name"))) { group = resolveTagReference(tag.getAttribute("ref")); diff --git a/xml/xml-psi-impl/src/com/intellij/xml/util/XmlAttributeValueReference.java b/xml/xml-psi-impl/src/com/intellij/xml/util/XmlAttributeValueReference.java new file mode 100644 index 000000000000..75a604b520c8 --- /dev/null +++ b/xml/xml-psi-impl/src/com/intellij/xml/util/XmlAttributeValueReference.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2013 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.xml.util; + +import com.intellij.codeInsight.daemon.EmptyResolveMessageProvider; +import com.intellij.codeInsight.daemon.XmlErrorMessages; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReferenceBase; +import com.intellij.psi.xml.XmlAttributeValue; +import com.intellij.util.ArrayUtil; +import com.intellij.xml.XmlAttributeDescriptor; +import com.intellij.xml.impl.BasicXmlAttributeDescriptor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** +* @author Dmitry Avdeev +* Date: 16.08.13 +*/ +public class XmlAttributeValueReference extends PsiReferenceBase<XmlAttributeValue> implements EmptyResolveMessageProvider { + private final XmlAttributeDescriptor myDescriptor; + + public XmlAttributeValueReference(XmlAttributeValue value, XmlAttributeDescriptor descriptor) { + super(value); + myDescriptor = descriptor; + } + + public XmlAttributeValueReference(XmlAttributeValue element, + TextRange range, + XmlAttributeDescriptor descriptor) { + super(element, range); + myDescriptor = descriptor; + } + + @Nullable + @Override + public PsiElement resolve() { + return ((BasicXmlAttributeDescriptor)myDescriptor).getValueDeclaration(getElement(), getValue()); + } + + @NotNull + @Override + public Object[] getVariants() { + if (myDescriptor.isFixed()) { + String defaultValue = myDescriptor.getDefaultValue(); + return defaultValue == null ? ArrayUtil.EMPTY_OBJECT_ARRAY : new Object[] {defaultValue}; + } + else { + String[] values = myDescriptor.getEnumeratedValues(); + return values == null ? ArrayUtil.EMPTY_OBJECT_ARRAY : values; + } + } + + @NotNull + @Override + public String getUnresolvedMessagePattern() { + return myDescriptor.isFixed() + ? XmlErrorMessages.message("attribute.should.have.fixed.value", myDescriptor.getDefaultValue()) + : XmlErrorMessages.message("wrong.attribute.value"); + } +} diff --git a/xml/xml-psi-impl/src/com/intellij/xml/util/XmlUtil.java b/xml/xml-psi-impl/src/com/intellij/xml/util/XmlUtil.java index ee38a7e63518..e8bb8f4c6f41 100644 --- a/xml/xml-psi-impl/src/com/intellij/xml/util/XmlUtil.java +++ b/xml/xml-psi-impl/src/com/intellij/xml/util/XmlUtil.java @@ -933,26 +933,33 @@ public class XmlUtil { }); } + /** + * @return true if enumeration is exhaustive + */ public static boolean processEnumerationValues(final XmlTag element, final Processor<XmlTag> tagProcessor) { - boolean exaustiveEnum = true; + boolean exhaustiveEnum = true; for (final XmlTag tag : element.getSubTags()) { @NonNls final String localName = tag.getLocalName(); if (localName.equals(ENUMERATION_TAG_NAME)) { final String attributeValue = tag.getAttributeValue(VALUE_ATTR_NAME); - if (attributeValue != null) tagProcessor.process(tag); + if (attributeValue != null) { + if (!tagProcessor.process(tag)) { + return exhaustiveEnum; + } + } } else if (localName.equals("union")) { - exaustiveEnum = false; + exhaustiveEnum = false; processEnumerationValues(tag, tagProcessor); } else if (!localName.equals("annotation")) { // don't go into annotation - exaustiveEnum &= processEnumerationValues(tag, tagProcessor); + exhaustiveEnum &= processEnumerationValues(tag, tagProcessor); } } - return exaustiveEnum; + return exhaustiveEnum; } /** @@ -1063,12 +1070,15 @@ public class XmlUtil { return new Pair<XmlTagChild, XmlTagChild>(first, last); } - public static boolean isSimpleXmlAttributeValue(final String unquotedValue, final XmlAttributeValue context) { + public static boolean isSimpleXmlAttributeValue(@NotNull final String unquotedValue, final XmlAttributeValue context) { for (int i = 0; i < unquotedValue.length(); ++i) { final char ch = unquotedValue.charAt(i); if (!Character.isJavaIdentifierPart(ch) && ch != ':' && ch != '-') { final XmlFile file = PsiTreeUtil.getParentOfType(context, XmlFile.class); - return file != null && !tagFromTemplateFramework(file.getRootTag()); + if (file != null) { + XmlTag tag = file.getRootTag(); + return tag != null && !tagFromTemplateFramework(tag); + } } } return true; |