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

org.eiichiro.reverb.lang.ClassResolver Maven / Gradle / Ivy

There is a newer version: 1.2.1
Show newest version
/*
 * Copyright (C) 2012 Eiichiro Uchiumi. 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.
 */
package org.eiichiro.reverb.lang;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.jar.Attributes.Name;

/**
 * {@code ClassResolver} is a base class for the component that resolves classes 
 * matches to the specified condition from its own search path.
 * {@code ClassResolver} can resolve classes by the following conditions: 
 * 
    *
  • By name
  • *
  • By superclass
  • *
  • By interface
  • *
  • By annotation
  • *
  • By {@code Matcher<T>}
  • *
* Type parameter T is the type of class representation. So the sub * class must override {@code #load(String, InputStream)} method to load a class * from the specified {@code InputStream} as its own type of class * representation and the methods described above according to the type of class * representation. By default, {@link JLCClassResolver} is provided, which * represents the loaded class as {@code java.lang.Class}. * * @author Eiichiro Uchiumi */ public abstract class ClassResolver { private Iterable urls = new ArrayList(); /** * {@code Matcher} indicates whether the specified class matches to some * condition or not. * * @author Eiichiro Uchiumi */ public static interface Matcher { /** * Indicates whether the specified class matches to some condition or * not. * * @param T The type of class representation. * @param clazz The class to be tested. * @return true If the specified class matches to some * condition. */ public boolean matches(T clazz); } /** * Constructs a new {@code ClassResolver} instance with the * {@code ClassLoader}'s search paths in the current thread context. */ public ClassResolver() { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if (!(classLoader instanceof URLClassLoader)) { throw new IllegalArgumentException( "'classLoader' must be an instance of URLClassLoader"); } this.urls = Arrays.asList(((URLClassLoader) classLoader).getURLs()); } /** * Constructs a new {@code ClassResolver} instance with the specified search * paths. * * @param urls The search path. */ public ClassResolver(Iterable urls) { this.urls = urls; } /** * Loads the class of the specified name from the specified * {@code InputStream} and returns loaded class representation as the type * of T. * * @param clazz The name of the class to be loaded. * @param stream {@code InputStream} to load a class file. * @return The loaded class representation as T. */ protected abstract T load(String clazz, InputStream stream); /** * Resolves the {@code Class}es that contains the specified name. * * @param name The part of the class name. * @return {@code Class}es that contains the specified name. * @throws IOException If any I/O access fails while traversing the search * path. */ public abstract Set resolveByName(final String name) throws IOException; /** * Resolves the {@code Class}es that inherits the specified superclass. * * @param superclass The superclass being inherited. * @return {@code Class}es that inherits the specified superclass. * @throws IOException If any I/O access fails while traversing the search * path. */ public abstract Set resolveBySuperclass(final Class superclass) throws IOException; /** * Resolves the {@code Class}es that implements the specified interface. * * @param interfaceClass The interface being implemented. * @return {@code Class}es that implements the specified interface. * @throws IOException If any I/O access fails while traversing the search * path. */ public abstract Set resolveByInterface(final Class interfaceClass) throws IOException; /** * Resolves the {@code Class}es that is annotated by the specified annotation. * * @param annotation The annotation the class being annotated. * @return {@code Class}es that is annotated by the specified annotation. * @throws IOException If any I/O access fails while traversing the search * path. */ public abstract Set resolveByAnnotation(final Class annotation) throws IOException; /** * Resolves the {@code Class}es that matches to the specified {@code Matcher}. * * @param matcher {@code Matcher}. * @return {@code Class}es that matches to the specified {@code Matcher}. * @throws IOException If any I/O access fails while traversing the search * path. */ public Set resolve(Matcher matcher) throws IOException { Set classes = new HashSet(); for (URL url : urls) { if (url.toString().endsWith(".jar")) { // System.out.println(url); JarFile jarFile = new JarFile(URLDecoder.decode(url.getPath(), "UTF-8")); Manifest manifest = jarFile.getManifest(); if (manifest != null) { // System.out.println(manifest); Attributes mainAttributes = manifest.getMainAttributes(); if (mainAttributes != null) { // System.out.println(mainAttributes); String classpath = mainAttributes.getValue(Name.CLASS_PATH); if (classpath != null) { // System.out.println(classpath); StringTokenizer stringTokenizer = new StringTokenizer(classpath); while (stringTokenizer.hasMoreTokens()) { String token = stringTokenizer.nextToken(); URL entry = new URL(url, token); if (entry.toString().endsWith("/")) { // System.out.println(entry); classes.addAll(getMatchedClasses(matcher, new File(URLDecoder.decode(entry.getPath(), "UTF-8")))); } else { // System.out.println(entry); classes.addAll(getMatchedClasses(matcher, new JarFile(URLDecoder.decode(entry.getPath(), "UTF-8")))); } } } } } classes.addAll(getMatchedClasses(matcher, jarFile)); } else { File base = new File(URLDecoder.decode(url.getPath(), "UTF-8")); classes.addAll(getMatchedClasses(matcher, base)); } } return classes; } private Set getMatchedClasses(Matcher matcher, File base) throws IOException { List files = getClasses(base); Set classes = new HashSet(); for (File file : files) { String path = file.getPath(); // System.out.println(path); InputStream stream = null; try { stream = new FileInputStream(file); T clazz = load(path.substring(base.getPath().length() + 1, path.length() - 6).replace(File.separatorChar, '.'), stream); if (clazz != null && matcher.matches(clazz)) { classes.add(clazz); } } finally { if (stream != null) { stream.close(); } } } return classes; } private List getClasses(File base) { // System.out.println(directory); List classes = new ArrayList(); if (base.isDirectory()) { File[] files = base.listFiles(); for (File file : files) { if (file.isDirectory()) { classes.addAll(getClasses(file)); } else { if (file.getPath().endsWith(".class")) { classes.add(file); } } } } else { if (base.getPath().endsWith(".class")) { classes.add(base); } } return classes; } private Set getMatchedClasses(Matcher matcher, JarFile jarFile) throws IOException { Enumeration entries = jarFile.entries(); Set classes = new HashSet(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String name = entry.getName(); // System.out.println(name); if (name.endsWith(".class")) { InputStream stream = null; try { stream = jarFile.getInputStream(entry); T clazz = load(name.substring(0, name.length() - 6).replace('/', '.'), stream); if (clazz != null && matcher.matches(clazz)) { classes.add(clazz); } } finally { if (stream != null) { stream.close(); } } } } return classes; } /** * Returns the search paths to be traversed. * * @return The search paths to be traversed. */ public Iterable urls() { return urls; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy