/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.util.annotation;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Pattern;
import org.fudgemsg.AnnotationReflector;
import org.fudgemsg.FudgeRuntimeException;
import org.reflections.scanners.FieldAnnotationsScanner;
import org.reflections.scanners.MethodAnnotationsScanner;
import org.reflections.scanners.MethodParameterScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.threeten.bp.Instant;
/**
* Scans the class path for classes that contain the given annotations.
*/
public final class ClasspathScanner {
private final Set<URL> _urls;
private final Instant _timestamp;
private static volatile Set<URL> s_classPathElements;
private boolean _scanClassAnnotations = true;
private boolean _scanMethodAnnotations = true;
private boolean _scanParameterAnnotations = true;
private boolean _scanFieldAnnotations = true;
public ClasspathScanner() {
_urls = getClassPathElements();
_timestamp = timestamp(_urls);
}
private Set<URL> getClassPathElements() {
if (s_classPathElements == null) {
s_classPathElements = findClassPathElements();
}
return s_classPathElements;
}
private Set<URL> findClassPathElements() {
Set<URL> results = new LinkedHashSet<URL>();
String javaClassPath = System.getProperty("java.class.path");
String[] paths = javaClassPath.split(Pattern.quote(File.pathSeparator));
for (String path : paths) {
File f = new File(path);
if (!f.exists()) {
continue;
}
URL url;
try {
url = f.toURI().toURL();
} catch (MalformedURLException e) {
throw new FudgeRuntimeException("Could not convert file " + f + " to URL", e);
}
results.add(url);
}
return results;
}
private static Instant timestamp(final Iterable<URL> urls) {
long ctime = 0;
for (URL url : urls) {
try {
final File f = new File(url.toURI());
final long l = f.lastModified();
if (l > ctime) {
ctime = l;
}
} catch (URISyntaxException e) {
// Ignore this one
}
}
return Instant.ofEpochMilli(ctime);
}
/**
* Returns the timestamp of the most recently modified Jar (or class) in the class path.
*
* @return the timestamp, not null
*/
public Instant getTimestamp() {
return _timestamp;
}
/**
* Scans the classpath to produce a populated cache.
*
* @param annotationClass the annotation to search for
* @return the cache, not null
*/
@SuppressWarnings("rawtypes")
public AnnotationCache scan(Class<? extends Annotation> annotationClass) {
int scanners = 0;
if (isScanClassAnnotations()) {
scanners++;
}
if (isScanFieldAnnotations()) {
scanners++;
}
if (isScanMethodAnnotations()) {
scanners++;
}
if (isScanParameterAnnotations()) {
scanners++;
}
final Object[] config = new Object[scanners + 2];
scanners = 0;
if (isScanClassAnnotations()) {
config[scanners++] = new TypeAnnotationsScanner();
}
if (isScanFieldAnnotations()) {
config[scanners++] = new FieldAnnotationsScanner();
}
if (isScanMethodAnnotations()) {
config[scanners++] = new MethodAnnotationsScanner();
}
if (isScanParameterAnnotations()) {
config[scanners++] = new MethodParameterScanner();
}
config[scanners++] = ClasspathScanner.class.getClassLoader();
config[scanners++] = Thread.currentThread().getContextClassLoader();
AnnotationReflector reflector = new AnnotationReflector(null, _urls, config);
final HashSet<String> classNames = new HashSet<String>();
if (isScanClassAnnotations()) {
classNames.addAll(reflector.getReflector().getStore().getTypesAnnotatedWith(annotationClass.getName()));
}
if (isScanFieldAnnotations()) {
Set<Field> fields = reflector.getReflector().getFieldsAnnotatedWith(annotationClass);
for (Field field : fields) {
classNames.add(field.getDeclaringClass().getName());
}
}
if (isScanMethodAnnotations()) {
Set<Method> methods = reflector.getReflector().getMethodsAnnotatedWith(annotationClass);
for (Method method : methods) {
classNames.add(method.getDeclaringClass().getName());
}
Set<Constructor> constructors = reflector.getReflector().getConstructorsAnnotatedWith(annotationClass);
for (Constructor constructor : constructors) {
classNames.add(constructor.getDeclaringClass().getName());
}
}
if (isScanParameterAnnotations()) {
Set<Method> paramMethods = reflector.getReflector().getMethodsWithAnyParamAnnotated(annotationClass);
for (Method method : paramMethods) {
classNames.add(method.getDeclaringClass().getName());
}
}
return AnnotationCache.create(getTimestamp(), annotationClass, classNames);
}
/**
* Gets the scanClassAnnotations.
*
* @return the scanClassAnnotations
*/
public boolean isScanClassAnnotations() {
return _scanClassAnnotations;
}
/**
* Sets the scanClassAnnotations.
*
* @param scanClassAnnotations the scanClassAnnotations
*/
public void setScanClassAnnotations(boolean scanClassAnnotations) {
_scanClassAnnotations = scanClassAnnotations;
}
/**
* Gets the scanMethodAnnotations.
*
* @return the scanMethodAnnotations
*/
public boolean isScanMethodAnnotations() {
return _scanMethodAnnotations;
}
/**
* Sets the scanMethodAnnotations.
*
* @param scanMethodAnnotations the scanMethodAnnotations
*/
public void setScanMethodAnnotations(boolean scanMethodAnnotations) {
_scanMethodAnnotations = scanMethodAnnotations;
}
/**
* Gets the scanParameterAnnotations.
*
* @return the scanParameterAnnotations
*/
public boolean isScanParameterAnnotations() {
return _scanParameterAnnotations;
}
/**
* Sets the scanParameterAnnotations.
*
* @param scanParameterAnnotations the scanParameterAnnotations
*/
public void setScanParameterAnnotations(boolean scanParameterAnnotations) {
_scanParameterAnnotations = scanParameterAnnotations;
}
/**
* Gets the scanFieldAnnotations.
*
* @return the scanFieldAnnotations
*/
public boolean isScanFieldAnnotations() {
return _scanFieldAnnotations;
}
/**
* Sets the scanFieldAnnotations.
*
* @param scanFieldAnnotations the scanFieldAnnotations
*/
public void setScanFieldAnnotations(boolean scanFieldAnnotations) {
_scanFieldAnnotations = scanFieldAnnotations;
}
}