Skip to content

Commit

Permalink
add support for one-hop-qualifiers for memory adaptor
Browse files Browse the repository at this point in the history
the memory adaptor didn't allow qualifiers across relationships. this commit will add traversal through one relation making all neighbors of the current eo available
  • Loading branch information
darkv authored and Pascal Robert committed Apr 30, 2012
1 parent ed887bd commit 4b272a9
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 38 deletions.
79 changes: 79 additions & 0 deletions Frameworks/Core/ERExtensions/Sources/er/extensions/eof/ERXQ.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import java.util.Enumeration;
import java.util.NoSuchElementException;

import com.webobjects.eocontrol.EOAndQualifier;
import com.webobjects.eocontrol.EOKeyValueQualifier;
import com.webobjects.eocontrol.EONotQualifier;
import com.webobjects.eocontrol.EOOrQualifier;
import com.webobjects.eocontrol.EOQualifier;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSMutableArray;
Expand All @@ -18,6 +21,7 @@
import er.extensions.qualifiers.ERXKeyValueQualifier;
import er.extensions.qualifiers.ERXNotQualifier;
import er.extensions.qualifiers.ERXOrQualifier;
import er.extensions.qualifiers.ERXTrueQualifier;

/**
* <p>
Expand Down Expand Up @@ -1030,4 +1034,79 @@ public static ERXAndQualifier containsAllInAny(String[] keys, String[] tokens) {
public static String keyPath(String... elements) {
return new NSArray<String>(elements).componentsJoinedByString(".");
}

/**
* Analyzes the given qualifier and returns all found {@link EOKeyValueQualifier} objects
* contained within.
*
* @param qualifier
* qualifier to analyze
* @return array of found key value qualifiers
*/
public static NSArray<EOKeyValueQualifier> extractKeyValueQualifiers(EOQualifier qualifier) {
if (qualifier == null) {
return NSArray.EmptyArray;
}
NSMutableArray<EOKeyValueQualifier> array = new NSMutableArray<EOKeyValueQualifier>();
_extractKeyValueQualifiers(array, qualifier);
return array;
}

private static void _extractKeyValueQualifiers(NSMutableArray<EOKeyValueQualifier> array, EOQualifier qualifier) {
if (qualifier instanceof EOKeyValueQualifier) {
array.add((EOKeyValueQualifier) qualifier);
} else if (qualifier instanceof EOAndQualifier || qualifier instanceof EOOrQualifier) {
NSArray<EOQualifier> qualifiers;
if (qualifier instanceof EOAndQualifier) {
qualifiers = ((EOAndQualifier)qualifier).qualifiers();
} else {
qualifiers = ((EOOrQualifier)qualifier).qualifiers();
}
for (EOQualifier item : qualifiers) {
_extractKeyValueQualifiers(array, item);
}
} else if (qualifier instanceof EONotQualifier) {
_extractKeyValueQualifiers(array, ((EONotQualifier)qualifier).qualifier());
}
}

/**
* Takes a qualifier and searches for the given qualifier within to replace it with another one.
*
* @param qualifier
* the qualifier to modify
* @param searchFor
* the qualifier to replace
* @param replaceWith
* the qualifier that replaces the searched one
* @return modified qualifier
*/
public static EOQualifier replaceQualifierWithQualifier(EOQualifier qualifier, EOQualifier searchFor, EOQualifier replaceWith) {
if (qualifier == null || searchFor == null) {
throw new IllegalStateException("The params qualifier and searchFor must not be null!");
}
if (replaceWith == null) {
replaceWith = new ERXTrueQualifier();
}
EOQualifier result = qualifier;
if (qualifier.equals(searchFor)) {
result = replaceWith;
} else if (qualifier instanceof EOAndQualifier) {
NSMutableArray<EOQualifier> array = new NSMutableArray<EOQualifier>();
for (EOQualifier item : ((EOAndQualifier)qualifier).qualifiers()) {
array.add(replaceQualifierWithQualifier(item, searchFor, replaceWith));
}
result = new EOAndQualifier(array);
} else if (qualifier instanceof EOOrQualifier) {
NSMutableArray<EOQualifier> array = new NSMutableArray<EOQualifier>();
for (EOQualifier item : ((EOOrQualifier)qualifier).qualifiers()) {
array.add(replaceQualifierWithQualifier(item, searchFor, replaceWith));
}
result = new EOOrQualifier(array);
} else if (qualifier instanceof EONotQualifier) {
EOQualifier replacedQualifier = replaceQualifierWithQualifier(((EONotQualifier)qualifier).qualifier(), searchFor, replaceWith);
result = new EONotQualifier(replacedQualifier);
}
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import com.webobjects.eoaccess.EOAttribute;
import com.webobjects.eoaccess.EOEntity;
import com.webobjects.eoaccess.EOGeneralAdaptorException;
import com.webobjects.eoaccess.EORelationship;
import com.webobjects.eocontrol.EOAndQualifier;
import com.webobjects.eocontrol.EOFetchSpecification;
import com.webobjects.eocontrol.EOKeyValueQualifier;
import com.webobjects.eocontrol.EOQualifier;
import com.webobjects.eocontrol.EOSortOrdering;
import com.webobjects.foundation.NSArray;
Expand All @@ -20,6 +22,10 @@
import com.webobjects.foundation.NSMutableDictionary;
import com.webobjects.foundation.NSRange;

import er.extensions.eof.ERXFetchSpecification;
import er.extensions.eof.ERXQ;
import er.extensions.foundation.ERXStringUtilities;

/**
* EREntityStore is an abstract datastore implementation for a single "table"
* in non relational EOAdaptors like ERMemoryAdaptor. It provides basic fetch support.
Expand Down Expand Up @@ -63,15 +69,16 @@ public int deleteRowsDescribedByQualifier(EOQualifier qualifier, EOEntity entity
}
}

public NSMutableArray<NSMutableDictionary<String, Object>> fetch(NSArray<EOAttribute> attributesToFetch, EOFetchSpecification fetchSpecification, boolean shouldLock, EOEntity entity) {
EOQualifier qualifier = null;
int fetchLimit = 0;
NSArray sortOrderings = null;
if (fetchSpecification != null) {
qualifier = fetchSpecification.qualifier();
fetchLimit = fetchSpecification.fetchLimit();
sortOrderings = fetchSpecification.sortOrderings();
}
public NSMutableArray<NSMutableDictionary<String, Object>> fetch(NSArray<EOAttribute> attributesToFetch,
EOFetchSpecification fetchSpecification, boolean shouldLock, EOEntity entity, ERMemoryAdaptorContext context) {
EOQualifier qualifier = null;
int fetchLimit = 0;
NSArray<EOSortOrdering> sortOrderings = null;
if (fetchSpecification != null) {
qualifier = fetchSpecification.qualifier();
fetchLimit = fetchSpecification.fetchLimit();
sortOrderings = fetchSpecification.sortOrderings();
}

if (entity.restrictingQualifier() != null) {
if (qualifier != null) {
Expand All @@ -80,35 +87,146 @@ public NSMutableArray<NSMutableDictionary<String, Object>> fetch(NSArray<EOAttri
qualifier = entity.restrictingQualifier();
}
}

// int count = 0;
NSMutableArray<NSMutableDictionary<String, Object>> fetchedRows = new NSMutableArray<NSMutableDictionary<String, Object>>();
Iterator<NSMutableDictionary<String, Object>> i = iterator();
while (i.hasNext()) {
NSMutableDictionary<String, Object> rawRow = i.next();
NSMutableDictionary<String, Object> row = rowFromStoredValues(rawRow, entity);
if (qualifier == null || qualifier.evaluateWithObject(row)) {
fetchedRows.addObject(row);
// count++;
}
// if (fetchLimit > 0 && count == fetchLimit) {
// break;
// }
}

if (sortOrderings != null) {
EOSortOrdering.sortArrayUsingKeyOrderArray(fetchedRows, sortOrderings);
}

if (fetchLimit > 0 && fetchedRows.count() > fetchLimit) {
fetchedRows.removeObjectsInRange(new NSRange(fetchLimit, fetchedRows.count() - fetchLimit));
}
return fetchedRows;
}
NSMutableArray<EORelationship> mergeRelationships = new NSMutableArray<EORelationship>();
if (qualifier != null && context != null) {
NSArray<EOKeyValueQualifier> keyValueQualifiers = ERXQ.extractKeyValueQualifiers(qualifier);
for (EOKeyValueQualifier keyValueQualifier : keyValueQualifiers) {
String qualifierKey = keyValueQualifier.key();
String relationshipName = qualifierKey;
if (relationshipName.contains(".")) {
relationshipName = ERXStringUtilities.firstPropertyKeyInKeyPath(relationshipName);
}
EORelationship mergeRelationship = entity.relationshipNamed(relationshipName);
if (mergeRelationship != null) {
mergeRelationships.add(mergeRelationship);
qualifier = ERXQ.replaceQualifierWithQualifier(qualifier, keyValueQualifier,
ERXQ.has(qualifierKey, new NSArray(keyValueQualifier.value())));
} else if (qualifierKey.equals(entity.primaryKeyAttributeNames().get(0))
&& keyValueQualifier.selector().name().equals("doesContain") && !(keyValueQualifier.value() instanceof NSArray)) {
// fix wrong schemaBasedQualifier
qualifier= ERXQ.replaceQualifierWithQualifier(qualifier, keyValueQualifier,
ERXQ.is(qualifierKey, keyValueQualifier.value()));
}
}
}

// int count = 0;
NSMutableArray<NSMutableDictionary<String, Object>> fetchedRows = new NSMutableArray<NSMutableDictionary<String, Object>>();
Iterator<NSMutableDictionary<String, Object>> i = iterator();
while (i.hasNext()) {
NSMutableDictionary<String, Object> rawRow = i.next();
NSMutableDictionary<String, Object> row = rowFromStoredValues(rawRow, entity);
for (EORelationship mergeRelationship : mergeRelationships) {
NSArray<NSMutableDictionary<String, Object>> found = null;
if (mergeRelationship.isFlattened() && mergeRelationship.isToMany()) {
found = fetchRelatedManyToManyRows(entity, row, mergeRelationship, context);
} else {
found = fetchRelatedRows(entity, row, mergeRelationship, context);
}
if (found != null && !found.isEmpty()) {
row.setObjectForKey(found, mergeRelationship.name());
}
}
if (qualifier == null || qualifier.evaluateWithObject(row)) {
for (EORelationship mergeRelationship : mergeRelationships) {
row.removeObjectForKey(mergeRelationship.name());
}
fetchedRows.addObject(row);
// count++;
}
// if (fetchLimit > 0 && count == fetchLimit) {
// break;
// }
}

if (sortOrderings != null) {
EOSortOrdering.sortArrayUsingKeyOrderArray(fetchedRows, sortOrderings);
}

if (fetchLimit > 0 && fetchedRows.count() > fetchLimit) {
fetchedRows.removeObjectsInRange(new NSRange(fetchLimit, fetchedRows.count() - fetchLimit));
}
return fetchedRows;
}

/**
* Will fetch related rows for the given row via the passed many-to-many
* relationship. The context will be used to access the needed entity
* stores.
*
* @param entity
* the current entity
* @param row
* the currently selected row
* @param relationship
* the many-to-many relationship
* @param context
* the memory adaptor context
* @return array of rows from related entity store
*/
protected NSArray<NSMutableDictionary<String, Object>> fetchRelatedManyToManyRows(EOEntity entity,
NSDictionary<String, Object> row, EORelationship relationship, ERMemoryAdaptorContext context) {
String relationshipPath = relationship.relationshipPath();
String toJoinKey = ERXStringUtilities.firstPropertyKeyInKeyPath(relationshipPath);
String toDestKey = ERXStringUtilities.keyPathWithoutFirstProperty(relationshipPath);
EORelationship toJoinRelationship = entity.anyRelationshipNamed(toJoinKey);
EOEntity joinEntity = toJoinRelationship.destinationEntity();
EREntityStore joinStore = context._entityStoreForEntity(joinEntity);
String sourceAttribute = toJoinRelationship.sourceAttributes().get(0).name();
String destinationAttribute = toJoinRelationship.destinationAttributes().get(0).name();

ERXFetchSpecification fs = new ERXFetchSpecification(joinEntity.name(), ERXQ.equals(destinationAttribute,
row.valueForKey(sourceAttribute)), null);
NSArray<NSMutableDictionary<String, Object>> fetchedObjects = joinStore.fetch(joinEntity.attributesToFetch(),
fs, false, joinEntity, context);

if (fetchedObjects.isEmpty()) {
return NSArray.EmptyArray;
}

EORelationship destRelationship = joinEntity.anyRelationshipNamed(toDestKey);
sourceAttribute = destRelationship.sourceAttributes().get(0).name();
destinationAttribute = destRelationship.destinationAttributes().get(0).name();
NSArray<Object> destValues = (NSArray<Object>) fetchedObjects.valueForKey(sourceAttribute);
EOEntity destEntity = relationship.destinationEntity();

fs = new ERXFetchSpecification(destEntity.name(), ERXQ.in(destinationAttribute, destValues), null);
EREntityStore destinationStore = context._entityStoreForEntity(destEntity);
fetchedObjects = destinationStore.fetch(destEntity.attributesToFetch(), fs, false, destEntity, context);

return fetchedObjects;
}

/**
* Will fetch related rows for the given row relationship. The
* context will be used to access the needed entity store.
*
* @param entity
* the current entity
* @param row
* the currently selected row
* @param relationship
* the relationship
* @param context
* the memory adaptor context
* @return array of rows from related entity store
*/
protected NSArray<NSMutableDictionary<String, Object>> fetchRelatedRows(EOEntity entity,
NSDictionary<String, Object> row, EORelationship relationship, ERMemoryAdaptorContext context) {
EOEntity destEntity = relationship.destinationEntity();
EREntityStore destStore = context._entityStoreForEntity(destEntity);
String sourceAttribute = relationship.sourceAttributes().get(0).name();
String destinationAttribute = relationship.destinationAttributes().get(0).name();

ERXFetchSpecification fs = new ERXFetchSpecification(destEntity.name(), ERXQ.equals(destinationAttribute,
row.valueForKey(sourceAttribute)), null);
return destStore.fetch(destEntity.attributesToFetch(), fs, false, destEntity, context);
}

protected NSMutableDictionary<String, Object> rowFromStoredValues(NSMutableDictionary<String, Object> rawRow, EOEntity entity) {
NSMutableDictionary<String, Object> row = new NSMutableDictionary<String, Object>(rawRow.count());
for (EOAttribute attribute : (NSArray<EOAttribute>)entity.attributesToFetch()) {
for (EOAttribute attribute : entity.attributesToFetch()) {
Object value = rawRow.objectForKey(attribute.columnName());
if (attribute.isDerived()) {
if (!attribute.isFlattened()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ public ERJoinEntityStore(NSDictionary<EOEntity, EREntityStore> stores, EOEntity

@Override
public NSMutableArray<NSMutableDictionary<String, Object>> fetch(NSArray<EOAttribute> attributesToFetch, EOFetchSpecification fetchSpecification,
boolean shouldLock, EOEntity entity) {
boolean shouldLock, EOEntity entity, ERMemoryAdaptorContext context) {
EREntityStore store = joinedStore(attributesToFetch, fetchSpecification, entity);
return store.fetch(attributesToFetch, fetchSpecification, shouldLock, entity);
return store.fetch(attributesToFetch, fetchSpecification, shouldLock, entity, context);
}

private EREntityStore joinedStore(NSArray<EOAttribute> attributesToFetch, EOFetchSpecification fetchSpecification, EOEntity entity) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public NSDictionary returnValuesForLastStoredProcedureInvocation() {
}

@Override
public void selectAttributes(NSArray attributesToFetch, EOFetchSpecification fetchSpecification, boolean shouldLock, EOEntity entity) {
public void selectAttributes(NSArray<EOAttribute> attributesToFetch, EOFetchSpecification fetchSpecification, boolean shouldLock, EOEntity entity) {
if (entity == null) {
throw new IllegalArgumentException("null entity.");
}
Expand All @@ -141,7 +141,7 @@ public void selectAttributes(NSArray attributesToFetch, EOFetchSpecification fet
EREntityStore store = adaptorContext()._entityStoreForEntity(entity);
try {
_fetchIndex = 0;
_fetchedRows = store.fetch(attributesToFetch, fetchSpecification, shouldLock, entity);
_fetchedRows = store.fetch(attributesToFetch, fetchSpecification, shouldLock, entity, adaptorContext());
}
catch (EOGeneralAdaptorException e) {
cancelFetch();
Expand Down

0 comments on commit 4b272a9

Please sign in to comment.