blob: 2228b2628fa03cf71f19de87644ec81457e1e6b8 [file] [log] [blame]
/*
* 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.codeInsight.javadoc;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class JavaDocUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.javadoc.JavaDocUtil");
@NonNls private static final Pattern ourTypePattern = Pattern.compile("[ ]+[^ ^\\[^\\]]");
private JavaDocUtil() {
}
/**
* Extracts a reference to a source element from the beginning of the text.
*
* @return length of the extracted reference
*/
public static int extractReference(String text) {
int lparenthIndex = text.indexOf('(');
int spaceIndex = text.indexOf(' ');
if (spaceIndex < 0) {
spaceIndex = text.length();
}
if (lparenthIndex < 0) {
return spaceIndex;
}
else {
if (spaceIndex < lparenthIndex) {
return spaceIndex;
}
int rparenthIndex = text.indexOf(')', lparenthIndex);
if (rparenthIndex < 0) {
rparenthIndex = text.length() - 1;
}
return rparenthIndex + 1;
}
}
@Nullable
public static PsiElement findReferenceTarget(PsiManager manager, String refText, PsiElement context) {
LOG.assertTrue(context == null || context.isValid());
int poundIndex = refText.indexOf('#');
final JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
if (poundIndex < 0) {
PsiClass aClass = facade.getResolveHelper().resolveReferencedClass(refText, context);
if (aClass == null) aClass = facade.findClass(refText, context.getResolveScope());
if (aClass != null) return aClass.getNavigationElement();
PsiPackage aPackage = facade.findPackage(refText);
if (aPackage!=null) return aPackage;
return null;
}
else {
String classRef = refText.substring(0, poundIndex).trim();
if (!classRef.isEmpty()) {
PsiClass aClass = facade.getResolveHelper().resolveReferencedClass(classRef, context);
if (aClass == null) aClass = facade.findClass(classRef, context.getResolveScope());
if (aClass == null) return null;
return findReferencedMember(aClass, refText.substring(poundIndex + 1), context);
}
else {
String memberRefText = refText.substring(1);
PsiElement scope = context;
while (true) {
if (scope instanceof PsiFile) break;
if (scope instanceof PsiClass) {
PsiElement member = findReferencedMember((PsiClass)scope, memberRefText, context);
if (member != null) return member;
}
scope = scope.getParent();
}
return null;
}
}
}
@Nullable
private static PsiElement findReferencedMember(PsiClass aClass, String memberRefText, PsiElement context) {
int parenthIndex = memberRefText.indexOf('(');
if (parenthIndex < 0) {
String name = memberRefText;
PsiField field = aClass.findFieldByName(name, true);
if (field != null) return field.getNavigationElement();
PsiClass inner = aClass.findInnerClassByName(name, true);
if (inner != null) return inner.getNavigationElement();
PsiMethod[] methods = aClass.getAllMethods();
for (PsiMethod method : methods) {
if (method.getName().equals(name)) return method.getNavigationElement();
}
return null;
}
else {
String name = memberRefText.substring(0, parenthIndex).trim();
int rparenIndex = memberRefText.lastIndexOf(')');
if (rparenIndex == -1) return null;
String parmsText = memberRefText.substring(parenthIndex + 1, rparenIndex).trim();
StringTokenizer tokenizer = new StringTokenizer(parmsText.replaceAll("[*]", ""), ",");
PsiType[] types = PsiType.createArray(tokenizer.countTokens());
int i = 0;
PsiElementFactory factory = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory();
while (tokenizer.hasMoreTokens()) {
String parmText = tokenizer.nextToken().trim();
try {
Matcher typeMatcher = ourTypePattern.matcher(parmText);
String typeText = parmText;
if (typeMatcher.find()) {
typeText = parmText.substring(0, typeMatcher.start());
}
PsiType type = factory.createTypeFromText(typeText, context);
types[i++] = type;
}
catch (IncorrectOperationException e) {
LOG.info(e);
}
}
PsiMethod[] methods = aClass.findMethodsByName(name, true);
MethodsLoop:
for (PsiMethod method : methods) {
PsiParameter[] parms = method.getParameterList().getParameters();
if (parms.length != types.length) continue;
for (int k = 0; k < parms.length; k++) {
PsiParameter parm = parms[k];
final PsiType parmType = parm.getType();
if (
types[k] != null &&
!TypeConversionUtil.erasure(parmType).getCanonicalText().equals(types[k].getCanonicalText()) &&
!parmType.getCanonicalText().equals(types[k].getCanonicalText()) &&
!TypeConversionUtil.isAssignable(parmType, types[k])
) {
continue MethodsLoop;
}
}
int hashIndex = memberRefText.indexOf('#',rparenIndex);
if (hashIndex != -1) {
int parameterNumber = Integer.parseInt(memberRefText.substring(hashIndex + 1));
if (parameterNumber < parms.length) return method.getParameterList().getParameters()[parameterNumber].getNavigationElement();
}
return method.getNavigationElement();
}
return null;
}
}
@Nullable
public static String getReferenceText(Project project, PsiElement element) {
if (element instanceof PsiPackage) {
return ((PsiPackage)element).getQualifiedName();
}
else if (element instanceof PsiClass) {
final String refText = ((PsiClass)element).getQualifiedName();
if (refText != null) return refText;
return ((PsiClass)element).getName();
}
else if (element instanceof PsiField) {
PsiField field = (PsiField)element;
String name = field.getName();
PsiClass aClass = field.getContainingClass();
if (aClass != null) {
return getReferenceText(project, aClass) + "#" + name;
}
else {
return "#" + name;
}
}
else if (element instanceof PsiMethod) {
PsiMethod method = (PsiMethod)element;
String name = method.getName();
StringBuffer buffer = new StringBuffer();
PsiClass aClass = method.getContainingClass();
if (aClass != null) {
buffer.append(getReferenceText(project, aClass));
}
buffer.append("#");
buffer.append(name);
buffer.append("(");
PsiParameter[] parms = method.getParameterList().getParameters();
boolean spaceBeforeComma = JavaDocCodeStyle.getInstance(project).spaceBeforeComma();
boolean spaceAfterComma = JavaDocCodeStyle.getInstance(project).spaceAfterComma();
for (int i = 0; i < parms.length; i++) {
PsiParameter parm = parms[i];
String typeText = TypeConversionUtil.erasure(parm.getType()).getCanonicalText();
buffer.append(typeText);
if (i < parms.length - 1) {
if (spaceBeforeComma) {
buffer.append(" ");
}
buffer.append(",");
if (spaceAfterComma) {
buffer.append(" ");
}
}
}
buffer.append(")");
return buffer.toString();
}
else if (element instanceof PsiParameter) {
final PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
if (method != null) {
return getReferenceText(project, method) +
"#"+
((PsiParameterList)element.getParent()).getParameterIndex((PsiParameter)element);
}
}
return null;
}
public static String getShortestClassName(PsiClass aClass, PsiElement context) {
@NonNls String shortName = aClass.getName();
if(shortName == null){
shortName = "null";
}
PsiClass containingClass = aClass.getContainingClass();
while (containingClass != null && containingClass.isPhysical()) {
shortName = containingClass.getName() + "." + shortName;
containingClass = containingClass.getContainingClass();
}
String qName = aClass.getQualifiedName();
if (qName == null) return shortName;
final PsiManager manager = aClass.getManager();
return manager.areElementsEquivalent(aClass, JavaPsiFacade.getInstance(manager.getProject()).getResolveHelper().resolveReferencedClass(shortName, context))
? shortName
: qName;
}
public static String getLabelText(Project project, PsiManager manager, String refText, PsiElement context) {
PsiElement refElement = findReferenceTarget(manager, refText, context);
if (refElement == null) {
return refText.replaceFirst("^#", "").replaceAll("#", ".");
}
int poundIndex = refText.indexOf('#');
if (poundIndex < 0) {
if (refElement instanceof PsiClass) {
return getShortestClassName((PsiClass)refElement, context);
}
else {
return refText;
}
}
else {
PsiClass aClass = null;
if (refElement instanceof PsiField) {
aClass = ((PsiField)refElement).getContainingClass();
}
else if (refElement instanceof PsiMethod) {
aClass = ((PsiMethod)refElement).getContainingClass();
}
else if (refElement instanceof PsiClass){
return refText.replaceAll("#", ".");
}
if (aClass == null) return refText;
String classRef = refText.substring(0, poundIndex).trim();
String memberText = refText.substring(poundIndex + 1);
String memberLabel = getMemberLabelText(project, manager, memberText, context);
if (!classRef.isEmpty()) {
PsiElement refClass = findReferenceTarget(manager, classRef, context);
if (refClass instanceof PsiClass) {
PsiElement scope = context;
while (true) {
if (scope == null || scope instanceof PsiFile) break;
if (scope.equals(refClass)) {
return memberLabel;
}
scope = scope.getParent();
}
}
return getLabelText(project, manager, classRef, context) + "." + memberLabel;
}
else {
return memberLabel;
}
}
}
private static String getMemberLabelText(Project project, PsiManager manager, String memberText, PsiElement context) {
int parenthIndex = memberText.indexOf('(');
if (parenthIndex < 0) return memberText;
if (!StringUtil.endsWithChar(memberText, ')')) return memberText;
String parms = memberText.substring(parenthIndex + 1, memberText.length() - 1);
StringBuffer buffer = new StringBuffer();
boolean spaceBeforeComma = JavaDocCodeStyle.getInstance(project).spaceBeforeComma();
boolean spaceAfterComma = JavaDocCodeStyle.getInstance(project).spaceAfterComma();
StringTokenizer tokenizer = new StringTokenizer(parms, ",");
while (tokenizer.hasMoreTokens()) {
String param = tokenizer.nextToken().trim();
int index1 = param.indexOf('[');
if (index1 < 0) index1 = param.length();
int index2 = param.indexOf(' ');
if (index2 < 0) index2 = param.length();
int index = Math.min(index1, index2);
String className = param.substring(0, index).trim();
String shortClassName = getLabelText(project, manager, className, context);
buffer.append(shortClassName);
buffer.append(param.substring(className.length()));
if (tokenizer.hasMoreElements()) {
if (spaceBeforeComma) {
buffer.append(" ");
}
buffer.append(",");
if (spaceAfterComma) {
buffer.append(" ");
}
}
}
return memberText.substring(0, parenthIndex + 1) + buffer.toString() + ")";
}
public static PsiClassType[] getImplementsList(PsiClass aClass) {
if (aClass instanceof PsiAnonymousClass) {
return new PsiClassType[]{((PsiAnonymousClass)aClass).getBaseClassType()};
}
PsiReferenceList list = aClass.getImplementsList();
return list == null ? PsiClassType.EMPTY_ARRAY : list.getReferencedTypes();
}
public static PsiClassType[] getExtendsList(PsiClass aClass) {
if (aClass instanceof PsiAnonymousClass) {
return new PsiClassType[]{((PsiAnonymousClass)aClass).getBaseClassType()};
}
PsiReferenceList list = aClass.getExtendsList();
return list == null ? PsiClassType.EMPTY_ARRAY : list.getReferencedTypes();
}
public static boolean isInsidePackageInfo(@Nullable PsiDocComment containingComment) {
return containingComment != null && containingComment.getOwner() == null && containingComment.getParent() instanceof PsiJavaFile;
}
}