libjava/ChangeLog:
[official-gcc.git] / libjava / classpath / tools / gnu / classpath / tools / doclets / AbstractDoclet.java
blob87772077ec249e45017a26538985cdfacd0fbd18
1 /* gnu.classpath.tools.doclets.AbstractDoclet
2 Copyright (C) 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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.
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.
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. */
21 package gnu.classpath.tools.doclets;
23 import com.sun.javadoc.ClassDoc;
24 import com.sun.javadoc.ConstructorDoc;
25 import com.sun.javadoc.Doc;
26 import com.sun.javadoc.Doclet;
27 import com.sun.javadoc.ExecutableMemberDoc;
28 import com.sun.javadoc.FieldDoc;
29 import com.sun.javadoc.MethodDoc;
30 import com.sun.javadoc.PackageDoc;
31 import com.sun.javadoc.Parameter;
32 import com.sun.javadoc.RootDoc;
33 import com.sun.javadoc.Tag;
34 import com.sun.javadoc.Type;
36 import com.sun.tools.doclets.Taglet;
38 import gnu.classpath.tools.taglets.GnuExtendedTaglet;
39 import gnu.classpath.tools.taglets.AuthorTaglet;
40 import gnu.classpath.tools.taglets.CodeTaglet;
41 import gnu.classpath.tools.taglets.DeprecatedTaglet;
42 import gnu.classpath.tools.taglets.GenericTaglet;
43 import gnu.classpath.tools.taglets.SinceTaglet;
44 import gnu.classpath.tools.taglets.ValueTaglet;
45 import gnu.classpath.tools.taglets.VersionTaglet;
46 import gnu.classpath.tools.taglets.TagletContext;
48 import gnu.classpath.tools.IOToolkit;
49 import gnu.classpath.tools.FileSystemClassLoader;
51 import java.io.File;
52 import java.io.IOException;
54 import java.lang.reflect.Method;
55 import java.lang.reflect.Modifier;
56 import java.lang.reflect.InvocationTargetException;
58 import java.text.MessageFormat;
60 import java.util.ArrayList;
61 import java.util.Arrays;
62 import java.util.Collections;
63 import java.util.Comparator;
64 import java.util.HashMap;
65 import java.util.Iterator;
66 import java.util.LinkedHashMap;
67 import java.util.LinkedHashSet;
68 import java.util.LinkedList;
69 import java.util.List;
70 import java.util.Locale;
71 import java.util.Map;
72 import java.util.ResourceBundle;
73 import java.util.Set;
74 import java.util.SortedSet;
75 import java.util.StringTokenizer;
76 import java.util.TreeMap;
77 import java.util.TreeSet;
79 /**
80 * An abstract Doclet implementation with helpers for common tasks
81 * performed by Doclets.
83 public abstract class AbstractDoclet
85 /**
86 * Mapping from tag type to Taglet for user Taglets specified on
87 * the command line.
89 protected Map tagletMap = new LinkedHashMap();
91 /**
92 * Stores the package groups specified in the user
93 * options. Contains objects of type PackageGroup.
95 private List packageGroups = new LinkedList();
97 /**
98 * The current classpath for loading taglet classes.
100 private String tagletPath;
103 * Keeps track of the tags mentioned by the user during option
104 * processiong so that an error can be emitted if a tag is
105 * mentioned more than once.
107 private List mentionedTags = new LinkedList();
109 public static int optionLength(String option) {
110 return instance.getOptionLength(option);
113 public static boolean validOptions(String[][] options) {
114 return true;
117 private static AbstractDoclet instance;
119 protected static void setInstance(AbstractDoclet instance)
121 AbstractDoclet.instance = instance;
124 protected abstract void run()
125 throws DocletConfigurationException, IOException;
127 public static boolean start(RootDoc rootDoc)
129 try {
131 instance.startInstance(rootDoc);
132 return true;
134 catch (DocletConfigurationException e) {
135 instance.printError(e.getMessage());
136 return false;
138 catch (Exception e) {
139 e.printStackTrace();
140 return false;
144 protected RootDoc getRootDoc()
146 return this.rootDoc;
149 private RootDoc rootDoc;
151 protected abstract InlineTagRenderer getInlineTagRenderer();
153 private void startInstance(RootDoc rootDoc)
154 throws DocletConfigurationException, IOException
156 this.rootDoc = rootDoc;
158 // Set the default Taglet order
160 registerTaglet(new VersionTaglet());
161 registerTaglet(new AuthorTaglet());
162 registerTaglet(new SinceTaglet(getInlineTagRenderer()));
163 registerTaglet(new StandardTaglet("serial"));
164 registerTaglet(new StandardTaglet("deprecated"));
165 registerTaglet(new StandardTaglet("see"));
166 registerTaglet(new StandardTaglet("param"));
167 registerTaglet(new StandardTaglet("return"));
169 registerTaglet(new ValueTaglet());
170 registerTaglet(new CodeTaglet());
172 // Process command line options
174 for (int i=0, ilim=rootDoc.options().length; i<ilim; ++i) {
176 String[] optionArr = rootDoc.options()[i];
177 String _optionTag = optionArr[0];
179 DocletOption option = (DocletOption)nameToOptionMap.get(_optionTag.toLowerCase());
181 if (null != option) {
182 option.set(optionArr);
186 // Enable/disable standard taglets based on user input
188 AuthorTaglet.setTagletEnabled(optionAuthor.getValue());
189 VersionTaglet.setTagletEnabled(optionVersion.getValue());
190 SinceTaglet.setTagletEnabled(!optionNoSince.getValue());
191 DeprecatedTaglet.setTagletEnabled(!optionNoDeprecated.getValue());
193 if (!getTargetDirectory().exists()) {
194 if (!getTargetDirectory().mkdirs()) {
195 throw new DocletConfigurationException("Cannot create target directory "
196 + getTargetDirectory());
200 run();
203 public File getTargetDirectory()
205 return optionTargetDirectory.getValue();
208 private DocletOptionFile optionTargetDirectory =
209 new DocletOptionFile("-d",
210 new File(System.getProperty("user.dir")));
212 private DocletOptionFlag optionNoEmailWarn =
213 new DocletOptionFlag("-noemailwarn");
215 private DocletOptionFlag optionAuthor =
216 new DocletOptionFlag("-author");
218 private DocletOptionFlag optionVersion =
219 new DocletOptionFlag("-version");
221 private DocletOptionFlag optionNoSince =
222 new DocletOptionFlag("-nosince");
224 private DocletOptionFlag optionNoDeprecated =
225 new DocletOptionFlag("-nodeprecated");
227 private DocletOptionGroup optionGroup =
228 new DocletOptionGroup("-group");
230 private DocletOptionPackageWildcard optionNoQualifier =
231 new DocletOptionPackageWildcard("-noqualifier", true);
233 private DocletOptionFlag optionDocFilesSubDirs =
234 new DocletOptionFlag("-docfilessubdirs");
236 private DocletOptionColonSeparated optionExcludeDocFilesSubDir =
237 new DocletOptionColonSeparated("-excludedocfilessubdir");
239 private DocletOptionTagletPath optionTagletPath =
240 new DocletOptionTagletPath("-tagletpath");
242 private DocletOptionTag optionTaglet =
243 new DocletOptionTag("-taglet");
245 private DocletOptionTag optionTag =
246 new DocletOptionTag("-tag");
248 private class DocletOptionTaglet
249 extends DocletOption
251 DocletOptionTaglet(String optionName)
253 super(optionName);
256 public int getLength()
258 return 2;
261 public boolean set(String[] optionArr)
264 boolean tagletLoaded = false;
266 String useTagletPath = AbstractDoclet.this.tagletPath;
267 if (null == useTagletPath) {
268 useTagletPath = System.getProperty("java.class.path");
271 try {
272 Class tagletClass;
273 try {
274 tagletClass
275 = new FileSystemClassLoader(useTagletPath).loadClass(optionArr[1]);
277 catch (ClassNotFoundException e) {
278 // If not found on specified tagletpath, try default classloader
279 tagletClass
280 = Class.forName(optionArr[1]);
282 Method registerTagletMethod
283 = tagletClass.getDeclaredMethod("register", new Class[] { java.util.Map.class });
285 if (!registerTagletMethod.getReturnType().equals(Void.TYPE)) {
286 printError("Taglet class '" + optionArr[1] + "' found, but register method doesn't return void.");
288 else if (registerTagletMethod.getExceptionTypes().length > 0) {
289 printError("Taglet class '" + optionArr[1] + "' found, but register method contains throws clause.");
291 else if ((registerTagletMethod.getModifiers() & (Modifier.STATIC | Modifier.PUBLIC | Modifier.ABSTRACT)) != (Modifier.STATIC | Modifier.PUBLIC)) {
292 printError("Taglet class '" + optionArr[1] + "' found, but register method isn't public static, or is abstract..");
294 else {
295 Map tempMap = new HashMap();
296 registerTagletMethod.invoke(null, new Object[] { tempMap });
297 tagletLoaded = true;
298 String name = (String)tempMap.keySet().iterator().next();
299 Taglet taglet = (Taglet)tempMap.get(name);
300 tagletMap.put(name, taglet);
301 mentionedTags.add(taglet);
304 catch (NoSuchMethodException e) {
305 printError("Taglet class '" + optionArr[1] + "' found, but doesn't contain the register method.");
307 catch (SecurityException e) {
308 printError("Taglet class '" + optionArr[1] + "' cannot be loaded: " + e.getMessage());
310 catch (InvocationTargetException e) {
311 printError("Taglet class '" + optionArr[1] + "' found, but register method throws exception: " + e.toString());
313 catch (IllegalAccessException e) {
314 printError("Taglet class '" + optionArr[1] + "' found, but there was a problem when accessing the register method: " + e.toString());
316 catch (IllegalArgumentException e) {
317 printError("Taglet class '" + optionArr[1] + "' found, but there was a problem when accessing the register method: " + e.toString());
319 catch (ClassNotFoundException e) {
320 printError("Taglet class '" + optionArr[1] + "' cannot be found.");
322 return tagletLoaded;
326 private class DocletOptionGroup
327 extends DocletOption
329 DocletOptionGroup(String optionName)
331 super(optionName);
334 public int getLength()
336 return 3;
339 public boolean set(String[] optionArr)
341 try {
342 PackageMatcher packageMatcher = new PackageMatcher();
344 StringTokenizer tokenizer = new StringTokenizer(optionArr[2], ":");
345 while (tokenizer.hasMoreTokens()) {
346 String packageWildcard = tokenizer.nextToken();
347 packageMatcher.addWildcard(packageWildcard);
350 SortedSet groupPackages = packageMatcher.filter(rootDoc.specifiedPackages());
352 packageGroups.add(new PackageGroup(optionArr[1], groupPackages));
354 return true;
356 catch (InvalidPackageWildcardException e) {
357 return false;
363 private class DocletOptionTagletPath
364 extends DocletOption
366 DocletOptionTagletPath(String optionName)
368 super(optionName);
371 public int getLength()
373 return 2;
376 public boolean set(String[] optionArr)
378 AbstractDoclet.this.tagletPath = optionArr[1];
379 return true;
383 private class DocletOptionTag
384 extends DocletOption
386 DocletOptionTag(String optionName)
388 super(optionName);
391 public int getLength()
393 return 2;
396 public boolean set(String[] optionArr)
398 String tagSpec = optionArr[1];
399 boolean validTagSpec = false;
400 int ndx1 = tagSpec.indexOf(':');
401 if (ndx1 < 0) {
402 Taglet taglet = (Taglet)tagletMap.get(tagSpec);
403 if (null == taglet) {
404 printError("There is no standard tag '" + tagSpec + "'.");
406 else {
407 if (mentionedTags.contains(taglet)) {
408 printError("Tag '" + tagSpec + "' has been added or moved before.");
410 else {
411 mentionedTags.add(taglet);
413 // re-append taglet
414 tagletMap.remove(tagSpec);
415 tagletMap.put(tagSpec, taglet);
419 else {
420 int ndx2 = tagSpec.indexOf(':', ndx1 + 1);
421 if (ndx2 > ndx1 && ndx2 < tagSpec.length() - 1) {
422 String tagName = tagSpec.substring(0, ndx1);
423 String tagHead = null;
424 if (tagSpec.charAt(ndx2 + 1) == '\"') {
425 if (tagSpec.charAt(tagSpec.length() - 1) == '\"') {
426 tagHead = tagSpec.substring(ndx2 + 2, tagSpec.length() - 1);
427 validTagSpec = true;
430 else {
431 tagHead = tagSpec.substring(ndx2 + 1);
432 validTagSpec = true;
435 boolean tagScopeOverview = false;
436 boolean tagScopePackages = false;
437 boolean tagScopeTypes = false;
438 boolean tagScopeConstructors = false;
439 boolean tagScopeMethods = false;
440 boolean tagScopeFields = false;
441 boolean tagDisabled = false;
443 tag_option_loop:
444 for (int n=ndx1+1; n<ndx2; ++n) {
445 switch (tagSpec.charAt(n)) {
446 case 'X':
447 tagDisabled = true;
448 break;
449 case 'a':
450 tagScopeOverview = true;
451 tagScopePackages = true;
452 tagScopeTypes = true;
453 tagScopeConstructors = true;
454 tagScopeMethods = true;
455 tagScopeFields = true;
456 break;
457 case 'o':
458 tagScopeOverview = true;
459 break;
460 case 'p':
461 tagScopePackages = true;
462 break;
463 case 't':
464 tagScopeTypes = true;
465 break;
466 case 'c':
467 tagScopeConstructors = true;
468 break;
469 case 'm':
470 tagScopeMethods = true;
471 break;
472 case 'f':
473 tagScopeFields = true;
474 break;
475 default:
476 validTagSpec = false;
477 break tag_option_loop;
481 if (validTagSpec) {
482 GenericTaglet taglet
483 = new GenericTaglet(tagName,
484 tagHead,
485 tagScopeOverview,
486 tagScopePackages,
487 tagScopeTypes,
488 tagScopeConstructors,
489 tagScopeMethods,
490 tagScopeFields);
491 taglet.setTagletEnabled(!tagDisabled);
492 taglet.register(tagletMap);
493 mentionedTags.add(taglet);
497 if (!validTagSpec) {
498 printError("Value for option -tag must be in format \"<tagname>:Xaoptcmf:<taghead>\".");
500 return validTagSpec;
504 private DocletOption[] commonOptions =
506 optionTargetDirectory,
507 optionAuthor,
508 optionVersion,
509 optionNoSince,
510 optionNoDeprecated,
511 optionGroup,
512 optionDocFilesSubDirs,
513 optionExcludeDocFilesSubDir,
514 optionTagletPath,
515 optionTaglet,
516 optionTag,
519 private void registerOptions()
521 if (!optionsRegistered) {
522 for (int i=0; i<commonOptions.length; ++i) {
523 DocletOption option = commonOptions[i];
524 registerOption(option);
526 DocletOption[] docletOptions = getOptions();
527 for (int i=0; i<docletOptions.length; ++i) {
528 DocletOption option = docletOptions[i];
529 registerOption(option);
531 optionsRegistered = true;
535 protected abstract DocletOption[] getOptions();
537 private boolean optionsRegistered = false;
539 private void registerOption(DocletOption option)
541 nameToOptionMap.put(option.getName(), option);
544 private Map nameToOptionMap = new HashMap();
546 private int getOptionLength(String optionName)
548 registerOptions();
549 DocletOption option = (DocletOption)nameToOptionMap.get(optionName.toLowerCase());
550 if (null != option) {
551 return option.getLength();
553 else {
554 return -1;
558 protected List getKnownDirectSubclasses(ClassDoc classDoc)
560 List result = new LinkedList();
561 if (!"java.lang.Object".equals(classDoc.qualifiedName())) {
562 ClassDoc[] classes = rootDoc.classes();
563 for (int i=0; i<classes.length; ++i) {
564 if (classDoc == classes[i].superclass()) {
565 result.add(classes[i]);
569 return result;
572 protected static class IndexKey
573 implements Comparable
575 private String name;
576 private String lowerName;
578 public IndexKey(String name)
580 this.name = name;
581 this.lowerName = name.toLowerCase();
584 public boolean equals(Object other)
586 return this.lowerName.equals(((IndexKey)other).lowerName);
589 public int hashCode()
591 return lowerName.hashCode();
594 public int compareTo(Object other)
596 return lowerName.compareTo(((IndexKey)other).lowerName);
599 public String getName()
601 return name;
605 private Map categorizedIndex;
607 protected Map getCategorizedIndex()
609 if (null == categorizedIndex) {
610 categorizedIndex = new LinkedHashMap();
612 Map indexMap = getIndexByName();
613 LinkedList keys = new LinkedList(); //indexMap.keySet().size());
614 keys.addAll(indexMap.keySet());
615 Collections.sort(keys);
616 Iterator it = keys.iterator(); //indexMap.keySet().iterator();
617 char previousCategoryLetter = '\0';
618 Character keyLetter = null;
619 while (it.hasNext()) {
620 IndexKey key = (IndexKey)it.next();
621 char firstChar = Character.toUpperCase(key.getName().charAt(0));
622 if (firstChar != previousCategoryLetter) {
623 keyLetter = new Character(firstChar);
624 previousCategoryLetter = firstChar;
625 categorizedIndex.put(keyLetter, new LinkedList());
627 List letterList = (List)categorizedIndex.get(keyLetter);
628 letterList.add(indexMap.get(key));
632 return categorizedIndex;
636 private Map indexByName;
638 protected Map getIndexByName()
640 if (null == indexByName) {
641 // Create index
643 // Collect index
645 indexByName = new HashMap(); //TreeMap();
647 // Add packages to index
649 PackageDoc[] packages = rootDoc.specifiedPackages();
650 for (int i=0, ilim=packages.length; i<ilim; ++i) {
651 PackageDoc c = packages[i];
652 if (c.name().length() > 0) {
653 indexByName.put(new IndexKey(c.name()), c);
657 // Add classes, fields and methods to index
659 ClassDoc[] sumclasses = rootDoc.classes();
660 for (int i=0, ilim=sumclasses.length; i<ilim; ++i) {
661 ClassDoc c = sumclasses[i];
662 if (null == c.containingClass()) {
663 indexByName.put(new IndexKey(c.name() + " " + c.containingPackage().name()), c);
665 else {
666 indexByName.put(new IndexKey(c.name().substring(c.containingClass().name().length() + 1)
667 + " " + c.containingClass().name() + " " + c.containingPackage().name()), c);
669 FieldDoc[] fields = c.fields();
670 for (int j=0, jlim=fields.length; j<jlim; ++j) {
671 indexByName.put(new IndexKey(fields[j].name() + " " + fields[j].containingClass().name() + " " + fields[j].containingPackage().name()), fields[j]);
673 MethodDoc[] methods = c.methods();
674 for (int j=0, jlim=methods.length; j<jlim; ++j) {
675 MethodDoc method = methods[j];
676 indexByName.put(new IndexKey(method.name() + method.signature() + " " + method.containingClass().name() + " " + method.containingPackage().name()), method);
678 ConstructorDoc[] constructors = c.constructors();
679 for (int j=0, jlim=constructors.length; j<jlim; ++j) {
680 ConstructorDoc constructor = constructors[j];
681 indexByName.put(new IndexKey(constructor.name() + constructor.signature() + " " + constructor.containingClass().name() + " " + constructor.containingPackage().name()), constructor);
685 return indexByName;
688 private void registerTaglet(Taglet taglet)
690 tagletMap.put(taglet.getName(), taglet);
693 protected void printTaglets(Tag[] tags, TagletContext context, TagletPrinter output, boolean inline)
695 for (Iterator it = tagletMap.keySet().iterator(); it.hasNext(); ) {
696 String tagName = (String)it.next();
697 Object o = tagletMap.get(tagName);
698 Taglet taglet = (Taglet)o;
699 Doc doc = context.getDoc();
700 if (inline == taglet.isInlineTag()
701 && ((doc == null
702 && taglet.inOverview())
703 || (doc != null
704 && ((doc.isConstructor() && taglet.inConstructor())
705 || (doc.isField() && taglet.inField())
706 || (doc.isMethod() && taglet.inMethod())
707 || (doc instanceof PackageDoc && taglet.inPackage())
708 || ((doc.isClass() || doc.isInterface()) && taglet.inType()))))) {
710 List tagsOfThisType = new LinkedList();
711 for (int i=0; i<tags.length; ++i) {
712 if (tags[i].name().substring(1).equals(tagName)) {
713 tagsOfThisType.add(tags[i]);
717 Tag[] tagletTags = (Tag[])tagsOfThisType.toArray(new Tag[tagsOfThisType.size()]);
719 String tagletString;
720 if (taglet instanceof StandardTaglet) {
721 tagletString = renderTag(tagName, tagletTags, context);
723 else if (taglet instanceof GnuExtendedTaglet) {
724 tagletString = ((GnuExtendedTaglet)taglet).toString(tagletTags, context);
726 else {
727 tagletString = taglet.toString(tagletTags);
729 if (null != tagletString) {
730 output.printTagletString(tagletString);
736 protected void printInlineTaglet(Tag tag, TagletContext context, TagletPrinter output)
738 Taglet taglet = (Taglet)tagletMap.get(tag.name().substring(1));
739 if (null != taglet) {
740 String tagletString;
741 if (taglet instanceof GnuExtendedTaglet) {
742 tagletString = ((GnuExtendedTaglet)taglet).toString(tag, context);
744 else {
745 tagletString = taglet.toString(tag);
747 if (null != tagletString) {
748 output.printTagletString(tagletString);
751 else {
752 printWarning("Unknown tag: " + tag.name());
756 protected void printMainTaglets(Tag[] tags, TagletContext context, TagletPrinter output)
758 printTaglets(tags, context, output, false);
762 * @param usedClassToPackagesMap ClassDoc to (PackageDoc to (UsageType to (Set of Doc)))
764 private void addUsedBy(Map usedClassToPackagesMap,
765 ClassDoc usedClass, UsageType usageType, Doc user, PackageDoc userPackage)
767 Map packageToUsageTypeMap = (Map)usedClassToPackagesMap.get(usedClass);
768 if (null == packageToUsageTypeMap) {
769 packageToUsageTypeMap = new HashMap();
770 usedClassToPackagesMap.put(usedClass, packageToUsageTypeMap);
773 Map usageTypeToUsersMap = (Map)packageToUsageTypeMap.get(userPackage);
774 if (null == usageTypeToUsersMap) {
775 usageTypeToUsersMap = new TreeMap();
776 packageToUsageTypeMap.put(userPackage, usageTypeToUsersMap);
779 Set userSet = (Set)usageTypeToUsersMap.get(usageType);
780 if (null == userSet) {
781 userSet = new TreeSet(); // FIXME: we need the collator from Main here
782 usageTypeToUsersMap.put(usageType, userSet);
784 userSet.add(user);
788 * Create the cross reference database.
790 private Map collectUsage() {
792 Map _usedClassToPackagesMap = new HashMap();
794 ClassDoc[] classes = rootDoc.classes();
795 for (int i = 0, ilim = classes.length; i < ilim; ++ i) {
796 ClassDoc clazz = classes[i];
798 if (clazz.isInterface()) {
799 // classes implementing
800 InterfaceRelation relation
801 = (InterfaceRelation)getInterfaceRelations().get(clazz);
802 Iterator it = relation.implementingClasses.iterator();
803 while (it.hasNext()) {
804 ClassDoc implementor = (ClassDoc)it.next();
805 addUsedBy(_usedClassToPackagesMap,
806 clazz, UsageType.CLASS_IMPLEMENTING, implementor, implementor.containingPackage());
809 else {
810 // classes derived from
811 for (ClassDoc superclass = clazz.superclass(); superclass != null;
812 superclass = superclass.superclass()) {
813 addUsedBy(_usedClassToPackagesMap,
814 superclass, UsageType.CLASS_DERIVED_FROM, clazz, clazz.containingPackage());
818 FieldDoc[] fields = clazz.fields();
819 for (int j = 0, jlim = fields.length; j < jlim; ++ j) {
820 FieldDoc field = fields[j];
822 // fields of type
823 ClassDoc fieldType = field.type().asClassDoc();
824 if (null != fieldType) {
825 addUsedBy(_usedClassToPackagesMap,
826 fieldType, UsageType.FIELD_OF_TYPE,
827 field, clazz.containingPackage());
831 MethodDoc[] methods = clazz.methods();
832 for (int j = 0, jlim = methods.length; j < jlim; ++ j) {
833 MethodDoc method = methods[j];
835 // methods with return type
837 ClassDoc returnType = method.returnType().asClassDoc();
838 if (null != returnType) {
839 addUsedBy(_usedClassToPackagesMap,
840 returnType, UsageType.METHOD_WITH_RETURN_TYPE,
841 method, clazz.containingPackage());
843 Parameter[] parameters = method.parameters();
844 for (int k=0; k<parameters.length; ++k) {
846 // methods with parameter type
848 Parameter parameter = parameters[k];
849 ClassDoc parameterType = parameter.type().asClassDoc();
850 if (null != parameterType) {
851 addUsedBy(_usedClassToPackagesMap,
852 parameterType, UsageType.METHOD_WITH_PARAMETER_TYPE,
853 method, clazz.containingPackage());
857 // methods which throw
859 ClassDoc[] thrownExceptions = method.thrownExceptions();
860 for (int k = 0, klim = thrownExceptions.length; k < klim; ++ k) {
861 ClassDoc thrownException = thrownExceptions[k];
862 addUsedBy(_usedClassToPackagesMap,
863 thrownException, UsageType.METHOD_WITH_THROWN_TYPE,
864 method, clazz.containingPackage());
868 ConstructorDoc[] constructors = clazz.constructors();
869 for (int j = 0, jlim = constructors.length; j < jlim; ++ j) {
871 ConstructorDoc constructor = constructors[j];
873 Parameter[] parameters = constructor.parameters();
874 for (int k = 0, klim = parameters.length; k < klim; ++ k) {
876 // constructors with parameter type
878 Parameter parameter = parameters[k];
879 ClassDoc parameterType = parameter.type().asClassDoc();
880 if (null != parameterType) {
881 addUsedBy(_usedClassToPackagesMap,
882 parameterType, UsageType.CONSTRUCTOR_WITH_PARAMETER_TYPE,
883 constructor, clazz.containingPackage());
887 // constructors which throw
889 ClassDoc[] thrownExceptions = constructor.thrownExceptions();
890 for (int k = 0, klim = thrownExceptions.length; k < klim; ++ k) {
891 ClassDoc thrownException = thrownExceptions[k];
892 addUsedBy(_usedClassToPackagesMap,
893 thrownException, UsageType.CONSTRUCTOR_WITH_THROWN_TYPE,
894 constructor, clazz.containingPackage());
898 return _usedClassToPackagesMap;
901 private Map usedClassToPackagesMap = null;
903 protected Map getUsageOfClass(ClassDoc classDoc)
905 if (null == this.usedClassToPackagesMap) {
906 this.usedClassToPackagesMap = collectUsage();
908 return (Map)this.usedClassToPackagesMap.get(classDoc);
911 protected static class UsageType
912 implements Comparable
914 public static final UsageType CLASS_DERIVED_FROM = new UsageType("class-derived-from");
915 public static final UsageType CLASS_IMPLEMENTING = new UsageType("class-implementing");
916 public static final UsageType FIELD_OF_TYPE = new UsageType("field-of-type");
917 public static final UsageType METHOD_WITH_RETURN_TYPE = new UsageType("method-with-return-type");
918 public static final UsageType METHOD_WITH_PARAMETER_TYPE = new UsageType("method-with-parameter-type");
919 public static final UsageType METHOD_WITH_THROWN_TYPE = new UsageType("method-with-thrown-type");
920 public static final UsageType CONSTRUCTOR_WITH_PARAMETER_TYPE = new UsageType("constructor-with-parameter-type");
921 public static final UsageType CONSTRUCTOR_WITH_THROWN_TYPE = new UsageType("constructor-with-thrown-type");
922 private String id;
924 private UsageType(String id)
926 this.id = id;
929 public int compareTo(Object other)
931 return this.id.compareTo(((UsageType)other).id);
934 public String toString() {
935 return "UsageType{id=" + id + "}";
938 public String getId() {
939 return id;
943 private ResourceBundle resources;
945 protected String getString(String key)
947 if (null == resources) {
948 Locale currentLocale = Locale.getDefault();
950 resources
951 = ResourceBundle.getBundle("htmldoclet.HtmlDoclet", currentLocale);
954 return resources.getString(key);
957 protected String format(String key, String value1)
959 return MessageFormat.format(getString(key), new Object[] { value1 });
962 protected List getPackageGroups()
964 return packageGroups;
967 protected void copyDocFiles(File sourceDir, File targetDir)
968 throws IOException
970 File sourceDocFiles = new File(sourceDir, "doc-files");
971 File targetDocFiles = new File(targetDir, "doc-files");
973 if (sourceDocFiles.exists()) {
974 IOToolkit.copyDirectory(sourceDocFiles,
975 targetDocFiles,
976 optionDocFilesSubDirs.getValue(),
977 optionExcludeDocFilesSubDir.getComponents());
981 private Set sourcePaths;
984 * Try to determine the source directory for the given package by
985 * looking at the path specified by -sourcepath, or the current
986 * directory if -sourcepath hasn't been specified.
988 * @throws IOException if the source directory couldn't be
989 * located.
991 * @return List of File
993 protected List getPackageSourceDirs(PackageDoc packageDoc)
994 throws IOException
996 if (null == sourcePaths) {
997 for (int i=0; i<rootDoc.options().length; ++i) {
998 if ("-sourcepath".equals(rootDoc.options()[i][0])
999 || "-s".equals(rootDoc.options()[i][0])) {
1000 sourcePaths = new LinkedHashSet();
1001 String sourcepathString = rootDoc.options()[i][1];
1002 StringTokenizer st = new StringTokenizer(sourcepathString, File.pathSeparator);
1003 while (st.hasMoreTokens()) {
1004 sourcePaths.add(new File(st.nextToken()));
1008 if (null == sourcePaths) {
1009 sourcePaths = new LinkedHashSet();
1010 sourcePaths.add(new File(System.getProperty("user.dir")));
1014 String packageSubDir = packageDoc.name().replace('.', File.separatorChar);
1015 Iterator it = sourcePaths.iterator();
1016 List result = new LinkedList();
1017 while (it.hasNext()) {
1018 File pathComponent = (File)it.next();
1019 File packageDir = new File(pathComponent, packageSubDir);
1020 if (packageDir.exists()) {
1021 result.add(packageDir);
1024 if (result.isEmpty()) {
1025 throw new IOException("Couldn't locate source directory for package " + packageDoc.name());
1027 else {
1028 return result;
1032 protected File getSourceFile(ClassDoc classDoc)
1033 throws IOException
1035 List packageDirs = getPackageSourceDirs(classDoc.containingPackage());
1036 Iterator it = packageDirs.iterator();
1037 while (it.hasNext()) {
1038 File packageDir = (File)it.next();
1039 File sourceFile = new File(packageDir, getOuterClassDoc(classDoc).name() + ".java");
1040 if (sourceFile.exists()) {
1041 return sourceFile;
1045 throw new IOException("Couldn't locate source file for class " + classDoc.qualifiedTypeName());
1048 protected void printError(String error)
1050 if (null != rootDoc) {
1051 rootDoc.printError(error);
1053 else {
1054 System.err.println("ERROR: "+error);
1058 protected void printWarning(String warning)
1060 if (null != rootDoc) {
1061 rootDoc.printWarning(warning);
1063 else {
1064 System.err.println("WARNING: "+warning);
1068 protected void printNotice(String notice)
1070 if (null != rootDoc) {
1071 rootDoc.printNotice(notice);
1073 else {
1074 System.err.println(notice);
1078 protected static ClassDoc getOuterClassDoc(ClassDoc classDoc)
1080 while (null != classDoc.containingClass()) {
1081 classDoc = classDoc.containingClass();
1083 return classDoc;
1086 private SortedSet allPackages;
1088 protected Set getAllPackages()
1090 if (null == this.allPackages) {
1091 allPackages = new TreeSet();
1092 PackageDoc[] specifiedPackages = rootDoc.specifiedPackages();
1093 for (int i=0; i<specifiedPackages.length; ++i) {
1094 allPackages.add(specifiedPackages[i]);
1096 ClassDoc[] specifiedClasses = rootDoc.specifiedClasses();
1097 for (int i=0; i<specifiedClasses.length; ++i) {
1098 allPackages.add(specifiedClasses[i].containingPackage());
1101 return this.allPackages;
1104 protected boolean omitPackageQualifier(PackageDoc packageDoc)
1106 if (!optionNoQualifier.isSpecified()) {
1107 return false;
1109 else {
1110 return optionNoQualifier.match(packageDoc);
1114 protected String possiblyQualifiedName(Type type)
1116 if (null == type.asClassDoc()
1117 || !omitPackageQualifier(type.asClassDoc().containingPackage())) {
1118 return type.qualifiedTypeName();
1120 else {
1121 return type.typeName();
1125 protected static class InterfaceRelation
1127 public Set superInterfaces;
1128 public Set subInterfaces;
1129 public Set implementingClasses;
1131 public InterfaceRelation()
1133 superInterfaces = new TreeSet();
1134 subInterfaces = new TreeSet();
1135 implementingClasses = new TreeSet();
1139 private void addAllInterfaces(ClassDoc classDoc, Set allInterfaces)
1141 ClassDoc[] interfaces = classDoc.interfaces();
1142 for (int i=0; i<interfaces.length; ++i) {
1143 allInterfaces.add(interfaces[i]);
1144 addAllInterfaces(interfaces[i], allInterfaces);
1148 private Map allSubClasses;
1150 protected Map getAllSubClasses()
1152 if (null == allSubClasses) {
1153 allSubClasses = new HashMap();
1155 ClassDoc[] classDocs = getRootDoc().classes();
1156 for (int i=0; i<classDocs.length; ++i) {
1157 if (!classDocs[i].isInterface()) {
1158 for (ClassDoc cd = classDocs[i].superclass();
1159 null != cd;
1160 cd = cd.superclass()) {
1162 if (!cd.qualifiedTypeName().equals("java.lang.Object")) {
1163 List subClasses = (List)allSubClasses.get(cd);
1164 if (null == subClasses) {
1165 subClasses = new LinkedList();
1166 allSubClasses.put(cd, subClasses);
1168 subClasses.add(classDocs[i]);
1174 return allSubClasses;
1177 private Map interfaceRelations;
1179 private void addToInterfaces(ClassDoc classDoc, ClassDoc[] interfaces)
1181 for (int i=0; i<interfaces.length; ++i) {
1182 InterfaceRelation interfaceRelation
1183 = (InterfaceRelation)interfaceRelations.get(interfaces[i]);
1184 if (null == interfaceRelation) {
1185 interfaceRelation = new InterfaceRelation();
1186 interfaceRelations.put(interfaces[i], interfaceRelation);
1188 interfaceRelation.implementingClasses.add(classDoc);
1189 addToInterfaces(classDoc, interfaces[i].interfaces());
1193 protected Map getInterfaceRelations()
1195 if (null == interfaceRelations) {
1196 interfaceRelations = new HashMap();
1198 ClassDoc[] classDocs = getRootDoc().classes();
1199 for (int i=0; i<classDocs.length; ++i) {
1200 if (classDocs[i].isInterface()) {
1201 InterfaceRelation relation = new InterfaceRelation();
1202 addAllInterfaces(classDocs[i], relation.superInterfaces);
1203 interfaceRelations.put(classDocs[i], relation);
1207 Iterator it = interfaceRelations.keySet().iterator();
1208 while (it.hasNext()) {
1209 ClassDoc interfaceDoc = (ClassDoc)it.next();
1210 InterfaceRelation relation
1211 = (InterfaceRelation)interfaceRelations.get(interfaceDoc);
1212 Iterator superIt = relation.superInterfaces.iterator();
1213 while (superIt.hasNext()) {
1214 ClassDoc superInterfaceDoc = (ClassDoc)superIt.next();
1215 InterfaceRelation superRelation
1216 = (InterfaceRelation)interfaceRelations.get(superInterfaceDoc);
1217 if (null != superRelation) {
1218 superRelation.subInterfaces.add(interfaceDoc);
1223 for (int i=0; i<classDocs.length; ++i) {
1224 if (!classDocs[i].isInterface()) {
1225 for (ClassDoc cd = classDocs[i]; null != cd; cd = cd.superclass()) {
1226 addToInterfaces(classDocs[i], cd.interfaces());
1232 return interfaceRelations;
1235 private Map sortedMethodMap = new HashMap();
1237 protected MethodDoc[] getSortedMethods(ClassDoc classDoc)
1239 MethodDoc[] result = (MethodDoc[])sortedMethodMap.get(classDoc);
1240 if (null == result) {
1241 MethodDoc[] methods = classDoc.methods();
1242 result = (MethodDoc[])methods.clone();
1243 Arrays.sort(result);
1244 return result;
1246 return result;
1249 private Map sortedConstructorMap = new HashMap();
1251 protected ConstructorDoc[] getSortedConstructors(ClassDoc classDoc)
1253 ConstructorDoc[] result = (ConstructorDoc[])sortedConstructorMap.get(classDoc);
1254 if (null == result) {
1255 ConstructorDoc[] constructors = classDoc.constructors();
1256 result = (ConstructorDoc[])constructors.clone();
1257 Arrays.sort(result);
1258 return result;
1260 return result;
1263 private Map sortedFieldMap = new HashMap();
1265 protected FieldDoc[] getSortedFields(ClassDoc classDoc)
1267 FieldDoc[] result = (FieldDoc[])sortedFieldMap.get(classDoc);
1268 if (null == result) {
1269 FieldDoc[] fields = classDoc.fields();
1270 result = (FieldDoc[])fields.clone();
1271 Arrays.sort(result);
1272 return result;
1274 return result;
1277 private Map sortedInnerClassMap = new HashMap();
1279 protected ClassDoc[] getSortedInnerClasses(ClassDoc classDoc)
1281 ClassDoc[] result = (ClassDoc[])sortedInnerClassMap.get(classDoc);
1282 if (null == result) {
1283 ClassDoc[] innerClasses = classDoc.innerClasses();
1284 result = (ClassDoc[])innerClasses.clone();
1285 Arrays.sort(result);
1286 return result;
1288 return result;
1291 protected abstract String renderTag(String tagName, Tag[] tags, TagletContext context);
1293 protected abstract String getDocletVersion();
1295 protected SortedSet getThrownExceptions(ExecutableMemberDoc execMemberDoc)
1297 SortedSet result = new TreeSet();
1298 ClassDoc[] thrownExceptions = execMemberDoc.thrownExceptions();
1299 for (int j=0; j<thrownExceptions.length; ++j) {
1300 result.add(thrownExceptions[j]);
1302 return result;
1305 protected boolean isUncheckedException(ClassDoc classDoc)
1307 if (classDoc.isException()) {
1308 while (null != classDoc) {
1309 if (classDoc.qualifiedTypeName().equals("java.lang.RuntimeException")) {
1310 return true;
1312 classDoc = classDoc.superclass();
1314 return false;
1316 else {
1317 return false;
1321 protected FieldDoc findField(ClassDoc classDoc, String fieldName)
1323 for (ClassDoc cd = classDoc; cd != null; cd = cd.superclass()) {
1324 FieldDoc[] fields = cd.fields(false);
1325 for (int i=0; i<fields.length; ++i) {
1326 if (fields[i].name().equals(fieldName)) {
1327 return fields[i];
1331 return null;
1334 private Map implementedInterfacesCache = new HashMap();
1336 protected Set getImplementedInterfaces(ClassDoc classDoc)
1338 Set result = (Set)implementedInterfacesCache.get(classDoc);
1339 if (null == result) {
1340 result = new TreeSet();
1342 for (ClassDoc cd = classDoc; cd != null; cd = cd.superclass()) {
1343 ClassDoc[] interfaces = cd.interfaces();
1344 for (int i=0; i<interfaces.length; ++i) {
1345 result.add(interfaces[i]);
1346 InterfaceRelation relation
1347 = (InterfaceRelation)getInterfaceRelations().get(interfaces[i]);
1348 if (null != relation) {
1349 result.addAll(relation.superInterfaces);
1354 implementedInterfacesCache.put(classDoc, result);
1357 return result;
1360 protected boolean isSinglePackage()
1362 return getAllPackages().size() <= 1;
1365 protected PackageDoc getSinglePackage()
1367 return (PackageDoc)getAllPackages().iterator().next();