> 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()));
}
}
}