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