| /* |
| * 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.psi.impl.source.resolve.reference.impl.providers; |
| |
| import com.intellij.openapi.extensions.Extensions; |
| import com.intellij.openapi.util.Key; |
| import com.intellij.psi.PsiComment; |
| import com.intellij.psi.PsiElement; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.XmlRecursiveElementVisitor; |
| import com.intellij.psi.search.PsiElementProcessor; |
| import com.intellij.psi.util.CachedValue; |
| import com.intellij.psi.xml.XmlAttribute; |
| import com.intellij.psi.xml.XmlAttributeValue; |
| import com.intellij.psi.xml.XmlComment; |
| import com.intellij.psi.xml.XmlTag; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.xml.util.XmlDeclareIdInCommentAction; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.ArrayList; |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| /** |
| * @author peter |
| */ |
| public class IdRefReference extends BasicAttributeValueReference { |
| private final boolean myIdAttrsOnly; |
| |
| public IdRefReference(final PsiElement element, int offset, boolean idAttrsOnly) { |
| super(element, offset); |
| myIdAttrsOnly = idAttrsOnly; |
| } |
| |
| public IdRefReference(final PsiElement element) { |
| super(element); |
| myIdAttrsOnly = false; |
| } |
| |
| @Nullable |
| protected PsiElement getIdValueElement(PsiElement element) { |
| if (element instanceof XmlTag) { |
| final XmlTag tag = (XmlTag)element; |
| XmlAttribute attribute = tag.getAttribute(IdReferenceProvider.ID_ATTR_NAME, null); |
| if (!myIdAttrsOnly) { |
| if (attribute == null) { |
| attribute = tag.getAttribute(IdReferenceProvider.NAME_ATTR_NAME, null); |
| } |
| if (attribute == null) { |
| attribute = tag.getAttribute(IdReferenceProvider.STYLE_ID_ATTR_NAME, null); |
| } |
| } |
| return attribute != null ? attribute.getValueElement() : getImplicitIdRefValueElement(tag); |
| } |
| else { |
| return element; |
| } |
| } |
| |
| @Nullable |
| protected String getIdValue(final PsiElement element) { |
| if (element instanceof XmlTag) { |
| final XmlTag tag = (XmlTag)element; |
| String s = tag.getAttributeValue(IdReferenceProvider.ID_ATTR_NAME); |
| if (!myIdAttrsOnly) { |
| if (s == null) s = tag.getAttributeValue(IdReferenceProvider.NAME_ATTR_NAME); |
| if (s == null) s = tag.getAttributeValue(IdReferenceProvider.STYLE_ID_ATTR_NAME); |
| } |
| return s != null ? s: getImplicitIdRefValue(tag); |
| } else if (element instanceof PsiComment) { |
| return getImplicitIdValue((PsiComment) element); |
| } |
| |
| return null; |
| } |
| |
| @Nullable |
| public static XmlAttribute getImplicitIdRefAttr(@NotNull XmlTag tag) { |
| for (ImplicitIdRefProvider idRefProvider : Extensions.getExtensions(ImplicitIdRefProvider.EXTENSION_POINT_NAME)) { |
| XmlAttribute value = idRefProvider.getIdRefAttribute(tag); |
| if (value != null) return value; |
| } |
| |
| return null; |
| } |
| |
| @Nullable |
| public static XmlAttributeValue getImplicitIdRefValueElement(@NotNull XmlTag tag) { |
| for (ImplicitIdRefProvider idRefProvider : Extensions.getExtensions(ImplicitIdRefProvider.EXTENSION_POINT_NAME)) { |
| XmlAttribute value = idRefProvider.getIdRefAttribute(tag); |
| if (value != null) return value.getValueElement(); |
| } |
| |
| return null; |
| } |
| |
| @Nullable |
| public static String getImplicitIdRefValue(@NotNull XmlTag tag) { |
| XmlAttributeValue attribute = getImplicitIdRefValueElement(tag); |
| |
| return attribute != null ? attribute.getValue() : null; |
| } |
| |
| protected static boolean isAcceptableTagType(final XmlTag subTag) { |
| return subTag.getAttributeValue(IdReferenceProvider.ID_ATTR_NAME) != null || |
| subTag.getAttributeValue(IdReferenceProvider.FOR_ATTR_NAME) != null || getImplicitIdRefValue(subTag) != null || |
| (subTag.getAttributeValue(IdReferenceProvider.NAME_ATTR_NAME) != null && |
| !subTag.getName().contains(".directive")); |
| } |
| |
| private static final FileBasedUserDataCache<List<PsiElement>> ourCachedIdsCache = new FileBasedUserDataCache<List<PsiElement>>() { |
| private final Key<CachedValue<List<PsiElement>>> ourCachedIdsValueKey = Key.create("my.ids.cached.value"); |
| |
| @Override |
| protected List<PsiElement> doCompute(PsiFile file) { |
| final List<PsiElement> result = new ArrayList<PsiElement>(); |
| |
| file.accept(new XmlRecursiveElementVisitor(true) { |
| @Override |
| public void visitXmlTag(XmlTag tag) { |
| if (isAcceptableTagType(tag)) result.add(tag); |
| super.visitXmlTag(tag); |
| } |
| |
| @Override |
| public void visitComment(final PsiComment comment) { |
| if (isDeclarationComment(comment)) result.add(comment); |
| |
| super.visitComment(comment); |
| } |
| |
| @Override |
| public void visitXmlComment(final XmlComment comment) { |
| if (isDeclarationComment(comment)) result.add(comment); |
| |
| super.visitComment(comment); |
| } |
| }); |
| return result; |
| } |
| |
| @Override |
| protected Key<CachedValue<List<PsiElement>>> getKey() { |
| return ourCachedIdsValueKey; |
| } |
| }; |
| |
| private static boolean isDeclarationComment(@NotNull final PsiComment comment) { |
| return comment.getText().contains("@declare id="); |
| } |
| |
| @Nullable |
| private static String getImplicitIdValue(@NotNull final PsiComment comment) { |
| return XmlDeclareIdInCommentAction.getImplicitlyDeclaredId(comment); |
| } |
| |
| private void process(PsiElementProcessor<PsiElement> processor) { |
| final PsiFile psiFile = getElement().getContainingFile(); |
| process(processor, psiFile); |
| } |
| |
| public static void process(final PsiElementProcessor<PsiElement> processor, PsiFile file) { |
| for (PsiElement e : ourCachedIdsCache.compute(file)) { |
| if (!processor.execute(e)) return; |
| } |
| } |
| |
| @Override |
| @Nullable |
| public PsiElement resolve() { |
| final PsiElement[] result = new PsiElement[1]; |
| process(new PsiElementProcessor<PsiElement>() { |
| final String canonicalText = getCanonicalText(); |
| |
| @Override |
| public boolean execute(@NotNull final PsiElement element) { |
| final String idValue = getIdValue(element); |
| if (idValue != null && idValue.equals(canonicalText)) { |
| result[0] = getIdValueElement(element); |
| return false; |
| } |
| return true; |
| } |
| }); |
| |
| return result[0]; |
| } |
| |
| @Override |
| @NotNull |
| public Object[] getVariants() { |
| final List<String> result = new LinkedList<String>(); |
| |
| process(new PsiElementProcessor<PsiElement>() { |
| @Override |
| public boolean execute(@NotNull final PsiElement element) { |
| String value = getIdValue(element); |
| if (value != null) { |
| result.add(value); |
| } |
| return true; |
| } |
| }); |
| |
| return ArrayUtil.toObjectArray(result); |
| } |
| |
| @Override |
| public boolean isSoft() { |
| return false; |
| } |
| |
| } |