Package com.google.doctool

Source Code of com.google.doctool.Booklet

/*
* 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) {
    BufferedReader br = null;
    try {
      br = new BufferedReader(new FileReader(position.file()));
      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 (br != null) {
          br.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.5.0/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);
      // Ignore result since the next line will fail if the directory doesn't
      // exist.
      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;

    // Check for HTML links
    if (seeTag.text().startsWith("<")) {
      // TODO: ignore for now
      return;
    }
    // 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.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);
  }
}
TOP

Related Classes of com.google.doctool.Booklet

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.