Save This Page
Home » qdox-sources » com.thoughtworks.qdox » [javadoc | source]
    1   package com.thoughtworks.qdox;
    2   
    3   import com.thoughtworks.qdox.directorywalker.DirectoryScanner;
    4   import com.thoughtworks.qdox.directorywalker.FileVisitor;
    5   import com.thoughtworks.qdox.directorywalker.SuffixFilter;
    6   import com.thoughtworks.qdox.model.ClassLibrary;
    7   import com.thoughtworks.qdox.model.DefaultDocletTagFactory;
    8   import com.thoughtworks.qdox.model.DocletTagFactory;
    9   import com.thoughtworks.qdox.model.JavaClass;
   10   import com.thoughtworks.qdox.model.JavaClassCache;
   11   import com.thoughtworks.qdox.model.JavaSource;
   12   import com.thoughtworks.qdox.model.ModelBuilder;
   13   import com.thoughtworks.qdox.parser.Lexer;
   14   import com.thoughtworks.qdox.parser.ParseException;
   15   import com.thoughtworks.qdox.parser.impl.JFlexLexer;
   16   import com.thoughtworks.qdox.parser.impl.Parser;
   17   import com.thoughtworks.qdox.parser.structs.ClassDef;
   18   import com.thoughtworks.qdox.parser.structs.FieldDef;
   19   import com.thoughtworks.qdox.parser.structs.MethodDef;
   20   
   21   import java.io.File;
   22   import java.io.FileInputStream;
   23   import java.io.FileNotFoundException;
   24   import java.io.FileOutputStream;
   25   import java.io.IOException;
   26   import java.io.InputStreamReader;
   27   import java.io.ObjectInputStream;
   28   import java.io.ObjectOutputStream;
   29   import java.io.Reader;
   30   import java.io.Serializable;
   31   import java.io.UnsupportedEncodingException;
   32   import java.lang.reflect.Constructor;
   33   import java.lang.reflect.Field;
   34   import java.lang.reflect.Member;
   35   import java.lang.reflect.Method;
   36   import java.lang.reflect.Modifier;
   37   import java.net.URL;
   38   import java.util.ArrayList;
   39   import java.util.HashMap;
   40   import java.util.HashSet;
   41   import java.util.Iterator;
   42   import java.util.LinkedList;
   43   import java.util.List;
   44   import java.util.Map;
   45   import java.util.Set;
   46   import java.util.StringTokenizer;
   47   
   48   /**
   49    * Simple facade to QDox allowing a source tree to be parsed and the resulting object model navigated.
   50    *
   51    * <h3>Example</h3>
   52    * <pre><code>
   53    * // -- Create JavaDocBuilder
   54    *
   55    * JavaDocBuilder builder = new JavaDocBuilder();
   56    *
   57    * // -- Add some files
   58    *
   59    * // Reading a single source file.
   60    * builder.addSource(new FileReader("MyFile.java"));
   61    *
   62    * // Reading from another kind of input stream.
   63    * builder.addSource(new StringReader("package test; public class Hello {}"));
   64    *
   65    * // Adding all .java files in a source tree (recursively).
   66    * builder.addSourceTree(new File("mysrcdir"));
   67    *
   68    * // -- Retrieve source files
   69    *
   70    * JavaSource[] source = builder.getSources();
   71    *
   72    * </code></pre>
   73    *
   74    * @author <a href="mailto:joew@thoughtworks.com">Joe Walnes</a>
   75    * @author Aslak Helles&oslash;y
   76    */
   77   public class JavaDocBuilder implements Serializable, JavaClassCache {
   78   
   79       private Map classes = new HashMap();
   80       private ClassLibrary classLibrary;
   81       private List sources = new ArrayList();
   82       private DocletTagFactory docletTagFactory;
   83       private String encoding = System.getProperty("file.encoding");
   84       private boolean debugLexer;
   85       private boolean debugParser;
   86   
   87       public JavaDocBuilder() {
   88           this(new DefaultDocletTagFactory());
   89       }
   90   
   91       public JavaDocBuilder(DocletTagFactory docletTagFactory) {
   92           this.docletTagFactory = docletTagFactory;
   93           classLibrary = new ClassLibrary(this);
   94           classLibrary.addDefaultLoader();
   95       }
   96   
   97       private void addClasses(JavaSource source) {
   98           Set resultSet = new HashSet();
   99           addClassesRecursive(source, resultSet);
  100           JavaClass[] javaClasses = (JavaClass[]) resultSet.toArray(new JavaClass[resultSet.size()]);
  101           for (int classIndex = 0; classIndex < javaClasses.length; classIndex++) {
  102               JavaClass cls = javaClasses[classIndex];
  103               addClass(cls);
  104           }
  105       }
  106   
  107       private void addClass(JavaClass cls) {
  108           classes.put(cls.getFullyQualifiedName(), cls);
  109           cls.setJavaClassCache(this);
  110       }
  111   
  112       public JavaClass getClassByName(String name) {
  113           if (name == null) {
  114               return null;
  115           }
  116           JavaClass result = (JavaClass) classes.get(name);
  117           if (result == null) {
  118               // Try to make a binary class out of it
  119               result = createBinaryClass(name);
  120               if (result != null) {
  121                   addClass(result);
  122               } else {
  123                   result = createUnknownClass(name);
  124               }
  125           }
  126           return result;
  127       }
  128   
  129       private JavaClass createUnknownClass(String name) {
  130           ModelBuilder unknownBuilder = new ModelBuilder(classLibrary, docletTagFactory);
  131           ClassDef classDef = new ClassDef();
  132           classDef.name = name;
  133           unknownBuilder.beginClass(classDef);
  134           unknownBuilder.endClass();
  135           JavaSource unknownSource = unknownBuilder.getSource();
  136           JavaClass result = unknownSource.getClasses()[0];
  137           return result;
  138       }
  139   
  140       private JavaClass createBinaryClass(String name) {
  141           // First see if the class exists at all.
  142           Class clazz = classLibrary.getClass(name);
  143           if (clazz == null) {
  144               return null;
  145           } else {
  146               // Create a new builder and mimic the behaviour of the parser.
  147               // We're getting all the information we need via reflection instead.
  148               ModelBuilder binaryBuilder = new ModelBuilder(classLibrary, docletTagFactory);
  149   
  150               // Set the package name and class name
  151               String packageName = getPackageName(name);
  152               binaryBuilder.addPackage(packageName);
  153   
  154               ClassDef classDef = new ClassDef();
  155               classDef.name = getClassName(name);
  156   
  157               // Set the extended class and interfaces.
  158               Class[] interfaces = clazz.getInterfaces();
  159               if (clazz.isInterface()) {
  160                   // It's an interface
  161                   classDef.type = ClassDef.INTERFACE;
  162                   for (int i = 0; i < interfaces.length; i++) {
  163                       Class anInterface = interfaces[i];
  164                       classDef.extendz.add(anInterface.getName());
  165                   }
  166               } else {
  167                   // It's a class
  168                   for (int i = 0; i < interfaces.length; i++) {
  169                       Class anInterface = interfaces[i];
  170                       classDef.implementz.add(anInterface.getName());
  171                   }
  172                   Class superclass = clazz.getSuperclass();
  173                   if (superclass != null) {
  174                       classDef.extendz.add(superclass.getName());
  175                   }
  176               }
  177   
  178               addModifiers(classDef.modifiers, clazz.getModifiers());
  179   
  180               binaryBuilder.beginClass(classDef);
  181   
  182               // add the constructors
  183               Constructor[] constructors = clazz.getConstructors();
  184               for (int i = 0; i < constructors.length; i++) {
  185                   addMethodOrConstructor(constructors[i], binaryBuilder);
  186               }
  187   
  188               // add the methods
  189               Method[] methods = clazz.getMethods();
  190               for (int i = 0; i < methods.length; i++) {
  191                   // Ignore methods defined in superclasses
  192                   if (methods[i].getDeclaringClass() == clazz) {
  193                       addMethodOrConstructor(methods[i], binaryBuilder);
  194                   }
  195               }
  196   
  197               Field[] fields = clazz.getFields();
  198               for (int i = 0; i < fields.length; i++) {
  199                   if (fields[i].getDeclaringClass() == clazz) {
  200                       addField(fields[i], binaryBuilder);
  201                   }
  202               }
  203   
  204               binaryBuilder.endClass();
  205               JavaSource binarySource = binaryBuilder.getSource();
  206               // There is always only one class in a "binary" source.
  207               JavaClass result = binarySource.getClasses()[0];
  208               return result;
  209           }
  210       }
  211   
  212       private void addModifiers(Set set, int modifier) {
  213           String modifierString = Modifier.toString(modifier);
  214           for (StringTokenizer stringTokenizer = new StringTokenizer(modifierString); stringTokenizer.hasMoreTokens();) {
  215               set.add(stringTokenizer.nextToken());
  216           }
  217       }
  218   
  219       private void addField(Field field, ModelBuilder binaryBuilder) {
  220           FieldDef fieldDef = new FieldDef();
  221           Class fieldType = field.getType();
  222           fieldDef.name = field.getName();
  223           fieldDef.type = getTypeName(fieldType);
  224           fieldDef.dimensions = getDimension(fieldType);
  225           binaryBuilder.addField(fieldDef);
  226       }
  227   
  228       private void addMethodOrConstructor(Member member, ModelBuilder binaryBuilder) {
  229           MethodDef methodDef = new MethodDef();
  230           // The name of constructors are qualified. Need to strip it.
  231           // This will work for regular methods too, since -1 + 1 = 0
  232           int lastDot = member.getName().lastIndexOf('.');
  233           methodDef.name = member.getName().substring(lastDot + 1);
  234   
  235           addModifiers(methodDef.modifiers, member.getModifiers());
  236           Class[] exceptions;
  237           Class[] parameterTypes;
  238           if (member instanceof Method) {
  239               methodDef.constructor = false;
  240   
  241               // For some stupid reason, these methods are not defined in Member,
  242               // but in both Method and Construcotr.
  243               exceptions = ((Method) member).getExceptionTypes();
  244               parameterTypes = ((Method) member).getParameterTypes();
  245   
  246               Class returnType = ((Method) member).getReturnType();
  247               methodDef.returns = getTypeName(returnType);
  248               methodDef.dimensions = getDimension(returnType);
  249   
  250           } else {
  251               methodDef.constructor = true;
  252   
  253               exceptions = ((Constructor) member).getExceptionTypes();
  254               parameterTypes = ((Constructor) member).getParameterTypes();
  255           }
  256           for (int j = 0; j < exceptions.length; j++) {
  257               Class exception = exceptions[j];
  258               methodDef.exceptions.add(exception.getName());
  259           }
  260           for (int j = 0; j < parameterTypes.length; j++) {
  261               FieldDef param = new FieldDef();
  262               Class parameterType = parameterTypes[j];
  263               param.name = "p" + j;
  264               param.type = getTypeName(parameterType);
  265               param.dimensions = getDimension(parameterType);
  266               methodDef.params.add(param);
  267           }
  268           binaryBuilder.addMethod(methodDef);
  269       }
  270   
  271       private static final int getDimension(Class c) {
  272           return c.getName().lastIndexOf('[') + 1;
  273       }
  274   
  275       private static String getTypeName(Class c) {
  276           return c.getComponentType() != null ? c.getComponentType().getName() : c.getName();
  277       }
  278   
  279       private String getPackageName(String fullClassName) {
  280           int lastDot = fullClassName.lastIndexOf('.');
  281           return lastDot == -1 ? "" : fullClassName.substring(0, lastDot);
  282       }
  283   
  284       private String getClassName(String fullClassName) {
  285           int lastDot = fullClassName.lastIndexOf('.');
  286           return lastDot == -1 ? fullClassName : fullClassName.substring(lastDot + 1);
  287       }
  288   
  289       public JavaSource addSource(Reader reader) {
  290           return addSource(reader, "UNKNOWN SOURCE");
  291       }
  292   
  293       public JavaSource addSource(Reader reader, String sourceInfo) {
  294           ModelBuilder builder = new ModelBuilder(classLibrary, docletTagFactory);
  295           Lexer lexer = new JFlexLexer(reader);
  296           Parser parser = new Parser(lexer, builder);
  297           parser.setDebugLexer(debugLexer);
  298           parser.setDebugParser(debugParser);
  299           try {
  300               parser.parse();
  301           } catch (ParseException e) {
  302               e.setSourceInfo(sourceInfo);
  303               throw e;
  304           }
  305           JavaSource source = builder.getSource();
  306           sources.add(source);
  307           addClasses(source);
  308           return source;
  309       }
  310   
  311       public JavaSource addSource(File file) throws IOException, FileNotFoundException {
  312           return addSource(file.toURL());
  313       }
  314   
  315       public JavaSource addSource(URL url) throws IOException, FileNotFoundException {
  316           JavaSource source = addSource(new InputStreamReader(url.openStream(),encoding), url.toExternalForm());
  317           source.setURL(url);
  318           return source;
  319       }
  320   
  321       public JavaSource[] getSources() {
  322           return (JavaSource[]) sources.toArray(new JavaSource[sources.size()]);
  323       }
  324   
  325       /**
  326        * Returns all the classes found in all the sources, including inner classes
  327        * and "extra" classes (multiple outer classes defined in the same source file).
  328        *
  329        * @return all the classes found in all the sources.
  330        * @since 1.3
  331        */
  332       public JavaClass[] getClasses() {
  333           Set resultSet = new HashSet();
  334           JavaSource[] javaSources = getSources();
  335           for (int i = 0; i < javaSources.length; i++) {
  336               JavaSource javaSource = javaSources[i];
  337               addClassesRecursive(javaSource, resultSet);
  338           }
  339           JavaClass[] result = (JavaClass[]) resultSet.toArray(new JavaClass[resultSet.size()]);
  340           return result;
  341       }
  342   
  343       private void addClassesRecursive(JavaSource javaSource, Set resultSet) {
  344           JavaClass[] classes = javaSource.getClasses();
  345           for (int j = 0; j < classes.length; j++) {
  346               JavaClass javaClass = classes[j];
  347               addClassesRecursive(javaClass, resultSet);
  348           }
  349       }
  350   
  351       private void addClassesRecursive(JavaClass javaClass, Set set) {
  352           // Add the class...
  353           set.add(javaClass);
  354   
  355           // And recursively all of its inner classes
  356           JavaClass[] innerClasses = javaClass.getNestedClasses();
  357           for (int i = 0; i < innerClasses.length; i++) {
  358               JavaClass innerClass = innerClasses[i];
  359               addClassesRecursive(innerClass, set);
  360           }
  361       }
  362   
  363       public void addSourceTree(File file) {
  364           DirectoryScanner scanner = new DirectoryScanner(file);
  365           scanner.addFilter(new SuffixFilter(".java"));
  366           scanner.scan(new FileVisitor() {
  367               public void visitFile(File currentFile) {
  368                   try {
  369                       addSource(currentFile);
  370                   } catch (UnsupportedEncodingException e) {
  371   					throw new RuntimeException("Unsupported encoding : " + encoding);
  372                   } catch (IOException e) {
  373                       throw new RuntimeException("Cannot read file : " + currentFile.getName());
  374                   }
  375               }
  376           });
  377       }
  378   
  379       public List search(Searcher searcher) {
  380           List results = new LinkedList();
  381           for (Iterator iterator = classLibrary.all().iterator(); iterator.hasNext();) {
  382               String clsName = (String) iterator.next();
  383               JavaClass cls = getClassByName(clsName);
  384               if (searcher.eval(cls)) {
  385                   results.add(cls);
  386               }
  387           }
  388           return results;
  389       }
  390   
  391       public ClassLibrary getClassLibrary() {
  392           return classLibrary;
  393       }
  394   
  395       public void save(File file) throws IOException {
  396           FileOutputStream fos = new FileOutputStream(file);
  397           ObjectOutputStream out = new ObjectOutputStream(fos);
  398           try {
  399               out.writeObject(this);
  400           } finally {
  401               out.close();
  402               fos.close();
  403           }
  404       }
  405   
  406       /**
  407        * Note that after loading JavaDocBuilder classloaders need to be re-added.
  408        */
  409       public static JavaDocBuilder load(File file) throws IOException {
  410           FileInputStream fis = new FileInputStream(file);
  411           ObjectInputStream in = new ObjectInputStream(fis);
  412           JavaDocBuilder builder = null;
  413           try {
  414               builder = (JavaDocBuilder) in.readObject();
  415           } catch (ClassNotFoundException e) {
  416               throw new Error("Couldn't load class : " + e.getMessage());
  417           } finally {
  418               in.close();
  419               fis.close();
  420           }
  421           return builder;
  422       }
  423   
  424       public void setEncoding(String encoding) {
  425           this.encoding = encoding;		
  426       }
  427   
  428       /**
  429        * Forces QDox to dump tokens returned from lexer to System.err.
  430        */
  431       public void setDebugLexer(boolean debugLexer) {
  432           this.debugLexer = debugLexer;
  433       }
  434   
  435       /**
  436        * Forces QDox to dump parser states to System.out.
  437        */
  438       public void setDebugParser(boolean debugParser) {
  439           this.debugParser = debugParser;
  440       }
  441   
  442   }

Save This Page
Home » qdox-sources » com.thoughtworks.qdox » [javadoc | source]