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 }