1 /*
2  * Copyright (c) 2003, 2021, 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.builders;
27 
28 
29 import java.util.List;
30 import java.util.SortedSet;
31 import java.util.TreeSet;
32 
33 import javax.lang.model.element.Element;
34 import javax.lang.model.element.ExecutableElement;
35 import javax.lang.model.element.PackageElement;
36 import javax.lang.model.element.TypeElement;
37 import javax.lang.model.element.VariableElement;
38 import javax.lang.model.type.TypeMirror;
39 
40 import com.sun.source.doctree.SerialFieldTree;
41 import com.sun.source.doctree.SerialTree;
42 import jdk.javadoc.internal.doclets.toolkit.Content;
43 import jdk.javadoc.internal.doclets.toolkit.DocletException;
44 import jdk.javadoc.internal.doclets.toolkit.SerializedFormWriter;
45 import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
46 import jdk.javadoc.internal.doclets.toolkit.util.Utils;
47 
48 /**
49  * Builds the serialized form.
50  *
51  *  <p><b>This is NOT part of any supported API.
52  *  If you write code that depends on this, you do so at your own risk.
53  *  This code and its internal interfaces are subject to change or
54  *  deletion without notice.</b>
55  */
56 public class SerializedFormBuilder extends AbstractBuilder {
57 
58     /**
59      * The writer for this builder.
60      */
61     private SerializedFormWriter writer;
62 
63     /**
64      * The writer for serializable fields.
65      */
66     private SerializedFormWriter.SerialFieldWriter fieldWriter;
67 
68     /**
69      * The writer for serializable method documentation.
70      */
71     private SerializedFormWriter.SerialMethodWriter methodWriter;
72 
73     /**
74      * The header for the serial version UID.  Save the string
75      * here instead of the properties file because we do not want
76      * this string to be localized.
77      */
78     private static final String SERIAL_VERSION_UID = "serialVersionUID";
79     private static final String SERIAL_VERSION_UID_HEADER = SERIAL_VERSION_UID + ":";
80 
81     /**
82      * The current package being documented.
83      */
84     private PackageElement currentPackage;
85 
86     /**
87      * The current class being documented.
88      */
89     private TypeElement currentTypeElement;
90 
91     /**
92      * The current member being documented.
93      */
94     protected Element currentMember;
95 
96     /**
97      * Construct a new SerializedFormBuilder.
98      * @param context  the build context.
99      */
SerializedFormBuilder(Context context)100     private SerializedFormBuilder(Context context) {
101         super(context);
102     }
103 
104     /**
105      * Construct a new SerializedFormBuilder.
106      *
107      * @param context  the build context.
108      * @return the new SerializedFormBuilder
109      */
getInstance(Context context)110     public static SerializedFormBuilder getInstance(Context context) {
111         return new SerializedFormBuilder(context);
112     }
113 
114     /**
115      * Build the serialized form.
116      *
117      * @throws DocletException if there is a problem while building the documentation
118      */
119     @Override
build()120     public void build() throws DocletException {
121         SortedSet<TypeElement> rootclasses = new TreeSet<>(utils.comparators.makeGeneralPurposeComparator());
122         rootclasses.addAll(configuration.getIncludedTypeElements());
123         if (!serialClassFoundToDocument(rootclasses)) {
124             //Nothing to document.
125             return;
126         }
127         writer = configuration.getWriterFactory().getSerializedFormWriter();
128         if (writer == null) {
129             //Doclet does not support this output.
130             return;
131         }
132         buildSerializedForm();
133     }
134 
135     /**
136      * Build the serialized form.
137      *
138      * @throws DocletException if there is a problem while building the documentation
139      */
buildSerializedForm()140     protected void buildSerializedForm() throws DocletException {
141         Content contentTree = writer.getHeader(resources.getText(
142                 "doclet.Serialized_Form"));
143 
144         buildSerializedFormSummaries();
145 
146         writer.addFooter();
147         writer.printDocument(contentTree);
148     }
149 
150     /**
151      * Build the serialized form summaries.
152      *
153      * @throws DocletException if there is a problem while building the documentation
154      */
buildSerializedFormSummaries()155     protected void buildSerializedFormSummaries()
156             throws DocletException {
157         Content serializedSummariesTree = writer.getSerializedSummariesHeader();
158         for (PackageElement pkg : configuration.packages) {
159             currentPackage = pkg;
160 
161             buildPackageSerializedForm(serializedSummariesTree);
162         }
163         writer.addSerializedContent(serializedSummariesTree);
164     }
165 
166     /**
167      * Build the package serialized form for the current package being processed.
168      *
169      * @param serializedSummariesTree content tree to which the documentation will be added
170      * @throws DocletException if there is a problem while building the documentation
171      */
buildPackageSerializedForm(Content serializedSummariesTree)172     protected void buildPackageSerializedForm(Content serializedSummariesTree) throws DocletException {
173         Content packageSerializedTree = writer.getPackageSerializedHeader();
174         SortedSet<TypeElement> classes = utils.getAllClassesUnfiltered(currentPackage);
175         if (classes.isEmpty()) {
176             return;
177         }
178         if (!serialInclude(utils, currentPackage)) {
179             return;
180         }
181         if (!serialClassFoundToDocument(classes)) {
182             return;
183         }
184 
185         buildPackageHeader(packageSerializedTree);
186         buildClassSerializedForm(packageSerializedTree);
187 
188         writer.addPackageSerializedTree(serializedSummariesTree, packageSerializedTree);
189     }
190 
191     /**
192      * Build the package header.
193      *
194      * @param packageSerializedTree content tree to which the documentation will be added
195      */
buildPackageHeader(Content packageSerializedTree)196     protected void buildPackageHeader(Content packageSerializedTree) {
197         packageSerializedTree.add(writer.getPackageHeader(currentPackage));
198     }
199 
200     /**
201      * Build the class serialized form.
202      *
203      * @param packageSerializedTree content tree to which the documentation will be added
204      * @throws DocletException if there is a problem while building the documentation
205      */
buildClassSerializedForm(Content packageSerializedTree)206     protected void buildClassSerializedForm(Content packageSerializedTree)
207             throws DocletException {
208         Content classSerializedTree = writer.getClassSerializedHeader();
209         SortedSet<TypeElement> typeElements = utils.getAllClassesUnfiltered(currentPackage);
210         for (TypeElement typeElement : typeElements) {
211             currentTypeElement = typeElement;
212             fieldWriter = writer.getSerialFieldWriter(currentTypeElement);
213             methodWriter = writer.getSerialMethodWriter(currentTypeElement);
214             if (utils.isClass(currentTypeElement) && utils.isSerializable(currentTypeElement)) {
215                 if (!serialClassInclude(utils, currentTypeElement)) {
216                     continue;
217                 }
218                 Content classTree = writer.getClassHeader(currentTypeElement);
219 
220                 buildSerialUIDInfo(classTree);
221                 buildClassContent(classTree);
222 
223                 classSerializedTree.add(writer.getMemberTree(classTree));
224             }
225         }
226         packageSerializedTree.add(classSerializedTree);
227     }
228 
229     /**
230      * Build the serial UID information for the given class.
231      *
232      * @param classTree content tree to which the serial UID information will be added
233      */
buildSerialUIDInfo(Content classTree)234     protected void buildSerialUIDInfo(Content classTree) {
235         Content serialUidTree = writer.getSerialUIDInfoHeader();
236         for (Element e : utils.getFieldsUnfiltered(currentTypeElement)) {
237             VariableElement field = (VariableElement)e;
238             if (field.getSimpleName().toString().compareTo(SERIAL_VERSION_UID) == 0 &&
239                 field.getConstantValue() != null) {
240                 writer.addSerialUIDInfo(SERIAL_VERSION_UID_HEADER,
241                                         utils.constantValueExpression(field), serialUidTree);
242                 break;
243             }
244         }
245         classTree.add(serialUidTree);
246     }
247 
248     /**
249      * Build the summaries for the methods and fields.
250      *
251      * @param classTree content tree to which the documentation will be added
252      * @throws DocletException if there is a problem while building the documentation
253      */
buildClassContent(Content classTree)254     protected void buildClassContent(Content classTree) throws DocletException {
255         Content classContentTree = writer.getClassContentHeader();
256 
257         buildSerializableMethods(classContentTree);
258         buildFieldHeader(classContentTree);
259         buildSerializableFields(classContentTree);
260 
261         classTree.add(classContentTree);
262     }
263 
264     /**
265      * Build the summaries for the methods that belong to the given class.
266      *
267      * @param classContentTree content tree to which the documentation will be added
268      * @throws DocletException if there is a problem while building the documentation
269      */
buildSerializableMethods(Content classContentTree)270     protected void buildSerializableMethods(Content classContentTree) throws DocletException {
271         Content serializableMethodTree = methodWriter.getSerializableMethodsHeader();
272         SortedSet<ExecutableElement> members = utils.serializationMethods(currentTypeElement);
273         if (!members.isEmpty()) {
274             for (ExecutableElement member : members) {
275                 currentMember = member;
276                 Content methodsContentTree = methodWriter.getMethodsContentHeader(
277                         currentMember == members.last());
278 
279                 buildMethodSubHeader(methodsContentTree);
280                 buildDeprecatedMethodInfo(methodsContentTree);
281                 buildMethodInfo(methodsContentTree);
282 
283                 serializableMethodTree.add(methodsContentTree);
284             }
285         }
286         if (!utils.serializationMethods(currentTypeElement).isEmpty()) {
287             classContentTree.add(methodWriter.getSerializableMethods(
288                     resources.getText("doclet.Serialized_Form_methods"),
289                     serializableMethodTree));
290             if (utils.isSerializable(currentTypeElement) && !utils.isExternalizable(currentTypeElement)) {
291                 if (utils.serializationMethods(currentTypeElement).isEmpty()) {
292                     Content noCustomizationMsg = methodWriter.getNoCustomizationMsg(
293                             resources.getText("doclet.Serializable_no_customization"));
294                     classContentTree.add(methodWriter.getSerializableMethods(
295                             resources.getText("doclet.Serialized_Form_methods"),
296                             noCustomizationMsg));
297                 }
298             }
299         }
300     }
301 
302     /**
303      * Build the method sub header.
304      *
305      * @param methodsContentTree content tree to which the documentation will be added
306      */
buildMethodSubHeader(Content methodsContentTree)307     protected void buildMethodSubHeader(Content methodsContentTree)  {
308         methodWriter.addMemberHeader((ExecutableElement)currentMember, methodsContentTree);
309     }
310 
311     /**
312      * Build the deprecated method description.
313      *
314      * @param methodsContentTree content tree to which the documentation will be added
315      */
buildDeprecatedMethodInfo(Content methodsContentTree)316     protected void buildDeprecatedMethodInfo(Content methodsContentTree) {
317         methodWriter.addDeprecatedMemberInfo((ExecutableElement)currentMember, methodsContentTree);
318     }
319 
320     /**
321      * Build the information for the method.
322      *
323      * @param methodsContentTree content tree to which the documentation will be added
324      * @throws DocletException if there is a problem while building the documentation
325      */
buildMethodInfo(Content methodsContentTree)326     protected void buildMethodInfo(Content methodsContentTree) throws DocletException  {
327         if (options.noComment()) {
328             return;
329         }
330 
331         buildMethodDescription(methodsContentTree);
332         buildMethodTags(methodsContentTree);
333     }
334 
335     /**
336      * Build method description.
337      *
338      * @param methodsContentTree content tree to which the documentation will be added
339      */
buildMethodDescription(Content methodsContentTree)340     protected void buildMethodDescription(Content methodsContentTree) {
341         methodWriter.addMemberDescription((ExecutableElement)currentMember, methodsContentTree);
342     }
343 
344     /**
345      * Build the method tags.
346      *
347      * @param methodsContentTree content tree to which the documentation will be added
348      */
buildMethodTags(Content methodsContentTree)349     protected void buildMethodTags(Content methodsContentTree) {
350         methodWriter.addMemberTags((ExecutableElement)currentMember, methodsContentTree);
351         ExecutableElement method = (ExecutableElement)currentMember;
352         if (method.getSimpleName().toString().compareTo("writeExternal") == 0
353                 && utils.getSerialDataTrees(method).isEmpty()) {
354             if (options.serialWarn()) {
355                 TypeElement encl  = (TypeElement) method.getEnclosingElement();
356                 messages.warning(currentMember,
357                         "doclet.MissingSerialDataTag", encl.getQualifiedName().toString(),
358                         method.getSimpleName().toString());
359             }
360         }
361     }
362 
363     /**
364      * Build the field header.
365      *
366      * @param classContentTree content tree to which the documentation will be added
367      */
buildFieldHeader(Content classContentTree)368     protected void buildFieldHeader(Content classContentTree) {
369         if (!utils.serializableFields(currentTypeElement).isEmpty()) {
370             buildFieldSerializationOverview(currentTypeElement, classContentTree);
371         }
372     }
373 
374     /**
375      * Build the serialization overview for the given class.
376      *
377      * @param typeElement the class to print the overview for.
378      * @param classContentTree content tree to which the documentation will be added
379      */
buildFieldSerializationOverview(TypeElement typeElement, Content classContentTree)380     public void buildFieldSerializationOverview(TypeElement typeElement, Content classContentTree) {
381         if (utils.definesSerializableFields(typeElement)) {
382             VariableElement ve = utils.serializableFields(typeElement).first();
383             // Check to see if there are inline comments, tags or deprecation
384             // information to be printed.
385             if (fieldWriter.shouldPrintOverview(ve)) {
386                 Content serializableFieldsTree = fieldWriter.getSerializableFieldsHeader();
387                 Content fieldsOverviewContentTree = fieldWriter.getFieldsContentHeader(true);
388                 fieldWriter.addMemberDeprecatedInfo(ve, fieldsOverviewContentTree);
389                 if (!options.noComment()) {
390                     fieldWriter.addMemberDescription(ve, fieldsOverviewContentTree);
391                     fieldWriter.addMemberTags(ve, fieldsOverviewContentTree);
392                 }
393                 serializableFieldsTree.add(fieldsOverviewContentTree);
394                 classContentTree.add(fieldWriter.getSerializableFields(
395                         resources.getText("doclet.Serialized_Form_class"),
396                         serializableFieldsTree));
397             }
398         }
399     }
400 
401     /**
402      * Build the summaries for the fields that belong to the given class.
403      *
404      * @param classContentTree content tree to which the documentation will be added
405      * @throws DocletException if there is a problem while building the documentation
406      */
buildSerializableFields(Content classContentTree)407     protected void buildSerializableFields(Content classContentTree)
408             throws DocletException {
409         SortedSet<VariableElement> members = utils.serializableFields(currentTypeElement);
410         if (!members.isEmpty()) {
411             Content serializableFieldsTree = fieldWriter.getSerializableFieldsHeader();
412             for (VariableElement ve : members) {
413                 currentMember = ve;
414                 if (!utils.definesSerializableFields(currentTypeElement)) {
415                     Content fieldsContentTree = fieldWriter.getFieldsContentHeader(
416                             currentMember == members.last());
417 
418                     buildFieldSubHeader(fieldsContentTree);
419                     buildFieldDeprecationInfo(fieldsContentTree);
420                     buildFieldInfo(fieldsContentTree);
421 
422                     serializableFieldsTree.add(fieldsContentTree);
423                 } else {
424                     buildSerialFieldTagsInfo(serializableFieldsTree);
425                 }
426             }
427             classContentTree.add(fieldWriter.getSerializableFields(
428                     resources.getText("doclet.Serialized_Form_fields"),
429                     serializableFieldsTree));
430         }
431     }
432 
433     /**
434      * Build the field sub header.
435      *
436      * @param fieldsContentTree content tree to which the documentation will be added
437      */
buildFieldSubHeader(Content fieldsContentTree)438     protected void buildFieldSubHeader(Content fieldsContentTree) {
439         if (!utils.definesSerializableFields(currentTypeElement)) {
440             VariableElement field = (VariableElement) currentMember;
441             fieldWriter.addMemberHeader(field.asType(),
442                     utils.getSimpleName(field),
443                     fieldsContentTree);
444         }
445     }
446 
447     /**
448      * Build the field deprecation information.
449      *
450      * @param fieldsContentTree content tree to which the documentation will be added
451      */
buildFieldDeprecationInfo(Content fieldsContentTree)452     protected void buildFieldDeprecationInfo(Content fieldsContentTree) {
453         if (!utils.definesSerializableFields(currentTypeElement)) {
454             fieldWriter.addMemberDeprecatedInfo((VariableElement)currentMember,
455                     fieldsContentTree);
456         }
457     }
458 
459     /**
460      * Build the serial field tags information.
461      *
462      * @param serializableFieldsTree content tree to which the documentation will be added
463      */
buildSerialFieldTagsInfo(Content serializableFieldsTree)464     protected void buildSerialFieldTagsInfo(Content serializableFieldsTree) {
465         if (options.noComment()) {
466             return;
467         }
468         VariableElement field = (VariableElement)currentMember;
469         // Process Serializable Fields specified as array of
470         // ObjectStreamFields. Print a member for each serialField tag.
471         // (There should be one serialField tag per ObjectStreamField
472         // element.)
473         SortedSet<SerialFieldTree> tags = new TreeSet<>(utils.comparators.makeSerialFieldTreeComparator());
474         // sort the elements
475         tags.addAll(utils.getSerialFieldTrees(field));
476 
477         CommentHelper ch = utils.getCommentHelper(field);
478         for (SerialFieldTree tag : tags) {
479             if (tag.getName() == null || tag.getType() == null)  // ignore malformed @serialField tags
480                 continue;
481             Content fieldsContentTree = fieldWriter.getFieldsContentHeader(tag.equals(tags.last()));
482             TypeMirror type = ch.getReferencedType(tag);
483             fieldWriter.addMemberHeader(type, tag.getName().getName().toString(), fieldsContentTree);
484             fieldWriter.addMemberDescription(field, tag, fieldsContentTree);
485             serializableFieldsTree.add(fieldsContentTree);
486         }
487     }
488 
489     /**
490      * Build the field information.
491      *
492      * @param fieldsContentTree content tree to which the documentation will be added
493      */
buildFieldInfo(Content fieldsContentTree)494     protected void buildFieldInfo(Content fieldsContentTree) {
495         if (options.noComment()) {
496             return;
497         }
498         VariableElement field = (VariableElement)currentMember;
499         TypeElement te = utils.getEnclosingTypeElement(currentMember);
500         // Process default Serializable field.
501         if ((utils.getSerialTrees(field).isEmpty()) /*&& !field.isSynthetic()*/
502                 && options.serialWarn()) {
503             messages.warning(field,
504                     "doclet.MissingSerialTag", utils.getFullyQualifiedName(te),
505                     utils.getSimpleName(field));
506         }
507         fieldWriter.addMemberDescription(field, fieldsContentTree);
508         fieldWriter.addMemberTags(field, fieldsContentTree);
509     }
510 
511     /**
512      * Returns true if the given Element should be included
513      * in the serialized form.
514      *
515      * @param utils the utils object
516      * @param element the Element object to check for serializability
517      * @return true if the element should be included in the serial form
518      */
serialInclude(Utils utils, Element element)519     public static boolean serialInclude(Utils utils, Element element) {
520         if (element == null) {
521             return false;
522         }
523         return utils.isClass(element)
524                 ? serialClassInclude(utils, (TypeElement)element)
525                 : serialDocInclude(utils, element);
526     }
527 
528     /**
529      * Returns true if the given TypeElement should be included
530      * in the serialized form.
531      *
532      * @param te the TypeElement object to check for serializability.
533      */
serialClassInclude(Utils utils, TypeElement te)534     private static boolean serialClassInclude(Utils utils, TypeElement te) {
535         if (utils.isEnum(te)) {
536             return false;
537         }
538         if (utils.isSerializable(te)) {
539             if (utils.hasDocCommentTree(te) && !utils.getSerialTrees(te).isEmpty()) {
540                 return serialDocInclude(utils, te);
541             } else {
542                 return utils.isPublic(te) || utils.isProtected(te);
543             }
544         }
545         return false;
546     }
547 
548     /**
549      * Return true if the given Element should be included
550      * in the serialized form.
551      *
552      * @param element the Element to check for serializability.
553      */
serialDocInclude(Utils utils, Element element)554     private static boolean serialDocInclude(Utils utils, Element element) {
555         if (utils.isEnum(element)) {
556             return false;
557         }
558         List<? extends SerialTree> serial = utils.getSerialTrees(element);
559         if (!serial.isEmpty()) {
560             CommentHelper ch = utils.getCommentHelper(element);
561             String serialtext = Utils.toLowerCase(ch.getText(serial.get(0)));
562             if (serialtext.contains("exclude")) {
563                 return false;
564             } else if (serialtext.contains("include")) {
565                 return true;
566             }
567         }
568         return true;
569     }
570 
571     /**
572      * Return true if any of the given typeElements have a {@code @serial include} tag.
573      *
574      * @param classes the typeElements to check.
575      * @return true if any of the given typeElements have a {@code @serial include} tag.
576      */
serialClassFoundToDocument(SortedSet<TypeElement> classes)577     private boolean serialClassFoundToDocument(SortedSet<TypeElement> classes) {
578         for (TypeElement aClass : classes) {
579             if (serialClassInclude(utils, aClass)) {
580                 return true;
581             }
582         }
583         return false;
584     }
585 }
586