Scanners.java
package org.reflections.scanners;
import javassist.bytecode.ClassFile;
import org.reflections.Store;
import org.reflections.util.FilterBuilder;
import org.reflections.util.NameHelper;
import org.reflections.util.QueryBuilder;
import org.reflections.util.QueryFunction;
import org.reflections.vfs.Vfs;
import java.lang.annotation.Inherited;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static org.reflections.util.JavassistHelper.*;
/**
* base Reflections {@link Scanner}s such as:
* <ul>
* <li>{@link #SubTypes}</li>
* <li>{@link #TypesAnnotated}</li>
* <li>{@link #MethodsAnnotated}</li>
* <li>{@link #ConstructorsAnnotated}</li>
* <li>{@link #FieldsAnnotated}</li>
* <li>{@link #Resources}</li>
* <li>{@link #MethodsParameter}</li>
* <li>{@link #ConstructorsParameter}</li>
* <li>{@link #MethodsSignature}</li>
* <li>{@link #ConstructorsSignature}</li>
* <li>{@link #MethodsReturn}</li>
* </ul>
* <i>note that scanners must be configured in {@link org.reflections.Configuration} in order to be queried</i>
* */
public enum Scanners implements Scanner, QueryBuilder, NameHelper {
/** scan type superclasses and interfaces
* <p></p>
* <i>Note that {@code Object} class is excluded by default, in order to reduce store size.
* <br>Use {@link #filterResultsBy(Predicate)} to change, for example {@code SubTypes.filterResultsBy(c -> true)}</i>
* */
SubTypes {
/* Object class is excluded by default from subtypes indexing */
{ filterResultsBy(new FilterBuilder().excludePattern("java\\.lang\\.Object")); }
@Override
public void scan(ClassFile classFile, List<Map.Entry<String, String>> entries) {
entries.add(entry(classFile.getSuperclass(), classFile.getName()));
entries.addAll(entries(Arrays.asList(classFile.getInterfaces()), classFile.getName()));
}
},
/** scan type annotations */
TypesAnnotated {
@Override
public boolean acceptResult(String annotation) {
return super.acceptResult(annotation) || annotation.equals(Inherited.class.getName());
}
@Override
public void scan(ClassFile classFile, List<Map.Entry<String, String>> entries) {
entries.addAll(entries(getAnnotations(classFile::getAttribute), classFile.getName()));
}
},
/** scan method annotations */
MethodsAnnotated {
@Override
public void scan(ClassFile classFile, List<Map.Entry<String, String>> entries) {
getMethods(classFile).forEach(method ->
entries.addAll(entries(getAnnotations(method::getAttribute), methodName(classFile, method))));
}
},
/** scan constructor annotations */
ConstructorsAnnotated {
@Override
public void scan(ClassFile classFile, List<Map.Entry<String, String>> entries) {
getConstructors(classFile).forEach(constructor ->
entries.addAll(entries(getAnnotations(constructor::getAttribute), methodName(classFile, constructor))));
}
},
/** scan field annotations */
FieldsAnnotated {
@Override
public void scan(ClassFile classFile, List<Map.Entry<String, String>> entries) {
classFile.getFields().forEach(field ->
entries.addAll(entries(getAnnotations(field::getAttribute), fieldName(classFile, field))));
}
},
/** scan non .class files such as xml or properties files */
Resources {
@Override
public boolean acceptsInput(String file) {
return !file.endsWith(".class");
}
@Override
public List<Map.Entry<String, String>> scan(Vfs.File file) {
return Collections.singletonList(entry(file.getName(), file.getRelativePath()));
}
@Override
public void scan(ClassFile classFile, List<Map.Entry<String, String>> entries) {
throw new IllegalStateException();
}
@Override
public QueryFunction<Store, String> with(String pattern) {
return store -> store.getOrDefault(index(), Collections.emptyMap())
.entrySet().stream().filter(entry -> entry.getKey().matches(pattern))
.flatMap(entry -> entry.getValue().stream()).collect(Collectors.toCollection(LinkedHashSet::new));
}
},
/** scan method parameters types and annotations */
MethodsParameter {
@Override
public void scan(ClassFile classFile, List<Map.Entry<String, String>> entries) {
getMethods(classFile).forEach(method -> {
String value = methodName(classFile, method);
entries.addAll(entries(getParameters(method), value));
getParametersAnnotations(method).forEach(annotations -> entries.addAll(entries(annotations, value)));
});
}
},
/** scan constructor parameters types and annotations */
ConstructorsParameter {
@Override
public void scan(ClassFile classFile, List<Map.Entry<String, String>> entries) {
getConstructors(classFile).forEach(constructor -> {
String value = methodName(classFile, constructor);
entries.addAll(entries(getParameters(constructor), value));
getParametersAnnotations(constructor).forEach(annotations -> entries.addAll(entries(annotations, value)));
});
}
},
/** scan methods signature */
MethodsSignature {
@Override
public void scan(ClassFile classFile, List<Map.Entry<String, String>> entries) {
getMethods(classFile).forEach(method ->
entries.add(entry(getParameters(method).toString(), methodName(classFile, method))));
}
@Override
public QueryFunction<Store, String> with(AnnotatedElement... keys) {
return QueryFunction.single(toNames(keys).toString()).getAll(this::get);
}
},
/** scan constructors signature */
ConstructorsSignature {
@Override
public void scan(ClassFile classFile, List<Map.Entry<String, String>> entries) {
getConstructors(classFile).forEach(constructor ->
entries.add(entry(getParameters(constructor).toString(), methodName(classFile, constructor))));
}
@Override
public QueryFunction<Store, String> with(AnnotatedElement... keys) {
return QueryFunction.single(toNames(keys).toString()).getAll(this::get);
}
},
/** scan method return type */
MethodsReturn {
@Override
public void scan(ClassFile classFile, List<Map.Entry<String, String>> entries) {
getMethods(classFile).forEach(method ->
entries.add(entry(getReturnType(method), methodName(classFile, method))));
}
};
private Predicate<String> resultFilter = s -> true; //accept all by default
@Override
public String index() {
return name();
}
public Scanners filterResultsBy(Predicate<String> filter) {
this.resultFilter = filter;
return this;
}
@Override
public final List<Map.Entry<String, String>> scan(ClassFile classFile) {
List<Map.Entry<String, String>> entries = new ArrayList<>();
scan(classFile, entries);
return entries.stream().filter(a -> acceptResult(a.getKey())).collect(Collectors.toList());
}
abstract void scan(ClassFile classFile, List<Map.Entry<String, String>> entries);
protected boolean acceptResult(String fqn) {
return fqn != null && resultFilter.test(fqn);
}
}