001/* ========================================================================
002 * JCommon : a free general purpose class library for the Java(tm) platform
003 * ========================================================================
004 *
005 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
006 * 
007 * Project Info:  http://www.jfree.org/jcommon/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it 
010 * under the terms of the GNU Lesser General Public License as published by 
011 * the Free Software Foundation; either version 2.1 of the License, or 
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but 
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022 * USA.  
023 *
024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025 * in the United States and other countries.]
026 * 
027 * ------------------------
028 * JavaSourceCollector.java
029 * ------------------------
030 * (C)opyright 2003, 2004, by Thomas Morgner and Contributors.
031 *
032 * Original Author:  Thomas Morgner;
033 * Contributor(s):   David Gilbert (for Object Refinery Limited);
034 *
035 * $Id: JavaSourceCollector.java,v 1.3 2005/10/18 13:32:20 mungady Exp $
036 *
037 * Changes
038 * -------------------------
039 * 21.06.2003 : Initial version
040 *
041 */
042
043package org.jfree.xml.generator;
044
045import java.io.File;
046import java.io.FileFilter;
047import java.lang.reflect.Modifier;
048import java.util.ArrayList;
049
050import org.jfree.ui.ExtensionFileFilter;
051import org.jfree.util.Log;
052import org.jfree.util.ObjectUtilities;
053
054/**
055 * The class collects all class-files and loads the class objects named
056 * by these files.
057 */
058public class JavaSourceCollector implements SourceCollector {
059
060    /**
061     * A file filter.
062     */
063    private static class CollectorFileFilter extends ExtensionFileFilter implements FileFilter {
064        /**
065         * Creates a new instance.
066         * 
067         * @param description  the file description.
068         * @param extension  the file extension.
069         */
070        public CollectorFileFilter(final String description, final String extension) {
071            super(description, extension);
072        }
073    }
074
075    /** A file filter. */
076    private CollectorFileFilter eff;
077    
078    /** The file list. */
079    private ArrayList fileList;
080    
081    /** A list of ignored packages. */
082    private ArrayList ignoredPackages;
083
084    /** A list of ignored base classes. */
085    private ArrayList ignoredBaseClasses;
086    
087    /** The start directory. */
088    private File startDirectory;
089    
090    /** The initial package name. */
091    private String initialPackageName;
092
093    /**
094     * Creates a new source collector.
095     * 
096     * @param startDirectory  the start directory.
097     */
098    public JavaSourceCollector(final File startDirectory) {
099        this(startDirectory, "");
100    }
101
102    /**
103     * Creates a new source collector.
104     * 
105     * @param startDirectory  the base directory.
106     * @param packageName  the base package name.
107     */
108    public JavaSourceCollector(final File startDirectory, final String packageName) {
109        this.eff = new CollectorFileFilter("<ignore>", ".java");
110        this.fileList = new ArrayList();
111        this.startDirectory = startDirectory;
112        this.initialPackageName = packageName;
113        this.ignoredPackages = new ArrayList();
114        this.ignoredBaseClasses = new ArrayList();
115    }
116
117    /**
118     * Adds a package that should be ignored.
119     * 
120     * @param pkg  the package name.
121     */
122    public void addIgnoredPackage(final String pkg) {
123        Log.debug (new Log.SimpleMessage("Added IgnPackage: " , pkg));
124        this.ignoredPackages.add(pkg);
125    }
126
127    /**
128     * Adds a base class that should be ignored.
129     * 
130     * @param baseClass  the base class name.
131     */
132    public void addIgnoredBaseClass(final String baseClass) {
133        final Class loadedClass = loadClass(baseClass);
134        if (loadedClass != null) {
135            Log.debug (new Log.SimpleMessage("Added IgnClass: " , baseClass));
136            this.ignoredBaseClasses.add(loadedClass);
137        }
138    }
139
140    /**
141     * Adds a class to the list of ignored base classes.
142     * 
143     * @param baseClass  the class.
144     */
145    public void addIgnoredBaseClass(final Class baseClass) {
146        this.ignoredBaseClasses.add(baseClass);
147    }
148
149    /**
150     * Returns <code>true</code> if the named class is being ignored (because of the package that 
151     * it belongs to), and <code>false</code> otherwise.
152     * 
153     * @param classname  the name of the class to test.
154     * 
155     * @return A boolean.
156     */
157    protected boolean isIgnoredPackage(final String classname) {
158        for (int i = 0; i < this.ignoredPackages.size(); i++) {
159            final String ignoredPackage = (String) this.ignoredPackages.get(i);
160            if (classname.startsWith(ignoredPackage)) {
161                return true;
162            }
163        }
164        return false;
165    }
166
167    /**
168     * Returns <code>true</code> if the named class is being ignored (because it is a descendant
169     * of an ignored base class), and <code>false</code> otherwise.
170     * 
171     * @param c  the class name.
172     * 
173     * @return A boolean.
174     */
175    protected boolean isIgnoredBaseClass(final Class c) {
176        for (int i = 0; i < this.ignoredBaseClasses.size(); i++) {
177            final Class ignoredClass = (Class) this.ignoredBaseClasses.get(i);
178            if (ignoredClass.isAssignableFrom(c)) {
179                return true;
180            }
181        }
182        return false;
183    }
184
185    /**
186     * Collects the files/classes.
187     */
188    public void collectFiles() {
189        collectFiles(this.startDirectory, this.initialPackageName);
190    }
191
192    /**
193     * Collects the files/classes.
194     * 
195     * @param directory  the starting directory.
196     * @param packageName  the initial package name.
197     */
198    protected void collectFiles(final File directory, final String packageName) {
199        final File[] files = directory.listFiles(this.eff);
200        for (int i = 0; i < files.length; i++) {
201            if (files[i].isDirectory()) {
202                collectFiles(files[i], buildJavaName(packageName, files[i].getName()));
203            }
204            else {
205                final String fname = files[i].getName();
206                final String className = fname.substring(0, fname.length() - 5);
207                final String fullName = buildJavaName(packageName, className);
208                if (isIgnoredPackage(fullName)) {
209                    Log.debug (new Log.SimpleMessage("Do not process: Ignored: ", className));
210                    continue;
211                }
212                final Class jclass = loadClass(fullName);
213                if (jclass == null || isIgnoredBaseClass(jclass)) {
214                    continue;
215                }
216                if (jclass.isInterface() || Modifier.isAbstract(jclass.getModifiers())) {
217                    Log.debug (new Log.SimpleMessage("Do not process: Abstract: ", className));
218                    continue;
219                }
220                if (!Modifier.isPublic(jclass.getModifiers())) {
221                    Log.debug (new Log.SimpleMessage("Do not process: Not public: ", className));
222                    continue;
223                }
224                this.fileList.add(jclass);
225            }
226        }
227    }
228
229    /**
230     * Loads a class by its fully qualified name.
231     * 
232     * @param name  the class name.
233     * 
234     * @return The class (or <code>null</code> if there was a problem loading the class).
235     */
236    protected Class loadClass(final String name) {
237        try {
238            return ObjectUtilities.getClassLoader(JavaSourceCollector.class).loadClass(name);
239        }
240        catch (Exception e) {
241            Log.warn (new Log.SimpleMessage("Do not process: Failed to load class:", name));
242            return null;
243        }
244    }
245
246    /**
247     * Creates a fully qualified Java class or package name.
248     * 
249     * @param packageName  the base package name.
250     * @param newPackage  the class/package name.
251     * 
252     * @return The fully qualified package/class name.
253     */
254    protected String buildJavaName(final String packageName, final String newPackage) {
255        if (packageName.length() == 0) {
256            return newPackage;
257        }
258        else {
259            return packageName + "." + newPackage;
260        }
261    }
262
263    /**
264     * Returns the list of classes as an array.
265     * 
266     * @return The list of classes.
267     */
268    public Class[] getClasses() {
269        return (Class[]) this.fileList.toArray(new Class[0]);
270    }
271    
272}