Thu, 31 Aug 2017 15:17:03 +0800
merge
1 /*
2 * Copyright (c) 2001, 2013, 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 */
26 package com.sun.tools.doclets.internal.toolkit.taglets;
28 import java.util.*;
30 import com.sun.javadoc.*;
31 import com.sun.tools.doclets.internal.toolkit.Content;
32 import com.sun.tools.doclets.internal.toolkit.util.*;
34 /**
35 * A taglet that represents the @param tag.
36 *
37 * <p><b>This is NOT part of any supported API.
38 * If you write code that depends on this, you do so at your own risk.
39 * This code and its internal interfaces are subject to change or
40 * deletion without notice.</b>
41 *
42 * @author Jamie Ho
43 * @since 1.4
44 */
45 public class ParamTaglet extends BaseTaglet implements InheritableTaglet {
47 /**
48 * Construct a ParamTaglet.
49 */
50 public ParamTaglet() {
51 name = "param";
52 }
54 /**
55 * Given an array of <code>Parameter</code>s, return
56 * a name/rank number map. If the array is null, then
57 * null is returned.
58 * @param params The array of parmeters (from type or executable member) to
59 * check.
60 * @return a name-rank number map.
61 */
62 private static Map<String,String> getRankMap(Object[] params){
63 if (params == null) {
64 return null;
65 }
66 HashMap<String,String> result = new HashMap<String,String>();
67 for (int i = 0; i < params.length; i++) {
68 String name = params[i] instanceof Parameter ?
69 ((Parameter) params[i]).name() :
70 ((TypeVariable) params[i]).typeName();
71 result.put(name, String.valueOf(i));
72 }
73 return result;
74 }
76 /**
77 * {@inheritDoc}
78 */
79 public void inherit(DocFinder.Input input, DocFinder.Output output) {
80 if (input.tagId == null) {
81 input.isTypeVariableParamTag = ((ParamTag) input.tag).isTypeParameter();
82 Object[] parameters = input.isTypeVariableParamTag ?
83 (Object[]) ((MethodDoc) input.tag.holder()).typeParameters() :
84 (Object[]) ((MethodDoc) input.tag.holder()).parameters();
85 String target = ((ParamTag) input.tag).parameterName();
86 int i;
87 for (i = 0; i < parameters.length; i++) {
88 String name = parameters[i] instanceof Parameter ?
89 ((Parameter) parameters[i]).name() :
90 ((TypeVariable) parameters[i]).typeName();
91 if (name.equals(target)) {
92 input.tagId = String.valueOf(i);
93 break;
94 }
95 }
96 if (i == parameters.length) {
97 //Someone used {@inheritDoc} on an invalid @param tag.
98 //We don't know where to inherit from.
99 //XXX: in the future when Configuration is available here,
100 //print a warning for this mistake.
101 return;
102 }
103 }
104 ParamTag[] tags = input.isTypeVariableParamTag ?
105 ((MethodDoc)input.element).typeParamTags() : ((MethodDoc)input.element).paramTags();
106 Map<String, String> rankMap = getRankMap(input.isTypeVariableParamTag ?
107 (Object[]) ((MethodDoc)input.element).typeParameters() :
108 (Object[]) ((MethodDoc)input.element).parameters());
109 for (int i = 0; i < tags.length; i++) {
110 if (rankMap.containsKey(tags[i].parameterName()) &&
111 rankMap.get(tags[i].parameterName()).equals((input.tagId))) {
112 output.holder = input.element;
113 output.holderTag = tags[i];
114 output.inlineTags = input.isFirstSentence ?
115 tags[i].firstSentenceTags() : tags[i].inlineTags();
116 return;
117 }
118 }
119 }
121 /**
122 * {@inheritDoc}
123 */
124 public boolean inField() {
125 return false;
126 }
128 /**
129 * {@inheritDoc}
130 */
131 public boolean inMethod() {
132 return true;
133 }
135 /**
136 * {@inheritDoc}
137 */
138 public boolean inOverview() {
139 return false;
140 }
142 /**
143 * {@inheritDoc}
144 */
145 public boolean inPackage() {
146 return false;
147 }
149 /**
150 * {@inheritDoc}
151 */
152 public boolean inType() {
153 return true;
154 }
156 /**
157 * {@inheritDoc}
158 */
159 public boolean isInlineTag() {
160 return false;
161 }
163 /**
164 * Given an array of <code>ParamTag</code>s,return its string representation.
165 * @param holder the member that holds the param tags.
166 * @param writer the TagletWriter that will write this tag.
167 * @return the TagletOutput representation of these <code>ParamTag</code>s.
168 */
169 public Content getTagletOutput(Doc holder, TagletWriter writer) {
170 if (holder instanceof ExecutableMemberDoc) {
171 ExecutableMemberDoc member = (ExecutableMemberDoc) holder;
172 Content output = getTagletOutput(false, member, writer,
173 member.typeParameters(), member.typeParamTags());
174 output.addContent(getTagletOutput(true, member, writer,
175 member.parameters(), member.paramTags()));
176 return output;
177 } else {
178 ClassDoc classDoc = (ClassDoc) holder;
179 return getTagletOutput(false, classDoc, writer,
180 classDoc.typeParameters(), classDoc.typeParamTags());
181 }
182 }
184 /**
185 * Given an array of <code>ParamTag</code>s,return its string representation.
186 * Try to inherit the param tags that are missing.
187 *
188 * @param holder the doc that holds the param tags.
189 * @param writer the TagletWriter that will write this tag.
190 * @param formalParameters The array of parmeters (from type or executable
191 * member) to check.
192 *
193 * @return the TagletOutput representation of these <code>ParamTag</code>s.
194 */
195 private Content getTagletOutput(boolean isNonTypeParams, Doc holder,
196 TagletWriter writer, Object[] formalParameters, ParamTag[] paramTags) {
197 Content result = writer.getOutputInstance();
198 Set<String> alreadyDocumented = new HashSet<String>();
199 if (paramTags.length > 0) {
200 result.addContent(
201 processParamTags(isNonTypeParams, paramTags,
202 getRankMap(formalParameters), writer, alreadyDocumented)
203 );
204 }
205 if (alreadyDocumented.size() != formalParameters.length) {
206 //Some parameters are missing corresponding @param tags.
207 //Try to inherit them.
208 result.addContent(getInheritedTagletOutput (isNonTypeParams, holder,
209 writer, formalParameters, alreadyDocumented));
210 }
211 return result;
212 }
214 /**
215 * Loop through each indivitual parameter. It it does not have a
216 * corresponding param tag, try to inherit it.
217 */
218 private Content getInheritedTagletOutput(boolean isNonTypeParams, Doc holder,
219 TagletWriter writer, Object[] formalParameters,
220 Set<String> alreadyDocumented) {
221 Content result = writer.getOutputInstance();
222 if ((! alreadyDocumented.contains(null)) &&
223 holder instanceof MethodDoc) {
224 for (int i = 0; i < formalParameters.length; i++) {
225 if (alreadyDocumented.contains(String.valueOf(i))) {
226 continue;
227 }
228 //This parameter does not have any @param documentation.
229 //Try to inherit it.
230 DocFinder.Output inheritedDoc =
231 DocFinder.search(new DocFinder.Input((MethodDoc) holder, this,
232 String.valueOf(i), ! isNonTypeParams));
233 if (inheritedDoc.inlineTags != null &&
234 inheritedDoc.inlineTags.length > 0) {
235 result.addContent(
236 processParamTag(isNonTypeParams, writer,
237 (ParamTag) inheritedDoc.holderTag,
238 isNonTypeParams ?
239 ((Parameter) formalParameters[i]).name():
240 ((TypeVariable) formalParameters[i]).typeName(),
241 alreadyDocumented.size() == 0));
242 }
243 alreadyDocumented.add(String.valueOf(i));
244 }
245 }
246 return result;
247 }
249 /**
250 * Given an array of <code>Tag</code>s representing this custom
251 * tag, return its string representation. Print a warning for param
252 * tags that do not map to parameters. Print a warning for param
253 * tags that are duplicated.
254 *
255 * @param paramTags the array of <code>ParamTag</code>s to convert.
256 * @param writer the TagletWriter that will write this tag.
257 * @param alreadyDocumented the set of exceptions that have already
258 * been documented.
259 * @param rankMap a {@link java.util.Map} which holds ordering
260 * information about the parameters.
261 * @param rankMap a {@link java.util.Map} which holds a mapping
262 * of a rank of a parameter to its name. This is
263 * used to ensure that the right name is used
264 * when parameter documentation is inherited.
265 * @return the Content representation of this <code>Tag</code>.
266 */
267 private Content processParamTags(boolean isNonTypeParams,
268 ParamTag[] paramTags, Map<String, String> rankMap, TagletWriter writer,
269 Set<String> alreadyDocumented) {
270 Content result = writer.getOutputInstance();
271 if (paramTags.length > 0) {
272 for (int i = 0; i < paramTags.length; ++i) {
273 ParamTag pt = paramTags[i];
274 String paramName = isNonTypeParams ?
275 pt.parameterName() : "<" + pt.parameterName() + ">";
276 if (! rankMap.containsKey(pt.parameterName())) {
277 writer.getMsgRetriever().warning(pt.position(),
278 isNonTypeParams ?
279 "doclet.Parameters_warn" :
280 "doclet.Type_Parameters_warn",
281 paramName);
282 }
283 String rank = rankMap.get(pt.parameterName());
284 if (rank != null && alreadyDocumented.contains(rank)) {
285 writer.getMsgRetriever().warning(pt.position(),
286 isNonTypeParams ?
287 "doclet.Parameters_dup_warn" :
288 "doclet.Type_Parameters_dup_warn",
289 paramName);
290 }
291 result.addContent(processParamTag(isNonTypeParams, writer, pt,
292 pt.parameterName(), alreadyDocumented.size() == 0));
293 alreadyDocumented.add(rank);
294 }
295 }
296 return result;
297 }
298 /**
299 * Convert the individual ParamTag into Content.
300 *
301 * @param isNonTypeParams true if this is just a regular param tag. False
302 * if this is a type param tag.
303 * @param writer the taglet writer for output writing.
304 * @param paramTag the tag whose inline tags will be printed.
305 * @param name the name of the parameter. We can't rely on
306 * the name in the param tag because we might be
307 * inheriting documentation.
308 * @param isFirstParam true if this is the first param tag being printed.
309 *
310 */
311 private Content processParamTag(boolean isNonTypeParams,
312 TagletWriter writer, ParamTag paramTag, String name,
313 boolean isFirstParam) {
314 Content result = writer.getOutputInstance();
315 String header = writer.configuration().getText(
316 isNonTypeParams ? "doclet.Parameters" : "doclet.TypeParameters");
317 if (isFirstParam) {
318 result.addContent(writer.getParamHeader(header));
319 }
320 result.addContent(writer.paramTagOutput(paramTag,
321 name));
322 return result;
323 }
324 }