/*
* Copyright 2012 Adaptrex, LLC
*
* Licensed 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.
*/
package com.adaptrex.core.persistence.cayenne;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.ObjectId;
import org.apache.cayenne.lifecycle.id.IdCoder;
import org.apache.cayenne.map.DataMap;
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.map.ObjRelationship;
import org.apache.cayenne.query.ObjectIdQuery;
import org.apache.commons.lang3.StringUtils;
import com.adaptrex.core.ext.ExtTypeFormatter;
import com.adaptrex.core.ext.FieldDefinition;
import com.adaptrex.core.persistence.api.AdaptrexCollectionType;
import com.adaptrex.core.persistence.api.AdaptrexEntityType;
import com.adaptrex.core.security.SecureEntity;
import com.adaptrex.core.utilities.StringUtilities;
public class CayenneCollectionType extends AdaptrexCollectionType {
private Method getter;
private Method setter;
private Method adder;
private Method remover;
/*
* For non-flat associations
*/
private Class<?> joinTable;
private Method joinGetter;
private Method targetGetter;
private Method primarySetter;
private Method targetSetter;
public CayenneCollectionType(ObjEntity objEntity, ObjRelationship relationship, Class<?> collectionType, Class<?> itemType, SecureEntity secureEntity) {
super(relationship.getName(), collectionType, itemType, secureEntity);
DataMap dataMap = objEntity.getDataMap();
String entityName = objEntity.getName();
String targetName = relationship.getTargetEntityName();
Class<?> declaringClass = objEntity.getJavaClass();
/*
* Is this a join table?
*/
if (targetName.startsWith(entityName + "Has") || targetName.endsWith("Has" + entityName)) {
ObjEntity joinEntity = dataMap.getObjEntity(targetName);
joinTable = joinEntity.getJavaClass();
/*
* Get the actual relationship name we want to use
*/
String relEntityName = targetName
.replace(entityName + "Has", "")
.replace("Has" + entityName, "");
String relName = StringUtilities.pluralize(
StringUtilities.uncapitalize(relEntityName)
);
this.name = relName;
this.itemType = dataMap.getObjEntity(relEntityName).getJavaClass();
this.idsFieldDefinition =
new FieldDefinition(
StringUtilities.uncapitalize(relEntityName) +
StringUtils.capitalize(AdaptrexEntityType.COLLECTION_IDS_NAME),
ExtTypeFormatter.AUTO
);
/*
* Get the methods to grab the join table and target table
*/
try {
joinGetter = declaringClass.getMethod(
"get" + StringUtilities.pluralize(joinTable.getSimpleName()));
targetGetter = joinTable.getMethod("get" + this.itemType.getSimpleName());
primarySetter = joinTable.getMethod("set" + declaringClass.getSimpleName(), declaringClass);
targetSetter = joinTable.getMethod("set" + this.itemType.getSimpleName(), this.itemType);
} catch (Exception e) {}
}
/*
* Flattened association
*/
else {
String relName = StringUtilities.singularize(relationship.getName());
this.idsFieldDefinition =
new FieldDefinition(
relName + StringUtils.capitalize(AdaptrexEntityType.COLLECTION_IDS_NAME),
ExtTypeFormatter.AUTO
);
String capName = StringUtilities.capitalize(getName());
try {
getter = declaringClass.getMethod("get" + capName);
} catch (Exception e) {}
if (getter == null) return;
try {
setter = declaringClass.getMethod("set" + capName, getter.getReturnType());
} catch (Exception e) {}
try {
adder = declaringClass.getMethod("addTo" + capName, itemType);
} catch (Exception e) {}
try {
remover = declaringClass.getMethod("removeFrom" + capName, itemType);
} catch (Exception e) {}
}
}
@Override
public Object getCollectionFrom(Object entity) throws Exception {
/*
* Handle non-flattened associations with join tables
*/
if (joinTable != null) {
/*
* Get all recods in join table
*/
try {
List<Object> targetCollection = new ArrayList<Object>();
for (Object joinTableEntity : (Collection<?>) joinGetter.invoke(entity)) {
targetCollection.add(targetGetter.invoke(joinTableEntity));
}
return targetCollection;
} catch (Exception e) {};
return null;
}
/*
* Handle flattened associations
*/
return getter.invoke(entity);
}
@SuppressWarnings("unchecked")
public void setByIds(ObjectContext context, Object entity, Object collectionIds) throws Exception {
List<Object> listItemIds = null;
if (collectionIds != null) {
if (collectionIds instanceof String) {
String stringVal = String.valueOf(collectionIds);
if (!stringVal.isEmpty())
listItemIds = (List<Object>) StringUtilities.fromJson(stringVal, List.class);
} else if (collectionIds instanceof List) {
listItemIds = (List<Object>) collectionIds;
}
}
/*
* Non flattened associations with a join table needs special processing
*/
Collection<Object> oldCollection;
Collection<Object> joinEntities = null;
if (joinTable != null) {
/*
* Get all recods in join table
*/
oldCollection = new ArrayList<Object>();
joinEntities = (Collection<Object>) joinGetter.invoke(entity);
try {
for (Object joinTableEntity : joinEntities) {
oldCollection.add(targetGetter.invoke(joinTableEntity));
}
} catch (Exception e) {};
} else {
oldCollection = (Collection<Object>) getter.invoke(entity);
}
/*
* Get the matching records for the new collection
*/
List<Object> newCollection = new ArrayList<Object>();
if (listItemIds != null) {
IdCoder idCoder = new IdCoder(context.getEntityResolver());
for (Object listItemId : listItemIds) {
ObjectId objectId = idCoder.getObjectId(String.valueOf(listItemId));
ObjectIdQuery query = new ObjectIdQuery(objectId);
List<?> list = context.performQuery(query);
Object newForeignObject = list.size() == 0 ? null : list.get(0);
if (newForeignObject != null) newCollection.add(newForeignObject);
}
}
/*
* Is there a setter for this collection
*/
if (setter != null) {
setter.invoke(entity, newCollection);
return;
}
/*
* Loop through oldCollection and remove those that no longer exist
*/
Iterator<Object> oldIterator = oldCollection.iterator();
while (oldIterator.hasNext()) {
Object oldObject = oldIterator.next();
if (!newCollection.contains(oldObject)) {
oldIterator.remove();
/*
* If we have a join table, delete the join record
*/
if (joinTable != null) {
for (Object joinEntity : joinEntities) {
Object testEntity = targetGetter.invoke(joinEntity);
if (oldObject.equals(testEntity)) {
context.deleteObjects(joinEntity);
break;
}
}
}
/*
* Standard removal
*/
else {
remover.invoke(entity, oldObject);
}
}
}
/*
* Add new entities
*/
newCollection.removeAll(oldCollection);
for (Object newObject : newCollection) {
if (joinTable != null) {
Object joinTableEntity = joinTable.newInstance();
this.primarySetter.invoke(joinTableEntity, entity);
this.targetSetter.invoke(joinTableEntity, newObject);
} else {
adder.invoke(entity, newObject);
}
}
}
}