| /* |
| * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package jdk.javadoc.internal.doclets.toolkit.util; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import javax.lang.model.element.Element; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.element.PackageElement; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.type.TypeMirror; |
| |
| import com.sun.source.doctree.AttributeTree; |
| import com.sun.source.doctree.AttributeTree.ValueKind; |
| import com.sun.source.doctree.AuthorTree; |
| import com.sun.source.doctree.BlockTagTree; |
| import com.sun.source.doctree.CommentTree; |
| import com.sun.source.doctree.DeprecatedTree; |
| import com.sun.source.doctree.DocCommentTree; |
| import com.sun.source.doctree.DocTree; |
| import com.sun.source.doctree.EndElementTree; |
| import com.sun.source.doctree.EntityTree; |
| import com.sun.source.doctree.IdentifierTree; |
| import com.sun.source.doctree.InlineTagTree; |
| import com.sun.source.doctree.LinkTree; |
| import com.sun.source.doctree.LiteralTree; |
| import com.sun.source.doctree.ParamTree; |
| import com.sun.source.doctree.ProvidesTree; |
| import com.sun.source.doctree.ReferenceTree; |
| import com.sun.source.doctree.ReturnTree; |
| import com.sun.source.doctree.SeeTree; |
| import com.sun.source.doctree.SerialDataTree; |
| import com.sun.source.doctree.SerialFieldTree; |
| import com.sun.source.doctree.SerialTree; |
| import com.sun.source.doctree.SinceTree; |
| import com.sun.source.doctree.StartElementTree; |
| import com.sun.source.doctree.TextTree; |
| import com.sun.source.doctree.ThrowsTree; |
| import com.sun.source.doctree.UnknownBlockTagTree; |
| import com.sun.source.doctree.UsesTree; |
| import com.sun.source.doctree.ValueTree; |
| import com.sun.source.doctree.VersionTree; |
| import com.sun.source.util.DocTreePath; |
| import com.sun.source.util.DocTrees; |
| import com.sun.source.util.SimpleDocTreeVisitor; |
| import com.sun.source.util.TreePath; |
| import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration; |
| |
| import static com.sun.source.doctree.DocTree.Kind.*; |
| |
| /** |
| * A utility class. |
| * |
| * <p><b>This is NOT part of any supported API. |
| * If you write code that depends on this, you do so at your own risk. |
| * This code and its internal interfaces are subject to change or |
| * deletion without notice.</b> |
| */ |
| public class CommentHelper { |
| public final TreePath path; |
| public final DocCommentTree dctree; |
| public final Element element; |
| private Element overriddenElement; |
| |
| public static final String SPACER = " "; |
| |
| public CommentHelper(BaseConfiguration configuration, Element element, TreePath path, DocCommentTree dctree) { |
| //this.configuration = configuration; |
| this.element = element; |
| this.path = path; |
| this.dctree = dctree; |
| } |
| |
| public void setOverrideElement(Element ove) { |
| if (this.element == ove) { |
| throw new AssertionError("cannot set given element as overriden element"); |
| } |
| overriddenElement = ove; |
| } |
| |
| @SuppressWarnings("fallthrough") |
| public String getTagName(DocTree dtree) { |
| switch (dtree.getKind()) { |
| case AUTHOR: |
| case DEPRECATED: |
| case PARAM: |
| case PROVIDES: |
| case RETURN: |
| case SEE: |
| case SERIAL_DATA: |
| case SERIAL_FIELD: |
| case THROWS: |
| case UNKNOWN_BLOCK_TAG: |
| case USES: |
| case VERSION: |
| return ((BlockTagTree)dtree).getTagName(); |
| case UNKNOWN_INLINE_TAG: |
| return ((InlineTagTree)dtree).getTagName(); |
| case ERRONEOUS: |
| return "erroneous"; |
| default: |
| return dtree.getKind().tagName; |
| } |
| } |
| |
| public boolean isTypeParameter(DocTree dtree) { |
| if (dtree.getKind() == PARAM) { |
| return ((ParamTree)dtree).isTypeParameter(); |
| } |
| return false; |
| } |
| |
| public String getParameterName(DocTree dtree) { |
| if (dtree.getKind() == PARAM) { |
| return ((ParamTree) dtree).getName().toString(); |
| } else { |
| return null; |
| } |
| } |
| |
| Element getElement(BaseConfiguration c, ReferenceTree rtree) { |
| // likely a synthesized tree |
| if (path == null) { |
| TypeMirror symbol = c.utils.getSymbol(rtree.getSignature()); |
| if (symbol == null) { |
| return null; |
| } |
| return c.docEnv.getTypeUtils().asElement(symbol); |
| } |
| // case A: the element contains no comments associated and |
| // the comments need to be copied from ancestor |
| // case B: the element has @inheritDoc, then the ancestral comment |
| // as appropriate has to be copied over. |
| |
| // Case A. |
| if (dctree == null && overriddenElement != null) { |
| CommentHelper ovch = c.utils.getCommentHelper(overriddenElement); |
| return ovch.getElement(c, rtree); |
| } |
| if (dctree == null) { |
| return null; |
| } |
| DocTreePath docTreePath = DocTreePath.getPath(path, dctree, rtree); |
| if (docTreePath == null) { |
| // Case B. |
| if (overriddenElement != null) { |
| CommentHelper ovch = c.utils.getCommentHelper(overriddenElement); |
| return ovch.getElement(c, rtree); |
| } |
| return null; |
| } |
| DocTrees doctrees = c.docEnv.getDocTrees(); |
| return doctrees.getElement(docTreePath); |
| } |
| |
| public Element getException(BaseConfiguration c, DocTree dtree) { |
| if (dtree.getKind() == THROWS || dtree.getKind() == EXCEPTION) { |
| ThrowsTree tt = (ThrowsTree)dtree; |
| ReferenceTree exceptionName = tt.getExceptionName(); |
| return getElement(c, exceptionName); |
| } |
| return null; |
| } |
| |
| public List<? extends DocTree> getDescription(BaseConfiguration c, DocTree dtree) { |
| return getTags(c, dtree); |
| } |
| |
| public String getText(List<? extends DocTree> list) { |
| StringBuilder sb = new StringBuilder(); |
| for (DocTree dt : list) { |
| sb.append(getText0(dt)); |
| } |
| return sb.toString(); |
| } |
| |
| public String getText(DocTree dt) { |
| return getText0(dt).toString(); |
| } |
| |
| private StringBuilder getText0(DocTree dt) { |
| final StringBuilder sb = new StringBuilder(); |
| new SimpleDocTreeVisitor<Void, Void>() { |
| @Override |
| public Void visitAttribute(AttributeTree node, Void p) { |
| sb.append(SPACER).append(node.getName()); |
| if (node.getValueKind() == ValueKind.EMPTY) { |
| return null; |
| } |
| |
| sb.append("="); |
| String quote; |
| switch (node.getValueKind()) { |
| case DOUBLE: |
| quote = "\""; |
| break; |
| case SINGLE: |
| quote = "\'"; |
| break; |
| default: |
| quote = ""; |
| break; |
| } |
| sb.append(quote); |
| node.getValue().stream().forEach((dt) -> { |
| dt.accept(this, null); |
| }); |
| sb.append(quote); |
| return null; |
| } |
| |
| @Override |
| public Void visitEndElement(EndElementTree node, Void p) { |
| sb.append("</") |
| .append(node.getName()) |
| .append(">"); |
| return null; |
| } |
| |
| @Override |
| public Void visitEntity(EntityTree node, Void p) { |
| sb.append(node.toString()); |
| return null; |
| } |
| |
| @Override |
| public Void visitLink(LinkTree node, Void p) { |
| if (node.getReference() == null) { |
| return null; |
| } |
| |
| node.getReference().accept(this, null); |
| node.getLabel().stream().forEach((dt) -> { |
| dt.accept(this, null); |
| }); |
| return null; |
| } |
| |
| @Override |
| public Void visitLiteral(LiteralTree node, Void p) { |
| if (node.getKind() == CODE) { |
| sb.append("<").append(node.getKind().tagName).append(">"); |
| } |
| sb.append(node.getBody().toString()); |
| if (node.getKind() == CODE) { |
| sb.append("</").append(node.getKind().tagName).append(">"); |
| } |
| return null; |
| } |
| |
| @Override |
| public Void visitReference(ReferenceTree node, Void p) { |
| sb.append(node.getSignature()); |
| return null; |
| } |
| |
| @Override |
| public Void visitSee(SeeTree node, Void p) { |
| node.getReference().stream().forEach((dt) -> { |
| dt.accept(this, null); |
| }); |
| return null; |
| } |
| |
| @Override |
| public Void visitSerial(SerialTree node, Void p) { |
| node.getDescription().stream().forEach((dt) -> { |
| dt.accept(this, null); |
| }); |
| return null; |
| } |
| |
| @Override |
| public Void visitStartElement(StartElementTree node, Void p) { |
| sb.append("<"); |
| sb.append(node.getName()); |
| node.getAttributes().stream().forEach((dt) -> { |
| dt.accept(this, null); |
| }); |
| sb.append((node.isSelfClosing() ? "/>" : ">")); |
| return null; |
| } |
| |
| @Override |
| public Void visitText(TextTree node, Void p) { |
| sb.append(node.getBody()); |
| return null; |
| } |
| |
| @Override |
| public Void visitUnknownBlockTag(UnknownBlockTagTree node, Void p) { |
| node.getContent().stream().forEach((dt) -> { |
| dt.accept(this, null); |
| }); |
| return null; |
| } |
| |
| @Override |
| public Void visitValue(ValueTree node, Void p) { |
| return node.getReference().accept(this, null); |
| } |
| |
| @Override |
| protected Void defaultAction(DocTree node, Void p) { |
| sb.append(node.toString()); |
| return null; |
| } |
| }.visit(dt, null); |
| return sb; |
| } |
| |
| public String getLabel(BaseConfiguration c, DocTree dtree) { |
| return new SimpleDocTreeVisitor<String, Void>() { |
| @Override |
| public String visitLink(LinkTree node, Void p) { |
| StringBuilder sb = new StringBuilder(); |
| node.getLabel().stream().forEach((dt) -> { |
| sb.append(getText(dt)); |
| }); |
| return sb.toString(); |
| } |
| |
| @Override |
| public String visitSee(SeeTree node, Void p) { |
| StringBuilder sb = new StringBuilder(); |
| node.getReference().stream().filter((dt) -> (c.utils.isText(dt))).forEach((dt) -> { |
| sb.append(((TextTree)dt).getBody()); |
| }); |
| return sb.toString(); |
| } |
| |
| @Override |
| protected String defaultAction(DocTree node, Void p) { |
| return ""; |
| } |
| }.visit(dtree, null); |
| } |
| |
| public TypeElement getReferencedClass(BaseConfiguration c, DocTree dtree) { |
| Element e = getReferencedElement(c, dtree); |
| if (e == null) { |
| return null; |
| } else if (c.utils.isTypeElement(e)) { |
| return (TypeElement) e; |
| } else if (!c.utils.isPackage(e)) { |
| return c.utils.getEnclosingTypeElement(e); |
| } |
| return null; |
| } |
| |
| public String getReferencedClassName(BaseConfiguration c, DocTree dtree) { |
| Element e = getReferencedClass(c, dtree); |
| if (e != null) { |
| return c.utils.isTypeElement(e) ? c.utils.getSimpleName(e) : null; |
| } |
| String s = getReferencedSignature(dtree); |
| if (s == null) { |
| return null; |
| } |
| int n = s.indexOf("#"); |
| return (n == -1) ? s : s.substring(0, n); |
| } |
| |
| public Element getReferencedMember(BaseConfiguration c, DocTree dtree) { |
| Element e = getReferencedElement(c, dtree); |
| if (e == null) { |
| return null; |
| } |
| return (c.utils.isExecutableElement(e) || c.utils.isVariableElement(e)) ? e : null; |
| } |
| |
| public String getReferencedMemberName(DocTree dtree) { |
| String s = getReferencedSignature(dtree); |
| if (s == null) { |
| return null; |
| } |
| int n = s.indexOf("#"); |
| return (n == -1) ? null : s.substring(n + 1); |
| } |
| |
| public String getReferencedMemberName(BaseConfiguration c, Element e) { |
| if (e == null) { |
| return null; |
| } |
| return c.utils.isExecutableElement(e) |
| ? c.utils.getSimpleName(e) + c.utils.makeSignature((ExecutableElement) e, true, true) |
| : c.utils.getSimpleName(e); |
| } |
| |
| public PackageElement getReferencedPackage(BaseConfiguration c, DocTree dtree) { |
| Element e = getReferencedElement(c, dtree); |
| if (e != null) { |
| return c.utils.containingPackage(e); |
| } |
| return null; |
| } |
| |
| public List<? extends DocTree> getFirstSentenceTrees(BaseConfiguration c, List<? extends DocTree> body) { |
| List<DocTree> firstSentence = c.docEnv.getDocTrees().getFirstSentence(body); |
| return firstSentence; |
| } |
| |
| public List<? extends DocTree> getFirstSentenceTrees(BaseConfiguration c, DocTree dtree) { |
| return getFirstSentenceTrees(c, getBody(c, dtree)); |
| } |
| |
| private Element getReferencedElement(BaseConfiguration c, DocTree dtree) { |
| return new SimpleDocTreeVisitor<Element, Void>() { |
| @Override |
| public Element visitSee(SeeTree node, Void p) { |
| for (DocTree dt : node.getReference()) { |
| return visit(dt, null); |
| } |
| return null; |
| } |
| |
| @Override |
| public Element visitLink(LinkTree node, Void p) { |
| return visit(node.getReference(), null); |
| } |
| |
| @Override |
| public Element visitProvides(ProvidesTree node, Void p) { |
| return visit(node.getServiceType(), null); |
| } |
| |
| @Override |
| public Element visitValue(ValueTree node, Void p) { |
| return visit(node.getReference(), null); |
| } |
| |
| @Override |
| public Element visitReference(ReferenceTree node, Void p) { |
| return getElement(c, node); |
| } |
| |
| @Override |
| public Element visitSerialField(SerialFieldTree node, Void p) { |
| return visit(node.getType(), null); |
| } |
| |
| @Override |
| public Element visitUses(UsesTree node, Void p) { |
| return visit(node.getServiceType(), null); |
| } |
| |
| @Override |
| protected Element defaultAction(DocTree node, Void p) { |
| return null; |
| } |
| }.visit(dtree, null); |
| } |
| |
| public TypeElement getServiceType(BaseConfiguration c, DocTree dtree) { |
| Element e = getReferencedElement(c, dtree); |
| if (e != null) { |
| return c.utils.isTypeElement(e) ? (TypeElement) e : null; |
| } |
| return null; |
| } |
| |
| public String getReferencedSignature(DocTree dtree) { |
| return new SimpleDocTreeVisitor<String, Void>() { |
| @Override |
| public String visitSee(SeeTree node, Void p) { |
| for (DocTree dt : node.getReference()) { |
| return visit(dt, null); |
| } |
| return null; |
| } |
| |
| @Override |
| public String visitLink(LinkTree node, Void p) { |
| return visit(node.getReference(), null); |
| } |
| |
| @Override |
| public String visitValue(ValueTree node, Void p) { |
| return visit(node.getReference(), null); |
| } |
| |
| @Override |
| public String visitReference(ReferenceTree node, Void p) { |
| return node.getSignature(); |
| } |
| |
| @Override |
| public String visitSerialField(SerialFieldTree node, Void p) { |
| return visit(node.getType(), null); |
| } |
| |
| @Override |
| protected String defaultAction(DocTree node, Void p) { |
| return null; |
| } |
| }.visit(dtree, null); |
| } |
| |
| public List<? extends DocTree> getReference(DocTree dtree) { |
| return dtree.getKind() == SEE ? ((SeeTree)dtree).getReference() : null; |
| } |
| |
| public ReferenceTree getExceptionName(DocTree dtree) { |
| return (dtree.getKind() == THROWS || dtree.getKind() == EXCEPTION) |
| ? ((ThrowsTree)dtree).getExceptionName() |
| : null; |
| } |
| |
| public IdentifierTree getName(DocTree dtree) { |
| switch (dtree.getKind()) { |
| case PARAM: |
| return ((ParamTree)dtree).getName(); |
| case SERIAL_FIELD: |
| return ((SerialFieldTree)dtree).getName(); |
| default: |
| return null; |
| } |
| } |
| |
| public List<? extends DocTree> getTags(BaseConfiguration c, DocTree dtree) { |
| return new SimpleDocTreeVisitor<List<? extends DocTree>, Void>() { |
| List<? extends DocTree> asList(String content) { |
| List<DocTree> out = new ArrayList<>(); |
| out.add((TextTree)c.cmtUtils.makeTextTree(content)); |
| return out; |
| } |
| |
| @Override |
| public List<? extends DocTree> visitAuthor(AuthorTree node, Void p) { |
| return node.getName(); |
| } |
| |
| @Override |
| public List<? extends DocTree> visitComment(CommentTree node, Void p) { |
| return asList(node.getBody()); |
| } |
| |
| @Override |
| public List<? extends DocTree> visitDeprecated(DeprecatedTree node, Void p) { |
| return node.getBody(); |
| } |
| |
| @Override |
| public List<? extends DocTree> visitDocComment(DocCommentTree node, Void p) { |
| return node.getBody(); |
| } |
| |
| @Override |
| public List<? extends DocTree> visitLiteral(LiteralTree node, Void p) { |
| return asList(node.getBody().getBody()); |
| } |
| |
| @Override |
| public List<? extends DocTree> visitProvides(ProvidesTree node, Void p) { |
| return node.getDescription(); |
| } |
| |
| @Override |
| public List<? extends DocTree> visitSince(SinceTree node, Void p) { |
| return node.getBody(); |
| } |
| |
| @Override |
| public List<? extends DocTree> visitText(TextTree node, Void p) { |
| return asList(node.getBody()); |
| } |
| |
| @Override |
| public List<? extends DocTree> visitVersion(VersionTree node, Void p) { |
| return node.getBody(); |
| } |
| |
| @Override |
| public List<? extends DocTree> visitParam(ParamTree node, Void p) { |
| return node.getDescription(); |
| } |
| |
| @Override |
| public List<? extends DocTree> visitReturn(ReturnTree node, Void p) { |
| return node.getDescription(); |
| } |
| |
| @Override |
| public List<? extends DocTree> visitSee(SeeTree node, Void p) { |
| return node.getReference(); |
| } |
| |
| @Override |
| public List<? extends DocTree> visitSerial(SerialTree node, Void p) { |
| return node.getDescription(); |
| } |
| |
| @Override |
| public List<? extends DocTree> visitSerialData(SerialDataTree node, Void p) { |
| return node.getDescription(); |
| } |
| |
| @Override |
| public List<? extends DocTree> visitSerialField(SerialFieldTree node, Void p) { |
| return node.getDescription(); |
| } |
| |
| @Override |
| public List<? extends DocTree> visitThrows(ThrowsTree node, Void p) { |
| return node.getDescription(); |
| } |
| |
| @Override |
| public List<? extends DocTree> visitUnknownBlockTag(UnknownBlockTagTree node, Void p) { |
| return node.getContent(); |
| } |
| |
| @Override |
| public List<? extends DocTree> visitUses(UsesTree node, Void p) { |
| return node.getDescription(); |
| } |
| |
| @Override |
| protected List<? extends DocTree> defaultAction(DocTree node, Void p) { |
| return Collections.emptyList(); |
| } |
| }.visit(dtree, null); |
| } |
| |
| public List<? extends DocTree> getBody(BaseConfiguration c, DocTree dtree) { |
| return getTags(c, dtree); |
| } |
| |
| public ReferenceTree getType(DocTree dtree) { |
| if (dtree.getKind() == SERIAL_FIELD) { |
| return ((SerialFieldTree)dtree).getType(); |
| } else { |
| return null; |
| } |
| } |
| |
| public DocTreePath getDocTreePath(DocTree dtree) { |
| if (path == null || dctree == null || dtree == null) |
| return null; |
| return DocTreePath.getPath(path, dctree, dtree); |
| } |
| |
| public Element getOverriddenElement() { |
| return overriddenElement; |
| } |
| |
| |
| /** |
| * For debugging purposes only. Do not rely on this for other things. |
| * @return a string representation. |
| */ |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder("CommentHelper{" + "path=" + path + ", dctree=" + dctree); |
| sb.append(", element="); |
| sb.append(element.getEnclosingElement()); |
| sb.append("::"); |
| sb.append(element); |
| sb.append(", overriddenElement="); |
| if (overriddenElement != null) { |
| sb.append(overriddenElement.getEnclosingElement()); |
| sb.append("::"); |
| sb.append(overriddenElement); |
| } else { |
| sb.append("<none>"); |
| } |
| sb.append('}'); |
| return sb.toString(); |
| } |
| } |