Imported Upstream version 4.8.1
[platform/upstream/gcc48.git] / libjava / classpath / tools / gnu / classpath / tools / doclets / AbstractDoclet.java
1 /* gnu.classpath.tools.doclets.AbstractDoclet
2    Copyright (C) 2004, 2012 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 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
24 combination.
25
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. */
37
38 package gnu.classpath.tools.doclets;
39
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;
52
53 import com.sun.tools.doclets.Taglet;
54
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;
64
65 import gnu.classpath.tools.IOToolkit;
66 import gnu.classpath.tools.FileSystemClassLoader;
67
68 import java.io.File;
69 import java.io.IOException;
70
71 import java.lang.reflect.Method;
72 import java.lang.reflect.Modifier;
73 import java.lang.reflect.InvocationTargetException;
74
75 import java.text.MessageFormat;
76
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;
88 import java.util.Map;
89 import java.util.ResourceBundle;
90 import java.util.Set;
91 import java.util.SortedSet;
92 import java.util.StringTokenizer;
93 import java.util.TreeMap;
94 import java.util.TreeSet;
95
96 /**
97  *  An abstract Doclet implementation with helpers for common tasks
98  *  performed by Doclets.
99  */
100 public abstract class AbstractDoclet
101 {
102    /**
103     *  Mapping from tag type to Taglet for user Taglets specified on
104     *  the command line.
105     */
106    protected Map<String,Taglet> tagletMap = new LinkedHashMap<String,Taglet>();
107
108    /**
109     *  Stores the package groups specified in the user
110     *  options. Contains objects of type PackageGroup.
111     */
112    private List<PackageGroup> packageGroups = new LinkedList<PackageGroup>();
113
114    /**
115     *  Keeps track of the tags mentioned by the user during option
116     *  processiong so that an error can be emitted if a tag is
117     *  mentioned more than once.
118     */
119    private List<Taglet> mentionedTags = new LinkedList<Taglet>();
120
121    public static int optionLength(String option) {
122       return instance.getOptionLength(option);
123    }
124
125    public static boolean validOptions(String[][] options) {
126       return true;
127    }
128
129    private static AbstractDoclet instance;
130
131    protected static void setInstance(AbstractDoclet instance)
132    {
133       AbstractDoclet.instance = instance;
134    }
135
136    protected abstract void run()
137       throws DocletConfigurationException, IOException;
138
139    public static boolean start(RootDoc rootDoc)
140    {
141       try {
142
143          instance.startInstance(rootDoc);
144          return true;
145       }
146       catch (DocletConfigurationException e) {
147          instance.printError(e.getMessage());
148          return false;
149       }
150       catch (Exception e) {
151          e.printStackTrace();
152          return false;
153       }
154    }
155
156    protected RootDoc getRootDoc()
157    {
158       return this.rootDoc;
159    }
160
161    private RootDoc rootDoc;
162
163    protected abstract InlineTagRenderer getInlineTagRenderer();
164
165    private void startInstance(RootDoc rootDoc)
166       throws DocletConfigurationException, IOException
167    {
168       this.rootDoc = rootDoc;
169
170       // Set the default Taglet order
171
172       registerTaglet(new VersionTaglet());
173       registerTaglet(new AuthorTaglet());
174       registerTaglet(new SinceTaglet(getInlineTagRenderer()));
175       registerTaglet(new StandardTaglet("serial"));
176       registerTaglet(new StandardTaglet("deprecated"));
177       registerTaglet(new StandardTaglet("see"));
178       registerTaglet(new StandardTaglet("param"));
179       registerTaglet(new StandardTaglet("return"));
180
181       registerTaglet(new ValueTaglet());
182       registerTaglet(new CodeTaglet());
183
184       // Process command line options
185
186       for (int i=0, ilim=rootDoc.options().length; i<ilim; ++i) {
187
188          String[] optionArr = rootDoc.options()[i];
189          String _optionTag = optionArr[0];
190
191          DocletOption option = (DocletOption)nameToOptionMap.get(_optionTag.toLowerCase());
192
193          if (null != option) {
194             option.set(optionArr);
195          }
196       }
197
198       // Enable/disable standard taglets based on user input
199
200       AuthorTaglet.setTagletEnabled(optionAuthor.getValue());
201       VersionTaglet.setTagletEnabled(optionVersion.getValue());
202       SinceTaglet.setTagletEnabled(!optionNoSince.getValue());
203       DeprecatedTaglet.setTagletEnabled(!optionNoDeprecated.getValue());
204
205       if (!getTargetDirectory().exists()) {
206          if (!getTargetDirectory().mkdirs()) {
207             throw new DocletConfigurationException("Cannot create target directory "
208                                                    + getTargetDirectory());
209          }
210       }
211
212       run();
213    }
214
215    public File getTargetDirectory()
216    {
217       return optionTargetDirectory.getValue();
218    }
219
220    private DocletOptionFile optionTargetDirectory =
221      new DocletOptionFile("-d",
222                           new File(System.getProperty("user.dir")));
223
224    private DocletOptionFlag optionAuthor =
225      new DocletOptionFlag("-author");
226
227    private DocletOptionFlag optionVersion =
228      new DocletOptionFlag("-version");
229
230    private DocletOptionFlag optionNoSince =
231      new DocletOptionFlag("-nosince");
232
233    private DocletOptionFlag optionNoDeprecated =
234      new DocletOptionFlag("-nodeprecated");
235
236    private DocletOptionGroup optionGroup =
237      new DocletOptionGroup("-group");
238
239    private DocletOptionPackageWildcard optionNoQualifier =
240      new DocletOptionPackageWildcard("-noqualifier", true);
241
242    private DocletOptionFlag optionDocFilesSubDirs =
243      new DocletOptionFlag("-docfilessubdirs");
244
245    private DocletOptionColonSeparated optionExcludeDocFilesSubDir =
246      new DocletOptionColonSeparated("-excludedocfilessubdir");
247
248    private DocletOptionTag optionTaglet =
249      new DocletOptionTag("-taglet");
250
251    private DocletOptionTag optionTag =
252      new DocletOptionTag("-tag");
253
254    private class DocletOptionGroup
255       extends DocletOption
256    {
257       DocletOptionGroup(String optionName)
258       {
259          super(optionName);
260       }
261
262       public int getLength()
263       {
264          return 3;
265       }
266
267       public boolean set(String[] optionArr)
268       {
269          try {
270             PackageMatcher packageMatcher = new PackageMatcher();
271
272             StringTokenizer tokenizer = new StringTokenizer(optionArr[2], ":");
273             while (tokenizer.hasMoreTokens()) {
274                String packageWildcard = tokenizer.nextToken();
275                packageMatcher.addWildcard(packageWildcard);
276             }
277
278             SortedSet<PackageDoc> groupPackages = packageMatcher.filter(rootDoc.specifiedPackages());
279
280             packageGroups.add(new PackageGroup(optionArr[1], groupPackages));
281
282             return true;
283          }
284          catch (InvalidPackageWildcardException e) {
285             return false;
286          }
287       }
288    }
289
290    private class DocletOptionTag
291       extends DocletOption
292    {
293       DocletOptionTag(String optionName)
294       {
295          super(optionName);
296       }
297
298       public int getLength()
299       {
300          return 2;
301       }
302
303       public boolean set(String[] optionArr)
304       {
305          String tagSpec = optionArr[1];
306          boolean validTagSpec = false;
307          int ndx1 = tagSpec.indexOf(':');
308          if (ndx1 < 0) {
309             Taglet taglet = (Taglet)tagletMap.get(tagSpec);
310             if (null == taglet) {
311                printError("There is no standard tag '" + tagSpec + "'.");
312             }
313             else {
314                if (mentionedTags.contains(taglet)) {
315                   printError("Tag '" + tagSpec + "' has been added or moved before.");
316                }
317                else {
318                   mentionedTags.add(taglet);
319
320                   // re-append taglet
321                   tagletMap.remove(tagSpec);
322                   tagletMap.put(tagSpec, taglet);
323                }
324             }
325          }
326          else {
327             int ndx2 = tagSpec.indexOf(':', ndx1 + 1);
328             if (ndx2 > ndx1 && ndx2 < tagSpec.length() - 1) {
329                String tagName = tagSpec.substring(0, ndx1);
330                String tagHead = null;
331                if (tagSpec.charAt(ndx2 + 1) == '\"') {
332                   if (tagSpec.charAt(tagSpec.length() - 1) == '\"') {
333                      tagHead = tagSpec.substring(ndx2 + 2, tagSpec.length() - 1);
334                      validTagSpec = true;
335                   }
336                }
337                else {
338                   tagHead = tagSpec.substring(ndx2 + 1);
339                   validTagSpec = true;
340                }
341
342                boolean tagScopeOverview = false;
343                boolean tagScopePackages = false;
344                boolean tagScopeTypes = false;
345                boolean tagScopeConstructors = false;
346                boolean tagScopeMethods = false;
347                boolean tagScopeFields = false;
348                boolean tagDisabled = false;
349
350             tag_option_loop:
351                for (int n=ndx1+1; n<ndx2; ++n) {
352                   switch (tagSpec.charAt(n)) {
353                   case 'X':
354                      tagDisabled = true;
355                      break;
356                   case 'a':
357                      tagScopeOverview = true;
358                      tagScopePackages = true;
359                      tagScopeTypes = true;
360                      tagScopeConstructors = true;
361                      tagScopeMethods = true;
362                      tagScopeFields = true;
363                      break;
364                   case 'o':
365                      tagScopeOverview = true;
366                      break;
367                   case 'p':
368                      tagScopePackages = true;
369                      break;
370                   case 't':
371                      tagScopeTypes = true;
372                      break;
373                   case 'c':
374                      tagScopeConstructors = true;
375                      break;
376                   case 'm':
377                      tagScopeMethods = true;
378                      break;
379                   case 'f':
380                      tagScopeFields = true;
381                      break;
382                   default:
383                      validTagSpec = false;
384                      break tag_option_loop;
385                   }
386                }
387
388                if (validTagSpec) {
389                   GenericTaglet taglet
390                      = new GenericTaglet(tagName,
391                                          tagHead,
392                                          tagScopeOverview,
393                                          tagScopePackages,
394                                          tagScopeTypes,
395                                          tagScopeConstructors,
396                                          tagScopeMethods,
397                                          tagScopeFields);
398                   taglet.setTagletEnabled(!tagDisabled);
399                   taglet.register(tagletMap);
400                   mentionedTags.add(taglet);
401                }
402             }
403          }
404          if (!validTagSpec) {
405             printError("Value for option -tag must be in format \"<tagname>:Xaoptcmf:<taghead>\".");
406          }
407          return validTagSpec;
408       }
409    }
410
411    private DocletOption[] commonOptions =
412       {
413          optionTargetDirectory,
414          optionAuthor,
415          optionVersion,
416          optionNoSince,
417          optionNoDeprecated,
418          optionGroup,
419          optionDocFilesSubDirs,
420          optionExcludeDocFilesSubDir,
421          optionTaglet,
422          optionTag,
423       };
424
425    private void registerOptions()
426    {
427       if (!optionsRegistered) {
428          for (int i=0; i<commonOptions.length; ++i) {
429             DocletOption option = commonOptions[i];
430             registerOption(option);
431          }
432          DocletOption[] docletOptions = getOptions();
433          for (int i=0; i<docletOptions.length; ++i) {
434             DocletOption option = docletOptions[i];
435             registerOption(option);
436          }
437          optionsRegistered = true;
438       }
439    }
440
441    protected abstract DocletOption[] getOptions();
442
443    private boolean optionsRegistered = false;
444
445    private void registerOption(DocletOption option)
446    {
447       nameToOptionMap.put(option.getName(), option);
448    }
449
450    private Map<String,DocletOption> nameToOptionMap = new HashMap<String,DocletOption>();
451
452    private int getOptionLength(String optionName)
453    {
454       registerOptions();
455       DocletOption option = nameToOptionMap.get(optionName.toLowerCase());
456       if (null != option) {
457          return option.getLength();
458       }
459       else {
460          return -1;
461       }
462    }
463
464    protected List<ClassDoc> getKnownDirectSubclasses(ClassDoc classDoc)
465    {
466       List<ClassDoc> result = new LinkedList<ClassDoc>();
467       if (!"java.lang.Object".equals(classDoc.qualifiedName())) {
468          ClassDoc[] classes = rootDoc.classes();
469          for (int i=0; i<classes.length; ++i) {
470             if (classDoc == classes[i].superclass()) {
471                result.add(classes[i]);
472             }
473          }
474       }
475       return result;
476    }
477
478    protected static class IndexKey
479       implements Comparable<IndexKey>
480    {
481       private String name;
482       private String lowerName;
483
484       public IndexKey(String name)
485       {
486          this.name = name;
487          this.lowerName = name.toLowerCase();
488       }
489
490       public boolean equals(Object other)
491       {
492          return this.lowerName.equals(((IndexKey)other).lowerName);
493       }
494
495       public int hashCode()
496       {
497          return lowerName.hashCode();
498       }
499
500       public int compareTo(IndexKey ik)
501       {
502          return lowerName.compareTo(ik.lowerName);
503       }
504
505       public String getName()
506       {
507          return name;
508       }
509    }
510
511    private Map<Character,List<Doc>> categorizedIndex;
512
513    protected Map<Character,List<Doc>> getCategorizedIndex()
514    {
515       if (null == categorizedIndex) {
516          categorizedIndex = new LinkedHashMap<Character,List<Doc>>();
517
518          Map<IndexKey,Doc> indexMap = getIndexByName();
519          LinkedList<IndexKey> keys = new LinkedList<IndexKey>(); //indexMap.keySet().size());
520          keys.addAll(indexMap.keySet());
521          Collections.sort(keys);
522          Iterator<IndexKey> it = keys.iterator(); //indexMap.keySet().iterator();
523          char previousCategoryLetter = '\0';
524          Character keyLetter = null;
525          while (it.hasNext()) {
526             IndexKey key = it.next();
527             char firstChar = Character.toUpperCase(key.getName().charAt(0));
528             if (firstChar != previousCategoryLetter) {
529                keyLetter = new Character(firstChar);
530                previousCategoryLetter = firstChar;
531                categorizedIndex.put(keyLetter, new LinkedList<Doc>());
532             }
533             List<Doc> letterList = categorizedIndex.get(keyLetter);
534             letterList.add(indexMap.get(key));
535          }
536       }
537
538       return categorizedIndex;
539    }
540
541
542    private Map<IndexKey,Doc> indexByName;
543
544    protected Map<IndexKey,Doc> getIndexByName()
545    {
546       if (null == indexByName) {
547          // Create index
548
549          // Collect index
550
551          indexByName = new HashMap<IndexKey,Doc>(); //TreeMap();
552
553          // Add packages to index
554
555          PackageDoc[] packages = rootDoc.specifiedPackages();
556          for (int i=0, ilim=packages.length; i<ilim; ++i) {
557             PackageDoc c = packages[i];
558             if (c.name().length() > 0) {
559                indexByName.put(new IndexKey(c.name()), c);
560             }
561          }
562
563          // Add classes, fields and methods to index
564
565          ClassDoc[] sumclasses = rootDoc.classes();
566          for (int i=0, ilim=sumclasses.length; i<ilim; ++i) {
567             ClassDoc c = sumclasses[i];
568             if (null == c.containingClass()) {
569                indexByName.put(new IndexKey(c.name() + " " + c.containingPackage().name()), c);
570             }
571             else {
572                indexByName.put(new IndexKey(c.name().substring(c.containingClass().name().length() + 1)
573                                             + " " + c.containingClass().name() + " " + c.containingPackage().name()), c);
574             }
575             FieldDoc[] fields = c.fields();
576             for (int j=0, jlim=fields.length; j<jlim; ++j) {
577                indexByName.put(new IndexKey(fields[j].name() + " " + fields[j].containingClass().name() + " " + fields[j].containingPackage().name()), fields[j]);
578             }
579             MethodDoc[] methods = c.methods();
580             for (int j=0, jlim=methods.length; j<jlim; ++j) {
581                MethodDoc method = methods[j];
582                indexByName.put(new IndexKey(method.name() + method.signature() + " " + method.containingClass().name() + " " + method.containingPackage().name()), method);
583             }
584             ConstructorDoc[] constructors = c.constructors();
585             for (int j=0, jlim=constructors.length; j<jlim; ++j) {
586                ConstructorDoc constructor = constructors[j];
587                indexByName.put(new IndexKey(constructor.name() + constructor.signature() + " " + constructor.containingClass().name() + " " + constructor.containingPackage().name()), constructor);
588             }
589          }
590       }
591       return indexByName;
592    }
593
594    private void registerTaglet(Taglet taglet)
595    {
596       tagletMap.put(taglet.getName(), taglet);
597    }
598
599    protected void printTaglets(Tag[] tags, TagletContext context, TagletPrinter output, boolean inline)
600    {
601       for (Iterator<String> it = tagletMap.keySet().iterator(); it.hasNext(); ) {
602          String tagName = it.next();
603          Taglet taglet = tagletMap.get(tagName);
604          Doc doc = context.getDoc();
605          if (inline == taglet.isInlineTag()
606              && ((doc == null
607                   && taglet.inOverview())
608                  || (doc != null
609                      && ((doc.isConstructor() && taglet.inConstructor())
610                          || (doc.isField() && taglet.inField())
611                          || (doc.isMethod() && taglet.inMethod())
612                          || (doc instanceof PackageDoc && taglet.inPackage())
613                          || ((doc.isClass() || doc.isInterface()) && taglet.inType()))))) {
614
615             List<Tag> tagsOfThisType = new LinkedList<Tag>();
616             for (int i=0; i<tags.length; ++i) {
617                if (tags[i].name().substring(1).equals(tagName)) {
618                   tagsOfThisType.add(tags[i]);
619                }
620             }
621
622             Tag[] tagletTags = tagsOfThisType.toArray(new Tag[tagsOfThisType.size()]);
623
624             String tagletString;
625             if (taglet instanceof StandardTaglet) {
626                tagletString = renderTag(tagName, tagletTags, context);
627             }
628             else if (taglet instanceof GnuExtendedTaglet) {
629                tagletString = ((GnuExtendedTaglet)taglet).toString(tagletTags, context);
630             }
631             else {
632                tagletString = taglet.toString(tagletTags);
633             }
634             if (null != tagletString) {
635                output.printTagletString(tagletString);
636             }
637          }
638       }
639    }
640
641    protected void printInlineTaglet(Tag tag, TagletContext context, TagletPrinter output)
642    {
643       Taglet taglet = (Taglet)tagletMap.get(tag.name().substring(1));
644       if (null != taglet) {
645          String tagletString;
646          if (taglet instanceof GnuExtendedTaglet) {
647             tagletString = ((GnuExtendedTaglet)taglet).toString(tag, context);
648          }
649          else {
650             tagletString = taglet.toString(tag);
651          }
652          if (null != tagletString) {
653             output.printTagletString(tagletString);
654          }
655       }
656       else {
657          printWarning("Unknown tag: " + tag.name());
658       }
659    }
660
661    protected void printMainTaglets(Tag[] tags, TagletContext context, TagletPrinter output)
662    {
663       printTaglets(tags, context, output, false);
664    }
665
666    /**
667     *  @param usedClassToPackagesMap  ClassDoc to (PackageDoc to (UsageType to (Set of Doc)))
668     */
669    private void addUsedBy(Map<ClassDoc,Map<PackageDoc,Map<UsageType,Set<Doc>>>> usedClassToPackagesMap,
670                           ClassDoc usedClass, UsageType usageType, Doc user, PackageDoc userPackage)
671    {
672       Map<PackageDoc,Map<UsageType,Set<Doc>>> packageToUsageTypeMap = usedClassToPackagesMap.get(usedClass);
673       if (null == packageToUsageTypeMap) {
674          packageToUsageTypeMap = new HashMap<PackageDoc,Map<UsageType,Set<Doc>>>();
675          usedClassToPackagesMap.put(usedClass, packageToUsageTypeMap);
676       }
677
678       Map<UsageType,Set<Doc>> usageTypeToUsersMap = packageToUsageTypeMap.get(userPackage);
679       if (null == usageTypeToUsersMap) {
680         usageTypeToUsersMap = new TreeMap<UsageType,Set<Doc>>();
681          packageToUsageTypeMap.put(userPackage, usageTypeToUsersMap);
682       }
683
684       Set<Doc> userSet = usageTypeToUsersMap.get(usageType);
685       if (null == userSet) {
686          userSet = new TreeSet<Doc>(); // FIXME: we need the collator from Main here
687          usageTypeToUsersMap.put(usageType, userSet);
688       }
689       userSet.add(user);
690    }
691
692    /**
693     *  Create the cross reference database.
694     */
695    private Map collectUsage() {
696
697       Map<ClassDoc,Map<PackageDoc,Map<UsageType,Set<Doc>>>> _usedClassToPackagesMap =
698         new HashMap<ClassDoc,Map<PackageDoc,Map<UsageType,Set<Doc>>>>();
699
700       ClassDoc[] classes = rootDoc.classes();
701       for (int i = 0, ilim = classes.length; i < ilim; ++ i) {
702          ClassDoc clazz = classes[i];
703
704          if (clazz.isInterface()) {
705             // classes implementing
706             InterfaceRelation relation
707                = (InterfaceRelation)getInterfaceRelations().get(clazz);
708             Iterator<ClassDoc> it = relation.implementingClasses.iterator();
709             while (it.hasNext()) {
710                ClassDoc implementor = it.next();
711                addUsedBy(_usedClassToPackagesMap,
712                          clazz, UsageType.CLASS_IMPLEMENTING, implementor, implementor.containingPackage());
713             }
714          }
715          else {
716             // classes derived from
717             for (ClassDoc superclass = clazz.superclass(); superclass != null;
718                  superclass = superclass.superclass()) {
719                addUsedBy(_usedClassToPackagesMap,
720                          superclass, UsageType.CLASS_DERIVED_FROM, clazz, clazz.containingPackage());
721             }
722          }
723
724          FieldDoc[] fields = clazz.fields();
725          for (int j = 0, jlim = fields.length; j < jlim; ++ j) {
726             FieldDoc field = fields[j];
727
728             // fields of type
729             ClassDoc fieldType = field.type().asClassDoc();
730             if (null != fieldType) {
731                addUsedBy(_usedClassToPackagesMap,
732                          fieldType, UsageType.FIELD_OF_TYPE,
733                          field, clazz.containingPackage());
734             }
735          }
736
737          MethodDoc[] methods = clazz.methods();
738          for (int j = 0, jlim = methods.length; j < jlim; ++ j) {
739             MethodDoc method = methods[j];
740
741             // methods with return type
742
743             ClassDoc returnType = method.returnType().asClassDoc();
744             if (null != returnType) {
745                addUsedBy(_usedClassToPackagesMap,
746                          returnType, UsageType.METHOD_WITH_RETURN_TYPE,
747                          method, clazz.containingPackage());
748             }
749             Parameter[] parameters = method.parameters();
750             for (int k=0; k<parameters.length; ++k) {
751
752                // methods with parameter type
753
754                Parameter parameter = parameters[k];
755                ClassDoc parameterType = parameter.type().asClassDoc();
756                if (null != parameterType) {
757                   addUsedBy(_usedClassToPackagesMap,
758                             parameterType, UsageType.METHOD_WITH_PARAMETER_TYPE,
759                             method, clazz.containingPackage());
760                }
761             }
762
763             // methods which throw
764
765             ClassDoc[] thrownExceptions = method.thrownExceptions();
766             for (int k = 0, klim = thrownExceptions.length; k < klim; ++ k) {
767                ClassDoc thrownException = thrownExceptions[k];
768                addUsedBy(_usedClassToPackagesMap,
769                          thrownException, UsageType.METHOD_WITH_THROWN_TYPE,
770                          method, clazz.containingPackage());
771             }
772          }
773
774          ConstructorDoc[] constructors = clazz.constructors();
775          for (int j = 0, jlim = constructors.length; j < jlim; ++ j) {
776
777             ConstructorDoc constructor = constructors[j];
778
779             Parameter[] parameters = constructor.parameters();
780             for (int k = 0, klim = parameters.length; k < klim; ++ k) {
781
782                // constructors with parameter type
783
784                Parameter parameter = parameters[k];
785                ClassDoc parameterType = parameter.type().asClassDoc();
786                if (null != parameterType) {
787                   addUsedBy(_usedClassToPackagesMap,
788                             parameterType, UsageType.CONSTRUCTOR_WITH_PARAMETER_TYPE,
789                             constructor, clazz.containingPackage());
790                }
791             }
792
793             // constructors which throw
794
795             ClassDoc[] thrownExceptions = constructor.thrownExceptions();
796             for (int k = 0, klim = thrownExceptions.length; k < klim; ++ k) {
797                ClassDoc thrownException = thrownExceptions[k];
798                addUsedBy(_usedClassToPackagesMap,
799                          thrownException, UsageType.CONSTRUCTOR_WITH_THROWN_TYPE,
800                          constructor, clazz.containingPackage());
801             }
802          }
803       }
804       return _usedClassToPackagesMap;
805    }
806
807    private Map<ClassDoc,Map<PackageDoc,Map<UsageType,Set<Doc>>>> usedClassToPackagesMap = null;
808
809    protected Map<PackageDoc,Map<UsageType,Set<Doc>>> getUsageOfClass(ClassDoc classDoc)
810    {
811       if (null == this.usedClassToPackagesMap) {
812          this.usedClassToPackagesMap = collectUsage();
813       }
814       return this.usedClassToPackagesMap.get(classDoc);
815    }
816
817    protected static class UsageType
818       implements Comparable<UsageType>
819    {
820       public static final UsageType CLASS_DERIVED_FROM = new UsageType("class-derived-from");
821       public static final UsageType CLASS_IMPLEMENTING = new UsageType("class-implementing");
822       public static final UsageType FIELD_OF_TYPE = new UsageType("field-of-type");
823       public static final UsageType METHOD_WITH_RETURN_TYPE = new UsageType("method-with-return-type");
824       public static final UsageType METHOD_WITH_PARAMETER_TYPE = new UsageType("method-with-parameter-type");
825       public static final UsageType METHOD_WITH_THROWN_TYPE = new UsageType("method-with-thrown-type");
826       public static final UsageType CONSTRUCTOR_WITH_PARAMETER_TYPE = new UsageType("constructor-with-parameter-type");
827       public static final UsageType CONSTRUCTOR_WITH_THROWN_TYPE = new UsageType("constructor-with-thrown-type");
828       private String id;
829
830       private UsageType(String id)
831       {
832          this.id = id;
833       }
834
835       public int compareTo(UsageType ut)
836       {
837          return this.id.compareTo(ut.id);
838       }
839
840       public String toString() {
841          return "UsageType{id=" + id + "}";
842       }
843
844       public String getId() {
845          return id;
846       }
847    }
848
849    private ResourceBundle resources;
850
851    protected String getString(String key)
852    {
853       if (null == resources) {
854          Locale currentLocale = Locale.getDefault();
855
856          resources
857             = ResourceBundle.getBundle("htmldoclet.HtmlDoclet", currentLocale);
858       }
859
860       return resources.getString(key);
861    }
862
863    protected String format(String key, String value1)
864    {
865       return MessageFormat.format(getString(key), new Object[] { value1 });
866    }
867
868    protected List<PackageGroup> getPackageGroups()
869    {
870       return packageGroups;
871    }
872
873    protected void copyDocFiles(File sourceDir, File targetDir)
874       throws IOException
875    {
876       File sourceDocFiles = new File(sourceDir, "doc-files");
877       File targetDocFiles = new File(targetDir, "doc-files");
878
879       if (sourceDocFiles.exists()) {
880          IOToolkit.copyDirectory(sourceDocFiles,
881                                  targetDocFiles,
882                                  optionDocFilesSubDirs.getValue(),
883                                  optionExcludeDocFilesSubDir.getComponents());
884       }
885    }
886
887    private Set sourcePaths;
888
889    /**
890     *  Try to determine the source directory for the given package by
891     *  looking at the path specified by -sourcepath, or the current
892     *  directory if -sourcepath hasn't been specified.
893     *
894     *  @throws IOException if the source directory couldn't be
895     *  located.
896     *
897     *  @return List of File
898     */
899    protected List getPackageSourceDirs(PackageDoc packageDoc)
900       throws IOException
901    {
902       if (null == sourcePaths) {
903          for (int i=0; i<rootDoc.options().length; ++i) {
904             if ("-sourcepath".equals(rootDoc.options()[i][0])
905                 || "-s".equals(rootDoc.options()[i][0])) {
906                sourcePaths = new LinkedHashSet();
907                String sourcepathString = rootDoc.options()[i][1];
908                StringTokenizer st = new StringTokenizer(sourcepathString, File.pathSeparator);
909                while (st.hasMoreTokens()) {
910                   sourcePaths.add(new File(st.nextToken()));
911                }
912             }
913          }
914          if (null == sourcePaths) {
915             sourcePaths = new LinkedHashSet();
916             sourcePaths.add(new File(System.getProperty("user.dir")));
917          }
918       }
919
920       String packageSubDir = packageDoc.name().replace('.', File.separatorChar);
921       Iterator it = sourcePaths.iterator();
922       List result = new LinkedList();
923       while (it.hasNext()) {
924          File pathComponent = (File)it.next();
925          File packageDir = new File(pathComponent, packageSubDir);
926          if (packageDir.exists()) {
927             result.add(packageDir);
928          }
929       }
930       if (result.isEmpty()) {
931          throw new IOException("Couldn't locate source directory for package " + packageDoc.name());
932       }
933       else {
934          return result;
935       }
936    }
937
938    protected File getSourceFile(ClassDoc classDoc)
939       throws IOException
940    {
941       List packageDirs = getPackageSourceDirs(classDoc.containingPackage());
942       Iterator it = packageDirs.iterator();
943       while (it.hasNext()) {
944          File packageDir = (File)it.next();
945          File sourceFile = new File(packageDir, getOuterClassDoc(classDoc).name() + ".java");
946          if (sourceFile.exists()) {
947             return sourceFile;
948          }
949       }
950
951       throw new IOException("Couldn't locate source file for class " + classDoc.qualifiedTypeName());
952    }
953
954    protected void printError(String error)
955    {
956       if (null != rootDoc) {
957          rootDoc.printError(error);
958       }
959       else {
960          System.err.println("ERROR: "+error);
961       }
962    }
963
964    protected void printWarning(String warning)
965    {
966       if (null != rootDoc) {
967          rootDoc.printWarning(warning);
968       }
969       else {
970          System.err.println("WARNING: "+warning);
971       }
972    }
973
974    protected void printNotice(String notice)
975    {
976       if (null != rootDoc) {
977          rootDoc.printNotice(notice);
978       }
979       else {
980          System.err.println(notice);
981       }
982    }
983
984    protected static ClassDoc getOuterClassDoc(ClassDoc classDoc)
985    {
986       while (null != classDoc.containingClass()) {
987          classDoc = classDoc.containingClass();
988       }
989       return classDoc;
990    }
991
992    private SortedSet allPackages;
993
994    protected Set getAllPackages()
995    {
996       if (null == this.allPackages) {
997          allPackages = new TreeSet();
998          PackageDoc[] specifiedPackages = rootDoc.specifiedPackages();
999          for (int i=0; i<specifiedPackages.length; ++i) {
1000             allPackages.add(specifiedPackages[i]);
1001          }
1002          ClassDoc[] specifiedClasses = rootDoc.specifiedClasses();
1003          for (int i=0; i<specifiedClasses.length; ++i) {
1004             allPackages.add(specifiedClasses[i].containingPackage());
1005          }
1006       }
1007       return this.allPackages;
1008    }
1009
1010    protected boolean omitPackageQualifier(PackageDoc packageDoc)
1011    {
1012       if (!optionNoQualifier.isSpecified()) {
1013          return false;
1014       }
1015       else {
1016          return optionNoQualifier.match(packageDoc);
1017       }
1018    }
1019
1020    protected String possiblyQualifiedName(Type type)
1021    {
1022       if (null == type.asClassDoc()
1023           || !omitPackageQualifier(type.asClassDoc().containingPackage())) {
1024          return type.qualifiedTypeName();
1025       }
1026       else {
1027          return type.typeName();
1028       }
1029    }
1030
1031    protected static class InterfaceRelation
1032    {
1033       public Set superInterfaces;
1034       public Set subInterfaces;
1035       public Set implementingClasses;
1036
1037       public InterfaceRelation()
1038       {
1039          superInterfaces = new TreeSet();
1040          subInterfaces = new TreeSet();
1041          implementingClasses = new TreeSet();
1042       }
1043    }
1044
1045    private void addAllInterfaces(ClassDoc classDoc, Set allInterfaces)
1046    {
1047       ClassDoc[] interfaces = classDoc.interfaces();
1048       for (int i=0; i<interfaces.length; ++i) {
1049          allInterfaces.add(interfaces[i]);
1050          addAllInterfaces(interfaces[i], allInterfaces);
1051       }
1052    }
1053
1054    private Map allSubClasses;
1055
1056    protected Map getAllSubClasses()
1057    {
1058       if (null == allSubClasses) {
1059          allSubClasses = new HashMap();
1060
1061          ClassDoc[] classDocs = getRootDoc().classes();
1062          for (int i=0; i<classDocs.length; ++i) {
1063             if (!classDocs[i].isInterface()) {
1064                for (ClassDoc cd = classDocs[i].superclass();
1065                     null != cd;
1066                     cd = cd.superclass()) {
1067
1068                   if (!cd.qualifiedTypeName().equals("java.lang.Object")) {
1069                      List subClasses = (List)allSubClasses.get(cd);
1070                      if (null == subClasses) {
1071                         subClasses = new LinkedList();
1072                         allSubClasses.put(cd, subClasses);
1073                      }
1074                      subClasses.add(classDocs[i]);
1075                   }
1076                }
1077             }
1078          }
1079       }
1080       return allSubClasses;
1081    }
1082
1083    private Map interfaceRelations;
1084
1085    private void addToInterfaces(ClassDoc classDoc, ClassDoc[] interfaces)
1086    {
1087       for (int i=0; i<interfaces.length; ++i) {
1088          InterfaceRelation interfaceRelation
1089             = (InterfaceRelation)interfaceRelations.get(interfaces[i]);
1090          if (null == interfaceRelation) {
1091             interfaceRelation = new InterfaceRelation();
1092             interfaceRelations.put(interfaces[i], interfaceRelation);
1093          }
1094          interfaceRelation.implementingClasses.add(classDoc);
1095          addToInterfaces(classDoc, interfaces[i].interfaces());
1096       }
1097    }
1098
1099    protected Map getInterfaceRelations()
1100    {
1101       if (null == interfaceRelations) {
1102          interfaceRelations = new HashMap();
1103
1104          ClassDoc[] classDocs = getRootDoc().classes();
1105          for (int i=0; i<classDocs.length; ++i) {
1106             if (classDocs[i].isInterface()) {
1107                InterfaceRelation relation = new InterfaceRelation();
1108                addAllInterfaces(classDocs[i], relation.superInterfaces);
1109                interfaceRelations.put(classDocs[i], relation);
1110             }
1111          }
1112
1113          Iterator it = interfaceRelations.keySet().iterator();
1114          while (it.hasNext()) {
1115             ClassDoc interfaceDoc = (ClassDoc)it.next();
1116             InterfaceRelation relation
1117                = (InterfaceRelation)interfaceRelations.get(interfaceDoc);
1118             Iterator superIt = relation.superInterfaces.iterator();
1119             while (superIt.hasNext()) {
1120                ClassDoc superInterfaceDoc = (ClassDoc)superIt.next();
1121                InterfaceRelation superRelation
1122                   = (InterfaceRelation)interfaceRelations.get(superInterfaceDoc);
1123                if (null != superRelation) {
1124                   superRelation.subInterfaces.add(interfaceDoc);
1125                }
1126             }
1127          }
1128
1129          for (int i=0; i<classDocs.length; ++i) {
1130             if (!classDocs[i].isInterface()) {
1131                for (ClassDoc cd = classDocs[i]; null != cd; cd = cd.superclass()) {
1132                   addToInterfaces(classDocs[i], cd.interfaces());
1133                }
1134             }
1135          }
1136       }
1137
1138       return interfaceRelations;
1139    }
1140
1141    private Map sortedMethodMap = new HashMap();
1142
1143    protected MethodDoc[] getSortedMethods(ClassDoc classDoc)
1144    {
1145       MethodDoc[] result = (MethodDoc[])sortedMethodMap.get(classDoc);
1146       if (null == result) {
1147          MethodDoc[] methods = classDoc.methods();
1148          result = (MethodDoc[])methods.clone();
1149          Arrays.sort(result);
1150          return result;
1151       }
1152       return result;
1153    }
1154
1155    private Map sortedConstructorMap = new HashMap();
1156
1157    protected ConstructorDoc[] getSortedConstructors(ClassDoc classDoc)
1158    {
1159       ConstructorDoc[] result = (ConstructorDoc[])sortedConstructorMap.get(classDoc);
1160       if (null == result) {
1161          ConstructorDoc[] constructors = classDoc.constructors();
1162          result = (ConstructorDoc[])constructors.clone();
1163          Arrays.sort(result);
1164          return result;
1165       }
1166       return result;
1167    }
1168
1169    private Map sortedFieldMap = new HashMap();
1170
1171    protected FieldDoc[] getSortedFields(ClassDoc classDoc)
1172    {
1173       FieldDoc[] result = (FieldDoc[])sortedFieldMap.get(classDoc);
1174       if (null == result) {
1175          FieldDoc[] fields = classDoc.fields();
1176          result = (FieldDoc[])fields.clone();
1177          Arrays.sort(result);
1178          return result;
1179       }
1180       return result;
1181    }
1182
1183    private Map sortedInnerClassMap = new HashMap();
1184
1185    protected ClassDoc[] getSortedInnerClasses(ClassDoc classDoc)
1186    {
1187       ClassDoc[] result = (ClassDoc[])sortedInnerClassMap.get(classDoc);
1188       if (null == result) {
1189          ClassDoc[] innerClasses = classDoc.innerClasses();
1190          result = (ClassDoc[])innerClasses.clone();
1191          Arrays.sort(result);
1192          return result;
1193       }
1194       return result;
1195    }
1196
1197    protected abstract String renderTag(String tagName, Tag[] tags, TagletContext context);
1198
1199    protected abstract String getDocletVersion();
1200
1201    protected SortedSet getThrownExceptions(ExecutableMemberDoc execMemberDoc)
1202    {
1203       SortedSet result = new TreeSet();
1204       ClassDoc[] thrownExceptions = execMemberDoc.thrownExceptions();
1205       for (int j=0; j<thrownExceptions.length; ++j) {
1206          result.add(thrownExceptions[j]);
1207       }
1208       return result;
1209    }
1210
1211    protected boolean isUncheckedException(ClassDoc classDoc)
1212    {
1213       if (classDoc.isException()) {
1214          while (null != classDoc) {
1215             if (classDoc.qualifiedTypeName().equals("java.lang.RuntimeException")) {
1216                return true;
1217             }
1218             classDoc = classDoc.superclass();
1219          }
1220          return false;
1221       }
1222       else {
1223          return false;
1224       }
1225    }
1226
1227    protected FieldDoc findField(ClassDoc classDoc, String fieldName)
1228    {
1229       for (ClassDoc cd = classDoc; cd != null; cd = cd.superclass()) {
1230          FieldDoc[] fields = cd.fields(false);
1231          for (int i=0; i<fields.length; ++i) {
1232             if (fields[i].name().equals(fieldName)) {
1233                return fields[i];
1234             }
1235          }
1236       }
1237       return null;
1238    }
1239
1240    private Map implementedInterfacesCache = new HashMap();
1241
1242    protected Set getImplementedInterfaces(ClassDoc classDoc)
1243    {
1244       Set result = (Set)implementedInterfacesCache.get(classDoc);
1245       if (null == result) {
1246          result = new TreeSet();
1247
1248          for (ClassDoc cd = classDoc; cd != null; cd = cd.superclass()) {
1249             ClassDoc[] interfaces = cd.interfaces();
1250             for (int i=0; i<interfaces.length; ++i) {
1251                result.add(interfaces[i]);
1252                InterfaceRelation relation
1253                   = (InterfaceRelation)getInterfaceRelations().get(interfaces[i]);
1254                if (null != relation) {
1255                   result.addAll(relation.superInterfaces);
1256                }
1257             }
1258          }
1259
1260          implementedInterfacesCache.put(classDoc, result);
1261       }
1262
1263       return result;
1264    }
1265
1266    protected boolean isSinglePackage()
1267    {
1268       return getAllPackages().size() <= 1;
1269    }
1270
1271    protected PackageDoc getSinglePackage()
1272    {
1273       return (PackageDoc)getAllPackages().iterator().next();
1274    }
1275 }