ResourceUtil.java

// @formatter:off
 /*******************************************************************************
 *
 * This file is part of JMad.
 * 
 * Copyright (c) 2008-2011, CERN. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 ******************************************************************************/
// @formatter:on

/*
 * $Id: ResourceUtil.java,v 1.5 2009-02-25 18:48:27 kfuchsbe Exp $
 *
 * $Date: 2009-02-25 18:48:27 $ $Revision: 1.5 $ $Author: kfuchsbe $
 *
 * Copyright CERN, All Rights Reserved.
 */
package cern.accsoft.steering.jmad.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.jar.JarFile;
import java.util.regex.Pattern;

/**
 * This class provides methods to handle resource files
 *
 * @author Kajetan Fuchsberger (kajetan.fuchsberger at cern.ch)
 */
public final class ResourceUtil {
    /**
     * the logger for the class
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(ResourceUtil.class);

    private ResourceUtil() {
        /* only static methods */
    }

    /**
     * searches all the resources in the classpath to get a list of all the resources in the given package.
     *
     * @param packageName the package name in which to search for the resources.
     * @return the names of the resources (without leading package names)
     */
    public static Collection<String> getResourceNames(String packageName) {
        String patternString = ".*" + packageName.replaceAll("\\/", "\\\\/").replaceAll("\\.", "\\\\/") + "\\/[^\\/]+";
        LOGGER.debug("Searching for pattern:" + patternString);
        Pattern pattern = Pattern.compile(patternString);
        Collection<String> resources = getResources(pattern);
        List<String> names = new ArrayList<String>();
        for (String resource : resources) {
            File file = new File(resource);
            names.add(file.getName());
        }
        return names;
    }

    /**
     * just adds a prefix to the path
     *
     * @param filepath the path to which to add the prefix
     * @param offset   the offset to add as prefix
     * @return the whole path
     */
    public static String prependPathOffset(String filepath, String offset) {
        if ((offset != null) && (offset.length() > 0)) {
            /*
             * IMPORTANT: Do NOT use File.separator here, since this is also used for resource-paths!
             */
            return offset + "/" + filepath;
        } else {
            return filepath;
        }
    }

    /**
     * Canonicalize a resource-path name by resolving "./" and "../", and replacing backslashes with slashes.
     *
     * @param path the path
     * @return a canonical representation of the path
     */
    public static String canonicalizePath(String path) {
        return Paths.get(path).normalize().toString().replace("\\", "/");
    }

    /**
     * Converts the given package name into a path like string (e.g. "a.java.pkg" is converted to "a/java/pkg"). Hereby
     * always slashes ("/") are used and not the system file separator.
     *
     * @param packageName the package name to convert
     * @return the converted package name as path represention
     */
    public static String packageToPath(String packageName) {
        return packageName.replaceAll("\\.", "/");
    }

    /*
     * The following code was found on the net at http://forums.devx.com/showthread.php?t=153784
     */

    /**
     * for all elements of java.class.path get a Collection of resources. All ressources can be found with:
     * <p>
     * <code>
     * Pattern pattern = Pattern.compile(".*");
     * </code>
     *
     * @param pattern the pattern to match
     * @return the resources in the order they are found
     */
    public static Collection<String> getResources(Pattern pattern) {
        ArrayList<String> retval = new ArrayList<String>();
        retval.addAll(getResourcesFromClassPath(pattern));
        return retval;
    }

    /**
     * @param pattern a regular expression pattern to search
     * @return all names of the resources included in the class-path (jars and directories)
     */
    private static Collection<String> getResourcesFromClassPath(Pattern pattern) {
        List<String> retval = new ArrayList<String>();

        Set<String> classPathElements = ClassPathUtil.getAllClassPathEntries();
        for (String element : classPathElements) {
            LOGGER.debug("Processing classpath entry '" + element + "'");
            retval.addAll(getResources(element, pattern));
        }
        return retval;
    }

    private static Collection<String> getResources(String element, Pattern pattern) {
        ArrayList<String> retval = new ArrayList<String>();
        if (JarUtil.isJarName(element)) {
            retval.addAll(getResourcesFromJarFile(element, pattern));
        } else {
            File file = getFileFromUrlString(element);
            if (file == null) {
                LOGGER.warn("Could not correctly associate '" + element + "' to a file!?");
            } else {
                if (file.isDirectory()) {
                    retval.addAll(getResourcesFromDirectory(file, pattern));
                } else {
                    LOGGER.warn("Do not know how to process classpath entry '" + element + "'");
                }
            }
        }
        return retval;
    }

    private static Collection<String> getResourcesFromJarFile(String fileName, Pattern pattern) {
        Collection<String> retvals = new ArrayList<String>();
        JarFile jarFile = null;
        try {
            jarFile = JarUtil.getJarFile(fileName);
            retvals = ZipUtil.getFileNames(jarFile, pattern);
        } catch (IOException e) {
            LOGGER.warn("Unable to open jar '" + fileName + "'. Ignoring it.");
        } finally {
            if (jarFile != null) {
                try {
                    jarFile.close();
                } catch (IOException e) {
                    LOGGER.error("Erro while closing jar file.", e);
                }
            }
        }
        return retvals;
    }

    private static Collection<String> getResourcesFromDirectory(File directory, Pattern pattern) {
        ArrayList<String> retval = new ArrayList<String>();
        File[] fileList = directory.listFiles();
        for (File file : fileList) {
            if (file.isDirectory()) {
                retval.addAll(getResourcesFromDirectory(file, pattern));
            } else {
                try {
                    String fileName = file.getCanonicalPath().replaceAll("\\\\", "/");
                    boolean accept = pattern.matcher(fileName).matches();
                    if (accept) {
                        retval.add(fileName);
                    }
                } catch (IOException e) {
                    throw new Error(e);
                }
            }
        }
        return retval;
    }

    private static final File getFileFromUrlString(String urlString) {
        URL url;
        try {
            url = new URL(urlString);
        } catch (MalformedURLException e1) {
            return null;
        }
        File f = null;
        f = new File(url.getPath());
        return f;
    }
}