/*
* Copyright 2012 Chris Pheby
*
* 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.jadira.scanner.classpath;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import javassist.bytecode.ClassFile;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.jadira.scanner.classfile.ClassFileResolver;
import org.jadira.scanner.classpath.types.JClass;
import org.jadira.scanner.classpath.types.JElement;
import org.jadira.scanner.classpath.types.JInterface;
import org.jadira.scanner.classpath.types.JPackage;
import org.jadira.scanner.core.api.Allocator;
import org.jadira.scanner.core.exception.ClasspathAccessException;
import org.jadira.scanner.core.exception.FileAccessException;
import org.jadira.scanner.core.helper.FileInputStreamOperation;
import org.jadira.scanner.core.helper.FileUtils;
import org.jadira.scanner.core.helper.JavassistClassFileHelper;
import org.jadira.scanner.core.helper.filenamefilter.ClassFilenameFilter;
import org.jadira.scanner.core.spi.AbstractFileResolver;
import org.jadira.scanner.core.utils.reflection.ClassLoaderUtils;
import org.jadira.scanner.file.locator.JdkBaseClasspathUrlLocator;
import de.schlichtherle.io.FileInputStream;
public class ClasspathResolver extends AbstractFileResolver<JElement> {
private static final List<URL> JDK_BASE_CLASSPATH_JARS = new JdkBaseClasspathUrlLocator().locate();
private static final ClassFilenameFilter CLASSFILE_FILTER = new ClassFilenameFilter();
private final ClasspathAssigner assigner = new ClasspathAssigner();
private final ClassFileResolver classFileResolver;
private final ClassLoader[] classLoaders;
public ClasspathResolver() {
super(JDK_BASE_CLASSPATH_JARS);
this.classLoaders = ClassLoaderUtils.getClassLoaders();
classFileResolver = new ClassFileResolver(classLoaders);
}
public ClasspathResolver(List<URL> classpaths) {
super();
this.classLoaders = ClassLoaderUtils.getClassLoaders();
classFileResolver = new ClassFileResolver(classpaths, this.classLoaders);
getDriverData().addAll(classpaths);
}
public ClasspathResolver(ClassLoader... classLoaders) {
super(JDK_BASE_CLASSPATH_JARS);
this.classLoaders = ClassLoaderUtils.getClassLoaders(classLoaders);
classFileResolver = new ClassFileResolver(this.classLoaders);
}
public ClasspathResolver(List<URL> classpaths, List<ClassLoader> classLoaders) {
super();
this.classLoaders = ClassLoaderUtils.getClassLoaders((ClassLoader[])classLoaders.toArray());
classFileResolver = new ClassFileResolver(classpaths, this.classLoaders);
getDriverData().addAll(classpaths);
}
public ClasspathResolver(List<URL> classpaths, ClassLoader... classLoaders) {
super();
this.classLoaders = ClassLoaderUtils.getClassLoaders(classLoaders);
classFileResolver = new ClassFileResolver(classpaths, this.classLoaders);
getDriverData().addAll(classpaths);
}
@Override
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
builder.append(getDriverData().toArray());
return builder.toString();
}
@Override
protected Allocator<JElement, File> getAssigner() {
return assigner;
}
private final class ClasspathAssigner implements Allocator<JElement, File> {
@Override
public JElement allocate(File e) {
JElement element;
if (e.isDirectory()) {
// Attempt to construct package
element = assignPackage(e);
} else {
try {
ClassFile f = JavassistClassFileHelper.constructClassFileForPath(e.getPath(), new FileInputStream(e));
if (f.isInterface()) {
return JInterface.getJInterface(f, ClasspathResolver.this);
} else {
return JClass.getJClass(f, ClasspathResolver.this);
}
} catch (FileNotFoundException e1) {
throw new ClasspathAccessException("Couldnt find file", e1);
} catch (IOException e1) {
throw new ClasspathAccessException("Couldnt access file", e1);
}
}
return element;
}
private JPackage assignPackage(File e) {
JPackage retVal;
final File[] classes = e.listFiles(CLASSFILE_FILTER);
for (int i = 0; i < classes.length; i++) {
ClassFile classFile = FileUtils.doWithFile(classes[i], new FileInputStreamOperation<ClassFile>() {
@Override
public ClassFile execute(String path, InputStream fileInputStream) {
try {
return JavassistClassFileHelper.constructClassFile(path, fileInputStream);
} catch (IOException e) {
throw new FileAccessException("Cannot access class file: " + e.getMessage(), e);
}
}
});
Package pkg = loadClass(classFile.getName()).getPackage();
retVal = JPackage.getJPackage(pkg, ClasspathResolver.this);
if (retVal != null) {
return retVal;
}
}
return null;
}
}
public ClassFileResolver getClassFileResolver() {
return classFileResolver;
}
public Class<?> loadClass(String className) {
if ("java.lang.Class".equals(className)) {
return java.lang.Class.class;
}
for (int i = 0; i < classLoaders.length; i++) {
try {
return Class.forName(className, true, classLoaders[i]);
} catch (ClassNotFoundException e) {
// Ignore
}
}
throw new ClasspathAccessException("Could not find class: " + className);
}
}