1/* 2 * Copyright (c) 2001, 2016, 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 26package jdk.javadoc.internal.doclets.toolkit.taglets; 27 28import java.util.*; 29 30import javax.lang.model.element.Element; 31import javax.lang.model.element.ExecutableElement; 32import javax.lang.model.element.TypeElement; 33 34import com.sun.source.doctree.DocTree; 35import com.sun.source.doctree.ParamTree; 36import jdk.javadoc.internal.doclets.toolkit.Content; 37import jdk.javadoc.internal.doclets.toolkit.Messages; 38import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; 39import jdk.javadoc.internal.doclets.toolkit.util.DocFinder; 40import jdk.javadoc.internal.doclets.toolkit.util.DocFinder.Input; 41import jdk.javadoc.internal.doclets.toolkit.util.Utils; 42 43import static com.sun.source.doctree.DocTree.Kind.*; 44 45/** 46 * A taglet that represents the @param tag. 47 * 48 * <p><b>This is NOT part of any supported API. 49 * If you write code that depends on this, you do so at your own risk. 50 * This code and its internal interfaces are subject to change or 51 * deletion without notice.</b> 52 * 53 * @author Jamie Ho 54 */ 55public class ParamTaglet extends BaseTaglet implements InheritableTaglet { 56 57 /** 58 * Construct a ParamTaglet. 59 */ 60 public ParamTaglet() { 61 name = PARAM.tagName; 62 } 63 64 /** 65 * Given an array of <code>Parameter</code>s, return 66 * a name/rank number map. If the array is null, then 67 * null is returned. 68 * @param params The array of parameters (from type or executable member) to 69 * check. 70 * @return a name-rank number map. 71 */ 72 private static Map<String, String> getRankMap(Utils utils, List<? extends Element> params){ 73 if (params == null) { 74 return null; 75 } 76 HashMap<String, String> result = new HashMap<>(); 77 int rank = 0; 78 for (Element e : params) { 79 String name = utils.isTypeParameterElement(e) 80 ? utils.getTypeName(e.asType(), false) 81 : utils.getSimpleName(e); 82 result.put(name, String.valueOf(rank)); 83 rank++; 84 } 85 return result; 86 } 87 88 /** 89 * {@inheritDoc} 90 */ 91 public void inherit(DocFinder.Input input, DocFinder.Output output) { 92 Utils utils = input.utils; 93 if (input.tagId == null) { 94 input.isTypeVariableParamTag = ((ParamTree)input.docTreeInfo.docTree).isTypeParameter(); 95 ExecutableElement ee = (ExecutableElement)input.docTreeInfo.element; 96 CommentHelper ch = utils.getCommentHelper(ee); 97 List<? extends Element> parameters = input.isTypeVariableParamTag 98 ? ee.getTypeParameters() 99 : ee.getParameters(); 100 String target = ch.getParameterName(input.docTreeInfo.docTree); 101 for (int i = 0 ; i < parameters.size(); i++) { 102 Element e = parameters.get(i); 103 String pname = input.isTypeVariableParamTag 104 ? utils.getTypeName(e.asType(), false) 105 : utils.getSimpleName(e); 106 if (pname.equals(target)) { 107 input.tagId = String.valueOf(i); 108 break; 109 } 110 } 111 } 112 ExecutableElement md = (ExecutableElement)input.element; 113 CommentHelper ch = utils.getCommentHelper(md); 114 List<? extends DocTree> tags = input.isTypeVariableParamTag 115 ? utils.getTypeParamTrees(md) 116 : utils.getParamTrees(md); 117 List<? extends Element> parameters = input.isTypeVariableParamTag 118 ? md.getTypeParameters() 119 : md.getParameters(); 120 Map<String, String> rankMap = getRankMap(utils, parameters); 121 for (DocTree tag : tags) { 122 String paramName = ch.getParameterName(tag); 123 if (rankMap.containsKey(paramName) && rankMap.get(paramName).equals((input.tagId))) { 124 output.holder = input.element; 125 output.holderTag = tag; 126 output.inlineTags = ch.getBody(utils.configuration, tag); 127 return; 128 } 129 } 130 } 131 132 /** 133 * {@inheritDoc} 134 */ 135 public boolean inField() { 136 return false; 137 } 138 139 /** 140 * {@inheritDoc} 141 */ 142 public boolean inMethod() { 143 return true; 144 } 145 146 /** 147 * {@inheritDoc} 148 */ 149 public boolean inOverview() { 150 return false; 151 } 152 153 /** 154 * {@inheritDoc} 155 */ 156 public boolean inModule() { 157 return false; 158 } 159 160 /** 161 * {@inheritDoc} 162 */ 163 public boolean inPackage() { 164 return false; 165 } 166 167 /** 168 * {@inheritDoc} 169 */ 170 public boolean inType() { 171 return true; 172 } 173 174 /** 175 * {@inheritDoc} 176 */ 177 public boolean isInlineTag() { 178 return false; 179 } 180 181 /** 182 * Given an array of <code>ParamTag</code>s,return its string representation. 183 * @param holder the member that holds the param tags. 184 * @param writer the TagletWriter that will write this tag. 185 * @return the TagletOutput representation of these <code>ParamTag</code>s. 186 */ 187 public Content getTagletOutput(Element holder, TagletWriter writer) { 188 Utils utils = writer.configuration().utils; 189 if (utils.isExecutableElement(holder)) { 190 ExecutableElement member = (ExecutableElement) holder; 191 Content output = getTagletOutput(false, member, writer, 192 member.getTypeParameters(), utils.getTypeParamTrees(member)); 193 output.addContent(getTagletOutput(true, member, writer, 194 member.getParameters(), utils.getParamTrees(member))); 195 return output; 196 } else { 197 TypeElement typeElement = (TypeElement) holder; 198 return getTagletOutput(false, typeElement, writer, 199 typeElement.getTypeParameters(), utils.getTypeParamTrees(typeElement)); 200 } 201 } 202 203 /** 204 * Given an array of <code>ParamTag</code>s,return its string representation. 205 * Try to inherit the param tags that are missing. 206 * 207 * @param holder the element that holds the param tags. 208 * @param writer the TagletWriter that will write this tag. 209 * @param formalParameters The array of parmeters (from type or executable 210 * member) to check. 211 * 212 * @return the TagletOutput representation of these <code>ParamTag</code>s. 213 */ 214 private Content getTagletOutput(boolean isParameters, Element holder, 215 TagletWriter writer, List<? extends Element> formalParameters, List<? extends DocTree> paramTags) { 216 Content result = writer.getOutputInstance(); 217 Set<String> alreadyDocumented = new HashSet<>(); 218 if (!paramTags.isEmpty()) { 219 result.addContent( 220 processParamTags(holder, isParameters, paramTags, 221 getRankMap(writer.configuration().utils, formalParameters), writer, alreadyDocumented) 222 ); 223 } 224 if (alreadyDocumented.size() != formalParameters.size()) { 225 //Some parameters are missing corresponding @param tags. 226 //Try to inherit them. 227 result.addContent(getInheritedTagletOutput(isParameters, holder, 228 writer, formalParameters, alreadyDocumented)); 229 } 230 return result; 231 } 232 233 /** 234 * Loop through each individual parameter, despite not having a 235 * corresponding param tag, try to inherit it. 236 */ 237 private Content getInheritedTagletOutput(boolean isParameters, Element holder, 238 TagletWriter writer, List<? extends Element> formalParameters, 239 Set<String> alreadyDocumented) { 240 Utils utils = writer.configuration().utils; 241 Content result = writer.getOutputInstance(); 242 if ((!alreadyDocumented.contains(null)) && utils.isExecutableElement(holder)) { 243 for (int i = 0; i < formalParameters.size(); i++) { 244 if (alreadyDocumented.contains(String.valueOf(i))) { 245 continue; 246 } 247 // This parameter does not have any @param documentation. 248 // Try to inherit it. 249 Input input = new DocFinder.Input(writer.configuration().utils, holder, this, 250 Integer.toString(i), !isParameters); 251 DocFinder.Output inheritedDoc = DocFinder.search(writer.configuration(), input); 252 if (inheritedDoc.inlineTags != null && !inheritedDoc.inlineTags.isEmpty()) { 253 Element e = formalParameters.get(i); 254 String lname = isParameters 255 ? utils.getSimpleName(e) 256 : utils.getTypeName(e.asType(), false); 257 CommentHelper ch = utils.getCommentHelper(holder); 258 ch.setOverrideElement(inheritedDoc.holder); 259 Content content = processParamTag(holder, isParameters, writer, 260 inheritedDoc.holderTag, 261 lname, 262 alreadyDocumented.isEmpty()); 263 result.addContent(content); 264 } 265 alreadyDocumented.add(String.valueOf(i)); 266 } 267 } 268 return result; 269 } 270 271 /** 272 * Given an array of <code>Tag</code>s representing this custom 273 * tag, return its string representation. Print a warning for param 274 * tags that do not map to parameters. Print a warning for param 275 * tags that are duplicated. 276 * 277 * @param paramTags the array of <code>ParamTag</code>s to convert. 278 * @param writer the TagletWriter that will write this tag. 279 * @param alreadyDocumented the set of exceptions that have already 280 * been documented. 281 * @param rankMap a {@link java.util.Map} which holds ordering 282 * information about the parameters. 283 * @param rankMap a {@link java.util.Map} which holds a mapping 284 of a rank of a parameter to its name. This is 285 used to ensure that the right name is used 286 when parameter documentation is inherited. 287 * @return the Content representation of this <code>Tag</code>. 288 */ 289 private Content processParamTags(Element e, boolean isParams, 290 List<? extends DocTree> paramTags, Map<String, String> rankMap, TagletWriter writer, 291 Set<String> alreadyDocumented) { 292 Messages messages = writer.configuration().getMessages(); 293 Content result = writer.getOutputInstance(); 294 if (!paramTags.isEmpty()) { 295 CommentHelper ch = writer.configuration().utils.getCommentHelper(e); 296 for (DocTree dt : paramTags) { 297 String paramName = isParams 298 ? ch.getParameterName(dt) 299 : "<" + ch.getParameterName(dt) + ">"; 300 if (!rankMap.containsKey(ch.getParameterName(dt))) { 301 messages.warning(ch.getDocTreePath(dt), 302 isParams 303 ? "doclet.Parameters_warn" 304 : "doclet.Type_Parameters_warn", 305 paramName); 306 } 307 String rank = rankMap.get(ch.getParameterName(dt)); 308 if (rank != null && alreadyDocumented.contains(rank)) { 309 messages.warning(ch.getDocTreePath(dt), 310 isParams 311 ? "doclet.Parameters_dup_warn" 312 : "doclet.Type_Parameters_dup_warn", 313 paramName); 314 } 315 result.addContent(processParamTag(e, isParams, writer, dt, 316 ch.getParameterName(dt), alreadyDocumented.isEmpty())); 317 alreadyDocumented.add(rank); 318 } 319 } 320 return result; 321 } 322 323 /** 324 * Convert the individual ParamTag into Content. 325 * 326 * @param isNonTypeParams true if this is just a regular param tag. False 327 * if this is a type param tag. 328 * @param writer the taglet writer for output writing. 329 * @param paramTag the tag whose inline tags will be printed. 330 * @param name the name of the parameter. We can't rely on 331 * the name in the param tag because we might be 332 * inheriting documentation. 333 * @param isFirstParam true if this is the first param tag being printed. 334 * 335 */ 336 private Content processParamTag(Element e, boolean isParams, 337 TagletWriter writer, DocTree paramTag, String name, 338 boolean isFirstParam) { 339 Content result = writer.getOutputInstance(); 340 String header = writer.configuration().getText( 341 isParams ? "doclet.Parameters" : "doclet.TypeParameters"); 342 if (isFirstParam) { 343 result.addContent(writer.getParamHeader(header)); 344 } 345 result.addContent(writer.paramTagOutput(e, paramTag, name)); 346 return result; 347 } 348} 349