diff options
Diffstat (limited to 'xml/xml-psi-impl/src/com/intellij/html/impl/util/MicrodataUtil.java')
-rw-r--r-- | xml/xml-psi-impl/src/com/intellij/html/impl/util/MicrodataUtil.java | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/xml/xml-psi-impl/src/com/intellij/html/impl/util/MicrodataUtil.java b/xml/xml-psi-impl/src/com/intellij/html/impl/util/MicrodataUtil.java new file mode 100644 index 000000000000..bfdcca25f294 --- /dev/null +++ b/xml/xml-psi-impl/src/com/intellij/html/impl/util/MicrodataUtil.java @@ -0,0 +1,207 @@ +/* + * Copyright 2000-2014 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.html.impl.util; + +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiReference; +import com.intellij.psi.XmlRecursiveElementVisitor; +import com.intellij.psi.impl.source.resolve.reference.impl.providers.DependentNSReference; +import com.intellij.psi.impl.source.resolve.reference.impl.providers.URLReference; +import com.intellij.psi.xml.XmlAttribute; +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.text.StringTokenizer; +import com.intellij.xml.util.HtmlUtil; +import gnu.trove.THashMap; +import gnu.trove.THashSet; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * @author: Fedor.Korotkov + */ +public class MicrodataUtil { + public static final Key<List<String>> ITEM_PROP_KEYS = Key.create("microdata.prop"); + public static final String ITEM_REF = "itemref"; + public static final String ITEM_SCOPE = "itemscope"; + public static final String ITEM_TYPE = "itemtype"; + public static final String ITEM_PROP = "itemprop"; + public static final String ITEM_ID = "itemid"; + + public static boolean hasScopeTag(@Nullable XmlTag tag) { + return findScopeTag(tag) != null; + } + + @Nullable + public static XmlTag findScopeTag(@Nullable XmlTag context) { + Map<String, XmlTag> id2tag = findScopesWithItemRef(context != null ? context.getContainingFile() : null); + XmlTag tag = context; + while (tag != null) { + 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; + } + + private static Map<String, XmlTag> findScopesWithItemRef(@Nullable PsiFile file) { + if (!(file instanceof XmlFile)) return Collections.emptyMap(); + final Map<String, XmlTag> result = new THashMap<String, XmlTag>(); + 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<String, Integer, PsiReference>() { + @Nullable + @Override + public PsiReference fun(String t, Integer v) { + result.put(t, tag); + return null; + } + }); + } + } + }); + return result; + } + + public static List<String> extractProperties(PsiFile file, String type) { + final VirtualFile virtualFile = file.getVirtualFile(); + List<String> result = virtualFile != null ? virtualFile.getUserData(ITEM_PROP_KEYS) : null; + if (virtualFile != null && result == null) { + result = collectNames(file, type); + virtualFile.putUserData(ITEM_PROP_KEYS, result); + } + return result; + } + + private static List<String> collectNames(PsiFile file, String type) { + if (file instanceof XmlFile) { + final CollectNamesVisitor collectNamesVisitor = getVisitorByType(type); + file.accept(collectNamesVisitor); + return collectNamesVisitor.getValues(); + } + return Collections.emptyList(); + } + + private static CollectNamesVisitor getVisitorByType(String type) { + if (type.contains("schema.org")) { + return new CollectNamesFromSchemaOrgVisitor(); + } + return new CollectNamesByMicrodataVisitor(type); + } + + public static PsiReference[] getUrlReferencesForAttributeValue(final XmlAttributeValue element) { + return getReferencesForAttributeValue(element, new PairFunction<String, Integer, PsiReference>() { + @Nullable + @Override + public PsiReference fun(String token, Integer offset) { + if (HtmlUtil.hasHtmlPrefix(token)) { + final TextRange range = TextRange.from(offset, token.length()); + final URLReference urlReference = new URLReference(element, range, true); + return new DependentNSReference(element, range, urlReference, true); + } + return null; + } + }); + } + + public static PsiReference[] getReferencesForAttributeValue(@Nullable XmlAttributeValue element, + PairFunction<String, Integer, PsiReference> refFun) { + if (element == null) { + return PsiReference.EMPTY_ARRAY; + } + String text = element.getText(); + String urls = StringUtil.stripQuotesAroundValue(text); + StringTokenizer tokenizer = new StringTokenizer(urls); + List<PsiReference> result = new ArrayList<PsiReference>(); + while (tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken(); + int index = text.indexOf(token); + PsiReference ref = refFun.fun(token, index); + if (ref != null) { + result.add(ref); + } + } + return result.toArray(new PsiReference[result.size()]); + } + + @Nullable + public static String getStripedAttributeValue(@Nullable XmlTag tag, @Nls String attributeName) { + String value = tag != null ? tag.getAttributeValue(attributeName) : null; + return value != null ? StringUtil.stripQuotesAroundValue(value) : null; + } + + private static class CollectNamesVisitor extends XmlRecursiveElementVisitor { + protected final Set<String> myValues = new THashSet<String>(); + + public List<String> getValues() { + return new ArrayList<String>(myValues); + } + } + + public static class CollectNamesByMicrodataVisitor extends CollectNamesVisitor { + protected final String myType; + private boolean myCollecting = false; + + public CollectNamesByMicrodataVisitor(String type) { + myType = type; + } + + @Override + public void visitXmlTag(XmlTag tag) { + String value = getStripedAttributeValue(tag, ITEM_ID); + final boolean isTypeTag = myType.equalsIgnoreCase(value); + if (isTypeTag) { + myCollecting = true; + } + + if (myCollecting && "name".equalsIgnoreCase(getStripedAttributeValue(tag, ITEM_PROP))) { + myValues.add(tag.getValue().getTrimmedText()); + } + + super.visitXmlTag(tag); + + if (isTypeTag) { + myCollecting = false; + } + } + } + + public static class CollectNamesFromSchemaOrgVisitor extends CollectNamesVisitor { + @Override + public void visitXmlTag(XmlTag tag) { + super.visitXmlTag(tag); + if ("prop-nam".equalsIgnoreCase(getStripedAttributeValue(tag, "class"))) { + final String code = tag.getSubTagText("code"); + if (code != null) { + myValues.add(code); + } + } + } + } +} |