001/*
002 *  Copyright 2013 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;
017
018import java.lang.annotation.Annotation;
019import java.net.URL;
020import java.util.ArrayList;
021import java.util.Collection;
022import java.util.List;
023
024import org.jadira.scanner.classpath.ClasspathResolver;
025import org.jadira.scanner.classpath.filter.JElementTypeFilter;
026import org.jadira.scanner.classpath.filter.JTypeAnnotatedWithFilter;
027import org.jadira.scanner.classpath.filter.JTypeSubTypeOfFilter;
028import org.jadira.scanner.classpath.projector.ClasspathProjector;
029import org.jadira.scanner.classpath.types.JElement;
030import org.jadira.scanner.classpath.types.JType;
031import org.jadira.scanner.core.api.Filter;
032import org.jadira.scanner.core.api.Locator;
033
034public class Scanner {
035
036    protected final transient Configuration configuration;
037    private ClasspathResolver classpathResolver;
038
039    public Scanner(final Configuration configuration) {
040        this.configuration = configuration;
041        
042        List<URL> urls = new ArrayList<URL>();
043        if (configuration.getUrls() != null) {
044            urls.addAll(configuration.getUrls());
045        }
046        if (configuration.getLocators() != null) {
047            for (Locator<URL> next : configuration.getLocators()) {
048                urls.addAll(next.locate());
049            }
050        }
051        if (configuration.getClassLoaders() == null) {
052            classpathResolver = new ClasspathResolver(urls);
053        } else {
054            classpathResolver = new ClasspathResolver(urls, configuration.getClassLoaders());
055        }
056    }
057
058    public Scanner(final String prefix, final Filter<?>...s) {
059        this((Object) prefix,s);
060    }
061
062    public Scanner(final Object... params) {
063        this(ConfigurationBuilder.build(params));
064    }
065
066    public Scanner() {
067        this(new ConfigurationBuilder());         
068    }
069
070    public <T> Class<? extends T>[] findSubTypesOf(final Class<T> type) {
071        
072        @SuppressWarnings("unchecked")
073        Class<? extends T>[] result = (Class<? extends T>[]) jtypeToClass(classpathResolver.resolveAll(null, ClasspathProjector.SINGLETON, assembleFilters(new JElementTypeFilter(JType.class), new JTypeSubTypeOfFilter(type))));
074        return result;
075    }
076
077    public Class<?>[] findTypesAnnotatedWith(final Class<? extends Annotation> annotation) {
078        
079        return jtypeToClass(classpathResolver.resolveAll(null, ClasspathProjector.SINGLETON, assembleFilters(new JElementTypeFilter(JType.class), new JTypeAnnotatedWithFilter(annotation))));
080    }
081    
082    public Configuration getConfiguration() {
083        return configuration;
084    }
085    
086    private static Class<?>[] jtypeToClass(Collection<? extends JElement> jclasses) {
087        
088        List<JType> elements = new ArrayList<JType>(jclasses.size());
089        for (JElement next : jclasses) {
090            if (!(next instanceof JType)) {
091                throw new IllegalStateException("Only JType can be converted to classes");
092            } else {
093                elements.add((JType)next);
094            }
095        }
096        return jtypeToClass(elements.toArray(new JType[]{}));
097    }
098    
099    private static Class<?>[] jtypeToClass(JType... jclasses) {
100        
101        Class<?>[] classes = new Class[jclasses.length];
102        for (int i=0; i<jclasses.length; i++) {
103            Class<?> actualClass = (Class<?>) jclasses[i].getActualClass();
104            classes[i] = actualClass;
105        }
106        return classes;
107    }
108    
109    private Filter<?>[] assembleFilters(Filter<?>... searchFilters) {
110
111        Filter<?>[] filters = new Filter<?>[configuration.getFilters().size() + searchFilters.length];
112        for (int i = 0; i < configuration.getFilters().size(); i++) {
113            filters[i] = configuration.getFilters().get(0);
114        }
115        
116        for (int i = 0; i < searchFilters.length; i++) {
117            filters[configuration.getFilters().size() + i] = searchFilters[i];
118        }
119        return filters;
120    }
121}