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