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    
043    package org.jfree.xml.generator;
044    
045    import java.io.File;
046    import java.io.FileFilter;
047    import java.lang.reflect.Modifier;
048    import java.util.ArrayList;
049    
050    import org.jfree.ui.ExtensionFileFilter;
051    import org.jfree.util.Log;
052    import org.jfree.util.ObjectUtilities;
053    
054    /**
055     * The class collects all class-files and loads the class objects named
056     * by these files.
057     */
058    public 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    }