blob: 57a707a1e09b27cca9403ed701530b5010c8abfd [file] [log] [blame]
/*
* Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.javadoc.internal.doclets.toolkit.builders;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import com.sun.source.doctree.DocTree;
import com.sun.source.doctree.SerialFieldTree;
import jdk.javadoc.internal.doclets.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.DocletException;
import jdk.javadoc.internal.doclets.toolkit.SerializedFormWriter;
import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
/**
* Builds the serialized form.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*
* @author Jamie Ho
* @author Bhavesh Patel (Modified)
*/
public class SerializedFormBuilder extends AbstractBuilder {
/**
* The writer for this builder.
*/
private SerializedFormWriter writer;
/**
* The writer for serializable fields.
*/
private SerializedFormWriter.SerialFieldWriter fieldWriter;
/**
* The writer for serializable method documentation.
*/
private SerializedFormWriter.SerialMethodWriter methodWriter;
/**
* The header for the serial version UID. Save the string
* here instead of the properties file because we do not want
* this string to be localized.
*/
private static final String SERIAL_VERSION_UID = "serialVersionUID";
private static final String SERIAL_VERSION_UID_HEADER = SERIAL_VERSION_UID + ":";
/**
* The current package being documented.
*/
private PackageElement currentPackage;
/**
* The current class being documented.
*/
private TypeElement currentTypeElement;
/**
* The current member being documented.
*/
protected Element currentMember;
/**
* The content that will be added to the serialized form documentation tree.
*/
private Content contentTree;
/**
* Construct a new SerializedFormBuilder.
* @param context the build context.
*/
private SerializedFormBuilder(Context context) {
super(context);
}
/**
* Construct a new SerializedFormBuilder.
*
* @param context the build context.
* @return the new SerializedFormBuilder
*/
public static SerializedFormBuilder getInstance(Context context) {
return new SerializedFormBuilder(context);
}
/**
* Build the serialized form.
*
* @throws DocletException if there is a problem while building the documentation
*/
@Override
public void build() throws DocletException {
SortedSet<TypeElement> rootclasses = new TreeSet<>(utils.makeGeneralPurposeComparator());
rootclasses.addAll(configuration.getIncludedTypeElements());
if (!serialClassFoundToDocument(rootclasses)) {
//Nothing to document.
return;
}
writer = configuration.getWriterFactory().getSerializedFormWriter();
if (writer == null) {
//Doclet does not support this output.
return;
}
buildSerializedForm(contentTree);
}
/**
* Build the serialized form.
*
* @param serializedTree content tree to which the documentation will be added
* @throws DocletException if there is a problem while building the documentation
*/
protected void buildSerializedForm(Content serializedTree) throws DocletException {
serializedTree = writer.getHeader(configuration.getText(
"doclet.Serialized_Form"));
buildSerializedFormSummaries(serializedTree);
writer.addFooter(serializedTree);
writer.printDocument(serializedTree);
}
/**
* Build the serialized form summaries.
*
* @param serializedTree content tree to which the documentation will be added
* @throws DocletException if there is a problem while building the documentation
*/
protected void buildSerializedFormSummaries(Content serializedTree)
throws DocletException {
Content serializedSummariesTree = writer.getSerializedSummariesHeader();
for (PackageElement pkg : configuration.packages) {
currentPackage = pkg;
buildPackageSerializedForm(serializedSummariesTree);
}
serializedTree.addContent(writer.getSerializedContent(
serializedSummariesTree));
}
/**
* Build the package serialized form for the current package being processed.
*
* @param serializedSummariesTree content tree to which the documentation will be added
* @throws DocletException if there is a problem while building the documentation
*/
protected void buildPackageSerializedForm(Content serializedSummariesTree) throws DocletException {
Content packageSerializedTree = writer.getPackageSerializedHeader();
SortedSet<TypeElement> classes = utils.getAllClassesUnfiltered(currentPackage);
if (classes.isEmpty()) {
return;
}
if (!serialInclude(utils, currentPackage)) {
return;
}
if (!serialClassFoundToDocument(classes)) {
return;
}
buildPackageHeader(packageSerializedTree);
buildClassSerializedForm(packageSerializedTree);
writer.addPackageSerializedTree(serializedSummariesTree, packageSerializedTree);
}
/**
* Build the package header.
*
* @param packageSerializedTree content tree to which the documentation will be added
*/
protected void buildPackageHeader(Content packageSerializedTree) {
packageSerializedTree.addContent(writer.getPackageHeader(
utils.getPackageName(currentPackage)));
}
/**
* Build the class serialized form.
*
* @param packageSerializedTree content tree to which the documentation will be added
* @throws DocletException if there is a problem while building the documentation
*/
protected void buildClassSerializedForm(Content packageSerializedTree)
throws DocletException {
Content classSerializedTree = writer.getClassSerializedHeader();
SortedSet<TypeElement> typeElements = utils.getAllClassesUnfiltered(currentPackage);
for (TypeElement typeElement : typeElements) {
currentTypeElement = typeElement;
fieldWriter = writer.getSerialFieldWriter(currentTypeElement);
methodWriter = writer.getSerialMethodWriter(currentTypeElement);
if (utils.isClass(currentTypeElement) && utils.isSerializable(currentTypeElement)) {
if (!serialClassInclude(utils, currentTypeElement)) {
continue;
}
Content classTree = writer.getClassHeader(currentTypeElement);
buildSerialUIDInfo(classTree);
buildClassContent(classTree);
classSerializedTree.addContent(classTree);
}
}
packageSerializedTree.addContent(classSerializedTree);
}
/**
* Build the serial UID information for the given class.
*
* @param classTree content tree to which the serial UID information will be added
*/
protected void buildSerialUIDInfo(Content classTree) {
Content serialUidTree = writer.getSerialUIDInfoHeader();
for (Element e : utils.getFieldsUnfiltered(currentTypeElement)) {
VariableElement field = (VariableElement)e;
if (field.getSimpleName().toString().compareTo(SERIAL_VERSION_UID) == 0 &&
field.getConstantValue() != null) {
writer.addSerialUIDInfo(SERIAL_VERSION_UID_HEADER,
utils.constantValueExpresion(field), serialUidTree);
break;
}
}
classTree.addContent(serialUidTree);
}
/**
* Build the summaries for the methods and fields.
*
* @param classTree content tree to which the documentation will be added
* @throws DocletException if there is a problem while building the documentation
*/
protected void buildClassContent(Content classTree) throws DocletException {
Content classContentTree = writer.getClassContentHeader();
buildSerializableMethods(classContentTree);
buildFieldHeader(classContentTree);
buildSerializableFields(classContentTree);
classTree.addContent(classContentTree);
}
/**
* Build the summaries for the methods that belong to the given class.
*
* @param classContentTree content tree to which the documentation will be added
* @throws DocletException if there is a problem while building the documentation
*/
protected void buildSerializableMethods(Content classContentTree) throws DocletException {
Content serializableMethodTree = methodWriter.getSerializableMethodsHeader();
SortedSet<ExecutableElement> members = utils.serializationMethods(currentTypeElement);
if (!members.isEmpty()) {
for (ExecutableElement member : members) {
currentMember = member;
Content methodsContentTree = methodWriter.getMethodsContentHeader(
currentMember == members.last());
buildMethodSubHeader(methodsContentTree);
buildDeprecatedMethodInfo(methodsContentTree);
buildMethodInfo(methodsContentTree);
serializableMethodTree.addContent(methodsContentTree);
}
}
if (!utils.serializationMethods(currentTypeElement).isEmpty()) {
classContentTree.addContent(methodWriter.getSerializableMethods(
configuration.getText("doclet.Serialized_Form_methods"),
serializableMethodTree));
if (utils.isSerializable(currentTypeElement) && !utils.isExternalizable(currentTypeElement)) {
if (utils.serializationMethods(currentTypeElement).isEmpty()) {
Content noCustomizationMsg = methodWriter.getNoCustomizationMsg(
configuration.getText("doclet.Serializable_no_customization"));
classContentTree.addContent(methodWriter.getSerializableMethods(
configuration.getText("doclet.Serialized_Form_methods"),
noCustomizationMsg));
}
}
}
}
/**
* Build the method sub header.
*
* @param methodsContentTree content tree to which the documentation will be added
*/
protected void buildMethodSubHeader(Content methodsContentTree) {
methodWriter.addMemberHeader((ExecutableElement)currentMember, methodsContentTree);
}
/**
* Build the deprecated method description.
*
* @param methodsContentTree content tree to which the documentation will be added
*/
protected void buildDeprecatedMethodInfo(Content methodsContentTree) {
methodWriter.addDeprecatedMemberInfo((ExecutableElement)currentMember, methodsContentTree);
}
/**
* Build the information for the method.
*
* @param methodsContentTree content tree to which the documentation will be added
* @throws DocletException if there is a problem while building the documentation
*/
protected void buildMethodInfo(Content methodsContentTree) throws DocletException {
if (configuration.nocomment) {
return;
}
buildMethodDescription(methodsContentTree);
buildMethodTags(methodsContentTree);
}
/**
* Build method description.
*
* @param methodsContentTree content tree to which the documentation will be added
*/
protected void buildMethodDescription(Content methodsContentTree) {
methodWriter.addMemberDescription((ExecutableElement)currentMember, methodsContentTree);
}
/**
* Build the method tags.
*
* @param methodsContentTree content tree to which the documentation will be added
*/
protected void buildMethodTags(Content methodsContentTree) {
methodWriter.addMemberTags((ExecutableElement)currentMember, methodsContentTree);
ExecutableElement method = (ExecutableElement)currentMember;
if (method.getSimpleName().toString().compareTo("writeExternal") == 0
&& utils.getSerialDataTrees(method).isEmpty()) {
if (configuration.serialwarn) {
TypeElement encl = (TypeElement) method.getEnclosingElement();
messages.warning(currentMember,
"doclet.MissingSerialDataTag", encl.getQualifiedName().toString(),
method.getSimpleName().toString());
}
}
}
/**
* Build the field header.
*
* @param classContentTree content tree to which the documentation will be added
*/
protected void buildFieldHeader(Content classContentTree) {
if (!utils.serializableFields(currentTypeElement).isEmpty()) {
buildFieldSerializationOverview(currentTypeElement, classContentTree);
}
}
/**
* Build the serialization overview for the given class.
*
* @param typeElement the class to print the overview for.
* @param classContentTree content tree to which the documentation will be added
*/
public void buildFieldSerializationOverview(TypeElement typeElement, Content classContentTree) {
if (utils.definesSerializableFields(typeElement)) {
VariableElement ve = utils.serializableFields(typeElement).first();
// Check to see if there are inline comments, tags or deprecation
// information to be printed.
if (fieldWriter.shouldPrintOverview(ve)) {
Content serializableFieldsTree = fieldWriter.getSerializableFieldsHeader();
Content fieldsOverviewContentTree = fieldWriter.getFieldsContentHeader(true);
fieldWriter.addMemberDeprecatedInfo(ve, fieldsOverviewContentTree);
if (!configuration.nocomment) {
fieldWriter.addMemberDescription(ve, fieldsOverviewContentTree);
fieldWriter.addMemberTags(ve, fieldsOverviewContentTree);
}
serializableFieldsTree.addContent(fieldsOverviewContentTree);
classContentTree.addContent(fieldWriter.getSerializableFields(
configuration.getText("doclet.Serialized_Form_class"),
serializableFieldsTree));
}
}
}
/**
* Build the summaries for the fields that belong to the given class.
*
* @param classContentTree content tree to which the documentation will be added
* @throws DocletException if there is a problem while building the documentation
*/
protected void buildSerializableFields(Content classContentTree)
throws DocletException {
SortedSet<VariableElement> members = utils.serializableFields(currentTypeElement);
if (!members.isEmpty()) {
Content serializableFieldsTree = fieldWriter.getSerializableFieldsHeader();
for (VariableElement ve : members) {
currentMember = ve;
if (!utils.definesSerializableFields(currentTypeElement)) {
Content fieldsContentTree = fieldWriter.getFieldsContentHeader(
currentMember == members.last());
buildFieldSubHeader(fieldsContentTree);
buildFieldDeprecationInfo(fieldsContentTree);
buildFieldInfo(fieldsContentTree);
serializableFieldsTree.addContent(fieldsContentTree);
} else {
buildSerialFieldTagsInfo(serializableFieldsTree);
}
}
classContentTree.addContent(fieldWriter.getSerializableFields(
configuration.getText("doclet.Serialized_Form_fields"),
serializableFieldsTree));
}
}
/**
* Build the field sub header.
*
* @param fieldsContentTree content tree to which the documentation will be added
*/
protected void buildFieldSubHeader(Content fieldsContentTree) {
if (!utils.definesSerializableFields(currentTypeElement)) {
VariableElement field = (VariableElement) currentMember;
fieldWriter.addMemberHeader(field.asType(),
utils.getSimpleName(field),
fieldsContentTree);
}
}
/**
* Build the field deprecation information.
*
* @param fieldsContentTree content tree to which the documentation will be added
*/
protected void buildFieldDeprecationInfo(Content fieldsContentTree) {
if (!utils.definesSerializableFields(currentTypeElement)) {
fieldWriter.addMemberDeprecatedInfo((VariableElement)currentMember,
fieldsContentTree);
}
}
/**
* Build the serial field tags information.
*
* @param serializableFieldsTree content tree to which the documentation will be added
*/
protected void buildSerialFieldTagsInfo(Content serializableFieldsTree) {
if (configuration.nocomment) {
return;
}
VariableElement field = (VariableElement)currentMember;
// Process Serializable Fields specified as array of
// ObjectStreamFields. Print a member for each serialField tag.
// (There should be one serialField tag per ObjectStreamField
// element.)
SortedSet<SerialFieldTree> tags = new TreeSet<>(utils.makeSerialFieldTreeComparator());
// sort the elements
for (DocTree dt : utils.getSerialFieldTrees(field)) {
SerialFieldTree st = (SerialFieldTree) dt;
tags.add(st);
}
CommentHelper ch = utils.getCommentHelper(field);
for (SerialFieldTree tag : tags) {
if (tag.getName() == null || tag.getType() == null) // ignore malformed @serialField tags
continue;
Content fieldsContentTree = fieldWriter.getFieldsContentHeader(tag.equals(tags.last()));
TypeElement te = ch.getReferencedClass(configuration, tag);
String fieldType = ch.getReferencedMemberName(tag);
if (te != null && utils.isPrimitive(te.asType())) {
fieldType = utils.getTypeName(te.asType(), false);
te = null;
}
String refSignature = ch.getReferencedSignature(tag);
// TODO: Print the signature directly, if it is an array, the
// current DocTree APIs makes it very hard to distinguish
// an as these are returned back as "Array" a DeclaredType.
if (refSignature.endsWith("[]")) {
te = null;
fieldType = refSignature;
}
fieldWriter.addMemberHeader(te, fieldType, "",
tag.getName().getName().toString(), fieldsContentTree);
fieldWriter.addMemberDescription(field, tag, fieldsContentTree);
serializableFieldsTree.addContent(fieldsContentTree);
}
}
/**
* Build the field information.
*
* @param fieldsContentTree content tree to which the documentation will be added
*/
protected void buildFieldInfo(Content fieldsContentTree) {
if (configuration.nocomment) {
return;
}
VariableElement field = (VariableElement)currentMember;
TypeElement te = utils.getEnclosingTypeElement(currentMember);
// Process default Serializable field.
if ((utils.getSerialTrees(field).isEmpty()) /*&& !field.isSynthetic()*/
&& configuration.serialwarn) {
messages.warning(field,
"doclet.MissingSerialTag", utils.getFullyQualifiedName(te),
utils.getSimpleName(field));
}
fieldWriter.addMemberDescription(field, fieldsContentTree);
fieldWriter.addMemberTags(field, fieldsContentTree);
}
/**
* Returns true if the given Element should be included
* in the serialized form.
*
* @param utils the utils object
* @param element the Element object to check for serializability
* @return true if the element should be included in the serial form
*/
public static boolean serialInclude(Utils utils, Element element) {
if (element == null) {
return false;
}
return utils.isClass(element)
? serialClassInclude(utils, (TypeElement)element)
: serialDocInclude(utils, element);
}
/**
* Returns true if the given TypeElement should be included
* in the serialized form.
*
* @param te the TypeElement object to check for serializability.
*/
private static boolean serialClassInclude(Utils utils, TypeElement te) {
if (utils.isEnum(te)) {
return false;
}
if (utils.isSerializable(te)) {
if (!utils.getSerialTrees(te).isEmpty()) {
return serialDocInclude(utils, te);
} else if (utils.isPublic(te) || utils.isProtected(te)) {
return true;
} else {
return false;
}
}
return false;
}
/**
* Return true if the given Element should be included
* in the serialized form.
*
* @param element the Element to check for serializability.
*/
private static boolean serialDocInclude(Utils utils, Element element) {
if (utils.isEnum(element)) {
return false;
}
List<? extends DocTree> serial = utils.getSerialTrees(element);
if (!serial.isEmpty()) {
CommentHelper ch = utils.getCommentHelper(element);
String serialtext = Utils.toLowerCase(ch.getText(serial.get(0)));
if (serialtext.contains("exclude")) {
return false;
} else if (serialtext.contains("include")) {
return true;
}
}
return true;
}
/**
* Return true if any of the given typeElements have a @serialinclude tag.
*
* @param classes the typeElements to check.
* @return true if any of the given typeElements have a @serialinclude tag.
*/
private boolean serialClassFoundToDocument(SortedSet<TypeElement> classes) {
for (TypeElement aClass : classes) {
if (serialClassInclude(utils, aClass)) {
return true;
}
}
return false;
}
}