| /* gnu.classpath.tools.doclets.AbstractDoclet |
| Copyright (C) 2004, 2012 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| package gnu.classpath.tools.doclets; |
| |
| import com.sun.javadoc.ClassDoc; |
| import com.sun.javadoc.ConstructorDoc; |
| import com.sun.javadoc.Doc; |
| import com.sun.javadoc.Doclet; |
| import com.sun.javadoc.ExecutableMemberDoc; |
| import com.sun.javadoc.FieldDoc; |
| import com.sun.javadoc.MethodDoc; |
| import com.sun.javadoc.PackageDoc; |
| import com.sun.javadoc.Parameter; |
| import com.sun.javadoc.RootDoc; |
| import com.sun.javadoc.Tag; |
| import com.sun.javadoc.Type; |
| |
| import com.sun.tools.doclets.Taglet; |
| |
| import gnu.classpath.tools.taglets.GnuExtendedTaglet; |
| import gnu.classpath.tools.taglets.AuthorTaglet; |
| import gnu.classpath.tools.taglets.CodeTaglet; |
| import gnu.classpath.tools.taglets.DeprecatedTaglet; |
| import gnu.classpath.tools.taglets.GenericTaglet; |
| import gnu.classpath.tools.taglets.SinceTaglet; |
| import gnu.classpath.tools.taglets.ValueTaglet; |
| import gnu.classpath.tools.taglets.VersionTaglet; |
| import gnu.classpath.tools.taglets.TagletContext; |
| |
| import gnu.classpath.tools.IOToolkit; |
| import gnu.classpath.tools.FileSystemClassLoader; |
| |
| import java.io.File; |
| import java.io.IOException; |
| |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.lang.reflect.InvocationTargetException; |
| |
| import java.text.MessageFormat; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedHashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.ResourceBundle; |
| import java.util.Set; |
| import java.util.SortedSet; |
| import java.util.StringTokenizer; |
| import java.util.TreeMap; |
| import java.util.TreeSet; |
| |
| /** |
| * An abstract Doclet implementation with helpers for common tasks |
| * performed by Doclets. |
| */ |
| public abstract class AbstractDoclet |
| { |
| /** |
| * Mapping from tag type to Taglet for user Taglets specified on |
| * the command line. |
| */ |
| protected Map<String,Taglet> tagletMap = new LinkedHashMap<String,Taglet>(); |
| |
| /** |
| * Stores the package groups specified in the user |
| * options. Contains objects of type PackageGroup. |
| */ |
| private List<PackageGroup> packageGroups = new LinkedList<PackageGroup>(); |
| |
| /** |
| * Keeps track of the tags mentioned by the user during option |
| * processiong so that an error can be emitted if a tag is |
| * mentioned more than once. |
| */ |
| private List<Taglet> mentionedTags = new LinkedList<Taglet>(); |
| |
| public static int optionLength(String option) { |
| return instance.getOptionLength(option); |
| } |
| |
| public static boolean validOptions(String[][] options) { |
| return true; |
| } |
| |
| private static AbstractDoclet instance; |
| |
| protected static void setInstance(AbstractDoclet instance) |
| { |
| AbstractDoclet.instance = instance; |
| } |
| |
| protected abstract void run() |
| throws DocletConfigurationException, IOException; |
| |
| public static boolean start(RootDoc rootDoc) |
| { |
| try { |
| |
| instance.startInstance(rootDoc); |
| return true; |
| } |
| catch (DocletConfigurationException e) { |
| instance.printError(e.getMessage()); |
| return false; |
| } |
| catch (Exception e) { |
| e.printStackTrace(); |
| return false; |
| } |
| } |
| |
| protected RootDoc getRootDoc() |
| { |
| return this.rootDoc; |
| } |
| |
| private RootDoc rootDoc; |
| |
| protected abstract InlineTagRenderer getInlineTagRenderer(); |
| |
| private void startInstance(RootDoc rootDoc) |
| throws DocletConfigurationException, IOException |
| { |
| this.rootDoc = rootDoc; |
| |
| // Set the default Taglet order |
| |
| registerTaglet(new VersionTaglet()); |
| registerTaglet(new AuthorTaglet()); |
| registerTaglet(new SinceTaglet(getInlineTagRenderer())); |
| registerTaglet(new StandardTaglet("serial")); |
| registerTaglet(new StandardTaglet("deprecated")); |
| registerTaglet(new StandardTaglet("see")); |
| registerTaglet(new StandardTaglet("param")); |
| registerTaglet(new StandardTaglet("return")); |
| |
| registerTaglet(new ValueTaglet()); |
| registerTaglet(new CodeTaglet()); |
| |
| // Process command line options |
| |
| for (int i=0, ilim=rootDoc.options().length; i<ilim; ++i) { |
| |
| String[] optionArr = rootDoc.options()[i]; |
| String _optionTag = optionArr[0]; |
| |
| DocletOption option = (DocletOption)nameToOptionMap.get(_optionTag.toLowerCase()); |
| |
| if (null != option) { |
| option.set(optionArr); |
| } |
| } |
| |
| // Enable/disable standard taglets based on user input |
| |
| AuthorTaglet.setTagletEnabled(optionAuthor.getValue()); |
| VersionTaglet.setTagletEnabled(optionVersion.getValue()); |
| SinceTaglet.setTagletEnabled(!optionNoSince.getValue()); |
| DeprecatedTaglet.setTagletEnabled(!optionNoDeprecated.getValue()); |
| |
| if (!getTargetDirectory().exists()) { |
| if (!getTargetDirectory().mkdirs()) { |
| throw new DocletConfigurationException("Cannot create target directory " |
| + getTargetDirectory()); |
| } |
| } |
| |
| run(); |
| } |
| |
| public File getTargetDirectory() |
| { |
| return optionTargetDirectory.getValue(); |
| } |
| |
| private DocletOptionFile optionTargetDirectory = |
| new DocletOptionFile("-d", |
| new File(System.getProperty("user.dir"))); |
| |
| private DocletOptionFlag optionAuthor = |
| new DocletOptionFlag("-author"); |
| |
| private DocletOptionFlag optionVersion = |
| new DocletOptionFlag("-version"); |
| |
| private DocletOptionFlag optionNoSince = |
| new DocletOptionFlag("-nosince"); |
| |
| private DocletOptionFlag optionNoDeprecated = |
| new DocletOptionFlag("-nodeprecated"); |
| |
| private DocletOptionGroup optionGroup = |
| new DocletOptionGroup("-group"); |
| |
| private DocletOptionPackageWildcard optionNoQualifier = |
| new DocletOptionPackageWildcard("-noqualifier", true); |
| |
| private DocletOptionFlag optionDocFilesSubDirs = |
| new DocletOptionFlag("-docfilessubdirs"); |
| |
| private DocletOptionColonSeparated optionExcludeDocFilesSubDir = |
| new DocletOptionColonSeparated("-excludedocfilessubdir"); |
| |
| private DocletOptionTag optionTaglet = |
| new DocletOptionTag("-taglet"); |
| |
| private DocletOptionTag optionTag = |
| new DocletOptionTag("-tag"); |
| |
| private class DocletOptionGroup |
| extends DocletOption |
| { |
| DocletOptionGroup(String optionName) |
| { |
| super(optionName); |
| } |
| |
| public int getLength() |
| { |
| return 3; |
| } |
| |
| public boolean set(String[] optionArr) |
| { |
| try { |
| PackageMatcher packageMatcher = new PackageMatcher(); |
| |
| StringTokenizer tokenizer = new StringTokenizer(optionArr[2], ":"); |
| while (tokenizer.hasMoreTokens()) { |
| String packageWildcard = tokenizer.nextToken(); |
| packageMatcher.addWildcard(packageWildcard); |
| } |
| |
| SortedSet<PackageDoc> groupPackages = packageMatcher.filter(rootDoc.specifiedPackages()); |
| |
| packageGroups.add(new PackageGroup(optionArr[1], groupPackages)); |
| |
| return true; |
| } |
| catch (InvalidPackageWildcardException e) { |
| return false; |
| } |
| } |
| } |
| |
| private class DocletOptionTag |
| extends DocletOption |
| { |
| DocletOptionTag(String optionName) |
| { |
| super(optionName); |
| } |
| |
| public int getLength() |
| { |
| return 2; |
| } |
| |
| public boolean set(String[] optionArr) |
| { |
| String tagSpec = optionArr[1]; |
| boolean validTagSpec = false; |
| int ndx1 = tagSpec.indexOf(':'); |
| if (ndx1 < 0) { |
| Taglet taglet = (Taglet)tagletMap.get(tagSpec); |
| if (null == taglet) { |
| printError("There is no standard tag '" + tagSpec + "'."); |
| } |
| else { |
| if (mentionedTags.contains(taglet)) { |
| printError("Tag '" + tagSpec + "' has been added or moved before."); |
| } |
| else { |
| mentionedTags.add(taglet); |
| |
| // re-append taglet |
| tagletMap.remove(tagSpec); |
| tagletMap.put(tagSpec, taglet); |
| } |
| } |
| } |
| else { |
| int ndx2 = tagSpec.indexOf(':', ndx1 + 1); |
| if (ndx2 > ndx1 && ndx2 < tagSpec.length() - 1) { |
| String tagName = tagSpec.substring(0, ndx1); |
| String tagHead = null; |
| if (tagSpec.charAt(ndx2 + 1) == '\"') { |
| if (tagSpec.charAt(tagSpec.length() - 1) == '\"') { |
| tagHead = tagSpec.substring(ndx2 + 2, tagSpec.length() - 1); |
| validTagSpec = true; |
| } |
| } |
| else { |
| tagHead = tagSpec.substring(ndx2 + 1); |
| validTagSpec = true; |
| } |
| |
| boolean tagScopeOverview = false; |
| boolean tagScopePackages = false; |
| boolean tagScopeTypes = false; |
| boolean tagScopeConstructors = false; |
| boolean tagScopeMethods = false; |
| boolean tagScopeFields = false; |
| boolean tagDisabled = false; |
| |
| tag_option_loop: |
| for (int n=ndx1+1; n<ndx2; ++n) { |
| switch (tagSpec.charAt(n)) { |
| case 'X': |
| tagDisabled = true; |
| break; |
| case 'a': |
| tagScopeOverview = true; |
| tagScopePackages = true; |
| tagScopeTypes = true; |
| tagScopeConstructors = true; |
| tagScopeMethods = true; |
| tagScopeFields = true; |
| break; |
| case 'o': |
| tagScopeOverview = true; |
| break; |
| case 'p': |
| tagScopePackages = true; |
| break; |
| case 't': |
| tagScopeTypes = true; |
| break; |
| case 'c': |
| tagScopeConstructors = true; |
| break; |
| case 'm': |
| tagScopeMethods = true; |
| break; |
| case 'f': |
| tagScopeFields = true; |
| break; |
| default: |
| validTagSpec = false; |
| break tag_option_loop; |
| } |
| } |
| |
| if (validTagSpec) { |
| GenericTaglet taglet |
| = new GenericTaglet(tagName, |
| tagHead, |
| tagScopeOverview, |
| tagScopePackages, |
| tagScopeTypes, |
| tagScopeConstructors, |
| tagScopeMethods, |
| tagScopeFields); |
| taglet.setTagletEnabled(!tagDisabled); |
| taglet.register(tagletMap); |
| mentionedTags.add(taglet); |
| } |
| } |
| } |
| if (!validTagSpec) { |
| printError("Value for option -tag must be in format \"<tagname>:Xaoptcmf:<taghead>\"."); |
| } |
| return validTagSpec; |
| } |
| } |
| |
| private DocletOption[] commonOptions = |
| { |
| optionTargetDirectory, |
| optionAuthor, |
| optionVersion, |
| optionNoSince, |
| optionNoDeprecated, |
| optionGroup, |
| optionDocFilesSubDirs, |
| optionExcludeDocFilesSubDir, |
| optionTaglet, |
| optionTag, |
| }; |
| |
| private void registerOptions() |
| { |
| if (!optionsRegistered) { |
| for (int i=0; i<commonOptions.length; ++i) { |
| DocletOption option = commonOptions[i]; |
| registerOption(option); |
| } |
| DocletOption[] docletOptions = getOptions(); |
| for (int i=0; i<docletOptions.length; ++i) { |
| DocletOption option = docletOptions[i]; |
| registerOption(option); |
| } |
| optionsRegistered = true; |
| } |
| } |
| |
| protected abstract DocletOption[] getOptions(); |
| |
| private boolean optionsRegistered = false; |
| |
| private void registerOption(DocletOption option) |
| { |
| nameToOptionMap.put(option.getName(), option); |
| } |
| |
| private Map<String,DocletOption> nameToOptionMap = new HashMap<String,DocletOption>(); |
| |
| private int getOptionLength(String optionName) |
| { |
| registerOptions(); |
| DocletOption option = nameToOptionMap.get(optionName.toLowerCase()); |
| if (null != option) { |
| return option.getLength(); |
| } |
| else { |
| return -1; |
| } |
| } |
| |
| protected List<ClassDoc> getKnownDirectSubclasses(ClassDoc classDoc) |
| { |
| List<ClassDoc> result = new LinkedList<ClassDoc>(); |
| if (!"java.lang.Object".equals(classDoc.qualifiedName())) { |
| ClassDoc[] classes = rootDoc.classes(); |
| for (int i=0; i<classes.length; ++i) { |
| if (classDoc == classes[i].superclass()) { |
| result.add(classes[i]); |
| } |
| } |
| } |
| return result; |
| } |
| |
| protected static class IndexKey |
| implements Comparable<IndexKey> |
| { |
| private String name; |
| private String lowerName; |
| |
| public IndexKey(String name) |
| { |
| this.name = name; |
| this.lowerName = name.toLowerCase(); |
| } |
| |
| public boolean equals(Object other) |
| { |
| return this.lowerName.equals(((IndexKey)other).lowerName); |
| } |
| |
| public int hashCode() |
| { |
| return lowerName.hashCode(); |
| } |
| |
| public int compareTo(IndexKey ik) |
| { |
| return lowerName.compareTo(ik.lowerName); |
| } |
| |
| public String getName() |
| { |
| return name; |
| } |
| } |
| |
| private Map<Character,List<Doc>> categorizedIndex; |
| |
| protected Map<Character,List<Doc>> getCategorizedIndex() |
| { |
| if (null == categorizedIndex) { |
| categorizedIndex = new LinkedHashMap<Character,List<Doc>>(); |
| |
| Map<IndexKey,Doc> indexMap = getIndexByName(); |
| LinkedList<IndexKey> keys = new LinkedList<IndexKey>(); //indexMap.keySet().size()); |
| keys.addAll(indexMap.keySet()); |
| Collections.sort(keys); |
| Iterator<IndexKey> it = keys.iterator(); //indexMap.keySet().iterator(); |
| char previousCategoryLetter = '\0'; |
| Character keyLetter = null; |
| while (it.hasNext()) { |
| IndexKey key = it.next(); |
| char firstChar = Character.toUpperCase(key.getName().charAt(0)); |
| if (firstChar != previousCategoryLetter) { |
| keyLetter = new Character(firstChar); |
| previousCategoryLetter = firstChar; |
| categorizedIndex.put(keyLetter, new LinkedList<Doc>()); |
| } |
| List<Doc> letterList = categorizedIndex.get(keyLetter); |
| letterList.add(indexMap.get(key)); |
| } |
| } |
| |
| return categorizedIndex; |
| } |
| |
| |
| private Map<IndexKey,Doc> indexByName; |
| |
| protected Map<IndexKey,Doc> getIndexByName() |
| { |
| if (null == indexByName) { |
| // Create index |
| |
| // Collect index |
| |
| indexByName = new HashMap<IndexKey,Doc>(); //TreeMap(); |
| |
| // Add packages to index |
| |
| PackageDoc[] packages = rootDoc.specifiedPackages(); |
| for (int i=0, ilim=packages.length; i<ilim; ++i) { |
| PackageDoc c = packages[i]; |
| if (c.name().length() > 0) { |
| indexByName.put(new IndexKey(c.name()), c); |
| } |
| } |
| |
| // Add classes, fields and methods to index |
| |
| ClassDoc[] sumclasses = rootDoc.classes(); |
| for (int i=0, ilim=sumclasses.length; i<ilim; ++i) { |
| ClassDoc c = sumclasses[i]; |
| if (null == c.containingClass()) { |
| indexByName.put(new IndexKey(c.name() + " " + c.containingPackage().name()), c); |
| } |
| else { |
| indexByName.put(new IndexKey(c.name().substring(c.containingClass().name().length() + 1) |
| + " " + c.containingClass().name() + " " + c.containingPackage().name()), c); |
| } |
| FieldDoc[] fields = c.fields(); |
| for (int j=0, jlim=fields.length; j<jlim; ++j) { |
| indexByName.put(new IndexKey(fields[j].name() + " " + fields[j].containingClass().name() + " " + fields[j].containingPackage().name()), fields[j]); |
| } |
| MethodDoc[] methods = c.methods(); |
| for (int j=0, jlim=methods.length; j<jlim; ++j) { |
| MethodDoc method = methods[j]; |
| indexByName.put(new IndexKey(method.name() + method.signature() + " " + method.containingClass().name() + " " + method.containingPackage().name()), method); |
| } |
| ConstructorDoc[] constructors = c.constructors(); |
| for (int j=0, jlim=constructors.length; j<jlim; ++j) { |
| ConstructorDoc constructor = constructors[j]; |
| indexByName.put(new IndexKey(constructor.name() + constructor.signature() + " " + constructor.containingClass().name() + " " + constructor.containingPackage().name()), constructor); |
| } |
| } |
| } |
| return indexByName; |
| } |
| |
| private void registerTaglet(Taglet taglet) |
| { |
| tagletMap.put(taglet.getName(), taglet); |
| } |
| |
| protected void printTaglets(Tag[] tags, TagletContext context, TagletPrinter output, boolean inline) |
| { |
| for (Iterator<String> it = tagletMap.keySet().iterator(); it.hasNext(); ) { |
| String tagName = it.next(); |
| Taglet taglet = tagletMap.get(tagName); |
| Doc doc = context.getDoc(); |
| if (inline == taglet.isInlineTag() |
| && ((doc == null |
| && taglet.inOverview()) |
| || (doc != null |
| && ((doc.isConstructor() && taglet.inConstructor()) |
| || (doc.isField() && taglet.inField()) |
| || (doc.isMethod() && taglet.inMethod()) |
| || (doc instanceof PackageDoc && taglet.inPackage()) |
| || ((doc.isClass() || doc.isInterface()) && taglet.inType()))))) { |
| |
| List<Tag> tagsOfThisType = new LinkedList<Tag>(); |
| for (int i=0; i<tags.length; ++i) { |
| if (tags[i].name().substring(1).equals(tagName)) { |
| tagsOfThisType.add(tags[i]); |
| } |
| } |
| |
| Tag[] tagletTags = tagsOfThisType.toArray(new Tag[tagsOfThisType.size()]); |
| |
| String tagletString; |
| if (taglet instanceof StandardTaglet) { |
| tagletString = renderTag(tagName, tagletTags, context); |
| } |
| else if (taglet instanceof GnuExtendedTaglet) { |
| tagletString = ((GnuExtendedTaglet)taglet).toString(tagletTags, context); |
| } |
| else { |
| tagletString = taglet.toString(tagletTags); |
| } |
| if (null != tagletString) { |
| output.printTagletString(tagletString); |
| } |
| } |
| } |
| } |
| |
| protected void printInlineTaglet(Tag tag, TagletContext context, TagletPrinter output) |
| { |
| Taglet taglet = (Taglet)tagletMap.get(tag.name().substring(1)); |
| if (null != taglet) { |
| String tagletString; |
| if (taglet instanceof GnuExtendedTaglet) { |
| tagletString = ((GnuExtendedTaglet)taglet).toString(tag, context); |
| } |
| else { |
| tagletString = taglet.toString(tag); |
| } |
| if (null != tagletString) { |
| output.printTagletString(tagletString); |
| } |
| } |
| else { |
| printWarning("Unknown tag: " + tag.name()); |
| } |
| } |
| |
| protected void printMainTaglets(Tag[] tags, TagletContext context, TagletPrinter output) |
| { |
| printTaglets(tags, context, output, false); |
| } |
| |
| /** |
| * @param usedClassToPackagesMap ClassDoc to (PackageDoc to (UsageType to (Set of Doc))) |
| */ |
| private void addUsedBy(Map<ClassDoc,Map<PackageDoc,Map<UsageType,Set<Doc>>>> usedClassToPackagesMap, |
| ClassDoc usedClass, UsageType usageType, Doc user, PackageDoc userPackage) |
| { |
| Map<PackageDoc,Map<UsageType,Set<Doc>>> packageToUsageTypeMap = usedClassToPackagesMap.get(usedClass); |
| if (null == packageToUsageTypeMap) { |
| packageToUsageTypeMap = new HashMap<PackageDoc,Map<UsageType,Set<Doc>>>(); |
| usedClassToPackagesMap.put(usedClass, packageToUsageTypeMap); |
| } |
| |
| Map<UsageType,Set<Doc>> usageTypeToUsersMap = packageToUsageTypeMap.get(userPackage); |
| if (null == usageTypeToUsersMap) { |
| usageTypeToUsersMap = new TreeMap<UsageType,Set<Doc>>(); |
| packageToUsageTypeMap.put(userPackage, usageTypeToUsersMap); |
| } |
| |
| Set<Doc> userSet = usageTypeToUsersMap.get(usageType); |
| if (null == userSet) { |
| userSet = new TreeSet<Doc>(); // FIXME: we need the collator from Main here |
| usageTypeToUsersMap.put(usageType, userSet); |
| } |
| userSet.add(user); |
| } |
| |
| /** |
| * Create the cross reference database. |
| */ |
| private Map collectUsage() { |
| |
| Map<ClassDoc,Map<PackageDoc,Map<UsageType,Set<Doc>>>> _usedClassToPackagesMap = |
| new HashMap<ClassDoc,Map<PackageDoc,Map<UsageType,Set<Doc>>>>(); |
| |
| ClassDoc[] classes = rootDoc.classes(); |
| for (int i = 0, ilim = classes.length; i < ilim; ++ i) { |
| ClassDoc clazz = classes[i]; |
| |
| if (clazz.isInterface()) { |
| // classes implementing |
| InterfaceRelation relation |
| = (InterfaceRelation)getInterfaceRelations().get(clazz); |
| Iterator<ClassDoc> it = relation.implementingClasses.iterator(); |
| while (it.hasNext()) { |
| ClassDoc implementor = it.next(); |
| addUsedBy(_usedClassToPackagesMap, |
| clazz, UsageType.CLASS_IMPLEMENTING, implementor, implementor.containingPackage()); |
| } |
| } |
| else { |
| // classes derived from |
| for (ClassDoc superclass = clazz.superclass(); superclass != null; |
| superclass = superclass.superclass()) { |
| addUsedBy(_usedClassToPackagesMap, |
| superclass, UsageType.CLASS_DERIVED_FROM, clazz, clazz.containingPackage()); |
| } |
| } |
| |
| FieldDoc[] fields = clazz.fields(); |
| for (int j = 0, jlim = fields.length; j < jlim; ++ j) { |
| FieldDoc field = fields[j]; |
| |
| // fields of type |
| ClassDoc fieldType = field.type().asClassDoc(); |
| if (null != fieldType) { |
| addUsedBy(_usedClassToPackagesMap, |
| fieldType, UsageType.FIELD_OF_TYPE, |
| field, clazz.containingPackage()); |
| } |
| } |
| |
| MethodDoc[] methods = clazz.methods(); |
| for (int j = 0, jlim = methods.length; j < jlim; ++ j) { |
| MethodDoc method = methods[j]; |
| |
| // methods with return type |
| |
| ClassDoc returnType = method.returnType().asClassDoc(); |
| if (null != returnType) { |
| addUsedBy(_usedClassToPackagesMap, |
| returnType, UsageType.METHOD_WITH_RETURN_TYPE, |
| method, clazz.containingPackage()); |
| } |
| Parameter[] parameters = method.parameters(); |
| for (int k=0; k<parameters.length; ++k) { |
| |
| // methods with parameter type |
| |
| Parameter parameter = parameters[k]; |
| ClassDoc parameterType = parameter.type().asClassDoc(); |
| if (null != parameterType) { |
| addUsedBy(_usedClassToPackagesMap, |
| parameterType, UsageType.METHOD_WITH_PARAMETER_TYPE, |
| method, clazz.containingPackage()); |
| } |
| } |
| |
| // methods which throw |
| |
| ClassDoc[] thrownExceptions = method.thrownExceptions(); |
| for (int k = 0, klim = thrownExceptions.length; k < klim; ++ k) { |
| ClassDoc thrownException = thrownExceptions[k]; |
| addUsedBy(_usedClassToPackagesMap, |
| thrownException, UsageType.METHOD_WITH_THROWN_TYPE, |
| method, clazz.containingPackage()); |
| } |
| } |
| |
| ConstructorDoc[] constructors = clazz.constructors(); |
| for (int j = 0, jlim = constructors.length; j < jlim; ++ j) { |
| |
| ConstructorDoc constructor = constructors[j]; |
| |
| Parameter[] parameters = constructor.parameters(); |
| for (int k = 0, klim = parameters.length; k < klim; ++ k) { |
| |
| // constructors with parameter type |
| |
| Parameter parameter = parameters[k]; |
| ClassDoc parameterType = parameter.type().asClassDoc(); |
| if (null != parameterType) { |
| addUsedBy(_usedClassToPackagesMap, |
| parameterType, UsageType.CONSTRUCTOR_WITH_PARAMETER_TYPE, |
| constructor, clazz.containingPackage()); |
| } |
| } |
| |
| // constructors which throw |
| |
| ClassDoc[] thrownExceptions = constructor.thrownExceptions(); |
| for (int k = 0, klim = thrownExceptions.length; k < klim; ++ k) { |
| ClassDoc thrownException = thrownExceptions[k]; |
| addUsedBy(_usedClassToPackagesMap, |
| thrownException, UsageType.CONSTRUCTOR_WITH_THROWN_TYPE, |
| constructor, clazz.containingPackage()); |
| } |
| } |
| } |
| return _usedClassToPackagesMap; |
| } |
| |
| private Map<ClassDoc,Map<PackageDoc,Map<UsageType,Set<Doc>>>> usedClassToPackagesMap = null; |
| |
| protected Map<PackageDoc,Map<UsageType,Set<Doc>>> getUsageOfClass(ClassDoc classDoc) |
| { |
| if (null == this.usedClassToPackagesMap) { |
| this.usedClassToPackagesMap = collectUsage(); |
| } |
| return this.usedClassToPackagesMap.get(classDoc); |
| } |
| |
| protected static class UsageType |
| implements Comparable<UsageType> |
| { |
| public static final UsageType CLASS_DERIVED_FROM = new UsageType("class-derived-from"); |
| public static final UsageType CLASS_IMPLEMENTING = new UsageType("class-implementing"); |
| public static final UsageType FIELD_OF_TYPE = new UsageType("field-of-type"); |
| public static final UsageType METHOD_WITH_RETURN_TYPE = new UsageType("method-with-return-type"); |
| public static final UsageType METHOD_WITH_PARAMETER_TYPE = new UsageType("method-with-parameter-type"); |
| public static final UsageType METHOD_WITH_THROWN_TYPE = new UsageType("method-with-thrown-type"); |
| public static final UsageType CONSTRUCTOR_WITH_PARAMETER_TYPE = new UsageType("constructor-with-parameter-type"); |
| public static final UsageType CONSTRUCTOR_WITH_THROWN_TYPE = new UsageType("constructor-with-thrown-type"); |
| private String id; |
| |
| private UsageType(String id) |
| { |
| this.id = id; |
| } |
| |
| public int compareTo(UsageType ut) |
| { |
| return this.id.compareTo(ut.id); |
| } |
| |
| public String toString() { |
| return "UsageType{id=" + id + "}"; |
| } |
| |
| public String getId() { |
| return id; |
| } |
| } |
| |
| private ResourceBundle resources; |
| |
| protected String getString(String key) |
| { |
| if (null == resources) { |
| Locale currentLocale = Locale.getDefault(); |
| |
| resources |
| = ResourceBundle.getBundle("htmldoclet.HtmlDoclet", currentLocale); |
| } |
| |
| return resources.getString(key); |
| } |
| |
| protected String format(String key, String value1) |
| { |
| return MessageFormat.format(getString(key), new Object[] { value1 }); |
| } |
| |
| protected List<PackageGroup> getPackageGroups() |
| { |
| return packageGroups; |
| } |
| |
| protected void copyDocFiles(File sourceDir, File targetDir) |
| throws IOException |
| { |
| File sourceDocFiles = new File(sourceDir, "doc-files"); |
| File targetDocFiles = new File(targetDir, "doc-files"); |
| |
| if (sourceDocFiles.exists()) { |
| IOToolkit.copyDirectory(sourceDocFiles, |
| targetDocFiles, |
| optionDocFilesSubDirs.getValue(), |
| optionExcludeDocFilesSubDir.getComponents()); |
| } |
| } |
| |
| private Set sourcePaths; |
| |
| /** |
| * Try to determine the source directory for the given package by |
| * looking at the path specified by -sourcepath, or the current |
| * directory if -sourcepath hasn't been specified. |
| * |
| * @throws IOException if the source directory couldn't be |
| * located. |
| * |
| * @return List of File |
| */ |
| protected List getPackageSourceDirs(PackageDoc packageDoc) |
| throws IOException |
| { |
| if (null == sourcePaths) { |
| for (int i=0; i<rootDoc.options().length; ++i) { |
| if ("-sourcepath".equals(rootDoc.options()[i][0]) |
| || "-s".equals(rootDoc.options()[i][0])) { |
| sourcePaths = new LinkedHashSet(); |
| String sourcepathString = rootDoc.options()[i][1]; |
| StringTokenizer st = new StringTokenizer(sourcepathString, File.pathSeparator); |
| while (st.hasMoreTokens()) { |
| sourcePaths.add(new File(st.nextToken())); |
| } |
| } |
| } |
| if (null == sourcePaths) { |
| sourcePaths = new LinkedHashSet(); |
| sourcePaths.add(new File(System.getProperty("user.dir"))); |
| } |
| } |
| |
| String packageSubDir = packageDoc.name().replace('.', File.separatorChar); |
| Iterator it = sourcePaths.iterator(); |
| List result = new LinkedList(); |
| while (it.hasNext()) { |
| File pathComponent = (File)it.next(); |
| File packageDir = new File(pathComponent, packageSubDir); |
| if (packageDir.exists()) { |
| result.add(packageDir); |
| } |
| } |
| if (result.isEmpty()) { |
| throw new IOException("Couldn't locate source directory for package " + packageDoc.name()); |
| } |
| else { |
| return result; |
| } |
| } |
| |
| protected File getSourceFile(ClassDoc classDoc) |
| throws IOException |
| { |
| List packageDirs = getPackageSourceDirs(classDoc.containingPackage()); |
| Iterator it = packageDirs.iterator(); |
| while (it.hasNext()) { |
| File packageDir = (File)it.next(); |
| File sourceFile = new File(packageDir, getOuterClassDoc(classDoc).name() + ".java"); |
| if (sourceFile.exists()) { |
| return sourceFile; |
| } |
| } |
| |
| throw new IOException("Couldn't locate source file for class " + classDoc.qualifiedTypeName()); |
| } |
| |
| protected void printError(String error) |
| { |
| if (null != rootDoc) { |
| rootDoc.printError(error); |
| } |
| else { |
| System.err.println("ERROR: "+error); |
| } |
| } |
| |
| protected void printWarning(String warning) |
| { |
| if (null != rootDoc) { |
| rootDoc.printWarning(warning); |
| } |
| else { |
| System.err.println("WARNING: "+warning); |
| } |
| } |
| |
| protected void printNotice(String notice) |
| { |
| if (null != rootDoc) { |
| rootDoc.printNotice(notice); |
| } |
| else { |
| System.err.println(notice); |
| } |
| } |
| |
| protected static ClassDoc getOuterClassDoc(ClassDoc classDoc) |
| { |
| while (null != classDoc.containingClass()) { |
| classDoc = classDoc.containingClass(); |
| } |
| return classDoc; |
| } |
| |
| private SortedSet allPackages; |
| |
| protected Set getAllPackages() |
| { |
| if (null == this.allPackages) { |
| allPackages = new TreeSet(); |
| PackageDoc[] specifiedPackages = rootDoc.specifiedPackages(); |
| for (int i=0; i<specifiedPackages.length; ++i) { |
| allPackages.add(specifiedPackages[i]); |
| } |
| ClassDoc[] specifiedClasses = rootDoc.specifiedClasses(); |
| for (int i=0; i<specifiedClasses.length; ++i) { |
| allPackages.add(specifiedClasses[i].containingPackage()); |
| } |
| } |
| return this.allPackages; |
| } |
| |
| protected boolean omitPackageQualifier(PackageDoc packageDoc) |
| { |
| if (!optionNoQualifier.isSpecified()) { |
| return false; |
| } |
| else { |
| return optionNoQualifier.match(packageDoc); |
| } |
| } |
| |
| protected String possiblyQualifiedName(Type type) |
| { |
| if (null == type.asClassDoc() |
| || !omitPackageQualifier(type.asClassDoc().containingPackage())) { |
| return type.qualifiedTypeName(); |
| } |
| else { |
| return type.typeName(); |
| } |
| } |
| |
| protected static class InterfaceRelation |
| { |
| public Set superInterfaces; |
| public Set subInterfaces; |
| public Set implementingClasses; |
| |
| public InterfaceRelation() |
| { |
| superInterfaces = new TreeSet(); |
| subInterfaces = new TreeSet(); |
| implementingClasses = new TreeSet(); |
| } |
| } |
| |
| private void addAllInterfaces(ClassDoc classDoc, Set allInterfaces) |
| { |
| ClassDoc[] interfaces = classDoc.interfaces(); |
| for (int i=0; i<interfaces.length; ++i) { |
| allInterfaces.add(interfaces[i]); |
| addAllInterfaces(interfaces[i], allInterfaces); |
| } |
| } |
| |
| private Map allSubClasses; |
| |
| protected Map getAllSubClasses() |
| { |
| if (null == allSubClasses) { |
| allSubClasses = new HashMap(); |
| |
| ClassDoc[] classDocs = getRootDoc().classes(); |
| for (int i=0; i<classDocs.length; ++i) { |
| if (!classDocs[i].isInterface()) { |
| for (ClassDoc cd = classDocs[i].superclass(); |
| null != cd; |
| cd = cd.superclass()) { |
| |
| if (!cd.qualifiedTypeName().equals("java.lang.Object")) { |
| List subClasses = (List)allSubClasses.get(cd); |
| if (null == subClasses) { |
| subClasses = new LinkedList(); |
| allSubClasses.put(cd, subClasses); |
| } |
| subClasses.add(classDocs[i]); |
| } |
| } |
| } |
| } |
| } |
| return allSubClasses; |
| } |
| |
| private Map interfaceRelations; |
| |
| private void addToInterfaces(ClassDoc classDoc, ClassDoc[] interfaces) |
| { |
| for (int i=0; i<interfaces.length; ++i) { |
| InterfaceRelation interfaceRelation |
| = (InterfaceRelation)interfaceRelations.get(interfaces[i]); |
| if (null == interfaceRelation) { |
| interfaceRelation = new InterfaceRelation(); |
| interfaceRelations.put(interfaces[i], interfaceRelation); |
| } |
| interfaceRelation.implementingClasses.add(classDoc); |
| addToInterfaces(classDoc, interfaces[i].interfaces()); |
| } |
| } |
| |
| protected Map getInterfaceRelations() |
| { |
| if (null == interfaceRelations) { |
| interfaceRelations = new HashMap(); |
| |
| ClassDoc[] classDocs = getRootDoc().classes(); |
| for (int i=0; i<classDocs.length; ++i) { |
| if (classDocs[i].isInterface()) { |
| InterfaceRelation relation = new InterfaceRelation(); |
| addAllInterfaces(classDocs[i], relation.superInterfaces); |
| interfaceRelations.put(classDocs[i], relation); |
| } |
| } |
| |
| Iterator it = interfaceRelations.keySet().iterator(); |
| while (it.hasNext()) { |
| ClassDoc interfaceDoc = (ClassDoc)it.next(); |
| InterfaceRelation relation |
| = (InterfaceRelation)interfaceRelations.get(interfaceDoc); |
| Iterator superIt = relation.superInterfaces.iterator(); |
| while (superIt.hasNext()) { |
| ClassDoc superInterfaceDoc = (ClassDoc)superIt.next(); |
| InterfaceRelation superRelation |
| = (InterfaceRelation)interfaceRelations.get(superInterfaceDoc); |
| if (null != superRelation) { |
| superRelation.subInterfaces.add(interfaceDoc); |
| } |
| } |
| } |
| |
| for (int i=0; i<classDocs.length; ++i) { |
| if (!classDocs[i].isInterface()) { |
| for (ClassDoc cd = classDocs[i]; null != cd; cd = cd.superclass()) { |
| addToInterfaces(classDocs[i], cd.interfaces()); |
| } |
| } |
| } |
| } |
| |
| return interfaceRelations; |
| } |
| |
| private Map sortedMethodMap = new HashMap(); |
| |
| protected MethodDoc[] getSortedMethods(ClassDoc classDoc) |
| { |
| MethodDoc[] result = (MethodDoc[])sortedMethodMap.get(classDoc); |
| if (null == result) { |
| MethodDoc[] methods = classDoc.methods(); |
| result = (MethodDoc[])methods.clone(); |
| Arrays.sort(result); |
| return result; |
| } |
| return result; |
| } |
| |
| private Map sortedConstructorMap = new HashMap(); |
| |
| protected ConstructorDoc[] getSortedConstructors(ClassDoc classDoc) |
| { |
| ConstructorDoc[] result = (ConstructorDoc[])sortedConstructorMap.get(classDoc); |
| if (null == result) { |
| ConstructorDoc[] constructors = classDoc.constructors(); |
| result = (ConstructorDoc[])constructors.clone(); |
| Arrays.sort(result); |
| return result; |
| } |
| return result; |
| } |
| |
| private Map sortedFieldMap = new HashMap(); |
| |
| protected FieldDoc[] getSortedFields(ClassDoc classDoc) |
| { |
| FieldDoc[] result = (FieldDoc[])sortedFieldMap.get(classDoc); |
| if (null == result) { |
| FieldDoc[] fields = classDoc.fields(); |
| result = (FieldDoc[])fields.clone(); |
| Arrays.sort(result); |
| return result; |
| } |
| return result; |
| } |
| |
| private Map sortedInnerClassMap = new HashMap(); |
| |
| protected ClassDoc[] getSortedInnerClasses(ClassDoc classDoc) |
| { |
| ClassDoc[] result = (ClassDoc[])sortedInnerClassMap.get(classDoc); |
| if (null == result) { |
| ClassDoc[] innerClasses = classDoc.innerClasses(); |
| result = (ClassDoc[])innerClasses.clone(); |
| Arrays.sort(result); |
| return result; |
| } |
| return result; |
| } |
| |
| protected abstract String renderTag(String tagName, Tag[] tags, TagletContext context); |
| |
| protected abstract String getDocletVersion(); |
| |
| protected SortedSet getThrownExceptions(ExecutableMemberDoc execMemberDoc) |
| { |
| SortedSet result = new TreeSet(); |
| ClassDoc[] thrownExceptions = execMemberDoc.thrownExceptions(); |
| for (int j=0; j<thrownExceptions.length; ++j) { |
| result.add(thrownExceptions[j]); |
| } |
| return result; |
| } |
| |
| protected boolean isUncheckedException(ClassDoc classDoc) |
| { |
| if (classDoc.isException()) { |
| while (null != classDoc) { |
| if (classDoc.qualifiedTypeName().equals("java.lang.RuntimeException")) { |
| return true; |
| } |
| classDoc = classDoc.superclass(); |
| } |
| return false; |
| } |
| else { |
| return false; |
| } |
| } |
| |
| protected FieldDoc findField(ClassDoc classDoc, String fieldName) |
| { |
| for (ClassDoc cd = classDoc; cd != null; cd = cd.superclass()) { |
| FieldDoc[] fields = cd.fields(false); |
| for (int i=0; i<fields.length; ++i) { |
| if (fields[i].name().equals(fieldName)) { |
| return fields[i]; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private Map implementedInterfacesCache = new HashMap(); |
| |
| protected Set getImplementedInterfaces(ClassDoc classDoc) |
| { |
| Set result = (Set)implementedInterfacesCache.get(classDoc); |
| if (null == result) { |
| result = new TreeSet(); |
| |
| for (ClassDoc cd = classDoc; cd != null; cd = cd.superclass()) { |
| ClassDoc[] interfaces = cd.interfaces(); |
| for (int i=0; i<interfaces.length; ++i) { |
| result.add(interfaces[i]); |
| InterfaceRelation relation |
| = (InterfaceRelation)getInterfaceRelations().get(interfaces[i]); |
| if (null != relation) { |
| result.addAll(relation.superInterfaces); |
| } |
| } |
| } |
| |
| implementedInterfacesCache.put(classDoc, result); |
| } |
| |
| return result; |
| } |
| |
| protected boolean isSinglePackage() |
| { |
| return getAllPackages().size() <= 1; |
| } |
| |
| protected PackageDoc getSinglePackage() |
| { |
| return (PackageDoc)getAllPackages().iterator().next(); |
| } |
| } |