package cz.cvut.fel.adaptiverestfulapi.meta;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
import cz.cvut.fel.adaptiverestfulapi.meta.configuration.Configuration;
import cz.cvut.fel.adaptiverestfulapi.meta.configuration.Pack;
import cz.cvut.fel.adaptiverestfulapi.meta.configuration.Variable;
import cz.cvut.fel.adaptiverestfulapi.meta.model.*;
import cz.cvut.fel.adaptiverestfulapi.meta.reflection.Reflection;
import cz.cvut.fel.adaptiverestfulapi.meta.reflection.Triplet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* For given package name creates a meta model.
*/
public class Inspector {
private ModelInspectionListener modeler;
private Set<ConfigurationInspectionListener> configurators = new HashSet<>();
private Logger logger = LoggerFactory.getLogger(Inspector.class);
/**
* Inspects given package for classes that extends Object class.
* @param pack package to model
* @return model
*/
public Model model(String pack) throws InspectionException {
return this.model(pack, java.lang.Object.class);
}
/**
* Inspects given package for classes that extends passed class.
* @param pack package to model
* @param clazz base class
* @return model
*/
public Model model(String pack, Class clazz) throws InspectionException {
if (pack == null || clazz == null) {
throw new InspectionException("Package name, or base class are missing.");
}
ModelBuilder builder = new ModelBuilder(pack, clazz);
Reflection reflection = new Reflection(builder.getName(), builder.getBaseClass());
// phase 1: model all leaf classes
this.addEntities(builder, reflection);
// phase 2: model attributes and relationships
this.addProperties(builder, reflection);
return builder.build(this.logger);
}
/**
* Inspects given model for it's configuration.
* @param model
* @return configuration
* @throws InspectionException
*/
public Configuration configuration(Model model) throws InspectionException {
// inspect global configuration
List<Variable> variables = null;
variables = new LinkedList<>();
for (ConfigurationInspectionListener listener : this.configurators) {
variables.addAll(listener.configuration());
}
Pack pack = new Pack(variables);
// inspect model configuration
variables = new LinkedList<>();
for (ConfigurationInspectionListener listener : this.configurators) {
variables.addAll(listener.configuration(model));
}
pack.addConfiguration(variables, model);
// TODO if configuration per submodel will be needed then the implementation goes here
// inspect entity configuration
for (Entity entity : model.getEntities().values()) {
variables = new LinkedList<>();
for (ConfigurationInspectionListener listener : this.configurators) {
variables.addAll(listener.configuration(entity));
}
pack.addConfiguration(variables, entity, model);
// inspect attribute configuration
for (Attribute attribute : entity.getAttributes().values()) {
variables = new LinkedList<>();
for (ConfigurationInspectionListener listener : this.configurators) {
variables.addAll(listener.configuration(attribute));
}
pack.addConfiguration(variables, attribute, entity);
}
// inspect relationship configuration
for (Relationship relationship : entity.getRelationships().values()) {
variables = new LinkedList<>();
for (ConfigurationInspectionListener listener : this.configurators) {
variables.addAll(listener.configuration(relationship));
}
pack.addConfiguration(variables, relationship, entity);
}
}
return pack;
}
/**
* Inspects classes for the builder.
* @param builder
*/
protected void addEntities(ModelBuilder builder, Reflection reflection) {
Set<Class<?>> clazzes = reflection.leafs(builder.getBaseClass());
for (Class<?> clazz : clazzes) {
Entity entity = this.modeler.entity(clazz);
builder.addEntity(entity);
}
}
/**
* Inspects properties for entities in the builder.
* @param builder
*/
protected void addProperties(ModelBuilder builder, Reflection reflection) {
Set<Entity> entities = builder.getEntities();
for (Entity entity : entities) {
Set<Triplet<Field, Method, Method>> triplets = reflection.triplets(entity.getEntityClass());
for (Triplet<Field, Method, Method> triplet : triplets) {
Property property = this.modeler.property(triplet, entity, entities);
builder.addProperty(property, entity);
}
}
}
public void setModeler(ModelInspectionListener modeler) {
this.modeler = modeler;
}
public void addConfigurator(ConfigurationInspectionListener configurator) {
this.configurators.add(configurator);
}
}