package play.utils.meta.cp; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import javax.persistence.Entity; import javax.persistence.MappedSuperclass; import javax.persistence.Transient; import org.reflections.ReflectionUtils; import org.reflections.Reflections; import org.reflections.scanners.SubTypesScanner; import org.reflections.scanners.TypeAnnotationsScanner; import org.reflections.util.ClasspathHelper; import org.reflections.util.ConfigurationBuilder; import play.Logger; import play.Logger.ALogger; import play.utils.meta.ConverterRegistry; import play.utils.meta.FieldMetadata; import play.utils.meta.ModelMetadata; import play.utils.meta.ModelRegistry; import play.utils.meta.convert.Converter; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; public class ClasspathScanningModelRegistry implements ModelRegistry { private static ALogger log = Logger.of(ClasspathScanningModelRegistry.class); private Map<Class<?>, ModelMetadata> models; private ConverterRegistry converters; public ClasspathScanningModelRegistry(ConverterRegistry converters, ClassLoader... cls) { this.converters = converters; this.models = scan(cls); } private Map<Class<?>, ModelMetadata> scan(ClassLoader... classloaders) { Map<Class<?>, ModelMetadata> map = Maps.newHashMap(); final Reflections reflections = new Reflections(new ConfigurationBuilder().setUrls( ClasspathHelper.forPackage("", classloaders)).setScanners(new SubTypesScanner(), new TypeAnnotationsScanner()).addClassLoaders(classloaders)); Set<Class<?>> entities = reflections.getTypesAnnotatedWith(Entity.class); for (Class<?> entity : entities) { ModelMetadata metadata = getMetadata(entity); map.put(entity, metadata); } return map; } private ModelMetadata getMetadata(Class<?> entity) { if (log.isDebugEnabled()) log.debug("getMetadata for: " + entity); List<Field> modelFields = Lists.newArrayList(entity.getDeclaredFields()); Class<?> superClass = entity.getSuperclass(); while (superClass != null && Iterables.tryFind(Lists.newArrayList(superClass.getAnnotations()), new Predicate<Annotation>() { @Override public boolean apply(Annotation annotation) { return annotation.annotationType().equals(MappedSuperclass.class); } }).isPresent()) { modelFields.addAll(Lists.newArrayList(superClass.getDeclaredFields())); superClass = superClass.getSuperclass(); } if (log.isDebugEnabled()) log.debug("superClass : " + superClass); Function<Field, FieldMetadata> fieldMetadata = extractFieldMetadata(); Function<FieldMetadata, String> fieldName = extractFieldName(); @SuppressWarnings("unchecked") Map<String, FieldMetadata> fields = Maps.uniqueIndex( Iterables.transform( Iterables.filter(modelFields, Predicates.<Field> and( Predicates.not(ReflectionUtils.withAnnotation(Transient.class)), Predicates.not(ReflectionUtils.withModifier(Modifier.STATIC)), Predicates.not(ReflectionUtils.withModifier(Modifier.FINAL)), Predicates.not(new Predicate<Field>() { @Override public boolean apply(Field field) { return field.getName().startsWith("_ebean"); } }))), fieldMetadata), fieldName); if (log.isDebugEnabled()) { log.debug("fields : "); Set<String> fieldNames = fields.keySet(); for (String fn : fieldNames) { log.debug(" * " + fn + " : " + fields.get(fn)); } } Optional<FieldMetadata> keyField = Iterables.tryFind(fields.values(), new Predicate<FieldMetadata>() { @Override public boolean apply(FieldMetadata fieldInfo) { return fieldInfo.isKey(); } }); if (log.isDebugEnabled()) log.debug("keyField : " + keyField); ModelMetadata metadata = null; if (keyField.isPresent()) { metadata = new ModelMetadata(entity, fields, keyField.get()); } return metadata; } @Override public Collection<ModelMetadata> getModels() { return models.values(); } @Override public <M> ModelMetadata getModel(Class<M> modelType) { return models.get(modelType); } private FieldMetadata toFieldMetadata(Field field) { Converter<?> converter = converters.getConverter(field.getType()); return new FieldMetadata(field, converter); } private Function<Field, FieldMetadata> extractFieldMetadata() { return new Function<Field, FieldMetadata>() { @Override public FieldMetadata apply(Field field) { return toFieldMetadata(field); } }; } private Function<FieldMetadata, String> extractFieldName() { return new Function<FieldMetadata, String>() { @Override public String apply(FieldMetadata fieldInfo) { return fieldInfo.getField().getName(); } }; } public Iterable<? extends String> getModelNames() { Collection<ModelMetadata> models = getModels(); List<String> list = new ArrayList<String>(); for (ModelMetadata model : models) { list.add(model.getName()); } return list ; } }