/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.plugin.ij.doc;
import com.intellij.codeInsight.javadoc.JavaDocExternalFilter;
import com.intellij.codeInsight.javadoc.JavaDocInfoGenerator;
import com.intellij.codeInsight.javadoc.JavaDocUtil;
import com.intellij.lang.ASTNode;
import com.intellij.lang.documentation.DocumentationProvider;
import com.intellij.lang.java.JavaDocumentationProvider;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementFactory;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiIdentifier;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiPackage;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiType;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class GosuDocumentationProvider implements DocumentationProvider {
private static final Pattern ourTypePattern = Pattern.compile("[ ]+[^ ^\\[^\\]]");
@Override
public String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) {
return null;
}
@Override
public List<String> getUrlFor(PsiElement element, PsiElement originalElement) {
return JavaDocumentationProvider.getExternalJavaDocUrl(element);
}
@Override
public String generateDoc(@Nullable PsiElement element, PsiElement originalElement) {
if (element != null) {
final List<String> docURLs = JavaDocumentationProvider.getExternalJavaDocUrl(element);
final GosuDocInfoGenerator javaDocInfoGenerator = new GosuDocInfoGenerator(element.getProject(), element);
return JavaDocExternalFilter.filterInternalDocInfo(javaDocInfoGenerator.generateDocInfo(docURLs));
}
return null;
}
@Override
public PsiElement getDocumentationElementForLookupItem(PsiManager psiManager, Object object, PsiElement element) {
return null;
}
@Override
public PsiElement getDocumentationElementForLink(@NotNull PsiManager manager, @NotNull String refText, @NotNull PsiElement context) {
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.length() > 0) {
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(@NotNull PsiClass aClass, @NotNull String memberRefText, PsiElement context) {
int parenthIndex = memberRefText.indexOf('(');
if (parenthIndex < 0) {
final PsiField field = aClass.findFieldByName(memberRefText, true);
if (field != null) {
return field.getNavigationElement();
}
final PsiClass inner = aClass.findInnerClassByName(memberRefText, true);
if (inner != null) {
return inner.getNavigationElement();
}
for (PsiMethod method : aClass.findMethodsByName(memberRefText, true)) {
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 = new PsiType[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) {
throw new RuntimeException(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];
if (
types[k] != null &&
!TypeConversionUtil.erasure(parm.getType()).getCanonicalText().equals(types[k].getCanonicalText()) &&
!parm.getType().getCanonicalText().equals(types[k].getCanonicalText())
) {
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;
}
}
}