re PR libgcj/37636 (java tools are unable to find resource files)
[gcc.git] / libjava / classpath / tools / gnu / classpath / tools / doclets / xmldoclet / Driver.java
1 /* gnu.classpath.tools.doclets.xmldoclet.Driver
2 Copyright (C) 2001 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
20
21 package gnu.classpath.tools.doclets.xmldoclet;
22
23 import com.sun.javadoc.*;
24 import java.io.*;
25
26 import com.sun.tools.doclets.Taglet;
27
28 import java.lang.reflect.InvocationTargetException;
29 import java.lang.reflect.Method;
30 import java.lang.reflect.Modifier;
31
32 import java.text.DateFormat;
33
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Comparator;
37 import java.util.HashSet;
38 import java.util.TreeSet;
39 import java.util.Iterator;
40 import java.util.LinkedHashMap;
41 import java.util.LinkedList;
42 import java.util.List;
43 import java.util.Locale;
44 import java.util.Map;
45 import java.util.HashMap;
46 import java.util.Properties;
47 import java.util.Set;
48 import java.util.SortedSet;
49 import java.util.StringTokenizer;
50 import java.util.TreeMap;
51
52 import gnu.classpath.tools.gjdoc.TemporaryStore;
53 import gnu.classpath.tools.gjdoc.GjdocPackageDoc;
54
55 import gnu.classpath.tools.doclets.PackageGroup;
56 import gnu.classpath.tools.doclets.PackageMatcher;
57 import gnu.classpath.tools.doclets.InvalidPackageWildcardException;
58
59 import gnu.classpath.tools.doclets.xmldoclet.doctranslet.DocTranslet;
60 import gnu.classpath.tools.doclets.xmldoclet.doctranslet.DocTransletOptions;
61
62 import gnu.classpath.tools.taglets.AuthorTaglet;
63 import gnu.classpath.tools.taglets.VersionTaglet;
64 import gnu.classpath.tools.taglets.SinceTaglet;
65 import gnu.classpath.tools.taglets.DeprecatedTaglet;
66 import gnu.classpath.tools.taglets.GenericTaglet;
67 import gnu.classpath.tools.doclets.StandardTaglet;
68
69 import gnu.classpath.tools.java2xhtml.Java2xhtml;
70
71 import gnu.classpath.tools.IOToolkit;
72 import gnu.classpath.tools.FileSystemClassLoader;
73
74 /**
75 * A Doclet which retrieves all information presented by the Doclet
76 * API, dumping it to stdout in XML format.
77 *
78 * @author Julian Scheid
79 */
80 public class Driver {
81
82 public static final String XMLDOCLET_VERSION = "0.6.1";
83
84 /**
85 * Used for redirecting error messages to <code>/dev/null</code>.
86 */
87 private static class NullErrorReporter implements DocErrorReporter {
88 public void printError(String ignore) {}
89 public void printWarning(String ignore) {}
90 public void printNotice(String ignore) {}
91 }
92
93 /*
94 * Taglet context constants.
95 */
96 private static final int CONTEXT_CONSTRUCTOR = 1;
97 private static final int CONTEXT_FIELD = 2;
98 private static final int CONTEXT_METHOD = 3;
99 private static final int CONTEXT_OVERVIEW = 4;
100 private static final int CONTEXT_PACKAGE = 5;
101 private static final int CONTEXT_TYPE = 6;
102
103 /**
104 * All XML output will go to this stream.
105 */
106 private PrintWriter out;
107
108 /**
109 * How many spaces to indent each XML node level,
110 * i.e. Tab size for output.
111 */
112 private static int indentStep = 1;
113
114 /**
115 * Won't output superfluous spaces if set to true.
116 * If set to false, output will be more legible.
117 */
118 private boolean compress = false;
119
120 /**
121 * Won't output warning messages while fixing
122 * HTML code if set to true.
123 */
124 private boolean noHTMLWarn = false;
125
126 /**
127 * Won't output warning messages when encountering tags
128 * that look like an email address if set to true.
129 */
130 private boolean noEmailWarn = false;
131
132 /**
133 * Will fix HTML if necessary so that each comment
134 * contains valid XML code if set to true. If set
135 * to false, HTML code will not be modified and
136 * instead encapsulated in a CDATA section.
137 */
138 private boolean fixHTML = true;
139
140 /**
141 * User-specified name of the directory where the final version of
142 * the generated files will be written to.
143 *
144 * If no XSLT sheet is given, the XML output will go directly into
145 * this directory. Otherwise, XML output will go to a temporary
146 * directory and XSLT output will go to this directory.
147 */
148 private File targetDirectory = null;
149
150 /**
151 * Directory where XML output will be written to. If no XSLT
152 * sheet was given, this is the target directory specified
153 * by the user. Otherwise, this is a temporary directory.
154 */
155 private File xmlTargetDirectory;
156
157 /**
158 * Contains a number of TargetContexts which describe which XSLT
159 * sheet to apply to the output of this doclet, to what directory
160 * the XSLT output is written, and which postprocess driver to use
161 * to process XSLT output.
162 */
163 private List targets = new ArrayList();
164
165 /**
166 * XML text to include at the end of every generated page. Read
167 * from the file specified on the command line using -bottomnote.
168 * If present, this will be written to the main output file
169 * (index.xml) in node /gjdoc:rootDoc/gjdoc:bottomnote.
170 */
171 private String bottomNote;
172
173 /**
174 * Brief description of the package set. Can be specified on the
175 * command line using -title. This will be written to the main
176 * output file (index.xml) in node
177 * /gjdoc:rootDoc/gjdoc:title. The HTML generating XSLT sheet
178 * uses this for example in window titles.
179 */
180 private String title;
181
182 /**
183 * Path to the directory where temporary files should be stored.
184 * Defaults to system tempdir, but can be overridden by user
185 * with -workpath.
186 */
187 private String workingPath = System.getProperty("java.io.tmpdir");
188
189 /**
190 * Temporary directory created by this doclet where all
191 * temporary files will be stored in. If no temporary
192 * files are needed (i.e. no XSLT postprocessing stage
193 * specified by user), this is <code>null</code>.
194 */
195 private File workingDirectory;
196
197 /**
198 * Whether to deep-copy the doc-files subdirectory.
199 */
200 private boolean docFilesSubdirsEnabled = false;
201
202 /**
203 * Which direct subdirectories of the doc-files directories to exclude.
204 * Set of String.
205 */
206 private Set excludeDocFilesSubDirs = new HashSet();
207
208 /**
209 * Stores the Doclet API RootDoc we are operating on.
210 */
211 private RootDoc rootDoc;
212
213 /**
214 * XML namespace prefix used for all tags, except for HTML
215 * tags copied from Javadoc comments. Excluding colon.
216 */
217 public static final String tagPrefix = "gjdoc";
218
219 /**
220 * Classpath for loading Taglet classes.
221 */
222 private String tagletPath = null;
223
224 /**
225 * The current class that is being processed.
226 * Set in outputClassDoc().
227 */
228 private ClassDoc currentClass;
229
230 /**
231 * The current member that is being processed.
232 * Set in outputMemberDoc().
233 */
234 private MemberDoc currentMember;
235
236 /**
237 * The current constructor/method that is being processed.
238 * Set in outputExecutableMemberDoc().
239 */
240 private ExecutableMemberDoc currentExecMember;
241
242 /**
243 * Mapping from tag type to Taglet for user Taglets specified on
244 * the command line.
245 */
246 private Map tagletMap = new LinkedHashMap();
247
248 /**
249 * Keeps track of the tags mentioned by the user during option
250 * processiong so that an error can be emitted if a tag is
251 * mentioned more than once.
252 */
253 private List mentionedTags = new LinkedList();
254
255 /**
256 * Stores options to be passed to the DocTranslet.
257 */
258 private DocTransletOptions docTransletOptions = new DocTransletOptions();
259
260 /**
261 * Stores the package groups specified in the user
262 * options. Contains objects of type PackageGroup.
263 */
264 private List packageGroups = new LinkedList();
265
266 private HtmlRepairer htmlRepairer;
267
268 public static boolean start(TemporaryStore _rootDocWrapper) {
269 return new Driver().instanceStart((RootDoc)_rootDocWrapper.getAndClear());
270 }
271
272 /**
273 * Official Doclet entry point.
274 */
275 public static boolean start(RootDoc _rootDoc) {
276
277 // Create a new XmlDoclet instance and delegate control.
278 TemporaryStore tstore = new TemporaryStore(_rootDoc);
279 _rootDoc = null;
280 return new Driver().instanceStart((RootDoc)tstore.getAndClear());
281 }
282
283 /**
284 * Output an XML tag describing a com.sun.javadoc.Type object.
285 * Assumes that the tag does not have subtags.
286 *
287 * @param level Level of indentation. Will be multiplied by
288 * <code>indentStep</code> to yield actual amount
289 * of whitespace inserted at start of line.
290 * @param tag Identifier for the XML tag being output.
291 * @param type The Javadoc Type to be output.
292 */
293 protected void outputType(int level, String tag, Type type) {
294 outputType(level, tag, type, true);
295 }
296
297 protected void outputType(int level, String tag, Type type, boolean atomic) {
298
299 boolean isIncluded = false;
300 ClassDoc typeAsClassDoc = type.asClassDoc();
301 String packageName = null;
302 if (null != typeAsClassDoc) {
303 isIncluded = typeAsClassDoc.isIncluded();
304 packageName = typeAsClassDoc.containingPackage().name();
305 }
306 println(level, "<"+tagPrefix+":"+tag + " typename=\""+type.typeName()+"\""+
307 " qualifiedtypename=\""+type.qualifiedTypeName()+"\""
308 +(type.dimension().length()==0?"":" dimension=\""+type.dimension()+"\"")
309 +(isIncluded?" isIncluded=\"true\"" : "")
310 +((null != packageName)?" package=\"" + packageName + "\"" : "")
311 +(atomic?"/":"")+">");
312 }
313
314 protected void outputExecutableMemberDocBody(int level, ExecutableMemberDoc memberDoc) {
315
316 currentExecMember = memberDoc;
317
318 outputMemberDocBody(level, memberDoc);
319
320 Parameter[] parameters = memberDoc.parameters();
321 for (int i=0, ilim=parameters.length; i<ilim; ++i) {
322 Parameter parameter = parameters[i];
323 outputType(level, "parameter name=\""+parameter.name()+"\"", parameter.type());
324 }
325
326 ClassDoc[] exceptions = memberDoc.thrownExceptions();
327 for (int i=0, ilim=exceptions.length; i<ilim; ++i) {
328 ClassDoc exception = exceptions[i];
329 outputType(level, "thrownException", exception);
330 }
331
332 printAtomTag(level, "signature full=\""+memberDoc.signature()+"\" flat=\""+memberDoc.flatSignature()+"\"");
333
334 if (memberDoc.isNative()) {
335 printAtomTag(level, "isNative");
336 }
337
338 if (memberDoc.isSynchronized()) {
339 printAtomTag(level, "isSynchronized");
340 }
341 }
342
343 protected void outputMethodDoc(int level, MethodDoc methodDoc) {
344 println();
345 printOpenTag(level, "methoddoc name=\""+methodDoc.name()+"\"");
346 outputExecutableMemberDocBody(level+1, methodDoc);
347 outputType(level+1, "returns", methodDoc.returnType());
348 printCloseTag(level, "methoddoc");
349 }
350
351 protected void outputMemberDocBody(int level, MemberDoc memberDoc) {
352 currentMember = memberDoc;
353 outputProgramElementDocBody(level, memberDoc);
354 }
355
356 protected void outputFieldDocBody(int level, FieldDoc fieldDoc) {
357 outputType(level, "type", fieldDoc.type());
358 if (fieldDoc.isTransient()) {
359 printAtomTag(level, "isTransient");
360 }
361 if (fieldDoc.isVolatile()) {
362 printAtomTag(level, "isVolatile");
363 }
364 }
365
366 private void outputFieldDoc(int level, FieldDoc fieldDoc) {
367 println();
368 printOpenTag(level, "fielddoc name=\""+fieldDoc.name()+"\"");
369 outputMemberDocBody(level+1, fieldDoc);
370 outputFieldDocBody(level+1, fieldDoc);
371 printCloseTag(level, "fielddoc");
372 }
373
374 protected void outputConstructorDoc(int level, ConstructorDoc constructorDoc) {
375 println();
376 printOpenTag(level, "constructordoc name=\""+constructorDoc.name()+"\"");
377 outputExecutableMemberDocBody(level+1, constructorDoc);
378 printCloseTag(level, "constructordoc");
379 }
380
381 protected void outputSuperInterfacesRec(int level, ClassDoc classDoc) {
382 if (null!=classDoc) {
383 ClassDoc[] interfaces = classDoc.interfaces();
384 if (null != interfaces) {
385 for (int i=0, ilim=interfaces.length; i<ilim; ++i) {
386 outputType(level, "superimplements", interfaces[i]);
387 }
388 }
389 outputSuperInterfacesRec(level, classDoc.superclass());
390 }
391 }
392
393 protected void outputClassDocSummary(ClassDoc classDoc) {
394 println();
395 printOpenTag(1, "classdoc name=\""+classDoc.name()+"\" qualifiedtypename=\""+classDoc.qualifiedName()+"\" isIncluded=\"true\"");
396 if (null!=classDoc.superclass()) {
397 outputType(2, "superclass", classDoc.superclass());
398 }
399
400 ClassDoc[] interfaces = classDoc.interfaces();
401 for (int i=0, ilim=interfaces.length; i<ilim; ++i) {
402 outputType(2, "implements", interfaces[i]);
403 }
404 outputSuperInterfacesRec(2, classDoc.superclass());
405
406 printAtomTag(2, "containingPackage name=\""+classDoc.containingPackage().name()+"\"");
407 if (classDoc.isError()) {
408 printAtomTag(2, "isError");
409 }
410 if (classDoc.isException()) {
411 printAtomTag(2, "isException");
412 }
413 if (classDoc.isInterface()) {
414 printAtomTag(2, "isInterface");
415 }
416 if (classDoc.isOrdinaryClass()) {
417 printAtomTag(2, "isOrdinaryClass");
418 }
419
420 printCloseTag(1, "classdoc");
421 }
422
423 protected void outputPackageDoc(PackageDoc packageDoc) {
424 println();
425 printOpenTag(1, "packagedoc name=\""+packageDoc.name()+"\"");
426 if (packageDoc.firstSentenceTags().length > 0) {
427 printOpenTag(2, "firstSentenceTags", false);
428 outputTags(3, packageDoc.firstSentenceTags(), true, CONTEXT_PACKAGE);
429 printCloseTag(0, "firstSentenceTags");
430 printOpenTag(2, "inlineTags", false);
431 outputTags(3, packageDoc.inlineTags(), true, CONTEXT_PACKAGE);
432 printCloseTag(0, "inlineTags");
433 }
434
435 if (packageDoc.tags().length > 0) {
436 printOpenTag(2, "tags");
437 outputTags(3, packageDoc.tags(), true, CONTEXT_PACKAGE);
438 printCloseTag(2, "tags");
439 }
440
441 if (packageDoc.seeTags().length > 0) {
442 printOpenTag(2, "seeTags");
443 outputTags(3, packageDoc.seeTags(), true, CONTEXT_PACKAGE);
444 printCloseTag(2, "seeTags");
445 }
446
447 ClassDoc[] allClasses = (ClassDoc[]) packageDoc.allClasses().clone();
448 Arrays.sort(allClasses);
449
450 if (false) {
451 for (int i = 0, ilim = allClasses.length; i < ilim; ++ i) {
452 printAtomTag(2, "containsClass qualifiedtypename=\""+allClasses[i].qualifiedTypeName()+"\"");
453 }
454 }
455
456 printCloseTag(1, "packagedoc");
457 }
458
459 protected void outputClassDoc(ClassDoc classDoc) throws IOException {
460
461 currentClass = classDoc;
462
463 println();
464 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()+"\"");
465
466 ClassDoc[] interfaces = classDoc.interfaces();
467 for (int i=0, ilim=interfaces.length; i<ilim; ++i) {
468 outputType(2, "implements", interfaces[i]);
469 }
470 outputSuperInterfacesRec(2, classDoc.superclass());
471
472 outputProgramElementDocBody(2, classDoc);
473 if (classDoc.isAbstract())
474 printAtomTag(2, "isAbstract");
475 if (classDoc.isSerializable())
476 printAtomTag(2, "isSerializable");
477 if (classDoc.isExternalizable())
478 printAtomTag(2, "isExternalizable");
479 if (classDoc.definesSerializableFields()) {
480 printAtomTag(2, "definesSerializableFields");
481 }
482
483 ConstructorDoc[] constructors = classDoc.constructors();
484 for (int i=0, ilim=constructors.length; i<ilim; ++i) {
485 outputConstructorDoc(2, constructors[i]);
486 }
487
488 MethodDoc[] methods = classDoc.methods();
489 for (int i=0, ilim=methods.length; i<ilim; ++i) {
490 outputMethodDoc(2, methods[i]);
491 }
492
493 FieldDoc[] fields = classDoc.fields();
494 for (int i=0, ilim=fields.length; i<ilim; ++i) {
495 outputFieldDoc(2, fields[i]);
496 }
497
498 if (classDoc.serializableFields().length > 0) {
499 printOpenTag(2, "serializableFields");
500
501 FieldDoc[] sfields = classDoc.serializableFields();
502 for (int i=0, ilim=sfields.length; i<ilim; ++i) {
503 outputFieldDoc(2, sfields[i]);
504 }
505 printCloseTag(2, "serializableFields");
506 }
507
508 Java2xhtml java2xhtml = new Java2xhtml();
509 Properties properties = new Properties();
510 properties.setProperty("isCodeSnippet", "true");
511 properties.setProperty("hasLineNumbers", "true");
512 java2xhtml.setProperties(properties);
513
514 if (null == classDoc.containingClass() && docTransletOptions.linksource) {
515 printOpenTag(2, "source");
516 StringWriter sourceBuffer = new StringWriter();
517 File sourceFile = new File(((GjdocPackageDoc)classDoc.containingPackage()).packageDirectory(),
518 classDoc.name() + ".java");
519 FileReader sourceReader = new FileReader(sourceFile);
520 IOToolkit.copyStream(sourceReader, sourceBuffer);
521 print(java2xhtml.makeHTML(sourceBuffer.getBuffer(), sourceFile.getName()));
522 printCloseTag(2, "source");
523 }
524
525 ClassDoc superclassDoc = classDoc.superclass();
526 while (superclassDoc != null) {
527 outputType(2, "superclass", superclassDoc, false);
528
529 // FIXME: remove the following after adjusting the XSLT sheets:
530 printAtomTag(3, "containingPackage name=\"" + superclassDoc.containingPackage().name() + "\"");
531
532 MethodDoc[] superMethods = superclassDoc.methods();
533 if (null != superMethods) {
534 for (int i=0, ilim=superMethods.length; i<ilim; ++i) {
535 printAtomTag(3, "methoddoc name=\"" + superMethods[i].name() + "\" signature=\"" + superMethods[i].signature() + "\"");
536 }
537 }
538
539 FieldDoc[] superFields = superclassDoc.fields();
540 if (null != superFields) {
541 for (int i=0, ilim=superFields.length; i<ilim; ++i) {
542 printAtomTag(3, "fielddoc name=\"" + superFields[i].name() + "\"");
543 }
544 }
545 printCloseTag(2, "superclass");
546
547 superclassDoc = superclassDoc.superclass();
548 }
549
550 outputUsage(classDoc, 2);
551
552 printCloseTag(1, "classdoc");
553
554 currentClass = null;
555 currentMember = null;
556 currentExecMember = null;
557 }
558
559 protected int outputHeritageOpen(int level, ClassDoc classDoc) {
560
561 ClassDoc superClassDoc = classDoc.superclass();
562 if (null != superClassDoc) {
563 level = outputHeritageOpen(level, superClassDoc);
564 ++ level;
565 }
566 outputType(level, "heritage", classDoc, false);
567 return level;
568 }
569
570 protected void outputHeritageClose(int level, ClassDoc classDoc) {
571
572 ClassDoc superClassDoc = classDoc.superclass();
573 if (null != superClassDoc) {
574 outputHeritageClose(level + 1, superClassDoc);
575 }
576 printCloseTag(level, "heritage");
577 }
578
579 protected void outputDocBody(int level, Doc doc) {
580
581 int context = CONTEXT_TYPE;
582
583 if (doc.isClass()) {
584 printAtomTag(level, "isClass");
585
586 ClassDoc classDoc = (ClassDoc)doc;
587 ClassDoc[] classes = rootDoc.classes();
588 for (int i=0, ilim=classes.length; i<ilim; ++i) {
589 if (classes[i].superclass() == classDoc) {
590 outputType(level, "extended-by", classes[i]);
591 }
592 }
593
594 outputHeritageOpen(level, classDoc);
595 outputHeritageClose(level, classDoc);
596 }
597 if (doc.isConstructor()) {
598 printAtomTag(level, "isConstructor");
599 context = CONTEXT_CONSTRUCTOR;
600 }
601 if (doc.isError()) {
602 printAtomTag(level, "isError");
603 }
604 if (doc.isException()) {
605 printAtomTag(level, "isException");
606 }
607 if (doc.isField()) {
608 printAtomTag(level, "isField");
609 context = CONTEXT_FIELD;
610 }
611 if (doc.isIncluded()) {
612 printAtomTag(level, "isIncluded");
613 }
614 if (doc.isInterface()) {
615 printAtomTag(level, "isInterface");
616
617 ClassDoc classDoc = (ClassDoc)doc;
618 ClassDoc[] classes = rootDoc.classes();
619 for (int i=0, ilim=classes.length; i<ilim; ++i) {
620 ClassDoc[] implementedInterfaces = classes[i].interfaces();
621 for (int j=0; j<implementedInterfaces.length; ++j) {
622 if (implementedInterfaces[j] == classDoc) {
623 if (classDoc.isInterface()) {
624 outputType(level, "subinterface", classes[i]);
625 }
626 else {
627 outputType(level, "implemented-by", classes[i]);
628 }
629 break;
630 }
631 }
632 }
633 }
634 if (doc.isMethod()) {
635 printAtomTag(level, "isMethod");
636 context = CONTEXT_METHOD;
637 }
638 if (doc.isOrdinaryClass()) {
639 printAtomTag(level, "isOrdinaryClass");
640 }
641
642 if (doc.inlineTags().length > 0) {
643 printOpenTag(level, "inlineTags", false);
644 outputTags(level+1, doc.inlineTags(), true, context);
645 printCloseTag(0, "inlineTags");
646 }
647
648 if (doc.firstSentenceTags().length > 0) {
649 printOpenTag(level, "firstSentenceTags", false);
650 outputTags(level+1, doc.firstSentenceTags(), true, context);
651 printCloseTag(0, "firstSentenceTags");
652 }
653
654 if (doc.tags().length > 0) {
655 printOpenTag(level, "tags");
656 outputTaglets(level+1, doc.tags(), true, context);
657 printCloseTag(level, "tags");
658 }
659
660 if (doc.seeTags().length > 0) {
661 printOpenTag(level, "seeTags");
662 outputTags(level+1, doc.seeTags(), true, context);
663 printCloseTag(level, "seeTags");
664 }
665
666 SourcePosition position = doc.position();
667 if (null != position) {
668 printAtomTag(level, "position file=\"" + position.file().getAbsolutePath() + "\" line=\"" + position.line() + "\" column=\"" + position.column() + "\"");
669 }
670 }
671
672 protected void outputProgramElementDocBody(int level, ProgramElementDoc programElementDoc) {
673 outputDocBody(level, programElementDoc);
674 printAtomTag(level, "containingPackage name=\""+programElementDoc.containingPackage().name()+"\"");
675 if (null!=programElementDoc.containingClass()) {
676 outputType(level, "containingClass", programElementDoc.containingClass());
677 }
678 String access;
679 if (programElementDoc.isPublic())
680 access="public";
681 else if (programElementDoc.isProtected())
682 access="protected";
683 else if (programElementDoc.isPrivate())
684 access="private";
685 else if (programElementDoc.isPackagePrivate())
686 access="package";
687 else
688 throw new RuntimeException("Huh? "+programElementDoc+" is neither public, protected, private nor package protected.");
689 printAtomTag(level, "access scope=\""+access+"\"");
690 if (programElementDoc.isFinal())
691 printAtomTag(level, "isFinal");
692 if (programElementDoc.isStatic())
693 printAtomTag(level, "isStatic");
694 }
695
696 protected void outputTags(int level, Tag[] tags, boolean descend, int context) {
697
698 for (int i=0; i<tags.length; ++i) {
699 outputTag(tags[i], level, descend, context, i == tags.length-1);
700 }
701 }
702
703 protected void outputTag(Tag tag, int level, boolean descend, int context, boolean lastTag) {
704
705 if (!"Text".equals(tag.name())) {
706 printOpenTag(0 /* don't introduce additional whitespace */,
707 "tag kind=\""+tag.kind()+"\" name=\""+tag.name()+"\"", false);
708 }
709 if (tag instanceof ThrowsTag) {
710 ThrowsTag throwsTag = (ThrowsTag)tag;
711 if (null!=throwsTag.exception()) {
712 outputType(level+1, "exception", throwsTag.exception());
713 }
714 else {
715 StringBuffer sb = new StringBuffer("Exception ");
716 sb.append(throwsTag.exceptionName());
717 sb.append(" not found in ");
718 if (currentExecMember instanceof MethodDoc) {
719 MethodDoc m = (MethodDoc)currentExecMember;
720 sb.append(m.returnType().typeName());
721 sb.append(m.returnType().dimension());
722 sb.append(' ');
723 }
724 sb.append(currentClass.qualifiedName());
725 sb.append('.');
726 sb.append(currentExecMember.name());
727 sb.append('(');
728 Parameter[] params = currentExecMember.parameters();
729 for (int j=0; j < params.length; j++) {
730 sb.append(params[j].type().typeName());
731 sb.append(params[j].type().dimension());
732 sb.append(' ');
733 sb.append(params[j].name());
734 if (j != params.length-1)
735 sb.append(", ");
736 }
737 sb.append(')');
738 printWarning(sb.toString());
739
740 printAtomTag(level+1, "exception typename=\""+throwsTag.exceptionName()+"\"");
741 }
742 }
743 else if (tag instanceof ParamTag) {
744 ParamTag paramTag = (ParamTag)tag;
745 printAtomTag(level+1, "parameter name=\""+paramTag.parameterName()+"\"");
746 }
747
748 if (null != tag.text()) {
749 //printOpenTag(level+1, "text", false);
750 if (fixHTML) {
751 print(htmlRepairer.getWellformedHTML(tag.text()));
752 }
753 else {
754 print("<![CDATA["+cdata(tag.text())+"]]>");
755 }
756 //printCloseTag(0 /* don't introduce additional whitespace */, "text");
757 }
758 else {
759 printWarning("Tag got null text: "+tag);
760 }
761
762 if ((descend && ("@throws".equals(tag.name()) || "@param".equals(tag.name()))) || "@deprecated".equals(tag.name())) {
763 if (tag.firstSentenceTags().length>0) {
764 printOpenTag(level+1, "firstSentenceTags", false);
765 outputTags(level+2, tag.firstSentenceTags(), false, context);
766 printCloseTag(0, "firstSentenceTags");
767 }
768
769 if (tag.inlineTags().length>0) {
770 printOpenTag(level+1, "inlineTags", false);
771 outputTags(level+2, tag.firstSentenceTags(), false, context);
772 printCloseTag(0, "inlineTags");
773 }
774 }
775
776 if (fixHTML && lastTag) {
777 String terminateText = htmlRepairer.terminateText();
778 if (null != terminateText && terminateText.length() > 0) {
779 print(terminateText);
780 }
781 }
782
783 if (!"Text".equals(tag.name())) {
784
785 Taglet inlineTaglet = (Taglet)tagletMap.get(tag.name().substring(1));
786 if (null != inlineTaglet && inlineTaglet.isInlineTag()) {
787 printOpenTag(0, "inlineTagletText", false);
788 print(inlineTaglet.toString(tag));
789 printCloseTag(0, "inlineTagletText");
790 }
791
792 printCloseTag(0, "tag", false);
793 }
794 }
795
796 void outputTaglets(int level, Tag[] tags, boolean descend, int context)
797 {
798 for (Iterator it = tagletMap.keySet().iterator(); it.hasNext(); ) {
799 String tagName = (String)it.next();
800 Object o = tagletMap.get(tagName);
801 Taglet taglet = (Taglet)o;
802
803 if (!taglet.isInlineTag()
804 && ((context != CONTEXT_CONSTRUCTOR || taglet.inConstructor())
805 || (context != CONTEXT_FIELD || taglet.inField())
806 || (context != CONTEXT_METHOD || taglet.inMethod())
807 || (context != CONTEXT_OVERVIEW || taglet.inOverview())
808 || (context != CONTEXT_PACKAGE || taglet.inPackage())
809 || (context != CONTEXT_TYPE || taglet.inType()))) {
810
811 List tagsOfThisType = new ArrayList();
812 for (int i=0, ilim=tags.length; i<ilim; ++i) {
813 if (tags[i].name().substring(1).equals(tagName)) {
814 tagsOfThisType.add(tags[i]);
815 }
816 }
817
818 if (!tagsOfThisType.isEmpty()) {
819 Tag[] tagletTags = (Tag[])tagsOfThisType.toArray(new Tag[tagsOfThisType.size()]);
820 if (taglet instanceof StandardTaglet) {
821 Iterator tagIterator = tagsOfThisType.iterator();
822 while (tagIterator.hasNext()) {
823 Tag tag = (Tag)tagIterator.next();
824 outputTag(tag, level, descend, context, !tagIterator.hasNext());
825 }
826 }
827 else {
828 String tagletString = taglet.toString(tagletTags);
829 if (null != tagletString) {
830 printOpenTag(0, "tag name=\"" + tagName + "\" taglet-generated=\"true\"");
831 if (fixHTML) {
832 print(htmlRepairer.getWellformedHTML(tagletString));
833 print(htmlRepairer.terminateText());
834 }
835 else {
836 print("<![CDATA["+cdata(tagletString)+"]]>");
837 }
838 printCloseTag(0, "tag", false);
839 }
840 }
841 }
842 }
843 }
844 }
845
846 /**
847 * Inofficial entry point. We got an instance here.
848 */
849 protected boolean instanceStart(RootDoc _rootDoc) {
850
851 this.rootDoc = _rootDoc;
852 _rootDoc = null;
853
854 boolean xmlOnly = true;
855
856 // Set the default Taglet order
857
858 registerTaglet(new VersionTaglet());
859 registerTaglet(new AuthorTaglet());
860 //registerTaglet(new SinceTaglet());
861 registerTaglet(new StandardTaglet("deprecated"));
862 registerTaglet(new StandardTaglet("see"));
863 registerTaglet(new StandardTaglet("param"));
864
865 // Set the built-in Taglet filter
866
867 AuthorTaglet.setTagletEnabled(false);
868 VersionTaglet.setTagletEnabled(false);
869 SinceTaglet.setTagletEnabled(true);
870 DeprecatedTaglet.setTagletEnabled(true);
871
872 try {
873 {
874
875 // Process command line options passed through to this doclet
876
877 TargetContext targetContext = null;
878
879 TargetContext htmlTargetContext
880 = new TargetContext(DocTranslet.fromClasspath("/doctranslets/html/gjdoc.xsl"),
881 targetDirectory);
882
883 for (int i=0, ilim=rootDoc.options().length; i<ilim; ++i) {
884
885 String[] option = rootDoc.options()[i];
886 String optionTag = option[0];
887
888 if ("-d".equals(optionTag)) {
889 if (null == targetDirectory) {
890 targetDirectory = new File(option[1]);
891 }
892 if (null != targetContext) {
893 targetContext.setTargetDirectory(targetDirectory);
894 }
895 }
896
897 else if ("-nofixhtml".equals(optionTag)) {
898 fixHTML = false;
899 printError("-nofixhtml currently not supported.");
900 return false;
901 }
902 else if ("-compress".equals(optionTag)) {
903 compress = true;
904 }
905 else if ("-nohtmlwarn".equals(optionTag)) {
906 noHTMLWarn = true;
907 }
908 else if ("-noemailwarn".equals(optionTag)) {
909 noEmailWarn = true;
910 }
911 else if ("-indentstep".equals(optionTag)) {
912 indentStep = Integer.parseInt(option[1]);
913 }
914 else if ("-doctranslet".equals(optionTag)) {
915 targets.add(targetContext = new TargetContext(DocTranslet.fromJarFile(new File(option[1])),
916 targetDirectory));
917 }
918 else if ("-genhtml".equals(optionTag)) {
919 htmlTargetContext.setTargetDirectory(targetDirectory);
920 targets.add(targetContext = htmlTargetContext);
921 xmlOnly = false;
922 }
923 else if ("-geninfo".equals(optionTag)) {
924 targetContext
925 = new TargetContext(DocTranslet.fromClasspath("/doctranslets/info/gengj.xsl"),
926 targetDirectory);
927 targets.add(targetContext);
928 if (!fixHTML) {
929 printNotice("NOTE: -geninfo implies -fixhtml.");
930 fixHTML = true;
931 }
932 xmlOnly = false;
933 }
934 else if ("-gendocbook".equals(optionTag)) {
935 targetContext = new TargetContext(DocTranslet.fromClasspath("/doctranslets/docbook/gengj.xsl"),
936 targetDirectory);
937 targets.add(targetContext);
938 if (!fixHTML) {
939 printNotice("NOTE: -gendocbook implies -fixhtml.");
940 fixHTML = true;
941 }
942 }
943 else if ("-genpdf".equals(optionTag)) {
944 targetContext
945 = new TargetContext(DocTranslet.fromClasspath("/doctranslets/docbook/gengj.xsl"),
946 targetDirectory);
947 /** "gnu.classpath.tools.doclets.xmldoclet.DocBookPostprocessor") **/
948 targets.add(targetContext);
949 if (!fixHTML) {
950 printNotice("NOTE: -genpdf implies -fixhtml.");
951 fixHTML = true;
952 }
953 }
954 else if ("-xmlonly".equals(optionTag)) {
955 xmlOnly = true;
956 }
957 else if ("-bottomnote".equals(optionTag)) {
958
959 FileReader reader = new FileReader(option[1]);
960 StringWriter writer = new StringWriter();
961 char[] buf = new char[256];
962 int nread;
963 while ((nread = reader.read(buf)) >= 0) {
964 writer.write(buf, 0, nread);
965 }
966 writer.flush();
967 bottomNote = writer.toString();
968 writer.close();
969 reader.close();
970 }
971 else if ("-title".equals(optionTag)) {
972
973 title = option[1];
974 }
975 else if ("-workpath".equals(optionTag)) {
976
977 workingPath = option[1];
978 }
979 else if ("-tagletpath".equals(optionTag)) {
980
981 if (null == tagletPath) {
982 tagletPath = option[1];
983 }
984 else {
985 tagletPath = tagletPath + File.pathSeparator + option[1];
986 }
987 }
988 else if ("-taglet".equals(optionTag)) {
989
990 boolean tagletLoaded = false;
991
992 String useTagletPath = this.tagletPath;
993 if (null == useTagletPath) {
994 useTagletPath = System.getProperty("java.class.path");
995 }
996
997 try {
998 Class tagletClass;
999 try {
1000 tagletClass
1001 = new FileSystemClassLoader(useTagletPath).loadClass(option[1]);
1002 }
1003 catch (ClassNotFoundException e) {
1004 // If not found on specified tagletpath, try default classloader
1005 tagletClass
1006 = Class.forName(option[1]);
1007 }
1008 Method registerTagletMethod
1009 = tagletClass.getDeclaredMethod("register", new Class[] { java.util.Map.class });
1010
1011 if (!registerTagletMethod.getReturnType().equals(Void.TYPE)) {
1012 printError("Taglet class '" + option[1] + "' found, but register method doesn't return void.");
1013 }
1014 else if (registerTagletMethod.getExceptionTypes().length > 0) {
1015 printError("Taglet class '" + option[1] + "' found, but register method contains throws clause.");
1016 }
1017 else if ((registerTagletMethod.getModifiers() & (Modifier.STATIC | Modifier.PUBLIC | Modifier.ABSTRACT)) != (Modifier.STATIC | Modifier.PUBLIC)) {
1018 printError("Taglet class '" + option[1] + "' found, but register method isn't public static, or is abstract..");
1019 }
1020 else {
1021 Map tempMap = new HashMap();
1022 registerTagletMethod.invoke(null, new Object[] { tempMap });
1023 tagletLoaded = true;
1024 String name = (String)tempMap.keySet().iterator().next();
1025 Taglet taglet = (Taglet)tempMap.get(name);
1026 tagletMap.put(name, taglet);
1027 mentionedTags.add(taglet);
1028 }
1029 }
1030 catch (NoSuchMethodException e) {
1031 printError("Taglet class '" + option[1] + "' found, but doesn't contain the register method.");
1032 }
1033 catch (SecurityException e) {
1034 printError("Taglet class '" + option[1] + "' cannot be loaded: " + e.getMessage());
1035 }
1036 catch (InvocationTargetException e) {
1037 printError("Taglet class '" + option[1] + "' found, but register method throws exception: " + e.toString());
1038 }
1039 catch (IllegalAccessException e) {
1040 printError("Taglet class '" + option[1] + "' found, but there was a problem when accessing the register method: " + e.toString());
1041 }
1042 catch (IllegalArgumentException e) {
1043 printError("Taglet class '" + option[1] + "' found, but there was a problem when accessing the register method: " + e.toString());
1044 }
1045 catch (ClassNotFoundException e) {
1046 printError("Taglet class '" + option[1] + "' cannot be found.");
1047 }
1048 if (!tagletLoaded) {
1049 return false;
1050 }
1051 }
1052 else if ("-author".equals(optionTag)) {
1053 AuthorTaglet.setTagletEnabled(true);
1054 }
1055 else if ("-version".equals(optionTag)) {
1056 VersionTaglet.setTagletEnabled(true);
1057 }
1058 else if ("-nosince".equals(optionTag)) {
1059 SinceTaglet.setTagletEnabled(false);
1060 }
1061 else if ("-nodeprecated".equals(optionTag)) {
1062 DeprecatedTaglet.setTagletEnabled(false);
1063 }
1064 else if ("-authormail".equals(optionTag)) {
1065
1066 if ("no-replace".equalsIgnoreCase(option[1])) {
1067 AuthorTaglet.setEmailReplacementType(AuthorTaglet.EmailReplacement.NO_REPLACEMENT);
1068 }
1069 else if ("mailto-name".equalsIgnoreCase(option[1])) {
1070 AuthorTaglet.setEmailReplacementType(AuthorTaglet.EmailReplacement.MAILTO_NAME);
1071 }
1072 else if ("name-mailto-address".equalsIgnoreCase(option[1])) {
1073 AuthorTaglet.setEmailReplacementType(AuthorTaglet.EmailReplacement.NAME_MAILTO_ADDRESS);
1074 }
1075 else if ("name-mangled-address".equalsIgnoreCase(option[1])) {
1076 AuthorTaglet.setEmailReplacementType(AuthorTaglet.EmailReplacement.NAME_MANGLED_ADDRESS);
1077 }
1078 else {
1079 printError("Invalid value for option '-authortag-email'. Allowed values are:"
1080 + " no-replace, mailto-name, name-mailto-address, name-mangled-address.");
1081 return false;
1082 }
1083 }
1084 else if ("-mailmangledot".equals(optionTag)) {
1085 AuthorTaglet.setDotReplacement(option[1]);
1086 }
1087 else if ("-mailmangleat".equals(optionTag)) {
1088 AuthorTaglet.setAtReplacement(option[1]);
1089 }
1090 else if ("-docfilessubdirs".equals(optionTag)) {
1091 docFilesSubdirsEnabled = true;
1092 }
1093 else if ("-excludedocfilessubdir".equals(optionTag)) {
1094 StringTokenizer st = new StringTokenizer(option[1]);
1095 while (st.hasMoreTokens()) {
1096 excludeDocFilesSubDirs.add(st.nextToken());
1097 }
1098 }
1099 else if ("-nonavbar".equals(optionTag)) {
1100 docTransletOptions.nonavbar = true;
1101 }
1102 else if ("-noindex".equals(optionTag)) {
1103 docTransletOptions.noindex = true;
1104 }
1105 else if ("-notree".equals(optionTag)) {
1106 docTransletOptions.notree = true;
1107 }
1108 else if ("-nocomment".equals(optionTag)) {
1109 docTransletOptions.nocomment = true;
1110 }
1111 else if ("-nohelp".equals(optionTag)) {
1112 docTransletOptions.nohelp = true;
1113 }
1114 else if ("-splitindex".equals(optionTag)) {
1115 docTransletOptions.splitindex = true;
1116 }
1117 else if ("-linksource".equals(optionTag)) {
1118 docTransletOptions.linksource = true;
1119 }
1120 else if ("-windowtitle".equals(optionTag)) {
1121 docTransletOptions.windowtitle = option[1];
1122 }
1123 else if ("-helpfile".equals(optionTag)) {
1124 docTransletOptions.helpfile = new File(option[1]).toURL().toString();
1125 }
1126 else if ("-stylesheetfile".equals(optionTag)) {
1127 docTransletOptions.stylesheetfile = new File(option[1]).toURL().toString();
1128 }
1129 else if ("-header".equals(optionTag)) {
1130 docTransletOptions.header = option[1];
1131 }
1132 else if ("-footer".equals(optionTag)) {
1133 docTransletOptions.footer = option[1];
1134 }
1135 else if ("-bottom".equals(optionTag)) {
1136 docTransletOptions.bottom = option[1];
1137 }
1138 else if ("-doctitle".equals(optionTag)) {
1139 docTransletOptions.doctitle = option[1];
1140 }
1141 else if ("-nodeprecatedlist".equals(optionTag)) {
1142 docTransletOptions.nodeprecatedlist = true;
1143 }
1144 else if ("-uses".equals(optionTag)) {
1145 docTransletOptions.uses = true;
1146 }
1147 else if ("-group".equals(optionTag)) {
1148 if (!processGroupOption(option[1], option[2])) {
1149 printError("Invalid package wildcard list in -group option \"" + option[1] + "\" " + option[2]);
1150 return false;
1151 }
1152 }
1153 else if ("-tag".equals(optionTag)) {
1154 String tagSpec = option[1];
1155 boolean validTagSpec = false;
1156 int ndx1 = tagSpec.indexOf(':');
1157 if (ndx1 < 0) {
1158 Taglet taglet = (Taglet)tagletMap.get(tagSpec);
1159 if (null == taglet) {
1160 printError("There is no standard tag '" + tagSpec + "'.");
1161 }
1162 else {
1163 if (mentionedTags.contains(taglet)) {
1164 printError("Tag '" + tagSpec + "' has been added or moved before.");
1165 }
1166 else {
1167 mentionedTags.add(taglet);
1168
1169 // re-append taglet
1170 tagletMap.remove(tagSpec);
1171 tagletMap.put(tagSpec, taglet);
1172 }
1173 }
1174 }
1175 else {
1176 int ndx2 = tagSpec.indexOf(':', ndx1 + 1);
1177 if (ndx2 > ndx1 && ndx2 < tagSpec.length() - 1) {
1178 String tagName = tagSpec.substring(0, ndx1);
1179 String tagHead = null;
1180 if (tagSpec.charAt(ndx2 + 1) == '\"') {
1181 if (tagSpec.charAt(tagSpec.length() - 1) == '\"') {
1182 tagHead = tagSpec.substring(ndx2 + 2, tagSpec.length() - 1);
1183 validTagSpec = true;
1184 }
1185 }
1186 else {
1187 tagHead = tagSpec.substring(ndx2 + 1);
1188 validTagSpec = true;
1189 }
1190
1191 boolean tagScopeOverview = false;
1192 boolean tagScopePackages = false;
1193 boolean tagScopeTypes = false;
1194 boolean tagScopeConstructors = false;
1195 boolean tagScopeMethods = false;
1196 boolean tagScopeFields = false;
1197 boolean tagDisabled = false;
1198
1199 tag_option_loop:
1200 for (int n=ndx1+1; n<ndx2; ++n) {
1201 switch (tagSpec.charAt(n)) {
1202 case 'X':
1203 tagDisabled = true;
1204 break;
1205 case 'a':
1206 tagScopeOverview = true;
1207 tagScopePackages = true;
1208 tagScopeTypes = true;
1209 tagScopeConstructors = true;
1210 tagScopeMethods = true;
1211 tagScopeFields = true;
1212 break;
1213 case 'o':
1214 tagScopeOverview = true;
1215 break;
1216 case 'p':
1217 tagScopePackages = true;
1218 break;
1219 case 't':
1220 tagScopeTypes = true;
1221 break;
1222 case 'c':
1223 tagScopeConstructors = true;
1224 break;
1225 case 'm':
1226 tagScopeMethods = true;
1227 break;
1228 case 'f':
1229 tagScopeFields = true;
1230 break;
1231 default:
1232 validTagSpec = false;
1233 break tag_option_loop;
1234 }
1235 }
1236
1237 if (validTagSpec) {
1238 GenericTaglet taglet
1239 = new GenericTaglet(tagName,
1240 tagHead,
1241 tagScopeOverview,
1242 tagScopePackages,
1243 tagScopeTypes,
1244 tagScopeConstructors,
1245 tagScopeMethods,
1246 tagScopeFields);
1247 taglet.setTagletEnabled(!tagDisabled);
1248 taglet.register(tagletMap);
1249 mentionedTags.add(taglet);
1250 }
1251 }
1252 }
1253 if (!validTagSpec) {
1254 printError("Value for option -tag must be in format \"<tagname>:Xaoptcmf:<taghead>\".");
1255 }
1256 }
1257 }
1258
1259 // Use current directory if target directory hasn't been set.
1260 if (null == targetDirectory) {
1261 targetDirectory = new File(System.getProperty("user.dir"));
1262 }
1263 if (null != targetContext) {
1264 targetContext.setTargetDirectory(targetDirectory);
1265 }
1266
1267 // It is illegal to specify targets AND -xmlonly.
1268
1269 if (xmlOnly && targets.size() > 0) {
1270
1271 printError("You can only specify one of -xmlonly and a target format.");
1272 return false;
1273 }
1274
1275 // If no target was specified and XML only was not
1276 // requested, use HTML as default target.
1277
1278 if (!xmlOnly && targets.size() == 0) {
1279 targets.add(targetContext = htmlTargetContext);
1280 }
1281
1282 // Set the same target directory for all output.
1283
1284 // FIXME: Allow separate target directories for different
1285 // output formats.
1286
1287 for (Iterator it = targets.iterator(); it.hasNext(); ) {
1288 TargetContext t = (TargetContext)it.next();
1289 t.setTargetDirectory(targetDirectory);
1290 }
1291
1292 // Create temporary directory if necessary
1293
1294 if (xmlOnly) {
1295
1296 xmlTargetDirectory = targetDirectory;
1297 }
1298 else {
1299
1300 File workingTopDirectory = new File(workingPath);
1301
1302 workingDirectory = new File(workingTopDirectory, "gjdoc.tmp."+System.currentTimeMillis());
1303
1304 if (!workingDirectory.mkdir()) {
1305 printError("Cannot create temporary directory at "+System.getProperty("java.io.tmpdir"));
1306 return false;
1307 }
1308
1309 File xmlTempDirectory = new File(workingDirectory, "xmloutput");
1310
1311 if (!xmlTempDirectory.mkdir()) {
1312 printError("Cannot create temporary directory for XML output at "+System.getProperty("java.io.tmpdir"));
1313 return false;
1314 }
1315
1316 xmlTargetDirectory = xmlTempDirectory;
1317 }
1318
1319 // Create target directory if necessary
1320
1321 if (!targetDirectory.exists()) {
1322 printNotice("Creating destination directory: \""
1323 + targetDirectory + "\"");
1324 if (!targetDirectory.mkdirs()) {
1325 printError("Failed to create destination directory \""
1326 + targetDirectory + "\"");
1327 return false;
1328 }
1329 }
1330
1331 // Check for deprecation
1332
1333 boolean hasDeprecatedClasses = false;
1334 boolean hasDeprecatedInterfaces = false;
1335 boolean hasDeprecatedExceptions = false;
1336 boolean hasDeprecatedErrors = false;
1337 boolean hasDeprecatedMethods = false;
1338 boolean hasDeprecatedFields = false;
1339
1340 {
1341 ClassDoc[] classes = rootDoc.classes();
1342 for (int i = 0, ilim = classes.length; i < ilim; ++ i) {
1343 ClassDoc c = classes[i];
1344 Tag[] deprecatedTags = c.tags("deprecated");
1345 if (null != deprecatedTags && 0 != deprecatedTags.length) {
1346 if (c.isInterface()) {
1347 hasDeprecatedInterfaces = true;
1348 }
1349 else if (c.isException()) {
1350 hasDeprecatedExceptions = true;
1351 }
1352 else if (c.isError()) {
1353 hasDeprecatedErrors = true;
1354 }
1355 else /*if (c.isOrdinaryClass())*/ {
1356 hasDeprecatedClasses = true;
1357 }
1358 }
1359
1360 MethodDoc[] methods = c.methods();
1361 for (int j = 0, jlim = methods.length; j < jlim; ++ j) {
1362 MethodDoc m = methods[j];
1363 deprecatedTags = m.tags("deprecated");
1364 if (null != deprecatedTags && 0 != deprecatedTags.length) {
1365 hasDeprecatedMethods = true;
1366 }
1367 }
1368
1369 FieldDoc[] fields = c.fields();
1370 for (int j = 0, jlim = fields.length; j < jlim; ++ j) {
1371 FieldDoc f = fields[j];
1372 deprecatedTags = f.tags("deprecated");
1373 if (null != deprecatedTags && 0 != deprecatedTags.length) {
1374 hasDeprecatedFields = true;
1375 }
1376 }
1377 }
1378 }
1379
1380 htmlRepairer = new HtmlRepairer(rootDoc, noHTMLWarn, noEmailWarn,
1381 currentClass, currentMember,
1382 false);
1383
1384 collectUsage();
1385
1386 // Begin XML generation
1387
1388 printNotice("Writing XML Index file...");
1389
1390 // Assign output stream
1391
1392 setTargetFile("index.xml");
1393
1394 // Output XML document header
1395
1396 println(0, "<?xml version=\"1.0\"?>");
1397 println("<!DOCTYPE gjdoc SYSTEM \"dtd/gjdoc.dtd\">");
1398 println();
1399 printOpenTag(0, "rootdoc xmlns=\"http://www.w3.org/TR/REC-html40\" xmlns:gjdoc=\"http://www.gnu.org/software/cp-tools/gjdocxml\"");
1400
1401 println();
1402 println(1, "<!-- Tags from overview page, if available -->");
1403
1404 if (rootDoc.firstSentenceTags().length > 0) {
1405 printOpenTag(2, "firstSentenceTags", false);
1406 outputTags(3, rootDoc.firstSentenceTags(), true, CONTEXT_PACKAGE);
1407 printCloseTag(0, "firstSentenceTags");
1408 }
1409
1410 if (rootDoc.inlineTags().length > 0) {
1411 printOpenTag(2, "inlineTags");
1412 outputTags(3, rootDoc.inlineTags(), true, CONTEXT_PACKAGE);
1413 printCloseTag(2, "inlineTags");
1414 }
1415
1416 if (null != bottomNote) {
1417 printOpenTag(1, "bottomnote");
1418 print(bottomNote);
1419 printCloseTag(1, "bottomnote");
1420 }
1421
1422 if (null != title) {
1423 printOpenTag(1, "title");
1424 println(2, title);
1425 printCloseTag(1, "title");
1426 }
1427
1428 printOpenTag(1, "created");
1429 println(2, DateFormat.getDateInstance(DateFormat.LONG, Locale.US).format(new java.util.Date()));
1430 printCloseTag(1, "created");
1431
1432 if (hasDeprecatedClasses) printAtomTag(1, "hasDeprecatedClasses");
1433 if (hasDeprecatedInterfaces) printAtomTag(1, "hasDeprecatedInterfaces");
1434 if (hasDeprecatedExceptions) printAtomTag(1, "hasDeprecatedExceptions");
1435 if (hasDeprecatedErrors) printAtomTag(1, "hasDeprecatedErrors");
1436 if (hasDeprecatedMethods) printAtomTag(1, "hasDeprecatedMethods");
1437 if (hasDeprecatedFields) printAtomTag(1, "hasDeprecatedFields");
1438
1439 // Output summary of all classes specified on command line
1440
1441 println();
1442 println(1, "<!-- Classes specified by user on command line -->");
1443 ClassDoc[] specifiedClasses = rootDoc.specifiedClasses();
1444 for (int i=0, ilim=specifiedClasses.length; i<ilim; ++i) {
1445 ClassDoc sc = specifiedClasses[i];
1446 printAtomTag(1, "specifiedclass fqname=\""+sc.qualifiedName()+"\" name=\""+sc.name()+"\"");
1447 }
1448 specifiedClasses = null;
1449
1450 // Output summary of all packages specified on command line
1451
1452 println();
1453 println(1, "<!-- Packages specified by user on command line -->");
1454 PackageDoc[] specifiedPackages = rootDoc.specifiedPackages();
1455 for (int i=0, ilim=specifiedPackages.length; i<ilim; ++i) {
1456 PackageDoc sp = specifiedPackages[i];
1457 printAtomTag(1, "specifiedpackage name=\""+sp.name()+"\"");
1458 }
1459 specifiedPackages = null;
1460
1461 // Output package group information specified on the
1462 // command line
1463
1464 println();
1465 println(1, "<!-- Package groups specified by user on command line -->");
1466 {
1467 Iterator packageGroupIt = packageGroups.iterator();
1468 while (packageGroupIt.hasNext()) {
1469 PackageGroup packageGroup = (PackageGroup)packageGroupIt.next();
1470 SortedSet groupedPackages = packageGroup.getPackages();
1471 if (groupedPackages.isEmpty()) {
1472 printWarning("Package group named '"
1473 + packageGroup.getName() + "' didn't match any packages.");
1474 }
1475 else {
1476 printOpenTag(1, "packagegroup name=\"" + packageGroup.getName() + "\"");
1477 Iterator groupedPackageIt = groupedPackages.iterator();
1478 while (groupedPackageIt.hasNext()) {
1479 PackageDoc groupedPackageDoc = (PackageDoc)groupedPackageIt.next();
1480 printAtomTag(2, "package name=\"" + groupedPackageDoc.name() + "\"");
1481 }
1482 printCloseTag(1, "packagegroup");
1483 }
1484 }
1485 packageGroups = null;
1486 }
1487
1488 // Output information on all packages for which documentation
1489 // has been made available via the Doclet API
1490
1491 println();
1492 println(1, "<!-- Documentation for all packages -->");
1493 PackageDoc[] packages = rootDoc.specifiedPackages();
1494 for (int i=0, ilim=packages.length; i<ilim; ++i) {
1495 PackageDoc c = packages[i];
1496 outputPackageDoc(c);
1497 }
1498 packages = null;
1499
1500 // Output brief summary on all classes for which documentation
1501 // has been made available via the Doclet API.
1502 //
1503 // While this is redundant, it can speed up XSLT
1504 // processing by orders of magnitude
1505
1506 println();
1507 println(1, "<!-- Brief summary for all classes -->");
1508 ClassDoc[] sumclasses = rootDoc.classes();
1509 for (int i=0, ilim=sumclasses.length; i<ilim; ++i) {
1510 ClassDoc c = sumclasses[i];
1511 outputClassDocSummary(c);
1512 }
1513 sumclasses = null;
1514
1515 // Output closing tag, finish output stream
1516
1517 println();
1518 printCloseTag(0, "rootdoc");
1519
1520 closeTargetFile();
1521
1522 createIndexByName();
1523
1524
1525
1526 // Output information on all classes for which documentation
1527 // has been made available via the Doclet API
1528
1529 println();
1530 println(1, "<!-- Documentation for all classes -->");
1531 ClassDoc[] classes = rootDoc.classes();
1532 String prevPackageName = null;
1533 for (int i = 0, ilim = classes.length; i < ilim; ++ i) {
1534 ClassDoc c = classes[i];
1535
1536 if (isVerbose()) {
1537 printNotice("Writing XML information for "+c.qualifiedName()+"...");
1538 }
1539 else {
1540 String packageName = c.containingPackage().name();
1541 if (null == prevPackageName || !packageName.equals(prevPackageName)) {
1542 printNotice("Writing XML information for "+packageName+"...");
1543 prevPackageName = packageName;
1544 }
1545 }
1546
1547 setTargetFile(c.qualifiedName().replace('/','.')+".xml");
1548
1549 println("<?xml version=\"1.0\"?>");
1550 println("<!DOCTYPE gjdoc SYSTEM \"dtd/gjdoc.dtd\">");
1551
1552 outputClassDoc(c);
1553
1554 closeTargetFile();
1555 }
1556 classes = null;
1557 }
1558
1559 // Copy DTD files to temporary directory
1560
1561 // FIXME: try to solve this via jar: URLs. but this will
1562 // probably break libxmlj compatibility (?)
1563
1564 String[] resources = new String[] {
1565 "gjdoc.dtd",
1566 "gjdoc-alphaindex.dtd",
1567 "dbcentx.mod",
1568 "ent/iso-amsa.ent",
1569 "ent/iso-amsb.ent",
1570 "ent/iso-amsc.ent",
1571 "ent/iso-amsn.ent",
1572 "ent/iso-amso.ent",
1573 "ent/iso-amsr.ent",
1574 "ent/iso-box.ent",
1575 "ent/iso-cyr1.ent",
1576 "ent/iso-cyr2.ent",
1577 "ent/iso-dia.ent",
1578 "ent/iso-grk1.ent",
1579 "ent/iso-grk2.ent",
1580 "ent/iso-grk3.ent",
1581 "ent/iso-grk4.ent",
1582 "ent/iso-lat1.ent",
1583 "ent/iso-lat2.ent",
1584 "ent/iso-num.ent",
1585 "ent/iso-pub.ent",
1586 "ent/iso-tech.ent",
1587 };
1588
1589 File tempDtdDirectory = new File(xmlTargetDirectory, "dtd");
1590 File tempDtdEntDirectory = new File(tempDtdDirectory, "ent");
1591
1592 if ((tempDtdDirectory.exists() || tempDtdDirectory.mkdir())
1593 && (tempDtdEntDirectory.exists() || tempDtdEntDirectory.mkdir())) {
1594 for (int i = 0; i < resources.length; ++ i) {
1595 copyResourceToFile("/dtd/" + resources[i],
1596 new File(tempDtdDirectory, resources[i]));
1597 }
1598 }
1599 else {
1600 printError("Cannot create temporary directories for DTD data at " + tempDtdDirectory);
1601 return false;
1602 }
1603
1604 // Copy package data-dir directory
1605
1606 {
1607 PackageDoc[] packages = rootDoc.specifiedPackages();
1608 for (int i=0, ilim=packages.length; i<ilim; ++i) {
1609 PackageDoc c = packages[i];
1610 if (c instanceof GjdocPackageDoc) {
1611 copyPackageDataDir((GjdocPackageDoc)c);
1612 }
1613 }
1614 }
1615
1616 // All information has been output. Apply stylesheet if given.
1617
1618 gnu.classpath.tools.gjdoc.Main.releaseRootDoc();
1619
1620 this.currentClass = null;
1621 this.currentMember = null;
1622 this.currentExecMember = null;
1623
1624 System.gc();
1625
1626 // From this point we are only operating on files, so we don't
1627 // need this anymore and can free up some memory
1628
1629 for (Iterator it = targets.iterator(); it.hasNext(); ) {
1630
1631 TargetContext target = (TargetContext)it.next();
1632
1633 // We have XSLT postprocessing, run DocTranslet.
1634
1635 //DocTranslet docTranslet = DocTranslet.fromClasspath("/doctranslets/html/gjdoc.xsl");
1636
1637 //docTranslet.setOptions(docTransletOptions);
1638
1639 target.getDocTranslet().setOptions(docTransletOptions);
1640
1641 target.getDocTranslet().apply(xmlTargetDirectory,
1642 target.getTargetDirectory(),
1643 rootDoc);
1644 }
1645
1646 // Done
1647
1648 targets = null;
1649
1650 System.gc();
1651 Runtime.getRuntime().runFinalization();
1652
1653 return true;
1654 }
1655 catch (Exception e) {
1656
1657 // Something went wrong. Report to stderr and pass error to
1658 // Javadoc Reporter
1659
1660 e.printStackTrace();
1661 printError(e.toString());
1662
1663 Throwable rootCause = e.getCause();
1664 if (null != rootCause) {
1665 while (null != rootCause.getCause()) {
1666 rootCause = rootCause.getCause();
1667 }
1668 System.err.println("Root cause:");
1669 rootCause.printStackTrace();
1670 }
1671
1672 return false;
1673 }
1674 finally {
1675
1676 // In any case, delete the working directory if we created one
1677
1678 if (null != workingDirectory) {
1679
1680 if (!deleteRecursive(workingDirectory)) {
1681 printWarning("Could not delete temporary directory at "+workingDirectory);
1682 }
1683 }
1684
1685 printNotice("Done.");
1686 }
1687 }
1688
1689 /**
1690 * Recursively delete the specified directory and its contents,
1691 * like <code>rm -Rf directory</code>
1692 *
1693 * @return <code>true</code> on success
1694 */
1695 private static boolean deleteRecursive(File directory) {
1696
1697 boolean success = true;
1698
1699 File[] files = directory.listFiles();
1700
1701 for (int i=0, ilim=files.length; i<ilim; ++i) {
1702
1703 File file = files[i];
1704
1705 if (file.isDirectory()) {
1706
1707 success = deleteRecursive(file) && success;
1708 }
1709 else {
1710
1711 success = file.delete() && success;
1712 }
1713 }
1714
1715 return directory.delete() && success;
1716 }
1717
1718 /**
1719 * Prints a string to stdout and appends a newline. Convenience
1720 * method.
1721 */
1722 protected void println(String str) {
1723 out.println(str);
1724 }
1725
1726 /**
1727 * Prints a string to stdout without appending a newline.
1728 * Convenience method.
1729 */
1730 protected void print(String str) {
1731 out.print(str);
1732 }
1733
1734 /**
1735 * In standard mode, prints an empty line to stdout.
1736 * In thight mode, nothing happens.
1737 * Convenience method.
1738 */
1739 protected void println() {
1740 if (!compress) {
1741 out.println();
1742 }
1743 }
1744
1745 /**
1746 * In standard mode, prints the given text indented to stdout and appends newline.
1747 * In tight mode, doesn't print indentation or newlines.
1748 */
1749 protected void print(int indentLevel, String msg) {
1750 if (compress) {
1751 out.print(msg);
1752 }
1753 else {
1754 StringBuffer indentation = new StringBuffer();
1755 for (int i=0; i<indentLevel*indentStep; ++i) {
1756 indentation.append(' ');
1757 }
1758 out.print(indentation+msg);
1759 }
1760 }
1761
1762 /**
1763 * In tight mode, prints a message at a given indentation level.
1764 * In standard mode, appends a newline in addition.
1765 */
1766 protected void println(int indentLevel, String msg) {
1767 print(indentLevel, msg);
1768 if (!compress) out.println();
1769 }
1770
1771 /**
1772 * Prints an atom tag at the given indentation level.
1773 */
1774 protected void printAtomTag(int level, String tag) {
1775 println(level, "<"+tagPrefix+":"+replaceCharsInTag(tag)+"/>");
1776 }
1777
1778 /**
1779 * Prints an open tag at the given indentation level.
1780 */
1781 protected void printOpenTag(int level, String tag) {
1782 printOpenTag(level, replaceCharsInTag(tag), true);
1783 }
1784
1785 /**
1786 * Prints an open tag at the given indentation level and
1787 * conditionally appends a newline (if not in tight mode).
1788 */
1789 protected void printOpenTag(int level, String tag, boolean appendNewline) {
1790 if (appendNewline && !compress) {
1791 println(level, "<"+tagPrefix+":"+replaceCharsInTag(tag)+">");
1792 }
1793 else {
1794 print(level, "<"+tagPrefix+":"+replaceCharsInTag(tag)+">");
1795 }
1796 }
1797
1798 /**
1799 * Prints a close tag at the given indentation level.
1800 */
1801 protected void printCloseTag(int level, String tag) {
1802 printCloseTag(level, tag, true);
1803 }
1804
1805 /**
1806 * Prints a close tag at the given indentation level and
1807 * conditionally appends a newline (if not in tight mode).
1808 */
1809 protected void printCloseTag(int level, String tag, boolean appendNewline) {
1810 if (appendNewline && !compress) {
1811 println(level, "</"+tagPrefix+":"+replaceCharsInTag(tag)+">");
1812 }
1813 else {
1814 print(level, "</"+tagPrefix+":"+replaceCharsInTag(tag)+">");
1815 }
1816 }
1817
1818 public static int optionLength(String option) {
1819 if ("-d".equals(option)) return 2;
1820 else if ("-fixhtml".equals(option)) return 1;
1821 else if ("-compress".equals(option)) return 1;
1822 else if ("-nohtmlwarn".equals(option)) return 1;
1823 else if ("-noemailwarn".equals(option)) return 1;
1824 else if ("-indentstep".equals(option)) return 2;
1825 else if ("-xslsheet".equals(option)) return 2;
1826 else if ("-xsltdriver".equals(option)) return 2;
1827 else if ("-postprocess".equals(option)) return 2;
1828 else if ("-genhtml".equals(option)) return 1;
1829 else if ("-geninfo".equals(option)) return 1;
1830 else if ("-gendocbook".equals(option)) return 1;
1831 else if ("-xmlonly".equals(option)) return 1;
1832 else if ("-bottomnote".equals(option)) return 2;
1833 else if ("-workpath".equals(option)) return 2;
1834 else if ("-title".equals(option)) return 2;
1835 else if ("-tagletpath".equals(option)) return 2;
1836 else if ("-taglet".equals(option)) return 2;
1837 else if ("-authormail".equals(option)) return 2;
1838 else if ("-mailmangledot".equals(option)) return 2;
1839 else if ("-mailmangleat".equals(option)) return 2;
1840 else if ("-noindex".equals(option)) return 1;
1841 else if ("-nocomment".equals(option)) return 1;
1842 else if ("-notree".equals(option)) return 1;
1843 else if ("-nohelp".equals(option)) return 1;
1844 else if ("-nonavbar".equals(option)) return 1;
1845 else if ("-splitindex".equals(option)) return 1;
1846 else if ("-author".equals(option)) return 1;
1847 else if ("-version".equals(option)) return 1;
1848 else if ("-nosince".equals(option)) return 1;
1849 else if ("-nodeprecated".equals(option)) return 1;
1850 else if ("-linksource".equals(option)) return 1;
1851 else if ("-windowtitle".equals(option)) return 2;
1852 else if ("-helpfile".equals(option)) return 2;
1853 else if ("-stylesheetfile".equals(option)) return 2;
1854 else if ("-tag".equals(option)) return 2;
1855 else if ("-header".equals(option)) return 2;
1856 else if ("-footer".equals(option)) return 2;
1857 else if ("-bottom".equals(option)) return 2;
1858 else if ("-doctitle".equals(option)) return 2;
1859 else if ("-nodeprecatedlist".equals(option)) return 1;
1860 else if ("-uses".equals(option)) return 1;
1861 else if ("-group".equals(option)) return 3;
1862
1863 else return -1;
1864 }
1865
1866 public static boolean validOptions(String[][] options) {
1867 return true;
1868 }
1869
1870
1871 /**
1872 * Workaround for non well-formed comments: fix tag contents
1873 * by replacing <code>&lt;</code> with <code>&amp;lt;</code>,
1874 * <code>&gt;</code> with <code>&amp;gt;</code> and
1875 * <code>&amp;</code> with <code>&amp;amp;</code>.
1876 *
1877 * @param tagContent String to process
1878 *
1879 * @return given String with all special characters replaced by
1880 * HTML entities.
1881 */
1882 private static String replaceCharsInTag(String tagContent) {
1883 return
1884 replaceString(
1885 replaceString(
1886 replaceString(
1887 tagContent,
1888 "<", "&lt;"
1889 ),
1890 ">", "&gt;"
1891 ),
1892 "&", "&amp;"
1893 );
1894 }
1895
1896 /**
1897 * Replaces all occurences of string <code>needle</code> within string
1898 * <code>haystack</code> by string <code>replacement</code>.
1899 *
1900 * @param haystack The string to search and replace in.
1901 * @param needle The string which is searched for.
1902 * @param replacement The string by which every occurence of <code>needle</code> is replaced.
1903 */
1904 private static String replaceString(String haystack, String needle, String replacement) {
1905 int ndx = haystack.indexOf(needle);
1906 if (ndx<0)
1907 return haystack;
1908 else
1909 return haystack.substring(0, ndx) + replacement
1910 + replaceString(haystack.substring(ndx+needle.length()), needle, replacement);
1911 }
1912
1913 protected void setTargetFile(String filename) throws IOException {
1914
1915 OutputStream fileOut = new FileOutputStream(new File(xmlTargetDirectory, filename));
1916 out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(fileOut, "UTF8")));;
1917 }
1918
1919 protected void closeTargetFile() {
1920
1921 out.flush();
1922 out.close();
1923 }
1924
1925 private String cdata(String str) {
1926
1927 if (null==str) {
1928 return str;
1929 } // end of if ((null==str)
1930
1931 StringBuffer rc = new StringBuffer();
1932 for (int i=0; i<str.length(); ++i) {
1933 char c = str.charAt(i);
1934 if (c==0x09 || c==0x0a || c==0x0d || (c>=0x20 && c<=0xd7ff) || (c>=0xe000 && c<=0xfffd) || (c>=0x10000 && c<=0x10ffff)) {
1935 rc.append(c);
1936 }
1937 else {
1938 printWarning("Invalid Unicode character 0x"+Integer.toString(c, 16)+" in javadoc markup has been stripped.");
1939 } // end of else
1940
1941 }
1942 return rc.toString();
1943 }
1944
1945 static void copyResourceToFile(String resourceName, File target) throws IOException {
1946
1947 InputStream in = Driver.class.getResourceAsStream(resourceName);
1948
1949 if (null != in) {
1950
1951 FileOutputStream out = new FileOutputStream(target);
1952 int size;
1953 byte[] buffer = new byte[512];
1954 while ((size = in.read(buffer)) >= 0) {
1955 out.write(buffer, 0, size);
1956 }
1957 out.close();
1958 }
1959 else {
1960
1961 throw new IOException("Can't find resource named "+resourceName);
1962 }
1963 }
1964
1965 private void printError(String error) {
1966 if (null != rootDoc) {
1967 rootDoc.printError(error);
1968 }
1969 else {
1970 System.err.println("ERROR: "+error);
1971 }
1972 }
1973
1974 private void printWarning(String warning) {
1975 if (null != rootDoc) {
1976 rootDoc.printWarning(warning);
1977 }
1978 else {
1979 System.err.println("WARNING: "+warning);
1980 }
1981 }
1982
1983 private void printNotice(String notice) {
1984 if (null != rootDoc) {
1985 rootDoc.printNotice(notice);
1986 }
1987 else {
1988 System.err.println(notice);
1989 }
1990 }
1991
1992 /**
1993 * Copy the contents of the input directory to the output
1994 * directory. The output directory must exist.
1995 */
1996 private void copyPackageDataDir(GjdocPackageDoc packageDoc) throws IOException {
1997 File docFilesSourceDirectory
1998 = new File(packageDoc.packageDirectory(), "doc-files");
1999 File docFilesTargetDirectory
2000 = new File(this.targetDirectory,
2001 packageDoc.name().replace('.', File.separatorChar));
2002 if (docFilesSourceDirectory.exists()) {
2003 printNotice("Copying files from " + docFilesSourceDirectory);
2004 copyDirectory(docFilesSourceDirectory, docFilesTargetDirectory,
2005 docFilesSubdirsEnabled,
2006 excludeDocFilesSubDirs);
2007 }
2008 }
2009
2010 /**
2011 * Recursively copy the contents of the input directory to the
2012 * output directory. The output directory must exist.
2013 */
2014 private static void copyDirectory(File sourceDir, File targetDir,
2015 boolean recursive,
2016 Set excludeDirs) throws IOException {
2017 if (!targetDir.exists() && !targetDir.mkdirs()) {
2018 throw new IOException("Cannot create directory " + targetDir);
2019 }
2020
2021 File[] sourceFiles = sourceDir.listFiles();
2022 for (int i=0; i<sourceFiles.length; ++i) {
2023 if (sourceFiles[i].isDirectory()) {
2024 if (recursive && (null == excludeDirs
2025 || !excludeDirs.contains(sourceFiles[i].getName()))) {
2026 File targetSubDir = new File(targetDir,
2027 sourceFiles[i].getName());
2028 if (targetSubDir.exists() || targetSubDir.mkdir()) {
2029 copyDirectory(sourceFiles[i], targetSubDir, recursive, null);
2030 }
2031 else {
2032 throw new IOException("Cannot create directory " + targetSubDir);
2033 }
2034 }
2035 }
2036 else {
2037 copyFile(sourceFiles[i], new File(targetDir, sourceFiles[i].getName()));
2038 }
2039 }
2040 }
2041
2042 /**
2043 * Copy the contents of the input file to the output file. The
2044 * output file's parent directory must exist.
2045 */
2046 private static void copyFile(File sourceFile, File targetFile) throws IOException {
2047
2048 InputStream in = new FileInputStream(sourceFile);
2049 OutputStream out = new FileOutputStream(targetFile);
2050 int nread;
2051 byte[] buf = new byte[512];
2052 while ((nread = in.read(buf)) >= 0) {
2053 out.write(buf, 0, nread);
2054 }
2055 in.close();
2056 out.close();
2057 }
2058
2059 private void createIndexByName() throws IOException {
2060 // Create index
2061
2062 // Collect index
2063
2064 Map indexMap = new TreeMap(new Comparator() {
2065 public int compare(Object o1, Object o2) {
2066 return o1.toString().toLowerCase().compareTo(o2.toString().toLowerCase());
2067 }
2068 });
2069
2070 // Add packages to index
2071
2072 PackageDoc[] packages = rootDoc.specifiedPackages();
2073 for (int i=0, ilim=packages.length; i<ilim; ++i) {
2074 PackageDoc c = packages[i];
2075 indexMap.put(c.name(), c);
2076 }
2077
2078 // Add classes, fields and methods to index
2079
2080 ClassDoc[] sumclasses = rootDoc.classes();
2081 for (int i=0, ilim=sumclasses.length; i<ilim; ++i) {
2082 ClassDoc c = sumclasses[i];
2083 if (null == c.containingClass()) {
2084 indexMap.put(c.name(), c);
2085 }
2086 else {
2087 indexMap.put(c.name().substring(c.containingClass().name().length() + 1), c);
2088 }
2089 FieldDoc[] fields = c.fields();
2090 for (int j=0, jlim=fields.length; j<jlim; ++j) {
2091 indexMap.put(fields[j].name(), fields[j]);
2092 }
2093 MethodDoc[] methods = c.methods();
2094 for (int j=0, jlim=methods.length; j<jlim; ++j) {
2095 MethodDoc method = methods[j];
2096 StringBuffer signature = new StringBuffer();
2097 signature.append(method.name());
2098 signature.append('(');
2099 Parameter[] parameters = method.parameters();
2100 for (int k=0, klim=parameters.length; k<klim; ++k) {
2101 if (k > 0) {
2102 signature.append(", ");
2103 }
2104 signature.append(parameters[k].typeName());
2105 }
2106 signature.append(')');
2107 indexMap.put(signature.toString(), method);
2108 }
2109 }
2110
2111 // Assign output stream
2112
2113 setTargetFile("alphaindex.xml");
2114
2115 // Output XML document header
2116
2117 println(0, "<?xml version=\"1.0\"?>");
2118 println("<!DOCTYPE gjdoc SYSTEM \"dtd/gjdoc-alphaindex.dtd\">");
2119 println();
2120 printOpenTag(0, "alphaindex xmlns=\"http://www.w3.org/TR/REC-html40\" xmlns:gjdoc=\"http://www.gnu.org/software/cp-tools/gjdocxml\"");
2121
2122 Iterator it = indexMap.keySet().iterator();
2123
2124 char previousCategoryLetter = '\0';
2125 boolean categoryOpen = false;
2126
2127 while (it.hasNext()) {
2128 String key = (String)it.next();
2129 Doc entry = (Doc)indexMap.get(key);
2130
2131 char firstChar = Character.toUpperCase(key.charAt(0));
2132 if (firstChar != previousCategoryLetter) {
2133 if (categoryOpen) {
2134 printCloseTag(1, "category");
2135 }
2136 printOpenTag(1, "category letter=\"" + firstChar + "\"");
2137 categoryOpen = true;
2138 previousCategoryLetter = firstChar;
2139 }
2140
2141 printOpenTag(2, "entry name=\"" + key + "\"");
2142 if (entry instanceof PackageDoc) {
2143 printAtomTag(3, "isPackage");
2144 }
2145 else if (entry instanceof ClassDoc) {
2146 printAtomTag(3, "isClass");
2147 ClassDoc centry = (ClassDoc)entry;
2148 currentClass = centry;
2149 printAtomTag(3, "containingPackage name=\"" + centry.containingPackage().name() + "\"");
2150 if (null != centry.containingClass()) {
2151 printAtomTag(3, "containingClass name=\"" + centry.containingClass().name() + "\"");
2152 }
2153 if (centry.isInterface()) {
2154 printAtomTag(3, "isInterface");
2155 }
2156 if (centry.isException()) {
2157 printAtomTag(3, "isException");
2158 }
2159 if (centry.isError()) {
2160 printAtomTag(3, "isError");
2161 }
2162 if (centry.isOrdinaryClass()) {
2163 printAtomTag(3, "isOrdinaryClass");
2164 }
2165 }
2166 else if (entry instanceof ProgramElementDoc) {
2167 ProgramElementDoc pentry = (ProgramElementDoc)entry;
2168 currentClass = pentry.containingClass();
2169 printAtomTag(3, "containingPackage name=\"" + pentry.containingPackage().name() + "\"");
2170 printAtomTag(3, "containingClass name=\"" + pentry.containingClass().name() + "\"");
2171 if (pentry.isMethod()) {
2172 printAtomTag(3, "isMethod");
2173 ExecutableMemberDoc mentry = (ExecutableMemberDoc)pentry;
2174 printAtomTag(3, "signature full=\""+mentry.signature()+"\" flat=\""+mentry.flatSignature()+"\"");
2175 printAtomTag(3, "method name=\"" + mentry.name() + "\"");
2176 }
2177 if (pentry.isField()) {
2178 printAtomTag(3, "isField");
2179 }
2180 }
2181
2182 Tag[] tags = entry.firstSentenceTags();
2183 for (int i=0, ilim=tags.length; i<ilim; ++i) {
2184 Tag tag = tags[i];
2185 if (tag.firstSentenceTags().length>0) {
2186 printOpenTag(3, "firstSentenceTags", false);
2187 outputTags(4, tag.firstSentenceTags(), false, CONTEXT_TYPE);
2188 printCloseTag(3, "firstSentenceTags");
2189 }
2190 }
2191
2192
2193 printCloseTag(2, "entry");
2194 }
2195
2196 if (categoryOpen) {
2197 printCloseTag(1, "category");
2198 }
2199
2200 printCloseTag(0, "alphaindex");
2201
2202 closeTargetFile();
2203 }
2204
2205 private static class UsageType
2206 {
2207 public static final UsageType CLASS_DERIVED_FROM = new UsageType("class-derived-from");
2208 public static final UsageType FIELD_OF_TYPE = new UsageType("field-of-type");
2209 public static final UsageType METHOD_WITH_RETURN_TYPE = new UsageType("method-with-return-type");
2210 public static final UsageType METHOD_WITH_PARAMETER_TYPE = new UsageType("method-with-parameter-type");
2211 public static final UsageType METHOD_WITH_THROWN_TYPE = new UsageType("method-with-thrown-type");
2212 public static final UsageType CONSTRUCTOR_WITH_PARAMETER_TYPE = new UsageType("constructor-with-parameter-type");
2213 public static final UsageType CONSTRUCTOR_WITH_THROWN_TYPE = new UsageType("constructor-with-thrown-type");
2214 private String id;
2215
2216 private UsageType(String id)
2217 {
2218 this.id = id;
2219 }
2220
2221 public String toString() {
2222 return "UsageType{id=" + id + "}";
2223 }
2224
2225 public String getId() {
2226 return id;
2227 }
2228 }
2229
2230 /**
2231 * ClassDoc -> (PackageDoc -> (UsageType -> (Set of Doc)))
2232 */
2233 private Map usedClassToPackagesMap = new HashMap();
2234
2235 private void addUsedBy(ClassDoc usedClass, UsageType usageType, Doc user, PackageDoc userPackage)
2236 {
2237 Map packageToUsageTypeMap = (Map)usedClassToPackagesMap.get(usedClass);
2238 if (null == packageToUsageTypeMap) {
2239 packageToUsageTypeMap = new HashMap();
2240 usedClassToPackagesMap.put(usedClass, packageToUsageTypeMap);
2241 }
2242
2243 Map usageTypeToUsersMap = (Map)packageToUsageTypeMap.get(userPackage);
2244 if (null == usageTypeToUsersMap) {
2245 usageTypeToUsersMap = new HashMap();
2246 packageToUsageTypeMap.put(userPackage, usageTypeToUsersMap);
2247 }
2248
2249 Set userSet = (Set)usageTypeToUsersMap.get(usageType);
2250 if (null == userSet) {
2251 userSet = new TreeSet(); // FIXME: we need the collator from Main here
2252 usageTypeToUsersMap.put(usageType, userSet);
2253 }
2254 userSet.add(user);
2255 }
2256
2257 /**
2258 * Create the cross reference database.
2259 */
2260 private void collectUsage() {
2261
2262 ClassDoc[] classes = rootDoc.classes();
2263 for (int i = 0, ilim = classes.length; i < ilim; ++ i) {
2264 ClassDoc clazz = classes[i];
2265
2266 // classes derived from
2267 for (ClassDoc superclass = clazz.superclass(); superclass != null;
2268 superclass = superclass.superclass()) {
2269 addUsedBy(superclass, UsageType.CLASS_DERIVED_FROM, clazz, clazz.containingPackage());
2270 }
2271
2272 FieldDoc[] fields = clazz.fields();
2273 for (int j = 0, jlim = fields.length; j < jlim; ++ j) {
2274 FieldDoc field = fields[j];
2275
2276 // fields of type
2277 ClassDoc fieldType = field.type().asClassDoc();
2278 if (null != fieldType) {
2279 addUsedBy(fieldType, UsageType.FIELD_OF_TYPE,
2280 field, clazz.containingPackage());
2281 }
2282 }
2283
2284 MethodDoc[] methods = clazz.methods();
2285 for (int j = 0, jlim = methods.length; j < jlim; ++ j) {
2286 MethodDoc method = methods[j];
2287
2288 // methods with return type
2289
2290 ClassDoc returnType = method.returnType().asClassDoc();
2291 if (null != returnType) {
2292 addUsedBy(returnType, UsageType.METHOD_WITH_RETURN_TYPE,
2293 method, clazz.containingPackage());
2294 }
2295 Parameter[] parameters = method.parameters();
2296 for (int k=0; k<parameters.length; ++k) {
2297
2298 // methods with parameter type
2299
2300 Parameter parameter = parameters[k];
2301 ClassDoc parameterType = parameter.type().asClassDoc();
2302 if (null != parameterType) {
2303 addUsedBy(parameterType, UsageType.METHOD_WITH_PARAMETER_TYPE,
2304 method, clazz.containingPackage());
2305 }
2306 }
2307
2308 // methods which throw
2309
2310 ClassDoc[] thrownExceptions = method.thrownExceptions();
2311 for (int k = 0, klim = thrownExceptions.length; k < klim; ++ k) {
2312 ClassDoc thrownException = thrownExceptions[k];
2313 addUsedBy(thrownException, UsageType.METHOD_WITH_THROWN_TYPE,
2314 method, clazz.containingPackage());
2315 }
2316 }
2317
2318 ConstructorDoc[] constructors = clazz.constructors();
2319 for (int j = 0, jlim = constructors.length; j < jlim; ++ j) {
2320
2321 ConstructorDoc constructor = constructors[j];
2322
2323 Parameter[] parameters = constructor.parameters();
2324 for (int k = 0, klim = parameters.length; k < klim; ++ k) {
2325
2326 // constructors with parameter type
2327
2328 Parameter parameter = parameters[k];
2329 ClassDoc parameterType = parameter.type().asClassDoc();
2330 if (null != parameterType) {
2331 addUsedBy(parameterType, UsageType.CONSTRUCTOR_WITH_PARAMETER_TYPE,
2332 constructor, clazz.containingPackage());
2333 }
2334 }
2335
2336 // constructors which throw
2337
2338 ClassDoc[] thrownExceptions = constructor.thrownExceptions();
2339 for (int k = 0, klim = thrownExceptions.length; k < klim; ++ k) {
2340 ClassDoc thrownException = thrownExceptions[k];
2341 addUsedBy(thrownException, UsageType.CONSTRUCTOR_WITH_THROWN_TYPE,
2342 constructor, clazz.containingPackage());
2343 }
2344 }
2345 }
2346 }
2347
2348 private void outputUsage(ClassDoc clazz, int level) {
2349
2350 Map packageToUsageTypeMap = (Map)usedClassToPackagesMap.get(clazz);
2351 if (null != packageToUsageTypeMap) {
2352 printOpenTag(level, "references");
2353
2354 Iterator packagesIterator = packageToUsageTypeMap.keySet().iterator();
2355
2356 while (packagesIterator.hasNext()) {
2357 PackageDoc packageDoc = (PackageDoc)packagesIterator.next();
2358 printOpenTag(level + 1, "referencing-package name=\"" + packageDoc.name() + "\"");
2359 Map usageTypeToUsersMap = (Map)packageToUsageTypeMap.get(packageDoc);
2360 Iterator usageTypeIterator = usageTypeToUsersMap.keySet().iterator();
2361 while (usageTypeIterator.hasNext()) {
2362 UsageType usageType = (UsageType)usageTypeIterator.next();
2363 printOpenTag(level + 2, "usage-type id=\"" + usageType.getId() + "\"");
2364 Set users = (Set)usageTypeToUsersMap.get(usageType);
2365 Iterator userIterator = users.iterator();
2366 while (userIterator.hasNext()) {
2367 Doc user = (Doc)userIterator.next();
2368 if (user instanceof ClassDoc) {
2369 printAtomTag(level + 3, "user"
2370 + " class=\"" + ((ClassDoc)user).name() + "\"");
2371 }
2372 else if (user instanceof FieldDoc) {
2373 FieldDoc fieldDoc = (FieldDoc)user;
2374 printAtomTag(level + 3, "user"
2375 + " class=\"" + fieldDoc.containingClass().name() + "\""
2376 + " field=\"" + fieldDoc.name() + "\"");
2377 }
2378 else if (user instanceof MethodDoc) {
2379 MethodDoc methodDoc = (MethodDoc)user;
2380 printAtomTag(level + 3, "user"
2381 + " class=\"" + methodDoc.containingClass().name() + "\""
2382 + " method=\"" + methodDoc.name() + "\""
2383 + " signature=\"" + methodDoc.signature() + "\""
2384 + " flatSignature=\"" + methodDoc.flatSignature() + "\"");
2385 }
2386 else if (user instanceof ConstructorDoc) {
2387 ConstructorDoc constructorDoc = (ConstructorDoc)user;
2388 printAtomTag(level + 3, "user"
2389 + " class=\"" + constructorDoc.containingClass().name() + "\""
2390 + " signature=\"" + constructorDoc.signature() + "\""
2391 + " flatSignature=\"" + constructorDoc.flatSignature() + "\"");
2392 }
2393 }
2394 printCloseTag(level +2, "usage-type");
2395 }
2396 printCloseTag(level + 1, "referencing-package");
2397 }
2398
2399 printCloseTag(level, "references");
2400 }
2401 }
2402
2403 private boolean processGroupOption(String groupName, String colonSeparatedPackageList)
2404 {
2405 try {
2406 PackageMatcher packageMatcher = new PackageMatcher();
2407
2408 StringTokenizer tokenizer = new StringTokenizer(colonSeparatedPackageList, ":");
2409 while (tokenizer.hasMoreTokens()) {
2410 String packageWildcard = tokenizer.nextToken();
2411 packageMatcher.addWildcard(packageWildcard);
2412 }
2413
2414 SortedSet groupPackages = packageMatcher.filter(rootDoc.specifiedPackages());
2415
2416 packageGroups.add(new PackageGroup(groupName, groupPackages));
2417
2418 return true;
2419 }
2420 catch (InvalidPackageWildcardException e) {
2421 return false;
2422 }
2423 }
2424
2425 private void registerTaglet(Taglet taglet)
2426 {
2427 tagletMap.put(taglet.getName(), taglet);
2428 }
2429
2430 private boolean isVerbose()
2431 {
2432 return false;
2433 }
2434 }