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 public class SerializedFormBuilder extends AbstractBuilder { 56 57 /** 58 * The writer for this builder. 59 */ 60 private SerializedFormWriter writer; 61 62 /** 63 * The writer for serializable fields. 64 */ 65 private SerializedFormWriter.SerialFieldWriter fieldWriter; 66 67 /** 68 * The writer for serializable method documentation. 69 */ 70 private SerializedFormWriter.SerialMethodWriter methodWriter; 71 72 /** 73 * The header for the serial version UID. Save the string 74 * here instead of the properties file because we do not want 75 * this string to be localized. 76 */ 77 private static final String SERIAL_VERSION_UID = "serialVersionUID"; 78 private static final String SERIAL_VERSION_UID_HEADER = SERIAL_VERSION_UID + ":"; 79 80 /** 81 * The current package being documented. 82 */ 83 private PackageElement currentPackage; 84 85 /** 86 * The current class being documented. 87 */ 88 private TypeElement currentTypeElement; 89 90 /** 91 * The current member being documented. 92 */ 93 protected Element currentMember; 94 95 /** 96 * Construct a new SerializedFormBuilder. 97 * @param context the build context. 98 */ 99 private SerializedFormBuilder(Context context) { 100 super(context); 101 } 102 103 /** 104 * Construct a new SerializedFormBuilder. 105 * 106 * @param context the build context. 107 * @return the new SerializedFormBuilder 108 */ 109 public static SerializedFormBuilder getInstance(Context context) { 110 return new SerializedFormBuilder(context); 111 } 112 113 /** 114 * Build the serialized form. 115 * 116 * @throws DocletException if there is a problem while building the documentation 117 */ 118 @Override 119 public void build() throws DocletException { 120 SortedSet<TypeElement> rootclasses = new TreeSet<>(utils.makeGeneralPurposeComparator()); 121 rootclasses.addAll(configuration.getIncludedTypeElements()); 122 if (!serialClassFoundToDocument(rootclasses)) { 123 //Nothing to document. 124 return; 125 } 126 writer = configuration.getWriterFactory().getSerializedFormWriter(); 127 if (writer == null) { 128 //Doclet does not support this output. 129 return; 130 } 131 buildSerializedForm(); 132 } 133 134 /** 135 * Build the serialized form. 136 * 137 * @throws DocletException if there is a problem while building the documentation 138 */ 139 protected void buildSerializedForm() throws DocletException { 140 Content contentTree = writer.getHeader(resources.getText( 141 "doclet.Serialized_Form")); 142 143 buildSerializedFormSummaries(); 144 145 writer.addFooter(); 146 writer.printDocument(contentTree); 147 } 148 149 /** 150 * Build the serialized form summaries. 151 * 152 * @throws DocletException if there is a problem while building the documentation 153 */ 154 protected void buildSerializedFormSummaries() 155 throws DocletException { 156 Content serializedSummariesTree = writer.getSerializedSummariesHeader(); 157 for (PackageElement pkg : configuration.packages) { 158 currentPackage = pkg; 159 160 buildPackageSerializedForm(serializedSummariesTree); 161 } 162 writer.addSerializedContent(serializedSummariesTree); 163 } 164 165 /** 166 * Build the package serialized form for the current package being processed. 167 * 168 * @param serializedSummariesTree content tree to which the documentation will be added 169 * @throws DocletException if there is a problem while building the documentation 170 */ 171 protected void buildPackageSerializedForm(Content serializedSummariesTree) throws DocletException { 172 Content packageSerializedTree = writer.getPackageSerializedHeader(); 173 SortedSet<TypeElement> classes = utils.getAllClassesUnfiltered(currentPackage); 174 if (classes.isEmpty()) { 175 return; 176 } 177 if (!serialInclude(utils, currentPackage)) { 178 return; 179 } 180 if (!serialClassFoundToDocument(classes)) { 181 return; 182 } 183 184 buildPackageHeader(packageSerializedTree); 185 buildClassSerializedForm(packageSerializedTree); 186 187 writer.addPackageSerializedTree(serializedSummariesTree, packageSerializedTree); 188 } 189 190 /** 191 * Build the package header. 192 * 193 * @param packageSerializedTree content tree to which the documentation will be added 194 */ 195 protected void buildPackageHeader(Content packageSerializedTree) { 196 packageSerializedTree.add(writer.getPackageHeader( 197 utils.getPackageName(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 */ 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 */ 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.constantValueExpresion(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 */ 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 */ 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 */ 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 */ 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 */ 326 protected void buildMethodInfo(Content methodsContentTree) throws DocletException { 327 if (configuration.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 */ 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 */ 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 (configuration.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 */ 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 */ 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 (!configuration.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 */ 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 */ 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 */ 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 */ 464 protected void buildSerialFieldTagsInfo(Content serializableFieldsTree) { 465 if (configuration.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.makeSerialFieldTreeComparator()); 474 // sort the elements 475 for (DocTree dt : utils.getSerialFieldTrees(field)) { 476 SerialFieldTree st = (SerialFieldTree) dt; 477 tags.add(st); 478 } 479 480 CommentHelper ch = utils.getCommentHelper(field); 481 for (SerialFieldTree tag : tags) { 482 if (tag.getName() == null || tag.getType() == null) // ignore malformed @serialField tags 483 continue; 484 Content fieldsContentTree = fieldWriter.getFieldsContentHeader(tag.equals(tags.last())); 485 TypeElement te = ch.getReferencedClass(configuration, tag); 486 String fieldType = ch.getReferencedMemberName(tag); 487 if (te != null && utils.isPrimitive(te.asType())) { 488 fieldType = utils.getTypeName(te.asType(), false); 489 te = null; 490 } 491 String refSignature = ch.getReferencedSignature(tag); 492 // TODO: Print the signature directly, if it is an array, the 493 // current DocTree APIs makes it very hard to distinguish 494 // an as these are returned back as "Array" a DeclaredType. 495 if (refSignature.endsWith("[]")) { 496 te = null; 497 fieldType = refSignature; 498 } 499 fieldWriter.addMemberHeader(te, fieldType, "", 500 tag.getName().getName().toString(), fieldsContentTree); 501 fieldWriter.addMemberDescription(field, tag, fieldsContentTree); 502 serializableFieldsTree.add(fieldsContentTree); 503 } 504 } 505 506 /** 507 * Build the field information. 508 * 509 * @param fieldsContentTree content tree to which the documentation will be added 510 */ 511 protected void buildFieldInfo(Content fieldsContentTree) { 512 if (configuration.nocomment) { 513 return; 514 } 515 VariableElement field = (VariableElement)currentMember; 516 TypeElement te = utils.getEnclosingTypeElement(currentMember); 517 // Process default Serializable field. 518 if ((utils.getSerialTrees(field).isEmpty()) /*&& !field.isSynthetic()*/ 519 && configuration.serialwarn) { 520 messages.warning(field, 521 "doclet.MissingSerialTag", utils.getFullyQualifiedName(te), 522 utils.getSimpleName(field)); 523 } 524 fieldWriter.addMemberDescription(field, fieldsContentTree); 525 fieldWriter.addMemberTags(field, fieldsContentTree); 526 } 527 528 /** 529 * Returns true if the given Element should be included 530 * in the serialized form. 531 * 532 * @param utils the utils object 533 * @param element the Element object to check for serializability 534 * @return true if the element should be included in the serial form 535 */ 536 public static boolean serialInclude(Utils utils, Element element) { 537 if (element == null) { 538 return false; 539 } 540 return utils.isClass(element) 541 ? serialClassInclude(utils, (TypeElement)element) 542 : serialDocInclude(utils, element); 543 } 544 545 /** 546 * Returns true if the given TypeElement should be included 547 * in the serialized form. 548 * 549 * @param te the TypeElement object to check for serializability. 550 */ 551 private static boolean serialClassInclude(Utils utils, TypeElement te) { 552 if (utils.isEnum(te)) { 553 return false; 554 } 555 if (utils.isSerializable(te)) { 556 if (!utils.getSerialTrees(te).isEmpty()) { 557 return serialDocInclude(utils, te); 558 } else if (utils.isPublic(te) || utils.isProtected(te)) { 559 return true; 560 } else { 561 return false; 562 } 563 } 564 return false; 565 } 566 567 /** 568 * Return true if the given Element should be included 569 * in the serialized form. 570 * 571 * @param element the Element to check for serializability. 572 */ 573 private static boolean serialDocInclude(Utils utils, Element element) { 574 if (utils.isEnum(element)) { 575 return false; 576 } 577 List<? extends DocTree> serial = utils.getSerialTrees(element); 578 if (!serial.isEmpty()) { 579 CommentHelper ch = utils.getCommentHelper(element); 580 String serialtext = Utils.toLowerCase(ch.getText(serial.get(0))); 581 if (serialtext.contains("exclude")) { 582 return false; 583 } else if (serialtext.contains("include")) { 584 return true; 585 } 586 } 587 return true; 588 } 589 590 /** 591 * Return true if any of the given typeElements have a {@code @serial include} tag. 592 * 593 * @param classes the typeElements to check. 594 * @return true if any of the given typeElements have a {@code @serial include} tag. 595 */ 596 private boolean serialClassFoundToDocument(SortedSet<TypeElement> classes) { 597 for (TypeElement aClass : classes) { 598 if (serialClassInclude(utils, aClass)) { 599 return true; 600 } 601 } 602 return false; 603 } 604 }