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