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)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 package gnu
.classpath
.tools
.gjdoc
;
23 import com
.sun
.javadoc
.*;
26 import java
.lang
.reflect
.*;
28 public class RootDocImpl
30 implements GjdocRootDoc
{
32 private ErrorReporter reporter
= new ErrorReporter();
34 private RandomAccessFile rawCommentCache
;
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
;
46 * All source files explicitly specified on the command line.
50 private List specifiedSourceFiles
= new LinkedList();
53 * The names of all packages explicitly specified on the
58 private Set specifiedPackageNames
= new LinkedHashSet();
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();
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();
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();
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
;
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
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
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
);
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
);
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() {
198 public ErrorReporter
getReporter() {
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");
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
+"...");
243 if (null != specifiedPackageName
) {
244 relPath
= specifiedPackageName
.replace('.',File
.separatorChar
);
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
);
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...");
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();
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
364 public long writeRawComment(String rawComment
) {
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
);
373 catch (IOException e
) {
374 printFatal("Cannot write to comment cache: "+e
.getMessage());
379 public String
readRawComment(long pos
) {
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
) {
390 printFatal("Cannot read from comment cache: "+e
.getMessage());
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
);
409 PackageDocImpl
findOrCreatePackageDoc(String packageName
) {
410 PackageDocImpl rc
=(PackageDocImpl
)getPackageDoc(packageName
);
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()) {
425 rc
.setRawCommentText(readHtmlBody(packageDocFile
));
427 catch (IOException e
) {
428 printWarning("Error while reading documentation for package "+packageName
+": "+e
.getMessage());
433 if (!packageDocFound
) {
434 printNotice("No description found for package "+packageName
);
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
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
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
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) {
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;
569 loadClass(outerClass);
570 //FIXME: shouldn't this be loadScheduledClass(outerClass, scheduledClassContext); ???
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());
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();
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 '"
653 + scheduledClassContext.qualifiedName()
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
;
673 ResolvedImportNotFound(String importSpecifier
)
675 this.importSpecifier
= importSpecifier
;
676 int ndx
= importSpecifier
.lastIndexOf('.');
678 this.name
= importSpecifier
.substring(ndx
+ 1);
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
)))
694 // FIXME: note that we don't handle on-demand imports here.
698 public boolean mismatch(String name
)
700 return true; // FIXME!
703 public ClassDoc
tryFetch(String name
)
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();
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();
740 String topLevelName
= name
;
741 int ndx
= topLevelName
.indexOf('.');
742 String innerClassName
= null;
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
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
) {
770 else if (null != match(name
)) {
772 String topLevelName
= name
;
773 int ndx
= topLevelName
.indexOf('.');
774 String innerClassName
= null;
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");
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
;
796 return getInnerClass(topLevelClass
, innerClassName
);
804 public String
toString()
806 return "ResolvedImportPackageFile{" + packageFile
+ "," + packageName
+ "}";
810 private ClassDoc
getInnerClass(ClassDoc topLevelClass
, String innerClassName
)
812 StringTokenizer st
= new StringTokenizer(innerClassName
, ".");
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
];
824 printWarning("Could not find inner class " + innerClassName
+ " in class " + topLevelClass
.qualifiedName());
827 return topLevelClass
;
830 private class ResolvedImportClassFile
831 implements ResolvedImport
833 private File classFile
;
834 private String innerClassName
;
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
;
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;
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
;
869 return qualifiedName
+ "." + _innerClassName
;
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
;
890 alreadyFetched
= true;
892 topLevelClass
= parser
.processSourceFile(classFile
, false, sourceEncoding
, null);
894 catch (Exception ignore
) {
895 printWarning("Could not parse source file " + classFile
);
898 if (null == topLevelClass
) {
902 return getInnerClass(topLevelClass
, innerClassName
);
910 public String
getName()
912 if (innerClassName
!= null) {
913 return name
+ innerClassName
;
921 private class ResolvedImportReflectionClass
922 implements ResolvedImport
927 ResolvedImportReflectionClass(Class clazz
)
930 String className
= clazz
.getName();
931 int ndx
= className
.lastIndexOf('.');
933 this.name
= className
.substring(ndx
+ 1);
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();
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?
971 public String
getName()
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
)
995 Class clazz
= Class
.forName(packagePrefix
+ "." + name
);
996 return clazz
.getName();
998 catch (Exception e
) {
1003 public boolean mismatch(String name
)
1005 return null == match(name
);
1008 public ClassDoc
tryFetch(String name
)
1011 Class clazz
= Class
.forName(packagePrefix
+ name
);
1012 return ClassDocReflectedImpl
.newInstance(clazz
);
1014 catch (Exception e
) {
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
);
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
);
1051 Class importedClass
= Class
.forName(importSpecifier
);
1052 return new ResolvedImportReflectionClass(importedClass
);
1054 catch (Throwable ignore
) {
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
)) {
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));
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
);
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
);
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
);
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
;
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
) {
1167 if (!inaccessibleReportedSet
.contains(scheduledClassName
)) {
1168 inaccessibleReportedSet
.add(scheduledClassName
);
1169 printWarning("Error while loading class " + scheduledClassName
);
1171 // FIXME: output resolved class name here
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()) {
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
);
1198 catch (Throwable ignore
) {
1199 unlocatableReflectedClassNames
.add(scheduledClassName
);
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());
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
{
1249 rawCommentCache
.close();
1251 catch (IOException e
) {
1252 printError("Cannot close raw comment cache");
1255 rawCommentCache
= null;
1256 customOptionArr
= null;
1257 specifiedPackageNames
= null;
1260 packageDocMap
= null;
1262 specifiedClasses
= null;
1263 specifiedPackages
= null;
1264 scheduledClasses
= null;
1267 unlocatableReportedSet
= null;
1268 inaccessibleReportedSet
= null;
1271 public void setSourceEncoding(String sourceEncoding
)
1273 this.sourceEncoding
= sourceEncoding
;
1276 public RootDocImpl()
1281 public static String
readHtmlBody(File file
)
1284 FileReader fr
=new FileReader(file
);
1285 long size
= file
.length();
1286 char[] packageDocBuf
=new char[(int)(size
)];
1288 int i
= fr
.read(packageDocBuf
, index
, (int)size
);
1292 i
= fr
.read(packageDocBuf
, index
, (int)size
);
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");
1300 start
= html
.indexOf("<BODY");
1301 int end
= html
.indexOf("</body>");
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
);
1313 public Parser
getParser()