package com.breeze.query;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import com.breeze.metadata.IEntityType;
import com.breeze.util.JsonGson;
import com.breeze.util.StringFns;
/**
* Represents a query against a strongly typed model.
* This query is designed to be easily converted into variety of persistence library
* query languages via subclasses of the QueryProcessor class.
* An EntityQuery is an immutable structure. This means that mutation methods
* on an EntityQuery actually all return new EntityQuery instances.
* @author IdeaBlade
*
*/
public class EntityQuery {
private String _resourceName;
private Predicate _wherePredicate;
private OrderByClause _orderByClause;
private ExpandClause _expandClause;
private SelectClause _selectClause;
private Integer _skipCount;
private Integer _takeCount;
private boolean _inlineCountEnabled;
private Map _parameters;
private IEntityType _entityType;
public EntityQuery() {
}
/**
* Materializes the serialized json representation of an EntityQuery.
* @param json The serialized json version of the EntityQuery.
*/
public EntityQuery(String json) {
if (json == null || json.length() == 0) {
return;
}
Map qmap;
try {
qmap = JsonGson.fromJson(json);
} catch (Exception e) {
throw new RuntimeException(
"This EntityQuery ctor requires a valid json string. The following is not json: "
+ json);
}
this._resourceName = (String) qmap.get("resourceName");
this._skipCount = processCount(qmap.get("skip"));
this._takeCount = processCount(qmap.get("take"));
this._wherePredicate = Predicate.predicateFromMap((Map) qmap
.get("where"));
this._orderByClause = OrderByClause.from(toStringList(qmap
.get("orderBy")));
this._selectClause = SelectClause
.from(toStringList(qmap.get("select")));
this._expandClause = ExpandClause
.from(toStringList(qmap.get("expand")));
this._parameters = (Map) qmap.get("parameters");
if (qmap.containsKey("inlineCount")) {
this._inlineCountEnabled = ((Boolean) qmap.get("inlineCount"))
.booleanValue();
}
}
/**
* Copy constructor
* @param query
*/
public EntityQuery(EntityQuery query) {
this._resourceName = query._resourceName;
this._skipCount = query._skipCount;
this._takeCount = query._takeCount;
this._wherePredicate = query._wherePredicate;
this._orderByClause = query._orderByClause;
this._selectClause = query._selectClause;
this._expandClause = query._expandClause;
this._inlineCountEnabled = query._inlineCountEnabled;
this._parameters = query._parameters;
}
/**
* Return a new query based on this query with an additional where clause added.
* @param json Json representation of the where clause.
* @return A new EntityQuery.
*/
public EntityQuery where(String json) {
Map qmap = JsonGson.fromJson(json);
Predicate pred = Predicate.predicateFromMap(qmap);
return this.where(pred);
}
/**
* Return a new query based on this query with an additional where clause added.
* @param predicate A Predicate representing the where clause to add.
* @return A new EntityQuery.
*/
public EntityQuery where(Predicate predicate) {
EntityQuery eq = new EntityQuery(this);
if (eq._wherePredicate == null) {
eq._wherePredicate = predicate;
} else if (eq._wherePredicate.getOperator() == Operator.And) {
AndOrPredicate andOrPred = (AndOrPredicate) eq._wherePredicate;
List<Predicate> preds = new ArrayList<Predicate>(andOrPred.getPredicates());
preds.add(predicate);
eq._wherePredicate = new AndOrPredicate(Operator.And, preds);
} else {
eq._wherePredicate = new AndOrPredicate(Operator.And,
eq._wherePredicate, predicate);
}
return eq;
}
/**
* Return a new query based on this query with the specified orderBy clauses added.
* @param propertyPaths A varargs array of orderBy clauses ( each consisting of a property path and an optional sort direction).
* @return A new EntityQuery.
*/
public EntityQuery orderBy(String... propertyPaths) {
return orderBy(Arrays.asList(propertyPaths));
}
/**
* Return a new query based on this query with the specified orderBy clauses added.
* @param propertyPaths An List of orderBy clauses ( each consisting of a property path and an optional sort direction).
* @return A new EntityQuery.
*/
public EntityQuery orderBy(List<String> propertyPaths) {
EntityQuery eq = new EntityQuery(this);
if (this._orderByClause == null) {
eq._orderByClause = new OrderByClause(propertyPaths);
} else {
List<String> propPaths = new ArrayList<String>(
this._orderByClause.getPropertyPaths());
propPaths.addAll(propertyPaths);
eq._orderByClause = new OrderByClause(propPaths);
}
return eq;
}
/**
* Return a new query based on this query with the specified expand clauses added.
* @param propertyPaths A varargs array of expand clauses ( each a dot delimited property path).
* @return A new EntityQuery.
*/
public EntityQuery expand(String... propertyPaths) {
return expand(Arrays.asList(propertyPaths));
}
/**
* Return a new query based on this query with the specified expand clauses added.
* @param propertyPaths A list of expand clauses (each a dot delimited property path).
* @return A new EntityQuery.
*/
public EntityQuery expand(List<String> propertyPaths) {
EntityQuery eq = new EntityQuery(this);
if (this._expandClause == null) {
eq._expandClause = new ExpandClause(propertyPaths);
} else {
// think about checking if any prop paths are duped.
List<String> propPaths = new ArrayList<String>(
this._expandClause.getPropertyPaths());
propPaths.addAll(propertyPaths);
eq._expandClause = new ExpandClause(propPaths);
}
return eq;
}
/**
* Return a new query based on this query with the specified select (projection) clauses added.
* @param propertyPaths A varargs array of select clauses (each a dot delimited property path).
* @return A new EntityQuery.
*/
public EntityQuery select(String... propertyPaths) {
return select(Arrays.asList(propertyPaths));
}
/**
* Return a new query based on this query with the specified select (projection) clauses added.
* @param propertyPaths A list of select clauses (each a dot delimited property path).
* @return A new EntityQuery.
*/
public EntityQuery select(List<String> propertyPaths) {
EntityQuery eq = new EntityQuery(this);
if (this._selectClause == null) {
eq._selectClause = new SelectClause(propertyPaths);
} else {
// think about checking if any prop paths are duped.
List<String> propPaths = new ArrayList<String>(
this._selectClause.getPropertyPaths());
propPaths.addAll(propertyPaths);
eq._selectClause = new SelectClause(propPaths);
}
return eq;
}
/**
* Return a new query based on this query that limits the results to the first n records.
* @param takeCount The number of records to take.
* @return A new EntityQuery
*/
public EntityQuery take(Integer takeCount) {
EntityQuery eq = new EntityQuery(this);
eq._takeCount = takeCount;
return eq;
}
/**
* Return a new query based on this query that skips the first n records.
* @param skipCount The number of records to skip.
* @return A new EntityQuery
*/
public EntityQuery skip(Integer skipCount) {
EntityQuery eq = new EntityQuery(this);
eq._skipCount = skipCount;
return eq;
}
/**
* Return a new query based on this query that either adds or removes the inline count capability.
* @param inlineCountEnabled Whether to enable inlineCount.
* @return A new EntityQuery
*/
public EntityQuery enableInlineCount(boolean inlineCountEnabled) {
EntityQuery eq = new EntityQuery(this);
eq._inlineCountEnabled = inlineCountEnabled;
return eq;
}
/**
* Return a new query based on this query with the specified resourceName
* @param resourceName The name of the url resource.
* @return A new EntityQuery
*/
public EntityQuery withResourceName(String resourceName) {
EntityQuery eq = new EntityQuery(this);
eq._resourceName = resourceName;
return eq;
}
@SuppressWarnings("unchecked")
private List<String> toStringList(Object src) {
if (src == null)
return null;
if (src instanceof List) {
return (List<String>) src;
} else if (src instanceof String) {
return StringFns.ToList((String) src);
}
throw new RuntimeException("Unable to convert to a List<String>");
}
private Integer processCount(Object o) {
if (o == null)
return null;
return ((Double) o).intValue();
}
/**
* Validates that all of the clauses that make up this query are consistent with the
* specified EntityType.
* @param entityType A EntityType
*/
public void validate(IEntityType entityType) {
_entityType = entityType;
if (_wherePredicate != null) {
_wherePredicate.validate(entityType);
}
if (_orderByClause != null) {
_orderByClause.validate(entityType);
}
if (_selectClause != null) {
_selectClause.validate(entityType);
}
}
/**
* Returns the EntityType that this query has been validated against. Not that this property
* will return null until the validate method has been called.
* @return The EntityType that this query has been validated against.
*/
public IEntityType getEntityType() {
return _entityType;
}
public String getResourceName() {
return _resourceName;
}
public Predicate getWherePredicate() {
return _wherePredicate;
}
public OrderByClause getOrderByClause() {
return _orderByClause;
}
public ExpandClause getExpandClause() {
return _expandClause;
}
public SelectClause getSelectClause() {
return _selectClause;
}
public Integer getSkipCount() {
return _skipCount;
}
public Integer getTakeCount() {
return _takeCount;
}
public boolean isInlineCountEnabled() {
return _inlineCountEnabled;
}
public Map getParameters() {
return _parameters;
}
}