/*==========================================================================*\
| $Id: QualifierUtils.java,v 1.2 2012/03/28 13:48:08 stedwar2 Exp $
|*-------------------------------------------------------------------------*|
| Copyright (C) 2006-2012 Virginia Tech
|
| This file is part of Web-CAT.
|
| Web-CAT is free software; you can redistribute it and/or modify
| it under the terms of the GNU Affero General Public License as published
| by the Free Software Foundation; either version 3 of the License, or
| (at your option) any later version.
|
| Web-CAT is distributed in the hope that it will be useful,
| but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
| GNU General Public License for more details.
|
| You should have received a copy of the GNU Affero General Public License
| along with Web-CAT; if not, see <http://www.gnu.org/licenses/>.
\*==========================================================================*/
package org.webcat.core;
import org.webcat.core.QualifierInSubquery;
import com.webobjects.eocontrol.EOAndQualifier;
import com.webobjects.eocontrol.EOClassDescription;
import com.webobjects.eocontrol.EOKeyComparisonQualifier;
import com.webobjects.eocontrol.EOKeyValueQualifier;
import com.webobjects.eocontrol.EOOrQualifier;
import com.webobjects.eocontrol.EOQualifier;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSMutableArray;
import com.webobjects.foundation.NSMutableDictionary;
import er.extensions.eof.qualifiers.ERXInQualifier;
//-------------------------------------------------------------------------
/**
* Utility methods to operate on qualifiers.
*
* @author Tony Allevato
* @author Last changed by $Author: stedwar2 $
* @version $Revision: 1.2 $, $Date: 2012/03/28 13:48:08 $
*/
public class QualifierUtils
{
//~ Constructor ...........................................................
// ----------------------------------------------------------
/**
* This class provides only static utility methods, so no instance
* should ever be created.
*/
private QualifierUtils()
{
// Nothing to do
}
//~ Public Methods ........................................................
// ----------------------------------------------------------
/**
* Returns a value indicating whether specified qualifier tree can be
* used in a fetch specification.
*
* @param q the qualifier to check
* @param entityName the entity being fetched
* @return true if the qualifier can be used in a fetch specification;
* otherwise, false.
*/
public static boolean isQualifierFetchable(EOQualifier q, String entityName)
{
try
{
EOClassDescription classDescription =
EOClassDescription.classDescriptionForEntityName(entityName);
q.validateKeysWithRootClassDescription(classDescription);
return true;
}
catch (IllegalStateException e)
{
return false;
}
}
// ----------------------------------------------------------
/**
* Partitions a qualifier two sets -- the set of qualifiers that can be
* used in a fetch specification, and the set of those that cannot. This is
* used to speed up many queries by passing the fetchable qualifiers to a
* fetch specification to reduce the size of the initial fetch, and then
* pruning that set further in-memory using the non-fetchable qualifiers.
*
* The result of this method is dependent on the type of qualifier passed
* into it. If the qualifier is an EOAndQualifier, then its child
* qualifiers are partitioned into fetchable and non-fetchable sets. For
* any other type of qualifier, the qualifier itself is placed into the
* appropriate partition depending on whether or not it is fetchable.
*
* @param q the qualifier to partition
* @param entityName the name of the entity being fetched
* @return an array with two EOQualifiers. array[0] is the AND of all
* of the fetchable qualifiers, and array[1] is the AND of all of the
* non-fetchable qualifiers. Either of these entries can be null if
* there were no qualifiers in either set.
*/
public static EOQualifier[] partitionQualifier(
EOQualifier q, String entityName)
{
EOQualifier fetchQualifier = null;
EOQualifier nonFetchQualifier = null;
if (q instanceof EOAndQualifier)
{
EOAndQualifier aq = (EOAndQualifier)q;
NSMutableArray<EOQualifier> fetchQualifiers =
new NSMutableArray<EOQualifier>();
NSMutableArray<EOQualifier> nonFetchQualifiers =
new NSMutableArray<EOQualifier>();
for (EOQualifier childQ : aq.qualifiers())
{
if (isQualifierFetchable(childQ, entityName))
{
fetchQualifiers.addObject(childQ);
}
else
{
nonFetchQualifiers.addObject(childQ);
}
}
if (fetchQualifiers.count() > 0)
{
fetchQualifier = new EOAndQualifier(fetchQualifiers);
}
if (nonFetchQualifiers.count() > 0)
{
nonFetchQualifier = new EOAndQualifier(nonFetchQualifiers);
}
}
else
{
if (isQualifierFetchable(q, entityName))
{
fetchQualifier = q;
}
else
{
nonFetchQualifier = q;
}
}
return new EOQualifier[] { fetchQualifier, nonFetchQualifier };
}
// ----------------------------------------------------------
/**
* Returns a qualifier that represents the condition (key IN choices).
* Currently this returns an OR qualifier of the form
* (key == choice1) OR (key == choice2) OR ..., but this could be changed
* to an ERXInQualifier once its in-memory behavior regarding cross-
* relationship keypaths is fixed.
*
* @param key the key being changed for containment
* @param choices the choices among which the value of key should be
*
* @return the qualifier that represents this condition
*/
public static EOQualifier qualifierForInCondition(
String key, NSArray<?> choices)
{
NSMutableArray<EOQualifier> terms = new NSMutableArray<EOQualifier>();
for (Object choice : choices)
{
terms.addObject(new EOKeyValueQualifier(key,
EOQualifier.QualifierOperatorEqual, choice));
}
return new EOOrQualifier(terms);
}
// ----------------------------------------------------------
/**
* Checks to see if the qualifier is essentially representative of an IN
* condition. Currently this means that it is an EOOrQualifier for which
* each child is an EOKeyValueQualifier with the same key; or that the
* qualifier is an ERXInQualifier (though this isn't really used yet
* because of some problems with that qualifier; see above).
*
* If the qualifier is representative of an IN condition, then the array
* returned is the array of choices for the condition. This method returns
* null if the qualifier is not representative of an IN condition.
*
* @param q the qualifier to check and get choices for
* @return an array of choices, or null if not an IN qualifier
*/
public static NSDictionary<String, Object> infoIfInQualifier(EOQualifier q)
{
NSMutableDictionary<String, Object> info =
new NSMutableDictionary<String, Object>();
if (q instanceof ERXInQualifier)
{
ERXInQualifier iq = (ERXInQualifier)q;
info.setObjectForKey(iq.key(), "key");
info.setObjectForKey(iq.values(), "values");
return info;
}
else if (q instanceof EOOrQualifier)
{
EOOrQualifier oq = (EOOrQualifier)q;
NSArray<EOQualifier> children = oq.qualifiers();
NSMutableArray<Object> choices = new NSMutableArray<Object>();
if (children.count() > 0)
{
EOQualifier cq = children.objectAtIndex(0);
if (cq instanceof EOKeyValueQualifier)
{
EOKeyValueQualifier kvq = (EOKeyValueQualifier)cq;
String firstKey = kvq.key();
info.setObjectForKey(firstKey, "key");
choices.addObject(kvq.value());
for (int i = 1; i < children.count(); i++)
{
cq = children.objectAtIndex(i);
if (cq instanceof EOKeyValueQualifier)
{
kvq = (EOKeyValueQualifier)cq;
String key = kvq.key();
if (key.equals(firstKey))
{
choices.addObject(kvq.value());
}
else
{
return null;
}
}
else
{
return null;
}
}
}
else
{
return null;
}
}
info.setObjectForKey(choices, "values");
return info;
}
else
{
return null;
}
}
// ----------------------------------------------------------
/**
* Shortcut method to return a subquery qualifier that determines if the
* object at a keypath is in a given relationship. Unlike a normal
* EOKeyComparisonQualifier that does this, the qualifier returned here can
* be properly negated with EONotQualifier to determine if the object is
* NOT in the relationship.
*
* ".id" is automatically appended to the ends of the relationships passed
* into this function.
*
* @param keyPath the key path of the object to test (a to-one relationship)
* @param relationship the relationship in which to look for keyPath
* (a to-many relationship)
* @return the qualifier
*/
public static EOQualifier qualifierForKeyPathInRelationship(String keyPath,
String relationship)
{
return new QualifierInSubquery(
new EOKeyComparisonQualifier(keyPath + ".id",
EOQualifier.QualifierOperatorEqual,
relationship + ".id"));
}
}