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)
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
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package gnu.classpath.tools.doclets;
40 import com.sun.javadoc.ClassDoc;
41 import com.sun.javadoc.ConstructorDoc;
42 import com.sun.javadoc.Doc;
43 import com.sun.javadoc.Doclet;
44 import com.sun.javadoc.ExecutableMemberDoc;
45 import com.sun.javadoc.FieldDoc;
46 import com.sun.javadoc.MethodDoc;
47 import com.sun.javadoc.PackageDoc;
48 import com.sun.javadoc.Parameter;
49 import com.sun.javadoc.RootDoc;
50 import com.sun.javadoc.Tag;
51 import com.sun.javadoc.Type;
53 import com.sun.tools.doclets.Taglet;
55 import gnu.classpath.tools.taglets.GnuExtendedTaglet;
56 import gnu.classpath.tools.taglets.AuthorTaglet;
57 import gnu.classpath.tools.taglets.CodeTaglet;
58 import gnu.classpath.tools.taglets.DeprecatedTaglet;
59 import gnu.classpath.tools.taglets.GenericTaglet;
60 import gnu.classpath.tools.taglets.SinceTaglet;
61 import gnu.classpath.tools.taglets.ValueTaglet;
62 import gnu.classpath.tools.taglets.VersionTaglet;
63 import gnu.classpath.tools.taglets.TagletContext;
65 import gnu.classpath.tools.IOToolkit;
66 import gnu.classpath.tools.FileSystemClassLoader;
69 import java.io.IOException;
71 import java.lang.reflect.Method;
72 import java.lang.reflect.Modifier;
73 import java.lang.reflect.InvocationTargetException;
75 import java.text.MessageFormat;
77 import java.util.ArrayList;
78 import java.util.Arrays;
79 import java.util.Collections;
80 import java.util.Comparator;
81 import java.util.HashMap;
82 import java.util.Iterator;
83 import java.util.LinkedHashMap;
84 import java.util.LinkedHashSet;
85 import java.util.LinkedList;
86 import java.util.List;
87 import java.util.Locale;
89 import java.util.ResourceBundle;
91 import java.util.SortedSet;
92 import java.util.StringTokenizer;
93 import java.util.TreeMap;
94 import java.util.TreeSet;
97 * An abstract Doclet implementation with helpers for common tasks
98 * performed by Doclets.
100 public abstract class AbstractDoclet
103 * Mapping from tag type to Taglet for user Taglets specified on
106 protected Map tagletMap = new LinkedHashMap();
109 * Stores the package groups specified in the user
110 * options. Contains objects of type PackageGroup.
112 private List packageGroups = new LinkedList();
115 * The current classpath for loading taglet classes.
117 private String tagletPath;
120 * Keeps track of the tags mentioned by the user during option
121 * processiong so that an error can be emitted if a tag is
122 * mentioned more than once.
124 private List mentionedTags = new LinkedList();
126 public static int optionLength(String option) {
127 return instance.getOptionLength(option);
130 public static boolean validOptions(String[][] options) {
134 private static AbstractDoclet instance;
136 protected static void setInstance(AbstractDoclet instance)
138 AbstractDoclet.instance = instance;
141 protected abstract void run()
142 throws DocletConfigurationException, IOException;
144 public static boolean start(RootDoc rootDoc)
148 instance.startInstance(rootDoc);
151 catch (DocletConfigurationException e) {
152 instance.printError(e.getMessage());
155 catch (Exception e) {
161 protected RootDoc getRootDoc()
166 private RootDoc rootDoc;
168 protected abstract InlineTagRenderer getInlineTagRenderer();
170 private void startInstance(RootDoc rootDoc)
171 throws DocletConfigurationException, IOException
173 this.rootDoc = rootDoc;
175 // Set the default Taglet order
177 registerTaglet(new VersionTaglet());
178 registerTaglet(new AuthorTaglet());
179 registerTaglet(new SinceTaglet(getInlineTagRenderer()));
180 registerTaglet(new StandardTaglet("serial"));
181 registerTaglet(new StandardTaglet("deprecated"));
182 registerTaglet(new StandardTaglet("see"));
183 registerTaglet(new StandardTaglet("param"));
184 registerTaglet(new StandardTaglet("return"));
186 registerTaglet(new ValueTaglet());
187 registerTaglet(new CodeTaglet());
189 // Process command line options
191 for (int i=0, ilim=rootDoc.options().length; i<ilim; ++i) {
193 String[] optionArr = rootDoc.options()[i];
194 String _optionTag = optionArr[0];
196 DocletOption option = (DocletOption)nameToOptionMap.get(_optionTag.toLowerCase());
198 if (null != option) {
199 option.set(optionArr);
203 // Enable/disable standard taglets based on user input
205 AuthorTaglet.setTagletEnabled(optionAuthor.getValue());
206 VersionTaglet.setTagletEnabled(optionVersion.getValue());
207 SinceTaglet.setTagletEnabled(!optionNoSince.getValue());
208 DeprecatedTaglet.setTagletEnabled(!optionNoDeprecated.getValue());
210 if (!getTargetDirectory().exists()) {
211 if (!getTargetDirectory().mkdirs()) {
212 throw new DocletConfigurationException("Cannot create target directory "
213 + getTargetDirectory());
220 public File getTargetDirectory()
222 return optionTargetDirectory.getValue();
225 private DocletOptionFile optionTargetDirectory =
226 new DocletOptionFile("-d",
227 new File(System.getProperty("user.dir")));
229 private DocletOptionFlag optionNoEmailWarn =
230 new DocletOptionFlag("-noemailwarn");
232 private DocletOptionFlag optionAuthor =
233 new DocletOptionFlag("-author");
235 private DocletOptionFlag optionVersion =
236 new DocletOptionFlag("-version");
238 private DocletOptionFlag optionNoSince =
239 new DocletOptionFlag("-nosince");
241 private DocletOptionFlag optionNoDeprecated =
242 new DocletOptionFlag("-nodeprecated");
244 private DocletOptionGroup optionGroup =
245 new DocletOptionGroup("-group");
247 private DocletOptionPackageWildcard optionNoQualifier =
248 new DocletOptionPackageWildcard("-noqualifier", true);
250 private DocletOptionFlag optionDocFilesSubDirs =
251 new DocletOptionFlag("-docfilessubdirs");
253 private DocletOptionColonSeparated optionExcludeDocFilesSubDir =
254 new DocletOptionColonSeparated("-excludedocfilessubdir");
256 private DocletOptionTagletPath optionTagletPath =
257 new DocletOptionTagletPath("-tagletpath");
259 private DocletOptionTag optionTaglet =
260 new DocletOptionTag("-taglet");
262 private DocletOptionTag optionTag =
263 new DocletOptionTag("-tag");
265 private class DocletOptionTaglet
268 DocletOptionTaglet(String optionName)
273 public int getLength()
278 public boolean set(String[] optionArr)
281 boolean tagletLoaded = false;
283 String useTagletPath = AbstractDoclet.this.tagletPath;
284 if (null == useTagletPath) {
285 useTagletPath = System.getProperty("java.class.path");
292 = new FileSystemClassLoader(useTagletPath).loadClass(optionArr[1]);
294 catch (ClassNotFoundException e) {
295 // If not found on specified tagletpath, try default classloader
297 = Class.forName(optionArr[1]);
299 Method registerTagletMethod
300 = tagletClass.getDeclaredMethod("register", new Class[] { java.util.Map.class });
302 if (!registerTagletMethod.getReturnType().equals(Void.TYPE)) {
303 printError("Taglet class '" + optionArr[1] + "' found, but register method doesn't return void.");
305 else if (registerTagletMethod.getExceptionTypes().length > 0) {
306 printError("Taglet class '" + optionArr[1] + "' found, but register method contains throws clause.");
308 else if ((registerTagletMethod.getModifiers() & (Modifier.STATIC | Modifier.PUBLIC | Modifier.ABSTRACT)) != (Modifier.STATIC | Modifier.PUBLIC)) {
309 printError("Taglet class '" + optionArr[1] + "' found, but register method isn't public static, or is abstract..");
312 Map tempMap = new HashMap();
313 registerTagletMethod.invoke(null, new Object[] { tempMap });
315 String name = (String)tempMap.keySet().iterator().next();
316 Taglet taglet = (Taglet)tempMap.get(name);
317 tagletMap.put(name, taglet);
318 mentionedTags.add(taglet);
321 catch (NoSuchMethodException e) {
322 printError("Taglet class '" + optionArr[1] + "' found, but doesn't contain the register method.");
324 catch (SecurityException e) {
325 printError("Taglet class '" + optionArr[1] + "' cannot be loaded: " + e.getMessage());
327 catch (InvocationTargetException e) {
328 printError("Taglet class '" + optionArr[1] + "' found, but register method throws exception: " + e.toString());
330 catch (IllegalAccessException e) {
331 printError("Taglet class '" + optionArr[1] + "' found, but there was a problem when accessing the register method: " + e.toString());
333 catch (IllegalArgumentException e) {
334 printError("Taglet class '" + optionArr[1] + "' found, but there was a problem when accessing the register method: " + e.toString());
336 catch (ClassNotFoundException e) {
337 printError("Taglet class '" + optionArr[1] + "' cannot be found.");
343 private class DocletOptionGroup
346 DocletOptionGroup(String optionName)
351 public int getLength()
356 public boolean set(String[] optionArr)
359 PackageMatcher packageMatcher = new PackageMatcher();
361 StringTokenizer tokenizer = new StringTokenizer(optionArr[2], ":");
362 while (tokenizer.hasMoreTokens()) {
363 String packageWildcard = tokenizer.nextToken();
364 packageMatcher.addWildcard(packageWildcard);
367 SortedSet groupPackages = packageMatcher.filter(rootDoc.specifiedPackages());
369 packageGroups.add(new PackageGroup(optionArr[1], groupPackages));
373 catch (InvalidPackageWildcardException e) {
380 private class DocletOptionTagletPath
383 DocletOptionTagletPath(String optionName)
388 public int getLength()
393 public boolean set(String[] optionArr)
395 AbstractDoclet.this.tagletPath = optionArr[1];
400 private class DocletOptionTag
403 DocletOptionTag(String optionName)
408 public int getLength()
413 public boolean set(String[] optionArr)
415 String tagSpec = optionArr[1];
416 boolean validTagSpec = false;
417 int ndx1 = tagSpec.indexOf(':');
419 Taglet taglet = (Taglet)tagletMap.get(tagSpec);
420 if (null == taglet) {
421 printError("There is no standard tag '" + tagSpec + "'.");
424 if (mentionedTags.contains(taglet)) {
425 printError("Tag '" + tagSpec + "' has been added or moved before.");
428 mentionedTags.add(taglet);
431 tagletMap.remove(tagSpec);
432 tagletMap.put(tagSpec, taglet);
437 int ndx2 = tagSpec.indexOf(':', ndx1 + 1);
438 if (ndx2 > ndx1 && ndx2 < tagSpec.length() - 1) {
439 String tagName = tagSpec.substring(0, ndx1);
440 String tagHead = null;
441 if (tagSpec.charAt(ndx2 + 1) == '\"') {
442 if (tagSpec.charAt(tagSpec.length() - 1) == '\"') {
443 tagHead = tagSpec.substring(ndx2 + 2, tagSpec.length() - 1);
448 tagHead = tagSpec.substring(ndx2 + 1);
452 boolean tagScopeOverview = false;
453 boolean tagScopePackages = false;
454 boolean tagScopeTypes = false;
455 boolean tagScopeConstructors = false;
456 boolean tagScopeMethods = false;
457 boolean tagScopeFields = false;
458 boolean tagDisabled = false;
461 for (int n=ndx1+1; n<ndx2; ++n) {
462 switch (tagSpec.charAt(n)) {
467 tagScopeOverview = true;
468 tagScopePackages = true;
469 tagScopeTypes = true;
470 tagScopeConstructors = true;
471 tagScopeMethods = true;
472 tagScopeFields = true;
475 tagScopeOverview = true;
478 tagScopePackages = true;
481 tagScopeTypes = true;
484 tagScopeConstructors = true;
487 tagScopeMethods = true;
490 tagScopeFields = true;
493 validTagSpec = false;
494 break tag_option_loop;
500 = new GenericTaglet(tagName,
505 tagScopeConstructors,
508 taglet.setTagletEnabled(!tagDisabled);
509 taglet.register(tagletMap);
510 mentionedTags.add(taglet);
515 printError("Value for option -tag must be in format \"<tagname>:Xaoptcmf:<taghead>\".");
521 private DocletOption[] commonOptions =
523 optionTargetDirectory,
529 optionDocFilesSubDirs,
530 optionExcludeDocFilesSubDir,
536 private void registerOptions()
538 if (!optionsRegistered) {
539 for (int i=0; i<commonOptions.length; ++i) {
540 DocletOption option = commonOptions[i];
541 registerOption(option);
543 DocletOption[] docletOptions = getOptions();
544 for (int i=0; i<docletOptions.length; ++i) {
545 DocletOption option = docletOptions[i];
546 registerOption(option);
548 optionsRegistered = true;
552 protected abstract DocletOption[] getOptions();
554 private boolean optionsRegistered = false;
556 private void registerOption(DocletOption option)
558 nameToOptionMap.put(option.getName(), option);
561 private Map nameToOptionMap = new HashMap();
563 private int getOptionLength(String optionName)
566 DocletOption option = (DocletOption)nameToOptionMap.get(optionName.toLowerCase());
567 if (null != option) {
568 return option.getLength();
575 protected List getKnownDirectSubclasses(ClassDoc classDoc)
577 List result = new LinkedList();
578 if (!"java.lang.Object".equals(classDoc.qualifiedName())) {
579 ClassDoc[] classes = rootDoc.classes();
580 for (int i=0; i<classes.length; ++i) {
581 if (classDoc == classes[i].superclass()) {
582 result.add(classes[i]);
589 protected static class IndexKey
590 implements Comparable
593 private String lowerName;
595 public IndexKey(String name)
598 this.lowerName = name.toLowerCase();
601 public boolean equals(Object other)
603 return this.lowerName.equals(((IndexKey)other).lowerName);
606 public int hashCode()
608 return lowerName.hashCode();
611 public int compareTo(Object other)
613 return lowerName.compareTo(((IndexKey)other).lowerName);
616 public String getName()
622 private Map categorizedIndex;
624 protected Map getCategorizedIndex()
626 if (null == categorizedIndex) {
627 categorizedIndex = new LinkedHashMap();
629 Map indexMap = getIndexByName();
630 LinkedList keys = new LinkedList(); //indexMap.keySet().size());
631 keys.addAll(indexMap.keySet());
632 Collections.sort(keys);
633 Iterator it = keys.iterator(); //indexMap.keySet().iterator();
634 char previousCategoryLetter = '\0';
635 Character keyLetter = null;
636 while (it.hasNext()) {
637 IndexKey key = (IndexKey)it.next();
638 char firstChar = Character.toUpperCase(key.getName().charAt(0));
639 if (firstChar != previousCategoryLetter) {
640 keyLetter = new Character(firstChar);
641 previousCategoryLetter = firstChar;
642 categorizedIndex.put(keyLetter, new LinkedList());
644 List letterList = (List)categorizedIndex.get(keyLetter);
645 letterList.add(indexMap.get(key));
649 return categorizedIndex;
653 private Map indexByName;
655 protected Map getIndexByName()
657 if (null == indexByName) {
662 indexByName = new HashMap(); //TreeMap();
664 // Add packages to index
666 PackageDoc[] packages = rootDoc.specifiedPackages();
667 for (int i=0, ilim=packages.length; i<ilim; ++i) {
668 PackageDoc c = packages[i];
669 if (c.name().length() > 0) {
670 indexByName.put(new IndexKey(c.name()), c);
674 // Add classes, fields and methods to index
676 ClassDoc[] sumclasses = rootDoc.classes();
677 for (int i=0, ilim=sumclasses.length; i<ilim; ++i) {
678 ClassDoc c = sumclasses[i];
679 if (null == c.containingClass()) {
680 indexByName.put(new IndexKey(c.name() + " " + c.containingPackage().name()), c);
683 indexByName.put(new IndexKey(c.name().substring(c.containingClass().name().length() + 1)
684 + " " + c.containingClass().name() + " " + c.containingPackage().name()), c);
686 FieldDoc[] fields = c.fields();
687 for (int j=0, jlim=fields.length; j<jlim; ++j) {
688 indexByName.put(new IndexKey(fields[j].name() + " " + fields[j].containingClass().name() + " " + fields[j].containingPackage().name()), fields[j]);
690 MethodDoc[] methods = c.methods();
691 for (int j=0, jlim=methods.length; j<jlim; ++j) {
692 MethodDoc method = methods[j];
693 indexByName.put(new IndexKey(method.name() + method.signature() + " " + method.containingClass().name() + " " + method.containingPackage().name()), method);
695 ConstructorDoc[] constructors = c.constructors();
696 for (int j=0, jlim=constructors.length; j<jlim; ++j) {
697 ConstructorDoc constructor = constructors[j];
698 indexByName.put(new IndexKey(constructor.name() + constructor.signature() + " " + constructor.containingClass().name() + " " + constructor.containingPackage().name()), constructor);
705 private void registerTaglet(Taglet taglet)
707 tagletMap.put(taglet.getName(), taglet);
710 protected void printTaglets(Tag[] tags, TagletContext context, TagletPrinter output, boolean inline)
712 for (Iterator it = tagletMap.keySet().iterator(); it.hasNext(); ) {
713 String tagName = (String)it.next();
714 Object o = tagletMap.get(tagName);
715 Taglet taglet = (Taglet)o;
716 Doc doc = context.getDoc();
717 if (inline == taglet.isInlineTag()
719 && taglet.inOverview())
721 && ((doc.isConstructor() && taglet.inConstructor())
722 || (doc.isField() && taglet.inField())
723 || (doc.isMethod() && taglet.inMethod())
724 || (doc instanceof PackageDoc && taglet.inPackage())
725 || ((doc.isClass() || doc.isInterface()) && taglet.inType()))))) {
727 List tagsOfThisType = new LinkedList();
728 for (int i=0; i<tags.length; ++i) {
729 if (tags[i].name().substring(1).equals(tagName)) {
730 tagsOfThisType.add(tags[i]);
734 Tag[] tagletTags = (Tag[])tagsOfThisType.toArray(new Tag[tagsOfThisType.size()]);
737 if (taglet instanceof StandardTaglet) {
738 tagletString = renderTag(tagName, tagletTags, context);
740 else if (taglet instanceof GnuExtendedTaglet) {
741 tagletString = ((GnuExtendedTaglet)taglet).toString(tagletTags, context);
744 tagletString = taglet.toString(tagletTags);
746 if (null != tagletString) {
747 output.printTagletString(tagletString);
753 protected void printInlineTaglet(Tag tag, TagletContext context, TagletPrinter output)
755 Taglet taglet = (Taglet)tagletMap.get(tag.name().substring(1));
756 if (null != taglet) {
758 if (taglet instanceof GnuExtendedTaglet) {
759 tagletString = ((GnuExtendedTaglet)taglet).toString(tag, context);
762 tagletString = taglet.toString(tag);
764 if (null != tagletString) {
765 output.printTagletString(tagletString);
769 printWarning("Unknown tag: " + tag.name());
773 protected void printMainTaglets(Tag[] tags, TagletContext context, TagletPrinter output)
775 printTaglets(tags, context, output, false);
779 * @param usedClassToPackagesMap ClassDoc to (PackageDoc to (UsageType to (Set of Doc)))
781 private void addUsedBy(Map usedClassToPackagesMap,
782 ClassDoc usedClass, UsageType usageType, Doc user, PackageDoc userPackage)
784 Map packageToUsageTypeMap = (Map)usedClassToPackagesMap.get(usedClass);
785 if (null == packageToUsageTypeMap) {
786 packageToUsageTypeMap = new HashMap();
787 usedClassToPackagesMap.put(usedClass, packageToUsageTypeMap);
790 Map usageTypeToUsersMap = (Map)packageToUsageTypeMap.get(userPackage);
791 if (null == usageTypeToUsersMap) {
792 usageTypeToUsersMap = new TreeMap();
793 packageToUsageTypeMap.put(userPackage, usageTypeToUsersMap);
796 Set userSet = (Set)usageTypeToUsersMap.get(usageType);
797 if (null == userSet) {
798 userSet = new TreeSet(); // FIXME: we need the collator from Main here
799 usageTypeToUsersMap.put(usageType, userSet);
805 * Create the cross reference database.
807 private Map collectUsage() {
809 Map _usedClassToPackagesMap = new HashMap();
811 ClassDoc[] classes = rootDoc.classes();
812 for (int i = 0, ilim = classes.length; i < ilim; ++ i) {
813 ClassDoc clazz = classes[i];
815 if (clazz.isInterface()) {
816 // classes implementing
817 InterfaceRelation relation
818 = (InterfaceRelation)getInterfaceRelations().get(clazz);
819 Iterator it = relation.implementingClasses.iterator();
820 while (it.hasNext()) {
821 ClassDoc implementor = (ClassDoc)it.next();
822 addUsedBy(_usedClassToPackagesMap,
823 clazz, UsageType.CLASS_IMPLEMENTING, implementor, implementor.containingPackage());
827 // classes derived from
828 for (ClassDoc superclass = clazz.superclass(); superclass != null;
829 superclass = superclass.superclass()) {
830 addUsedBy(_usedClassToPackagesMap,
831 superclass, UsageType.CLASS_DERIVED_FROM, clazz, clazz.containingPackage());
835 FieldDoc[] fields = clazz.fields();
836 for (int j = 0, jlim = fields.length; j < jlim; ++ j) {
837 FieldDoc field = fields[j];
840 ClassDoc fieldType = field.type().asClassDoc();
841 if (null != fieldType) {
842 addUsedBy(_usedClassToPackagesMap,
843 fieldType, UsageType.FIELD_OF_TYPE,
844 field, clazz.containingPackage());
848 MethodDoc[] methods = clazz.methods();
849 for (int j = 0, jlim = methods.length; j < jlim; ++ j) {
850 MethodDoc method = methods[j];
852 // methods with return type
854 ClassDoc returnType = method.returnType().asClassDoc();
855 if (null != returnType) {
856 addUsedBy(_usedClassToPackagesMap,
857 returnType, UsageType.METHOD_WITH_RETURN_TYPE,
858 method, clazz.containingPackage());
860 Parameter[] parameters = method.parameters();
861 for (int k=0; k<parameters.length; ++k) {
863 // methods with parameter type
865 Parameter parameter = parameters[k];
866 ClassDoc parameterType = parameter.type().asClassDoc();
867 if (null != parameterType) {
868 addUsedBy(_usedClassToPackagesMap,
869 parameterType, UsageType.METHOD_WITH_PARAMETER_TYPE,
870 method, clazz.containingPackage());
874 // methods which throw
876 ClassDoc[] thrownExceptions = method.thrownExceptions();
877 for (int k = 0, klim = thrownExceptions.length; k < klim; ++ k) {
878 ClassDoc thrownException = thrownExceptions[k];
879 addUsedBy(_usedClassToPackagesMap,
880 thrownException, UsageType.METHOD_WITH_THROWN_TYPE,
881 method, clazz.containingPackage());
885 ConstructorDoc[] constructors = clazz.constructors();
886 for (int j = 0, jlim = constructors.length; j < jlim; ++ j) {
888 ConstructorDoc constructor = constructors[j];
890 Parameter[] parameters = constructor.parameters();
891 for (int k = 0, klim = parameters.length; k < klim; ++ k) {
893 // constructors with parameter type
895 Parameter parameter = parameters[k];
896 ClassDoc parameterType = parameter.type().asClassDoc();
897 if (null != parameterType) {
898 addUsedBy(_usedClassToPackagesMap,
899 parameterType, UsageType.CONSTRUCTOR_WITH_PARAMETER_TYPE,
900 constructor, clazz.containingPackage());
904 // constructors which throw
906 ClassDoc[] thrownExceptions = constructor.thrownExceptions();
907 for (int k = 0, klim = thrownExceptions.length; k < klim; ++ k) {
908 ClassDoc thrownException = thrownExceptions[k];
909 addUsedBy(_usedClassToPackagesMap,
910 thrownException, UsageType.CONSTRUCTOR_WITH_THROWN_TYPE,
911 constructor, clazz.containingPackage());
915 return _usedClassToPackagesMap;
918 private Map usedClassToPackagesMap = null;
920 protected Map getUsageOfClass(ClassDoc classDoc)
922 if (null == this.usedClassToPackagesMap) {
923 this.usedClassToPackagesMap = collectUsage();
925 return (Map)this.usedClassToPackagesMap.get(classDoc);
928 protected static class UsageType
929 implements Comparable
931 public static final UsageType CLASS_DERIVED_FROM = new UsageType("class-derived-from");
932 public static final UsageType CLASS_IMPLEMENTING = new UsageType("class-implementing");
933 public static final UsageType FIELD_OF_TYPE = new UsageType("field-of-type");
934 public static final UsageType METHOD_WITH_RETURN_TYPE = new UsageType("method-with-return-type");
935 public static final UsageType METHOD_WITH_PARAMETER_TYPE = new UsageType("method-with-parameter-type");
936 public static final UsageType METHOD_WITH_THROWN_TYPE = new UsageType("method-with-thrown-type");
937 public static final UsageType CONSTRUCTOR_WITH_PARAMETER_TYPE = new UsageType("constructor-with-parameter-type");
938 public static final UsageType CONSTRUCTOR_WITH_THROWN_TYPE = new UsageType("constructor-with-thrown-type");
941 private UsageType(String id)
946 public int compareTo(Object other)
948 return this.id.compareTo(((UsageType)other).id);
951 public String toString() {
952 return "UsageType{id=" + id + "}";
955 public String getId() {
960 private ResourceBundle resources;
962 protected String getString(String key)
964 if (null == resources) {
965 Locale currentLocale = Locale.getDefault();
968 = ResourceBundle.getBundle("htmldoclet.HtmlDoclet", currentLocale);
971 return resources.getString(key);
974 protected String format(String key, String value1)
976 return MessageFormat.format(getString(key), new Object[] { value1 });
979 protected List getPackageGroups()
981 return packageGroups;
984 protected void copyDocFiles(File sourceDir, File targetDir)
987 File sourceDocFiles = new File(sourceDir, "doc-files");
988 File targetDocFiles = new File(targetDir, "doc-files");
990 if (sourceDocFiles.exists()) {
991 IOToolkit.copyDirectory(sourceDocFiles,
993 optionDocFilesSubDirs.getValue(),
994 optionExcludeDocFilesSubDir.getComponents());
998 private Set sourcePaths;
1001 * Try to determine the source directory for the given package by
1002 * looking at the path specified by -sourcepath, or the current
1003 * directory if -sourcepath hasn't been specified.
1005 * @throws IOException if the source directory couldn't be
1008 * @return List of File
1010 protected List getPackageSourceDirs(PackageDoc packageDoc)
1013 if (null == sourcePaths) {
1014 for (int i=0; i<rootDoc.options().length; ++i) {
1015 if ("-sourcepath".equals(rootDoc.options()[i][0])
1016 || "-s".equals(rootDoc.options()[i][0])) {
1017 sourcePaths = new LinkedHashSet();
1018 String sourcepathString = rootDoc.options()[i][1];
1019 StringTokenizer st = new StringTokenizer(sourcepathString, File.pathSeparator);
1020 while (st.hasMoreTokens()) {
1021 sourcePaths.add(new File(st.nextToken()));
1025 if (null == sourcePaths) {
1026 sourcePaths = new LinkedHashSet();
1027 sourcePaths.add(new File(System.getProperty("user.dir")));
1031 String packageSubDir = packageDoc.name().replace('.', File.separatorChar);
1032 Iterator it = sourcePaths.iterator();
1033 List result = new LinkedList();
1034 while (it.hasNext()) {
1035 File pathComponent = (File)it.next();
1036 File packageDir = new File(pathComponent, packageSubDir);
1037 if (packageDir.exists()) {
1038 result.add(packageDir);
1041 if (result.isEmpty()) {
1042 throw new IOException("Couldn't locate source directory for package " + packageDoc.name());
1049 protected File getSourceFile(ClassDoc classDoc)
1052 List packageDirs = getPackageSourceDirs(classDoc.containingPackage());
1053 Iterator it = packageDirs.iterator();
1054 while (it.hasNext()) {
1055 File packageDir = (File)it.next();
1056 File sourceFile = new File(packageDir, getOuterClassDoc(classDoc).name() + ".java");
1057 if (sourceFile.exists()) {
1062 throw new IOException("Couldn't locate source file for class " + classDoc.qualifiedTypeName());
1065 protected void printError(String error)
1067 if (null != rootDoc) {
1068 rootDoc.printError(error);
1071 System.err.println("ERROR: "+error);
1075 protected void printWarning(String warning)
1077 if (null != rootDoc) {
1078 rootDoc.printWarning(warning);
1081 System.err.println("WARNING: "+warning);
1085 protected void printNotice(String notice)
1087 if (null != rootDoc) {
1088 rootDoc.printNotice(notice);
1091 System.err.println(notice);
1095 protected static ClassDoc getOuterClassDoc(ClassDoc classDoc)
1097 while (null != classDoc.containingClass()) {
1098 classDoc = classDoc.containingClass();
1103 private SortedSet allPackages;
1105 protected Set getAllPackages()
1107 if (null == this.allPackages) {
1108 allPackages = new TreeSet();
1109 PackageDoc[] specifiedPackages = rootDoc.specifiedPackages();
1110 for (int i=0; i<specifiedPackages.length; ++i) {
1111 allPackages.add(specifiedPackages[i]);
1113 ClassDoc[] specifiedClasses = rootDoc.specifiedClasses();
1114 for (int i=0; i<specifiedClasses.length; ++i) {
1115 allPackages.add(specifiedClasses[i].containingPackage());
1118 return this.allPackages;
1121 protected boolean omitPackageQualifier(PackageDoc packageDoc)
1123 if (!optionNoQualifier.isSpecified()) {
1127 return optionNoQualifier.match(packageDoc);
1131 protected String possiblyQualifiedName(Type type)
1133 if (null == type.asClassDoc()
1134 || !omitPackageQualifier(type.asClassDoc().containingPackage())) {
1135 return type.qualifiedTypeName();
1138 return type.typeName();
1142 protected static class InterfaceRelation
1144 public Set superInterfaces;
1145 public Set subInterfaces;
1146 public Set implementingClasses;
1148 public InterfaceRelation()
1150 superInterfaces = new TreeSet();
1151 subInterfaces = new TreeSet();
1152 implementingClasses = new TreeSet();
1156 private void addAllInterfaces(ClassDoc classDoc, Set allInterfaces)
1158 ClassDoc[] interfaces = classDoc.interfaces();
1159 for (int i=0; i<interfaces.length; ++i) {
1160 allInterfaces.add(interfaces[i]);
1161 addAllInterfaces(interfaces[i], allInterfaces);
1165 private Map allSubClasses;
1167 protected Map getAllSubClasses()
1169 if (null == allSubClasses) {
1170 allSubClasses = new HashMap();
1172 ClassDoc[] classDocs = getRootDoc().classes();
1173 for (int i=0; i<classDocs.length; ++i) {
1174 if (!classDocs[i].isInterface()) {
1175 for (ClassDoc cd = classDocs[i].superclass();
1177 cd = cd.superclass()) {
1179 if (!cd.qualifiedTypeName().equals("java.lang.Object")) {
1180 List subClasses = (List)allSubClasses.get(cd);
1181 if (null == subClasses) {
1182 subClasses = new LinkedList();
1183 allSubClasses.put(cd, subClasses);
1185 subClasses.add(classDocs[i]);
1191 return allSubClasses;
1194 private Map interfaceRelations;
1196 private void addToInterfaces(ClassDoc classDoc, ClassDoc[] interfaces)
1198 for (int i=0; i<interfaces.length; ++i) {
1199 InterfaceRelation interfaceRelation
1200 = (InterfaceRelation)interfaceRelations.get(interfaces[i]);
1201 if (null == interfaceRelation) {
1202 interfaceRelation = new InterfaceRelation();
1203 interfaceRelations.put(interfaces[i], interfaceRelation);
1205 interfaceRelation.implementingClasses.add(classDoc);
1206 addToInterfaces(classDoc, interfaces[i].interfaces());
1210 protected Map getInterfaceRelations()
1212 if (null == interfaceRelations) {
1213 interfaceRelations = new HashMap();
1215 ClassDoc[] classDocs = getRootDoc().classes();
1216 for (int i=0; i<classDocs.length; ++i) {
1217 if (classDocs[i].isInterface()) {
1218 InterfaceRelation relation = new InterfaceRelation();
1219 addAllInterfaces(classDocs[i], relation.superInterfaces);
1220 interfaceRelations.put(classDocs[i], relation);
1224 Iterator it = interfaceRelations.keySet().iterator();
1225 while (it.hasNext()) {
1226 ClassDoc interfaceDoc = (ClassDoc)it.next();
1227 InterfaceRelation relation
1228 = (InterfaceRelation)interfaceRelations.get(interfaceDoc);
1229 Iterator superIt = relation.superInterfaces.iterator();
1230 while (superIt.hasNext()) {
1231 ClassDoc superInterfaceDoc = (ClassDoc)superIt.next();
1232 InterfaceRelation superRelation
1233 = (InterfaceRelation)interfaceRelations.get(superInterfaceDoc);
1234 if (null != superRelation) {
1235 superRelation.subInterfaces.add(interfaceDoc);
1240 for (int i=0; i<classDocs.length; ++i) {
1241 if (!classDocs[i].isInterface()) {
1242 for (ClassDoc cd = classDocs[i]; null != cd; cd = cd.superclass()) {
1243 addToInterfaces(classDocs[i], cd.interfaces());
1249 return interfaceRelations;
1252 private Map sortedMethodMap = new HashMap();
1254 protected MethodDoc[] getSortedMethods(ClassDoc classDoc)
1256 MethodDoc[] result = (MethodDoc[])sortedMethodMap.get(classDoc);
1257 if (null == result) {
1258 MethodDoc[] methods = classDoc.methods();
1259 result = (MethodDoc[])methods.clone();
1260 Arrays.sort(result);
1266 private Map sortedConstructorMap = new HashMap();
1268 protected ConstructorDoc[] getSortedConstructors(ClassDoc classDoc)
1270 ConstructorDoc[] result = (ConstructorDoc[])sortedConstructorMap.get(classDoc);
1271 if (null == result) {
1272 ConstructorDoc[] constructors = classDoc.constructors();
1273 result = (ConstructorDoc[])constructors.clone();
1274 Arrays.sort(result);
1280 private Map sortedFieldMap = new HashMap();
1282 protected FieldDoc[] getSortedFields(ClassDoc classDoc)
1284 FieldDoc[] result = (FieldDoc[])sortedFieldMap.get(classDoc);
1285 if (null == result) {
1286 FieldDoc[] fields = classDoc.fields();
1287 result = (FieldDoc[])fields.clone();
1288 Arrays.sort(result);
1294 private Map sortedInnerClassMap = new HashMap();
1296 protected ClassDoc[] getSortedInnerClasses(ClassDoc classDoc)
1298 ClassDoc[] result = (ClassDoc[])sortedInnerClassMap.get(classDoc);
1299 if (null == result) {
1300 ClassDoc[] innerClasses = classDoc.innerClasses();
1301 result = (ClassDoc[])innerClasses.clone();
1302 Arrays.sort(result);
1308 protected abstract String renderTag(String tagName, Tag[] tags, TagletContext context);
1310 protected abstract String getDocletVersion();
1312 protected SortedSet getThrownExceptions(ExecutableMemberDoc execMemberDoc)
1314 SortedSet result = new TreeSet();
1315 ClassDoc[] thrownExceptions = execMemberDoc.thrownExceptions();
1316 for (int j=0; j<thrownExceptions.length; ++j) {
1317 result.add(thrownExceptions[j]);
1322 protected boolean isUncheckedException(ClassDoc classDoc)
1324 if (classDoc.isException()) {
1325 while (null != classDoc) {
1326 if (classDoc.qualifiedTypeName().equals("java.lang.RuntimeException")) {
1329 classDoc = classDoc.superclass();
1338 protected FieldDoc findField(ClassDoc classDoc, String fieldName)
1340 for (ClassDoc cd = classDoc; cd != null; cd = cd.superclass()) {
1341 FieldDoc[] fields = cd.fields(false);
1342 for (int i=0; i<fields.length; ++i) {
1343 if (fields[i].name().equals(fieldName)) {
1351 private Map implementedInterfacesCache = new HashMap();
1353 protected Set getImplementedInterfaces(ClassDoc classDoc)
1355 Set result = (Set)implementedInterfacesCache.get(classDoc);
1356 if (null == result) {
1357 result = new TreeSet();
1359 for (ClassDoc cd = classDoc; cd != null; cd = cd.superclass()) {
1360 ClassDoc[] interfaces = cd.interfaces();
1361 for (int i=0; i<interfaces.length; ++i) {
1362 result.add(interfaces[i]);
1363 InterfaceRelation relation
1364 = (InterfaceRelation)getInterfaceRelations().get(interfaces[i]);
1365 if (null != relation) {
1366 result.addAll(relation.superInterfaces);
1371 implementedInterfacesCache.put(classDoc, result);
1377 protected boolean isSinglePackage()
1379 return getAllPackages().size() <= 1;
1382 protected PackageDoc getSinglePackage()
1384 return (PackageDoc)getAllPackages().iterator().next();