public void updateObjectWithFilter(Object obj, ERXKeyFilter keyFilter, ERXRestContext context) {
if (obj == null) {
return;
}
EOClassDescription classDescription = ERXRestClassDescriptionFactory.classDescriptionForObject(obj, false);
for (Map.Entry<String, Object> attribute : _attributes.entrySet()) {
ERXKey<Object> key = keyFilter.keyMap(new ERXKey<Object>(attribute.getKey()));
String keyName = key.key();
if (keyFilter.matches(key, ERXKey.Type.Attribute) && isClassProperty(classDescription, keyName)) {
Object value = ERXRestUtils.coerceValueToAttributeType(attribute.getValue(), null, obj, keyName, context);
if (value instanceof NSKeyValueCoding.Null) {
value = null;
}
_safeWillTakeValueForKey(keyFilter, obj, value, keyName);
key.takeValueInObject(value, obj);
_safeDidTakeValueForKey(keyFilter, obj, value, keyName);
}
else {
_safeDidSkipValueForKey(keyFilter, obj, attribute.getValue(), keyName); // MS: we didn't coerce the
// value .. i think that's ok
}
}
for (ERXRestRequestNode childNode : _children) {
ERXKey<Object> key = keyFilter.keyMap(new ERXKey<Object>(childNode.name()));
String keyName = key.key();
if (isClassProperty(classDescription, keyName)) {
NSKeyValueCoding._KeyBinding binding = NSKeyValueCoding.DefaultImplementation._keyGetBindingForKey(obj, keyName);
Class<?> valueType = binding.valueType();
if (valueType == Object.class) {
if (childNode.isArray()) {
valueType = NSArray.class;
}
else {
Object childValue = childNode.value();
if (childValue != null) {
valueType = childValue.getClass();
}
}
}
if (List.class.isAssignableFrom(valueType) && keyFilter.matches(key, ERXKey.Type.ToManyRelationship)) {
EOClassDescription destinationClassDescription;
// this is sort of expensive, but we want to support non-eomodel to-many relationships on EO's, so
// we fallback and lookup the class entity ...
if (!classDescription.toManyRelationshipKeys().containsObject(keyName) && classDescription instanceof EOEntityClassDescription) {
EOClassDescription nonModelClassDescription = ERXRestClassDescriptionFactory.classDescriptionForObject(obj, true);
if (!nonModelClassDescription.toManyRelationshipKeys().containsObject(keyName)) {
throw new IllegalArgumentException("There is no to-many relationship named '" + key.key() + "' on '" + classDescription.entityName() + "'.");
}
destinationClassDescription = classDescription.classDescriptionForDestinationKey(keyName);
}
else {
destinationClassDescription = classDescription.classDescriptionForDestinationKey(keyName);
}
if (destinationClassDescription == null) {
if (keyFilter.isUnknownKeyIgnored()) {
continue;
}
else {
throw new NSKeyValueCoding.UnknownKeyException("There is no key '" + keyName + "' on this object.", obj, keyName);
}
}
boolean lockedRelationship = keyFilter.lockedRelationship(key);
@SuppressWarnings("unchecked")
List<Object> existingValues = (List<Object>) NSKeyValueCoding.DefaultImplementation.valueForKey(obj, keyName);
Set<Object> removedValues;
if (existingValues == null) {
removedValues = new HashSet<Object>();
}
else {
removedValues = new HashSet<Object>(existingValues);
}
List<Object> newValues = new LinkedList<Object>();
List<Object> allValues = new LinkedList<Object>();
for (ERXRestRequestNode toManyNode : childNode.children()) {
Object id = toManyNode.id();
if (toManyNode.type() != null) {
destinationClassDescription = ERXRestClassDescriptionFactory.classDescriptionForEntityName(toManyNode.type());
}
Object childObj;
if (toManyNode.children().count() == 0 && ERXRestUtils.isPrimitive(toManyNode.value())) {
if (lockedRelationship) {
childObj = null;
}
else {
if (toManyNode.value() != null) {
childObj = toManyNode.value();
} else {
childObj = IERXRestDelegate.Factory.delegateForClassDescription(destinationClassDescription).objectOfEntityWithID(destinationClassDescription, id, context);
}
}
}
else if (id == null) {
if (lockedRelationship) {
childObj = null;
}
else {
childObj = IERXRestDelegate.Factory.delegateForClassDescription(destinationClassDescription).createObjectOfEntityWithID(destinationClassDescription, id, context);
}
}
else {
childObj = IERXRestDelegate.Factory.delegateForClassDescription(destinationClassDescription).objectOfEntityWithID(destinationClassDescription, id, context);
}
if (childObj != null) {
boolean newMemberOfRelationship = existingValues == null || !existingValues.contains(childObj);
if (newMemberOfRelationship) {
if (!lockedRelationship) {
toManyNode.updateObjectWithFilter(childObj, keyFilter._filterForKey(key), context);
newValues.add(childObj);
allValues.add(childObj);
}
}
else {
toManyNode.updateObjectWithFilter(childObj, keyFilter._filterForKey(key), context);
allValues.add(childObj);
}
removedValues.remove(childObj);
}
}
if (!lockedRelationship) {
_safeWillTakeValueForKey(keyFilter, obj, allValues, keyName);
if (obj instanceof EOEnterpriseObject) {
for (Object removedValue : removedValues) {
((EOEnterpriseObject) obj).removeObjectFromBothSidesOfRelationshipWithKey((EOEnterpriseObject) removedValue, keyName);
}
for (Object newValue : newValues) {
((EOEnterpriseObject) obj).addObjectToBothSidesOfRelationshipWithKey((EOEnterpriseObject) newValue, keyName);
}
}
else {
key.takeValueInObject(allValues, obj);
}
_safeDidTakeValueForKey(keyFilter, obj, allValues, keyName);
}
else {
_safeDidSkipValueForKey(keyFilter, obj, allValues, keyName);
}
}
else if (!ERXRestUtils.isPrimitive(valueType) && keyFilter.matches(key, ERXKey.Type.ToOneRelationship)) {
EOClassDescription destinationClassDescription;
// this is sort of expensive, but we want to support non-eomodel to-one relationships on EO's, so
// we fallback and lookup the class entity ...
if (!classDescription.toOneRelationshipKeys().containsObject(keyName) && classDescription instanceof EOEntityClassDescription) {
EOClassDescription nonModelClassDescription = ERXRestClassDescriptionFactory.classDescriptionForObject(obj, true);
if (!nonModelClassDescription.toOneRelationshipKeys().containsObject(keyName)) {
throw new IllegalArgumentException("There is no to-one relationship named '" + key.key() + "' on '" + classDescription.entityName() + "'.");
}
destinationClassDescription = nonModelClassDescription.classDescriptionForDestinationKey(keyName);
}
else {
destinationClassDescription = classDescription.classDescriptionForDestinationKey(keyName);
}
if (destinationClassDescription == null) {