/**
*
*/
package org.activejpa.entity;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Root;
import org.activejpa.ActiveJpaException;
import org.activejpa.util.PropertyUtil;
import org.javalite.common.Inflector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author ganeshs
*
*/
public class EntityCollection<T extends Model> extends BaseObject {
private Model parent;
private String name;
private Class<T> elementType;
private static final Logger logger = LoggerFactory.getLogger(EntityCollection.class);
/**
* @param parent
* @param name
*/
public EntityCollection(Model parent, String name, Class<T> elementType) {
this.parent = parent;
this.name = name;
this.elementType = elementType;
}
/**
* Adds the item to the underlying collection
*/
public T add(T item) {
logger.debug("Adding the item {} to the collection", item);
return addOrRemove(item, true);
}
/**
* Removes the item from the underlying collection
*/
public T remove(T item) {
logger.debug("Removing the item {} from the collection", item);
return addOrRemove(item, false);
}
/**
* Adds or Removes the item to/from the underlying collection. This method attempts in the following order to add/remove the item,
* <ul>
* <li> Checks if there's a method with the signature <code>addOrderItem(OrderItem orderItem)</code> in the parent model where orderItem is a collection.
* <li> Retrieves the getter for the collection property and adds/removes the item to/from that
* <li> Retrieves the collection property field and adds/removes the item to/from that
* </ul>
*
* Throws {@link ActiveJpaException} if none of the above ways adds/removes the item to/from the collection
* @param item
*/
@SuppressWarnings("unchecked")
protected T addOrRemove(T item, boolean add) {
Method method = null;
String methodName = getMethodName(add ? "add" : "remove", name);
try {
logger.trace("Attempting to invoke the method {} on the parent {}", methodName, parent);
method = parent.getClass().getDeclaredMethod(methodName, elementType);
method.invoke(parent, item);
return item;
} catch (NoSuchMethodException e) {
logger.debug("Method {} doesn't exist in the class {}", methodName, parent.getClass());
} catch (Exception e) {
throw new ActiveJpaException("Failed while invoking the method " + method.getName(), e);
}
// Try adding the item to the collection property returned by the getter
logger.trace("Attempting to invoke the getter for the property {} on the parent {}", name, parent);
Collection<T> collection = (Collection<T>) PropertyUtil.getProperty(parent, name);
if (collection != null) {
if (add) {
collection.add(item);
} else {
collection.remove(item);
}
return item;
}
// Try to find out the field and add/remove it to/from that
try {
logger.trace("Attempting to invoke the the property {} on the parent {}", name, parent);
Field field = parent.getClass().getDeclaredField(name);
field.setAccessible(true);
collection = (Collection<T>) field.get(parent);
if (add) {
collection.add(item);
} else {
collection.remove(item);
}
return item;
} catch (Exception e) {
logger.error("Failed to {} the item {} to the collection of the parent", add ? "add" : "remove", item, parent);
throw new ActiveJpaException("Failed while adding/removing the item to the collection - " + name, e);
}
}
public T findById(Serializable id) {
return one(name + ".id", id);
}
public List<T> all() {
return where(new Filter());
}
public T first(Object... paramValues) {
List<T> list = where(createFilter(paramValues));
if (list == null || list.isEmpty()) {
return null;
}
return list.get(0);
}
public T one(Object... paramValues) {
Filter filter = createFilter(paramValues);
return createCollectionQuery(filter).getSingleResult();
}
public List<T> where(Filter filter) {
return createCollectionQuery(filter).getResultList();
}
public List<T> where(Object... paramValues) {
return where(createFilter(paramValues));
}
public long count(Filter filter) {
filter.addCondition("id", parent.getId());
CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Long> cQuery = builder.createQuery(Long.class);
Root<? extends Model> root = cQuery.from(parent.getClass());
Join join = root.join(name);
cQuery.select(builder.count(join));
filter.constructQuery(builder, cQuery, root);
TypedQuery<Long> query = createQuery(cQuery, filter);
return query.getSingleResult();
}
private TypedQuery<T> createCollectionQuery(Filter filter) {
filter.addCondition("id", parent.getId());
TypedQuery<T> query = createQuery(parent.getClass(), name, elementType, filter);
return query;
}
public void persist() {
parent.persist();
}
/**
* Constructs the name of the method from the collectionName
*
* @param prefix
* @param collectionName
* @return
*/
private String getMethodName(String prefix, String collectionName) {
collectionName = Inflector.singularize(collectionName);
return prefix + collectionName.substring(0, 1).toUpperCase() + collectionName.substring(1);
}
}