001/*
002 *  Copyright 2012 Chris Pheby
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.jadira.scanner.classpath;
017
018import java.io.File;
019import java.io.FileNotFoundException;
020import java.io.IOException;
021import java.io.InputStream;
022import java.net.URL;
023import java.util.List;
024
025import javassist.bytecode.ClassFile;
026
027import org.apache.commons.lang3.builder.ToStringBuilder;
028import org.apache.commons.lang3.builder.ToStringStyle;
029import org.jadira.scanner.classfile.ClassFileResolver;
030import org.jadira.scanner.classpath.types.JClass;
031import org.jadira.scanner.classpath.types.JElement;
032import org.jadira.scanner.classpath.types.JInterface;
033import org.jadira.scanner.classpath.types.JPackage;
034import org.jadira.scanner.core.api.Allocator;
035import org.jadira.scanner.core.exception.ClasspathAccessException;
036import org.jadira.scanner.core.exception.FileAccessException;
037import org.jadira.scanner.core.helper.FileInputStreamOperation;
038import org.jadira.scanner.core.helper.FileUtils;
039import org.jadira.scanner.core.helper.JavassistClassFileHelper;
040import org.jadira.scanner.core.helper.filenamefilter.ClassFilenameFilter;
041import org.jadira.scanner.core.spi.AbstractFileResolver;
042import org.jadira.scanner.core.utils.reflection.ClassLoaderUtils;
043import org.jadira.scanner.file.locator.JdkBaseClasspathUrlLocator;
044
045import de.schlichtherle.io.FileInputStream;
046
047public class ClasspathResolver extends AbstractFileResolver<JElement> {
048
049    private static final List<URL> JDK_BASE_CLASSPATH_JARS = new JdkBaseClasspathUrlLocator().locate();
050    
051        private static final ClassFilenameFilter CLASSFILE_FILTER = new ClassFilenameFilter();
052        
053        private final ClasspathAssigner assigner = new ClasspathAssigner();
054        
055        private final ClassFileResolver classFileResolver;
056
057    private final ClassLoader[] classLoaders;
058        
059    public ClasspathResolver() {        
060        super(JDK_BASE_CLASSPATH_JARS);
061        this.classLoaders  = ClassLoaderUtils.getClassLoaders();
062        classFileResolver = new ClassFileResolver(classLoaders);
063        }
064
065        public ClasspathResolver(List<URL> classpaths) {
066                super();
067                this.classLoaders = ClassLoaderUtils.getClassLoaders();
068                classFileResolver = new ClassFileResolver(classpaths, this.classLoaders);
069                getDriverData().addAll(classpaths);
070        }
071        
072    public ClasspathResolver(ClassLoader... classLoaders) {
073        super(JDK_BASE_CLASSPATH_JARS);
074        this.classLoaders  = ClassLoaderUtils.getClassLoaders(classLoaders);
075        classFileResolver = new ClassFileResolver(this.classLoaders);
076        }
077
078    public ClasspathResolver(List<URL> classpaths, List<ClassLoader> classLoaders) {
079        super();
080        this.classLoaders  = ClassLoaderUtils.getClassLoaders((ClassLoader[])classLoaders.toArray());
081        classFileResolver = new ClassFileResolver(classpaths, this.classLoaders);
082        getDriverData().addAll(classpaths);
083    }
084    
085        public ClasspathResolver(List<URL> classpaths, ClassLoader... classLoaders) {
086                super();
087                this.classLoaders  = ClassLoaderUtils.getClassLoaders(classLoaders);
088                classFileResolver = new ClassFileResolver(classpaths, this.classLoaders);
089                getDriverData().addAll(classpaths);
090        }
091
092        @Override
093        public String toString() {
094
095                ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
096                builder.append(getDriverData().toArray());
097
098                return builder.toString();
099        }
100
101        @Override
102        protected Allocator<JElement, File> getAssigner() {
103                return assigner;
104        }
105
106        private final class ClasspathAssigner implements Allocator<JElement, File> {
107
108                @Override
109                public JElement allocate(File e) {
110
111                        JElement element;
112                        if (e.isDirectory()) {
113                                // Attempt to construct package
114                                element = assignPackage(e);
115                        } else {
116                                try {
117                                        ClassFile f = JavassistClassFileHelper.constructClassFileForPath(e.getPath(), new FileInputStream(e));
118                                        if (f.isInterface()) {
119                                                return JInterface.getJInterface(f, ClasspathResolver.this);
120                                        } else {
121                                                return JClass.getJClass(f, ClasspathResolver.this);
122                                        }
123                                } catch (FileNotFoundException e1) {
124                                        throw new ClasspathAccessException("Couldnt find file", e1);
125                                } catch (IOException e1) {
126                                        throw new ClasspathAccessException("Couldnt access file", e1);
127                                }
128                        }
129                        return element;
130                }
131
132                private JPackage assignPackage(File e) {
133
134                        JPackage retVal;
135
136                        final File[] classes = e.listFiles(CLASSFILE_FILTER);
137                        for (int i = 0; i < classes.length; i++) {
138
139                                ClassFile classFile = FileUtils.doWithFile(classes[i], new FileInputStreamOperation<ClassFile>() {
140
141                                        @Override
142                                        public ClassFile execute(String path, InputStream fileInputStream) {
143
144                                                try {
145                                                        return JavassistClassFileHelper.constructClassFile(path, fileInputStream);
146                                                } catch (IOException e) {
147                                                        throw new FileAccessException("Cannot access class file: " + e.getMessage(), e);
148                                                }
149                                        }
150                                });
151
152                                Package pkg = loadClass(classFile.getName()).getPackage();
153                                retVal = JPackage.getJPackage(pkg, ClasspathResolver.this);
154
155                                if (retVal != null) {
156                                        return retVal;
157                                }
158                        }
159                        return null;
160                }
161        }       
162        
163        public ClassFileResolver getClassFileResolver() {
164                return classFileResolver;
165        }
166        
167        public Class<?> loadClass(String className) {
168            
169                if ("java.lang.Class".equals(className)) {
170                        return java.lang.Class.class;
171                }
172                
173            for (int i = 0; i < classLoaders.length; i++) {
174            try {
175                return Class.forName(className, true, classLoaders[i]);
176            } catch (ClassNotFoundException e) {
177                // Ignore
178            }
179            }
180            throw new ClasspathAccessException("Could not find class: " + className);
181        }
182}