001: /*
002: * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.tools.doclets.internal.toolkit.builders;
027:
028: import com.sun.tools.doclets.internal.toolkit.util.*;
029: import com.sun.tools.doclets.internal.toolkit.*;
030: import com.sun.javadoc.*;
031: import java.io.*;
032: import java.lang.reflect.*;
033: import java.util.*;
034:
035: /**
036: * Builds the serialized form.
037: *
038: * This code is not part of an API.
039: * It is implementation that is subject to change.
040: * Do not use it as an API
041: *
042: * @author Jamie Ho
043: * @since 1.5
044: */
045: public class SerializedFormBuilder extends AbstractBuilder {
046:
047: /**
048: * The root element of the serialized form XML is {@value}.
049: */
050: public static final String NAME = "SerializedForm";
051:
052: /**
053: * The writer for this builder.
054: */
055: private SerializedFormWriter writer;
056:
057: /**
058: * The writer for serializable fields.
059: */
060: private SerializedFormWriter.SerialFieldWriter fieldWriter;
061:
062: /**
063: * The writer for serializable method documentation.
064: */
065: private SerializedFormWriter.SerialMethodWriter methodWriter;
066:
067: /**
068: * The header for the serial version UID. Save the string
069: * here instead of the properties file because we do not want
070: * this string to be localized.
071: */
072: private static final String SERIAL_VERSION_UID_HEADER = "serialVersionUID:";
073:
074: /**
075: * The current package being documented.
076: */
077: private PackageDoc currentPackage;
078:
079: /**
080: * The current class being documented.
081: */
082: private ClassDoc currentClass;
083:
084: /**
085: * The current member being documented.
086: */
087: protected MemberDoc currentMember;
088:
089: private SerializedFormBuilder(Configuration configuration) {
090: super (configuration);
091: }
092:
093: /**
094: * Construct a new SerializedFormBuilder.
095: * @param configuration the current configuration of the doclet.
096: */
097: public static SerializedFormBuilder getInstance(
098: Configuration configuration) {
099: SerializedFormBuilder builder = new SerializedFormBuilder(
100: configuration);
101: return builder;
102: }
103:
104: /**
105: * Build the serialized form.
106: */
107: public void build() throws IOException {
108: if (!serialClassFoundToDocument(configuration.root.classes())) {
109: //Nothing to document.
110: return;
111: }
112: try {
113: writer = configuration.getWriterFactory()
114: .getSerializedFormWriter();
115: if (writer == null) {
116: //Doclet does not support this output.
117: return;
118: }
119: } catch (Exception e) {
120: throw new DocletAbortException();
121: }
122: build(LayoutParser.getInstance(configuration).parseXML(NAME));
123: writer.close();
124: }
125:
126: /**
127: * {@inheritDoc}
128: */
129: public String getName() {
130: return NAME;
131: }
132:
133: /**
134: * Build the serialized form.
135: */
136: public void buildSerializedForm(List elements) throws Exception {
137: build(elements);
138: writer.close();
139: }
140:
141: /**
142: * {@inheritDoc}
143: */
144: public void invokeMethod(String methodName, Class[] paramClasses,
145: Object[] params) throws Exception {
146: if (DEBUG) {
147: configuration.root.printError("DEBUG: "
148: + this .getClass().getName() + "." + methodName);
149: }
150: Method method = this .getClass().getMethod(methodName,
151: paramClasses);
152: method.invoke(this , params);
153: }
154:
155: /**
156: * Build the header.
157: */
158: public void buildHeader() {
159: writer.writeHeader(configuration
160: .getText("doclet.Serialized_Form"));
161: }
162:
163: /**
164: * Build the contents.
165: */
166: public void buildSerializedFormSummaries(List elements) {
167: PackageDoc[] packages = configuration.packages;
168: for (int i = 0; i < packages.length; i++) {
169: currentPackage = packages[i];
170: build(elements);
171: }
172: }
173:
174: /**
175: * Build the package serialized for for the current package being processed.
176: */
177: public void buildPackageSerializedForm(List elements) {
178: String foo = currentPackage.name();
179: ClassDoc[] classes = currentPackage.allClasses(false);
180: if (classes == null || classes.length == 0) {
181: return;
182: }
183: if (!serialInclude(currentPackage)) {
184: return;
185: }
186: if (!serialClassFoundToDocument(classes)) {
187: return;
188: }
189: build(elements);
190: }
191:
192: public void buildPackageHeader() {
193: writer.writePackageHeader(Util.getPackageName(currentPackage));
194: }
195:
196: public void buildClassSerializedForm(List elements) {
197: ClassDoc[] classes = currentPackage.allClasses(false);
198: Arrays.sort(classes);
199: for (int j = 0; j < classes.length; j++) {
200: currentClass = classes[j];
201: fieldWriter = writer.getSerialFieldWriter(currentClass);
202: methodWriter = writer.getSerialMethodWriter(currentClass);
203: if (currentClass.isClass() && currentClass.isSerializable()) {
204: if (!serialClassInclude(currentClass)) {
205: continue;
206: }
207: build(elements);
208: }
209: }
210: }
211:
212: public void buildClassHeader() {
213: writer.writeClassHeader(currentClass);
214: }
215:
216: /**
217: * Build the serial UID information for the given class.
218: */
219: public void buildSerialUIDInfo() {
220: FieldDoc[] fields = currentClass.fields(false);
221: for (int i = 0; i < fields.length; i++) {
222: if (fields[i].name().equals("serialVersionUID")
223: && fields[i].constantValueExpression() != null) {
224: writer.writeSerialUIDInfo(SERIAL_VERSION_UID_HEADER,
225: fields[i].constantValueExpression());
226: return;
227: }
228: }
229: }
230:
231: /**
232: * Build the footer.
233: */
234: public void buildFooter() {
235: writer.writeFooter();
236: }
237:
238: /**
239: * Return true if the given Doc should be included
240: * in the serialized form.
241: *
242: * @param doc the Doc object to check for serializability.
243: */
244: public static boolean serialInclude(Doc doc) {
245: if (doc == null) {
246: return false;
247: }
248: return doc.isClass() ? serialClassInclude((ClassDoc) doc)
249: : serialDocInclude(doc);
250: }
251:
252: /**
253: * Return true if the given ClassDoc should be included
254: * in the serialized form.
255: *
256: * @param cd the ClassDoc object to check for serializability.
257: */
258: private static boolean serialClassInclude(ClassDoc cd) {
259: if (cd.isEnum()) {
260: return false;
261: }
262: try {
263: cd.super classType();
264: } catch (NullPointerException e) {
265: //Workaround for null pointer bug in ClassDoc.superclassType().
266: return false;
267: }
268: if (cd.isSerializable()) {
269: if (cd.tags("serial").length > 0) {
270: return serialDocInclude(cd);
271: } else if (cd.isPublic() || cd.isProtected()) {
272: return true;
273: } else {
274: return false;
275: }
276: }
277: return false;
278: }
279:
280: /**
281: * Return true if the given Doc should be included
282: * in the serialized form.
283: *
284: * @param doc the Doc object to check for serializability.
285: */
286: private static boolean serialDocInclude(Doc doc) {
287: if (doc.isEnum()) {
288: return false;
289: }
290: Tag[] serial = doc.tags("serial");
291: if (serial.length > 0) {
292: String serialtext = serial[0].text().toLowerCase();
293: if (serialtext.indexOf("exclude") >= 0) {
294: return false;
295: } else if (serialtext.indexOf("include") >= 0) {
296: return true;
297: }
298: }
299: return true;
300: }
301:
302: /**
303: * Return true if any of the given classes have a @serialinclude tag.
304: *
305: * @param classes the classes to check.
306: * @return true if any of the given classes have a @serialinclude tag.
307: */
308: private boolean serialClassFoundToDocument(ClassDoc[] classes) {
309: for (int i = 0; i < classes.length; i++) {
310: if (serialClassInclude(classes[i])) {
311: return true;
312: }
313: }
314: return false;
315: }
316:
317: /**
318: * Build the method header.
319: */
320: public void buildMethodHeader() {
321: if (currentClass.serializationMethods().length > 0) {
322: methodWriter.writeHeader(configuration
323: .getText("doclet.Serialized_Form_methods"));
324: if (currentClass.isSerializable()
325: && !currentClass.isExternalizable()) {
326: if (currentClass.serializationMethods().length == 0) {
327: methodWriter
328: .writeNoCustomizationMsg(configuration
329: .getText("doclet.Serializable_no_customization"));
330: }
331: }
332: }
333: }
334:
335: /**
336: * Build the method sub header.
337: */
338: public void buildMethodSubHeader() {
339: methodWriter.writeMemberHeader((MethodDoc) currentMember);
340: }
341:
342: /**
343: * Build the deprecated method description.
344: */
345: public void buildDeprecatedMethodInfo() {
346: methodWriter
347: .writeDeprecatedMemberInfo((MethodDoc) currentMember);
348: }
349:
350: /**
351: * Build method tags.
352: */
353: public void buildMethodDescription() {
354: methodWriter.writeMemberDescription((MethodDoc) currentMember);
355: }
356:
357: /**
358: * Build the method tags.
359: */
360: public void buildMethodTags() {
361: methodWriter.writeMemberTags((MethodDoc) currentMember);
362: MethodDoc method = (MethodDoc) currentMember;
363: if (method.name().compareTo("writeExternal") == 0
364: && method.tags("serialData").length == 0) {
365: if (configuration.serialwarn) {
366: configuration.getDocletSpecificMsg().warning(
367: currentMember.position(),
368: "doclet.MissingSerialDataTag",
369: method.containingClass().qualifiedName(),
370: method.name());
371: }
372: }
373: }
374:
375: /**
376: * build the information for the method.
377: */
378: public void buildMethodInfo(List elements) {
379: if (configuration.nocomment) {
380: return;
381: }
382: build(elements);
383: }
384:
385: /**
386: * Build the method footer.
387: */
388: public void buildMethodFooter() {
389: methodWriter.writeMemberFooter((MethodDoc) currentMember);
390: }
391:
392: /**
393: * Build the field header.
394: */
395: public void buildFieldHeader() {
396: if (currentClass.serializableFields().length > 0) {
397: buildFieldSerializationOverview(currentClass);
398: fieldWriter.writeHeader(configuration
399: .getText("doclet.Serialized_Form_fields"));
400: }
401: }
402:
403: /**
404: * If possible, build the serialization overview for the given
405: * class.
406: *
407: * @param classDoc the class to print the overview for.
408: */
409: public void buildFieldSerializationOverview(ClassDoc classDoc) {
410: if (classDoc.definesSerializableFields()) {
411: FieldDoc serialPersistentField = (FieldDoc) ((Util
412: .asList(classDoc.serializableFields()).get(0)));
413: String comment = serialPersistentField.commentText();
414: if (comment.length() > 0) {
415: fieldWriter.writeHeader(configuration
416: .getText("doclet.Serialized_Form_class"));
417: if (!configuration.nocomment) {
418: fieldWriter
419: .writeMemberDeprecatedInfo(serialPersistentField);
420: fieldWriter
421: .writeMemberDescription(serialPersistentField);
422: fieldWriter.writeMemberTags(serialPersistentField);
423: fieldWriter
424: .writeMemberFooter(serialPersistentField);
425: }
426: }
427: }
428: }
429:
430: /**
431: * Build the field sub header.
432: */
433: public void buildFieldSubHeader() {
434: if (!currentClass.definesSerializableFields()) {
435: FieldDoc field = (FieldDoc) currentMember;
436: fieldWriter.writeMemberHeader(field.type().asClassDoc(),
437: field.type().typeName(), field.type().dimension(),
438: field.name());
439: }
440: }
441:
442: /**
443: * Build the field information.
444: */
445: public void buildFieldInfo() {
446: if (configuration.nocomment) {
447: return;
448: }
449: FieldDoc field = (FieldDoc) currentMember;
450: ClassDoc cd = field.containingClass();
451: if (cd.definesSerializableFields()) {
452: // Process Serializable Fields specified as array of
453: // ObjectStreamFields. Print a member for each serialField tag.
454: // (There should be one serialField tag per ObjectStreamField
455: // element.)
456: SerialFieldTag[] tags = field.serialFieldTags();
457: Arrays.sort(tags);
458: for (int i = 0; i < tags.length; i++) {
459: fieldWriter.writeMemberHeader(tags[i].fieldTypeDoc(),
460: tags[i].fieldType(), "", tags[i].fieldName());
461: fieldWriter.writeMemberDescription(tags[i]);
462:
463: }
464: } else {
465:
466: // Process default Serializable field.
467: if ((field.tags("serial").length == 0)
468: && !field.isSynthetic() && configuration.serialwarn) {
469: configuration.message.warning(field.position(),
470: "doclet.MissingSerialTag", cd.qualifiedName(),
471: field.name());
472: }
473: fieldWriter.writeMemberDeprecatedInfo(field);
474: fieldWriter.writeMemberDescription(field);
475: fieldWriter.writeMemberTags(field);
476: }
477: }
478:
479: /**
480: * Build the field footer.
481: */
482: public void buildFieldFooter() {
483: if (!currentClass.definesSerializableFields()) {
484: fieldWriter.writeMemberFooter((FieldDoc) currentMember);
485: }
486: }
487:
488: /**
489: * Build the summaries for the methods that belong to the given
490: * class.
491: */
492: public void buildSerializableMethods(List elements) {
493: MemberDoc[] members = currentClass.serializationMethods();
494: if (members.length > 0) {
495: for (int i = 0; i < members.length; i++) {
496: currentMember = members[i];
497: build(elements);
498: }
499: }
500: }
501:
502: /**
503: * Build the summaries for the fields that belong to the given
504: * class.
505: */
506: public void buildSerializableFields(List elements) {
507: MemberDoc[] members = currentClass.serializableFields();
508: if (members.length > 0) {
509: for (int i = 0; i < members.length; i++) {
510: currentMember = members[i];
511: build(elements);
512: }
513: }
514: }
515: }
|