package er.extensions.eof; import java.util.Iterator; import com.webobjects.eocontrol.EOClassDescription; import com.webobjects.eocontrol.EOEditingContext; import com.webobjects.eocontrol.EOFetchSpecification; import com.webobjects.eocontrol.EOQualifier; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSDictionary; import com.webobjects.foundation.NSMutableArray; /** * Extended fetch specification (work in progress). * <ul> * <li>has grouping support</li> * </ul> * @author ak * * @param <T> */ public class ERXGroupingFetchSpecification<T extends NSDictionary> extends ERXFetchSpecification { /** * Do I need to update serialVersionUID? * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a> */ private static final long serialVersionUID = 1L; /** * List of supported aggregate operators. */ public static interface Operators { public String SUM = "sum"; public String AVG = "avg"; public String MIN = "min"; public String MAX = "max"; public String CNT = "count"; } private static class Aggregate { private String _operator; private String _keypath; public Aggregate(String operator, String keypath) { _operator = operator; _keypath = keypath; } public String operator() { return _operator; } public String keyPath() { return _keypath; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((_keypath == null) ? 0 : _keypath.hashCode()); result = prime * result + ((_operator == null) ? 0 : _operator.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final Aggregate other = (Aggregate) obj; if (_keypath == null) { if (other._keypath != null) return false; } else if (!_keypath.equals(other._keypath)) return false; if (_operator == null) { if (other._operator != null) return false; } else if (!_operator.equals(other._operator)) return false; return true; } @Override public String toString() { return _operator + "(" + _keypath + ")"; } } private NSMutableArray<Aggregate> _aggegrateKeyPaths = new NSMutableArray(); private EOQualifier _havingQualifier; public ERXGroupingFetchSpecification(String entityName, EOQualifier qualifier, NSArray sortOrderings, NSArray<Aggregate> aggregates, EOQualifier havingQualifier) { super(entityName, qualifier, sortOrderings); init(); setAggregates(aggregates); setHavingQualifier(havingQualifier); } public ERXGroupingFetchSpecification(EOFetchSpecification spec) { super(spec); init(); } public ERXGroupingFetchSpecification(ERXFetchSpecification spec) { super(spec); init(); } public ERXGroupingFetchSpecification(ERXGroupingFetchSpecification<T> spec) { this((ERXFetchSpecification)spec); setAggregates(spec.aggregates()); setHavingQualifier(spec.havingQualifier()); } private void init() { setFetchesRawRows(true); setRawRowKeyPaths(EOClassDescription.classDescriptionForEntityName(entityName()).attributeKeys()); } @Override public void setRawRowKeyPaths(NSArray keyPaths) { super.setRawRowKeyPaths(keyPaths); } public NSArray<Aggregate> aggregates() { return _aggegrateKeyPaths.immutableClone(); } public void setAggregates(NSArray<Aggregate> value) { _aggegrateKeyPaths.removeAllObjects(); _aggegrateKeyPaths.addObjectsFromArray(value); } public void addAggregateForPath(String operator, String keyPath) { _aggegrateKeyPaths.addObject(new Aggregate(operator, keyPath)); } public void removeAggregateForPath(String operator, String keyPath) { _aggegrateKeyPaths.removeObject(new Aggregate(operator, keyPath)); } public NSArray<String> groupingKeyPaths() { NSMutableArray<String> result = new NSMutableArray<>(); result.addObjectsFromArray(rawRowKeyPaths()); for (Iterator<String> iterator = result.iterator(); iterator.hasNext();) { String key = iterator.next(); for (Iterator<Aggregate> iterator2 = aggregates().iterator(); iterator2.hasNext();) { Aggregate aggregate = iterator2.next(); if(aggregate.keyPath().equals(key)) { iterator.remove(); } } } return result; } public EOQualifier havingQualifier() { return _havingQualifier; } public void setHavingQualifier(EOQualifier qualifier) { _havingQualifier = qualifier; } @Override protected String additionalIdentifierInfo() { return super.additionalIdentifierInfo() + aggregates() + identifierForQualifier(havingQualifier()); } /** * Type-safe method to fetch the rows for this fetch spec. * @param ec */ @Override public NSArray<T> fetchObjects(EOEditingContext ec) { NSArray oldKeyPaths = rawRowKeyPaths(); try { setRawRowKeyPaths(groupingKeyPaths()); return super.fetchObjects(ec); } finally { setRawRowKeyPaths(oldKeyPaths); } } }