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