diff --git a/xml/impl/src/com/intellij/html/impl/MicrodataAttributeDescriptorsProvider.java b/xml/impl/src/com/intellij/html/impl/MicrodataAttributeDescriptorsProvider.java index 319ae1a9d5b4..a652b4c9cc82 100644 --- a/xml/impl/src/com/intellij/html/impl/MicrodataAttributeDescriptorsProvider.java +++ b/xml/impl/src/com/intellij/html/impl/MicrodataAttributeDescriptorsProvider.java @@ -115,7 +115,7 @@ public class MicrodataAttributeDescriptorsProvider implements XmlAttributeDescri @Override public String[] getEnumeratedValues() { - final XmlTag scopeParent = findScopeTag(myContext.getParentTag()); + final XmlTag scopeParent = findScopeTag(myContext); return scopeParent != null ? findProperties(scopeParent) : super.getEnumeratedValues(); } diff --git a/xml/impl/src/com/intellij/html/impl/util/MicrodataUtil.java b/xml/impl/src/com/intellij/html/impl/util/MicrodataUtil.java index 5467388e914a..78fb7255d147 100644 --- a/xml/impl/src/com/intellij/html/impl/util/MicrodataUtil.java +++ b/xml/impl/src/com/intellij/html/impl/util/MicrodataUtil.java @@ -22,7 +22,6 @@ import com.intellij.codeInsight.daemon.impl.quickfix.ManuallySetupExtResourceAct import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; @@ -37,17 +36,14 @@ import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.intellij.util.PairFunction; -import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.StringTokenizer; +import gnu.trove.THashMap; import gnu.trove.THashSet; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.util.*; /** * @author: Fedor.Korotkov @@ -68,39 +64,39 @@ public class MicrodataUtil { } @Nullable - public static XmlTag findScopeTag(@Nullable XmlTag tag) { + public static XmlTag findScopeTag(@Nullable XmlTag context) { + Map id2tag = findScopesWithItemRef(context != null ? context.getContainingFile() : null); + XmlTag tag = context; while (tag != null) { - if (tag.getAttribute(ITEM_SCOPE) != null) return tag; - XmlTag scopeTag = findInRefsById(tag); - if (scopeTag != null) return scopeTag; + if (tag != context && tag.getAttribute(ITEM_SCOPE) != null) return tag; + final String id = getStripedAttributeValue(tag, "id"); + if (id != null && id2tag.containsKey(id)) return id2tag.get(id); tag = tag.getParentTag(); } return null; } - @Nullable - private static XmlTag findInRefsById(XmlTag tag) { - XmlAttribute idAttr = tag.getAttribute("id"); - final XmlAttributeValue idValue = idAttr != null ? idAttr.getValueElement() : null; - if (idValue == null) { - return null; - } - final String idToFind = StringUtil.stripQuotesAroundValue(idValue.getText()); - XmlTag parentTag = tag.getParentTag(); - while (parentTag != null) { - XmlTag scopeTag = ContainerUtil.find(parentTag.getSubTags(), new Condition() { - @Override - public boolean value(XmlTag tag) { - String refValue = tag.getAttributeValue(ITEM_REF); - return refValue != null && refValue.contains(idToFind); + private static Map findScopesWithItemRef(@Nullable PsiFile file) { + if (!(file instanceof XmlFile)) return Collections.emptyMap(); + final Map result = new THashMap(); + file.accept(new XmlRecursiveElementVisitor() { + @Override + public void visitXmlTag(final XmlTag tag) { + super.visitXmlTag(tag); + XmlAttribute refAttr = tag.getAttribute(ITEM_REF); + if (refAttr != null && tag.getAttribute(ITEM_SCOPE) != null) { + getReferencesForAttributeValue(refAttr.getValueElement(), new PairFunction() { + @Nullable + @Override + public PsiReference fun(String t, Integer v) { + result.put(t, tag); + return null; + } + }); } - }); - if (scopeTag != null) { - return scopeTag; } - parentTag = parentTag.getParentTag(); - } - return null; + }); + return result; } public static List extractProperties(PsiFile file, String type) { @@ -151,8 +147,11 @@ public class MicrodataUtil { }); } - public static PsiReference[] getReferencesForAttributeValue(XmlAttributeValue element, + public static PsiReference[] getReferencesForAttributeValue(@Nullable XmlAttributeValue element, PairFunction refFun) { + if (element == null) { + return PsiReference.EMPTY_ARRAY; + } String text = element.getText(); String urls = StringUtil.stripQuotesAroundValue(text); StringTokenizer tokenizer = new StringTokenizer(urls); diff --git a/xml/tests/src/com/intellij/html/MicrodataCompletionTest.java b/xml/tests/src/com/intellij/html/MicrodataCompletionTest.java index d621b286d092..9a0c19ed5542 100644 --- a/xml/tests/src/com/intellij/html/MicrodataCompletionTest.java +++ b/xml/tests/src/com/intellij/html/MicrodataCompletionTest.java @@ -214,4 +214,29 @@ public class MicrodataCompletionTest extends CodeInsightFixtureTestCase { "name", "nickname", "photo", "title", "role", "url", "affiliation", "friend", "acquaintance", "address" ); } + + public void testPropValueNestedScopesDifferentTrees() throws Throwable { + final VirtualFile personFile = myFixture.copyFileToProject("Person.html"); + final VirtualFile addressFile = myFixture.copyFileToProject("Address.html"); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + ExternalResourceManager.getInstance().addResource("http://data-vocabulary.org/Person", personFile.getPath()); + ExternalResourceManager.getInstance().addResource("http://data-vocabulary.org/Address", addressFile.getPath()); + } + }); + doTestInHtml("
\n" + + " name is ann\n" + + " role is smth\n" + + " \n" + + " spb\n" + + " \n" + + "
\n" + + "
\n" + + " \">russia\n" + + "
", + "street-address", "locality", "region", "postal-code", "country-name" + ); + } }