Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.tobedevoured.modelcitizen.ModelFactory Maven / Gradle / Ivy
Go to download
Model Citizen is an annotation based model factory for Java.
A Model is mapped by a Blueprint using annotated fields. Blueprints contain
default values and references to other blueprinted models. The ModelFactory can
create Models based on registered Blueprints. A Model already created can be
passed into the ModelFactory as a Reference Model, which will be used as the basis
for the new Model.
package com.tobedevoured.modelcitizen;
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.metapossum.utils.scanner.reflect.ClassesInPackageScanner;
import java.lang.reflect.InvocationTargetException;
import org.apache.commons.lang.reflect.ConstructorUtils;
import com.tobedevoured.modelcitizen.annotation.Blueprint;
import com.tobedevoured.modelcitizen.annotation.Default;
import com.tobedevoured.modelcitizen.annotation.Mapped;
import com.tobedevoured.modelcitizen.annotation.MappedList;
import com.tobedevoured.modelcitizen.annotation.MappedSet;
import com.tobedevoured.modelcitizen.annotation.NotSet;
import com.tobedevoured.modelcitizen.annotation.Nullable;
import com.tobedevoured.modelcitizen.erector.Command;
import com.tobedevoured.modelcitizen.policy.BlueprintPolicy;
import com.tobedevoured.modelcitizen.policy.FieldPolicy;
import com.tobedevoured.modelcitizen.policy.Policy;
import com.tobedevoured.modelcitizen.policy.PolicyException;
import com.tobedevoured.modelcitizen.template.BlueprintTemplate;
import com.tobedevoured.modelcitizen.template.BlueprintTemplateException;
import com.tobedevoured.modelcitizen.template.JavaBeanTemplate;
import com.tobedevoured.modelcitizen.callback.AfterCreateCallback;
import com.tobedevoured.modelcitizen.callback.Callback;
import com.tobedevoured.modelcitizen.callback.ConstructorCallback;
import com.tobedevoured.modelcitizen.callback.internal.Constructable;
import com.tobedevoured.modelcitizen.callback.internal.Getable;
import com.tobedevoured.modelcitizen.field.*;
/**
* ModelFactory for generating Models. A Model's {@link Blueprint} is registered
* with the ModelFactory. Then a Model can be generated with {@link #createModel(Class)}
* or {@link #createModel(Object)}
*/
public class ModelFactory {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private List blueprints;
private Map erectors = new HashMap();
private Map> fieldPolicies = new HashMap>();
private Map> blueprintPolicies = new HashMap>();
/**
* Create new instance
*/
public ModelFactory() {
blueprints = new ArrayList();
erectors = new HashMap();
}
/**
* Add Policy to ModelFactory
*
* @param policy {@link FieldPolicy} or {@link BlueprintPolicy}
* @throws PolicyException
*/
public void addPolicy(Policy policy) throws PolicyException {
// Add BlueprintPolicy
if (policy instanceof BlueprintPolicy) {
if (erectors.get(policy.getTarget()) == null) {
throw new PolicyException("Blueprint does not exist for BlueprintPolicy target: " + policy.getTarget());
}
List policies = blueprintPolicies.get(policy.getTarget());
if (policies == null) {
policies = new ArrayList();
}
policies.add((BlueprintPolicy) policy);
logger.info("Setting BlueprintPolicy {} for {}", policy, policy.getTarget());
blueprintPolicies.put(policy.getTarget(), policies);
// Add FieldPolicy
} else if (policy instanceof FieldPolicy) {
// XXX: force FieldPolicy's to be mapped to a blueprint? Limits their scope, but enables validation
if (erectors.get(policy.getTarget()) == null) {
throw new PolicyException("Blueprint does not exist for FieldPolicy target: " + policy.getTarget());
}
List policies = fieldPolicies.get(policy.getTarget());
if (policies == null) {
policies = new ArrayList();
}
policies.add((FieldPolicy) policy);
logger.info("Setting FieldPolicy {} for {}", policy, policy.getTarget());
fieldPolicies.put(policy.getTarget(), policies);
}
}
/**
* Register all {@link Blueprint} in package.
*
* @param _package String package to scan
* @throws RegisterBlueprintException
* @throws IOException
*/
public void setRegisterBlueprintsByPackage(String _package) throws RegisterBlueprintException {
Set> annotated = null;
try {
annotated = new ClassesInPackageScanner().findAnnotatedClasses(_package, Blueprint.class);
} catch (IOException e) {
throw new RegisterBlueprintException(e);
}
logger.info("Scanned {} and found {}", _package, annotated);
this.setRegisterBlueprints(annotated);
}
/**
* Register a List of {@link Blueprint}, Class, or String
* class names of Blueprint
*
* @param blueprints List
* @throws RegisterBlueprintException
*/
public void setRegisterBlueprints(Collection blueprints) throws RegisterBlueprintException {
for (Object blueprint : blueprints) {
if (blueprint instanceof Class) {
registerBlueprint((Class) blueprint);
} else if (blueprint instanceof String) {
registerBlueprint((String) blueprint);
} else if (blueprint instanceof String) {
registerBlueprint(blueprint);
} else {
throw new RegisterBlueprintException("Only supports List comprised of Class, Blueprint, or String className");
}
}
}
/**
* Register a {@link Blueprint} from a String Class name
*/
public void registerBlueprint(String className) throws RegisterBlueprintException {
try {
registerBlueprint(Class.forName(className));
} catch (ClassNotFoundException e) {
throw new RegisterBlueprintException(e);
}
}
/**
* Register a {@link Blueprint} from Class
*
* @param clazz Blueprint class
* @throws RegisterBlueprintException
*/
public void registerBlueprint(Class clazz) throws RegisterBlueprintException {
Object blueprint = null;
try {
blueprint = clazz.newInstance();
} catch (InstantiationException e) {
throw new RegisterBlueprintException(e);
} catch (IllegalAccessException e) {
throw new RegisterBlueprintException(e);
}
registerBlueprint(blueprint);
}
/**
* Register {@link Blueprint} from instance.
*
* @param blueprint {@link Blueprint}
* @throws RegisterBlueprintException
*/
public void registerBlueprint(Object blueprint) throws RegisterBlueprintException {
Blueprint blueprintAnnotation = blueprint.getClass().getAnnotation(Blueprint.class);
if (blueprintAnnotation == null) {
throw new RegisterBlueprintException("Blueprint class not annotated by @Blueprint: " + blueprint);
}
Class target = blueprintAnnotation.value();
List modelFields = new ArrayList();
logger.debug("Registering {} blueprint for {}", blueprint.getClass(), target);
Constructable newInstance = null;
List afterCreateCallbacks = new ArrayList();
// Get all fields for the blueprint target class
Collection fields = getAllFields(blueprint.getClass()).values();
for (Field field : fields) {
field.setAccessible(true);
// Register ConstructorCallback field
if ( field.getType().equals(ConstructorCallback.class) || field.getType().equals(com.tobedevoured.modelcitizen.callback.ConstructorCallback.class)) {
Object fieldVal = null;
try {
fieldVal = field.get(blueprint);
} catch (IllegalArgumentException e) {
throw new RegisterBlueprintException(e);
} catch (IllegalAccessException e) {
throw new RegisterBlueprintException(e);
}
if (fieldVal instanceof Constructable) {
logger.debug("Registering ConstructorCallback for {}", blueprint.getClass());
newInstance = (Constructable) fieldVal;
} else {
throw new RegisterBlueprintException("Blueprint " + blueprint.getClass().getSimpleName() + " Field class for " + field.getName() + " is invalid ConstructorCallback");
}
// ConstructorCallback is only used to create new instance.
continue;
}
// Register AfterCreateCallback field
if ( field.getType().equals(AfterCreateCallback.class) ) {
Object fieldVal = null;
try {
fieldVal = field.get(blueprint);
} catch (IllegalArgumentException e) {
throw new RegisterBlueprintException(e);
} catch (IllegalAccessException e) {
throw new RegisterBlueprintException(e);
}
if (fieldVal instanceof AfterCreateCallback) {
logger.debug("Registering AfterCreateCallback for {}", blueprint.getClass());
afterCreateCallbacks.add((AfterCreateCallback)fieldVal);
} else {
throw new RegisterBlueprintException("Blueprint " + blueprint.getClass().getSimpleName() + " Field class for " + field.getName() + " is invalid AfterCreateCallback");
}
// AfterCreateCallback is only used in callbacks
continue;
}
// Process @Default
Default defaultAnnotation = field.getAnnotation(Default.class);
if (defaultAnnotation != null) {
DefaultField defaultField = new DefaultField();
defaultField.setName(field.getName());
defaultField.setForce(defaultAnnotation.force());
try {
defaultField.setValue(field.get(blueprint));
} catch (IllegalArgumentException e) {
throw new RegisterBlueprintException(e);
} catch (IllegalAccessException e) {
throw new RegisterBlueprintException(e);
}
defaultField.setTarget(field.getType());
defaultField.setFieldClass(field.getType());
modelFields.add(defaultField);
logger.trace(" Setting default for {} to {} and forced {}", new Object[]{defaultField.getName(), defaultField.getValue(), defaultField.isForce()});
}
// Process @Mapped
Mapped mapped = field.getAnnotation(Mapped.class);
if (mapped != null) {
MappedField mappedField = new MappedField();
mappedField.setName(field.getName());
if (field.getAnnotation(Nullable.class) != null) {
mappedField.setNullable(true);
}
// If @Mapped(target) not set, use Field's class
if (NotSet.class.equals(mapped.target())) {
mappedField.setTarget(field.getType());
// Use @Mapped(target) for MappedField#target
} else {
mappedField.setTarget(mapped.target());
}
mappedField.setFieldClass(field.getType());
modelFields.add(mappedField);
logger.trace(" Setting mapped for {} to {}", mappedField.getName(), mappedField.getTarget());
}
// Process @MappedList
MappedList mappedCollection = field.getAnnotation(MappedList.class);
if (mappedCollection != null) {
MappedListField listField = new MappedListField();
listField.setName(field.getName());
listField.setFieldClass(field.getType());
listField.setSize(mappedCollection.size());
listField.setIgnoreEmpty(mappedCollection.ignoreEmpty());
listField.setForce(mappedCollection.force());
// If @MappedList(target) not set, use Field's class
if (NotSet.class.equals(mappedCollection.target())) {
listField.setTarget(field.getType());
// Use @MappedList(target) for MappedListField#target
} else {
listField.setTarget(mappedCollection.target());
}
// If @MappedList(targetList) not set, use ArrayList
if (NotSet.class.equals(mappedCollection.targetList())) {
listField.setTargetList(ArrayList.class);
} else {
// Ensure that the targetList implements List
boolean implementsList = false;
for (Class interf : mappedCollection.targetList().getInterfaces()) {
if (List.class.equals(interf)) {
implementsList = true;
break;
}
}
if (!implementsList) {
throw new RegisterBlueprintException("@MappedList targetList must implement List for field " + field.getName());
}
listField.setTargetList(mappedCollection.targetList());
}
modelFields.add(listField);
logger.trace(" Setting mapped list for {} to {} as <{}> and forced {}", new Object[]{listField.getName(), listField.getFieldClass(), listField.getTarget(), listField.isForce()});
}
// Process @MappedSet
MappedSet mappedSet = field.getAnnotation(MappedSet.class);
if (mappedSet != null) {
MappedSetField setField = new MappedSetField();
setField.setName(field.getName());
setField.setFieldClass(field.getType());
setField.setSize(mappedSet.size());
setField.setIgnoreEmpty(mappedSet.ignoreEmpty());
setField.setForce(mappedSet.force());
// XXX: @MappedSet( target ) is required
// If @MappedSet(target) not set
if (NotSet.class.equals(mappedSet.target())) {
// XXX: incorrect, should use generic defined by Set, luckily annotation forces target to be set
setField.setTarget(field.getType());
// Use @MappedSet(target) for MappedSet#target
} else {
setField.setTarget(mappedSet.target());
}
// If @MappedSet(targetSet) not set, use HashSet
if (NotSet.class.equals(mappedSet.targetSet())) {
setField.setTargetSet(HashSet.class);
} else {
// Ensure that the targetSet implements Set
boolean implementsSet = false;
for (Class interf : mappedSet.targetSet().getInterfaces()) {
if (Set.class.equals(interf)) {
implementsSet = true;
break;
}
}
if (!implementsSet) {
throw new RegisterBlueprintException("@MappedSet targetSet must implement Set for field " + field.getName());
}
setField.setTargetSet(mappedSet.targetSet());
}
modelFields.add(setField);
logger.trace(" Setting mapped set for {} to {} as <{}> and is forced {}", new Object[]{setField.getName(), setField.getFieldClass(), setField.getTarget(), setField.isForce()});
}
}
blueprints.add(blueprint);
Class templateClass = blueprintAnnotation.template();
BlueprintTemplate template = null;
try {
template = (BlueprintTemplate)ConstructorUtils.invokeConstructor( templateClass, null );
} catch (NoSuchMethodException e) {
throw new RegisterBlueprintException( e );
} catch (IllegalAccessException e) {
throw new RegisterBlueprintException( e );
} catch (InvocationTargetException e) {
throw new RegisterBlueprintException( e );
} catch (InstantiationException e) {
throw new RegisterBlueprintException( e );
}
// Create Erector for this Blueprint
Erector erector = new Erector();
erector.setTemplate(template);
erector.setBlueprint(blueprint);
erector.setModelFields(modelFields);
erector.setTarget(target);
erector.setNewInstance(newInstance);
erector.setCallbacks("afterCreate", afterCreateCallbacks);
erectors.put(target, erector);
}
/**
* Create a Model for a registered {@link Blueprint}
*
* @param clazz Model class
* @return Model
* @throws CreateModelException
*/
public T createModel(Class clazz) throws CreateModelException {
return createModel(clazz, true);
}
/**
* Create a Model for a registered {@link Blueprint}
*
* @param clazz Model class
* @param withPolicies boolean if Policies should be applied to the create
* @return Model
* @throws CreateModelException
*/
public T createModel(Class clazz, boolean withPolicies) throws CreateModelException {
Erector erector = erectors.get(clazz);
if (erector == null) {
throw new CreateModelException("Unregistered class: " + clazz);
}
return createModel(erector, null, withPolicies);
}
/**
* Create a Model for a registered {@link Blueprint}. Values set in the
* model will not be overridden by defaults in the {@link Blueprint}.
*
* @param referenceModel Object
* @return Model
* @throws CreateModelException
*/
public T createModel(T referenceModel) throws CreateModelException {
return createModel(referenceModel, true);
}
/**
* Create a Model for a registered {@link Blueprint}. Values set in the
* model will not be overridden by defaults in the {@link Blueprint}.
*
* @param referenceModel Object
* @param withPolicies boolean if Policies should be applied to the create
* @return Model
* @throws CreateModelException
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public T createModel(T referenceModel, boolean withPolicies) throws CreateModelException {
Erector erector = erectors.get(referenceModel.getClass());
if (erector == null) {
throw new CreateModelException("Unregistered class: " + referenceModel.getClass());
}
return createModel(erector, referenceModel, withPolicies);
}
/**
* Create a Model for a registered {@link Blueprint} using {@link Erector}.
* Values set in the model will not be overridden by defaults in the
* {@link Blueprint}.
*
* @param erector {@link Erector}
* @param referenceModel T the reference model instance, or null
* @param withPolicies boolean if Policies should be applied to the create
* @return T new Model
* @throws CreateModelException
*/
public T createModel(Erector erector, T referenceModel, boolean withPolicies) throws CreateModelException {
erector.clearCommands();
T createdModel;
try {
createdModel = (T) createNewInstance(erector);
} catch (BlueprintTemplateException e) {
throw new CreateModelException(e);
}
logger.trace("Created model {} from {} based on {}", new Object[] { createdModel, erector, referenceModel });
final T nonNullReferenceModel = referenceModel == null ? createdModel : referenceModel;
erector.setReference(nonNullReferenceModel);
if (withPolicies) {
List blueprintPolicies = this.getBlueprintPolicies().get(erector.getTarget());
if (blueprintPolicies != null) {
logger.debug(" Running Blueprint policies");
for (BlueprintPolicy policy : blueprintPolicies) {
Map> modelFieldCommands = null;
try {
logger.info(" processing {}", policy);
modelFieldCommands = policy.process(this, erector, createdModel);
} catch (PolicyException e) {
new CreateModelException(e);
}
for (ModelField modelField : modelFieldCommands.keySet()) {
erector.addCommands(modelField, modelFieldCommands.get(modelField));
}
}
}
}
for (ModelField modelField : erector.getModelFields()) {
logger.trace("ModelField {}", ReflectionToStringBuilder.toString(modelField));
Object value = null;
if (withPolicies) {
List policiesForSingleField = this.getFieldPolicies().get(modelField.getTarget());
if (policiesForSingleField != null) {
logger.debug(" Running Field policies");
for (FieldPolicy policy : policiesForSingleField) {
try {
logger.info(" processing {} for {}", policy, modelField.getTarget());
Command command = policy.process(this, erector, modelField, createdModel);
if (command != null) {
erector.addCommand(modelField, command);
}
} catch (PolicyException e) {
new CreateModelException(e);
}
}
}
}
if (erector.getCommands(modelField).size() > 0) {
logger.debug(" ModelField commands: {}", erector.getCommands(modelField));
}
if (!erector.getCommands(modelField).contains(Command.SKIP_INJECTION)) {
// Process DefaultField
if (modelField instanceof DefaultField) {
DefaultField defaultField = (DefaultField) modelField;
if (!erector.getCommands(modelField).contains(Command.SKIP_REFERENCE_INJECTION)) {
try {
value = erector.getTemplate().get(nonNullReferenceModel, defaultField.getName());
} catch (BlueprintTemplateException e) {
throw new CreateModelException(e);
}
}
// If null or the field forces, use value set in blueprint, otherwise
// use the value of the reference model
if (!erector.getCommands(modelField).contains(Command.SKIP_BLUEPRINT_INJECTION) && (value == null || defaultField.isForce())) {
value = defaultField.getValue();
}
// If value is an instance of FieldCallBack, eval the callback and use the value
if (value != null && value instanceof Getable) {
Getable callBack = (Getable)value;
value = callBack.get(nonNullReferenceModel);
}
try {
createdModel = erector.getTemplate().set(createdModel, defaultField.getName(), value);
} catch (BlueprintTemplateException e) {
throw new CreateModelException(e);
}
// Process MappedField
} else if (modelField instanceof MappedField) {
MappedField mappedField = (MappedField) modelField;
if (!erector.getCommands(modelField).contains(Command.SKIP_REFERENCE_INJECTION)) {
try {
value = erector.getTemplate().get(nonNullReferenceModel, mappedField.getName());
} catch (BlueprintTemplateException e) {
throw new CreateModelException(e);
}
}
if (!erector.getCommands(modelField).contains(Command.SKIP_BLUEPRINT_INJECTION) && value == null && !mappedField.isNullable()) {
value = this.createModel(mappedField.getTarget());
}
try {
createdModel = erector.getTemplate().set(createdModel, mappedField.getName(), value);
} catch (BlueprintTemplateException e) {
throw new CreateModelException(e);
}
// Process MappedListField
} else if (modelField instanceof MappedListField) {
MappedListField listField = (MappedListField) modelField;
List modelList = null;
try {
value = (List) erector.getTemplate().construct(listField.getTargetList());
} catch (BlueprintTemplateException e) {
throw new CreateModelException(e);
}
if (!erector.getCommands(modelField).contains(Command.SKIP_INJECTION)) {
try {
modelList = (List) erector.getTemplate().get(nonNullReferenceModel, listField.getName());
} catch (BlueprintTemplateException e) {
throw new CreateModelException(e);
}
}
if (!erector.getCommands(modelField).contains(Command.SKIP_BLUEPRINT_INJECTION)) {
// Inject models into List If list is null or force is true or it is an empty list that is ignored
if ((modelList == null || listField.isForce()) || (modelList.size() == 0 && !listField.isIgnoreEmpty())) {
for (int x = 0; x < listField.getSize(); x++) {
((List) value).add(this.createModel(listField.getTarget()));
}
} else {
for (Object object : modelList) {
((List) value).add(this.createModel(object));
}
}
}
try {
createdModel = erector.getTemplate().set(createdModel, listField.getName(), value);
} catch (BlueprintTemplateException e) {
throw new CreateModelException(e);
}
// Process MappedSetField
} else if (modelField instanceof MappedSetField) {
MappedSetField setField = (MappedSetField) modelField;
try {
value = erector.getTemplate().construct(setField.getTargetSet());
} catch (BlueprintTemplateException e) {
throw new CreateModelException(e);
}
Set referenceModelSet = null;
if (!erector.getCommands(modelField).contains(Command.SKIP_INJECTION)) {
try {
referenceModelSet = (Set) erector.getTemplate().get(nonNullReferenceModel, setField.getName());
} catch (BlueprintTemplateException e) {
throw new CreateModelException(e);
}
}
if (!erector.getCommands(modelField).contains(Command.SKIP_BLUEPRINT_INJECTION)) {
// Inject models into Set If list is null or force is true or it is an empty set that is ignored
if ((referenceModelSet == null || setField.isForce()) || (referenceModelSet.size() == 0 && !setField.isIgnoreEmpty())) {
for (int x = 0; x < setField.getSize(); x++) {
((Set) value).add(this.createModel(setField.getTarget()));
}
} else {
for (Object object : referenceModelSet) {
((Set) value).add(this.createModel(object));
}
}
}
try {
createdModel = erector.getTemplate().set(createdModel, setField.getName(), value);
} catch (BlueprintTemplateException e) {
throw new CreateModelException(e);
}
}
}
}
List afterCreateCallbacks = erector.getCallbacks("afterCreate");
if ( afterCreateCallbacks != null ) {
for (Callback callback : afterCreateCallbacks ) {
if ( callback instanceof AfterCreateCallback ) {
createdModel = ((AfterCreateCallback)callback).afterCreate(createdModel);
} else {
// XXX: should this toss an exception?
logger.error("Invalid AfterCreateCallback registered for {}", referenceModel.getClass() );
}
}
}
return createdModel;
}
protected Object createNewInstance(Erector erector) throws BlueprintTemplateException {
return erector.createNewInstance();
}
/**
* Registered Blueprints
*
* @return {@link List}
*/
public List getBlueprints() {
return blueprints;
}
/**
* Map of Class to their {@link Erector}.
*
* @return {@link Map}
*/
public Map getErectors() {
return erectors;
}
public Map> getBlueprintPolicies() {
return blueprintPolicies;
}
public Map> getFieldPolicies() {
return fieldPolicies;
}
/**
* Get complete inherited list of {@link Field} for Class, with the exception
* that {@link ConstructorCallback} fields are not inherited.
*
* @param clazz Class
* @return Map
*/
private Map getAllFields(Class clazz) {
return this.getAllFields(clazz, false);
}
private Map getAllFields(Class clazz, boolean isParent) {
Map fieldsMap = new HashMap();
Class superClazz = clazz.getSuperclass();
if(superClazz != null){
fieldsMap.putAll( getAllFields(superClazz, true) );
}
for ( Field field: clazz.getDeclaredFields() ) {
field.setAccessible(true);
if (isParent) {
// ConstructorCallbacks are not inherited
if (!field.getType().equals( ConstructorCallback.class ) ) {
fieldsMap.put( field.getName(), field );
}
} else {
fieldsMap.put( field.getName(), field );
}
}
return fieldsMap;
}
}