/* * 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.codeInsight.javadoc; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.codeInsight.ExternalAnnotationsManager; import com.intellij.codeInsight.InferredAnnotationsManager; import com.intellij.codeInsight.documentation.DocumentationManagerUtil; import com.intellij.lang.ASTNode; import com.intellij.lang.LangBundle; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.impl.JavaConstantExpressionEvaluator; import com.intellij.psi.impl.source.javadoc.PsiInlineDocTagImpl; import com.intellij.psi.impl.source.tree.JavaDocElementType; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.javadoc.PsiDocTag; import com.intellij.psi.javadoc.PsiDocTagValue; import com.intellij.psi.javadoc.PsiInlineDocTag; import com.intellij.psi.util.PsiFormatUtil; import com.intellij.psi.util.PsiFormatUtilBase; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.ArrayUtilRt; import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; import com.intellij.xml.util.XmlStringUtil; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.*; import java.util.regex.Pattern; public class JavaDocInfoGenerator { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.javadoc.JavaDocInfoGenerator"); @NonNls private static final Pattern ourNotDot = Pattern.compile("[^.]"); @NonNls private static final Pattern ourWhitespaces = Pattern.compile("[ \\n\\r\\t]+"); @NonNls private static final String THROWS_KEYWORD = "throws"; @NonNls private static final String BR_TAG = "
"; @NonNls private static final String LINK_TAG = "link"; @NonNls private static final String LITERAL_TAG = "literal"; @NonNls private static final String CODE_TAG = "code"; @NonNls private static final String LINKPLAIN_TAG = "linkplain"; @NonNls private static final String INHERITDOC_TAG = "inheritDoc"; @NonNls private static final String DOCROOT_TAG = "docRoot"; @NonNls private static final String VALUE_TAG = "value"; private static final String LT = "<"; private static final String GT = ">"; private final Project myProject; private final PsiElement myElement; interface InheritDocProvider { Pair> getInheritDoc(); PsiClass getElement(); } private static final InheritDocProvider ourEmptyProvider = new InheritDocProvider() { @Override public Pair> getInheritDoc() { return null; } @Override public PsiClass getElement() { return null; } }; private static final InheritDocProvider ourEmptyElementsProvider = mapProvider(ourEmptyProvider, false); private static InheritDocProvider mapProvider(final InheritDocProvider i, final boolean dropFirst) { return new InheritDocProvider() { @Override public Pair> getInheritDoc() { Pair> pair = i.getInheritDoc(); if (pair == null) { return null; } PsiElement[] elements; PsiElement[] rawElements = pair.first.getDataElements(); if (dropFirst && rawElements != null && rawElements.length > 0) { elements = new PsiElement[rawElements.length - 1]; System.arraycopy(rawElements, 1, elements, 0, elements.length); } else { elements = rawElements; } return Pair.create(elements, mapProvider(pair.second, dropFirst)); } @Override public PsiClass getElement() { return i.getElement(); } }; } interface DocTagLocator { T find(PsiDocComment comment); } private static DocTagLocator parameterLocator(final String name) { return new DocTagLocator() { @Override public PsiDocTag find(PsiDocComment comment) { if (comment == null) { return null; } PsiDocTag[] tags = comment.findTagsByName("param"); for (PsiDocTag tag : tags) { PsiDocTagValue value = tag.getValueElement(); if (value != null) { String text = value.getText(); if (text != null && text.equals(name)) { return tag; } } } return null; } }; } private static DocTagLocator exceptionLocator(final String name) { return new DocTagLocator() { @Override public PsiDocTag find(PsiDocComment comment) { if (comment == null) { return null; } PsiDocTag[] tags = getThrowsTags(comment); for (PsiDocTag tag : tags) { PsiDocTagValue value = tag.getValueElement(); if (value != null) { String text = value.getText(); if (text != null && areWeakEqual(text, name)) { return tag; } } } return null; } }; } public JavaDocInfoGenerator(Project project, PsiElement element) { myProject = project; myElement = element; } @Nullable public String generateFileInfo() { StringBuilder buffer = new StringBuilder(); if (myElement instanceof PsiFile) { generateFileJavaDoc(buffer, (PsiFile)myElement, true); //used for Ctrl-Click } return fixupDoc(buffer); } @Nullable private static String fixupDoc(@NotNull final StringBuilder buffer) { String text = buffer.toString(); if (text.isEmpty()) { return null; } if (LOG.isDebugEnabled()) { LOG.debug("Generated JavaDoc:"); LOG.debug(text); } text = StringUtil.replaceIgnoreCase(text, "

", "

"); return StringUtil.replace(text, "/>", ">"); } public boolean generateDocInfoCore (final StringBuilder buffer, final boolean generatePrologueAndEpilogue) { if (myElement instanceof PsiClass) { generateClassJavaDoc(buffer, (PsiClass)myElement, generatePrologueAndEpilogue); } else if (myElement instanceof PsiMethod) { generateMethodJavaDoc(buffer, (PsiMethod)myElement, generatePrologueAndEpilogue); } else if (myElement instanceof PsiParameter) { generateMethodParameterJavaDoc(buffer, (PsiParameter)myElement, generatePrologueAndEpilogue); } else if (myElement instanceof PsiField) { generateFieldJavaDoc(buffer, (PsiField)myElement, generatePrologueAndEpilogue); } else if (myElement instanceof PsiVariable) { generateVariableJavaDoc(buffer, (PsiVariable)myElement, generatePrologueAndEpilogue); } else if (myElement instanceof PsiDirectory) { final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage((PsiDirectory)myElement); if (aPackage == null) return false; generatePackageJavaDoc(buffer, aPackage, generatePrologueAndEpilogue); } else if (myElement instanceof PsiPackage) { generatePackageJavaDoc(buffer, (PsiPackage) myElement, generatePrologueAndEpilogue); } else { return false; } return true; } public static String generateSignature(PsiElement element) { StringBuilder buf = new StringBuilder(); if (element instanceof PsiClass) { if (generateClassSignature(buf, (PsiClass)element, false)) return null; } else if (element instanceof PsiField) { generateFieldSignature(buf, (PsiField)element, false); } else if (element instanceof PsiMethod) { generateMethodSignature(buf, (PsiMethod)element, false); } return buf.toString(); } @Nullable public String generateDocInfo(List docURLs) { StringBuilder buffer = new StringBuilder(); if (!generateDocInfoCore(buffer, true)) return null; if (docURLs != null) { if (buffer.length() == 0) { buffer.append(""); } String errorSection = "

Following external urls were checked:
   " + StringUtil.join(docURLs, new Function() { @Override public String fun(String url) { return XmlStringUtil.escapeString(url); } }, "
   ") + "
The documentation for this element is not found. Please add all the needed paths to API docs in " + "Project Settings.

"; buffer.insert(buffer.indexOf(""), errorSection); } return fixupDoc(buffer); } private void generateClassJavaDoc(@NonNls StringBuilder buffer, PsiClass aClass, boolean generatePrologueAndEpilogue) { if (aClass instanceof PsiAnonymousClass) return; if (generatePrologueAndEpilogue) generatePrologue(buffer); PsiFile file = aClass.getContainingFile(); if (file instanceof PsiJavaFile) { String packageName = ((PsiJavaFile)file).getPackageName(); if (!packageName.isEmpty()) { buffer.append(""); buffer.append(packageName); buffer.append(""); //buffer.append("
"); } } buffer.append("
");
    if (generateClassSignature(buffer, aClass, true)) return;
    buffer.append("
"); //buffer.append("
"); PsiDocComment comment = getDocComment(aClass); if (comment != null) { generateCommonSection(buffer, comment); generateTypeParametersSection(buffer, aClass); } if (generatePrologueAndEpilogue) generateEpilogue(buffer); } private static boolean generateClassSignature(StringBuilder buffer, PsiClass aClass, boolean generateLink) { generateAnnotations(buffer, aClass, generateLink, true); String modifiers = PsiFormatUtil.formatModifiers(aClass, PsiFormatUtilBase.JAVADOC_MODIFIERS_ONLY); if (!modifiers.isEmpty()) { buffer.append(modifiers); buffer.append(" "); } buffer.append(aClass.isInterface() ? LangBundle.message("java.terms.interface") : LangBundle.message("java.terms.class")); buffer.append(" "); String refText = JavaDocUtil.getReferenceText(aClass.getProject(), aClass); if (refText == null) { buffer.setLength(0); return true; } String labelText = JavaDocUtil.getLabelText(aClass.getProject(), aClass.getManager(), refText, aClass); buffer.append(""); buffer.append(labelText); buffer.append(""); buffer.append(generateTypeParameters(aClass)); buffer.append("\n"); PsiClassType[] refs = aClass.getExtendsListTypes(); String qName = aClass.getQualifiedName(); if (refs.length > 0 || !aClass.isInterface() && (qName == null || !qName.equals(CommonClassNames.JAVA_LANG_OBJECT))) { buffer.append("extends "); if (refs.length == 0) { generateLink(buffer, CommonClassNames.JAVA_LANG_OBJECT, null, aClass, false); } else { for (int i = 0; i < refs.length; i++) { generateType(buffer, refs[i], aClass, generateLink); if (i < refs.length - 1) { buffer.append(", "); } } } buffer.append("\n"); } refs = aClass.getImplementsListTypes(); if (refs.length > 0) { buffer.append("implements "); for (int i = 0; i < refs.length; i++) { generateType(buffer, refs[i], aClass, generateLink); if (i < refs.length - 1) { buffer.append(", "); } } buffer.append("\n"); } if (buffer.charAt(buffer.length() - 1) == '\n') { buffer.setLength(buffer.length() - 1); } return false; } private void generateTypeParametersSection(final StringBuilder buffer, final PsiClass aClass) { final PsiDocComment docComment = aClass.getDocComment(); if (docComment == null) return; final LinkedList>> result = new LinkedList>>(); final PsiTypeParameter[] typeParameters = aClass.getTypeParameters(); for (PsiTypeParameter typeParameter : typeParameters) { final DocTagLocator locator = parameterLocator("<" + typeParameter.getName() + ">"); final Pair> inClassComment = findInClassComment(aClass, locator); if (inClassComment != null) { result.add(inClassComment); } else { final Pair> pair = findInHierarchy(aClass, locator); if (pair != null) { result.add(pair); } } } generateTypeParametersSection(buffer, result); } @Nullable private static Pair> findInHierarchy(PsiClass psiClass, final DocTagLocator locator) { for (final PsiClass superClass : psiClass.getSupers()) { final Pair> pair = findInClassComment(superClass, locator); if (pair != null) return pair; } for (PsiClass superInterface : psiClass.getInterfaces()) { final Pair> pair = findInClassComment(superInterface, locator); if (pair != null) return pair; } return null; } private static Pair> findInClassComment(final PsiClass psiClass, final DocTagLocator locator) { final PsiDocTag tag = locator.find(getDocComment(psiClass)); if (tag != null) { return new Pair>(tag, new InheritDocProvider() { @Override public Pair> getInheritDoc() { return findInHierarchy(psiClass, locator); } @Override public PsiClass getElement() { return psiClass; } }); } return null; } @Nullable private static PsiDocComment getDocComment(final PsiDocCommentOwner docOwner) { PsiElement navElement = docOwner.getNavigationElement(); if (!(navElement instanceof PsiDocCommentOwner)) { LOG.info("Wrong navElement: " + navElement + "; original = " + docOwner + " of class " + docOwner.getClass()); return null; } PsiDocComment comment = ((PsiDocCommentOwner)navElement).getDocComment(); if (comment == null) { //check for non-normalized fields final PsiModifierList modifierList = docOwner.getModifierList(); if (modifierList != null) { final PsiElement parent = modifierList.getParent(); if (parent instanceof PsiDocCommentOwner && parent.getNavigationElement() instanceof PsiDocCommentOwner) { return ((PsiDocCommentOwner)parent.getNavigationElement()).getDocComment(); } } } return comment; } private void generateFieldJavaDoc(@NonNls StringBuilder buffer, PsiField field, boolean generatePrologueAndEpilogue) { if (generatePrologueAndEpilogue) generatePrologue(buffer); PsiClass parentClass = field.getContainingClass(); if (parentClass != null) { String qName = parentClass.getQualifiedName(); if (qName != null) { buffer.append(""); //buffer.append(qName); generateLink(buffer, qName, qName, field, false); buffer.append(""); //buffer.append("
"); } } buffer.append("
");
    generateFieldSignature(buffer, field, true);
    buffer.append("
"); //buffer.append("
"); ColorUtil.appendColorPreview(field, buffer); PsiDocComment comment = getDocComment(field); if (comment != null) { generateCommonSection(buffer, comment); } if (generatePrologueAndEpilogue) generateEpilogue(buffer); } private static void generateFieldSignature(StringBuilder buffer, PsiField field, boolean generateLink) { generateAnnotations(buffer, field, generateLink, true); String modifiers = PsiFormatUtil.formatModifiers(field, PsiFormatUtilBase.JAVADOC_MODIFIERS_ONLY); if (!modifiers.isEmpty()) { buffer.append(modifiers); buffer.append(" "); } generateType(buffer, field.getType(), field, generateLink); buffer.append(" "); buffer.append(""); buffer.append(field.getName()); appendInitializer(buffer, field); buffer.append(""); } public static void enumConstantOrdinal(StringBuilder buffer, PsiField field, PsiClass parentClass, final String newLine) { if (parentClass != null && field instanceof PsiEnumConstant) { final PsiField[] fields = parentClass.getFields(); final int idx = ArrayUtilRt.find(fields, field); if (idx >= 0) { buffer.append(newLine); buffer.append("Enum constant ordinal: ").append(idx); } } } // not a javadoc in fact.. private static void generateVariableJavaDoc(@NonNls StringBuilder buffer, PsiVariable variable, boolean generatePrologueAndEpilogue) { if (generatePrologueAndEpilogue) generatePrologue(buffer); buffer.append("
");
    String modifiers = PsiFormatUtil.formatModifiers(variable, PsiFormatUtilBase.JAVADOC_MODIFIERS_ONLY);
    if (!modifiers.isEmpty()) {
      buffer.append(modifiers);
      buffer.append(" ");
    }
    generateType(buffer, variable.getType(), variable);
    buffer.append(" ");
    buffer.append("");
    buffer.append(variable.getName());
    appendInitializer(buffer, variable);
    buffer.append("");
    buffer.append("
"); //buffer.append("
"); ColorUtil.appendColorPreview(variable, buffer); if (generatePrologueAndEpilogue) generateEpilogue(buffer); } // not a javadoc in fact.. private static void generateFileJavaDoc(StringBuilder buffer, PsiFile file, boolean generatePrologueAndEpilogue) { if (generatePrologueAndEpilogue) generatePrologue(buffer); final VirtualFile virtualFile = file.getVirtualFile(); if (virtualFile != null) { buffer.append(virtualFile.getPresentableUrl()); } if (generatePrologueAndEpilogue) generateEpilogue(buffer); } private void generatePackageJavaDoc(final StringBuilder buffer, final PsiPackage psiPackage, boolean generatePrologueAndEpilogue) { for(PsiDirectory directory: psiPackage.getDirectories()) { final PsiFile packageInfoFile = directory.findFile(PsiPackage.PACKAGE_INFO_FILE); if (packageInfoFile != null) { final ASTNode node = packageInfoFile.getNode(); if (node != null) { final ASTNode docCommentNode = node.findChildByType(JavaDocElementType.DOC_COMMENT); if (docCommentNode != null) { final PsiDocComment docComment = (PsiDocComment)docCommentNode.getPsi(); if (generatePrologueAndEpilogue) generatePrologue(buffer); generateCommonSection(buffer, docComment); if (generatePrologueAndEpilogue) generateEpilogue(buffer); break; } } } PsiFile packageHtmlFile = directory.findFile("package.html"); if (packageHtmlFile != null) { generatePackageHtmlJavaDoc(buffer, packageHtmlFile, generatePrologueAndEpilogue); break; } } } public void generateCommonSection(StringBuilder buffer, PsiDocComment docComment) { generateDescription(buffer, docComment); generateDeprecatedSection(buffer, docComment); generateSinceSection(buffer, docComment); generateSeeAlsoSection(buffer, docComment); } private void generatePackageHtmlJavaDoc(final StringBuilder buffer, final PsiFile packageHtmlFile, boolean generatePrologueAndEpilogue) { String htmlText = packageHtmlFile.getText(); try { final Document document = JDOMUtil.loadDocument(new ByteArrayInputStream(htmlText.getBytes(CharsetToolkit.UTF8_CHARSET))); final Element rootTag = document.getRootElement(); if (rootTag != null) { final Element subTag = rootTag.getChild("body"); if (subTag != null) { htmlText = subTag.getValue(); } } } catch (JDOMException ignore) {} catch (IOException ignore) {} htmlText = StringUtil.replace(htmlText, "*/", "*/"); final String fileText = "/** " + htmlText + " */"; final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(packageHtmlFile.getProject()).getElementFactory(); final PsiDocComment docComment; try { docComment = elementFactory.createDocCommentFromText(fileText); } catch (IncorrectOperationException e) { LOG.error(e); return; } if (generatePrologueAndEpilogue) generatePrologue(buffer); generateCommonSection(buffer, docComment); if (generatePrologueAndEpilogue) generateEpilogue(buffer); } public static @Nullable PsiExpression calcInitializerExpression(PsiVariable variable) { PsiExpression initializer = variable.getInitializer(); if (initializer != null) { PsiModifierList modifierList = variable.getModifierList(); if (modifierList != null && modifierList.hasModifierProperty(PsiModifier.FINAL) && !(initializer instanceof PsiLiteralExpression)) { JavaPsiFacade instance = JavaPsiFacade.getInstance(variable.getProject()); Object o = instance.getConstantEvaluationHelper().computeConstantExpression(initializer); if (o != null) { String text = o.toString(); PsiType type = variable.getType(); if (type.equalsToText(CommonClassNames.JAVA_LANG_STRING)) { text = "\"" + StringUtil.shortenPathWithEllipsis(text, 120) + "\""; } else if (type.equalsToText("char")) text = "'" + text + "'"; try { return instance.getElementFactory().createExpressionFromText(text, variable); } catch (IncorrectOperationException ex) { LOG.error(text, ex); } } } } return null; } public static boolean appendExpressionValue(StringBuilder buffer, PsiExpression initializer, String label) { String text = initializer.getText().trim(); int index1 = text.indexOf('\n'); if (index1 < 0) index1 = text.length(); int index2 = text.indexOf('\r'); if (index2 < 0) index2 = text.length(); int index = Math.min(index1, index2); boolean trunc = index < text.length(); text = text.substring(0, index); buffer.append(label); buffer.append(StringUtil.escapeXml(text)); if (trunc) { buffer.append("..."); } return trunc; } private static void appendInitializer(StringBuilder buffer, PsiVariable variable) { PsiExpression initializer = variable.getInitializer(); if (initializer != null) { buffer.append(" = "); String text = initializer.getText(); text = text.trim(); int index1 = text.indexOf('\n'); if (index1 < 0) index1 = text.length(); int index2 = text.indexOf('\r'); if (index2 < 0) index2 = text.length(); int index = Math.min(index1, index2); boolean trunc = index < text.length(); if (trunc) { text = text.substring(0, index); buffer.append(StringUtil.escapeXml(text)); buffer.append("..."); } else { initializer.accept(new MyVisitor(buffer)); } PsiExpression constantInitializer = calcInitializerExpression(variable); if (constantInitializer != null) { buffer.append("\n"); appendExpressionValue(buffer, constantInitializer, CodeInsightBundle.message("javadoc.resolved.value")); } } } private static void generateAnnotations(@NonNls @NotNull StringBuilder buffer, @NotNull PsiModifierListOwner owner, boolean generateLink, boolean splitAnnotations) { final PsiModifierList ownerModifierList = owner.getModifierList(); if (ownerModifierList == null) return; generateAnnotations(buffer, owner, ownerModifierList.getAnnotations(), false, generateLink, splitAnnotations); PsiAnnotation[] externalAnnotations = ExternalAnnotationsManager.getInstance(owner.getProject()).findExternalAnnotations(owner); if (externalAnnotations == null) { externalAnnotations = new PsiAnnotation[]{}; } PsiAnnotation[] inferredAnnotations = InferredAnnotationsManager.getInstance(owner.getProject()).findInferredAnnotations(owner); externalAnnotations = ArrayUtil.mergeArrays(externalAnnotations, inferredAnnotations, PsiAnnotation.ARRAY_FACTORY); generateAnnotations(buffer, owner, externalAnnotations, true, generateLink, splitAnnotations); } private static void generateAnnotations(StringBuilder buffer, PsiModifierListOwner owner, PsiAnnotation[] annotations, boolean external, boolean generateLink, boolean splitAnnotations) { PsiManager manager = owner.getManager(); for (PsiAnnotation annotation : annotations) { final PsiJavaCodeReferenceElement nameReferenceElement = annotation.getNameReferenceElement(); if (nameReferenceElement == null) continue; final PsiElement resolved = nameReferenceElement.resolve(); boolean inferred = AnnotationUtil.isInferredAnnotation(annotation); if (resolved instanceof PsiClass) { final PsiClass annotationType = (PsiClass)resolved; if (AnnotationUtil.isAnnotated(annotationType, "java.lang.annotation.Documented", false)) { if (inferred) buffer.append(""); final PsiClassType type = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createType(annotationType, PsiSubstitutor.EMPTY); buffer.append("@"); generateType(buffer, type, owner, generateLink); final PsiNameValuePair[] attributes = annotation.getParameterList().getAttributes(); if (attributes.length > 0) { buffer.append("("); boolean first = true; for (PsiNameValuePair pair : attributes) { if (!first) buffer.append(" "); first = false; final String name = pair.getName(); if (name != null) { buffer.append(name); buffer.append(" = "); } final PsiAnnotationMemberValue value = pair.getValue(); if (value != null) { buffer.append(XmlStringUtil.escapeString(value.getText())); } } buffer.append(")"); } if (inferred) buffer.append(""); buffer.append(" "); } } else if (external) { if (inferred) buffer.append(""); buffer.append(XmlStringUtil.escapeString(annotation.getText())); buffer.append(" "); if (inferred) buffer.append(""); } else { buffer.append(""); buffer.append(XmlStringUtil.escapeString(annotation.getText())); buffer.append(""); buffer.append(" "); } if (splitAnnotations) buffer.append("\n"); } } private void generateMethodParameterJavaDoc(@NonNls StringBuilder buffer, PsiParameter parameter, boolean generatePrologueAndEpilogue) { if (generatePrologueAndEpilogue) generatePrologue(buffer); buffer.append("
");
    String modifiers = PsiFormatUtil.formatModifiers(parameter, PsiFormatUtilBase.JAVADOC_MODIFIERS_ONLY);
    if (!modifiers.isEmpty()) {
      buffer.append(modifiers);
      buffer.append(" ");
    }
    generateAnnotations(buffer, parameter, true, true);
    generateType(buffer, parameter.getType(), parameter);
    buffer.append(" ");
    buffer.append("");
    buffer.append(parameter.getName());
    appendInitializer(buffer, parameter);
    buffer.append("");
    buffer.append("
"); final PsiMethod method = PsiTreeUtil.getParentOfType(parameter, PsiMethod.class); if (method != null) { final PsiDocComment docComment = getDocComment(method); if (docComment != null) { final Pair> tagInfoProvider = findDocTag(docComment.getTags(), parameter.getName(), method); if (tagInfoProvider != null) { PsiElement[] elements = tagInfoProvider.first.getDataElements(); if (elements.length != 0) generateOneParameter(elements, buffer, tagInfoProvider); } } } if (generatePrologueAndEpilogue) generateEpilogue(buffer); } private void generateMethodJavaDoc(@NonNls StringBuilder buffer, PsiMethod method, boolean generatePrologueAndEpilogue) { if (generatePrologueAndEpilogue) generatePrologue(buffer); PsiClass parentClass = method.getContainingClass(); if (parentClass != null) { String qName = parentClass.getQualifiedName(); if (qName != null) { buffer.append(""); generateLink(buffer, qName, qName, method, false); //buffer.append(qName); buffer.append(""); //buffer.append("
"); } } buffer.append("
");
    generateMethodSignature(buffer, method, true);

    buffer.append("
"); //buffer.append("
"); PsiDocComment comment = getMethodDocComment(method); generateMethodDescription(buffer, method, comment); generateSuperMethodsSection(buffer, method, false); generateSuperMethodsSection(buffer, method, true); if (comment != null) { generateDeprecatedSection(buffer, comment); } generateParametersSection(buffer, method, comment); generateTypeParametersSection(buffer, method); generateReturnsSection(buffer, method, comment); generateThrowsSection(buffer, method, comment); if (comment != null) { generateSinceSection(buffer, comment); generateSeeAlsoSection(buffer, comment); } if (generatePrologueAndEpilogue) generateEpilogue(buffer); } private static void generateMethodSignature(StringBuilder buffer, PsiMethod method, boolean generateLink) { generateAnnotations(buffer, method, generateLink, true); String modifiers = PsiFormatUtil.formatModifiers(method, PsiFormatUtilBase.JAVADOC_MODIFIERS_ONLY); int indent = 0; if (!modifiers.isEmpty()) { buffer.append(modifiers); buffer.append(" "); indent += modifiers.length() + 1; } final String typeParamsString = generateTypeParameters(method); indent += StringUtil.unescapeXml(StringUtil.stripHtml(typeParamsString, true)).length(); if (!typeParamsString.isEmpty()) { buffer.append(typeParamsString); buffer.append(" "); indent++; } if (method.getReturnType() != null) { indent += generateType(buffer, method.getReturnType(), method, generateLink); buffer.append(" "); indent++; } buffer.append(""); String name = method.getName(); buffer.append(name); buffer.append(""); indent += name.length(); buffer.append("("); PsiParameter[] parms = method.getParameterList().getParameters(); for (int i = 0; i < parms.length; i++) { PsiParameter parm = parms[i]; generateAnnotations(buffer, parm, generateLink, false); generateType(buffer, parm.getType(), method, generateLink); buffer.append(" "); if (parm.getName() != null) { buffer.append(parm.getName()); } if (i < parms.length - 1) { buffer.append(",\n "); for (int j = 0; j < indent; j++) { buffer.append(" "); } } } buffer.append(")"); PsiClassType[] refs = method.getThrowsList().getReferencedTypes(); if (refs.length > 0) { buffer.append("\n"); indent -= THROWS_KEYWORD.length() + 1; for (int i = 0; i < indent; i++) { buffer.append(" "); } indent += THROWS_KEYWORD.length() + 1; buffer.append(THROWS_KEYWORD); buffer.append(" "); for (int i = 0; i < refs.length; i++) { generateLink(buffer, refs[i].getCanonicalText(), null, method, false); if (i < refs.length - 1) { buffer.append(",\n"); for (int j = 0; j < indent; j++) { buffer.append(" "); } } } } } @SuppressWarnings({"HardCodedStringLiteral"}) private PsiDocComment getMethodDocComment(final PsiMethod method) { final PsiClass parentClass = method.getContainingClass(); if (parentClass != null && parentClass.isEnum()) { final PsiParameterList parameterList = method.getParameterList(); if (method.getName().equals("values") && parameterList.getParametersCount() == 0) { return loadSyntheticDocComment(method, "/javadoc/EnumValues.java.template"); } if (method.getName().equals("valueOf") && parameterList.getParametersCount() == 1) { final PsiType psiType = parameterList.getParameters()[0].getType(); if (psiType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) { return loadSyntheticDocComment(method, "/javadoc/EnumValueOf.java.template"); } } } return getDocComment(method); } private PsiDocComment loadSyntheticDocComment(final PsiMethod method, final String resourceName) { final InputStream commentStream = JavaDocInfoGenerator.class.getResourceAsStream(resourceName); if (commentStream == null) { return null; } final StringBuilder buffer; try { buffer = new StringBuilder(); try { for (int ch; (ch = commentStream.read()) != -1;) { buffer.append((char)ch); } } catch (IOException e) { return null; } } finally { try { commentStream.close(); } catch (IOException e) { LOG.error(e); } } String s = buffer.toString(); s = StringUtil.replace(s, "", method.getContainingClass().getName()); final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(myProject).getElementFactory(); try { return elementFactory.createDocCommentFromText(s); } catch (IncorrectOperationException e) { return null; } } @SuppressWarnings({"HardCodedStringLiteral"}) private static void generatePrologue(StringBuilder buffer) { buffer.append("" + " " + ""); } @SuppressWarnings({"HardCodedStringLiteral"}) private static void generateEpilogue(StringBuilder buffer) { while (true) { if (buffer.length() < BR_TAG.length()) break; char c = buffer.charAt(buffer.length() - 1); if (c == '\n' || c == '\r' || c == ' ' || c == '\t') { buffer.setLength(buffer.length() - 1); continue; } String tail = buffer.substring(buffer.length() - BR_TAG.length()); if (tail.equalsIgnoreCase(BR_TAG)) { buffer.setLength(buffer.length() - BR_TAG.length()); continue; } break; } buffer.append(""); } private void generateDescription(StringBuilder buffer, PsiDocComment comment) { PsiElement[] elements = comment.getDescriptionElements(); generateValue(buffer, elements, ourEmptyElementsProvider); } private static boolean isEmptyDescription(PsiDocComment comment) { if (comment == null) { return true; } PsiElement[] descriptionElements = comment.getDescriptionElements(); for (PsiElement description : descriptionElements) { String text = description.getText(); if (text != null) { if (!ourWhitespaces.matcher(text).replaceAll("").isEmpty()) { return false; } } } return true; } private void generateMethodDescription(@NonNls StringBuilder buffer, final PsiMethod method, final PsiDocComment comment) { final DocTagLocator descriptionLocator = new DocTagLocator() { @Override public PsiElement[] find(PsiDocComment comment) { if (comment == null) { return null; } if (isEmptyDescription(comment)) { return null; } return comment.getDescriptionElements(); } }; if (comment != null) { if (!isEmptyDescription(comment)) { generateValue (buffer, comment.getDescriptionElements(), new InheritDocProvider() { @Override public Pair> getInheritDoc() { return findInheritDocTag(method, descriptionLocator); } @Override public PsiClass getElement() { return method.getContainingClass(); } }); return; } } Pair> pair = findInheritDocTag(method, descriptionLocator); if (pair != null) { PsiElement[] elements = pair.first; PsiClass extendee = pair.second.getElement(); if (elements != null) { buffer.append("
"); buffer.append("
"); buffer.append(extendee.isInterface() ? CodeInsightBundle.message("javadoc.description.copied.from.interface") : CodeInsightBundle .message("javadoc.description.copied.from.class")); buffer.append(" "); generateLink(buffer, extendee, JavaDocUtil.getShortestClassName(extendee, method), false); buffer.append(BR_TAG); generateValue(buffer, elements, pair.second); buffer.append("
"); } } } private void generateValue(StringBuilder buffer, PsiElement[] elements, InheritDocProvider provider) { generateValue(buffer, elements, 0, provider); } private String getDocRoot() { PsiClass aClass; if (myElement instanceof PsiClass) { aClass = (PsiClass)myElement; } else if (myElement instanceof PsiMember) { aClass = ((PsiMember)myElement).getContainingClass(); } else { aClass = PsiTreeUtil.getParentOfType(myElement, PsiClass.class); } if (aClass == null) { return ""; } String qName = aClass.getQualifiedName(); if (qName == null) { return ""; } return "../" + ourNotDot.matcher(qName).replaceAll("").replaceAll(".", "../"); } private void generateValue(StringBuilder buffer, PsiElement[] elements, int startIndex, InheritDocProvider provider) { int predictOffset = startIndex < elements.length ? elements[startIndex].getTextOffset() + elements[startIndex].getText().length() : 0; for (int i = startIndex; i < elements.length; i++) { if (elements[i].getTextOffset() > predictOffset) buffer.append(" "); predictOffset = elements[i].getTextOffset() + elements[i].getText().length(); PsiElement element = elements[i]; if (element instanceof PsiInlineDocTag) { PsiInlineDocTag tag = (PsiInlineDocTag)element; final String tagName = tag.getName(); if (tagName.equals(LINK_TAG)) { generateLinkValue(tag, buffer, false); } else if (tagName.equals(LITERAL_TAG)) { final PsiElement[] dataElements = tag instanceof PsiInlineDocTagImpl ?((PsiInlineDocTagImpl)tag).getDataElementsIgnoreWhitespaces() : tag.getDataElements(); generateLiteralValue(buffer, dataElements); } else if (tagName.equals(CODE_TAG)) { generateCodeValue(tag, buffer); } else if (tagName.equals(LINKPLAIN_TAG)) { generateLinkValue(tag, buffer, true); } else if (tagName.equals(INHERITDOC_TAG)) { Pair> inheritInfo = provider.getInheritDoc(); if (inheritInfo != null) { generateValue(buffer, inheritInfo.first, inheritInfo.second); } } else if (tagName.equals(DOCROOT_TAG)) { buffer.append(getDocRoot()); } else if (tagName.equals(VALUE_TAG)) { generateValueValue(tag, buffer, element); } else { buffer.append(element.getText()); } } else { buffer.append(element.getText()); } } } @SuppressWarnings({"HardCodedStringLiteral"}) private static void generateCodeValue(PsiInlineDocTag tag, StringBuilder buffer) { buffer.append(""); generateLiteralValue(buffer, tag.getDataElements()); buffer.append(""); } private static void generateLiteralValue(StringBuilder buffer, final PsiElement[] dataElements) { for (PsiElement element : dataElements) { appendPlainText(element.getText(), buffer); } } private static void appendPlainText(@NonNls String text, final StringBuilder buffer) { text = text.replaceAll("<", LT); text = text.replaceAll(">", GT); buffer.append(text); } private static void generateLinkValue(PsiInlineDocTag tag, StringBuilder buffer, boolean plainLink) { PsiElement[] tagElements = tag.getDataElements(); String text = createLinkText(tagElements); if (!text.isEmpty()) { int index = JavaDocUtil.extractReference(text); String refText = text.substring(0, index).trim(); String label = text.substring(index).trim(); if (label.isEmpty()) { label = null; } generateLink(buffer, refText, label, tagElements[0], plainLink); } } private void generateValueValue(final PsiInlineDocTag tag, final StringBuilder buffer, final PsiElement element) { String text = createLinkText(tag.getDataElements()); PsiField valueField = null; if (text.isEmpty()) { if (myElement instanceof PsiField) valueField = (PsiField) myElement; } else { PsiElement target = JavaDocUtil.findReferenceTarget(PsiManager.getInstance(myProject), text, myElement); if (target instanceof PsiField) { valueField = (PsiField) target; } } Object value = null; if (valueField != null) { PsiExpression initializer = valueField.getInitializer(); value = JavaConstantExpressionEvaluator.computeConstantExpression(initializer, false); } if (value != null) { buffer.append(value); } else { buffer.append(element.getText()); } } private static String createLinkText(final PsiElement[] tagElements) { int predictOffset = tagElements.length > 0 ? tagElements[0].getTextOffset() + tagElements[0].getText().length() : 0; StringBuilder buffer1 = new StringBuilder(); for (int j = 0; j < tagElements.length; j++) { PsiElement tagElement = tagElements[j]; if (tagElement.getTextOffset() > predictOffset) buffer1.append(" "); predictOffset = tagElement.getTextOffset() + tagElement.getText().length(); buffer1.append(tagElement.getText()); if (j < tagElements.length - 1) { buffer1.append(" "); } } return buffer1.toString().trim(); } @SuppressWarnings({"HardCodedStringLiteral"}) private void generateDeprecatedSection(StringBuilder buffer, PsiDocComment comment) { PsiDocTag tag = comment.findTagByName("deprecated"); if (tag != null) { buffer.append("
"); buffer.append("").append(CodeInsightBundle.message("javadoc.deprecated")).append(" "); buffer.append(""); generateValue(buffer, tag.getDataElements(), ourEmptyElementsProvider); buffer.append(""); buffer.append("
"); } } @SuppressWarnings({"HardCodedStringLiteral"}) private void generateSinceSection(StringBuilder buffer, PsiDocComment comment) { PsiDocTag tag = comment.findTagByName("since"); if (tag != null) { buffer.append("
"); buffer.append("
").append(CodeInsightBundle.message("javadoc.since")).append(""); buffer.append("
"); generateValue(buffer, tag.getDataElements(), ourEmptyElementsProvider); buffer.append("
"); } } @SuppressWarnings({"HardCodedStringLiteral"}) private static void generateSeeAlsoSection(StringBuilder buffer, PsiDocComment comment) { PsiDocTag[] tags = comment.findTagsByName("see"); if (tags.length > 0) { buffer.append("
"); buffer.append("
").append(CodeInsightBundle.message("javadoc.see.also")).append(""); buffer.append("
"); for (int i = 0; i < tags.length; i++) { PsiDocTag tag = tags[i]; PsiElement[] elements = tag.getDataElements(); if (elements.length > 0) { String text = createLinkText(elements); if (text.startsWith("<")) { buffer.append(text); } else if (text.startsWith("\"")) { appendPlainText(text, buffer); } else { int index = JavaDocUtil.extractReference(text); String refText = text.substring(0, index).trim(); String label = text.substring(index).trim(); if (label.isEmpty()) { label = null; } generateLink(buffer, refText, label, comment, false); } } if (i < tags.length - 1) { buffer.append(",\n"); } } buffer.append("
"); } } @SuppressWarnings({"HardCodedStringLiteral"}) private void generateParametersSection(StringBuilder buffer, final PsiMethod method, final PsiDocComment comment) { PsiParameter[] params = method.getParameterList().getParameters(); PsiDocTag[] localTags = comment != null ? comment.findTagsByName("param") : PsiDocTag.EMPTY_ARRAY; LinkedList>> collectedTags = new LinkedList>>(); for (PsiParameter param : params) { final String paramName = param.getName(); Pair> parmTag = findDocTag(localTags, paramName, method); if (parmTag != null) { collectedTags.addLast(parmTag); } } if (!collectedTags.isEmpty()) { buffer.append("
"); buffer.append("
").append(CodeInsightBundle.message("javadoc.parameters")).append(""); for (Pair> tag : collectedTags) { PsiElement[] elements = tag.first.getDataElements(); if (elements.length == 0) continue; generateOneParameter(elements, buffer, tag); } buffer.append("
"); } } private void generateTypeParametersSection(final StringBuilder buffer, final PsiMethod method) { final PsiDocComment docComment = method.getDocComment(); if (docComment == null) return; final PsiDocTag[] localTags = docComment.findTagsByName("param"); final PsiTypeParameter[] typeParameters = method.getTypeParameters(); final LinkedList>> collectedTags = new LinkedList>>(); for (PsiTypeParameter typeParameter : typeParameters) { final String paramName = "<" + typeParameter.getName() + ">"; Pair> parmTag = findDocTag(localTags, paramName, method); if (parmTag != null) { collectedTags.addLast(parmTag); } } generateTypeParametersSection(buffer, collectedTags); } private void generateTypeParametersSection(final StringBuilder buffer, final LinkedList>> collectedTags) { if (!collectedTags.isEmpty()) { buffer.append("
"); buffer.append("
").append(CodeInsightBundle.message("javadoc.type.parameters")).append(""); for (Pair> tag : collectedTags) { PsiElement[] elements = tag.first.getDataElements(); if (elements.length == 0) continue; generateOneParameter(elements, buffer, tag); } buffer.append("
"); } } @Nullable private Pair> findDocTag(final PsiDocTag[] localTags, final String paramName, final PsiMethod method) { Pair> parmTag = null; for (PsiDocTag localTag : localTags) { PsiDocTagValue value = localTag.getValueElement(); if (value != null) { String tagName = value.getText(); if (tagName != null && tagName.equals(paramName)) { parmTag = new Pair> (localTag, new InheritDocProvider() { @Override public Pair> getInheritDoc() { return findInheritDocTag(method, parameterLocator(paramName)); } @Override public PsiClass getElement() { return method.getContainingClass(); } }); break; } } } if (parmTag == null) { parmTag = findInheritDocTag(method, parameterLocator(paramName)); } return parmTag; } @SuppressWarnings({"HardCodedStringLiteral"}) private void generateOneParameter(final PsiElement[] elements, final StringBuilder buffer, final Pair> tag) { String text = elements[0].getText(); buffer.append("
"); int spaceIndex = text.indexOf(' '); if (spaceIndex < 0) { spaceIndex = text.length(); } String parmName = text.substring(0, spaceIndex); buffer.append(""); buffer.append(StringUtil.escapeXml(parmName)); buffer.append(""); buffer.append(" - "); buffer.append(text.substring(spaceIndex)); generateValue(buffer, elements, 1, mapProvider(tag.second, true)); } @SuppressWarnings({"HardCodedStringLiteral"}) private void generateReturnsSection(StringBuilder buffer, final PsiMethod method, final PsiDocComment comment) { PsiDocTag tag = comment == null ? null : comment.findTagByName("return"); Pair> pair = tag == null ? null : new Pair>(tag, new InheritDocProvider(){ @Override public Pair> getInheritDoc() { return findInheritDocTag(method, new ReturnTagLocator()); } @Override public PsiClass getElement() { return method.getContainingClass(); } }); if (pair == null && myElement instanceof PsiMethod) { pair = findInheritDocTag((PsiMethod)myElement, new ReturnTagLocator()); } if (pair != null) { buffer.append("
"); buffer.append("
").append(CodeInsightBundle.message("javadoc.returns")).append(""); buffer.append("
"); generateValue(buffer, pair.first.getDataElements(), mapProvider(pair.second, false)); buffer.append("
"); } } private static PsiDocTag[] getThrowsTags(PsiDocComment comment) { if (comment == null) { return PsiDocTag.EMPTY_ARRAY; } PsiDocTag[] tags1 = comment.findTagsByName(THROWS_KEYWORD); PsiDocTag[] tags2 = comment.findTagsByName("exception"); return ArrayUtil.mergeArrays(tags1, tags2); } private static boolean areWeakEqual(String one, String two) { return one.equals(two) || one.endsWith("." + two) || two.endsWith("." + one); } @SuppressWarnings({"HardCodedStringLiteral"}) private void generateThrowsSection(StringBuilder buffer, final PsiMethod method, final PsiDocComment comment) { final PsiDocTag[] localTags = getThrowsTags(comment); LinkedList>> collectedTags = new LinkedList>>(); final List declaredThrows = new ArrayList(Arrays.asList(method.getThrowsList().getReferencedTypes())); for (int i = localTags.length - 1; i > -1; i--) { PsiDocTagValue valueElement = localTags[i].getValueElement(); if (valueElement != null) { for (Iterator iterator = declaredThrows.iterator(); iterator.hasNext();) { PsiClassType classType = iterator.next(); if (Comparing.strEqual(valueElement.getText(), classType.getClassName()) || Comparing.strEqual(valueElement.getText(), classType.getCanonicalText())) { iterator.remove(); break; } } final Pair> tag = findInheritDocTag(method, exceptionLocator(valueElement.getText())); collectedTags.addFirst(new Pair>(localTags[i], new InheritDocProvider() { @Override public Pair> getInheritDoc() { return tag; } @Override public PsiClass getElement() { return method.getContainingClass(); } })); } } PsiClassType[] trousers = declaredThrows.toArray(new PsiClassType[declaredThrows.size()]); for (PsiClassType trouser : trousers) { if (trouser != null) { String paramName = trouser.getCanonicalText(); Pair> parmTag = null; for (PsiDocTag localTag : localTags) { PsiDocTagValue value = localTag.getValueElement(); if (value != null) { String tagName = value.getText(); if (tagName != null && areWeakEqual(tagName, paramName)) { parmTag = Pair.create(localTag, ourEmptyProvider); break; } } } if (parmTag == null) { parmTag = findInheritDocTag(method, exceptionLocator(paramName)); } if (parmTag != null) { collectedTags.addLast(parmTag); } else { try { final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory(); final PsiDocTag tag = elementFactory.createDocTagFromText("@exception " + paramName); collectedTags.addLast(Pair.create(tag, ourEmptyProvider)); } catch (IncorrectOperationException e) { LOG.error(e); } } } } if (!collectedTags.isEmpty()) { buffer.append("
"); buffer.append("
").append(CodeInsightBundle.message("javadoc.throws")).append(""); for (Pair> tag : collectedTags) { PsiElement[] elements = tag.first.getDataElements(); if (elements.length == 0) continue; buffer.append("
"); String text = elements[0].getText(); int index = JavaDocUtil.extractReference(text); String refText = text.substring(0, index).trim(); generateLink(buffer, refText, null, method, false); String rest = text.substring(index); if (!rest.isEmpty() || elements.length > 1) buffer.append(" - "); buffer.append(rest); generateValue(buffer, elements, 1, mapProvider(tag.second, true)); } buffer.append("
"); } } @SuppressWarnings({"HardCodedStringLiteral"}) private static void generateSuperMethodsSection(StringBuilder buffer, PsiMethod method, boolean overrides) { PsiClass parentClass = method.getContainingClass(); if (parentClass == null) return; if (parentClass.isInterface() && !overrides) return; PsiMethod[] supers = method.findSuperMethods(); if (supers.length == 0) return; boolean headerGenerated = false; for (PsiMethod superMethod : supers) { boolean isAbstract = superMethod.hasModifierProperty(PsiModifier.ABSTRACT); if (overrides) { if (parentClass.isInterface() ? !isAbstract : isAbstract) continue; } else { if (!isAbstract) continue; } PsiClass superClass = superMethod.getContainingClass(); if (superClass == null) continue; if (!headerGenerated) { buffer.append("
"); buffer.append("
"); buffer.append(overrides ? CodeInsightBundle.message("javadoc.method.overrides") : CodeInsightBundle .message("javadoc.method.specified.by")); buffer.append(""); headerGenerated = true; } buffer.append("
"); StringBuilder methodBuffer = new StringBuilder(); generateLink(methodBuffer, superMethod, superMethod.getName(), false); StringBuilder classBuffer = new StringBuilder(); generateLink(classBuffer, superClass, superClass.getName(), false); if (superClass.isInterface()) { buffer.append(CodeInsightBundle.message("javadoc.method.in.interface", methodBuffer.toString(), classBuffer.toString())); } else { buffer.append(CodeInsightBundle.message("javadoc.method.in.class", methodBuffer.toString(), classBuffer.toString())); } } if (headerGenerated) { buffer.append("
"); } } private static void generateLink(StringBuilder buffer, PsiElement element, String label, boolean plainLink) { String refText = JavaDocUtil.getReferenceText(element.getProject(), element); if (refText != null) { DocumentationManagerUtil.createHyperlink(buffer, element, refText, label, plainLink); //return generateLink(buffer, refText, label, context, false); } } /** * @return Length of the generated label. */ @SuppressWarnings({"HardCodedStringLiteral"}) private static int generateLink(StringBuilder buffer, String refText, String label, @NotNull PsiElement context, boolean plainLink) { if (label == null) { final PsiManager manager = context.getManager(); label = JavaDocUtil.getLabelText(manager.getProject(), manager, refText, context); } LOG.assertTrue(refText != null, "refText appears to be null."); final PsiElement target = JavaDocUtil.findReferenceTarget(context.getManager(), refText, context); boolean isBrokenLink = target == null; if (isBrokenLink) { buffer.append(""); buffer.append(label); buffer.append(""); return StringUtil.stripHtml(label, true).length(); } generateLink(buffer, target, label, plainLink); return StringUtil.stripHtml(label, true).length(); } /** * @return Length of the generated label. */ @SuppressWarnings({"HardCodedStringLiteral"}) public static int generateType(StringBuilder buffer, PsiType type, PsiElement context) { return generateType(buffer, type, context, true); } /** * @return Length of the generated label. */ @SuppressWarnings({"HardCodedStringLiteral"}) public static int generateType(StringBuilder buffer, PsiType type, PsiElement context, boolean generateLink) { if (type instanceof PsiPrimitiveType) { String text = StringUtil.escapeXml(type.getCanonicalText()); buffer.append(text); return text.length(); } if (type instanceof PsiArrayType) { int rest = generateType(buffer, ((PsiArrayType)type).getComponentType(), context, generateLink); if (type instanceof PsiEllipsisType) { buffer.append("..."); return rest + 3; } else { buffer.append("[]"); return rest + 2; } } if (type instanceof PsiCapturedWildcardType) { type = ((PsiCapturedWildcardType)type).getWildcard(); } if (type instanceof PsiWildcardType){ PsiWildcardType wt = (PsiWildcardType)type; buffer.append("?"); PsiType bound = wt.getBound(); if (bound != null){ final String keyword = wt.isExtends() ? " extends " : " super "; buffer.append(keyword); return generateType(buffer, bound, context, generateLink) + 1 + keyword.length(); } return 1; } if (type instanceof PsiClassType) { PsiClassType.ClassResolveResult result = ((PsiClassType)type).resolveGenerics(); PsiClass psiClass = result.getElement(); PsiSubstitutor psiSubst = result.getSubstitutor(); if (psiClass == null) { String canonicalText = type.getCanonicalText(); String text = "" + StringUtil.escapeXml(canonicalText) + ""; buffer.append(text); return canonicalText.length(); } String qName = psiClass.getQualifiedName(); if (qName == null || psiClass instanceof PsiTypeParameter) { String text = StringUtil.escapeXml(type.getCanonicalText()); buffer.append(text); return text.length(); } int length; if (generateLink) { length = generateLink(buffer, qName, null, context, false); } else { buffer.append(qName); length = buffer.length(); } if (psiClass.hasTypeParameters()) { StringBuilder subst = new StringBuilder(); PsiTypeParameter[] params = psiClass.getTypeParameters(); subst.append(LT); length += 1; boolean goodSubst = true; for (int i = 0; i < params.length; i++) { PsiType t = psiSubst.substitute(params[i]); if (t == null) { goodSubst = false; break; } length += generateType(subst, t, context, generateLink); if (i < params.length - 1) { subst.append(", "); } } subst.append(GT); length += 1; if (goodSubst) { String text = subst.toString(); buffer.append(text); } } return length; } if (type instanceof PsiDisjunctionType || type instanceof PsiIntersectionType) { if (!generateLink) { String canonicalText = type.getCanonicalText(); final String text = StringUtil.escapeXml(canonicalText); buffer.append(text); return canonicalText.length(); } else { final String separator = type instanceof PsiDisjunctionType ? " | " : " & "; final List componentTypes; if (type instanceof PsiIntersectionType) { componentTypes = Arrays.asList(((PsiIntersectionType)type).getConjuncts()); } else { componentTypes = ((PsiDisjunctionType)type).getDisjunctions(); } int length = 0; for (PsiType psiType : componentTypes) { if (length > 0) { buffer.append(separator); length += 3; } length += generateType(buffer, psiType, context, generateLink); } return length; } } return 0; } @SuppressWarnings({"HardCodedStringLiteral"}) private static String generateTypeParameters(PsiTypeParameterListOwner owner) { if (owner.hasTypeParameters()) { PsiTypeParameter[] parms = owner.getTypeParameters(); StringBuilder buffer = new StringBuilder(); buffer.append(LT); for (int i = 0; i < parms.length; i++) { PsiTypeParameter p = parms[i]; buffer.append(p.getName()); PsiClassType[] refs = JavaDocUtil.getExtendsList(p); if (refs.length > 0) { buffer.append(" extends "); for (int j = 0; j < refs.length; j++) { generateType(buffer, refs[j], owner); if (j < refs.length - 1) { buffer.append(" & "); } } } if (i < parms.length - 1) { buffer.append(", "); } } buffer.append(GT); return buffer.toString(); } return ""; } private Pair> searchDocTagInOverridenMethod(PsiMethod method, final PsiClass aSuper, final DocTagLocator loc) { if (aSuper != null) { final PsiMethod overriden = findMethodInSuperClass(method, aSuper); if (overriden != null) { T tag = loc.find(getDocComment(overriden)); if (tag != null) { return new Pair> (tag, new InheritDocProvider() { @Override public Pair> getInheritDoc() { return findInheritDocTag(overriden, loc); } @Override public PsiClass getElement() { return aSuper; } }); } } } return null; } @Nullable private static PsiMethod findMethodInSuperClass(PsiMethod method, PsiClass aSuper) { for (PsiMethod superMethod : method.findDeepestSuperMethods()) { PsiMethod overriden = aSuper.findMethodBySignature(superMethod, false); if (overriden != null) { return overriden; } } return null; } @Nullable private Pair> searchDocTagInSupers(PsiClassType[] supers, PsiMethod method, DocTagLocator loc, Set visitedClasses) { for (PsiClassType superType : supers) { PsiClass aSuper = superType.resolve(); if (aSuper != null) { Pair> tag = searchDocTagInOverridenMethod(method, aSuper, loc); if (tag != null) return tag; } } for (PsiClassType superType : supers) { PsiClass aSuper = superType.resolve(); if (aSuper != null && visitedClasses.add(aSuper)) { Pair> tag = findInheritDocTagInClass(method, aSuper, loc, visitedClasses); if (tag != null) { return tag; } } } return null; } private Pair> findInheritDocTagInClass(PsiMethod aMethod, PsiClass aClass, DocTagLocator loc, Set visitedClasses) { if (aClass == null) { return null; } PsiClassType[] implementsTypes = aClass.getImplementsListTypes(); Pair> tag = searchDocTagInSupers(implementsTypes, aMethod, loc, visitedClasses); if (tag != null) { return tag; } PsiClassType[] extendsTypes = aClass.getExtendsListTypes(); return searchDocTagInSupers(extendsTypes, aMethod, loc, visitedClasses); } @Nullable private Pair> findInheritDocTag(PsiMethod method, DocTagLocator loc) { PsiClass aClass = method.getContainingClass(); if (aClass == null) return null; return findInheritDocTagInClass(method, aClass, loc, new HashSet()); } private static class ReturnTagLocator implements DocTagLocator { @Override public PsiDocTag find(PsiDocComment comment) { if (comment == null) { return null; } return comment.findTagByName("return"); } } private static class MyVisitor extends JavaElementVisitor { @NotNull private final StringBuilder myBuffer; MyVisitor(@NotNull StringBuilder buffer) { myBuffer = buffer; } @Override public void visitNewExpression(PsiNewExpression expression) { myBuffer.append("new "); PsiType type = expression.getType(); if (type != null) { generateType(myBuffer, type, expression); } myBuffer.append("("); expression.acceptChildren(this); myBuffer.append(")"); } @Override public void visitExpressionList(PsiExpressionList list) { String separator = ", "; PsiExpression[] expressions = list.getExpressions(); for (PsiExpression expression : expressions) { expression.accept(this); myBuffer.append(separator); } if (expressions.length > 0) { myBuffer.setLength(myBuffer.length() - separator.length()); } } @Override public void visitMethodCallExpression(PsiMethodCallExpression expression) { myBuffer.append(expression.getMethodExpression().getText()).append("("); expression.getArgumentList().acceptChildren(this); myBuffer.append(")"); } @Override public void visitLiteralExpression(PsiLiteralExpression expression) { myBuffer.append(StringUtil.escapeXml(expression.getText())); } @Override public void visitReferenceExpression(PsiReferenceExpression expression) { myBuffer.append(StringUtil.escapeXml(expression.getText())); } } }