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