All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.unkrig.commons.lang.ClassLoaders Maven / Gradle / Ivy


/*
 * de.unkrig.commons - A general-purpose Java class library
 *
 * Copyright (c) 2016, Arno Unkrig
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 * following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *       following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 *       following disclaimer in the documentation and/or other materials provided with the distribution.
 *    3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
 *       products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package de.unkrig.commons.lang;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import de.unkrig.commons.nullanalysis.Nullable;

public final
class ClassLoaders {

    private
    ClassLoaders() {}

    /**
     * Returns the locations of all resources "under" a given directory name.
     * 

* Iff the name does not end with a slash, then calling this method is equivalent with calling * {@link ClassLoader#getResources(String)}. *

*

* Otherwise, if the name does end with a slash, then this method returns the locations of * all resources who's names begin with the given name. Iff includeDirectories is * {@code true}, then name, and all the subdirectories underneath, are also included in the result * set. *

*

* Notice that it is not (reliably) possible to determine the names of the retrieved resources; to * get these, use {@link #getSubresources(ClassLoader, String, boolean)}. *

* * @param classLoader The class loader to use; {@code null} means use the system class loader * @param name No leading slash */ public static URL[] getAllSubresources(@Nullable ClassLoader classLoader, String name, boolean includeDirectories) throws IOException { if (classLoader == null) classLoader = ClassLoader.getSystemClassLoader(); assert classLoader != null; List result = new ArrayList(); for (URL r : Collections.list(classLoader.getResources(name))) { result.addAll(ClassLoaders.getSubresourcesOf(r, name, includeDirectories).values()); } return result.toArray(new URL[result.size()]); } /** * Equivalent with {@link #getSubresources(ClassLoader, String, boolean, boolean)} with the recurse * parameter set to {@code true}. */ public static Map getSubresources(@Nullable ClassLoader classLoader, String name, boolean includeDirectories) throws IOException { return ClassLoaders.getSubresources(classLoader, name, includeDirectories, true); } /** * Returns a name-to-URL mapping of all resources "under" a given directory name. *

* Iff the name does not end with a slash, then calling this method is equivalent with calling * {@link ClassLoader#getResource(String)}. *

*

* Otherwise, if the name does end with a slash, then this method returns a name-to-URL * mapping of all content resources who's names begin with the given name. * Iff recurse is {@code false}, then only immediate subresources are included. * Iff includeDirectories is {@code true}, then also directory resources are included in the result * set; their names all ending with a slash. *

*

* If multiple resources have the name, then the resources are retrieved from the first * occurrence. *

* * @param classLoader The class loader to use; {@code null} means use the system class loader * @param name No leading slash * @return Keys ending with a slash map to "directory resources", the other keys map to "content * resources" */ public static Map getSubresources(@Nullable ClassLoader classLoader, String name, boolean includeDirectories, boolean recurse) throws IOException { if (classLoader == null) classLoader = ClassLoader.getSystemClassLoader(); assert classLoader != null; URL r = classLoader.getResource(name); if (r == null) { if (name.startsWith("java/") && name.endsWith("/")) { // The "rt.jar" (the basic source of the BOOTCLASS) lacks directory entries; to work around this we // have to get well-known resource. After that, we can list the JAR. r = classLoader.getResource("java/lang/Object.class"); if (r != null) { String protocol = r.getProtocol(); if ("jar".equalsIgnoreCase(protocol)) { JarURLConnection juc = (JarURLConnection) r.openConnection(); juc.setUseCaches(false); URL jarFileUrl = juc.getJarFileURL(); JarFile jarFile = juc.getJarFile(); Map result = ClassLoaders.getSubresources( jarFileUrl, jarFile, name, includeDirectories, recurse ); return result; } } } return Collections.emptyMap(); } return ClassLoaders.getSubresourcesOf(r, name, includeDirectories, recurse); } /** * Equivalent with {@link #getSubresourcesOf(URL, String, boolean, boolean)} with the recurse * parameter set to {@code true}. */ public static Map getSubresourcesOf(URL root, String rootName, boolean includeDirectories) throws IOException { return ClassLoaders.getSubresourcesOf(root, rootName, includeDirectories, true); } /** * Returns a name-to-URL mapping of all resources "under" a given root resource. *

* If the root designates a "content resource" (as opposed to a "directory resource"), then the * method returns {@code Collections.singletonMap(name, rootName)}. *

*

* Otherwise, if the root designates a "directory resource", then this method returns a name-to-URL * mapping of all content resources that are located "under" the root resource. * Iff recurse is {@code false}, then only immediate subresources are included. * Iff includeDirectories is {@code true}, then directory resources are also included in the result * set; their names all ending with a slash. *

* * @return Keys ending with a slash map to "directory resources", the other keys map to "content resources" */ public static Map getSubresourcesOf(URL root, String rootName, boolean includeDirectories, boolean recurse) throws IOException { String protocol = root.getProtocol(); if ("jar".equalsIgnoreCase(protocol)) { JarURLConnection juc = (JarURLConnection) root.openConnection(); juc.setUseCaches(false); if (!juc.getJarEntry().isDirectory()) return Collections.singletonMap(rootName, root); URL jarFileUrl = juc.getJarFileURL(); JarFile jarFile = juc.getJarFile(); Map result = ClassLoaders.getSubresources( jarFileUrl, jarFile, rootName, includeDirectories, recurse ); if (includeDirectories) result.put(rootName, root); return result; } if ("file".equalsIgnoreCase(protocol)) { return ClassLoaders.getFileResources(root, rootName, includeDirectories, recurse); } return Collections.singletonMap(rootName, root); } private static Map getSubresources(URL jarFileUrl, JarFile jarFile, String namePrefix, boolean includeDirectories, boolean recurse) { Map result = new HashMap(); for (JarEntry je : Collections.list(jarFile.entries())) { if ( (!je.isDirectory() || includeDirectories) && je.getName().startsWith(namePrefix) && (recurse || je.getName().indexOf('/', namePrefix.length()) == -1) ) { URL url; try { url = new URL("jar", null, jarFileUrl.toString() + "!/" + je.getName()); } catch (MalformedURLException mue) { throw new AssertionError(mue); } result.put(je.getName(), url); } } return result; } private static Map getFileResources(URL fileUrl, String namePrefix, boolean includeDirectories, boolean recurse) { File file = new File(fileUrl.getFile()); if (file.isFile()) return Collections.singletonMap(namePrefix, fileUrl); if (file.isDirectory()) { if (!namePrefix.isEmpty() && !namePrefix.endsWith("/")) namePrefix += '/'; Map result = new HashMap(); if (includeDirectories) result.put(namePrefix, fileUrl); for (File member : file.listFiles()) { String memberName = namePrefix + member.getName(); URL memberUrl = ClassLoaders.fileUrl(member); if (recurse) { result.putAll(ClassLoaders.getFileResources(memberUrl, memberName, includeDirectories, recurse)); } else { if (member.isFile()) result.put(memberName, memberUrl); } } return result; } return Collections.emptyMap(); } private static URL fileUrl(File file) { try { return file.toURI().toURL(); } catch (MalformedURLException mue) { throw ExceptionUtil.wrap(file.toString(), mue, IllegalStateException.class); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy