package play.modules.cream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.jcrom.annotations.JcrBaseVersionCreated;
import org.jcrom.annotations.JcrCheckedout;
import org.jcrom.annotations.JcrCreated;
import org.jcrom.annotations.JcrPath;
import org.jcrom.annotations.JcrReference;
import org.jcrom.annotations.JcrUUID;
import org.jcrom.annotations.JcrVersionCreated;
import org.jcrom.annotations.JcrVersionName;
import play.Play;
import play.data.binding.BeanWrapper;
import play.data.validation.Validation;
import play.exceptions.UnexpectedException;
import play.modules.cream.ocm.JcrMapper;
import play.modules.cream.ocm.JcrQueryResult;
import play.modules.cream.ocm.JcrVersionMapper;
import play.utils.Utils;
@SuppressWarnings("unchecked")
public abstract class Model implements play.db.Model {
/**
* Prepare a query to find *all* entities.
*
* @return JcrQuery
*/
public static JcrQueryResult all() {
throw new UnsupportedOperationException("Class not enhanced.");
}
/**
* Prepare a query to find *all* entities.
*
* @param Root
* path to search
*
* @return JcrQuery
*/
public static JcrQueryResult all(String rootPath) {
throw new UnsupportedOperationException("Class not enhanced.");
}
public static <T extends Model> T create(Class<T> type, String name, Map<String, String[]> params,
Annotation[] annotations) {
try {
Constructor c = type.getDeclaredConstructor();
c.setAccessible(true);
T model = (T) c.newInstance();
if (model.path == null) {
model.path = JcrMapper.getDefaultPath(type);
}
return (T) edit(model, name, params, annotations);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("unchecked")
public static <T extends Model> T edit(Object o, String name, Map<String, String[]> params, Annotation[] annotations) {
try {
BeanWrapper bw = new BeanWrapper(o.getClass());
// Start with relations
Set<Field> fields = new HashSet<Field>();
Class clazz = o.getClass();
while (!clazz.equals(Object.class)) {
Collections.addAll(fields, clazz.getDeclaredFields());
clazz = clazz.getSuperclass();
}
for (Field field : fields) {
boolean isEntity = false;
String relation = null;
boolean multiple = false;
//
if (Collection.class.isAssignableFrom(field.getType()) && field.isAnnotationPresent(JcrReference.class)) {
Class fieldType = (Class) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
isEntity = true;
relation = fieldType.getName();
multiple = true;
} else if (field.isAnnotationPresent(JcrReference.class)) {
isEntity = true;
relation = field.getType().getName();
}
if (isEntity) {
Class<Model> c = (Class<Model>) Play.classloader.loadClass(relation);
if (Model.class.isAssignableFrom(c)) {
String keyName = Model.Manager.factoryFor(c).keyName();
if (multiple && Collection.class.isAssignableFrom(field.getType())) {
Collection l = new ArrayList();
if (SortedSet.class.isAssignableFrom(field.getType())) {
l = new TreeSet();
} else if (Set.class.isAssignableFrom(field.getType())) {
l = new HashSet();
}
String[] ids = params.get(name + "." + field.getName() + "." + keyName);
if (ids != null) {
params.remove(name + "." + field.getName() + "." + keyName);
for (String _id : ids) {
if (_id.equals("")) {
continue;
}
// We only need uuid
Constructor<Model> ctor = c.getDeclaredConstructor();
try {
Model to = ctor.newInstance();
to.uuid = _id;
l.add(to);
} catch (Exception e) {
Validation.addError(name + "." + field.getName(), "validation.notFound", _id);
}
}
bw.set(field.getName(), o, l);
}
} else {
String[] ids = params.get(name + "." + field.getName() + "." + keyName);
if (ids != null && ids.length > 0 && !ids[0].equals("")) {
params.remove(name + "." + field.getName() + "." + keyName);
// We only need uuid
Constructor<Model> ctor = c.getDeclaredConstructor();
try {
String localName = name + "." + field.getName();
Model to = ctor.newInstance();
to.uuid = ids[0];
edit(to, localName, params, field.getAnnotations());
params = Utils.filterMap(params, localName);
bw.set(field.getName(), o, to);
} catch (Exception e) {
Validation.addError(name + "." + field.getName(), "validation.notFound", ids[0]);
}
} else if (ids != null && ids.length > 0 && ids[0].equals("")) {
bw.set(field.getName(), o, null);
params.remove(name + "." + field.getName() + "." + keyName);
}
}
}
}
if (field.getType().isEnum()) {
String fieldKey = name + "." + field.getName();
String[] enumValues = params.get(fieldKey);
if (enumValues != null) {
Enum<?> enumValue = Enum.valueOf((Class<? extends Enum>) field.getType(), enumValues[0]);
bw.set(field.getName(), o, enumValue);
params.remove(fieldKey);
}
}
}
bw.bind(name, o.getClass(), params, "", o, annotations);
return (T) o;
} catch (Exception e) {
throw new UnexpectedException(e);
}
}
/**
* Prepare a query to find entities.
*
* @param query
* SQL2 query
* @param params
* Params to bind to the query
* @return JcrQuery
*/
public static JcrQueryResult find(String query, Object... params) {
throw new UnsupportedOperationException("Class not enhanced.");
}
public static <T extends Model> List<T> findAll() {
throw new UnsupportedOperationException("Class not enhanced.");
}
/**
*
* @param rootPath
* @return
*/
public static <T extends Model> List<T> findAll(String rootPath) {
throw new UnsupportedOperationException("Class not enhanced.");
}
public static JcrQueryResult findBy(String where, Object... params) {
throw new UnsupportedOperationException("Class not enhanced.");
}
/**
* Find the entity with the corresponding id.
*
* @param id
* The entity id
* @return The entity
*/
public static <T extends Model> T findById(Object id) {
throw new UnsupportedOperationException("Class not enhanced.");
}
public static JcrQueryResult findByPath(String path, String where, Object... params) {
throw new UnsupportedOperationException("Class not enhanced.");
}
public static <T extends Model> T get() {
throw new UnsupportedOperationException("Class not enhanced.");
}
public static <T extends Model> T get(String path) {
throw new UnsupportedOperationException("Class not enhanced.");
}
@JcrUUID
public String uuid;
@JcrPath
public String path;
@JcrCreated
public Date created;
@JcrBaseVersionCreated
public Date baseVersionCreated;
@JcrVersionCreated
public Date versionCreated;
@JcrVersionName
public String versionName;
@JcrCheckedout
public boolean checkedout;
public void _delete() {
JcrMapper.remove(this);
}
@Override
public Object _key() {
return getId();
}
public void _save() {
if (path == null) {
path = JcrMapper.getDefaultPath(this.getClass());
JcrMapper.create(this);
} else {
// TODO support weak references by path?
if (uuid == null) {
JcrMapper.create(this);
} else {
JcrMapper.update(this);
}
}
}
/**
* store (ie insert) the entity.
*/
public boolean create() {
JcrMapper.create(this);
return true;
}
/**
* Delete the entity.
*
* @return The deleted entity.
*/
@SuppressWarnings("unchecked")
public <T extends Model> T delete() {
_delete();
return (T) this;
}
public Date getCreated() {
return created;
}
public String getId() {
return uuid;
}
public String getPath() {
return path;
}
public Date getUpdated() {
return versionCreated;
}
public String getUuid() {
return uuid;
}
public String getVersionName() {
return versionName;
}
public <T> List<T> getVersions() {
return (List<T>) JcrVersionMapper.getVersionList(this.getClass(), path);
}
public boolean isCheckedout() {
return checkedout;
}
public <T> T merge() {
return (T) JcrMapper.merge(this);
}
public <T> T merge(String childNodeFilter, int maxDepth) {
return (T) JcrMapper.merge(this, childNodeFilter, maxDepth);
}
/**
* store (ie insert) the entity.
*/
public <T extends Model> T save() {
_save();
return (T) this;
}
public void setPath(String path) {
this.path = path;
}
public <T> T update() {
return (T) JcrMapper.update(this);
}
public <T> T update(String childNodeFilter, int maxDepth) {
return (T) JcrMapper.update(this, childNodeFilter, maxDepth);
}
public boolean validateAndCreate() {
if (Validation.current().valid(this).ok) {
return create();
}
return false;
}
public boolean validateAndSave() {
if (Validation.current().valid(this).ok) {
save();
return true;
}
return false;
}
}