|  | /* | 
|  | * Copyright 2006 Google Inc. | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); you may not | 
|  | * use this file except in compliance with the License. You may obtain a copy of | 
|  | * the License at | 
|  | * | 
|  | * http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | 
|  | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | 
|  | * License for the specific language governing permissions and limitations under | 
|  | * the License. | 
|  | */ | 
|  | package com.google.doctool; | 
|  |  | 
|  | import com.google.doctool.LinkResolver.ExtraClassResolver; | 
|  |  | 
|  | import com.sun.javadoc.ClassDoc; | 
|  | import com.sun.javadoc.ConstructorDoc; | 
|  | import com.sun.javadoc.Doc; | 
|  | import com.sun.javadoc.DocErrorReporter; | 
|  | import com.sun.javadoc.ExecutableMemberDoc; | 
|  | import com.sun.javadoc.FieldDoc; | 
|  | import com.sun.javadoc.MemberDoc; | 
|  | import com.sun.javadoc.MethodDoc; | 
|  | import com.sun.javadoc.PackageDoc; | 
|  | import com.sun.javadoc.ParamTag; | 
|  | import com.sun.javadoc.Parameter; | 
|  | import com.sun.javadoc.ProgramElementDoc; | 
|  | import com.sun.javadoc.RootDoc; | 
|  | import com.sun.javadoc.SeeTag; | 
|  | import com.sun.javadoc.SourcePosition; | 
|  | import com.sun.javadoc.Tag; | 
|  | import com.sun.javadoc.Type; | 
|  |  | 
|  | import java.io.BufferedReader; | 
|  | import java.io.File; | 
|  | import java.io.FileReader; | 
|  | import java.io.FileWriter; | 
|  | import java.io.IOException; | 
|  | import java.io.PrintWriter; | 
|  | import java.util.HashSet; | 
|  | import java.util.Iterator; | 
|  | import java.util.Stack; | 
|  |  | 
|  | /** | 
|  | * Generates XML from Javadoc source, with particular idioms to make it possible | 
|  | * to translate into either expository doc or API doc. | 
|  | */ | 
|  | public class Booklet { | 
|  |  | 
|  | private static final String OPT_BKCODE = "-bkcode"; | 
|  | private static final String OPT_BKDOCPKG = "-bkdocpkg"; | 
|  | private static final String OPT_BKOUT = "-bkout"; | 
|  |  | 
|  | private static Booklet sBooklet; | 
|  |  | 
|  | public static void main(String[] args) { | 
|  | // Strip off our arguments at the beginning. | 
|  | // | 
|  | com.sun.tools.javadoc.Main.execute(args); | 
|  | } | 
|  |  | 
|  | public static int optionLength(String option) { | 
|  | if (option.equals(OPT_BKOUT)) { | 
|  | return 2; | 
|  | } else if (option.equals(OPT_BKDOCPKG)) { | 
|  | return 2; | 
|  | } else if (option.equals(OPT_BKCODE)) { | 
|  | return 1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | public static String slurpSource(SourcePosition position) { | 
|  | FileReader fr = null; | 
|  | try { | 
|  | fr = new FileReader(position.file()); | 
|  | BufferedReader br = new BufferedReader(fr); | 
|  | for (int i = 0, n = position.line() - 1; i < n; ++i) { | 
|  | br.readLine(); | 
|  | } | 
|  |  | 
|  | StringBuffer lines = new StringBuffer(); | 
|  | String line = br.readLine(); | 
|  | int braceDepth = 0; | 
|  | int indent = -1; | 
|  | boolean seenSemiColonOrBrace = false; | 
|  | while (line != null) { | 
|  | if (indent == -1) { | 
|  | for (indent = 0; Character.isWhitespace(line.charAt(indent)); ++indent) { | 
|  | // just accumulate | 
|  | } | 
|  | } | 
|  |  | 
|  | if (line.length() >= indent) { | 
|  | line = line.substring(indent); | 
|  | } | 
|  |  | 
|  | lines.append(line + "\n"); | 
|  | for (int i = 0, n = line.length(); i < n; ++i) { | 
|  | char c = line.charAt(i); | 
|  | if (c == '{') { | 
|  | seenSemiColonOrBrace = true; | 
|  | ++braceDepth; | 
|  | } else if (c == '}') { | 
|  | --braceDepth; | 
|  | } else if (c == ';') { | 
|  | seenSemiColonOrBrace = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (braceDepth > 0 || !seenSemiColonOrBrace) { | 
|  | line = br.readLine(); | 
|  | } else { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | String code = lines.toString(); | 
|  | return code; | 
|  | } catch (Exception e) { | 
|  | e.printStackTrace(); | 
|  | } finally { | 
|  | try { | 
|  | if (fr != null) { | 
|  | fr.close(); | 
|  | } | 
|  | } catch (IOException e) { | 
|  | e.printStackTrace(); | 
|  | } | 
|  | } | 
|  | return ""; | 
|  | } | 
|  |  | 
|  | public static boolean start(RootDoc rootDoc) { | 
|  | getBooklet().process(rootDoc); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | public static boolean validOptions(String[][] options, | 
|  | DocErrorReporter reporter) { | 
|  | return getBooklet().analyzeOptions(options, reporter); | 
|  | } | 
|  |  | 
|  | private static Booklet getBooklet() { | 
|  | if (sBooklet == null) { | 
|  | sBooklet = new Booklet(); | 
|  | } | 
|  | return sBooklet; | 
|  | } | 
|  |  | 
|  | private String outputPath; | 
|  |  | 
|  | private HashSet packagesToGenerate; | 
|  |  | 
|  | private RootDoc initialRootDoc; | 
|  |  | 
|  | private String rootDocId; | 
|  |  | 
|  | private boolean showCode; | 
|  |  | 
|  | private HashSet standardTagKinds = new HashSet(); | 
|  |  | 
|  | private Stack tagStack = new Stack(); | 
|  |  | 
|  | private PrintWriter pw; | 
|  |  | 
|  | public Booklet() { | 
|  | // Set up standard tags (to ignore during tag processing) | 
|  | // | 
|  | standardTagKinds.add("@see"); | 
|  | standardTagKinds.add("@serial"); | 
|  | standardTagKinds.add("@throws"); | 
|  | standardTagKinds.add("@param"); | 
|  | standardTagKinds.add("@id"); | 
|  | } | 
|  |  | 
|  | private boolean analyzeOptions(String[][] options, DocErrorReporter reporter) { | 
|  | for (int i = 0, n = options.length; i < n; ++i) { | 
|  | if (options[i][0].equals(OPT_BKOUT)) { | 
|  | outputPath = options[i][1]; | 
|  | } else if (options[i][0].equals(OPT_BKDOCPKG)) { | 
|  | String[] packages = options[i][1].split(";"); | 
|  | packagesToGenerate = new HashSet(); | 
|  | for (int packageIndex = 0; packageIndex < packages.length; ++packageIndex) { | 
|  | packagesToGenerate.add(packages[packageIndex]); | 
|  | } | 
|  | } else if (options[i][0].equals(OPT_BKCODE)) { | 
|  | showCode = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (outputPath == null) { | 
|  | reporter.printError("You must specify an output directory with " | 
|  | + OPT_BKOUT); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | private void begin(String tag) { | 
|  | pw.print("<" + tag + ">"); | 
|  | tagStack.push(tag); | 
|  | } | 
|  |  | 
|  | private void begin(String tag, String attr, String value) { | 
|  | pw.print("<" + tag + " " + attr + "='" + value + "'>"); | 
|  | tagStack.push(tag); | 
|  | } | 
|  |  | 
|  | private void beginCDATA() { | 
|  | pw.print("<![CDATA["); | 
|  | } | 
|  |  | 
|  | private void beginEndln(String tag) { | 
|  | pw.println("<" + tag + "/>"); | 
|  | } | 
|  |  | 
|  | private void beginln(String tag) { | 
|  | pw.println(); | 
|  | begin(tag); | 
|  | } | 
|  |  | 
|  | private void beginln(String tag, String attr, String value) { | 
|  | pw.println(); | 
|  | begin(tag, attr, value); | 
|  | } | 
|  |  | 
|  | private void emitDescription(ClassDoc enclosing, Doc forWhat, | 
|  | Tag[] leadInline, Tag[] descInline) { | 
|  | emitJRELink(enclosing, forWhat); | 
|  |  | 
|  | beginln("lead"); | 
|  | processTags(leadInline); | 
|  | endln(); | 
|  |  | 
|  | beginln("description"); | 
|  | processTags(descInline); | 
|  | endln(); | 
|  | } | 
|  |  | 
|  | private void emitIdentity(String id, String name) { | 
|  | beginln("id"); | 
|  | text(id); | 
|  | endln(); | 
|  |  | 
|  | beginln("name"); | 
|  | text(name); | 
|  | endln(); | 
|  | } | 
|  |  | 
|  | private void emitJRELink(ClassDoc enclosing, Doc doc) { | 
|  | String jreLink = "http://java.sun.com/j2se/1.4.2/docs/api/"; | 
|  | if (doc instanceof ClassDoc) { | 
|  | ClassDoc classDoc = (ClassDoc) doc; | 
|  | String pkg = classDoc.containingPackage().name(); | 
|  | if (!pkg.startsWith("java.")) { | 
|  | return; | 
|  | } | 
|  | String clazz = classDoc.name(); | 
|  |  | 
|  | jreLink += pkg.replace('.', '/') + "/"; | 
|  | jreLink += clazz; | 
|  | jreLink += ".html"; | 
|  | } else if (doc instanceof ExecutableMemberDoc) { | 
|  | ExecutableMemberDoc execMemberDoc = (ExecutableMemberDoc) doc; | 
|  | String pkg = enclosing.containingPackage().name(); | 
|  | if (!pkg.startsWith("java.")) { | 
|  | return; | 
|  | } | 
|  | String clazz = enclosing.name(); | 
|  | String method = execMemberDoc.name(); | 
|  | String sig = execMemberDoc.signature(); | 
|  |  | 
|  | jreLink += pkg.replace('.', '/') + "/"; | 
|  | jreLink += clazz; | 
|  | jreLink += ".html"; | 
|  | jreLink += "#"; | 
|  | jreLink += method; | 
|  | jreLink += sig; | 
|  | } else if (doc instanceof PackageDoc) { | 
|  | String pkg = doc.name(); | 
|  | if (!pkg.startsWith("java.")) { | 
|  | return; | 
|  | } | 
|  | jreLink += pkg.replace('.', '/') + "/package-summary.html"; | 
|  | } else if (doc instanceof FieldDoc) { | 
|  | FieldDoc fieldDoc = (FieldDoc) doc; | 
|  | String pkg = enclosing.containingPackage().name(); | 
|  | if (!pkg.startsWith("java.")) { | 
|  | return; | 
|  | } | 
|  | String clazz = fieldDoc.containingClass().name(); | 
|  | String field = fieldDoc.name(); | 
|  |  | 
|  | jreLink += pkg.replace('.', '/') + "/"; | 
|  | jreLink += clazz; | 
|  | jreLink += ".html"; | 
|  | jreLink += "#"; | 
|  | jreLink += field; | 
|  | } | 
|  |  | 
|  | // Add the link. | 
|  | // | 
|  | beginln("jre"); | 
|  | text(jreLink); | 
|  | endln(); | 
|  | } | 
|  |  | 
|  | private void emitLocation(Doc doc) { | 
|  | Doc parent = getParentDoc(doc); | 
|  | if (parent != null) { | 
|  | beginln("location"); | 
|  | emitLocationLink(parent); | 
|  | endln(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private void emitLocationLink(Doc doc) { | 
|  | // Intentionally reverses the order. | 
|  | // | 
|  | String myId; | 
|  | String myTitle; | 
|  | if (doc instanceof MemberDoc) { | 
|  | MemberDoc memberDoc = (MemberDoc) doc; | 
|  | myId = getId(memberDoc); | 
|  | myTitle = memberDoc.name(); | 
|  | } else if (doc instanceof ClassDoc) { | 
|  | ClassDoc classDoc = (ClassDoc) doc; | 
|  | myId = getId(classDoc); | 
|  | myTitle = classDoc.name(); | 
|  | } else if (doc instanceof PackageDoc) { | 
|  | PackageDoc pkgDoc = (PackageDoc) doc; | 
|  | myId = getId(pkgDoc); | 
|  | myTitle = pkgDoc.name(); | 
|  | } else if (doc instanceof RootDoc) { | 
|  | myId = rootDocId; | 
|  | myTitle = initialRootDoc.name(); | 
|  | } else { | 
|  | throw new IllegalStateException( | 
|  | "Expected only a member, type, or package"); | 
|  | } | 
|  |  | 
|  | Doc parent = getParentDoc(doc); | 
|  | if (parent != null) { | 
|  | emitLocationLink(parent); | 
|  | } | 
|  |  | 
|  | beginln("link", "ref", myId); | 
|  |  | 
|  | Tag[] titleTag = doc.tags("@title"); | 
|  | if (titleTag.length > 0) { | 
|  | myTitle = titleTag[0].text().trim(); | 
|  | } | 
|  |  | 
|  | if (myTitle == null || myTitle.length() == 0) { | 
|  | myTitle = "[NO TITLE]"; | 
|  | } | 
|  |  | 
|  | text(myTitle); | 
|  |  | 
|  | endln(); | 
|  | } | 
|  |  | 
|  | private void emitModifiers(ProgramElementDoc doc) { | 
|  | if (doc.isPrivate()) { | 
|  | beginEndln("isPrivate"); | 
|  | } else if (doc.isProtected()) { | 
|  | beginEndln("isProtected"); | 
|  | } else if (doc.isPublic()) { | 
|  | beginEndln("isPublic"); | 
|  | } else if (doc.isPackagePrivate()) { | 
|  | beginEndln("isPackagePrivate"); | 
|  | } | 
|  |  | 
|  | if (doc.isStatic()) { | 
|  | beginEndln("isStatic"); | 
|  | } | 
|  |  | 
|  | if (doc.isFinal()) { | 
|  | beginEndln("isFinal"); | 
|  | } | 
|  |  | 
|  | if (doc instanceof MethodDoc) { | 
|  | MethodDoc methodDoc = (MethodDoc) doc; | 
|  |  | 
|  | if (methodDoc.isAbstract()) { | 
|  | beginEndln("isAbstract"); | 
|  | } | 
|  |  | 
|  | if (methodDoc.isSynchronized()) { | 
|  | beginEndln("isSynchronized"); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private void emitOutOfLineTags(Tag[] tags) { | 
|  | beginln("tags"); | 
|  | processTags(tags); | 
|  | endln(); | 
|  | } | 
|  |  | 
|  | private void emitType(Type type) { | 
|  | ClassDoc typeAsClass = type.asClassDoc(); | 
|  |  | 
|  | if (typeAsClass != null) { | 
|  | begin("type", "ref", getId(typeAsClass)); | 
|  | } else { | 
|  | begin("type"); | 
|  | } | 
|  |  | 
|  | String typeName = type.typeName(); | 
|  | String dims = type.dimension(); | 
|  |  | 
|  | text(typeName + dims); | 
|  |  | 
|  | end(); | 
|  | } | 
|  |  | 
|  | private void end() { | 
|  | pw.print("</" + tagStack.pop() + ">"); | 
|  | } | 
|  |  | 
|  | private void endCDATA() { | 
|  | pw.print("]]>"); | 
|  | } | 
|  |  | 
|  | private void endln() { | 
|  | end(); | 
|  | pw.println(); | 
|  | } | 
|  |  | 
|  | private MethodDoc findMatchingInterfaceMethodDoc(ClassDoc[] interfaces, | 
|  | MethodDoc methodDoc) { | 
|  | if (interfaces != null) { | 
|  | // Look through the methods on superInterface for a matching methodDoc. | 
|  | // | 
|  | for (int intfIndex = 0; intfIndex < interfaces.length; ++intfIndex) { | 
|  | ClassDoc currentIntfDoc = interfaces[intfIndex]; | 
|  | MethodDoc[] intfMethodDocs = currentIntfDoc.methods(); | 
|  | for (int methodIndex = 0; methodIndex < intfMethodDocs.length; ++methodIndex) { | 
|  | MethodDoc intfMethodDoc = intfMethodDocs[methodIndex]; | 
|  | String methodDocName = methodDoc.name(); | 
|  | String intfMethodDocName = intfMethodDoc.name(); | 
|  | if (methodDocName.equals(intfMethodDocName)) { | 
|  | if (methodDoc.signature().equals(intfMethodDoc.signature())) { | 
|  | // It's a match! | 
|  | // | 
|  | return intfMethodDoc; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Try the superinterfaces of this interface. | 
|  | // | 
|  | MethodDoc foundMethodDoc = findMatchingInterfaceMethodDoc( | 
|  | currentIntfDoc.interfaces(), methodDoc); | 
|  | if (foundMethodDoc != null) { | 
|  | return foundMethodDoc; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Just didn't find it anywhere. Must not be based on an implemented | 
|  | // interface. | 
|  | // | 
|  | return null; | 
|  | } | 
|  |  | 
|  | private ExtraClassResolver getExtraClassResolver(Tag tag) { | 
|  |  | 
|  | if (tag.holder() instanceof PackageDoc) { | 
|  | return new ExtraClassResolver() { | 
|  | public ClassDoc findClass(String className) { | 
|  | return initialRootDoc.classNamed(className); | 
|  | } | 
|  | }; | 
|  | } | 
|  |  | 
|  | return null; | 
|  | } | 
|  |  | 
|  | private String getId(ClassDoc classDoc) { | 
|  | return classDoc.qualifiedName(); | 
|  | } | 
|  |  | 
|  | private String getId(ExecutableMemberDoc memberDoc) { | 
|  | // Use the mangled name to look up a unique id (based on its hashCode). | 
|  | // | 
|  | String clazz = memberDoc.containingClass().qualifiedName(); | 
|  | String id = clazz + "#" + memberDoc.name() + memberDoc.signature(); | 
|  | return id; | 
|  | } | 
|  |  | 
|  | private String getId(FieldDoc fieldDoc) { | 
|  | String clazz = fieldDoc.containingClass().qualifiedName(); | 
|  | String id = clazz + "#" + fieldDoc.name(); | 
|  | return id; | 
|  | } | 
|  |  | 
|  | private String getId(MemberDoc memberDoc) { | 
|  | if (memberDoc.isMethod()) { | 
|  | return getId((MethodDoc) memberDoc); | 
|  | } else if (memberDoc.isConstructor()) { | 
|  | return getId((ConstructorDoc) memberDoc); | 
|  | } else if (memberDoc.isField()) { | 
|  | return getId((FieldDoc) memberDoc); | 
|  | } else { | 
|  | throw new RuntimeException("Unknown member type"); | 
|  | } | 
|  | } | 
|  |  | 
|  | private String getId(PackageDoc packageDoc) { | 
|  | return packageDoc.name(); | 
|  | } | 
|  |  | 
|  | private Doc getParentDoc(Doc doc) { | 
|  | if (doc instanceof MemberDoc) { | 
|  | MemberDoc memberDoc = (MemberDoc) doc; | 
|  | return memberDoc.containingClass(); | 
|  | } else if (doc instanceof ClassDoc) { | 
|  | ClassDoc classDoc = (ClassDoc) doc; | 
|  | Doc enclosingClass = classDoc.containingClass(); | 
|  | if (enclosingClass != null) { | 
|  | return enclosingClass; | 
|  | } else { | 
|  | return classDoc.containingPackage(); | 
|  | } | 
|  | } else if (doc instanceof PackageDoc) { | 
|  | return initialRootDoc; | 
|  | } else if (doc instanceof RootDoc) { | 
|  | return null; | 
|  | } else { | 
|  | throw new IllegalStateException( | 
|  | "Expected only a member, type, or package"); | 
|  | } | 
|  | } | 
|  |  | 
|  | private boolean looksSynthesized(ExecutableMemberDoc memberDoc) { | 
|  | SourcePosition memberPos = memberDoc.position(); | 
|  | int memberLine = memberPos.line(); | 
|  |  | 
|  | SourcePosition classPos = memberDoc.containingClass().position(); | 
|  | int classLine = classPos.line(); | 
|  |  | 
|  | if (memberLine == classLine) { | 
|  | return true; | 
|  | } else { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | private void process(ClassDoc enclosing, ClassDoc classDoc) { | 
|  | // Make sure it isn't a @skip-ped topic. | 
|  | // | 
|  | if (classDoc.tags("@skip").length > 0) { | 
|  | // This one is explicitly skipped right now. | 
|  | // | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (classDoc.isInterface()) { | 
|  | beginln("interface"); | 
|  | } else { | 
|  | beginln("class"); | 
|  | } | 
|  |  | 
|  | emitIdentity(getId(classDoc), classDoc.name()); | 
|  | emitLocation(classDoc); | 
|  | emitDescription(enclosing, classDoc, classDoc.firstSentenceTags(), | 
|  | classDoc.inlineTags()); | 
|  | emitOutOfLineTags(classDoc.tags()); | 
|  | emitModifiers(classDoc); | 
|  |  | 
|  | ClassDoc superclassDoc = classDoc.superclass(); | 
|  | if (superclassDoc != null) { | 
|  | beginln("superclass", "ref", getId(superclassDoc)); | 
|  | text(superclassDoc.name()); | 
|  | endln(); | 
|  | } | 
|  |  | 
|  | ClassDoc[] superinterfacesDoc = classDoc.interfaces(); | 
|  | for (int i = 0; i < superinterfacesDoc.length; i++) { | 
|  | ClassDoc superinterfaceDoc = superinterfacesDoc[i]; | 
|  | beginln("superinterface", "ref", getId(superinterfaceDoc)); | 
|  | text(superinterfaceDoc.name()); | 
|  | endln(); | 
|  | } | 
|  |  | 
|  | ClassDoc[] cda = classDoc.innerClasses(); | 
|  | for (int i = 0; i < cda.length; i++) { | 
|  | process(classDoc, cda[i]); | 
|  | } | 
|  |  | 
|  | FieldDoc[] fda = classDoc.fields(); | 
|  | for (int i = 0; i < fda.length; i++) { | 
|  | process(classDoc, fda[i]); | 
|  | } | 
|  |  | 
|  | ConstructorDoc[] ctorDocs = classDoc.constructors(); | 
|  | for (int i = 0; i < ctorDocs.length; i++) { | 
|  | process(classDoc, ctorDocs[i]); | 
|  | } | 
|  |  | 
|  | MethodDoc[] methods = classDoc.methods(); | 
|  | for (int i = 0; i < methods.length; i++) { | 
|  | process(classDoc, methods[i]); | 
|  | } | 
|  |  | 
|  | endln(); | 
|  | } | 
|  |  | 
|  | private void process(ClassDoc enclosing, ExecutableMemberDoc memberDoc) { | 
|  | if (looksSynthesized(memberDoc)) { | 
|  | // Skip it. | 
|  | // | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Make sure it isn't a @skip-ped member. | 
|  | // | 
|  | if (memberDoc.tags("@skip").length > 0) { | 
|  | // This one is explicitly skipped right now. | 
|  | // | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (memberDoc instanceof MethodDoc) { | 
|  | beginln("method"); | 
|  | emitIdentity(getId(memberDoc), memberDoc.name()); | 
|  | emitLocation(memberDoc); | 
|  |  | 
|  | // If this method is not explicitly documented, use the best inherited | 
|  | // one. | 
|  | // | 
|  | String rawComment = memberDoc.getRawCommentText(); | 
|  | if (rawComment.length() == 0) { | 
|  | // Switch out the member doc being used. | 
|  | // | 
|  | MethodDoc methodDoc = (MethodDoc) memberDoc; | 
|  | MethodDoc superMethodDoc = methodDoc.overriddenMethod(); | 
|  |  | 
|  | if (superMethodDoc == null) { | 
|  |  | 
|  | ClassDoc classDocToTry = methodDoc.containingClass(); | 
|  | while (classDocToTry != null) { | 
|  | // See if this is a method from an interface. | 
|  | // If so, borrow its description. | 
|  | // | 
|  | superMethodDoc = findMatchingInterfaceMethodDoc( | 
|  | classDocToTry.interfaces(), methodDoc); | 
|  |  | 
|  | if (superMethodDoc != null) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | classDocToTry = classDocToTry.superclass(); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (superMethodDoc != null) { | 
|  | // Borrow the description from the superclass/superinterface. | 
|  | // | 
|  | memberDoc = superMethodDoc; | 
|  | } | 
|  | } | 
|  | } else if (memberDoc instanceof ConstructorDoc) { | 
|  | beginln("constructor"); | 
|  | emitIdentity(getId(memberDoc), memberDoc.containingClass().name()); | 
|  | emitLocation(memberDoc); | 
|  | } else { | 
|  | throw new IllegalStateException("What kind of executable member is this?"); | 
|  | } | 
|  |  | 
|  | emitDescription(enclosing, memberDoc, memberDoc.firstSentenceTags(), | 
|  | memberDoc.inlineTags()); | 
|  | emitOutOfLineTags(memberDoc.tags()); | 
|  | emitModifiers(memberDoc); | 
|  |  | 
|  | begin("flatSignature"); | 
|  | text(memberDoc.flatSignature()); | 
|  | end(); | 
|  |  | 
|  | // Return type if it's a method | 
|  | // | 
|  | if (memberDoc instanceof MethodDoc) { | 
|  | emitType(((MethodDoc) memberDoc).returnType()); | 
|  | } | 
|  |  | 
|  | // Parameters | 
|  | // | 
|  | beginln("params"); | 
|  | Parameter[] pda = memberDoc.parameters(); | 
|  | for (int i = 0; i < pda.length; i++) { | 
|  | Parameter pd = pda[i]; | 
|  |  | 
|  | begin("param"); | 
|  | emitType(pd.type()); | 
|  | begin("name"); | 
|  | text(pd.name()); | 
|  | end(); | 
|  | end(); | 
|  | } | 
|  | endln(); | 
|  |  | 
|  | // Exceptions thrown | 
|  | // | 
|  | ClassDoc[] tea = memberDoc.thrownExceptions(); | 
|  | if (tea.length > 0) { | 
|  | beginln("throws"); | 
|  | for (int i = 0; i < tea.length; ++i) { | 
|  | ClassDoc te = tea[i]; | 
|  | beginln("throw", "ref", getId(te)); | 
|  | text(te.name()); | 
|  | endln(); | 
|  | } | 
|  | endln(); | 
|  | } | 
|  |  | 
|  | // Maybe show code | 
|  | // | 
|  | if (showCode) { | 
|  | SourcePosition pos = memberDoc.position(); | 
|  | if (pos != null) { | 
|  | beginln("code"); | 
|  | String source = slurpSource(pos); | 
|  | begin("pre", "class", "code"); | 
|  | beginCDATA(); | 
|  | text(source); | 
|  | endCDATA(); | 
|  | endln(); | 
|  | endln(); | 
|  | } | 
|  | } | 
|  |  | 
|  | endln(); | 
|  | } | 
|  |  | 
|  | private void process(ClassDoc enclosing, FieldDoc fieldDoc) { | 
|  | // Make sure it isn't @skip-ped. | 
|  | // | 
|  | if (fieldDoc.tags("@skip").length > 0) { | 
|  | // This one is explicitly skipped right now. | 
|  | // | 
|  | return; | 
|  | } | 
|  |  | 
|  | String commentText = fieldDoc.commentText(); | 
|  | if (fieldDoc.isPrivate() | 
|  | && (commentText == null || commentText.length() == 0)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | beginln("field"); | 
|  | emitIdentity(fieldDoc.qualifiedName(), fieldDoc.name()); | 
|  | emitLocation(fieldDoc); | 
|  | emitDescription(enclosing, fieldDoc, fieldDoc.firstSentenceTags(), | 
|  | fieldDoc.inlineTags()); | 
|  | emitOutOfLineTags(fieldDoc.tags()); | 
|  | emitModifiers(fieldDoc); | 
|  | emitType(fieldDoc.type()); | 
|  | endln(); | 
|  | } | 
|  |  | 
|  | private void process(PackageDoc packageDoc) { | 
|  | beginln("package"); | 
|  |  | 
|  | emitIdentity(packageDoc.name(), packageDoc.name()); | 
|  | emitLocation(packageDoc); | 
|  | emitDescription(null, packageDoc, packageDoc.firstSentenceTags(), | 
|  | packageDoc.inlineTags()); | 
|  | emitOutOfLineTags(packageDoc.tags()); | 
|  |  | 
|  | // Top-level classes | 
|  | // | 
|  | ClassDoc[] cda = packageDoc.allClasses(); | 
|  | for (int i = 0; i < cda.length; i++) { | 
|  | ClassDoc cd = cda[i]; | 
|  |  | 
|  | // Make sure we have source. | 
|  | // | 
|  | SourcePosition p = cd.position(); | 
|  | if (p == null || p.line() == 0) { | 
|  | // Skip this since it isn't ours (otherwise we would have source). | 
|  | // | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (cd.containingClass() == null) { | 
|  | process(cd, cda[i]); | 
|  | } else { | 
|  | // Not a top-level class. | 
|  | // | 
|  | cd = cda[i]; | 
|  | } | 
|  | } | 
|  |  | 
|  | endln(); | 
|  | } | 
|  |  | 
|  | private void process(RootDoc rootDoc) { | 
|  | try { | 
|  | initialRootDoc = rootDoc; | 
|  | File outputFile = new File(outputPath); | 
|  | outputFile.getParentFile().mkdirs(); | 
|  | FileWriter fw = new FileWriter(outputFile); | 
|  | pw = new PrintWriter(fw, true); | 
|  |  | 
|  | beginln("booklet"); | 
|  |  | 
|  | rootDocId = ""; | 
|  | String title = ""; | 
|  | Tag[] idTags = rootDoc.tags("@id"); | 
|  | if (idTags.length > 0) { | 
|  | rootDocId = idTags[0].text(); | 
|  | } else { | 
|  | initialRootDoc.printWarning("Expecting @id in an overview html doc; see -overview"); | 
|  | } | 
|  |  | 
|  | Tag[] titleTags = rootDoc.tags("@title"); | 
|  | if (titleTags.length > 0) { | 
|  | title = titleTags[0].text(); | 
|  | } else { | 
|  | initialRootDoc.printWarning("Expecting @title in an overview html doc; see -overview"); | 
|  | } | 
|  |  | 
|  | emitIdentity(rootDocId, title); | 
|  | emitLocation(rootDoc); | 
|  | emitDescription(null, rootDoc, rootDoc.firstSentenceTags(), | 
|  | rootDoc.inlineTags()); | 
|  | emitOutOfLineTags(rootDoc.tags()); | 
|  |  | 
|  | // Create a list of the packages to iterate over. | 
|  | // | 
|  | HashSet packageNames = new HashSet(); | 
|  | ClassDoc[] cda = initialRootDoc.classes(); | 
|  | for (int i = 0; i < cda.length; i++) { | 
|  | ClassDoc cd = cda[i]; | 
|  | // Only top-level classes matter. | 
|  | // | 
|  | if (cd.containingClass() == null) { | 
|  | packageNames.add(cd.containingPackage().name()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Packages | 
|  | // | 
|  | for (Iterator iter = packageNames.iterator(); iter.hasNext();) { | 
|  | String packageName = (String) iter.next(); | 
|  |  | 
|  | // Only process this package if either no "docpkg" is set, or it is | 
|  | // included. | 
|  | // | 
|  | if (packagesToGenerate == null | 
|  | || packagesToGenerate.contains(packageName)) { | 
|  | PackageDoc pd = initialRootDoc.packageNamed(packageName); | 
|  | process(pd); | 
|  | } | 
|  | } | 
|  |  | 
|  | endln(); | 
|  | } catch (Exception e) { | 
|  | e.printStackTrace(); | 
|  | initialRootDoc.printError("Caught exception: " + e.toString()); | 
|  | } | 
|  | } | 
|  |  | 
|  | private void processSeeTag(SeeTag seeTag) { | 
|  | String ref = null; | 
|  | ClassDoc cd = null; | 
|  | PackageDoc pd = null; | 
|  | MemberDoc md = null; | 
|  | String title = null; | 
|  |  | 
|  | // Ordered: most-specific to least-specific | 
|  | if (null != (md = seeTag.referencedMember())) { | 
|  | ref = getId(md); | 
|  | } else if (null != (cd = seeTag.referencedClass())) { | 
|  | ref = getId(cd); | 
|  |  | 
|  | // See if the target has a title. | 
|  | // | 
|  | Tag[] titleTag = cd.tags("@title"); | 
|  | if (titleTag.length > 0) { | 
|  | title = titleTag[0].text().trim(); | 
|  | if (title.length() == 0) { | 
|  | title = null; | 
|  | } | 
|  | } | 
|  | } else if (null != (pd = seeTag.referencedPackage())) { | 
|  | ref = getId(pd); | 
|  | } | 
|  |  | 
|  | String label = seeTag.label(); | 
|  |  | 
|  | // If there is a label, use it. | 
|  | if (label == null || (label != null && label.trim().length() == 0)) { | 
|  |  | 
|  | // If there isn't a label, see if the @see target has a @title. | 
|  | // | 
|  | if (title != null) { | 
|  | label = title; | 
|  | } else { | 
|  | label = seeTag.text(); | 
|  |  | 
|  | if (label.endsWith(".")) { | 
|  | label = label.substring(0, label.length() - 1); | 
|  | } | 
|  |  | 
|  | // Rip off all but the last interesting part to prevent fully-qualified | 
|  | // names everywhere. | 
|  | // | 
|  | int last1 = label.lastIndexOf('.'); | 
|  | int last2 = label.lastIndexOf('#'); | 
|  |  | 
|  | if (last2 > last1) { | 
|  | // Use the class name plus the member name. | 
|  | // | 
|  | label = label.substring(last1 + 1).replace('#', '.'); | 
|  | } else if (last1 != -1) { | 
|  | label = label.substring(last1 + 1); | 
|  | } | 
|  |  | 
|  | if (label.charAt(0) == '.') { | 
|  | // Started with "#" so remove the dot. | 
|  | // | 
|  | label = label.substring(1); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (ref != null) { | 
|  | begin("link", "ref", ref); | 
|  | text(label != null ? label.trim() : ""); | 
|  | end(); | 
|  | } else { | 
|  | initialRootDoc.printWarning(seeTag.position(), | 
|  | "Unverifiable cross-reference to '" + seeTag.text() + "'"); | 
|  | // The link probably won't work, but emit it anyway. | 
|  | begin("link"); | 
|  | text(label != null ? label.trim() : ""); | 
|  | end(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private void processTags(Tag[] tags) { | 
|  | for (int i = 0; i < tags.length; i++) { | 
|  | Tag tag = tags[i]; | 
|  | String tagKind = tag.kind(); | 
|  | if (tagKind.equals("Text")) { | 
|  | text(tag.text()); | 
|  | } else if (tagKind.equals("@see")) { | 
|  | processSeeTag((SeeTag) tag); | 
|  | } else if (tagKind.equals("@param")) { | 
|  | ParamTag paramTag = (ParamTag) tag; | 
|  | beginln("param"); | 
|  | begin("name"); | 
|  | text(paramTag.parameterName()); | 
|  | end(); | 
|  | begin("description"); | 
|  | processTags(paramTag.inlineTags()); | 
|  | end(); | 
|  | endln(); | 
|  | } else if (tagKind.equals("@example")) { | 
|  | ExtraClassResolver extraClassResolver = getExtraClassResolver(tag); | 
|  | SourcePosition pos = LinkResolver.resolveLink(tag, extraClassResolver); | 
|  | String source = slurpSource(pos); | 
|  | begin("pre", "class", "code"); | 
|  | beginCDATA(); | 
|  | text(source); | 
|  | endCDATA(); | 
|  | endln(); | 
|  | } else if (tagKind.equals("@gwt.include")) { | 
|  | String contents = ResourceIncluder.getResourceFromClasspathScrubbedForHTML(tag); | 
|  | begin("pre", "class", "code"); | 
|  | text(contents); | 
|  | endln(); | 
|  | } else if (!standardTagKinds.contains(tag.name())) { | 
|  | // Custom tag; pass it along other tag. | 
|  | // | 
|  | String tagName = tag.name().substring(1); | 
|  | begin(tagName); | 
|  | processTags(tag.inlineTags()); | 
|  | end(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private void text(String s) { | 
|  | pw.print(s); | 
|  | } | 
|  | } |