uk.co.danielrendall.imagetiler.utils.PackageFinder.java Source code

Java tutorial

Introduction

Here is the source code for uk.co.danielrendall.imagetiler.utils.PackageFinder.java

Source

/*
 * Copyright (c) 2009, 2010, 2011 Daniel Rendall
 * This file is part of ImageTiler.
 *
 * ImageTiler 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.
 *
 * ImageTiler 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 ImageTiler.  If not, see <http://www.gnu.org/licenses/>
 */

package uk.co.danielrendall.imagetiler.utils;

import org.apache.commons.lang.StringUtils;
import uk.co.danielrendall.imagetiler.logging.Log;
import uk.co.danielrendall.imagetiler.shared.ScannerStrategy;
import uk.co.danielrendall.imagetiler.svg.SVGTile;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @author Daniel Rendall
 */
public class PackageFinder {
    private final List<ClassInfo> tileClasses;
    private final List<ClassInfo> strategyClasses;

    public static PackageFinder create() {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        List<ClassInfo> tileClasses = new ArrayList<ClassInfo>();
        List<ClassInfo> strategyClasses = new ArrayList<ClassInfo>();
        assert classLoader != null;
        try {
            Log.app.info("Finding packages.properties files");
            Enumeration<URL> resources = classLoader.getResources("packages.properties");
            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                Log.app.info("Found file " + resource.toExternalForm());
                Properties props = new Properties();
                props.load(resource.openStream());
                String author = props.getProperty("imagetiler.author");
                String url = props.getProperty("imagetiler.url");
                String tiles = props.getProperty("imagetiler.tiles");
                String strategy = props.getProperty("imagetiler.strategy");
                if (StringUtils.isNotBlank(tiles)) {
                    Log.app.debug("Adding tiles in package '" + tiles + "'");
                    addAllClasses(SVGTile.class, tileClasses, tiles, author, url);
                }
                if (StringUtils.isNotBlank(strategy)) {
                    Log.app.debug("Adding strategies in package '" + strategy + "'");
                    addAllClasses(ScannerStrategy.class, strategyClasses, strategy, author, url);
                }
            }
        } catch (Exception e) {
            Log.app.warn("Couldn't get tile classes", e);
        }
        return new PackageFinder(Collections.unmodifiableList(tileClasses),
                Collections.unmodifiableList(strategyClasses));
    }

    private PackageFinder(List<ClassInfo> tileClasses, List<ClassInfo> strategyClasses) {

        this.tileClasses = tileClasses;
        this.strategyClasses = strategyClasses;
    }

    public List<ClassInfo> getAllTileClasses() {
        return tileClasses;
    }

    public List<ClassInfo> getAllStrategyClasses() {
        return strategyClasses;
    }

    private static void addAllClasses(Class baseType, List<ClassInfo> ret, String packageName, String author,
            String url) throws IOException, ClassNotFoundException {
        Log.app.debug("Finding classes in " + packageName);
        Class[] allClasses = getClasses(packageName);
        for (int i = 0; i < allClasses.length; i++) {
            Class clazz = allClasses[i];
            Log.app.debug("Found class " + clazz.getName());
            if (baseType.isAssignableFrom(clazz)) {
                ret.add(new ClassInfo(clazz, clazz.getName(), author, url));
            } else {
                Log.app.debug("Rejecting " + clazz.getName() + " as not of correct type");
            }
        }
    }

    // http://snippets.dzone.com/posts/show/4831

    /**
     * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
     *
     * @param packageName The base package
     * @return The classes
     * @throws ClassNotFoundException
     * @throws IOException
     */
    private static Class[] getClasses(String packageName) throws ClassNotFoundException, IOException {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        assert classLoader != null;
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources = classLoader.getResources(path);
        ArrayList<Class> classes = new ArrayList<Class>();
        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            try {
                classes.addAll(findClasses(resource, packageName));
            } catch (URISyntaxException e) {
                Log.app.warn("Problem with " + resource.toExternalForm() + " - " + e.getMessage());
            }
        }
        return classes.toArray(new Class[classes.size()]);
    }

    /**
     * Recursive method used to find all classes in a given directory and subdirs.
     *
     * @param directory   The base directory
     * @param packageName The package name for classes found inside the base directory
     * @return The classes
     * @throws ClassNotFoundException
     */
    private static List<Class> findClasses(URL directory, String packageName)
            throws ClassNotFoundException, URISyntaxException {
        // file:/home/daniel/Development/ImageTiler/resources/sampletiles.jar!/com/example/tiles
        String uriPath = directory.toExternalForm();
        if ("jar".equals(directory.getProtocol())) {
            int indexOfJar = uriPath.toLowerCase().indexOf(".jar!/");
            // will start jar:file:/.../
            return findClassesInJar(new URI(uriPath.substring(4, indexOfJar + 4)),
                    uriPath.substring(indexOfJar + 5), packageName);
        } else {
            return findClassesOnFilesystem(new File(directory.toURI()), packageName);
        }
    }

    private static List<Class> findClassesOnFilesystem(File directory, String packageName)
            throws ClassNotFoundException {
        List<Class> classes = new ArrayList<Class>();
        if (!directory.exists()) {
            return classes;
        }
        File[] files = directory.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                assert !file.getName().contains(".");
                classes.addAll(findClassesOnFilesystem(file, packageName + "." + file.getName()));
            } else if (file.getName().endsWith(".class")) {
                classes.add(Class
                        .forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
            }
        }
        return classes;
    }

    private static List<Class> findClassesInJar(URI uriOfJar, String pathInJar, String packageName)
            throws ClassNotFoundException {
        if (pathInJar.startsWith("/"))
            pathInJar = pathInJar.substring(1);
        if (!pathInJar.endsWith("/"))
            pathInJar = pathInJar + "/";
        List<Class> classes = new ArrayList<Class>();
        File theFile = new File(uriOfJar);
        if (!theFile.isFile()) {
            return classes;
        }
        try {
            JarFile jarFile = new JarFile(theFile);
            for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
                JarEntry entry = entries.nextElement();
                String name = entry.getName();
                if (name.startsWith(pathInJar) && name.endsWith(".class")) {
                    Log.app.debug("Found entry: " + entry.getName());
                    String className = name.replaceAll("/", ".").substring(0, name.length() - 6);
                    classes.add(Class.forName(className));
                }
            }
        } catch (IOException e) {
            Log.app.warn("Couldn't open jar file " + uriOfJar + " - " + e.getMessage());
        }
        return classes;
    }

}