duke@1: /*
jjg@1357: * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@1: *
duke@1: * This code is free software; you can redistribute it and/or modify it
duke@1: * under the terms of the GNU General Public License version 2 only, as
ohair@554: * published by the Free Software Foundation. Oracle designates this
duke@1: * particular file as subject to the "Classpath" exception as provided
ohair@554: * by Oracle in the LICENSE file that accompanied this code.
duke@1: *
duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT
duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@1: * version 2 for more details (a copy is included in the LICENSE file that
duke@1: * accompanied this code).
duke@1: *
duke@1: * You should have received a copy of the GNU General Public License version
duke@1: * 2 along with this work; if not, write to the Free Software Foundation,
duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@1: *
ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
ohair@554: * or visit www.oracle.com if you need additional information or have any
ohair@554: * questions.
duke@1: */
duke@1:
duke@1: package com.sun.tools.javadoc;
duke@1:
duke@1: import com.sun.javadoc.*;
duke@1: import com.sun.tools.javac.code.Flags;
duke@1: import com.sun.tools.javac.code.Kinds;
duke@1: import com.sun.tools.javac.code.Scope;
duke@1: import com.sun.tools.javac.code.Symbol.ClassSymbol;
duke@1: import com.sun.tools.javac.code.Symbol.MethodSymbol;
jjg@1357: import com.sun.tools.javac.code.Symbol.VarSymbol;
jjg@1357: import com.sun.tools.javac.util.ListBuffer;
duke@1: import com.sun.tools.javac.util.Name;
jjg@113: import com.sun.tools.javac.util.Names;
duke@1:
duke@1: /**
duke@1: * The serialized form is the specification of a class' serialization
duke@1: * state.
duke@1: *
duke@1: * It consists of the following information:
duke@1: *
duke@1: *
duke@1: * 1. Whether class is Serializable or Externalizable.
duke@1: * 2. Javadoc for serialization methods.
duke@1: * a. For Serializable, the optional readObject, writeObject,
duke@1: * readResolve and writeReplace.
duke@1: * serialData tag describes, in prose, the sequence and type
duke@1: * of optional data written by writeObject.
duke@1: * b. For Externalizable, writeExternal and readExternal.
duke@1: * serialData tag describes, in prose, the sequence and type
duke@1: * of optional data written by writeExternal.
duke@1: * 3. Javadoc for serialization data layout.
duke@1: * a. For Serializable, the name,type and description
duke@1: * of each Serializable fields.
duke@1: * b. For Externalizable, data layout is described by 2(b).
duke@1: *
duke@1: *
jjg@1359: * This is NOT part of any supported API.
jjg@1359: * If you write code that depends on this, you do so at your own risk.
jjg@1359: * This code and its internal interfaces are subject to change or
jjg@1359: * deletion without notice.
jjg@1359: *
duke@1: * @since 1.2
duke@1: * @author Joe Fialli
duke@1: * @author Neal Gafter (rewrite but not too proud)
duke@1: */
duke@1: class SerializedForm {
duke@1: ListBuffer methods = new ListBuffer();
duke@1:
duke@1: /* List of FieldDocImpl - Serializable fields.
duke@1: * Singleton list if class defines Serializable fields explicitly.
duke@1: * Otherwise, list of default serializable fields.
duke@1: * 0 length list for Externalizable.
duke@1: */
duke@1: private final ListBuffer fields = new ListBuffer();
duke@1:
duke@1: /* True if class specifies serializable fields explicitly.
duke@1: * using special static member, serialPersistentFields.
duke@1: */
duke@1: private boolean definesSerializableFields = false;
duke@1:
duke@1: // Specially treated field/method names defined by Serialization.
duke@1: private static final String SERIALIZABLE_FIELDS = "serialPersistentFields";
duke@1: private static final String READOBJECT = "readObject";
duke@1: private static final String WRITEOBJECT = "writeObject";
duke@1: private static final String READRESOLVE = "readResolve";
duke@1: private static final String WRITEREPLACE = "writeReplace";
duke@1: private static final String READOBJECTNODATA = "readObjectNoData";
duke@1:
duke@1: /**
duke@1: * Constructor.
duke@1: *
duke@1: * Catalog Serializable fields for Serializable class.
duke@1: * Catalog serialization methods for Serializable and
duke@1: * Externalizable classes.
duke@1: */
duke@1: SerializedForm(DocEnv env, ClassSymbol def, ClassDocImpl cd) {
duke@1: if (cd.isExternalizable()) {
duke@1: /* look up required public accessible methods,
duke@1: * writeExternal and readExternal.
duke@1: */
duke@1: String[] readExternalParamArr = { "java.io.ObjectInput" };
duke@1: String[] writeExternalParamArr = { "java.io.ObjectOutput" };
duke@1: MethodDoc md = cd.findMethod("readExternal", readExternalParamArr);
duke@1: if (md != null) {
duke@1: methods.append(md);
duke@1: }
duke@1: md = cd.findMethod("writeExternal", writeExternalParamArr);
duke@1: if (md != null) {
duke@1: methods.append(md);
duke@1: Tag tag[] = md.tags("serialData");
duke@1: }
duke@1: // } else { // isSerializable() //### ???
duke@1: } else if (cd.isSerializable()) {
duke@1:
duke@1: VarSymbol dsf = getDefinedSerializableFields(def);
duke@1: if (dsf != null) {
duke@1:
duke@1: /* Define serializable fields with array of ObjectStreamField.
duke@1: * Each ObjectStreamField should be documented by a
duke@1: * serialField tag.
duke@1: */
duke@1: definesSerializableFields = true;
duke@1: //### No modifier filtering applied here.
duke@1: FieldDocImpl dsfDoc = env.getFieldDoc(dsf);
duke@1: fields.append(dsfDoc);
duke@1: mapSerialFieldTagImplsToFieldDocImpls(dsfDoc, env, def);
duke@1: } else {
duke@1:
duke@1: /* Calculate default Serializable fields as all
duke@1: * non-transient, non-static fields.
duke@1: * Fields should be documented by serial tag.
duke@1: */
duke@1: computeDefaultSerializableFields(env, def, cd);
duke@1: }
duke@1:
duke@1: /* Check for optional customized readObject, writeObject,
duke@1: * readResolve and writeReplace, which can all contain
duke@1: * the serialData tag. */
duke@1: addMethodIfExist(env, def, READOBJECT);
duke@1: addMethodIfExist(env, def, WRITEOBJECT);
duke@1: addMethodIfExist(env, def, READRESOLVE);
duke@1: addMethodIfExist(env, def, WRITEREPLACE);
duke@1: addMethodIfExist(env, def, READOBJECTNODATA);
duke@1: }
duke@1: }
duke@1:
duke@1: /*
duke@1: * Check for explicit Serializable fields.
duke@1: * Check for a private static array of ObjectStreamField with
duke@1: * name SERIALIZABLE_FIELDS.
duke@1: */
duke@1: private VarSymbol getDefinedSerializableFields(ClassSymbol def) {
jjg@113: Names names = def.name.table.names;
duke@1:
duke@1: /* SERIALIZABLE_FIELDS can be private,
duke@1: * so must lookup by ClassSymbol, not by ClassDocImpl.
duke@1: */
duke@1: for (Scope.Entry e = def.members().lookup(names.fromString(SERIALIZABLE_FIELDS)); e.scope != null; e = e.next()) {
duke@1: if (e.sym.kind == Kinds.VAR) {
duke@1: VarSymbol f = (VarSymbol)e.sym;
duke@1: if ((f.flags() & Flags.STATIC) != 0 &&
duke@1: (f.flags() & Flags.PRIVATE) != 0) {
duke@1: return f;
duke@1: }
duke@1: }
duke@1: }
duke@1: return null;
duke@1: }
duke@1:
duke@1: /*
duke@1: * Compute default Serializable fields from all members of ClassSymbol.
duke@1: *
duke@1: * Since the fields of ClassDocImpl might not contain private or
duke@1: * package accessible fields, must walk over all members of ClassSymbol.
duke@1: */
duke@1: private void computeDefaultSerializableFields(DocEnv env,
duke@1: ClassSymbol def,
duke@1: ClassDocImpl cd) {
duke@1: for (Scope.Entry e = def.members().elems; e != null; e = e.sibling) {
duke@1: if (e.sym != null && e.sym.kind == Kinds.VAR) {
duke@1: VarSymbol f = (VarSymbol)e.sym;
duke@1: if ((f.flags() & Flags.STATIC) == 0 &&
duke@1: (f.flags() & Flags.TRANSIENT) == 0) {
duke@1: //### No modifier filtering applied here.
duke@1: FieldDocImpl fd = env.getFieldDoc(f);
duke@1: //### Add to beginning.
duke@1: //### Preserve order used by old 'javadoc'.
duke@1: fields.prepend(fd);
duke@1: }
duke@1: }
duke@1: }
duke@1: }
duke@1:
duke@1: /*
duke@1: * Catalog Serializable method if it exists in current ClassSymbol.
duke@1: * Do not look for method in superclasses.
duke@1: *
duke@1: * Serialization requires these methods to be non-static.
duke@1: *
duke@1: * @param method should be an unqualified Serializable method
duke@1: * name either READOBJECT, WRITEOBJECT, READRESOLVE
duke@1: * or WRITEREPLACE.
duke@1: * @param visibility the visibility flag for the given method.
duke@1: */
duke@1: private void addMethodIfExist(DocEnv env, ClassSymbol def, String methodName) {
jjg@113: Names names = def.name.table.names;
duke@1:
duke@1: for (Scope.Entry e = def.members().lookup(names.fromString(methodName)); e.scope != null; e = e.next()) {
duke@1: if (e.sym.kind == Kinds.MTH) {
duke@1: MethodSymbol md = (MethodSymbol)e.sym;
duke@1: if ((md.flags() & Flags.STATIC) == 0) {
duke@1: /*
duke@1: * WARNING: not robust if unqualifiedMethodName is overloaded
duke@1: * method. Signature checking could make more robust.
duke@1: * READOBJECT takes a single parameter, java.io.ObjectInputStream.
duke@1: * WRITEOBJECT takes a single parameter, java.io.ObjectOutputStream.
duke@1: */
duke@1: methods.append(env.getMethodDoc(md));
duke@1: }
duke@1: }
duke@1: }
duke@1: }
duke@1:
duke@1: /*
duke@1: * Associate serialField tag fieldName with FieldDocImpl member.
duke@1: * Note: A serialField tag does not have to map an existing field
duke@1: * of a class.
duke@1: */
duke@1: private void mapSerialFieldTagImplsToFieldDocImpls(FieldDocImpl spfDoc,
duke@1: DocEnv env,
duke@1: ClassSymbol def) {
jjg@113: Names names = def.name.table.names;
duke@1:
duke@1: SerialFieldTag[] sfTag = spfDoc.serialFieldTags();
duke@1: for (int i = 0; i < sfTag.length; i++) {
jjg@1372: if (sfTag[i].fieldName() == null || sfTag[i].fieldType() == null) // ignore malformed @serialField tags
jjg@1372: continue;
jjg@1372:
duke@1: Name fieldName = names.fromString(sfTag[i].fieldName());
duke@1:
duke@1: // Look for a FieldDocImpl that is documented by serialFieldTagImpl.
duke@1: for (Scope.Entry e = def.members().lookup(fieldName); e.scope != null; e = e.next()) {
duke@1: if (e.sym.kind == Kinds.VAR) {
duke@1: VarSymbol f = (VarSymbol)e.sym;
duke@1: FieldDocImpl fdi = env.getFieldDoc(f);
duke@1: ((SerialFieldTagImpl)(sfTag[i])).mapToFieldDocImpl(fdi);
duke@1: break;
duke@1: }
duke@1: }
duke@1: }
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return serializable fields in class.
duke@1: *
duke@1: * Returns either a list of default fields documented by serial tag comment or
duke@1: * javadoc comment
duke@1: * Or Returns a single FieldDocImpl for serialPersistentField. There is a
duke@1: * serialField tag for each serializable field.
duke@1: *
duke@1: * @return an array of FieldDocImpl for representing the visible
duke@1: * fields in this class.
duke@1: */
duke@1: FieldDoc[] fields() {
duke@1: return (FieldDoc[])fields.toArray(new FieldDocImpl[fields.length()]);
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return serialization methods in class.
duke@1: *
duke@1: * @return an array of MethodDocImpl for serialization methods in this class.
duke@1: */
duke@1: MethodDoc[] methods() {
jjg@74: return methods.toArray(new MethodDoc[methods.length()]);
duke@1: }
duke@1:
duke@1: /**
duke@1: * Returns true if Serializable fields are defined explicitly using
duke@1: * member, serialPersistentFields.
duke@1: *
duke@1: * @see #fields()
duke@1: */
duke@1: boolean definesSerializableFields() {
duke@1: return definesSerializableFields;
duke@1: }
duke@1: }