diff options
author | Tor Norbye <tnorbye@google.com> | 2013-08-21 11:17:55 -0700 |
---|---|---|
committer | Tor Norbye <tnorbye@google.com> | 2013-08-21 11:17:55 -0700 |
commit | d34b4c8d74407f5b2be1a344e41f2ecdeb25f4fe (patch) | |
tree | 1263896cdf5d3e25697287c40cf3a1d35de8e95d /xml | |
parent | 4db7dfd37df60de478b5d99be0554bc0e06dfdba (diff) | |
download | idea-d34b4c8d74407f5b2be1a344e41f2ecdeb25f4fe.tar.gz |
Snapshot 32d31e4915ef17b3ec6ec4e87923f017e8e41bce from master branch of git://git.jetbrains.org/idea/community.git
Change-Id: Idb2188c1f4e63d654aa3e3b832b9121d0be2dbe9
Diffstat (limited to 'xml')
22 files changed, 348 insertions, 102 deletions
diff --git a/xml/dom-impl/src/com/intellij/util/xml/EnumConverter.java b/xml/dom-impl/src/com/intellij/util/xml/EnumConverter.java index 8dc15edb48e2..0d86645a37fc 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/EnumConverter.java +++ b/xml/dom-impl/src/com/intellij/util/xml/EnumConverter.java @@ -3,6 +3,7 @@ package com.intellij.util.xml; import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.psi.xml.XmlElement; import com.intellij.psi.xml.XmlTag; +import com.intellij.util.ReflectionCache; import com.intellij.util.containers.ConcurrentFactoryMap; import com.intellij.xml.util.XmlUtil; import org.jetbrains.annotations.NotNull; @@ -60,4 +61,8 @@ public class EnumConverter<T extends Enum> extends ResolvingConverter<T>{ } return Arrays.asList(myType.getEnumConstants()); } + + public boolean isExhaustive() { + return !ReflectionCache.isAssignable(NonExhaustiveEnum.class, myType); + } } diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/GenericValueReferenceProvider.java b/xml/dom-impl/src/com/intellij/util/xml/impl/GenericValueReferenceProvider.java index b457c9d6b10a..c36c2bfa2454 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/GenericValueReferenceProvider.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/GenericValueReferenceProvider.java @@ -24,6 +24,7 @@ import com.intellij.psi.xml.*; import com.intellij.util.ProcessingContext; import com.intellij.util.ReflectionCache; import com.intellij.util.xml.*; +import com.intellij.xml.util.XmlAttributeValueReferenceProvider; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -86,6 +87,13 @@ public class GenericValueReferenceProvider extends PsiReferenceProvider { } } } + if (references.length > 0) { + if (converter instanceof EnumConverter && !((EnumConverter)converter).isExhaustive()) { + // will be handled by core XML + return PsiReference.EMPTY_ARRAY; + } + context.put(XmlAttributeValueReferenceProvider.SUPPRESS, Boolean.TRUE); + } return references; } diff --git a/xml/dom-openapi/src/com/intellij/util/xml/NonExhaustiveEnum.java b/xml/dom-openapi/src/com/intellij/util/xml/NonExhaustiveEnum.java new file mode 100644 index 000000000000..95b522ecb32d --- /dev/null +++ b/xml/dom-openapi/src/com/intellij/util/xml/NonExhaustiveEnum.java @@ -0,0 +1,23 @@ +/* + * 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.util.xml; + +/** + * @author Dmitry Avdeev + * Date: 21.08.13 + */ +public interface NonExhaustiveEnum { +} diff --git a/xml/impl/src/com/intellij/codeInsight/completion/XmlCompletionData.java b/xml/impl/src/com/intellij/codeInsight/completion/XmlCompletionData.java index 00d6c1e18650..bf87f35c9411 100644 --- a/xml/impl/src/com/intellij/codeInsight/completion/XmlCompletionData.java +++ b/xml/impl/src/com/intellij/codeInsight/completion/XmlCompletionData.java @@ -71,11 +71,14 @@ public class XmlCompletionData extends CompletionData { } { - final CompletionVariant variant = new CompletionVariant(createAttributeValueCompletionFilter()); - variant.includeScopeClass(XmlAttributeValue.class); - variant.addCompletion(getAttributeValueGetter(), TailType.NONE); - variant.addCompletionFilter(TrueFilter.INSTANCE, TailType.NONE); - registerVariant(variant); + XmlAttributeValueGetter getter = getAttributeValueGetter(); + if (getter != null) { + final CompletionVariant variant = new CompletionVariant(createAttributeValueCompletionFilter()); + variant.includeScopeClass(XmlAttributeValue.class); + variant.addCompletion(getter, TailType.NONE); + variant.addCompletionFilter(TrueFilter.INSTANCE, TailType.NONE); + registerVariant(variant); + } } final ElementFilter entityCompletionFilter = createXmlEntityCompletionFilter(); diff --git a/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/DependentNSReferenceQuickFixProvider.java b/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/DependentNSReferenceQuickFixProvider.java index 0fb6ef242977..142e7df51f21 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/DependentNSReferenceQuickFixProvider.java +++ b/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/DependentNSReferenceQuickFixProvider.java @@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull; */ public class DependentNSReferenceQuickFixProvider extends UnresolvedReferenceQuickFixProvider<DependentNSReference> { @Override - public void registerFixes(DependentNSReference ref, QuickFixActionRegistrar registrar) { + public void registerFixes(@NotNull DependentNSReference ref, @NotNull QuickFixActionRegistrar registrar) { registrar.register(new FetchExtResourceAction(ref.isForceFetchResultValid())); registrar.register(new ManuallySetupExtResourceAction()); registrar.register(new IgnoreExtResourceAction()); diff --git a/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/SchemaReferenceQuickFixProvider.java b/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/SchemaReferenceQuickFixProvider.java index 6b8bebde5652..eb3d558b6e18 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/SchemaReferenceQuickFixProvider.java +++ b/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/SchemaReferenceQuickFixProvider.java @@ -27,7 +27,7 @@ import org.jetbrains.annotations.PropertyKey; */ public class SchemaReferenceQuickFixProvider extends UnresolvedReferenceQuickFixProvider<SchemaReferencesProvider.TypeOrElementOrAttributeReference> { @Override - public void registerFixes(SchemaReferencesProvider.TypeOrElementOrAttributeReference ref, QuickFixActionRegistrar registrar) { + public void registerFixes(@NotNull SchemaReferencesProvider.TypeOrElementOrAttributeReference ref, @NotNull QuickFixActionRegistrar registrar) { if (ref.getType() == SchemaReferencesProvider.TypeOrElementOrAttributeReference.ReferenceType.TypeReference) { registrar.register( new CreateXmlElementIntentionAction("xml.schema.create.complex.type.intention.name", SchemaReferencesProvider.COMPLEX_TYPE_TAG_NAME, ref) diff --git a/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/URLReferenceQuickFixProvider.java b/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/URLReferenceQuickFixProvider.java index 1c63a9737aef..13bae7ba826f 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/URLReferenceQuickFixProvider.java +++ b/xml/impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/URLReferenceQuickFixProvider.java @@ -27,7 +27,7 @@ import org.jetbrains.annotations.NotNull; */ public class URLReferenceQuickFixProvider extends UnresolvedReferenceQuickFixProvider<URLReference> { @Override - public void registerFixes(URLReference ref, QuickFixActionRegistrar registrar) { + public void registerFixes(@NotNull URLReference ref, @NotNull QuickFixActionRegistrar registrar) { registrar.register(new FetchExtResourceAction()); registrar.register(new ManuallySetupExtResourceAction()); registrar.register(new IgnoreExtResourceAction()); diff --git a/xml/impl/src/com/intellij/xml/util/XmlAttributeValueReferenceProvider.java b/xml/impl/src/com/intellij/xml/util/XmlAttributeValueReferenceProvider.java new file mode 100644 index 000000000000..33d72010189d --- /dev/null +++ b/xml/impl/src/com/intellij/xml/util/XmlAttributeValueReferenceProvider.java @@ -0,0 +1,61 @@ +/* + * 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.impl.analysis.XmlHighlightVisitor; +import com.intellij.openapi.util.Key; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.psi.PsiReferenceProvider; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlAttributeValue; +import com.intellij.util.ProcessingContext; +import com.intellij.xml.XmlAttributeDescriptor; +import com.intellij.xml.impl.BasicXmlAttributeDescriptor; +import com.intellij.xml.impl.schema.XmlSchemaTagsProcessor; +import org.jetbrains.annotations.NotNull; + +/** + * @author Dmitry Avdeev + * Date: 15.08.13 + */ +public class XmlAttributeValueReferenceProvider extends PsiReferenceProvider { + + public final static Key<Boolean> SUPPRESS = Key.create("suppress attribute value references"); + + @NotNull + @Override + public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) { + + if (XmlSchemaTagsProcessor.PROCESSING_FLAG.get() != null || context.get(SUPPRESS) != null) { + return PsiReference.EMPTY_ARRAY; + } + XmlAttributeValue value = (XmlAttributeValue)element; + String unquotedValue = value.getValue(); + if (unquotedValue == null || XmlHighlightVisitor.skipValidation(value) || !XmlUtil.isSimpleXmlAttributeValue(unquotedValue, value)) { + return PsiReference.EMPTY_ARRAY; + } + PsiElement parent = value.getParent(); + if (parent instanceof XmlAttribute) { + final XmlAttributeDescriptor descriptor = ((XmlAttribute)parent).getDescriptor(); + if (descriptor instanceof BasicXmlAttributeDescriptor && + (descriptor.isFixed() || descriptor.isEnumerated() || unquotedValue.equals(descriptor.getDefaultValue()))) { // todo case insensitive + return ((BasicXmlAttributeDescriptor)descriptor).getValueReferences(value); + } + } + return PsiReference.EMPTY_ARRAY; + } +} diff --git a/xml/impl/src/com/intellij/xml/util/XmlReferenceContributor.java b/xml/impl/src/com/intellij/xml/util/XmlReferenceContributor.java index 21d6abcca803..6fcd606e18fa 100644 --- a/xml/impl/src/com/intellij/xml/util/XmlReferenceContributor.java +++ b/xml/impl/src/com/intellij/xml/util/XmlReferenceContributor.java @@ -89,5 +89,6 @@ public class XmlReferenceContributor extends PsiReferenceContributor { new XmlEncodingReferenceProvider()); registrar.registerReferenceProvider(xmlAttributeValue(), new XmlPrefixReferenceProvider()); + registrar.registerReferenceProvider(xmlAttributeValue(), new XmlAttributeValueReferenceProvider(), PsiReferenceRegistrar.LOWER_PRIORITY); } } diff --git a/xml/relaxng/src/org/intellij/plugins/relaxNG/model/descriptors/RngXmlAttributeDescriptor.java b/xml/relaxng/src/org/intellij/plugins/relaxNG/model/descriptors/RngXmlAttributeDescriptor.java index 36760ce1016a..db3d85e44600 100644 --- a/xml/relaxng/src/org/intellij/plugins/relaxNG/model/descriptors/RngXmlAttributeDescriptor.java +++ b/xml/relaxng/src/org/intellij/plugins/relaxNG/model/descriptors/RngXmlAttributeDescriptor.java @@ -17,14 +17,20 @@ package org.intellij.plugins.relaxNG.model.descriptors; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.DelimitedListProcessor; +import com.intellij.psi.ElementManipulators; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiReference; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlElement; import com.intellij.psi.xml.XmlTag; import com.intellij.util.ArrayUtil; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; +import com.intellij.xml.util.XmlAttributeValueReference; import com.intellij.xml.impl.BasicXmlAttributeDescriptor; import gnu.trove.THashSet; import gnu.trove.TObjectHashingStrategy; @@ -201,4 +207,24 @@ public class RngXmlAttributeDescriptor extends BasicXmlAttributeDescriptor { private static String normalizeSpace(String value) { return value.replaceAll("\\s+", " ").trim(); } + + @Override + public PsiReference[] getValueReferences(final XmlAttributeValue value) { + String text = value.getValue(); + if (text == null) return PsiReference.EMPTY_ARRAY; + final int offset = ElementManipulators.getValueTextRange(value).getStartOffset(); + final List<PsiReference> list = new ArrayList<PsiReference>(); + new DelimitedListProcessor("") { + @Override + protected boolean isDelimiter(char ch) { + return Character.isWhitespace(ch); + } + + @Override + protected void processToken(int start, int end, boolean delimitersOnly) { + list.add(new XmlAttributeValueReference(value, TextRange.create(offset + start, offset + end), RngXmlAttributeDescriptor.this)); + } + }.processText(text); + return list.toArray(new PsiReference[list.size()]); + } }
\ No newline at end of file diff --git a/xml/relaxng/testData/highlighting/attribute-choice-3.xml b/xml/relaxng/testData/highlighting/attribute-choice-3.xml index c095d96e136f..312c85d7e8cd 100644 --- a/xml/relaxng/testData/highlighting/attribute-choice-3.xml +++ b/xml/relaxng/testData/highlighting/attribute-choice-3.xml @@ -1,2 +1,2 @@ -<attribute-choice xmlns="urn:test:simple.rng" type=<error>"c"</error>> +<attribute-choice xmlns="urn:test:simple.rng" type="<error descr="Wrong attribute value">c</error>"> </attribute-choice>
\ No newline at end of file diff --git a/xml/relaxng/testData/highlighting/fixed-attribute_2.xml b/xml/relaxng/testData/highlighting/fixed-attribute_2.xml index a7a4b233198b..cf6c47a396b7 100644 --- a/xml/relaxng/testData/highlighting/fixed-attribute_2.xml +++ b/xml/relaxng/testData/highlighting/fixed-attribute_2.xml @@ -1,2 +1,2 @@ -<fixed-attribute xmlns="urn:test:simple.rng" x=<error>"2.0"</error>> +<fixed-attribute xmlns="urn:test:simple.rng" x="<error descr="Attribute should have fixed value 1.0">2.0</error>"> </fixed-attribute>
\ No newline at end of file diff --git a/xml/relaxng/testData/highlighting/value-choice-2.xml b/xml/relaxng/testData/highlighting/value-choice-2.xml index 8b026fed37e5..fba9ef5c72ae 100644 --- a/xml/relaxng/testData/highlighting/value-choice-2.xml +++ b/xml/relaxng/testData/highlighting/value-choice-2.xml @@ -1,2 +1,2 @@ -<value-choice xmlns="urn:test:simple.rng" x=<error>"c"</error>> +<value-choice xmlns="urn:test:simple.rng" x="<error descr="Wrong attribute value">c</error>"> </value-choice>
\ No newline at end of file diff --git a/xml/tests/testData/xml/HighlightWhenNoNsSchemaLocation.xml b/xml/tests/testData/xml/HighlightWhenNoNsSchemaLocation.xml index c9f2474359d5..0ffab3101ea5 100644 --- a/xml/tests/testData/xml/HighlightWhenNoNsSchemaLocation.xml +++ b/xml/tests/testData/xml/HighlightWhenNoNsSchemaLocation.xml @@ -1,4 +1,4 @@ <build number="1" xsi:noNamespaceSchemaLocation="HighlightWhenNoNsSchemaLocation.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <statusInfo status="SUCCESS"/> -<statusInfo status=<error>"SUCCESS2"</error>/> +<statusInfo status="<error descr="Wrong attribute value">SUCCESS2</error>"/> </build>
\ No newline at end of file diff --git a/xml/xml-psi-impl/resources/messages/XmlErrorMessages.properties b/xml/xml-psi-impl/resources/messages/XmlErrorMessages.properties index 2f90357d2e09..8ab43e2f5311 100644 --- a/xml/xml-psi-impl/resources/messages/XmlErrorMessages.properties +++ b/xml/xml-psi-impl/resources/messages/XmlErrorMessages.properties @@ -20,8 +20,7 @@ duplicate.id.reference=Duplicate id reference invalid.id.reference=Invalid id reference uri.is.not.registered=URI is not registered (Settings | Project Settings | Schemas and DTDs) registered.resource.is.not.recognized=Resource registered by this uri is not recognized (Settings | Project Settings | Schemas and DTDs) -# 0 - attr name, 1 - expected default value -attribute.should.have.fixed.value=Attribute {0} should have fixed value {1} +attribute.should.have.fixed.value=Attribute should have fixed value {0} #quickfixes insert.required.attribute.quickfix.text=Insert Required Attribute {0} 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; |