| /* gnu.classpath.tools.doclets.xmldoclet.Driver |
| Copyright (C) 2001 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath 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 for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307 USA. */ |
| |
| package gnu.classpath.tools.doclets.xmldoclet; |
| |
| import com.sun.javadoc.*; |
| import java.io.*; |
| |
| import com.sun.tools.doclets.Taglet; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| |
| import java.text.DateFormat; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Comparator; |
| import java.util.HashSet; |
| import java.util.TreeSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.HashMap; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.SortedSet; |
| import java.util.StringTokenizer; |
| import java.util.TreeMap; |
| |
| import gnu.classpath.tools.gjdoc.TemporaryStore; |
| import gnu.classpath.tools.gjdoc.GjdocPackageDoc; |
| |
| import gnu.classpath.tools.doclets.PackageGroup; |
| import gnu.classpath.tools.doclets.PackageMatcher; |
| import gnu.classpath.tools.doclets.InvalidPackageWildcardException; |
| |
| import gnu.classpath.tools.doclets.xmldoclet.doctranslet.DocTranslet; |
| import gnu.classpath.tools.doclets.xmldoclet.doctranslet.DocTransletOptions; |
| |
| import gnu.classpath.tools.taglets.AuthorTaglet; |
| import gnu.classpath.tools.taglets.VersionTaglet; |
| import gnu.classpath.tools.taglets.SinceTaglet; |
| import gnu.classpath.tools.taglets.DeprecatedTaglet; |
| import gnu.classpath.tools.taglets.GenericTaglet; |
| import gnu.classpath.tools.doclets.StandardTaglet; |
| |
| import gnu.classpath.tools.java2xhtml.Java2xhtml; |
| |
| import gnu.classpath.tools.IOToolkit; |
| import gnu.classpath.tools.FileSystemClassLoader; |
| |
| /** |
| * A Doclet which retrieves all information presented by the Doclet |
| * API, dumping it to stdout in XML format. |
| * |
| * @author Julian Scheid |
| */ |
| public class Driver { |
| |
| public static final String XMLDOCLET_VERSION = "0.6.1"; |
| |
| /** |
| * Used for redirecting error messages to <code>/dev/null</code>. |
| */ |
| private static class NullErrorReporter implements DocErrorReporter { |
| public void printError(String ignore) {} |
| public void printWarning(String ignore) {} |
| public void printNotice(String ignore) {} |
| } |
| |
| /* |
| * Taglet context constants. |
| */ |
| private static final int CONTEXT_CONSTRUCTOR = 1; |
| private static final int CONTEXT_FIELD = 2; |
| private static final int CONTEXT_METHOD = 3; |
| private static final int CONTEXT_OVERVIEW = 4; |
| private static final int CONTEXT_PACKAGE = 5; |
| private static final int CONTEXT_TYPE = 6; |
| |
| /** |
| * All XML output will go to this stream. |
| */ |
| private PrintWriter out; |
| |
| /** |
| * How many spaces to indent each XML node level, |
| * i.e. Tab size for output. |
| */ |
| private static int indentStep = 1; |
| |
| /** |
| * Won't output superfluous spaces if set to true. |
| * If set to false, output will be more legible. |
| */ |
| private boolean compress = false; |
| |
| /** |
| * Won't output warning messages while fixing |
| * HTML code if set to true. |
| */ |
| private boolean noHTMLWarn = false; |
| |
| /** |
| * Won't output warning messages when encountering tags |
| * that look like an email address if set to true. |
| */ |
| private boolean noEmailWarn = false; |
| |
| /** |
| * Will fix HTML if necessary so that each comment |
| * contains valid XML code if set to true. If set |
| * to false, HTML code will not be modified and |
| * instead encapsulated in a CDATA section. |
| */ |
| private boolean fixHTML = true; |
| |
| /** |
| * User-specified name of the directory where the final version of |
| * the generated files will be written to. |
| * |
| * If no XSLT sheet is given, the XML output will go directly into |
| * this directory. Otherwise, XML output will go to a temporary |
| * directory and XSLT output will go to this directory. |
| */ |
| private File targetDirectory = null; |
| |
| /** |
| * Directory where XML output will be written to. If no XSLT |
| * sheet was given, this is the target directory specified |
| * by the user. Otherwise, this is a temporary directory. |
| */ |
| private File xmlTargetDirectory; |
| |
| /** |
| * Contains a number of TargetContexts which describe which XSLT |
| * sheet to apply to the output of this doclet, to what directory |
| * the XSLT output is written, and which postprocess driver to use |
| * to process XSLT output. |
| */ |
| private List targets = new ArrayList(); |
| |
| /** |
| * XML text to include at the end of every generated page. Read |
| * from the file specified on the command line using -bottomnote. |
| * If present, this will be written to the main output file |
| * (index.xml) in node /gjdoc:rootDoc/gjdoc:bottomnote. |
| */ |
| private String bottomNote; |
| |
| /** |
| * Brief description of the package set. Can be specified on the |
| * command line using -title. This will be written to the main |
| * output file (index.xml) in node |
| * /gjdoc:rootDoc/gjdoc:title. The HTML generating XSLT sheet |
| * uses this for example in window titles. |
| */ |
| private String title; |
| |
| /** |
| * Path to the directory where temporary files should be stored. |
| * Defaults to system tempdir, but can be overridden by user |
| * with -workpath. |
| */ |
| private String workingPath = System.getProperty("java.io.tmpdir"); |
| |
| /** |
| * Temporary directory created by this doclet where all |
| * temporary files will be stored in. If no temporary |
| * files are needed (i.e. no XSLT postprocessing stage |
| * specified by user), this is <code>null</code>. |
| */ |
| private File workingDirectory; |
| |
| /** |
| * Whether to deep-copy the doc-files subdirectory. |
| */ |
| private boolean docFilesSubdirsEnabled = false; |
| |
| /** |
| * Which direct subdirectories of the doc-files directories to exclude. |
| * Set of String. |
| */ |
| private Set excludeDocFilesSubDirs = new HashSet(); |
| |
| /** |
| * Stores the Doclet API RootDoc we are operating on. |
| */ |
| private RootDoc rootDoc; |
| |
| /** |
| * XML namespace prefix used for all tags, except for HTML |
| * tags copied from Javadoc comments. Excluding colon. |
| */ |
| public static final String tagPrefix = "gjdoc"; |
| |
| /** |
| * Classpath for loading Taglet classes. |
| */ |
| private String tagletPath = null; |
| |
| /** |
| * The current class that is being processed. |
| * Set in outputClassDoc(). |
| */ |
| private ClassDoc currentClass; |
| |
| /** |
| * The current member that is being processed. |
| * Set in outputMemberDoc(). |
| */ |
| private MemberDoc currentMember; |
| |
| /** |
| * The current constructor/method that is being processed. |
| * Set in outputExecutableMemberDoc(). |
| */ |
| private ExecutableMemberDoc currentExecMember; |
| |
| /** |
| * Mapping from tag type to Taglet for user Taglets specified on |
| * the command line. |
| */ |
| private Map tagletMap = new LinkedHashMap(); |
| |
| /** |
| * Keeps track of the tags mentioned by the user during option |
| * processiong so that an error can be emitted if a tag is |
| * mentioned more than once. |
| */ |
| private List mentionedTags = new LinkedList(); |
| |
| /** |
| * Stores options to be passed to the DocTranslet. |
| */ |
| private DocTransletOptions docTransletOptions = new DocTransletOptions(); |
| |
| /** |
| * Stores the package groups specified in the user |
| * options. Contains objects of type PackageGroup. |
| */ |
| private List packageGroups = new LinkedList(); |
| |
| private HtmlRepairer htmlRepairer; |
| |
| public static boolean start(TemporaryStore _rootDocWrapper) { |
| return new Driver().instanceStart((RootDoc)_rootDocWrapper.getAndClear()); |
| } |
| |
| /** |
| * Official Doclet entry point. |
| */ |
| public static boolean start(RootDoc _rootDoc) { |
| |
| // Create a new XmlDoclet instance and delegate control. |
| TemporaryStore tstore = new TemporaryStore(_rootDoc); |
| _rootDoc = null; |
| return new Driver().instanceStart((RootDoc)tstore.getAndClear()); |
| } |
| |
| /** |
| * Output an XML tag describing a com.sun.javadoc.Type object. |
| * Assumes that the tag does not have subtags. |
| * |
| * @param level Level of indentation. Will be multiplied by |
| * <code>indentStep</code> to yield actual amount |
| * of whitespace inserted at start of line. |
| * @param tag Identifier for the XML tag being output. |
| * @param type The Javadoc Type to be output. |
| */ |
| protected void outputType(int level, String tag, Type type) { |
| outputType(level, tag, type, true); |
| } |
| |
| protected void outputType(int level, String tag, Type type, boolean atomic) { |
| |
| boolean isIncluded = false; |
| ClassDoc typeAsClassDoc = type.asClassDoc(); |
| String packageName = null; |
| if (null != typeAsClassDoc) { |
| isIncluded = typeAsClassDoc.isIncluded(); |
| packageName = typeAsClassDoc.containingPackage().name(); |
| } |
| println(level, "<"+tagPrefix+":"+tag + " typename=\""+type.typeName()+"\""+ |
| " qualifiedtypename=\""+type.qualifiedTypeName()+"\"" |
| +(type.dimension().length()==0?"":" dimension=\""+type.dimension()+"\"") |
| +(isIncluded?" isIncluded=\"true\"" : "") |
| +((null != packageName)?" package=\"" + packageName + "\"" : "") |
| +(atomic?"/":"")+">"); |
| } |
| |
| protected void outputExecutableMemberDocBody(int level, ExecutableMemberDoc memberDoc) { |
| |
| currentExecMember = memberDoc; |
| |
| outputMemberDocBody(level, memberDoc); |
| |
| Parameter[] parameters = memberDoc.parameters(); |
| for (int i=0, ilim=parameters.length; i<ilim; ++i) { |
| Parameter parameter = parameters[i]; |
| outputType(level, "parameter name=\""+parameter.name()+"\"", parameter.type()); |
| } |
| |
| ClassDoc[] exceptions = memberDoc.thrownExceptions(); |
| for (int i=0, ilim=exceptions.length; i<ilim; ++i) { |
| ClassDoc exception = exceptions[i]; |
| outputType(level, "thrownException", exception); |
| } |
| |
| printAtomTag(level, "signature full=\""+memberDoc.signature()+"\" flat=\""+memberDoc.flatSignature()+"\""); |
| |
| if (memberDoc.isNative()) { |
| printAtomTag(level, "isNative"); |
| } |
| |
| if (memberDoc.isSynchronized()) { |
| printAtomTag(level, "isSynchronized"); |
| } |
| } |
| |
| protected void outputMethodDoc(int level, MethodDoc methodDoc) { |
| println(); |
| printOpenTag(level, "methoddoc name=\""+methodDoc.name()+"\""); |
| outputExecutableMemberDocBody(level+1, methodDoc); |
| outputType(level+1, "returns", methodDoc.returnType()); |
| printCloseTag(level, "methoddoc"); |
| } |
| |
| protected void outputMemberDocBody(int level, MemberDoc memberDoc) { |
| currentMember = memberDoc; |
| outputProgramElementDocBody(level, memberDoc); |
| } |
| |
| protected void outputFieldDocBody(int level, FieldDoc fieldDoc) { |
| outputType(level, "type", fieldDoc.type()); |
| if (fieldDoc.isTransient()) { |
| printAtomTag(level, "isTransient"); |
| } |
| if (fieldDoc.isVolatile()) { |
| printAtomTag(level, "isVolatile"); |
| } |
| } |
| |
| private void outputFieldDoc(int level, FieldDoc fieldDoc) { |
| println(); |
| printOpenTag(level, "fielddoc name=\""+fieldDoc.name()+"\""); |
| outputMemberDocBody(level+1, fieldDoc); |
| outputFieldDocBody(level+1, fieldDoc); |
| printCloseTag(level, "fielddoc"); |
| } |
| |
| protected void outputConstructorDoc(int level, ConstructorDoc constructorDoc) { |
| println(); |
| printOpenTag(level, "constructordoc name=\""+constructorDoc.name()+"\""); |
| outputExecutableMemberDocBody(level+1, constructorDoc); |
| printCloseTag(level, "constructordoc"); |
| } |
| |
| protected void outputSuperInterfacesRec(int level, ClassDoc classDoc) { |
| if (null!=classDoc) { |
| ClassDoc[] interfaces = classDoc.interfaces(); |
| if (null != interfaces) { |
| for (int i=0, ilim=interfaces.length; i<ilim; ++i) { |
| outputType(level, "superimplements", interfaces[i]); |
| } |
| } |
| outputSuperInterfacesRec(level, classDoc.superclass()); |
| } |
| } |
| |
| protected void outputClassDocSummary(ClassDoc classDoc) { |
| println(); |
| printOpenTag(1, "classdoc name=\""+classDoc.name()+"\" qualifiedtypename=\""+classDoc.qualifiedName()+"\" isIncluded=\"true\""); |
| if (null!=classDoc.superclass()) { |
| outputType(2, "superclass", classDoc.superclass()); |
| } |
| |
| ClassDoc[] interfaces = classDoc.interfaces(); |
| for (int i=0, ilim=interfaces.length; i<ilim; ++i) { |
| outputType(2, "implements", interfaces[i]); |
| } |
| outputSuperInterfacesRec(2, classDoc.superclass()); |
| |
| printAtomTag(2, "containingPackage name=\""+classDoc.containingPackage().name()+"\""); |
| if (classDoc.isError()) { |
| printAtomTag(2, "isError"); |
| } |
| if (classDoc.isException()) { |
| printAtomTag(2, "isException"); |
| } |
| if (classDoc.isInterface()) { |
| printAtomTag(2, "isInterface"); |
| } |
| if (classDoc.isOrdinaryClass()) { |
| printAtomTag(2, "isOrdinaryClass"); |
| } |
| |
| printCloseTag(1, "classdoc"); |
| } |
| |
| protected void outputPackageDoc(PackageDoc packageDoc) { |
| println(); |
| printOpenTag(1, "packagedoc name=\""+packageDoc.name()+"\""); |
| if (packageDoc.firstSentenceTags().length > 0) { |
| printOpenTag(2, "firstSentenceTags", false); |
| outputTags(3, packageDoc.firstSentenceTags(), true, CONTEXT_PACKAGE); |
| printCloseTag(0, "firstSentenceTags"); |
| printOpenTag(2, "inlineTags", false); |
| outputTags(3, packageDoc.inlineTags(), true, CONTEXT_PACKAGE); |
| printCloseTag(0, "inlineTags"); |
| } |
| |
| if (packageDoc.tags().length > 0) { |
| printOpenTag(2, "tags"); |
| outputTags(3, packageDoc.tags(), true, CONTEXT_PACKAGE); |
| printCloseTag(2, "tags"); |
| } |
| |
| if (packageDoc.seeTags().length > 0) { |
| printOpenTag(2, "seeTags"); |
| outputTags(3, packageDoc.seeTags(), true, CONTEXT_PACKAGE); |
| printCloseTag(2, "seeTags"); |
| } |
| |
| ClassDoc[] allClasses = (ClassDoc[]) packageDoc.allClasses().clone(); |
| Arrays.sort(allClasses); |
| |
| if (false) { |
| for (int i = 0, ilim = allClasses.length; i < ilim; ++ i) { |
| printAtomTag(2, "containsClass qualifiedtypename=\""+allClasses[i].qualifiedTypeName()+"\""); |
| } |
| } |
| |
| printCloseTag(1, "packagedoc"); |
| } |
| |
| protected void outputClassDoc(ClassDoc classDoc) throws IOException { |
| |
| currentClass = classDoc; |
| |
| println(); |
| printOpenTag(1, "classdoc xmlns=\"http://www.w3.org/TR/REC-html40\" xmlns:"+tagPrefix+"=\"http://www.gnu.org/software/cp-tools/gjdocxml\" name=\""+classDoc.name()+"\" qualifiedtypename=\""+classDoc.qualifiedName()+"\""); |
| |
| ClassDoc[] interfaces = classDoc.interfaces(); |
| for (int i=0, ilim=interfaces.length; i<ilim; ++i) { |
| outputType(2, "implements", interfaces[i]); |
| } |
| outputSuperInterfacesRec(2, classDoc.superclass()); |
| |
| outputProgramElementDocBody(2, classDoc); |
| if (classDoc.isAbstract()) |
| printAtomTag(2, "isAbstract"); |
| if (classDoc.isSerializable()) |
| printAtomTag(2, "isSerializable"); |
| if (classDoc.isExternalizable()) |
| printAtomTag(2, "isExternalizable"); |
| if (classDoc.definesSerializableFields()) { |
| printAtomTag(2, "definesSerializableFields"); |
| } |
| |
| ConstructorDoc[] constructors = classDoc.constructors(); |
| for (int i=0, ilim=constructors.length; i<ilim; ++i) { |
| outputConstructorDoc(2, constructors[i]); |
| } |
| |
| MethodDoc[] methods = classDoc.methods(); |
| for (int i=0, ilim=methods.length; i<ilim; ++i) { |
| outputMethodDoc(2, methods[i]); |
| } |
| |
| FieldDoc[] fields = classDoc.fields(); |
| for (int i=0, ilim=fields.length; i<ilim; ++i) { |
| outputFieldDoc(2, fields[i]); |
| } |
| |
| if (classDoc.serializableFields().length > 0) { |
| printOpenTag(2, "serializableFields"); |
| |
| FieldDoc[] sfields = classDoc.serializableFields(); |
| for (int i=0, ilim=sfields.length; i<ilim; ++i) { |
| outputFieldDoc(2, sfields[i]); |
| } |
| printCloseTag(2, "serializableFields"); |
| } |
| |
| Java2xhtml java2xhtml = new Java2xhtml(); |
| Properties properties = new Properties(); |
| properties.setProperty("isCodeSnippet", "true"); |
| properties.setProperty("hasLineNumbers", "true"); |
| java2xhtml.setProperties(properties); |
| |
| if (null == classDoc.containingClass() && docTransletOptions.linksource) { |
| printOpenTag(2, "source"); |
| StringWriter sourceBuffer = new StringWriter(); |
| File sourceFile = new File(((GjdocPackageDoc)classDoc.containingPackage()).packageDirectory(), |
| classDoc.name() + ".java"); |
| FileReader sourceReader = new FileReader(sourceFile); |
| IOToolkit.copyStream(sourceReader, sourceBuffer); |
| print(java2xhtml.makeHTML(sourceBuffer.getBuffer(), sourceFile.getName())); |
| printCloseTag(2, "source"); |
| } |
| |
| ClassDoc superclassDoc = classDoc.superclass(); |
| while (superclassDoc != null) { |
| outputType(2, "superclass", superclassDoc, false); |
| |
| // FIXME: remove the following after adjusting the XSLT sheets: |
| printAtomTag(3, "containingPackage name=\"" + superclassDoc.containingPackage().name() + "\""); |
| |
| MethodDoc[] superMethods = superclassDoc.methods(); |
| if (null != superMethods) { |
| for (int i=0, ilim=superMethods.length; i<ilim; ++i) { |
| printAtomTag(3, "methoddoc name=\"" + superMethods[i].name() + "\" signature=\"" + superMethods[i].signature() + "\""); |
| } |
| } |
| |
| FieldDoc[] superFields = superclassDoc.fields(); |
| if (null != superFields) { |
| for (int i=0, ilim=superFields.length; i<ilim; ++i) { |
| printAtomTag(3, "fielddoc name=\"" + superFields[i].name() + "\""); |
| } |
| } |
| printCloseTag(2, "superclass"); |
| |
| superclassDoc = superclassDoc.superclass(); |
| } |
| |
| outputUsage(classDoc, 2); |
| |
| printCloseTag(1, "classdoc"); |
| |
| currentClass = null; |
| currentMember = null; |
| currentExecMember = null; |
| } |
| |
| protected int outputHeritageOpen(int level, ClassDoc classDoc) { |
| |
| ClassDoc superClassDoc = classDoc.superclass(); |
| if (null != superClassDoc) { |
| level = outputHeritageOpen(level, superClassDoc); |
| ++ level; |
| } |
| outputType(level, "heritage", classDoc, false); |
| return level; |
| } |
| |
| protected void outputHeritageClose(int level, ClassDoc classDoc) { |
| |
| ClassDoc superClassDoc = classDoc.superclass(); |
| if (null != superClassDoc) { |
| outputHeritageClose(level + 1, superClassDoc); |
| } |
| printCloseTag(level, "heritage"); |
| } |
| |
| protected void outputDocBody(int level, Doc doc) { |
| |
| int context = CONTEXT_TYPE; |
| |
| if (doc.isClass()) { |
| printAtomTag(level, "isClass"); |
| |
| ClassDoc classDoc = (ClassDoc)doc; |
| ClassDoc[] classes = rootDoc.classes(); |
| for (int i=0, ilim=classes.length; i<ilim; ++i) { |
| if (classes[i].superclass() == classDoc) { |
| outputType(level, "extended-by", classes[i]); |
| } |
| } |
| |
| outputHeritageOpen(level, classDoc); |
| outputHeritageClose(level, classDoc); |
| } |
| if (doc.isConstructor()) { |
| printAtomTag(level, "isConstructor"); |
| context = CONTEXT_CONSTRUCTOR; |
| } |
| if (doc.isError()) { |
| printAtomTag(level, "isError"); |
| } |
| if (doc.isException()) { |
| printAtomTag(level, "isException"); |
| } |
| if (doc.isField()) { |
| printAtomTag(level, "isField"); |
| context = CONTEXT_FIELD; |
| } |
| if (doc.isIncluded()) { |
| printAtomTag(level, "isIncluded"); |
| } |
| if (doc.isInterface()) { |
| printAtomTag(level, "isInterface"); |
| |
| ClassDoc classDoc = (ClassDoc)doc; |
| ClassDoc[] classes = rootDoc.classes(); |
| for (int i=0, ilim=classes.length; i<ilim; ++i) { |
| ClassDoc[] implementedInterfaces = classes[i].interfaces(); |
| for (int j=0; j<implementedInterfaces.length; ++j) { |
| if (implementedInterfaces[j] == classDoc) { |
| if (classDoc.isInterface()) { |
| outputType(level, "subinterface", classes[i]); |
| } |
| else { |
| outputType(level, "implemented-by", classes[i]); |
| } |
| break; |
| } |
| } |
| } |
| } |
| if (doc.isMethod()) { |
| printAtomTag(level, "isMethod"); |
| context = CONTEXT_METHOD; |
| } |
| if (doc.isOrdinaryClass()) { |
| printAtomTag(level, "isOrdinaryClass"); |
| } |
| |
| if (doc.inlineTags().length > 0) { |
| printOpenTag(level, "inlineTags", false); |
| outputTags(level+1, doc.inlineTags(), true, context); |
| printCloseTag(0, "inlineTags"); |
| } |
| |
| if (doc.firstSentenceTags().length > 0) { |
| printOpenTag(level, "firstSentenceTags", false); |
| outputTags(level+1, doc.firstSentenceTags(), true, context); |
| printCloseTag(0, "firstSentenceTags"); |
| } |
| |
| if (doc.tags().length > 0) { |
| printOpenTag(level, "tags"); |
| outputTaglets(level+1, doc.tags(), true, context); |
| printCloseTag(level, "tags"); |
| } |
| |
| if (doc.seeTags().length > 0) { |
| printOpenTag(level, "seeTags"); |
| outputTags(level+1, doc.seeTags(), true, context); |
| printCloseTag(level, "seeTags"); |
| } |
| |
| SourcePosition position = doc.position(); |
| if (null != position) { |
| printAtomTag(level, "position file=\"" + position.file().getAbsolutePath() + "\" line=\"" + position.line() + "\" column=\"" + position.column() + "\""); |
| } |
| } |
| |
| protected void outputProgramElementDocBody(int level, ProgramElementDoc programElementDoc) { |
| outputDocBody(level, programElementDoc); |
| printAtomTag(level, "containingPackage name=\""+programElementDoc.containingPackage().name()+"\""); |
| if (null!=programElementDoc.containingClass()) { |
| outputType(level, "containingClass", programElementDoc.containingClass()); |
| } |
| String access; |
| if (programElementDoc.isPublic()) |
| access="public"; |
| else if (programElementDoc.isProtected()) |
| access="protected"; |
| else if (programElementDoc.isPrivate()) |
| access="private"; |
| else if (programElementDoc.isPackagePrivate()) |
| access="package"; |
| else |
| throw new RuntimeException("Huh? "+programElementDoc+" is neither public, protected, private nor package protected."); |
| printAtomTag(level, "access scope=\""+access+"\""); |
| if (programElementDoc.isFinal()) |
| printAtomTag(level, "isFinal"); |
| if (programElementDoc.isStatic()) |
| printAtomTag(level, "isStatic"); |
| } |
| |
| protected void outputTags(int level, Tag[] tags, boolean descend, int context) { |
| |
| for (int i=0; i<tags.length; ++i) { |
| outputTag(tags[i], level, descend, context, i == tags.length-1); |
| } |
| } |
| |
| protected void outputTag(Tag tag, int level, boolean descend, int context, boolean lastTag) { |
| |
| if (!"Text".equals(tag.name())) { |
| printOpenTag(0 /* don't introduce additional whitespace */, |
| "tag kind=\""+tag.kind()+"\" name=\""+tag.name()+"\"", false); |
| } |
| if (tag instanceof ThrowsTag) { |
| ThrowsTag throwsTag = (ThrowsTag)tag; |
| if (null!=throwsTag.exception()) { |
| outputType(level+1, "exception", throwsTag.exception()); |
| } |
| else { |
| StringBuffer sb = new StringBuffer("Exception "); |
| sb.append(throwsTag.exceptionName()); |
| sb.append(" not found in "); |
| if (currentExecMember instanceof MethodDoc) { |
| MethodDoc m = (MethodDoc)currentExecMember; |
| sb.append(m.returnType().typeName()); |
| sb.append(m.returnType().dimension()); |
| sb.append(' '); |
| } |
| sb.append(currentClass.qualifiedName()); |
| sb.append('.'); |
| sb.append(currentExecMember.name()); |
| sb.append('('); |
| Parameter[] params = currentExecMember.parameters(); |
| for (int j=0; j < params.length; j++) { |
| sb.append(params[j].type().typeName()); |
| sb.append(params[j].type().dimension()); |
| sb.append(' '); |
| sb.append(params[j].name()); |
| if (j != params.length-1) |
| sb.append(", "); |
| } |
| sb.append(')'); |
| printWarning(sb.toString()); |
| |
| printAtomTag(level+1, "exception typename=\""+throwsTag.exceptionName()+"\""); |
| } |
| } |
| else if (tag instanceof ParamTag) { |
| ParamTag paramTag = (ParamTag)tag; |
| printAtomTag(level+1, "parameter name=\""+paramTag.parameterName()+"\""); |
| } |
| |
| if (null != tag.text()) { |
| //printOpenTag(level+1, "text", false); |
| if (fixHTML) { |
| print(htmlRepairer.getWellformedHTML(tag.text())); |
| } |
| else { |
| print("<![CDATA["+cdata(tag.text())+"]]>"); |
| } |
| //printCloseTag(0 /* don't introduce additional whitespace */, "text"); |
| } |
| else { |
| printWarning("Tag got null text: "+tag); |
| } |
| |
| if ((descend && ("@throws".equals(tag.name()) || "@param".equals(tag.name()))) || "@deprecated".equals(tag.name())) { |
| if (tag.firstSentenceTags().length>0) { |
| printOpenTag(level+1, "firstSentenceTags", false); |
| outputTags(level+2, tag.firstSentenceTags(), false, context); |
| printCloseTag(0, "firstSentenceTags"); |
| } |
| |
| if (tag.inlineTags().length>0) { |
| printOpenTag(level+1, "inlineTags", false); |
| outputTags(level+2, tag.firstSentenceTags(), false, context); |
| printCloseTag(0, "inlineTags"); |
| } |
| } |
| |
| if (fixHTML && lastTag) { |
| String terminateText = htmlRepairer.terminateText(); |
| if (null != terminateText && terminateText.length() > 0) { |
| print(terminateText); |
| } |
| } |
| |
| if (!"Text".equals(tag.name())) { |
| |
| Taglet inlineTaglet = (Taglet)tagletMap.get(tag.name().substring(1)); |
| if (null != inlineTaglet && inlineTaglet.isInlineTag()) { |
| printOpenTag(0, "inlineTagletText", false); |
| print(inlineTaglet.toString(tag)); |
| printCloseTag(0, "inlineTagletText"); |
| } |
| |
| printCloseTag(0, "tag", false); |
| } |
| } |
| |
| void outputTaglets(int level, Tag[] tags, boolean descend, int context) |
| { |
| for (Iterator it = tagletMap.keySet().iterator(); it.hasNext(); ) { |
| String tagName = (String)it.next(); |
| Object o = tagletMap.get(tagName); |
| Taglet taglet = (Taglet)o; |
| |
| if (!taglet.isInlineTag() |
| && ((context != CONTEXT_CONSTRUCTOR || taglet.inConstructor()) |
| || (context != CONTEXT_FIELD || taglet.inField()) |
| || (context != CONTEXT_METHOD || taglet.inMethod()) |
| || (context != CONTEXT_OVERVIEW || taglet.inOverview()) |
| || (context != CONTEXT_PACKAGE || taglet.inPackage()) |
| || (context != CONTEXT_TYPE || taglet.inType()))) { |
| |
| List tagsOfThisType = new ArrayList(); |
| for (int i=0, ilim=tags.length; i<ilim; ++i) { |
| if (tags[i].name().substring(1).equals(tagName)) { |
| tagsOfThisType.add(tags[i]); |
| } |
| } |
| |
| if (!tagsOfThisType.isEmpty()) { |
| Tag[] tagletTags = (Tag[])tagsOfThisType.toArray(new Tag[tagsOfThisType.size()]); |
| if (taglet instanceof StandardTaglet) { |
| Iterator tagIterator = tagsOfThisType.iterator(); |
| while (tagIterator.hasNext()) { |
| Tag tag = (Tag)tagIterator.next(); |
| outputTag(tag, level, descend, context, !tagIterator.hasNext()); |
| } |
| } |
| else { |
| String tagletString = taglet.toString(tagletTags); |
| if (null != tagletString) { |
| printOpenTag(0, "tag name=\"" + tagName + "\" taglet-generated=\"true\""); |
| if (fixHTML) { |
| print(htmlRepairer.getWellformedHTML(tagletString)); |
| print(htmlRepairer.terminateText()); |
| } |
| else { |
| print("<![CDATA["+cdata(tagletString)+"]]>"); |
| } |
| printCloseTag(0, "tag", false); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Inofficial entry point. We got an instance here. |
| */ |
| protected boolean instanceStart(RootDoc _rootDoc) { |
| |
| this.rootDoc = _rootDoc; |
| _rootDoc = null; |
| |
| boolean xmlOnly = true; |
| |
| // Set the default Taglet order |
| |
| registerTaglet(new VersionTaglet()); |
| registerTaglet(new AuthorTaglet()); |
| //registerTaglet(new SinceTaglet()); |
| registerTaglet(new StandardTaglet("deprecated")); |
| registerTaglet(new StandardTaglet("see")); |
| registerTaglet(new StandardTaglet("param")); |
| |
| // Set the built-in Taglet filter |
| |
| AuthorTaglet.setTagletEnabled(false); |
| VersionTaglet.setTagletEnabled(false); |
| SinceTaglet.setTagletEnabled(true); |
| DeprecatedTaglet.setTagletEnabled(true); |
| |
| try { |
| { |
| |
| // Process command line options passed through to this doclet |
| |
| TargetContext targetContext = null; |
| |
| TargetContext htmlTargetContext |
| = new TargetContext(DocTranslet.fromClasspath("/doctranslets/html/gjdoc.xsl"), |
| targetDirectory); |
| |
| for (int i=0, ilim=rootDoc.options().length; i<ilim; ++i) { |
| |
| String[] option = rootDoc.options()[i]; |
| String optionTag = option[0]; |
| |
| if ("-d".equals(optionTag)) { |
| if (null == targetDirectory) { |
| targetDirectory = new File(option[1]); |
| } |
| if (null != targetContext) { |
| targetContext.setTargetDirectory(targetDirectory); |
| } |
| } |
| |
| else if ("-nofixhtml".equals(optionTag)) { |
| fixHTML = false; |
| printError("-nofixhtml currently not supported."); |
| return false; |
| } |
| else if ("-compress".equals(optionTag)) { |
| compress = true; |
| } |
| else if ("-nohtmlwarn".equals(optionTag)) { |
| noHTMLWarn = true; |
| } |
| else if ("-noemailwarn".equals(optionTag)) { |
| noEmailWarn = true; |
| } |
| else if ("-indentstep".equals(optionTag)) { |
| indentStep = Integer.parseInt(option[1]); |
| } |
| else if ("-doctranslet".equals(optionTag)) { |
| targets.add(targetContext = new TargetContext(DocTranslet.fromJarFile(new File(option[1])), |
| targetDirectory)); |
| } |
| else if ("-genhtml".equals(optionTag)) { |
| htmlTargetContext.setTargetDirectory(targetDirectory); |
| targets.add(targetContext = htmlTargetContext); |
| xmlOnly = false; |
| } |
| else if ("-geninfo".equals(optionTag)) { |
| targetContext |
| = new TargetContext(DocTranslet.fromClasspath("/doctranslets/info/gengj.xsl"), |
| targetDirectory); |
| targets.add(targetContext); |
| if (!fixHTML) { |
| printNotice("NOTE: -geninfo implies -fixhtml."); |
| fixHTML = true; |
| } |
| xmlOnly = false; |
| } |
| else if ("-gendocbook".equals(optionTag)) { |
| targetContext = new TargetContext(DocTranslet.fromClasspath("/doctranslets/docbook/gengj.xsl"), |
| targetDirectory); |
| targets.add(targetContext); |
| if (!fixHTML) { |
| printNotice("NOTE: -gendocbook implies -fixhtml."); |
| fixHTML = true; |
| } |
| } |
| else if ("-genpdf".equals(optionTag)) { |
| targetContext |
| = new TargetContext(DocTranslet.fromClasspath("/doctranslets/docbook/gengj.xsl"), |
| targetDirectory); |
| /** "gnu.classpath.tools.doclets.xmldoclet.DocBookPostprocessor") **/ |
| targets.add(targetContext); |
| if (!fixHTML) { |
| printNotice("NOTE: -genpdf implies -fixhtml."); |
| fixHTML = true; |
| } |
| } |
| else if ("-xmlonly".equals(optionTag)) { |
| xmlOnly = true; |
| } |
| else if ("-bottomnote".equals(optionTag)) { |
| |
| FileReader reader = new FileReader(option[1]); |
| StringWriter writer = new StringWriter(); |
| char[] buf = new char[256]; |
| int nread; |
| while ((nread = reader.read(buf)) >= 0) { |
| writer.write(buf, 0, nread); |
| } |
| writer.flush(); |
| bottomNote = writer.toString(); |
| writer.close(); |
| reader.close(); |
| } |
| else if ("-title".equals(optionTag)) { |
| |
| title = option[1]; |
| } |
| else if ("-workpath".equals(optionTag)) { |
| |
| workingPath = option[1]; |
| } |
| else if ("-tagletpath".equals(optionTag)) { |
| |
| if (null == tagletPath) { |
| tagletPath = option[1]; |
| } |
| else { |
| tagletPath = tagletPath + File.pathSeparator + option[1]; |
| } |
| } |
| else if ("-taglet".equals(optionTag)) { |
| |
| boolean tagletLoaded = false; |
| |
| String useTagletPath = this.tagletPath; |
| if (null == useTagletPath) { |
| useTagletPath = System.getProperty("java.class.path"); |
| } |
| |
| try { |
| Class tagletClass; |
| try { |
| tagletClass |
| = new FileSystemClassLoader(useTagletPath).loadClass(option[1]); |
| } |
| catch (ClassNotFoundException e) { |
| // If not found on specified tagletpath, try default classloader |
| tagletClass |
| = Class.forName(option[1]); |
| } |
| Method registerTagletMethod |
| = tagletClass.getDeclaredMethod("register", new Class[] { java.util.Map.class }); |
| |
| if (!registerTagletMethod.getReturnType().equals(Void.TYPE)) { |
| printError("Taglet class '" + option[1] + "' found, but register method doesn't return void."); |
| } |
| else if (registerTagletMethod.getExceptionTypes().length > 0) { |
| printError("Taglet class '" + option[1] + "' found, but register method contains throws clause."); |
| } |
| else if ((registerTagletMethod.getModifiers() & (Modifier.STATIC | Modifier.PUBLIC | Modifier.ABSTRACT)) != (Modifier.STATIC | Modifier.PUBLIC)) { |
| printError("Taglet class '" + option[1] + "' found, but register method isn't public static, or is abstract.."); |
| } |
| else { |
| Map tempMap = new HashMap(); |
| registerTagletMethod.invoke(null, new Object[] { tempMap }); |
| tagletLoaded = true; |
| String name = (String)tempMap.keySet().iterator().next(); |
| Taglet taglet = (Taglet)tempMap.get(name); |
| tagletMap.put(name, taglet); |
| mentionedTags.add(taglet); |
| } |
| } |
| catch (NoSuchMethodException e) { |
| printError("Taglet class '" + option[1] + "' found, but doesn't contain the register method."); |
| } |
| catch (SecurityException e) { |
| printError("Taglet class '" + option[1] + "' cannot be loaded: " + e.getMessage()); |
| } |
| catch (InvocationTargetException e) { |
| printError("Taglet class '" + option[1] + "' found, but register method throws exception: " + e.toString()); |
| } |
| catch (IllegalAccessException e) { |
| printError("Taglet class '" + option[1] + "' found, but there was a problem when accessing the register method: " + e.toString()); |
| } |
| catch (IllegalArgumentException e) { |
| printError("Taglet class '" + option[1] + "' found, but there was a problem when accessing the register method: " + e.toString()); |
| } |
| catch (ClassNotFoundException e) { |
| printError("Taglet class '" + option[1] + "' cannot be found."); |
| } |
| if (!tagletLoaded) { |
| return false; |
| } |
| } |
| else if ("-author".equals(optionTag)) { |
| AuthorTaglet.setTagletEnabled(true); |
| } |
| else if ("-version".equals(optionTag)) { |
| VersionTaglet.setTagletEnabled(true); |
| } |
| else if ("-nosince".equals(optionTag)) { |
| SinceTaglet.setTagletEnabled(false); |
| } |
| else if ("-nodeprecated".equals(optionTag)) { |
| DeprecatedTaglet.setTagletEnabled(false); |
| } |
| else if ("-authormail".equals(optionTag)) { |
| |
| if ("no-replace".equalsIgnoreCase(option[1])) { |
| AuthorTaglet.setEmailReplacementType(AuthorTaglet.EmailReplacement.NO_REPLACEMENT); |
| } |
| else if ("mailto-name".equalsIgnoreCase(option[1])) { |
| AuthorTaglet.setEmailReplacementType(AuthorTaglet.EmailReplacement.MAILTO_NAME); |
| } |
| else if ("name-mailto-address".equalsIgnoreCase(option[1])) { |
| AuthorTaglet.setEmailReplacementType(AuthorTaglet.EmailReplacement.NAME_MAILTO_ADDRESS); |
| } |
| else if ("name-mangled-address".equalsIgnoreCase(option[1])) { |
| AuthorTaglet.setEmailReplacementType(AuthorTaglet.EmailReplacement.NAME_MANGLED_ADDRESS); |
| } |
| else { |
| printError("Invalid value for option '-authortag-email'. Allowed values are:" |
| + " no-replace, mailto-name, name-mailto-address, name-mangled-address."); |
| return false; |
| } |
| } |
| else if ("-mailmangledot".equals(optionTag)) { |
| AuthorTaglet.setDotReplacement(option[1]); |
| } |
| else if ("-mailmangleat".equals(optionTag)) { |
| AuthorTaglet.setAtReplacement(option[1]); |
| } |
| else if ("-docfilessubdirs".equals(optionTag)) { |
| docFilesSubdirsEnabled = true; |
| } |
| else if ("-excludedocfilessubdir".equals(optionTag)) { |
| StringTokenizer st = new StringTokenizer(option[1]); |
| while (st.hasMoreTokens()) { |
| excludeDocFilesSubDirs.add(st.nextToken()); |
| } |
| } |
| else if ("-nonavbar".equals(optionTag)) { |
| docTransletOptions.nonavbar = true; |
| } |
| else if ("-noindex".equals(optionTag)) { |
| docTransletOptions.noindex = true; |
| } |
| else if ("-notree".equals(optionTag)) { |
| docTransletOptions.notree = true; |
| } |
| else if ("-nocomment".equals(optionTag)) { |
| docTransletOptions.nocomment = true; |
| } |
| else if ("-nohelp".equals(optionTag)) { |
| docTransletOptions.nohelp = true; |
| } |
| else if ("-splitindex".equals(optionTag)) { |
| docTransletOptions.splitindex = true; |
| } |
| else if ("-linksource".equals(optionTag)) { |
| docTransletOptions.linksource = true; |
| } |
| else if ("-windowtitle".equals(optionTag)) { |
| docTransletOptions.windowtitle = option[1]; |
| } |
| else if ("-helpfile".equals(optionTag)) { |
| docTransletOptions.helpfile = new File(option[1]).toURL().toString(); |
| } |
| else if ("-stylesheetfile".equals(optionTag)) { |
| docTransletOptions.stylesheetfile = new File(option[1]).toURL().toString(); |
| } |
| else if ("-header".equals(optionTag)) { |
| docTransletOptions.header = option[1]; |
| } |
| else if ("-footer".equals(optionTag)) { |
| docTransletOptions.footer = option[1]; |
| } |
| else if ("-bottom".equals(optionTag)) { |
| docTransletOptions.bottom = option[1]; |
| } |
| else if ("-doctitle".equals(optionTag)) { |
| docTransletOptions.doctitle = option[1]; |
| } |
| else if ("-nodeprecatedlist".equals(optionTag)) { |
| docTransletOptions.nodeprecatedlist = true; |
| } |
| else if ("-uses".equals(optionTag)) { |
| docTransletOptions.uses = true; |
| } |
| else if ("-group".equals(optionTag)) { |
| if (!processGroupOption(option[1], option[2])) { |
| printError("Invalid package wildcard list in -group option \"" + option[1] + "\" " + option[2]); |
| return false; |
| } |
| } |
| else if ("-tag".equals(optionTag)) { |
| String tagSpec = option[1]; |
| boolean validTagSpec = false; |
| int ndx1 = tagSpec.indexOf(':'); |
| if (ndx1 < 0) { |
| Taglet taglet = (Taglet)tagletMap.get(tagSpec); |
| if (null == taglet) { |
| printError("There is no standard tag '" + tagSpec + "'."); |
| } |
| else { |
| if (mentionedTags.contains(taglet)) { |
| printError("Tag '" + tagSpec + "' has been added or moved before."); |
| } |
| else { |
| mentionedTags.add(taglet); |
| |
| // re-append taglet |
| tagletMap.remove(tagSpec); |
| tagletMap.put(tagSpec, taglet); |
| } |
| } |
| } |
| else { |
| int ndx2 = tagSpec.indexOf(':', ndx1 + 1); |
| if (ndx2 > ndx1 && ndx2 < tagSpec.length() - 1) { |
| String tagName = tagSpec.substring(0, ndx1); |
| String tagHead = null; |
| if (tagSpec.charAt(ndx2 + 1) == '\"') { |
| if (tagSpec.charAt(tagSpec.length() - 1) == '\"') { |
| tagHead = tagSpec.substring(ndx2 + 2, tagSpec.length() - 1); |
| validTagSpec = true; |
| } |
| } |
| else { |
| tagHead = tagSpec.substring(ndx2 + 1); |
| validTagSpec = true; |
| } |
| |
| boolean tagScopeOverview = false; |
| boolean tagScopePackages = false; |
| boolean tagScopeTypes = false; |
| boolean tagScopeConstructors = false; |
| boolean tagScopeMethods = false; |
| boolean tagScopeFields = false; |
| boolean tagDisabled = false; |
| |
| tag_option_loop: |
| for (int n=ndx1+1; n<ndx2; ++n) { |
| switch (tagSpec.charAt(n)) { |
| case 'X': |
| tagDisabled = true; |
| break; |
| case 'a': |
| tagScopeOverview = true; |
| tagScopePackages = true; |
| tagScopeTypes = true; |
| tagScopeConstructors = true; |
| tagScopeMethods = true; |
| tagScopeFields = true; |
| break; |
| case 'o': |
| tagScopeOverview = true; |
| break; |
| case 'p': |
| tagScopePackages = true; |
| break; |
| case 't': |
| tagScopeTypes = true; |
| break; |
| case 'c': |
| tagScopeConstructors = true; |
| break; |
| case 'm': |
| tagScopeMethods = true; |
| break; |
| case 'f': |
| tagScopeFields = true; |
| break; |
| default: |
| validTagSpec = false; |
| break tag_option_loop; |
| } |
| } |
| |
| if (validTagSpec) { |
| GenericTaglet taglet |
| = new GenericTaglet(tagName, |
| tagHead, |
| tagScopeOverview, |
| tagScopePackages, |
| tagScopeTypes, |
| tagScopeConstructors, |
| tagScopeMethods, |
| tagScopeFields); |
| taglet.setTagletEnabled(!tagDisabled); |
| taglet.register(tagletMap); |
| mentionedTags.add(taglet); |
| } |
| } |
| } |
| if (!validTagSpec) { |
| printError("Value for option -tag must be in format \"<tagname>:Xaoptcmf:<taghead>\"."); |
| } |
| } |
| } |
| |
| // Use current directory if target directory hasn't been set. |
| if (null == targetDirectory) { |
| targetDirectory = new File(System.getProperty("user.dir")); |
| } |
| if (null != targetContext) { |
| targetContext.setTargetDirectory(targetDirectory); |
| } |
| |
| // It is illegal to specify targets AND -xmlonly. |
| |
| if (xmlOnly && targets.size() > 0) { |
| |
| printError("You can only specify one of -xmlonly and a target format."); |
| return false; |
| } |
| |
| // If no target was specified and XML only was not |
| // requested, use HTML as default target. |
| |
| if (!xmlOnly && targets.size() == 0) { |
| targets.add(targetContext = htmlTargetContext); |
| } |
| |
| // Set the same target directory for all output. |
| |
| // FIXME: Allow separate target directories for different |
| // output formats. |
| |
| for (Iterator it = targets.iterator(); it.hasNext(); ) { |
| TargetContext t = (TargetContext)it.next(); |
| t.setTargetDirectory(targetDirectory); |
| } |
| |
| // Create temporary directory if necessary |
| |
| if (xmlOnly) { |
| |
| xmlTargetDirectory = targetDirectory; |
| } |
| else { |
| |
| File workingTopDirectory = new File(workingPath); |
| |
| workingDirectory = new File(workingTopDirectory, "gjdoc.tmp."+System.currentTimeMillis()); |
| |
| if (!workingDirectory.mkdir()) { |
| printError("Cannot create temporary directory at "+System.getProperty("java.io.tmpdir")); |
| return false; |
| } |
| |
| File xmlTempDirectory = new File(workingDirectory, "xmloutput"); |
| |
| if (!xmlTempDirectory.mkdir()) { |
| printError("Cannot create temporary directory for XML output at "+System.getProperty("java.io.tmpdir")); |
| return false; |
| } |
| |
| xmlTargetDirectory = xmlTempDirectory; |
| } |
| |
| // Create target directory if necessary |
| |
| if (!targetDirectory.exists()) { |
| printNotice("Creating destination directory: \"" |
| + targetDirectory + "\""); |
| if (!targetDirectory.mkdirs()) { |
| printError("Failed to create destination directory \"" |
| + targetDirectory + "\""); |
| return false; |
| } |
| } |
| |
| // Check for deprecation |
| |
| boolean hasDeprecatedClasses = false; |
| boolean hasDeprecatedInterfaces = false; |
| boolean hasDeprecatedExceptions = false; |
| boolean hasDeprecatedErrors = false; |
| boolean hasDeprecatedMethods = false; |
| boolean hasDeprecatedFields = false; |
| |
| { |
| ClassDoc[] classes = rootDoc.classes(); |
| for (int i = 0, ilim = classes.length; i < ilim; ++ i) { |
| ClassDoc c = classes[i]; |
| Tag[] deprecatedTags = c.tags("deprecated"); |
| if (null != deprecatedTags && 0 != deprecatedTags.length) { |
| if (c.isInterface()) { |
| hasDeprecatedInterfaces = true; |
| } |
| else if (c.isException()) { |
| hasDeprecatedExceptions = true; |
| } |
| else if (c.isError()) { |
| hasDeprecatedErrors = true; |
| } |
| else /*if (c.isOrdinaryClass())*/ { |
| hasDeprecatedClasses = true; |
| } |
| } |
| |
| MethodDoc[] methods = c.methods(); |
| for (int j = 0, jlim = methods.length; j < jlim; ++ j) { |
| MethodDoc m = methods[j]; |
| deprecatedTags = m.tags("deprecated"); |
| if (null != deprecatedTags && 0 != deprecatedTags.length) { |
| hasDeprecatedMethods = true; |
| } |
| } |
| |
| FieldDoc[] fields = c.fields(); |
| for (int j = 0, jlim = fields.length; j < jlim; ++ j) { |
| FieldDoc f = fields[j]; |
| deprecatedTags = f.tags("deprecated"); |
| if (null != deprecatedTags && 0 != deprecatedTags.length) { |
| hasDeprecatedFields = true; |
| } |
| } |
| } |
| } |
| |
| htmlRepairer = new HtmlRepairer(rootDoc, noHTMLWarn, noEmailWarn, |
| currentClass, currentMember, |
| false); |
| |
| collectUsage(); |
| |
| // Begin XML generation |
| |
| printNotice("Writing XML Index file..."); |
| |
| // Assign output stream |
| |
| setTargetFile("index.xml"); |
| |
| // Output XML document header |
| |
| println(0, "<?xml version=\"1.0\"?>"); |
| println("<!DOCTYPE gjdoc SYSTEM \"dtd/gjdoc.dtd\">"); |
| println(); |
| printOpenTag(0, "rootdoc xmlns=\"http://www.w3.org/TR/REC-html40\" xmlns:gjdoc=\"http://www.gnu.org/software/cp-tools/gjdocxml\""); |
| |
| println(); |
| println(1, "<!-- Tags from overview page, if available -->"); |
| |
| if (rootDoc.firstSentenceTags().length > 0) { |
| printOpenTag(2, "firstSentenceTags", false); |
| outputTags(3, rootDoc.firstSentenceTags(), true, CONTEXT_PACKAGE); |
| printCloseTag(0, "firstSentenceTags"); |
| } |
| |
| if (rootDoc.inlineTags().length > 0) { |
| printOpenTag(2, "inlineTags"); |
| outputTags(3, rootDoc.inlineTags(), true, CONTEXT_PACKAGE); |
| printCloseTag(2, "inlineTags"); |
| } |
| |
| if (null != bottomNote) { |
| printOpenTag(1, "bottomnote"); |
| print(bottomNote); |
| printCloseTag(1, "bottomnote"); |
| } |
| |
| if (null != title) { |
| printOpenTag(1, "title"); |
| println(2, title); |
| printCloseTag(1, "title"); |
| } |
| |
| printOpenTag(1, "created"); |
| println(2, DateFormat.getDateInstance(DateFormat.LONG, Locale.US).format(new java.util.Date())); |
| printCloseTag(1, "created"); |
| |
| if (hasDeprecatedClasses) printAtomTag(1, "hasDeprecatedClasses"); |
| if (hasDeprecatedInterfaces) printAtomTag(1, "hasDeprecatedInterfaces"); |
| if (hasDeprecatedExceptions) printAtomTag(1, "hasDeprecatedExceptions"); |
| if (hasDeprecatedErrors) printAtomTag(1, "hasDeprecatedErrors"); |
| if (hasDeprecatedMethods) printAtomTag(1, "hasDeprecatedMethods"); |
| if (hasDeprecatedFields) printAtomTag(1, "hasDeprecatedFields"); |
| |
| // Output summary of all classes specified on command line |
| |
| println(); |
| println(1, "<!-- Classes specified by user on command line -->"); |
| ClassDoc[] specifiedClasses = rootDoc.specifiedClasses(); |
| for (int i=0, ilim=specifiedClasses.length; i<ilim; ++i) { |
| ClassDoc sc = specifiedClasses[i]; |
| printAtomTag(1, "specifiedclass fqname=\""+sc.qualifiedName()+"\" name=\""+sc.name()+"\""); |
| } |
| specifiedClasses = null; |
| |
| // Output summary of all packages specified on command line |
| |
| println(); |
| println(1, "<!-- Packages specified by user on command line -->"); |
| PackageDoc[] specifiedPackages = rootDoc.specifiedPackages(); |
| for (int i=0, ilim=specifiedPackages.length; i<ilim; ++i) { |
| PackageDoc sp = specifiedPackages[i]; |
| printAtomTag(1, "specifiedpackage name=\""+sp.name()+"\""); |
| } |
| specifiedPackages = null; |
| |
| // Output package group information specified on the |
| // command line |
| |
| println(); |
| println(1, "<!-- Package groups specified by user on command line -->"); |
| { |
| Iterator packageGroupIt = packageGroups.iterator(); |
| while (packageGroupIt.hasNext()) { |
| PackageGroup packageGroup = (PackageGroup)packageGroupIt.next(); |
| SortedSet groupedPackages = packageGroup.getPackages(); |
| if (groupedPackages.isEmpty()) { |
| printWarning("Package group named '" |
| + packageGroup.getName() + "' didn't match any packages."); |
| } |
| else { |
| printOpenTag(1, "packagegroup name=\"" + packageGroup.getName() + "\""); |
| Iterator groupedPackageIt = groupedPackages.iterator(); |
| while (groupedPackageIt.hasNext()) { |
| PackageDoc groupedPackageDoc = (PackageDoc)groupedPackageIt.next(); |
| printAtomTag(2, "package name=\"" + groupedPackageDoc.name() + "\""); |
| } |
| printCloseTag(1, "packagegroup"); |
| } |
| } |
| packageGroups = null; |
| } |
| |
| // Output information on all packages for which documentation |
| // has been made available via the Doclet API |
| |
| println(); |
| println(1, "<!-- Documentation for all packages -->"); |
| PackageDoc[] packages = rootDoc.specifiedPackages(); |
| for (int i=0, ilim=packages.length; i<ilim; ++i) { |
| PackageDoc c = packages[i]; |
| outputPackageDoc(c); |
| } |
| packages = null; |
| |
| // Output brief summary on all classes for which documentation |
| // has been made available via the Doclet API. |
| // |
| // While this is redundant, it can speed up XSLT |
| // processing by orders of magnitude |
| |
| println(); |
| println(1, "<!-- Brief summary for all classes -->"); |
| ClassDoc[] sumclasses = rootDoc.classes(); |
| for (int i=0, ilim=sumclasses.length; i<ilim; ++i) { |
| ClassDoc c = sumclasses[i]; |
| outputClassDocSummary(c); |
| } |
| sumclasses = null; |
| |
| // Output closing tag, finish output stream |
| |
| println(); |
| printCloseTag(0, "rootdoc"); |
| |
| closeTargetFile(); |
| |
| createIndexByName(); |
| |
| |
| |
| // Output information on all classes for which documentation |
| // has been made available via the Doclet API |
| |
| println(); |
| println(1, "<!-- Documentation for all classes -->"); |
| ClassDoc[] classes = rootDoc.classes(); |
| String prevPackageName = null; |
| for (int i = 0, ilim = classes.length; i < ilim; ++ i) { |
| ClassDoc c = classes[i]; |
| |
| if (isVerbose()) { |
| printNotice("Writing XML information for "+c.qualifiedName()+"..."); |
| } |
| else { |
| String packageName = c.containingPackage().name(); |
| if (null == prevPackageName || !packageName.equals(prevPackageName)) { |
| printNotice("Writing XML information for "+packageName+"..."); |
| prevPackageName = packageName; |
| } |
| } |
| |
| setTargetFile(c.qualifiedName().replace('/','.')+".xml"); |
| |
| println("<?xml version=\"1.0\"?>"); |
| println("<!DOCTYPE gjdoc SYSTEM \"dtd/gjdoc.dtd\">"); |
| |
| outputClassDoc(c); |
| |
| closeTargetFile(); |
| } |
| classes = null; |
| } |
| |
| // Copy DTD files to temporary directory |
| |
| // FIXME: try to solve this via jar: URLs. but this will |
| // probably break libxmlj compatibility (?) |
| |
| String[] resources = new String[] { |
| "gjdoc.dtd", |
| "gjdoc-alphaindex.dtd", |
| "dbcentx.mod", |
| "ent/iso-amsa.ent", |
| "ent/iso-amsb.ent", |
| "ent/iso-amsc.ent", |
| "ent/iso-amsn.ent", |
| "ent/iso-amso.ent", |
| "ent/iso-amsr.ent", |
| "ent/iso-box.ent", |
| "ent/iso-cyr1.ent", |
| "ent/iso-cyr2.ent", |
| "ent/iso-dia.ent", |
| "ent/iso-grk1.ent", |
| "ent/iso-grk2.ent", |
| "ent/iso-grk3.ent", |
| "ent/iso-grk4.ent", |
| "ent/iso-lat1.ent", |
| "ent/iso-lat2.ent", |
| "ent/iso-num.ent", |
| "ent/iso-pub.ent", |
| "ent/iso-tech.ent", |
| }; |
| |
| File tempDtdDirectory = new File(xmlTargetDirectory, "dtd"); |
| File tempDtdEntDirectory = new File(tempDtdDirectory, "ent"); |
| |
| if ((tempDtdDirectory.exists() || tempDtdDirectory.mkdir()) |
| && (tempDtdEntDirectory.exists() || tempDtdEntDirectory.mkdir())) { |
| for (int i = 0; i < resources.length; ++ i) { |
| copyResourceToFile("/dtd/" + resources[i], |
| new File(tempDtdDirectory, resources[i])); |
| } |
| } |
| else { |
| printError("Cannot create temporary directories for DTD data at " + tempDtdDirectory); |
| return false; |
| } |
| |
| // Copy package data-dir directory |
| |
| { |
| PackageDoc[] packages = rootDoc.specifiedPackages(); |
| for (int i=0, ilim=packages.length; i<ilim; ++i) { |
| PackageDoc c = packages[i]; |
| if (c instanceof GjdocPackageDoc) { |
| copyPackageDataDir((GjdocPackageDoc)c); |
| } |
| } |
| } |
| |
| // All information has been output. Apply stylesheet if given. |
| |
| gnu.classpath.tools.gjdoc.Main.releaseRootDoc(); |
| |
| this.currentClass = null; |
| this.currentMember = null; |
| this.currentExecMember = null; |
| |
| System.gc(); |
| |
| // From this point we are only operating on files, so we don't |
| // need this anymore and can free up some memory |
| |
| for (Iterator it = targets.iterator(); it.hasNext(); ) { |
| |
| TargetContext target = (TargetContext)it.next(); |
| |
| // We have XSLT postprocessing, run DocTranslet. |
| |
| //DocTranslet docTranslet = DocTranslet.fromClasspath("/doctranslets/html/gjdoc.xsl"); |
| |
| //docTranslet.setOptions(docTransletOptions); |
| |
| target.getDocTranslet().setOptions(docTransletOptions); |
| |
| target.getDocTranslet().apply(xmlTargetDirectory, |
| target.getTargetDirectory(), |
| rootDoc); |
| } |
| |
| // Done |
| |
| targets = null; |
| |
| System.gc(); |
| Runtime.getRuntime().runFinalization(); |
| |
| return true; |
| } |
| catch (Exception e) { |
| |
| // Something went wrong. Report to stderr and pass error to |
| // Javadoc Reporter |
| |
| e.printStackTrace(); |
| printError(e.toString()); |
| |
| Throwable rootCause = e.getCause(); |
| if (null != rootCause) { |
| while (null != rootCause.getCause()) { |
| rootCause = rootCause.getCause(); |
| } |
| System.err.println("Root cause:"); |
| rootCause.printStackTrace(); |
| } |
| |
| return false; |
| } |
| finally { |
| |
| // In any case, delete the working directory if we created one |
| |
| if (null != workingDirectory) { |
| |
| if (!deleteRecursive(workingDirectory)) { |
| printWarning("Could not delete temporary directory at "+workingDirectory); |
| } |
| } |
| |
| printNotice("Done."); |
| } |
| } |
| |
| /** |
| * Recursively delete the specified directory and its contents, |
| * like <code>rm -Rf directory</code> |
| * |
| * @return <code>true</code> on success |
| */ |
| private static boolean deleteRecursive(File directory) { |
| |
| boolean success = true; |
| |
| File[] files = directory.listFiles(); |
| |
| for (int i=0, ilim=files.length; i<ilim; ++i) { |
| |
| File file = files[i]; |
| |
| if (file.isDirectory()) { |
| |
| success = deleteRecursive(file) && success; |
| } |
| else { |
| |
| success = file.delete() && success; |
| } |
| } |
| |
| return directory.delete() && success; |
| } |
| |
| /** |
| * Prints a string to stdout and appends a newline. Convenience |
| * method. |
| */ |
| protected void println(String str) { |
| out.println(str); |
| } |
| |
| /** |
| * Prints a string to stdout without appending a newline. |
| * Convenience method. |
| */ |
| protected void print(String str) { |
| out.print(str); |
| } |
| |
| /** |
| * In standard mode, prints an empty line to stdout. |
| * In thight mode, nothing happens. |
| * Convenience method. |
| */ |
| protected void println() { |
| if (!compress) { |
| out.println(); |
| } |
| } |
| |
| /** |
| * In standard mode, prints the given text indented to stdout and appends newline. |
| * In tight mode, doesn't print indentation or newlines. |
| */ |
| protected void print(int indentLevel, String msg) { |
| if (compress) { |
| out.print(msg); |
| } |
| else { |
| StringBuffer indentation = new StringBuffer(); |
| for (int i=0; i<indentLevel*indentStep; ++i) { |
| indentation.append(' '); |
| } |
| out.print(indentation+msg); |
| } |
| } |
| |
| /** |
| * In tight mode, prints a message at a given indentation level. |
| * In standard mode, appends a newline in addition. |
| */ |
| protected void println(int indentLevel, String msg) { |
| print(indentLevel, msg); |
| if (!compress) out.println(); |
| } |
| |
| /** |
| * Prints an atom tag at the given indentation level. |
| */ |
| protected void printAtomTag(int level, String tag) { |
| println(level, "<"+tagPrefix+":"+replaceCharsInTag(tag)+"/>"); |
| } |
| |
| /** |
| * Prints an open tag at the given indentation level. |
| */ |
| protected void printOpenTag(int level, String tag) { |
| printOpenTag(level, replaceCharsInTag(tag), true); |
| } |
| |
| /** |
| * Prints an open tag at the given indentation level and |
| * conditionally appends a newline (if not in tight mode). |
| */ |
| protected void printOpenTag(int level, String tag, boolean appendNewline) { |
| if (appendNewline && !compress) { |
| println(level, "<"+tagPrefix+":"+replaceCharsInTag(tag)+">"); |
| } |
| else { |
| print(level, "<"+tagPrefix+":"+replaceCharsInTag(tag)+">"); |
| } |
| } |
| |
| /** |
| * Prints a close tag at the given indentation level. |
| */ |
| protected void printCloseTag(int level, String tag) { |
| printCloseTag(level, tag, true); |
| } |
| |
| /** |
| * Prints a close tag at the given indentation level and |
| * conditionally appends a newline (if not in tight mode). |
| */ |
| protected void printCloseTag(int level, String tag, boolean appendNewline) { |
| if (appendNewline && !compress) { |
| println(level, "</"+tagPrefix+":"+replaceCharsInTag(tag)+">"); |
| } |
| else { |
| print(level, "</"+tagPrefix+":"+replaceCharsInTag(tag)+">"); |
| } |
| } |
| |
| public static int optionLength(String option) { |
| if ("-d".equals(option)) return 2; |
| else if ("-fixhtml".equals(option)) return 1; |
| else if ("-compress".equals(option)) return 1; |
| else if ("-nohtmlwarn".equals(option)) return 1; |
| else if ("-noemailwarn".equals(option)) return 1; |
| else if ("-indentstep".equals(option)) return 2; |
| else if ("-xslsheet".equals(option)) return 2; |
| else if ("-xsltdriver".equals(option)) return 2; |
| else if ("-postprocess".equals(option)) return 2; |
| else if ("-genhtml".equals(option)) return 1; |
| else if ("-geninfo".equals(option)) return 1; |
| else if ("-gendocbook".equals(option)) return 1; |
| else if ("-xmlonly".equals(option)) return 1; |
| else if ("-bottomnote".equals(option)) return 2; |
| else if ("-workpath".equals(option)) return 2; |
| else if ("-title".equals(option)) return 2; |
| else if ("-tagletpath".equals(option)) return 2; |
| else if ("-taglet".equals(option)) return 2; |
| else if ("-authormail".equals(option)) return 2; |
| else if ("-mailmangledot".equals(option)) return 2; |
| else if ("-mailmangleat".equals(option)) return 2; |
| else if ("-noindex".equals(option)) return 1; |
| else if ("-nocomment".equals(option)) return 1; |
| else if ("-notree".equals(option)) return 1; |
| else if ("-nohelp".equals(option)) return 1; |
| else if ("-nonavbar".equals(option)) return 1; |
| else if ("-splitindex".equals(option)) return 1; |
| else if ("-author".equals(option)) return 1; |
| else if ("-version".equals(option)) return 1; |
| else if ("-nosince".equals(option)) return 1; |
| else if ("-nodeprecated".equals(option)) return 1; |
| else if ("-linksource".equals(option)) return 1; |
| else if ("-windowtitle".equals(option)) return 2; |
| else if ("-helpfile".equals(option)) return 2; |
| else if ("-stylesheetfile".equals(option)) return 2; |
| else if ("-tag".equals(option)) return 2; |
| else if ("-header".equals(option)) return 2; |
| else if ("-footer".equals(option)) return 2; |
| else if ("-bottom".equals(option)) return 2; |
| else if ("-doctitle".equals(option)) return 2; |
| else if ("-nodeprecatedlist".equals(option)) return 1; |
| else if ("-uses".equals(option)) return 1; |
| else if ("-group".equals(option)) return 3; |
| |
| else return -1; |
| } |
| |
| public static boolean validOptions(String[][] options) { |
| return true; |
| } |
| |
| |
| /** |
| * Workaround for non well-formed comments: fix tag contents |
| * by replacing <code><</code> with <code>&lt;</code>, |
| * <code>></code> with <code>&gt;</code> and |
| * <code>&</code> with <code>&amp;</code>. |
| * |
| * @param tagContent String to process |
| * |
| * @return given String with all special characters replaced by |
| * HTML entities. |
| */ |
| private static String replaceCharsInTag(String tagContent) { |
| return |
| replaceString( |
| replaceString( |
| replaceString( |
| tagContent, |
| "<", "<" |
| ), |
| ">", ">" |
| ), |
| "&", "&" |
| ); |
| } |
| |
| /** |
| * Replaces all occurences of string <code>needle</code> within string |
| * <code>haystack</code> by string <code>replacement</code>. |
| * |
| * @param haystack The string to search and replace in. |
| * @param needle The string which is searched for. |
| * @param replacement The string by which every occurence of <code>needle</code> is replaced. |
| */ |
| private static String replaceString(String haystack, String needle, String replacement) { |
| int ndx = haystack.indexOf(needle); |
| if (ndx<0) |
| return haystack; |
| else |
| return haystack.substring(0, ndx) + replacement |
| + replaceString(haystack.substring(ndx+needle.length()), needle, replacement); |
| } |
| |
| protected void setTargetFile(String filename) throws IOException { |
| |
| OutputStream fileOut = new FileOutputStream(new File(xmlTargetDirectory, filename)); |
| out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(fileOut, "UTF8")));; |
| } |
| |
| protected void closeTargetFile() { |
| |
| out.flush(); |
| out.close(); |
| } |
| |
| private String cdata(String str) { |
| |
| if (null==str) { |
| return str; |
| } // end of if ((null==str) |
| |
| StringBuffer rc = new StringBuffer(); |
| for (int i=0; i<str.length(); ++i) { |
| char c = str.charAt(i); |
| if (c==0x09 || c==0x0a || c==0x0d || (c>=0x20 && c<=0xd7ff) || (c>=0xe000 && c<=0xfffd) || (c>=0x10000 && c<=0x10ffff)) { |
| rc.append(c); |
| } |
| else { |
| printWarning("Invalid Unicode character 0x"+Integer.toString(c, 16)+" in javadoc markup has been stripped."); |
| } // end of else |
| |
| } |
| return rc.toString(); |
| } |
| |
| static void copyResourceToFile(String resourceName, File target) throws IOException { |
| |
| InputStream in = Driver.class.getResourceAsStream(resourceName); |
| |
| if (null != in) { |
| |
| FileOutputStream out = new FileOutputStream(target); |
| int size; |
| byte[] buffer = new byte[512]; |
| while ((size = in.read(buffer)) >= 0) { |
| out.write(buffer, 0, size); |
| } |
| out.close(); |
| } |
| else { |
| |
| throw new IOException("Can't find resource named "+resourceName); |
| } |
| } |
| |
| private void printError(String error) { |
| if (null != rootDoc) { |
| rootDoc.printError(error); |
| } |
| else { |
| System.err.println("ERROR: "+error); |
| } |
| } |
| |
| private void printWarning(String warning) { |
| if (null != rootDoc) { |
| rootDoc.printWarning(warning); |
| } |
| else { |
| System.err.println("WARNING: "+warning); |
| } |
| } |
| |
| private void printNotice(String notice) { |
| if (null != rootDoc) { |
| rootDoc.printNotice(notice); |
| } |
| else { |
| System.err.println(notice); |
| } |
| } |
| |
| /** |
| * Copy the contents of the input directory to the output |
| * directory. The output directory must exist. |
| */ |
| private void copyPackageDataDir(GjdocPackageDoc packageDoc) throws IOException { |
| File docFilesSourceDirectory |
| = new File(packageDoc.packageDirectory(), "doc-files"); |
| File docFilesTargetDirectory |
| = new File(this.targetDirectory, |
| packageDoc.name().replace('.', File.separatorChar)); |
| if (docFilesSourceDirectory.exists()) { |
| printNotice("Copying files from " + docFilesSourceDirectory); |
| copyDirectory(docFilesSourceDirectory, docFilesTargetDirectory, |
| docFilesSubdirsEnabled, |
| excludeDocFilesSubDirs); |
| } |
| } |
| |
| /** |
| * Recursively copy the contents of the input directory to the |
| * output directory. The output directory must exist. |
| */ |
| private static void copyDirectory(File sourceDir, File targetDir, |
| boolean recursive, |
| Set excludeDirs) throws IOException { |
| if (!targetDir.exists() && !targetDir.mkdirs()) { |
| throw new IOException("Cannot create directory " + targetDir); |
| } |
| |
| File[] sourceFiles = sourceDir.listFiles(); |
| for (int i=0; i<sourceFiles.length; ++i) { |
| if (sourceFiles[i].isDirectory()) { |
| if (recursive && (null == excludeDirs |
| || !excludeDirs.contains(sourceFiles[i].getName()))) { |
| File targetSubDir = new File(targetDir, |
| sourceFiles[i].getName()); |
| if (targetSubDir.exists() || targetSubDir.mkdir()) { |
| copyDirectory(sourceFiles[i], targetSubDir, recursive, null); |
| } |
| else { |
| throw new IOException("Cannot create directory " + targetSubDir); |
| } |
| } |
| } |
| else { |
| copyFile(sourceFiles[i], new File(targetDir, sourceFiles[i].getName())); |
| } |
| } |
| } |
| |
| /** |
| * Copy the contents of the input file to the output file. The |
| * output file's parent directory must exist. |
| */ |
| private static void copyFile(File sourceFile, File targetFile) throws IOException { |
| |
| InputStream in = new FileInputStream(sourceFile); |
| OutputStream out = new FileOutputStream(targetFile); |
| int nread; |
| byte[] buf = new byte[512]; |
| while ((nread = in.read(buf)) >= 0) { |
| out.write(buf, 0, nread); |
| } |
| in.close(); |
| out.close(); |
| } |
| |
| private void createIndexByName() throws IOException { |
| // Create index |
| |
| // Collect index |
| |
| Map indexMap = new TreeMap(new Comparator() { |
| public int compare(Object o1, Object o2) { |
| return o1.toString().toLowerCase().compareTo(o2.toString().toLowerCase()); |
| } |
| }); |
| |
| // Add packages to index |
| |
| PackageDoc[] packages = rootDoc.specifiedPackages(); |
| for (int i=0, ilim=packages.length; i<ilim; ++i) { |
| PackageDoc c = packages[i]; |
| indexMap.put(c.name(), c); |
| } |
| |
| // Add classes, fields and methods to index |
| |
| ClassDoc[] sumclasses = rootDoc.classes(); |
| for (int i=0, ilim=sumclasses.length; i<ilim; ++i) { |
| ClassDoc c = sumclasses[i]; |
| if (null == c.containingClass()) { |
| indexMap.put(c.name(), c); |
| } |
| else { |
| indexMap.put(c.name().substring(c.containingClass().name().length() + 1), c); |
| } |
| FieldDoc[] fields = c.fields(); |
| for (int j=0, jlim=fields.length; j<jlim; ++j) { |
| indexMap.put(fields[j].name(), fields[j]); |
| } |
| MethodDoc[] methods = c.methods(); |
| for (int j=0, jlim=methods.length; j<jlim; ++j) { |
| MethodDoc method = methods[j]; |
| StringBuffer signature = new StringBuffer(); |
| signature.append(method.name()); |
| signature.append('('); |
| Parameter[] parameters = method.parameters(); |
| for (int k=0, klim=parameters.length; k<klim; ++k) { |
| if (k > 0) { |
| signature.append(", "); |
| } |
| signature.append(parameters[k].typeName()); |
| } |
| signature.append(')'); |
| indexMap.put(signature.toString(), method); |
| } |
| } |
| |
| // Assign output stream |
| |
| setTargetFile("alphaindex.xml"); |
| |
| // Output XML document header |
| |
| println(0, "<?xml version=\"1.0\"?>"); |
| println("<!DOCTYPE gjdoc SYSTEM \"dtd/gjdoc-alphaindex.dtd\">"); |
| println(); |
| printOpenTag(0, "alphaindex xmlns=\"http://www.w3.org/TR/REC-html40\" xmlns:gjdoc=\"http://www.gnu.org/software/cp-tools/gjdocxml\""); |
| |
| Iterator it = indexMap.keySet().iterator(); |
| |
| char previousCategoryLetter = '\0'; |
| boolean categoryOpen = false; |
| |
| while (it.hasNext()) { |
| String key = (String)it.next(); |
| Doc entry = (Doc)indexMap.get(key); |
| |
| char firstChar = Character.toUpperCase(key.charAt(0)); |
| if (firstChar != previousCategoryLetter) { |
| if (categoryOpen) { |
| printCloseTag(1, "category"); |
| } |
| printOpenTag(1, "category letter=\"" + firstChar + "\""); |
| categoryOpen = true; |
| previousCategoryLetter = firstChar; |
| } |
| |
| printOpenTag(2, "entry name=\"" + key + "\""); |
| if (entry instanceof PackageDoc) { |
| printAtomTag(3, "isPackage"); |
| } |
| else if (entry instanceof ClassDoc) { |
| printAtomTag(3, "isClass"); |
| ClassDoc centry = (ClassDoc)entry; |
| currentClass = centry; |
| printAtomTag(3, "containingPackage name=\"" + centry.containingPackage().name() + "\""); |
| if (null != centry.containingClass()) { |
| printAtomTag(3, "containingClass name=\"" + centry.containingClass().name() + "\""); |
| } |
| if (centry.isInterface()) { |
| printAtomTag(3, "isInterface"); |
| } |
| if (centry.isException()) { |
| printAtomTag(3, "isException"); |
| } |
| if (centry.isError()) { |
| printAtomTag(3, "isError"); |
| } |
| if (centry.isOrdinaryClass()) { |
| printAtomTag(3, "isOrdinaryClass"); |
| } |
| } |
| else if (entry instanceof ProgramElementDoc) { |
| ProgramElementDoc pentry = (ProgramElementDoc)entry; |
| currentClass = pentry.containingClass(); |
| printAtomTag(3, "containingPackage name=\"" + pentry.containingPackage().name() + "\""); |
| printAtomTag(3, "containingClass name=\"" + pentry.containingClass().name() + "\""); |
| if (pentry.isMethod()) { |
| printAtomTag(3, "isMethod"); |
| ExecutableMemberDoc mentry = (ExecutableMemberDoc)pentry; |
| printAtomTag(3, "signature full=\""+mentry.signature()+"\" flat=\""+mentry.flatSignature()+"\""); |
| printAtomTag(3, "method name=\"" + mentry.name() + "\""); |
| } |
| if (pentry.isField()) { |
| printAtomTag(3, "isField"); |
| } |
| } |
| |
| Tag[] tags = entry.firstSentenceTags(); |
| for (int i=0, ilim=tags.length; i<ilim; ++i) { |
| Tag tag = tags[i]; |
| if (tag.firstSentenceTags().length>0) { |
| printOpenTag(3, "firstSentenceTags", false); |
| outputTags(4, tag.firstSentenceTags(), false, CONTEXT_TYPE); |
| printCloseTag(3, "firstSentenceTags"); |
| } |
| } |
| |
| |
| printCloseTag(2, "entry"); |
| } |
| |
| if (categoryOpen) { |
| printCloseTag(1, "category"); |
| } |
| |
| printCloseTag(0, "alphaindex"); |
| |
| closeTargetFile(); |
| } |
| |
| private static class UsageType |
| { |
| public static final UsageType CLASS_DERIVED_FROM = new UsageType("class-derived-from"); |
| public static final UsageType FIELD_OF_TYPE = new UsageType("field-of-type"); |
| public static final UsageType METHOD_WITH_RETURN_TYPE = new UsageType("method-with-return-type"); |
| public static final UsageType METHOD_WITH_PARAMETER_TYPE = new UsageType("method-with-parameter-type"); |
| public static final UsageType METHOD_WITH_THROWN_TYPE = new UsageType("method-with-thrown-type"); |
| public static final UsageType CONSTRUCTOR_WITH_PARAMETER_TYPE = new UsageType("constructor-with-parameter-type"); |
| public static final UsageType CONSTRUCTOR_WITH_THROWN_TYPE = new UsageType("constructor-with-thrown-type"); |
| private String id; |
| |
| private UsageType(String id) |
| { |
| this.id = id; |
| } |
| |
| public String toString() { |
| return "UsageType{id=" + id + "}"; |
| } |
| |
| public String getId() { |
| return id; |
| } |
| } |
| |
| /** |
| * ClassDoc -> (PackageDoc -> (UsageType -> (Set of Doc))) |
| */ |
| private Map usedClassToPackagesMap = new HashMap(); |
| |
| private void addUsedBy(ClassDoc usedClass, UsageType usageType, Doc user, PackageDoc userPackage) |
| { |
| Map packageToUsageTypeMap = (Map)usedClassToPackagesMap.get(usedClass); |
| if (null == packageToUsageTypeMap) { |
| packageToUsageTypeMap = new HashMap(); |
| usedClassToPackagesMap.put(usedClass, packageToUsageTypeMap); |
| } |
| |
| Map usageTypeToUsersMap = (Map)packageToUsageTypeMap.get(userPackage); |
| if (null == usageTypeToUsersMap) { |
| usageTypeToUsersMap = new HashMap(); |
| packageToUsageTypeMap.put(userPackage, usageTypeToUsersMap); |
| } |
| |
| Set userSet = (Set)usageTypeToUsersMap.get(usageType); |
| if (null == userSet) { |
| userSet = new TreeSet(); // FIXME: we need the collator from Main here |
| usageTypeToUsersMap.put(usageType, userSet); |
| } |
| userSet.add(user); |
| } |
| |
| /** |
| * Create the cross reference database. |
| */ |
| private void collectUsage() { |
| |
| ClassDoc[] classes = rootDoc.classes(); |
| for (int i = 0, ilim = classes.length; i < ilim; ++ i) { |
| ClassDoc clazz = classes[i]; |
| |
| // classes derived from |
| for (ClassDoc superclass = clazz.superclass(); superclass != null; |
| superclass = superclass.superclass()) { |
| addUsedBy(superclass, UsageType.CLASS_DERIVED_FROM, clazz, clazz.containingPackage()); |
| } |
| |
| FieldDoc[] fields = clazz.fields(); |
| for (int j = 0, jlim = fields.length; j < jlim; ++ j) { |
| FieldDoc field = fields[j]; |
| |
| // fields of type |
| ClassDoc fieldType = field.type().asClassDoc(); |
| if (null != fieldType) { |
| addUsedBy(fieldType, UsageType.FIELD_OF_TYPE, |
| field, clazz.containingPackage()); |
| } |
| } |
| |
| MethodDoc[] methods = clazz.methods(); |
| for (int j = 0, jlim = methods.length; j < jlim; ++ j) { |
| MethodDoc method = methods[j]; |
| |
| // methods with return type |
| |
| ClassDoc returnType = method.returnType().asClassDoc(); |
| if (null != returnType) { |
| addUsedBy(returnType, UsageType.METHOD_WITH_RETURN_TYPE, |
| method, clazz.containingPackage()); |
| } |
| Parameter[] parameters = method.parameters(); |
| for (int k=0; k<parameters.length; ++k) { |
| |
| // methods with parameter type |
| |
| Parameter parameter = parameters[k]; |
| ClassDoc parameterType = parameter.type().asClassDoc(); |
| if (null != parameterType) { |
| addUsedBy(parameterType, UsageType.METHOD_WITH_PARAMETER_TYPE, |
| method, clazz.containingPackage()); |
| } |
| } |
| |
| // methods which throw |
| |
| ClassDoc[] thrownExceptions = method.thrownExceptions(); |
| for (int k = 0, klim = thrownExceptions.length; k < klim; ++ k) { |
| ClassDoc thrownException = thrownExceptions[k]; |
| addUsedBy(thrownException, UsageType.METHOD_WITH_THROWN_TYPE, |
| method, clazz.containingPackage()); |
| } |
| } |
| |
| ConstructorDoc[] constructors = clazz.constructors(); |
| for (int j = 0, jlim = constructors.length; j < jlim; ++ j) { |
| |
| ConstructorDoc constructor = constructors[j]; |
| |
| Parameter[] parameters = constructor.parameters(); |
| for (int k = 0, klim = parameters.length; k < klim; ++ k) { |
| |
| // constructors with parameter type |
| |
| Parameter parameter = parameters[k]; |
| ClassDoc parameterType = parameter.type().asClassDoc(); |
| if (null != parameterType) { |
| addUsedBy(parameterType, UsageType.CONSTRUCTOR_WITH_PARAMETER_TYPE, |
| constructor, clazz.containingPackage()); |
| } |
| } |
| |
| // constructors which throw |
| |
| ClassDoc[] thrownExceptions = constructor.thrownExceptions(); |
| for (int k = 0, klim = thrownExceptions.length; k < klim; ++ k) { |
| ClassDoc thrownException = thrownExceptions[k]; |
| addUsedBy(thrownException, UsageType.CONSTRUCTOR_WITH_THROWN_TYPE, |
| constructor, clazz.containingPackage()); |
| } |
| } |
| } |
| } |
| |
| private void outputUsage(ClassDoc clazz, int level) { |
| |
| Map packageToUsageTypeMap = (Map)usedClassToPackagesMap.get(clazz); |
| if (null != packageToUsageTypeMap) { |
| printOpenTag(level, "references"); |
| |
| Iterator packagesIterator = packageToUsageTypeMap.keySet().iterator(); |
| |
| while (packagesIterator.hasNext()) { |
| PackageDoc packageDoc = (PackageDoc)packagesIterator.next(); |
| printOpenTag(level + 1, "referencing-package name=\"" + packageDoc.name() + "\""); |
| Map usageTypeToUsersMap = (Map)packageToUsageTypeMap.get(packageDoc); |
| Iterator usageTypeIterator = usageTypeToUsersMap.keySet().iterator(); |
| while (usageTypeIterator.hasNext()) { |
| UsageType usageType = (UsageType)usageTypeIterator.next(); |
| printOpenTag(level + 2, "usage-type id=\"" + usageType.getId() + "\""); |
| Set users = (Set)usageTypeToUsersMap.get(usageType); |
| Iterator userIterator = users.iterator(); |
| while (userIterator.hasNext()) { |
| Doc user = (Doc)userIterator.next(); |
| if (user instanceof ClassDoc) { |
| printAtomTag(level + 3, "user" |
| + " class=\"" + ((ClassDoc)user).name() + "\""); |
| } |
| else if (user instanceof FieldDoc) { |
| FieldDoc fieldDoc = (FieldDoc)user; |
| printAtomTag(level + 3, "user" |
| + " class=\"" + fieldDoc.containingClass().name() + "\"" |
| + " field=\"" + fieldDoc.name() + "\""); |
| } |
| else if (user instanceof MethodDoc) { |
| MethodDoc methodDoc = (MethodDoc)user; |
| printAtomTag(level + 3, "user" |
| + " class=\"" + methodDoc.containingClass().name() + "\"" |
| + " method=\"" + methodDoc.name() + "\"" |
| + " signature=\"" + methodDoc.signature() + "\"" |
| + " flatSignature=\"" + methodDoc.flatSignature() + "\""); |
| } |
| else if (user instanceof ConstructorDoc) { |
| ConstructorDoc constructorDoc = (ConstructorDoc)user; |
| printAtomTag(level + 3, "user" |
| + " class=\"" + constructorDoc.containingClass().name() + "\"" |
| + " signature=\"" + constructorDoc.signature() + "\"" |
| + " flatSignature=\"" + constructorDoc.flatSignature() + "\""); |
| } |
| } |
| printCloseTag(level +2, "usage-type"); |
| } |
| printCloseTag(level + 1, "referencing-package"); |
| } |
| |
| printCloseTag(level, "references"); |
| } |
| } |
| |
| private boolean processGroupOption(String groupName, String colonSeparatedPackageList) |
| { |
| try { |
| PackageMatcher packageMatcher = new PackageMatcher(); |
| |
| StringTokenizer tokenizer = new StringTokenizer(colonSeparatedPackageList, ":"); |
| while (tokenizer.hasMoreTokens()) { |
| String packageWildcard = tokenizer.nextToken(); |
| packageMatcher.addWildcard(packageWildcard); |
| } |
| |
| SortedSet groupPackages = packageMatcher.filter(rootDoc.specifiedPackages()); |
| |
| packageGroups.add(new PackageGroup(groupName, groupPackages)); |
| |
| return true; |
| } |
| catch (InvalidPackageWildcardException e) { |
| return false; |
| } |
| } |
| |
| private void registerTaglet(Taglet taglet) |
| { |
| tagletMap.put(taglet.getName(), taglet); |
| } |
| |
| private boolean isVerbose() |
| { |
| return false; |
| } |
| } |