content: /* ========================================================================
   * JCommon : a free general purpose class library for the Java(tm) platform
   * ========================================================================
   *
   * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
   * 
   * Project Info:  http://www.jfree.org/jcommon/index.html
   *
   * This library is free software; you can redistribute it and/or modify it 
   * under the terms of the GNU Lesser General Public License as published by 
   * the Free Software Foundation; either version 2.1 of the License, or 
   * (at your option) any later version.
   *
   * This library 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 Lesser General Public 
   * License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
   * USA.  
   *
   * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
   * in the United States and other countries.]
   * 
   * ------------------------
   * JavaSourceCollector.java
   * ------------------------
   * (C)opyright 2003, 2004, by Thomas Morgner and Contributors.
   *
   * Original Author:  Thomas Morgner;
   * Contributor(s):   David Gilbert (for Object Refinery Limited);
   *
   * $Id: JavaSourceCollector.java,v 1.3 2005/10/18 13:32:20 mungady Exp $
   *
   * Changes
   * -------------------------
   * 21.06.2003 : Initial version
   *
   */
  
  package org.jfree.xml.generator;
  
  import java.io.File;
  import java.io.FileFilter;
  import java.lang.reflect.Modifier;
  import java.util.ArrayList;
  
  import org.jfree.ui.ExtensionFileFilter;
  import org.jfree.util.Log;
  import org.jfree.util.ObjectUtilities;
  
  /**
   * The class collects all class-files and loads the class objects named
   * by these files.
   */
  public class JavaSourceCollector implements SourceCollector {
  
      /**
       * A file filter.
       */
      private static class CollectorFileFilter extends ExtensionFileFilter implements FileFilter {
          /**
           * Creates a new instance.
           * 
           * @param description  the file description.
           * @param extension  the file extension.
           */
          public CollectorFileFilter(final String description, final String extension) {
              super(description, extension);
          }
      }
  
      /** A file filter. */
      private CollectorFileFilter eff;
      
      /** The file list. */
      private ArrayList fileList;
      
      /** A list of ignored packages. */
      private ArrayList ignoredPackages;
  
      /** A list of ignored base classes. */
      private ArrayList ignoredBaseClasses;
      
      /** The start directory. */
      private File startDirectory;
      
      /** The initial package name. */
      private String initialPackageName;
  
      /**
       * Creates a new source collector.
       * 
       * @param startDirectory  the start directory.
       */
      public JavaSourceCollector(final File startDirectory) {
          this(startDirectory, "");
      }
  
      /**
       * Creates a new source collector.
       * 
       * @param startDirectory  the base directory.
       * @param packageName  the base package name.
       */
      public JavaSourceCollector(final File startDirectory, final String packageName) {
          this.eff = new CollectorFileFilter("<ignore>", ".java");
          this.fileList = new ArrayList();
          this.startDirectory = startDirectory;
          this.initialPackageName = packageName;
          this.ignoredPackages = new ArrayList();
          this.ignoredBaseClasses = new ArrayList();
      }
  
      /**
       * Adds a package that should be ignored.
       * 
       * @param pkg  the package name.
       */
      public void addIgnoredPackage(final String pkg) {
          Log.debug (new Log.SimpleMessage("Added IgnPackage: " , pkg));
          this.ignoredPackages.add(pkg);
      }
  
      /**
       * Adds a base class that should be ignored.
       * 
       * @param baseClass  the base class name.
       */
      public void addIgnoredBaseClass(final String baseClass) {
          final Class loadedClass = loadClass(baseClass);
          if (loadedClass != null) {
              Log.debug (new Log.SimpleMessage("Added IgnClass: " , baseClass));
              this.ignoredBaseClasses.add(loadedClass);
          }
      }
  
      /**
       * Adds a class to the list of ignored base classes.
       * 
       * @param baseClass  the class.
       */
      public void addIgnoredBaseClass(final Class baseClass) {
          this.ignoredBaseClasses.add(baseClass);
      }
  
      /**
       * Returns <code>true</code> if the named class is being ignored (because of the package that 
       * it belongs to), and <code>false</code> otherwise.
       * 
       * @param classname  the name of the class to test.
       * 
       * @return A boolean.
       */
      protected boolean isIgnoredPackage(final String classname) {
          for (int i = 0; i < this.ignoredPackages.size(); i++) {
              final String ignoredPackage = (String) this.ignoredPackages.get(i);
              if (classname.startsWith(ignoredPackage)) {
                  return true;
              }
          }
          return false;
      }
  
      /**
       * Returns <code>true</code> if the named class is being ignored (because it is a descendant
       * of an ignored base class), and <code>false</code> otherwise.
       * 
       * @param c  the class name.
       * 
       * @return A boolean.
       */
      protected boolean isIgnoredBaseClass(final Class c) {
          for (int i = 0; i < this.ignoredBaseClasses.size(); i++) {
              final Class ignoredClass = (Class) this.ignoredBaseClasses.get(i);
              if (ignoredClass.isAssignableFrom(c)) {
                  return true;
              }
          }
          return false;
      }
  
      /**
       * Collects the files/classes.
       */
      public void collectFiles() {
          collectFiles(this.startDirectory, this.initialPackageName);
      }
  
      /**
       * Collects the files/classes.
       * 
       * @param directory  the starting directory.
       * @param packageName  the initial package name.
       */
      protected void collectFiles(final File directory, final String packageName) {
          final File[] files = directory.listFiles(this.eff);
          for (int i = 0; i < files.length; i++) {
              if (files[i].isDirectory()) {
                  collectFiles(files[i], buildJavaName(packageName, files[i].getName()));
              }
              else {
                  final String fname = files[i].getName();
                  final String className = fname.substring(0, fname.length() - 5);
                  final String fullName = buildJavaName(packageName, className);
                  if (isIgnoredPackage(fullName)) {
                      Log.debug (new Log.SimpleMessage("Do not process: Ignored: ", className));
                      continue;
                  }
                  final Class jclass = loadClass(fullName);
                  if (jclass == null || isIgnoredBaseClass(jclass)) {
                      continue;
                  }
                  if (jclass.isInterface() || Modifier.isAbstract(jclass.getModifiers())) {
                      Log.debug (new Log.SimpleMessage("Do not process: Abstract: ", className));
                      continue;
                  }
                  if (!Modifier.isPublic(jclass.getModifiers())) {
                      Log.debug (new Log.SimpleMessage("Do not process: Not public: ", className));
                      continue;
                  }
                  this.fileList.add(jclass);
              }
          }
      }
  
      /**
       * Loads a class by its fully qualified name.
       * 
       * @param name  the class name.
       * 
       * @return The class (or <code>null</code> if there was a problem loading the class).
       */
      protected Class loadClass(final String name) {
          try {
              return ObjectUtilities.getClassLoader(JavaSourceCollector.class).loadClass(name);
          }
          catch (Exception e) {
              Log.warn (new Log.SimpleMessage("Do not process: Failed to load class:", name));
              return null;
          }
      }
  
      /**
       * Creates a fully qualified Java class or package name.
       * 
       * @param packageName  the base package name.
       * @param newPackage  the class/package name.
       * 
       * @return The fully qualified package/class name.
       */
      protected String buildJavaName(final String packageName, final String newPackage) {
          if (packageName.length() == 0) {
              return newPackage;
          }
          else {
              return packageName + "." + newPackage;
          }
      }
  
      /**
       * Returns the list of classes as an array.
       * 
       * @return The list of classes.
       */
      public Class[] getClasses() {
          return (Class[]) this.fileList.toArray(new Class[0]);
      }
      
  }
  