blob: 21e8c6cbbc386ca3c73eaa422fda812dcd3298f3 [file] [log] [blame]
/*
* Copyright 2000-2012 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.javadoc;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.*;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.javadoc.PsiDocTag;
import com.intellij.psi.javadoc.PsiDocTagValue;
import com.intellij.psi.javadoc.PsiDocToken;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.CharTable;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author mike
*/
public class PsiDocParamRef extends CompositePsiElement implements PsiDocTagValue {
public PsiDocParamRef() {
super(JavaDocElementType.DOC_PARAMETER_REF);
}
@Override
public PsiReference getReference() {
final PsiDocComment comment = PsiTreeUtil.getParentOfType(this, PsiDocComment.class);
if (comment == null) return null;
final PsiDocCommentOwner owner = comment.getOwner();
if (!(owner instanceof PsiMethod) &&
!(owner instanceof PsiClass)) return null;
final ASTNode valueToken = findChildByType(JavaDocTokenType.DOC_TAG_VALUE_TOKEN);
if (valueToken == null) return null;
final String name = valueToken.getText();
PsiElement reference = null;
final PsiElement firstChild = getFirstChild();
if (firstChild instanceof PsiDocToken && ((PsiDocToken)firstChild).getTokenType().equals(JavaDocTokenType.DOC_TAG_VALUE_LT)) {
final PsiTypeParameter[] typeParameters = ((PsiTypeParameterListOwner)owner).getTypeParameters();
for (PsiTypeParameter typeParameter : typeParameters) {
if (typeParameter.getName().equals(name)) {
reference = typeParameter;
}
}
}
else if (owner instanceof PsiMethod) {
final PsiParameter[] parameters = ((PsiMethod)owner).getParameterList().getParameters();
for (PsiParameter parameter : parameters) {
if (parameter.getName().equals(name)) {
reference = parameter;
}
}
}
final PsiElement resultReference = reference;
return new PsiJavaReference() {
@Override
public PsiElement resolve() {
return resultReference;
}
@Override
@NotNull
public String getCanonicalText() {
return valueToken.getText();
}
@Override
public PsiElement handleElementRename(String newElementName) {
final CharTable charTableByTree = SharedImplUtil.findCharTableByTree(getNode());
LeafElement newElement = Factory.createSingleLeafElement(JavaDocTokenType.DOC_TAG_VALUE_TOKEN, newElementName, charTableByTree, getManager());
replaceChild(valueToken, newElement);
return PsiDocParamRef.this;
}
@Override
public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
if (isReferenceTo(element)) return PsiDocParamRef.this;
if(!(element instanceof PsiParameter)) {
throw new IncorrectOperationException("Unsupported operation");
}
return handleElementRename(((PsiParameter) element).getName());
}
@Override
public boolean isReferenceTo(PsiElement element) {
if (!(element instanceof PsiNamedElement)) return false;
PsiNamedElement namedElement = (PsiNamedElement)element;
if (!getCanonicalText().equals(namedElement.getName())) return false;
return getManager().areElementsEquivalent(resolve(), element);
}
@Override
@NotNull
public PsiElement[] getVariants() {
final PsiElement firstChild = getFirstChild();
Set<String> usedNames = new HashSet<String>();
for (PsiDocTag tag : comment.getTags()) {
if (tag.getName().equals("param")) {
PsiDocTagValue valueElement = tag.getValueElement();
if (valueElement != null) {
usedNames.add(valueElement.getText());
}
}
}
PsiNamedElement[] result = PsiNamedElement.EMPTY_ARRAY;
if (firstChild instanceof PsiDocToken && ((PsiDocToken)firstChild).getTokenType().equals(JavaDocTokenType.DOC_TAG_VALUE_LT)) {
result = ((PsiTypeParameterListOwner)owner).getTypeParameters();
} else if (owner instanceof PsiMethod) {
result = ((PsiMethod)owner).getParameterList().getParameters();
}
List<PsiElement> filtered = new ArrayList<PsiElement>();
for (PsiNamedElement namedElement : result) {
if (!usedNames.contains(namedElement.getName())) {
filtered.add(namedElement);
}
}
return filtered.toArray(new PsiElement[filtered.size()]);
}
@Override
public boolean isSoft(){
return false;
}
@Override
public TextRange getRangeInElement() {
final int startOffsetInParent = valueToken.getPsi().getStartOffsetInParent();
return new TextRange(startOffsetInParent, startOffsetInParent + valueToken.getTextLength());
}
@Override
public PsiElement getElement() {
return PsiDocParamRef.this;
}
@Override
public void processVariants(@NotNull PsiScopeProcessor processor) {
for (final PsiElement element : getVariants()) {
if (!processor.execute(element, ResolveState.initial())) {
return;
}
}
}
@Override
@NotNull
public JavaResolveResult advancedResolve(boolean incompleteCode) {
return resultReference == null ? JavaResolveResult.EMPTY : new CandidateInfo(resultReference, PsiSubstitutor.EMPTY);
}
@Override
@NotNull
public JavaResolveResult[] multiResolve(boolean incompleteCode) {
return resultReference == null
? JavaResolveResult.EMPTY_ARRAY
: new JavaResolveResult[]{new CandidateInfo(resultReference, PsiSubstitutor.EMPTY)};
}
};
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof JavaElementVisitor) {
((JavaElementVisitor)visitor).visitDocTagValue(this);
}
else {
visitor.visitElement(this);
}
}
}