1 /*
2  * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package jdk.javadoc.internal.doclets.toolkit.util;
27 
28 import java.util.*;
29 
30 import javax.lang.model.element.Element;
31 import javax.lang.model.element.ExecutableElement;
32 import javax.lang.model.element.TypeElement;
33 import javax.lang.model.type.TypeMirror;
34 
35 import com.sun.source.doctree.DocTree;
36 import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
37 import jdk.javadoc.internal.doclets.toolkit.taglets.InheritableTaglet;
38 
39 /**
40  * Search for the requested documentation.  Inherit documentation if necessary.
41  *
42  *  <p><b>This is NOT part of any supported API.
43  *  If you write code that depends on this, you do so at your own risk.
44  *  This code and its internal interfaces are subject to change or
45  *  deletion without notice.</b>
46  */
47 public class DocFinder {
48 
49     public static final class DocTreeInfo {
50         public final DocTree docTree;
51         public final Element element;
52 
DocTreeInfo()53         public DocTreeInfo() {
54             this.docTree = null;
55             this.element = null;
56         }
57 
DocTreeInfo(DocTree docTree, Element baseElement)58         public DocTreeInfo(DocTree docTree, Element baseElement) {
59             this.docTree = docTree;
60             this.element = baseElement;
61         }
62 
63         @Override
toString()64         public String toString() {
65             return "DocTreeInfo{" + "docTree=" + docTree + ", element=" + element + '}';
66         }
67     }
68 
69     /**
70      * The class that encapsulates the input.
71      */
72     public static class Input {
73         /**
74          * The element to search documentation from.
75          */
76         public Element element;
77         /**
78          * The taglet to search for documentation on behalf of. Null if we want
79          * to search for overall documentation.
80          */
81         public InheritableTaglet taglet = null;
82 
83         /**
84          * The id of the tag to retrieve documentation for.
85          */
86         public String tagId = null;
87 
88         /**
89          * The tag to retrieve documentation for.  This is only used for the
90          * inheritDoc tag.
91          */
92         public final DocTreeInfo docTreeInfo;
93 
94         /**
95          * True if we only want to search for the first sentence.
96          */
97         public boolean isFirstSentence = false;
98 
99         /**
100          * True if we are looking for documentation to replace the inheritDocTag.
101          */
102         public boolean isInheritDocTag = false;
103 
104         /**
105          * Used to distinguish between type variable param tags and regular
106          * param tags.
107          */
108         public boolean isTypeVariableParamTag = false;
109 
110         public final Utils utils;
111 
Input(Utils utils, Element element, InheritableTaglet taglet, DocTreeInfo dtInfo, boolean isFirstSentence, boolean isInheritDocTag)112         public Input(Utils utils, Element element, InheritableTaglet taglet, DocTreeInfo dtInfo,
113                 boolean isFirstSentence, boolean isInheritDocTag) {
114             this.utils = utils;
115             this.element = element;
116             this.taglet = taglet;
117             this.isFirstSentence = isFirstSentence;
118             this.isInheritDocTag = isInheritDocTag;
119             this.docTreeInfo = dtInfo;
120         }
121 
Input(Utils utils, Element element, InheritableTaglet taglet, String tagId)122         public Input(Utils utils, Element element, InheritableTaglet taglet, String tagId) {
123             this(utils, element);
124             this.taglet = taglet;
125             this.tagId = tagId;
126         }
127 
Input(Utils utils, Element element, InheritableTaglet taglet, String tagId, boolean isTypeVariableParamTag)128         public Input(Utils utils, Element element, InheritableTaglet taglet, String tagId,
129             boolean isTypeVariableParamTag) {
130             this(utils, element);
131             this.taglet = taglet;
132             this.tagId = tagId;
133             this.isTypeVariableParamTag = isTypeVariableParamTag;
134         }
135 
Input(Utils utils, Element element, InheritableTaglet taglet)136         public Input(Utils utils, Element element, InheritableTaglet taglet) {
137             this(utils, element);
138             this.taglet = taglet;
139         }
140 
Input(Utils utils, Element element)141         public Input(Utils utils, Element element) {
142             if (element == null)
143                 throw new NullPointerException();
144             this.element = element;
145             this.utils = utils;
146             this.docTreeInfo = new DocTreeInfo();
147         }
148 
Input(Utils utils, Element element, boolean isFirstSentence)149         public Input(Utils utils, Element element, boolean isFirstSentence) {
150             this(utils, element);
151             this.isFirstSentence = isFirstSentence;
152         }
153 
copy(Utils utils)154         public Input copy(Utils utils) {
155             if (this.element == null) {
156                 throw new NullPointerException();
157             }
158             Input clone = new Input(utils, this.element, this.taglet, this.docTreeInfo,
159                     this.isFirstSentence, this.isInheritDocTag);
160             clone.tagId = this.tagId;
161             clone.isTypeVariableParamTag = this.isTypeVariableParamTag;
162             return clone;
163         }
164 
165         /**
166          * For debugging purposes
167          * @return string representation
168          */
169         @Override
toString()170         public String toString() {
171             String encl = element == null ? "" : element.getEnclosingElement().toString() + "::";
172             return "Input{" + "element=" + encl + element
173                     + ", taglet=" + taglet
174                     + ", tagId=" + tagId + ", tag=" + docTreeInfo
175                     + ", isFirstSentence=" + isFirstSentence
176                     + ", isInheritDocTag=" + isInheritDocTag
177                     + ", isTypeVariableParamTag=" + isTypeVariableParamTag
178                     + ", utils=" + utils + '}';
179         }
180     }
181 
182     /**
183      * The class that encapsulates the output.
184      */
185     public static class Output {
186         /**
187          * The tag that holds the documentation.  Null if documentation
188          * is not held by a tag.
189          */
190         public DocTree holderTag;
191 
192         /**
193          * The Doc object that holds the documentation.
194          */
195         public Element holder;
196 
197         /**
198          * The inherited documentation.
199          */
200         public List<? extends DocTree> inlineTags = Collections.emptyList();
201 
202         /**
203          * False if documentation could not be inherited.
204          */
205         public boolean isValidInheritDocTag = true;
206 
207         /**
208          * When automatically inheriting throws tags, you sometime must inherit
209          * more than one tag.  For example if the element declares that it throws
210          * IOException and the overridden element has throws tags for IOException and
211          * ZipException, both tags would be inherited because ZipException is a
212          * subclass of IOException.  This subclass of DocFinder.Output allows
213          * multiple tag inheritance.
214          */
215         public List<DocTree> tagList  = new ArrayList<>();
216 
217         /**
218          * Returns a string representation for debugging purposes
219          * @return string
220          */
221         @Override
toString()222         public String toString() {
223             String encl = holder == null ? "" : holder.getEnclosingElement().toString() + "::";
224             return "Output{" + "holderTag=" + holderTag
225                     + ", holder=" + encl + holder
226                     + ", inlineTags=" + inlineTags
227                     + ", isValidInheritDocTag=" + isValidInheritDocTag
228                     + ", tagList=" + tagList + '}';
229         }
230     }
231 
232     /**
233      * Search for the requested comments in the given element.  If it does not
234      * have comments, return documentation from the overridden element if possible.
235      * If the overridden element does not exist or does not have documentation to
236      * inherit, search for documentation to inherit from implemented methods.
237      *
238      * @param input the input object used to perform the search.
239      *
240      * @return an Output object representing the documentation that was found.
241      */
search(BaseConfiguration configuration, Input input)242     public static Output search(BaseConfiguration configuration, Input input) {
243         Output output = new Output();
244         Utils utils = configuration.utils;
245         if (input.isInheritDocTag) {
246             //Do nothing because "element" does not have any documentation.
247             //All it has is {@inheritDoc}.
248         } else if (input.taglet == null) {
249             //We want overall documentation.
250             output.inlineTags = input.isFirstSentence
251                     ? utils.getFirstSentenceTrees(input.element)
252                     : utils.getFullBody(input.element);
253             output.holder = input.element;
254         } else {
255             input.taglet.inherit(input, output);
256         }
257 
258         if (output.inlineTags != null && !output.inlineTags.isEmpty()) {
259             return output;
260         }
261         output.isValidInheritDocTag = false;
262         Input inheritedSearchInput = input.copy(configuration.utils);
263         inheritedSearchInput.isInheritDocTag = false;
264         if (utils.isMethod(input.element)) {
265             ExecutableElement overriddenMethod = utils.overriddenMethod((ExecutableElement) input.element);
266             if (overriddenMethod != null) {
267                 inheritedSearchInput.element = overriddenMethod;
268                 output = search(configuration, inheritedSearchInput);
269                 output.isValidInheritDocTag = true;
270                 if (!output.inlineTags.isEmpty()) {
271                     return output;
272                 }
273             }
274             //NOTE:  When we fix the bug where ClassDoc.interfaceTypes() does
275             //       not pass all implemented interfaces, we will use the
276             //       appropriate element here.
277             TypeElement encl = utils.getEnclosingTypeElement(input.element);
278             VisibleMemberTable vmt = configuration.getVisibleMemberTable(encl);
279             List<ExecutableElement> implementedMethods =
280                     vmt.getImplementedMethods((ExecutableElement)input.element);
281             for (ExecutableElement implementedMethod : implementedMethods) {
282                 inheritedSearchInput.element = implementedMethod;
283                 output = search(configuration, inheritedSearchInput);
284                 output.isValidInheritDocTag = true;
285                 if (!output.inlineTags.isEmpty()) {
286                     return output;
287                 }
288             }
289         } else if (utils.isTypeElement(input.element)) {
290             TypeMirror t = ((TypeElement) input.element).getSuperclass();
291             Element superclass = utils.asTypeElement(t);
292             if (superclass != null) {
293                 inheritedSearchInput.element = superclass;
294                 output = search(configuration, inheritedSearchInput);
295                 output.isValidInheritDocTag = true;
296                 if (!output.inlineTags.isEmpty()) {
297                     return output;
298                 }
299             }
300         }
301         return output;
302     }
303 }
304