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.classfile;
017
018import java.io.BufferedInputStream;
019import java.io.DataInputStream;
020import java.io.File;
021import java.io.FileNotFoundException;
022import java.io.IOException;
023import java.io.InputStream;
024import java.net.URL;
025import java.util.List;
026import java.util.WeakHashMap;
027
028import javassist.bytecode.ClassFile;
029
030import org.apache.commons.lang3.builder.ToStringBuilder;
031import org.apache.commons.lang3.builder.ToStringStyle;
032import org.jadira.scanner.classfile.filter.ClassFileFilter;
033import org.jadira.scanner.classfile.filter.NameFilter;
034import org.jadira.scanner.classfile.filter.PackageFileFilter;
035import org.jadira.scanner.classpath.projector.ClasspathProjector;
036import org.jadira.scanner.core.api.Allocator;
037import org.jadira.scanner.core.api.Projector;
038import org.jadira.scanner.core.helper.JavassistClassFileHelper;
039import org.jadira.scanner.core.spi.AbstractFileResolver;
040import org.jadira.scanner.core.utils.reflection.ClassLoaderUtils;
041import org.jadira.scanner.file.locator.JdkBaseClasspathUrlLocator;
042
043import de.schlichtherle.io.FileInputStream;
044
045public class ClassFileResolver extends AbstractFileResolver<ClassFile> {
046
047    private static final WeakHashMap<File, ClassFile> CACHED_CLASSFILES = new WeakHashMap<File, ClassFile>();
048    
049        private static final Projector<File> CLASSPATH_PROJECTOR = ClasspathProjector.SINGLETON;
050        
051    private static final List<URL> JDK_BASE_CLASSPATH_JARS = new JdkBaseClasspathUrlLocator().locate();
052
053        private final ClassFileAssigner assigner = new ClassFileAssigner();
054
055        private final ClassLoader[] classLoaders;
056        
057    public ClassFileResolver() {        
058        this.classLoaders  = ClassLoaderUtils.getClassLoaders();
059        }
060    
061    public ClassFileResolver(ClassLoader... classLoaders) {     
062        super(JDK_BASE_CLASSPATH_JARS);
063        this.classLoaders  = ClassLoaderUtils.getClassLoaders(classLoaders);
064        }
065        
066        public ClassFileResolver(List<URL> classpaths, ClassLoader... classLoaders) {
067                super(JDK_BASE_CLASSPATH_JARS);
068                getDriverData().addAll(classpaths);
069                this.classLoaders  = ClassLoaderUtils.getClassLoaders(classLoaders);
070        }
071
072        @Override
073        public String toString() {
074
075                ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
076                builder.append(getDriverData().toArray());
077
078                return builder.toString();
079        }
080
081        @Override
082        protected Allocator<ClassFile, File> getAssigner() {
083                return assigner;
084        }
085
086        private final class ClassFileAssigner implements Allocator<ClassFile, File> {
087
088                @Override
089                public ClassFile allocate(File e) {
090
091                    ClassFile res = CACHED_CLASSFILES.get(e);
092                    if (res != null) {
093                        return res;
094                    }
095                    
096                        FileInputStream tis = null;
097                        
098                        try {
099                                tis = new FileInputStream(e);
100                                res = JavassistClassFileHelper.constructClassFileForPath(e.getPath(), tis);
101                                CACHED_CLASSFILES.put(e, res);
102                                return res;
103                        } catch (FileNotFoundException e1) {
104                                throw new IllegalArgumentException(e + " is not a valid File", e1);
105                        } catch (IOException e1) {
106                                throw new IllegalArgumentException("Could not load ClassFile for " + e, e1);
107                        } finally {
108                                if (tis != null) {
109                                        try {
110                                                tis.close();
111                                        } catch (IOException e1) {
112                                                // Ignore
113                                        }
114                                }
115                        }
116                }
117        }
118        
119        public ClassFile resolveClassFile(String name) {
120                
121                ClassFile cf = null;
122                
123        String className = name.replace('.', '/').concat(".class");
124                
125                for (ClassLoader classLoader : classLoaders) {
126                    
127                if (classLoader != null) {
128                        
129                        InputStream is = classLoader.getResourceAsStream(className);
130                        if (is == null) {
131                            continue;
132                        }
133                        BufferedInputStream fin = new BufferedInputStream(is);
134                        
135                        try {
136                                cf = new ClassFile(new DataInputStream(fin));
137                                if (cf != null) {
138                                        return cf;
139                                }
140                        } catch (IOException e) {
141                                // Ignore
142                        }
143                }
144                }
145                        
146                cf = resolveFirst(null, CLASSPATH_PROJECTOR, new PackageFileFilter(name, true), new NameFilter(name));
147                if (cf == null) {
148                        cf = resolveFirst(null, CLASSPATH_PROJECTOR, new ClassFileFilter(name));
149                }
150                return cf;
151        }
152}