libjava/ChangeLog:
[official-gcc.git] / libjava / classpath / tools / gnu / classpath / tools / gjdoc / RootDocImpl.java
blobb60a0b6e2fb84473cc6c3c221971e8e46527fee1
1 /* gnu.classpath.tools.gjdoc.RootDocImpl
2 Copyright (C) 2001, 2007 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
21 package gnu.classpath.tools.gjdoc;
23 import com.sun.javadoc.*;
24 import java.util.*;
25 import java.io.*;
26 import java.lang.reflect.*;
28 public class RootDocImpl
29 extends DocImpl
30 implements GjdocRootDoc {
32 private ErrorReporter reporter = new ErrorReporter();
34 private RandomAccessFile rawCommentCache;
36 /**
37 * All options and their corresponding values which are not recognized
38 * by Gjdoc. These are passed to the Doclet as "custom options".
39 * Each element in this array is again a String array, with the
40 * option name as first element (including prefix dash) and possible
41 * option values as following elements.
43 private String[][] customOptionArr;
45 /**
46 * All source files explicitly specified on the command line.
48 * @contains File
50 private List specifiedSourceFiles = new LinkedList();
52 /**
53 * The names of all packages explicitly specified on the
54 * command line.
56 * @contains String
58 private Set specifiedPackageNames = new LinkedHashSet();
60 /**
61 * Stores all classes specified by the user: those given by
62 * individual class names on the command line, and those
63 * contained in the packages given on the command line.
65 * @contains ClassDocImpl
67 private List classesList = new LinkedList(); //new LinkedList();
69 /**
70 * Stores all classes loaded in the course of preparing
71 * the documentation data. Maps the fully qualified name
72 * of a class to its ClassDocImpl representation.
74 * @contains String->ClassDocImpl
76 private Map classDocMap = new HashMap();
78 /**
79 * Stores all packages loaded in the course of preparing
80 * the documentation data. Maps the package name
81 * to its PackageDocImpl representation.
83 * @contains String->PackageDocImpl
85 private Map packageDocMap = new HashMap();
87 /**
88 * All classes specified by the user, both those explicitly
89 * individually specified on the command line and those contained
90 * in packages specified on the command line (as Array for quick
91 * retrieval by Doclet). This is created from classesList after
92 * all classes have been loaded.
94 private ClassDocImpl[] classes;
96 /**
97 * All classes which were individually specified on the command
98 * line (as Array for quick retrieval by Doclet). This is created
99 * from specifiedClassNames after all classes have been loaded.
101 private List specifiedClasses;
104 * All packages which were specified on the command line (as Array
105 * for quick retrieval by Doclet). This is created from
106 * specifiedPackageNames after all classes have been loaded.
108 private Set specifiedPackages;
112 * Temporarily stores a list of classes which are referenced
113 * by classes already loaded and which still have to be
114 * resolved.
116 private List scheduledClasses=new LinkedList();
118 private List sourcePath;
120 private String sourceEncoding;
122 private Parser parser = new Parser();
124 private Set unlocatableReportedSet = new HashSet();
126 private Set inaccessibleReportedSet = new HashSet();
128 //--------------------------------------------------------------------------
130 // Implementation of RootDoc interface
132 //--------------------------------------------------------------------------
135 * Return classes and interfaces to be documented.
137 public ClassDoc[] classes() { return classes; }
140 * Return a ClassDoc object for the specified class/interface
141 * name.
143 * @return a ClassDoc object describing the given class, or
144 * <code>null</code> if no corresponding ClassDoc object
145 * has been constructed.
147 public ClassDoc classNamed(String qualifiedName) {
148 return (ClassDoc)classDocMap.get(qualifiedName);
152 * Return an xxx
154 public String[][] options() { return customOptionArr; }
156 // Return a PackageDoc for the specified package name
157 public PackageDoc packageNamed(String name) {
158 return (PackageDoc)packageDocMap.get(name);
162 // classes and interfaces specified on the command line.
163 public ClassDoc[] specifiedClasses()
165 return (ClassDocImpl[]) specifiedClasses.toArray(new ClassDocImpl[0]);
168 // packages specified on the command line.
169 public PackageDoc[] specifiedPackages()
171 return (PackageDocImpl[])specifiedPackages.toArray(new PackageDocImpl[0]);
174 // Print error message, increment error count.
175 public void printError(java.lang.String msg) {
176 reporter.printError(msg);
179 // Print error message, increment error count.
180 public void printFatal(java.lang.String msg) {
181 reporter.printFatal(msg);
184 // Print a message.
185 public void printNotice(java.lang.String msg) {
186 reporter.printNotice(msg);
189 // Print warning message, increment warning count.
190 public void printWarning(java.lang.String msg) {
191 reporter.printWarning(msg);
194 public String name() {
195 return "RootDoc";
198 public ErrorReporter getReporter() {
199 return reporter;
202 public void build() throws ParseException, IOException {
204 //--- Create a temporary random access file for caching comment text.
206 //File rawCommentCacheFile=File.createTempFile("gjdoc_rawcomment",".cache");
207 File rawCommentCacheFile = new File("gjdoc_rawcomment.cache");
208 rawCommentCacheFile.deleteOnExit();
209 rawCommentCache = new RandomAccessFile(rawCommentCacheFile, "rw");
211 //--- Parse all files in "java.lang".
213 List javaLangSourceDirs = findSourceFiles("java/lang");
214 if (!javaLangSourceDirs.isEmpty()) {
215 Iterator it = javaLangSourceDirs.iterator();
216 while (it.hasNext()) {
217 File javaLangSourceDir = (File)it.next();
218 parser.processSourceDir(javaLangSourceDir,
219 sourceEncoding, "java.lang");
222 else {
224 Debug.log(1, "Sourcepath is "+sourcePath);
226 // Core docs not included in source-path:
227 // we need to gather the information about java.lang
228 // classes via reflection...
232 //--- Parse all files in explicitly specified package directories.
234 for (Iterator it=specifiedPackageNames.iterator(); it.hasNext(); ) {
236 String specifiedPackageName = (String)it.next();
237 String displayPackageName = specifiedPackageName;
238 if (null == displayPackageName || 0 == displayPackageName.length()) {
239 displayPackageName = "<unnamed>";
241 printNotice("Loading classes for package "+displayPackageName+"...");
242 String relPath;
243 if (null != specifiedPackageName) {
244 relPath = specifiedPackageName.replace('.',File.separatorChar);
246 else {
247 relPath = "";
249 List sourceDirs = findSourceFiles(relPath);
250 if (!sourceDirs.isEmpty()) {
251 Iterator sourceDirIt = sourceDirs.iterator();
252 while (sourceDirIt.hasNext()) {
253 File sourceDir = (File)sourceDirIt.next();
254 parser.processSourceDir(sourceDir, sourceEncoding, specifiedPackageName);
257 else {
258 printError("Package '"+specifiedPackageName+"' not found.");
262 specifiedClasses = new LinkedList();
264 //--- Parse all explicitly specified source files.
266 for (Iterator it=specifiedSourceFiles.iterator(); it.hasNext(); ) {
268 File specifiedSourceFile = (File)it.next();
269 printNotice("Loading source file "+specifiedSourceFile+" ...");
270 ClassDocImpl classDoc = parser.processSourceFile(specifiedSourceFile, true, sourceEncoding, null);
271 if (null != classDoc) {
272 specifiedClasses.add(classDoc);
273 classesList.add(classDoc);
274 classDoc.setIsIncluded(true);
275 addPackageDoc(classDoc.containingPackage());
280 //--- Let the user know that all specified classes are loaded.
282 printNotice("Constructing Javadoc information...");
284 //--- Load all classes implicitly referenced by explicitly specified classes.
286 loadScheduledClasses(parser);
288 printNotice("Resolving references in comments...");
290 resolveComments();
292 //--- Resolve pending references in all ClassDocImpls
294 printNotice("Resolving references in classes...");
296 for (Iterator it = classDocMap.values().iterator(); it.hasNext(); ) {
297 ClassDoc cd=(ClassDoc)it.next();
298 if (cd instanceof ClassDocImpl) {
299 ((ClassDocImpl)cd).resolve();
303 //--- Resolve pending references in all PackageDocImpls
305 printNotice("Resolving references in packages...");
307 for (Iterator it = packageDocMap.values().iterator(); it.hasNext(); ) {
308 PackageDocImpl pd=(PackageDocImpl)it.next();
309 pd.resolve();
312 //--- Assemble the array with all specified packages
314 specifiedPackages = new LinkedHashSet();
315 for (Iterator it = specifiedPackageNames.iterator(); it.hasNext(); ) {
316 String specifiedPackageName = (String)it.next();
317 PackageDoc specifiedPackageDoc = (PackageDoc)packageDocMap.get(specifiedPackageName);
318 if (null!=specifiedPackageDoc) {
319 ((PackageDocImpl)specifiedPackageDoc).setIsIncluded(true);
320 specifiedPackages.add(specifiedPackageDoc);
322 ClassDoc[] packageClassDocs=specifiedPackageDoc.allClasses();
323 for (int i=0; i<packageClassDocs.length; ++i) {
324 ClassDocImpl specifiedPackageClassDoc=(ClassDocImpl)packageClassDocs[i];
326 specifiedPackageClassDoc.setIsIncluded(true);
327 classesList.add(specifiedPackageClassDoc);
332 //--- Resolve pending references in comment data of all classes
334 printNotice("Resolving references in class comments...");
336 for (Iterator it=classDocMap.values().iterator(); it.hasNext(); ) {
337 ClassDoc cd=(ClassDoc)it.next();
338 if (cd instanceof ClassDocImpl) {
339 ((ClassDocImpl)cd).resolveComments();
343 //--- Resolve pending references in comment data of all packages
345 printNotice("Resolving references in package comments...");
347 for (Iterator it=packageDocMap.values().iterator(); it.hasNext(); ) {
348 PackageDocImpl pd=(PackageDocImpl)it.next();
349 pd.resolveComments();
352 //--- Create array with all loaded classes
354 this.classes=(ClassDocImpl[])classesList.toArray(new ClassDocImpl[0]);
355 Arrays.sort(this.classes);
357 //--- Close comment cache
359 parser = null;
360 System.gc();
361 System.gc();
364 public long writeRawComment(String rawComment) {
365 try {
366 long pos=rawCommentCache.getFilePointer();
367 //rawCommentCache.writeUTF(rawComment);
368 byte[] bytes = rawComment.getBytes("utf-8");
369 rawCommentCache.writeInt(bytes.length);
370 rawCommentCache.write(bytes);
371 return pos;
373 catch (IOException e) {
374 printFatal("Cannot write to comment cache: "+e.getMessage());
375 return -1;
379 public String readRawComment(long pos) {
380 try {
381 rawCommentCache.seek(pos);
382 int sz = rawCommentCache.readInt();
383 byte[] bytes = new byte[sz];
384 rawCommentCache.read(bytes);
385 return new String(bytes, "utf-8");
386 //return rawCommentCache.readUTF();
388 catch (IOException e) {
389 e.printStackTrace();
390 printFatal("Cannot read from comment cache: "+e.getMessage());
391 return null;
395 List findSourceFiles(String relPath) {
397 List result = new LinkedList();
398 for (Iterator it = sourcePath.iterator(); it.hasNext(); ) {
399 File path = (File)it.next();
400 File file = new File(path, relPath);
401 if (file.exists()) {
402 result.add(file);
406 return result;
409 PackageDocImpl findOrCreatePackageDoc(String packageName) {
410 PackageDocImpl rc=(PackageDocImpl)getPackageDoc(packageName);
411 if (null==rc) {
412 rc=new PackageDocImpl(packageName);
413 if (specifiedPackageNames.contains(packageName)) {
414 String packageDirectoryName = packageName.replace('.', File.separatorChar);
415 List packageDirectories = findSourceFiles(packageDirectoryName);
416 Iterator it = packageDirectories.iterator();
417 boolean packageDocFound = false;
418 while (it.hasNext()) {
419 File packageDirectory = (File)it.next();
420 File packageDocFile = new File(packageDirectory, "package.html");
421 rc.setPackageDirectory(packageDirectory);
422 packageDocFound = true;
423 if (null!=packageDocFile && packageDocFile.exists()) {
424 try {
425 rc.setRawCommentText(readHtmlBody(packageDocFile));
427 catch (IOException e) {
428 printWarning("Error while reading documentation for package "+packageName+": "+e.getMessage());
430 break;
433 if (!packageDocFound) {
434 printNotice("No description found for package "+packageName);
437 addPackageDoc(rc);
439 return rc;
442 public void addClassDoc(ClassDoc cd) {
443 classDocMap.put(cd.qualifiedName(), cd);
446 public void addClassDocRecursive(ClassDoc cd) {
447 classDocMap.put(cd.qualifiedName(), cd);
448 ClassDoc[] innerClasses = cd.innerClasses(false);
449 for (int i=0; i<innerClasses.length; ++i) {
450 addClassDocRecursive(innerClasses[i]);
454 public void addPackageDoc(PackageDoc pd) {
455 packageDocMap.put(pd.name(), pd);
458 public PackageDocImpl getPackageDoc(String name) {
459 return (PackageDocImpl)packageDocMap.get(name);
462 public ClassDocImpl getClassDoc(String qualifiedName) {
463 return (ClassDocImpl)classDocMap.get(qualifiedName);
466 class ScheduledClass {
468 ClassDoc contextClass;
469 String qualifiedName;
470 ScheduledClass(ClassDoc contextClass, String qualifiedName) {
471 this.contextClass=contextClass;
472 this.qualifiedName=qualifiedName;
475 public String toString() { return "ScheduledClass{"+qualifiedName+"}"; }
478 public void scheduleClass(ClassDoc context, String qualifiedName) throws ParseException, IOException {
480 if (classDocMap.get(qualifiedName)==null) {
482 //Debug.log(9,"Scheduling "+qualifiedName+", context "+context+".");
483 //System.err.println("Scheduling " + qualifiedName + ", context " + context);
485 scheduledClasses.add(new ScheduledClass(context, qualifiedName));
490 * Load all classes that were implictly referenced by the classes
491 * (already loaded) that the user explicitly specified on the
492 * command line.
494 * For example, if the user generates Documentation for his simple
495 * 'class Test {}', which of course 'extends java.lang.Object',
496 * then 'java.lang.Object' is implicitly referenced because it is
497 * the base class of Test.
499 * Gjdoc needs a ClassDocImpl representation of all classes
500 * implicitly referenced through derivation (base class),
501 * or implementation (interface), or field type, method argument
502 * type, or method return type.
504 * The task of this method is to ensure that Gjdoc has all this
505 * information at hand when it exits.
509 public void loadScheduledClasses(Parser parser) throws ParseException, IOException {
511 // Because the referenced classes could in turn reference other
512 // classes, this method runs as long as there are still unloaded
513 // classes.
515 while (!scheduledClasses.isEmpty()) {
517 // Make a copy of scheduledClasses and empty it. This
518 // prevents any Concurrent Modification issues.
519 // As the copy won't need to grow (as it won't change)
520 // we make it an Array for performance reasons.
522 ScheduledClass[] scheduledClassesArr = (ScheduledClass[])scheduledClasses.toArray(new ScheduledClass[0]);
523 scheduledClasses.clear();
525 // Load each class specified in our array copy
527 for (int i=0; i<scheduledClassesArr.length; ++i) {
529 // The name of the class we are looking for. This name
530 // needs not be fully qualified.
532 String scheduledClassName=scheduledClassesArr[i].qualifiedName;
534 // The ClassDoc in whose context the scheduled class was looked for.
535 // This is necessary in order to resolve non-fully qualified
536 // class names.
537 ClassDoc scheduledClassContext=scheduledClassesArr[i].contextClass;
539 // If there already is a class doc with this name, skip. There's
540 // nothing to do for us.
541 if (classDocMap.get(scheduledClassName)!=null) {
542 continue;
545 try {
546 // Try to load the class
547 //printNotice("Trying to load " + scheduledClassName);
548 loadScheduledClass(parser, scheduledClassName, scheduledClassContext);
550 catch (ParseException e) {
552 /**********************************************************
554 // Check whether the following is necessary at all.
557 if (scheduledClassName.indexOf('.')>0) {
559 // Maybe the dotted notation doesn't mean a package
560 // name but instead an inner class, as in 'Outer.Inner'.
561 // so let's assume this and try to load the outer class.
563 String outerClass="";
564 for (StringTokenizer st=new StringTokenizer(scheduledClassName,"."); st.hasMoreTokens(); ) {
565 if (outerClass.length()>0) outerClass+=".";
566 outerClass+=st.nextToken();
567 if (!st.hasMoreTokens()) break;
568 try {
569 loadClass(outerClass);
570 //FIXME: shouldn't this be loadScheduledClass(outerClass, scheduledClassContext); ???
571 continue;
573 catch (Exception ee) {
574 // Ignore: try next level
579 **********************************************************/
581 // If we arrive here, the class could not be found
583 printWarning("Couldn't load class "+scheduledClassName+" referenced by "+scheduledClassContext);
585 //FIXME: shouldn't this be throw new Error("cannot load: "+scheduledClassName);
591 private void loadScheduledClass(Parser parser, String scheduledClassName, ClassDoc scheduledClassContext) throws ParseException, IOException {
593 ClassDoc loadedClass=(ClassDoc)scheduledClassContext.findClass(scheduledClassName);
595 if (loadedClass==null || loadedClass instanceof ClassDocProxy) {
597 ClassDoc classDoc = findScheduledClassFile(scheduledClassName, scheduledClassContext);
598 if (null != classDoc) {
600 if (classDoc instanceof ClassDocReflectedImpl) {
601 Main.getRootDoc().addClassDocRecursive(classDoc);
604 if (Main.DESCEND_SUPERCLASS
605 && null != classDoc.superclass()
606 && (classDoc.superclass() instanceof ClassDocProxy)) {
607 scheduleClass(classDoc, classDoc.superclass().qualifiedName());
610 else {
611 // It might be an inner class of one of the outer/super classes.
612 // But we can only check that when they are all fully loaded.
613 boolean retryLater = false;
615 int numberOfProcessedFilesBefore = parser.getNumberOfProcessedFiles();
617 ClassDoc cc = scheduledClassContext.containingClass();
618 while (cc != null && !retryLater) {
619 ClassDoc sc = cc.superclass();
620 while (sc != null && !retryLater) {
621 if (sc instanceof ClassDocProxy) {
622 ((ClassDocImpl)cc).resolve();
623 retryLater = true;
625 sc = sc.superclass();
627 cc = cc.containingClass();
630 // Now that outer/super references have been resolved, try again
631 // to find the class.
633 loadedClass = (ClassDoc)scheduledClassContext.findClass(scheduledClassName);
635 int numberOfProcessedFilesAfter = parser.getNumberOfProcessedFiles();
637 boolean filesWereProcessed = numberOfProcessedFilesAfter > numberOfProcessedFilesBefore;
639 // Only re-schedule class if additional files have been processed
640 // If there haven't, there's no point in re-scheduling.
641 // Will avoid infinite loops of re-scheduling
642 if (null == loadedClass && retryLater && filesWereProcessed)
643 scheduleClass(scheduledClassContext, scheduledClassName);
645 /* A warning needn't be emitted - this is normal, can happen
646 if the scheduled class is in a package which is not
647 included on the command line.
649 else if (null == loadedClass)
650 printWarning("Can't find scheduled class '"
651 + scheduledClassName
652 + "' in context '"
653 + scheduledClassContext.qualifiedName()
654 + "'");
660 private static interface ResolvedImport
662 public String match(String name);
663 public boolean mismatch(String name);
664 public ClassDoc tryFetch(String name);
667 private class ResolvedImportNotFound
668 implements ResolvedImport
670 private String importSpecifier;
671 private String name;
673 ResolvedImportNotFound(String importSpecifier)
675 this.importSpecifier = importSpecifier;
676 int ndx = importSpecifier.lastIndexOf('.');
677 if (ndx >= 0) {
678 this.name = importSpecifier.substring(ndx + 1);
680 else {
681 this.name = importSpecifier;
685 public String toString()
687 return "ResolvedImportNotFound{" + importSpecifier + "}";
690 public String match(String name)
692 if ((name.equals(this.name)) || (importSpecifier.equals(name)))
693 return this.name;
694 // FIXME: note that we don't handle on-demand imports here.
695 return null;
698 public boolean mismatch(String name)
700 return true; // FIXME!
703 public ClassDoc tryFetch(String name)
705 return null;
709 private class ResolvedImportPackageFile
710 implements ResolvedImport
712 private Set topLevelClassNames;
713 private File packageFile;
714 private String packageName;
715 private Map cache = new HashMap();
717 ResolvedImportPackageFile(File packageFile, String packageName)
719 this.packageFile = packageFile;
720 this.packageName = packageName;
721 topLevelClassNames = new HashSet();
722 File[] files = packageFile.listFiles();
723 for (int i=0; i<files.length; ++i) {
724 if (!files[i].isDirectory() && files[i].getName().endsWith(".java")) {
725 String topLevelClassName = files[i].getName();
726 topLevelClassName
727 = topLevelClassName.substring(0, topLevelClassName.length() - 5);
728 topLevelClassNames.add(topLevelClassName);
733 public String match(String name)
735 ClassDoc loadedClass = classNamed(packageName + "." + name);
736 if (null != loadedClass) {
737 return loadedClass.qualifiedName();
739 else {
740 String topLevelName = name;
741 int ndx = topLevelName.indexOf('.');
742 String innerClassName = null;
743 if (ndx > 0) {
744 innerClassName = topLevelName.substring(ndx + 1);
745 topLevelName = topLevelName.substring(0, ndx);
748 if (topLevelClassNames.contains(topLevelName)) {
749 //System.err.println(this + ".match returns " + packageName + "." + name);
750 return packageName + "." + name;
752 // FIXME: inner classes
753 else {
754 return null;
759 public boolean mismatch(String name)
761 return null == match(name);
764 public ClassDoc tryFetch(String name)
766 ClassDoc loadedClass = classNamed(packageName + "." + name);
767 if (null != loadedClass) {
768 return loadedClass;
770 else if (null != match(name)) {
772 String topLevelName = name;
773 int ndx = topLevelName.indexOf('.');
774 String innerClassName = null;
775 if (ndx > 0) {
776 innerClassName = topLevelName.substring(ndx + 1);
777 topLevelName = topLevelName.substring(0, ndx);
780 ClassDoc topLevelClass = (ClassDoc)cache.get(topLevelName);
781 if (null == topLevelClass) {
782 File classFile = new File(packageFile, topLevelName + ".java");
783 try {
784 // FIXME: inner classes
785 topLevelClass = parser.processSourceFile(classFile, false, sourceEncoding, null);
787 catch (Exception ignore) {
788 printWarning("Could not parse source file " + classFile);
790 cache.put(topLevelName, topLevelClass);
792 if (null == innerClassName) {
793 return topLevelClass;
795 else {
796 return getInnerClass(topLevelClass, innerClassName);
799 else {
800 return null;
804 public String toString()
806 return "ResolvedImportPackageFile{" + packageFile + "," + packageName + "}";
810 private ClassDoc getInnerClass(ClassDoc topLevelClass, String innerClassName)
812 StringTokenizer st = new StringTokenizer(innerClassName, ".");
813 outer:
815 while (st.hasMoreTokens()) {
816 String innerClassNameComponent = st.nextToken();
817 ClassDoc[] innerClasses = topLevelClass.innerClasses();
818 for (int i=0; i<innerClasses.length; ++i) {
819 if (innerClasses[i].name().equals(innerClassNameComponent)) {
820 topLevelClass = innerClasses[i];
821 continue outer;
824 printWarning("Could not find inner class " + innerClassName + " in class " + topLevelClass.qualifiedName());
825 return null;
827 return topLevelClass;
830 private class ResolvedImportClassFile
831 implements ResolvedImport
833 private File classFile;
834 private String innerClassName;
835 private String name;
836 private ClassDoc classDoc;
837 private boolean alreadyFetched;
838 private String qualifiedName;
840 ResolvedImportClassFile(File classFile, String innerClassName, String name, String qualifiedName)
842 this.classFile = classFile;
843 this.innerClassName = innerClassName;
844 this.name = name;
845 this.qualifiedName = qualifiedName;
848 public String toString()
850 return "ResolvedImportClassFile{" + classFile + "," + innerClassName + "}";
853 public String match(String name)
855 String topLevelName = name;
856 int ndx = topLevelName.indexOf('.');
858 String _innerClassName = null;
859 if (ndx > 0) {
860 _innerClassName = topLevelName.substring(ndx + 1);
861 topLevelName = topLevelName.substring(0, ndx);
864 if (this.name.equals(topLevelName)) {
865 if (null == _innerClassName) {
866 return qualifiedName;
868 else {
869 return qualifiedName + "." + _innerClassName;
872 else {
873 return null;
877 public boolean mismatch(String name)
879 return null == match(name);
882 public ClassDoc tryFetch(String name)
884 if (null != match(name)) {
885 ClassDoc topLevelClass = null;
886 if (alreadyFetched) {
887 topLevelClass = classDoc;
889 else {
890 alreadyFetched = true;
891 try {
892 topLevelClass = parser.processSourceFile(classFile, false, sourceEncoding, null);
894 catch (Exception ignore) {
895 printWarning("Could not parse source file " + classFile);
898 if (null == topLevelClass) {
899 return null;
901 else {
902 return getInnerClass(topLevelClass, innerClassName);
905 else {
906 return null;
910 public String getName()
912 if (innerClassName != null) {
913 return name + innerClassName;
915 else {
916 return name;
921 private class ResolvedImportReflectionClass
922 implements ResolvedImport
924 private Class clazz;
925 private String name;
927 ResolvedImportReflectionClass(Class clazz)
929 this.clazz = clazz;
930 String className = clazz.getName();
931 int ndx = className.lastIndexOf('.');
932 if (ndx >= 0) {
933 this.name = className.substring(ndx + 1);
935 else {
936 this.name = className;
940 public String toString()
942 return "ResolvedImportReflectionClass{" + clazz.getName() + "}";
945 public String match(String name)
947 if ((this.name.equals(name)) || (clazz.getName().equals(name))) {
948 return clazz.getName();
950 else {
951 return null;
955 public boolean mismatch(String name)
957 return null == match(name);
960 public ClassDoc tryFetch(String name)
962 if (null != match(name)) {
963 return new ClassDocReflectedImpl(clazz);
965 // FIXME: inner classes?
966 else {
967 return null;
971 public String getName()
973 return name;
977 private class ResolvedImportReflectionPackage
978 implements ResolvedImport
980 private String packagePrefix;
982 ResolvedImportReflectionPackage(String packagePrefix)
984 this.packagePrefix = packagePrefix;
987 public String toString()
989 return "ResolvedImportReflectionPackage{" + packagePrefix + ".*}";
992 public String match(String name)
994 try {
995 Class clazz = Class.forName(packagePrefix + "." + name);
996 return clazz.getName();
998 catch (Exception e) {
999 return null;
1003 public boolean mismatch(String name)
1005 return null == match(name);
1008 public ClassDoc tryFetch(String name)
1010 try {
1011 Class clazz = Class.forName(packagePrefix + name);
1012 return ClassDocReflectedImpl.newInstance(clazz);
1014 catch (Exception e) {
1015 return null;
1019 public String getName()
1021 return packagePrefix;
1025 private List unlocatablePrefixes = new LinkedList();
1027 private ResolvedImport resolveImport(String importSpecifier)
1029 ResolvedImport result = resolveImportFileSystem(importSpecifier);
1030 if (null == result && Main.getInstance().isReflectionEnabled()) {
1031 result = resolveImportReflection(importSpecifier);
1033 if (null == result) {
1034 result = new ResolvedImportNotFound(importSpecifier);
1036 return result;
1039 private ResolvedImport resolveImportReflection(String importSpecifier)
1041 String importedPackageOrClass = importSpecifier;
1042 if (importedPackageOrClass.endsWith(".*")) {
1043 importedPackageOrClass = importedPackageOrClass.substring(0, importedPackageOrClass.length() - 2);
1045 return new ResolvedImportReflectionPackage(importedPackageOrClass);
1047 //return null;
1049 else {
1050 try {
1051 Class importedClass = Class.forName(importSpecifier);
1052 return new ResolvedImportReflectionClass(importedClass);
1054 catch (Throwable ignore) {
1055 return null;
1060 private ResolvedImport resolveImportFileSystem(String importSpecifier)
1062 for (Iterator it = unlocatablePrefixes.iterator(); it.hasNext(); ) {
1063 String unlocatablePrefix = (String)it.next();
1064 if (importSpecifier.startsWith(unlocatablePrefix)) {
1065 return null;
1069 String longestUnlocatablePrefix = "";
1071 for (Iterator it=sourcePath.iterator(); it.hasNext(); ) {
1073 File _sourcePath = (File)it.next();
1075 StringBuffer packageOrClassPrefix = new StringBuffer();
1076 StringTokenizer st = new StringTokenizer(importSpecifier, ".");
1077 while (st.hasMoreTokens() && _sourcePath.isDirectory()) {
1078 String token = st.nextToken();
1079 if ("*".equals(token)) {
1080 return new ResolvedImportPackageFile(_sourcePath,
1081 packageOrClassPrefix.substring(0, packageOrClassPrefix.length() - 1));
1083 else {
1084 packageOrClassPrefix.append(token);
1085 packageOrClassPrefix.append('.');
1086 File classFile = new File(_sourcePath, token + ".java");
1087 //System.err.println(" looking for file " + classFile);
1088 if (classFile.exists()) {
1089 StringBuffer innerClassName = new StringBuffer();
1090 while (st.hasMoreTokens()) {
1091 token = st.nextToken();
1092 if (innerClassName.length() > 0) {
1093 innerClassName.append('.');
1095 innerClassName.append(token);
1097 return new ResolvedImportClassFile(classFile, innerClassName.toString(), token, importSpecifier);
1099 else {
1100 _sourcePath = new File(_sourcePath, token);
1104 if (st.hasMoreTokens()) {
1105 if (packageOrClassPrefix.length() > longestUnlocatablePrefix.length()) {
1106 longestUnlocatablePrefix = packageOrClassPrefix.toString();
1111 if (longestUnlocatablePrefix.length() > 0) {
1112 unlocatablePrefixes.add(longestUnlocatablePrefix);
1115 return null;
1118 private Map resolvedImportCache = new HashMap();
1120 private ResolvedImport getResolvedImport(String importSpecifier)
1122 ResolvedImport result
1123 = (ResolvedImport)resolvedImportCache.get(importSpecifier);
1124 if (null == result) {
1125 result = resolveImport(importSpecifier);
1126 resolvedImportCache.put(importSpecifier, result);
1128 return result;
1131 public String resolveClassName(String className, ClassDocImpl context)
1133 Iterator it = context.getImportSpecifierList().iterator();
1134 while (it.hasNext()) {
1135 String importSpecifier = (String)it.next();
1136 ResolvedImport resolvedImport = getResolvedImport(importSpecifier);
1137 String resolvedScheduledClassName = resolvedImport.match(className);
1139 if (null != resolvedScheduledClassName) {
1140 return resolvedScheduledClassName;
1143 return className;
1146 public ClassDoc findScheduledClassFile(String scheduledClassName,
1147 ClassDoc scheduledClassContext)
1148 throws ParseException, IOException
1150 String resolvedScheduledClassName = null;
1152 if (scheduledClassContext instanceof ClassDocImpl) {
1154 //((ClassDocImpl)scheduledClassContext).resolveReferencedName(scheduledClassName);
1155 Iterator it = ((ClassDocImpl)scheduledClassContext).getImportSpecifierList().iterator();
1156 while (it.hasNext()) {
1157 String importSpecifier = (String)it.next();
1158 ResolvedImport resolvedImport = getResolvedImport(importSpecifier);
1159 //System.err.println(" looking in import '" + resolvedImport + "'");
1160 resolvedScheduledClassName = resolvedImport.match(scheduledClassName);
1161 if (null != resolvedScheduledClassName) {
1162 ClassDoc result = resolvedImport.tryFetch(scheduledClassName);
1163 if (null != result) {
1164 return result;
1166 else {
1167 if (!inaccessibleReportedSet.contains(scheduledClassName)) {
1168 inaccessibleReportedSet.add(scheduledClassName);
1169 printWarning("Error while loading class " + scheduledClassName);
1171 // FIXME: output resolved class name here
1172 return null;
1177 else {
1178 System.err.println("findScheduledClassFile for '" + scheduledClassName + "' in proxy for " + scheduledClassContext);
1181 // interpret as fully qualified name on file system
1183 ResolvedImport fqImport = resolveImportFileSystem(scheduledClassName);
1184 if (null != fqImport && fqImport instanceof ResolvedImportClassFile) {
1185 return fqImport.tryFetch(((ResolvedImportClassFile)fqImport).getName());
1188 // use reflection, assume fully qualified class name
1190 if (!unlocatableReflectedClassNames.contains(scheduledClassName)) {
1191 if (Main.getInstance().isReflectionEnabled()) {
1192 try {
1193 Class clazz = Class.forName(scheduledClassName);
1194 printWarning("Cannot locate class " + scheduledClassName + " on file system, falling back to reflection.");
1195 ClassDoc result = new ClassDocReflectedImpl(clazz);
1196 return result;
1198 catch (Throwable ignore) {
1199 unlocatableReflectedClassNames.add(scheduledClassName);
1202 else {
1203 unlocatableReflectedClassNames.add(scheduledClassName);
1207 if (null == resolvedScheduledClassName) {
1208 resolvedScheduledClassName = scheduledClassName;
1210 if (!unlocatableReportedSet.contains(resolvedScheduledClassName)) {
1211 unlocatableReportedSet.add(resolvedScheduledClassName);
1212 printWarning("Cannot locate class " + resolvedScheduledClassName + " referenced in class " + scheduledClassContext.qualifiedName());
1214 return null;
1217 private Set unlocatableReflectedClassNames = new HashSet();
1219 public static boolean recursiveClasses = false;
1221 public void addSpecifiedPackageName(String packageName) {
1222 specifiedPackageNames.add(packageName);
1225 public void addSpecifiedSourceFile(File sourceFile) {
1226 specifiedSourceFiles.add(sourceFile);
1229 public boolean hasSpecifiedPackagesOrClasses() {
1230 return !specifiedPackageNames.isEmpty()
1231 || !specifiedSourceFiles.isEmpty();
1234 public void setOptions(String[][] customOptionArr) {
1235 this.customOptionArr = customOptionArr;
1238 public void setSourcePath(List sourcePath) {
1239 this.sourcePath = sourcePath;
1242 public void finalize() throws Throwable {
1243 super.finalize();
1246 public void flush()
1248 try {
1249 rawCommentCache.close();
1251 catch (IOException e) {
1252 printError("Cannot close raw comment cache");
1255 rawCommentCache = null;
1256 customOptionArr = null;
1257 specifiedPackageNames = null;
1258 classesList = null;
1259 classDocMap = null;
1260 packageDocMap = null;
1261 classes = null;
1262 specifiedClasses = null;
1263 specifiedPackages = null;
1264 scheduledClasses = null;
1265 sourcePath = null;
1266 parser = null;
1267 unlocatableReportedSet = null;
1268 inaccessibleReportedSet = null;
1271 public void setSourceEncoding(String sourceEncoding)
1273 this.sourceEncoding = sourceEncoding;
1276 public RootDocImpl()
1278 super(null);
1281 public static String readHtmlBody(File file)
1282 throws IOException
1284 FileReader fr=new FileReader(file);
1285 long size = file.length();
1286 char[] packageDocBuf=new char[(int)(size)];
1287 int index = 0;
1288 int i = fr.read(packageDocBuf, index, (int)size);
1289 while (i > 0) {
1290 index += i;
1291 size -= i;
1292 i = fr.read(packageDocBuf, index, (int)size);
1294 fr.close();
1296 // We only need the part between the begin and end body tag.
1297 String html = new String(packageDocBuf);
1298 int start = html.indexOf("<body");
1299 if (start == -1)
1300 start = html.indexOf("<BODY");
1301 int end = html.indexOf("</body>");
1302 if (end == -1)
1303 end = html.indexOf("</BODY>");
1304 if (start != -1 && end != -1) {
1305 // Start is end of body tag.
1306 start = html.indexOf('>', start) + 1;
1307 if (start != -1 && start < end)
1308 html = html.substring(start, end);
1310 return html.trim();
1313 public Parser getParser()
1315 return parser;