/* * This file is part of muCommander, http://www.mucommander.com * Copyright (C) 2002-2016 Maxence Bernard * * muCommander is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * muCommander 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.mucommander.extension; import java.io.IOException; import java.util.List; import java.util.Vector; import com.mucommander.commons.file.AbstractFile; import com.mucommander.commons.file.AbstractFileClassLoader; import com.mucommander.commons.file.filter.AttributeFileFilter; import com.mucommander.commons.file.filter.AttributeFileFilter.FileAttribute; import com.mucommander.commons.file.filter.ExtensionFilenameFilter; import com.mucommander.commons.file.filter.OrFileFilter; /** * Finds specific classes within a browsable file. * <p> * This class will explore the content of a browsable {@link com.mucommander.commons.file.AbstractFile} and match * all discovered classes to a {@link ClassFilter}. * </p> * <p> * In order for classes to be analyzed, they need to be loaded. This can be achieved in two ways: * <ul> * <li>By using a custom class loader through {@link #find(AbstractFile,ClassFilter,ClassLoader)}.</li> * <li> * By using an {@link com.mucommander.commons.file.AbstractFileClassLoader} * </ul> * </p> * @author Nicolas Rinaudo */ public class ClassFinder { // - Instance fields ----------------------------------------------------------------- // ----------------------------------------------------------------------------------- /** ClassLoader used to load classes from explored files. */ private ClassLoader loader; /** Used to filter out files that are neither classes nor directories. */ private OrFileFilter filter; /** Used to filter out unwanted classes. */ private ClassFilter classFilter; // - Initialization ------------------------------------------------------------------ // ----------------------------------------------------------------------------------- /** * Creates a new instance of <code>ClassFinder</code>. */ public ClassFinder() { filter = new OrFileFilter( new ExtensionFilenameFilter(".class"), new AttributeFileFilter(FileAttribute.DIRECTORY) ); } // - File exploring ------------------------------------------------------------------ // ----------------------------------------------------------------------------------- /** * Explores the specified file for classes that match {@link #classFilter}. * @param currentPackage package we're currently exploring (with a trailing '.'). * @param currentFile file we're currently exploring. * @return a vector containing all the classes that were found and matched <code>classFilter</code>. * @throws IOException if an error occurs while exploring <code>currentFile</code>. */ private List<Class<?>> find(String currentPackage, AbstractFile currentFile) throws IOException { AbstractFile[] files; // All subfolders or child class files of currentFile. Class<?> currentClass; // Buffer for the current class. List<Class<?>> result = new Vector<Class<?>>(); // Analyses all subdirectories and class files. files = currentFile.ls(filter); for (AbstractFile file : files) { // Explores subdirectories recursively. if (file.isDirectory()) result.addAll(find(currentPackage + file.getName() + '.', file)); // Passes each class through the class filter. // Errors are treated as 'this class is not wanted'. else { try { if (classFilter.accept(currentClass = Class.forName(currentPackage + file.getNameWithoutExtension(), false, loader))) result.add(currentClass); } catch (Throwable e) { } } } return result; } // - Public code --------------------------------------------------------------------- // ----------------------------------------------------------------------------------- /** * Explores the content of the specified file and looks for classes that match the specified class filter. * <p> * The <code>browsable</code> argument must be browsable as defined by {@link com.mucommander.commons.file.AbstractFile#isBrowsable()}. * If such is not the case, the returned vector will be empty. * </p> * @param browsable file in which to look for classes. * @param classFilter how to decide which classes should be kept. * @param classLoader used to load each class found in <code>browsable</code>. * @return a vector containing all the classes that were found and matched <code>classFilter</code>. * @throws IOException if an error occurs while exploring <code>browsable</code>. * @see #find(AbstractFile,ClassFilter) */ public List<Class<?>> find(AbstractFile browsable, ClassFilter classFilter, ClassLoader classLoader) throws IOException { // Ignore non-browsable files. if(!browsable.isBrowsable()) return new Vector<Class<?>>(); // Initializes exploring. loader = classLoader; this.classFilter = classFilter; // Looks for all matched classes in browsable. return find("", browsable); } /** * Explores the content of the specified file and looks for classes that match the specified class filter. * <p> * This is a convenience method and is strictly equivalent to calling {@link #find(AbstractFile,ClassFilter,ClassLoader)} * with a class loader argument initialized with the following code: * <pre> * AbstractFileClassLoader loader; * * loader = new AbstractFileClassLoader(); * loader.addFile(browsable); * </pre> * </p> * @param browsable file in which to look for classes. * @param classFilter how to decide which classes should be kept. * @return a vector containing all the classes that were found and matched <code>classFilter</code>. * @throws IOException if an error occurs while exploring <code>browsable</code>. */ public List<Class<?>> find(AbstractFile browsable, ClassFilter classFilter) throws IOException { AbstractFileClassLoader classLoader; // Default class loader. // Initializes the default class loader. classLoader = new AbstractFileClassLoader(); classLoader.addFile(browsable); // Explores browsable. return find(browsable, classFilter, classLoader); } }