Wed, 01 Dec 2010 11:02:38 -0800
6851834: Javadoc doclet needs a structured approach to generate the output HTML.
Reviewed-by: jjg
1 /*
2 * Copyright (c) 2003, 2009, 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.builders;
28 import java.io.*;
29 import java.util.*;
30 import com.sun.javadoc.*;
31 import com.sun.tools.doclets.internal.toolkit.util.*;
32 import com.sun.tools.doclets.internal.toolkit.*;
34 /**
35 * Builds the serialized form.
36 *
37 * This code is not part of an API.
38 * It is implementation that is subject to change.
39 * Do not use it as an API
40 *
41 * @author Jamie Ho
42 * @author Bhavesh Patel (Modified)
43 * @since 1.5
44 */
45 public class SerializedFormBuilder extends AbstractBuilder {
47 /**
48 * The root element of the serialized form XML is {@value}.
49 */
50 public static final String NAME = "SerializedForm";
52 /**
53 * The writer for this builder.
54 */
55 private SerializedFormWriter writer;
57 /**
58 * The writer for serializable fields.
59 */
60 private SerializedFormWriter.SerialFieldWriter fieldWriter;
62 /**
63 * The writer for serializable method documentation.
64 */
65 private SerializedFormWriter.SerialMethodWriter methodWriter;
67 /**
68 * The header for the serial version UID. Save the string
69 * here instead of the properties file because we do not want
70 * this string to be localized.
71 */
72 private static final String SERIAL_VERSION_UID_HEADER = "serialVersionUID:";
74 /**
75 * The current package being documented.
76 */
77 private PackageDoc currentPackage;
79 /**
80 * The current class being documented.
81 */
82 private ClassDoc currentClass;
84 /**
85 * The current member being documented.
86 */
87 protected MemberDoc currentMember;
89 /**
90 * The content that will be added to the serialized form documentation tree.
91 */
92 private Content contentTree;
94 private SerializedFormBuilder(Configuration configuration) {
95 super(configuration);
96 }
98 /**
99 * Construct a new SerializedFormBuilder.
100 * @param configuration the current configuration of the doclet.
101 */
102 public static SerializedFormBuilder getInstance(Configuration configuration) {
103 SerializedFormBuilder builder = new SerializedFormBuilder(configuration);
104 return builder;
105 }
107 /**
108 * Build the serialized form.
109 */
110 public void build() throws IOException {
111 if (! serialClassFoundToDocument(configuration.root.classes())) {
112 //Nothing to document.
113 return;
114 }
115 try {
116 writer = configuration.getWriterFactory().getSerializedFormWriter();
117 if (writer == null) {
118 //Doclet does not support this output.
119 return;
120 }
121 } catch (Exception e) {
122 throw new DocletAbortException();
123 }
124 build(LayoutParser.getInstance(configuration).parseXML(NAME), contentTree);
125 writer.close();
126 }
128 /**
129 * {@inheritDoc}
130 */
131 public String getName() {
132 return NAME;
133 }
135 /**
136 * Build the serialized form.
137 *
138 * @param node the XML element that specifies which components to document
139 * @param serializedTree content tree to which the documentation will be added
140 */
141 public void buildSerializedForm(XMLNode node, Content serializedTree) throws Exception {
142 serializedTree = writer.getHeader(configuration.getText(
143 "doclet.Serialized_Form"));
144 buildChildren(node, serializedTree);
145 writer.addFooter(serializedTree);
146 writer.printDocument(serializedTree);
147 writer.close();
148 }
150 /**
151 * Build the serialized form summaries.
152 *
153 * @param node the XML element that specifies which components to document
154 * @param serializedTree content tree to which the documentation will be added
155 */
156 public void buildSerializedFormSummaries(XMLNode node, Content serializedTree) {
157 Content serializedSummariesTree = writer.getSerializedSummariesHeader();
158 PackageDoc[] packages = configuration.packages;
159 for (int i = 0; i < packages.length; i++) {
160 currentPackage = packages[i];
161 buildChildren(node, serializedSummariesTree);
162 }
163 serializedTree.addContent(writer.getSerializedContent(
164 serializedSummariesTree));
165 }
167 /**
168 * Build the package serialized form for the current package being processed.
169 *
170 * @param node the XML element that specifies which components to document
171 * @param serializedSummariesTree content tree to which the documentation will be added
172 */
173 public void buildPackageSerializedForm(XMLNode node, Content serializedSummariesTree) {
174 Content packageSerializedTree = writer.getPackageSerializedHeader();
175 String foo = currentPackage.name();
176 ClassDoc[] classes = currentPackage.allClasses(false);
177 if (classes == null || classes.length == 0) {
178 return;
179 }
180 if (!serialInclude(currentPackage)) {
181 return;
182 }
183 if (!serialClassFoundToDocument(classes)) {
184 return;
185 }
186 buildChildren(node, packageSerializedTree);
187 serializedSummariesTree.addContent(packageSerializedTree);
188 }
190 /**
191 * Build the package header.
192 *
193 * @param node the XML element that specifies which components to document
194 * @param packageSerializedTree content tree to which the documentation will be added
195 */
196 public void buildPackageHeader(XMLNode node, Content packageSerializedTree) {
197 packageSerializedTree.addContent(writer.getPackageHeader(
198 Util.getPackageName(currentPackage)));
199 }
201 /**
202 * Build the class serialized form.
203 *
204 * @param node the XML element that specifies which components to document
205 * @param packageSerializedTree content tree to which the documentation will be added
206 */
207 public void buildClassSerializedForm(XMLNode node, Content packageSerializedTree) {
208 Content classSerializedTree = writer.getClassSerializedHeader();
209 ClassDoc[] classes = currentPackage.allClasses(false);
210 Arrays.sort(classes);
211 for (int j = 0; j < classes.length; j++) {
212 currentClass = classes[j];
213 fieldWriter = writer.getSerialFieldWriter(currentClass);
214 methodWriter = writer.getSerialMethodWriter(currentClass);
215 if(currentClass.isClass() && currentClass.isSerializable()) {
216 if(!serialClassInclude(currentClass)) {
217 continue;
218 }
219 Content classTree = writer.getClassHeader(currentClass);
220 buildChildren(node, classTree);
221 classSerializedTree.addContent(classTree);
222 }
223 }
224 packageSerializedTree.addContent(classSerializedTree);
225 }
227 /**
228 * Build the serial UID information for the given class.
229 *
230 * @param node the XML element that specifies which components to document
231 * @param classTree content tree to which the serial UID information will be added
232 */
233 public void buildSerialUIDInfo(XMLNode node, Content classTree) {
234 Content serialUidTree = writer.getSerialUIDInfoHeader();
235 FieldDoc[] fields = currentClass.fields(false);
236 for (int i = 0; i < fields.length; i++) {
237 if (fields[i].name().equals("serialVersionUID") &&
238 fields[i].constantValueExpression() != null) {
239 writer.addSerialUIDInfo(SERIAL_VERSION_UID_HEADER,
240 fields[i].constantValueExpression(), serialUidTree);
241 break;
242 }
243 }
244 classTree.addContent(serialUidTree);
245 }
247 /**
248 * Build the summaries for the methods and fields.
249 *
250 * @param node the XML element that specifies which components to document
251 * @param classTree content tree to which the documentation will be added
252 */
253 public void buildClassContent(XMLNode node, Content classTree) {
254 Content classContentTree = writer.getClassContentHeader();
255 buildChildren(node, classContentTree);
256 classTree.addContent(classContentTree);
257 }
259 /**
260 * Build the summaries for the methods that belong to the given
261 * class.
262 *
263 * @param node the XML element that specifies which components to document
264 * @param classContentTree content tree to which the documentation will be added
265 */
266 public void buildSerializableMethods(XMLNode node, Content classContentTree) {
267 Content serializableMethodTree = methodWriter.getSerializableMethodsHeader();
268 MemberDoc[] members = currentClass.serializationMethods();
269 int membersLength = members.length;
270 if (membersLength > 0) {
271 for (int i = 0; i < membersLength; i++) {
272 currentMember = members[i];
273 Content methodsContentTree = methodWriter.getMethodsContentHeader(
274 (i == membersLength - 1));
275 buildChildren(node, methodsContentTree);
276 serializableMethodTree.addContent(methodsContentTree);
277 }
278 }
279 if (currentClass.serializationMethods().length > 0) {
280 classContentTree.addContent(methodWriter.getSerializableMethods(
281 configuration.getText("doclet.Serialized_Form_methods"),
282 serializableMethodTree));
283 if (currentClass.isSerializable() && !currentClass.isExternalizable()) {
284 if (currentClass.serializationMethods().length == 0) {
285 Content noCustomizationMsg = methodWriter.getNoCustomizationMsg(
286 configuration.getText(
287 "doclet.Serializable_no_customization"));
288 classContentTree.addContent(methodWriter.getSerializableMethods(
289 configuration.getText("doclet.Serialized_Form_methods"),
290 noCustomizationMsg));
291 }
292 }
293 }
294 }
296 /**
297 * Build the method sub header.
298 *
299 * @param node the XML element that specifies which components to document
300 * @param methodsContentTree content tree to which the documentation will be added
301 */
302 public void buildMethodSubHeader(XMLNode node, Content methodsContentTree) {
303 methodWriter.addMemberHeader((MethodDoc)currentMember, methodsContentTree);
304 }
306 /**
307 * Build the deprecated method description.
308 *
309 * @param node the XML element that specifies which components to document
310 * @param methodsContentTree content tree to which the documentation will be added
311 */
312 public void buildDeprecatedMethodInfo(XMLNode node, Content methodsContentTree) {
313 methodWriter.addDeprecatedMemberInfo((MethodDoc) currentMember, methodsContentTree);
314 }
316 /**
317 * Build the information for the method.
318 *
319 * @param node the XML element that specifies which components to document
320 * @param methodsContentTree content tree to which the documentation will be added
321 */
322 public void buildMethodInfo(XMLNode node, Content methodsContentTree) {
323 if(configuration.nocomment){
324 return;
325 }
326 buildChildren(node, methodsContentTree);
327 }
329 /**
330 * Build method description.
331 *
332 * @param node the XML element that specifies which components to document
333 * @param methodsContentTree content tree to which the documentation will be added
334 */
335 public void buildMethodDescription(XMLNode node, Content methodsContentTree) {
336 methodWriter.addMemberDescription((MethodDoc) currentMember, methodsContentTree);
337 }
339 /**
340 * Build the method tags.
341 *
342 * @param node the XML element that specifies which components to document
343 * @param methodsContentTree content tree to which the documentation will be added
344 */
345 public void buildMethodTags(XMLNode node, Content methodsContentTree) {
346 methodWriter.addMemberTags((MethodDoc) currentMember, methodsContentTree);
347 MethodDoc method = (MethodDoc)currentMember;
348 if (method.name().compareTo("writeExternal") == 0
349 && method.tags("serialData").length == 0) {
350 if (configuration.serialwarn) {
351 configuration.getDocletSpecificMsg().warning(
352 currentMember.position(), "doclet.MissingSerialDataTag",
353 method.containingClass().qualifiedName(), method.name());
354 }
355 }
356 }
358 /**
359 * Build the field header.
360 *
361 * @param node the XML element that specifies which components to document
362 * @param classContentTree content tree to which the documentation will be added
363 */
364 public void buildFieldHeader(XMLNode node, Content classContentTree) {
365 if (currentClass.serializableFields().length > 0) {
366 buildFieldSerializationOverview(currentClass, classContentTree);
367 }
368 }
370 /**
371 * Build the serialization overview for the given class.
372 *
373 * @param classDoc the class to print the overview for.
374 * @param classContentTree content tree to which the documentation will be added
375 */
376 public void buildFieldSerializationOverview(ClassDoc classDoc, Content classContentTree) {
377 if (classDoc.definesSerializableFields()) {
378 FieldDoc serialPersistentField =
379 Util.asList(classDoc.serializableFields()).get(0);
380 // Check to see if there are inline comments, tags or deprecation
381 // information to be printed.
382 if (fieldWriter.shouldPrintOverview(serialPersistentField)) {
383 Content serializableFieldsTree = fieldWriter.getSerializableFieldsHeader();
384 Content fieldsOverviewContentTree = fieldWriter.getFieldsContentHeader(true);
385 fieldWriter.addMemberDeprecatedInfo(serialPersistentField,
386 fieldsOverviewContentTree);
387 if (!configuration.nocomment) {
388 fieldWriter.addMemberDescription(serialPersistentField,
389 fieldsOverviewContentTree);
390 fieldWriter.addMemberTags(serialPersistentField,
391 fieldsOverviewContentTree);
392 }
393 serializableFieldsTree.addContent(fieldsOverviewContentTree);
394 classContentTree.addContent(fieldWriter.getSerializableFields(
395 configuration.getText("doclet.Serialized_Form_class"),
396 serializableFieldsTree));
397 }
398 }
399 }
401 /**
402 * Build the summaries for the fields that belong to the given class.
403 *
404 * @param node the XML element that specifies which components to document
405 * @param classContentTree content tree to which the documentation will be added
406 */
407 public void buildSerializableFields(XMLNode node, Content classContentTree) {
408 MemberDoc[] members = currentClass.serializableFields();
409 int membersLength = members.length;
410 if (membersLength > 0) {
411 Content serializableFieldsTree = fieldWriter.getSerializableFieldsHeader();
412 for (int i = 0; i < membersLength; i++) {
413 currentMember = members[i];
414 if (!currentClass.definesSerializableFields()) {
415 Content fieldsContentTree = fieldWriter.getFieldsContentHeader(
416 (i == membersLength - 1));
417 buildChildren(node, fieldsContentTree);
418 serializableFieldsTree.addContent(fieldsContentTree);
419 }
420 else {
421 buildSerialFieldTagsInfo(serializableFieldsTree);
422 }
423 }
424 classContentTree.addContent(fieldWriter.getSerializableFields(
425 configuration.getText("doclet.Serialized_Form_fields"),
426 serializableFieldsTree));
427 }
428 }
430 /**
431 * Build the field sub header.
432 *
433 * @param node the XML element that specifies which components to document
434 * @param fieldsContentTree content tree to which the documentation will be added
435 */
436 public void buildFieldSubHeader(XMLNode node, Content fieldsContentTree) {
437 if (!currentClass.definesSerializableFields()) {
438 FieldDoc field = (FieldDoc) currentMember;
439 fieldWriter.addMemberHeader(field.type().asClassDoc(),
440 field.type().typeName(), field.type().dimension(), field.name(),
441 fieldsContentTree);
442 }
443 }
445 /**
446 * Build the field deprecation information.
447 *
448 * @param node the XML element that specifies which components to document
449 * @param fieldsContentTree content tree to which the documentation will be added
450 */
451 public void buildFieldDeprecationInfo(XMLNode node, Content fieldsContentTree) {
452 if (!currentClass.definesSerializableFields()) {
453 FieldDoc field = (FieldDoc)currentMember;
454 fieldWriter.addMemberDeprecatedInfo(field, fieldsContentTree);
455 }
456 }
458 /**
459 * Build the serial field tags information.
460 *
461 * @param serializableFieldsTree content tree to which the documentation will be added
462 */
463 public void buildSerialFieldTagsInfo(Content serializableFieldsTree) {
464 if(configuration.nocomment){
465 return;
466 }
467 FieldDoc field = (FieldDoc)currentMember;
468 // Process Serializable Fields specified as array of
469 // ObjectStreamFields. Print a member for each serialField tag.
470 // (There should be one serialField tag per ObjectStreamField
471 // element.)
472 SerialFieldTag[] tags = field.serialFieldTags();
473 Arrays.sort(tags);
474 int tagsLength = tags.length;
475 for (int i = 0; i < tagsLength; i++) {
476 Content fieldsContentTree = fieldWriter.getFieldsContentHeader(
477 (i == tagsLength - 1));
478 fieldWriter.addMemberHeader(tags[i].fieldTypeDoc(),
479 tags[i].fieldType(), "", tags[i].fieldName(), fieldsContentTree);
480 fieldWriter.addMemberDescription(tags[i], fieldsContentTree);
481 serializableFieldsTree.addContent(fieldsContentTree);
482 }
483 }
485 /**
486 * Build the field information.
487 *
488 * @param node the XML element that specifies which components to document
489 * @param fieldsContentTree content tree to which the documentation will be added
490 */
491 public void buildFieldInfo(XMLNode node, Content fieldsContentTree) {
492 if(configuration.nocomment){
493 return;
494 }
495 FieldDoc field = (FieldDoc)currentMember;
496 ClassDoc cd = field.containingClass();
497 // Process default Serializable field.
498 if ((field.tags("serial").length == 0) && ! field.isSynthetic()
499 && configuration.serialwarn) {
500 configuration.message.warning(field.position(),
501 "doclet.MissingSerialTag", cd.qualifiedName(),
502 field.name());
503 }
504 fieldWriter.addMemberDescription(field, fieldsContentTree);
505 fieldWriter.addMemberTags(field, fieldsContentTree);
506 }
508 /**
509 * Return true if the given Doc should be included
510 * in the serialized form.
511 *
512 * @param doc the Doc object to check for serializability.
513 */
514 public static boolean serialInclude(Doc doc) {
515 if (doc == null) {
516 return false;
517 }
518 return doc.isClass() ?
519 serialClassInclude((ClassDoc)doc) :
520 serialDocInclude(doc);
521 }
523 /**
524 * Return true if the given ClassDoc should be included
525 * in the serialized form.
526 *
527 * @param cd the ClassDoc object to check for serializability.
528 */
529 private static boolean serialClassInclude(ClassDoc cd) {
530 if (cd.isEnum()) {
531 return false;
532 }
533 try {
534 cd.superclassType();
535 } catch (NullPointerException e) {
536 //Workaround for null pointer bug in ClassDoc.superclassType().
537 return false;
538 }
539 if (cd.isSerializable()) {
540 if (cd.tags("serial").length > 0) {
541 return serialDocInclude(cd);
542 } else if (cd.isPublic() || cd.isProtected()) {
543 return true;
544 } else {
545 return false;
546 }
547 }
548 return false;
549 }
551 /**
552 * Return true if the given Doc should be included
553 * in the serialized form.
554 *
555 * @param doc the Doc object to check for serializability.
556 */
557 private static boolean serialDocInclude(Doc doc) {
558 if (doc.isEnum()) {
559 return false;
560 }
561 Tag[] serial = doc.tags("serial");
562 if (serial.length > 0) {
563 String serialtext = serial[0].text().toLowerCase();
564 if (serialtext.indexOf("exclude") >= 0) {
565 return false;
566 } else if (serialtext.indexOf("include") >= 0) {
567 return true;
568 }
569 }
570 return true;
571 }
573 /**
574 * Return true if any of the given classes have a @serialinclude tag.
575 *
576 * @param classes the classes to check.
577 * @return true if any of the given classes have a @serialinclude tag.
578 */
579 private boolean serialClassFoundToDocument(ClassDoc[] classes) {
580 for (int i = 0; i < classes.length; i++) {
581 if (serialClassInclude(classes[i])) {
582 return true;
583 }
584 }
585 return false;
586 }
587 }