package meetup.beeno;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.List;
import meetup.beeno.filter.ColumnMatchFilter;
import meetup.beeno.filter.WhileMatchFilter;
import meetup.beeno.mapping.EntityInfo;
import meetup.beeno.mapping.FieldMapping;
import meetup.beeno.mapping.MappingException;
import meetup.beeno.util.IOUtil;
import meetup.beeno.util.PBUtil;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.log4j.Logger;
/**
* Utility for building up criteria for HBaseEntity queries.
*
* @author garyh
*
*/
public class Criteria implements Externalizable {
private static Logger log = Logger.getLogger(Criteria.class);
private List<Expression> expressions = new ArrayList<Expression>();
public Criteria() {
}
public Criteria add(Expression expr) {
this.expressions.add(expr);
return this;
}
public boolean isEmpty() {
return this.expressions.isEmpty();
}
public List<Expression> getExpressions() {
return this.expressions;
}
@Override
public void readExternal( ObjectInput in ) throws IOException,
ClassNotFoundException {
this.expressions = (in.readBoolean() ? null : (List<Expression>)in.readObject());
}
@Override
public void writeExternal( ObjectOutput out ) throws IOException {
IOUtil.writeNullable(out, this.expressions);
}
/* ************* Expressions and builder methods ************** */
public static Expression require(Expression expr) {
return new RequireExpression(expr);
}
public static Expression and(Expression... expr) {
CompoundExpression wrapper = new CompoundExpression(true);
for (Expression e : expr) {
wrapper.add(e);
}
return wrapper;
}
public static Expression or(Expression... expr) {
CompoundExpression wrapper = new CompoundExpression(false);
for (Expression e : expr) {
if (log.isDebugEnabled())
log.debug(String.format("Adding OR expression %s", expr.toString()));
wrapper.add(e);
}
return wrapper;
}
public static Expression or(List<Expression> expr) {
CompoundExpression wrapper = new CompoundExpression(false);
for (Expression e : expr) {
if (log.isDebugEnabled())
log.debug(String.format("Adding OR expression %s", expr));
wrapper.add(e);
}
return wrapper;
}
public static Expression eq(String prop, Object val) {
return new PropertyComparison(prop, val, ColumnMatchFilter.CompareOp.EQUAL);
}
public static Expression ne(String prop, Object val) {
return new PropertyComparison(prop, val, ColumnMatchFilter.CompareOp.NOT_EQUAL);
}
public static abstract class Expression implements Externalizable {
public Expression() {
}
public abstract Filter getFilter(EntityInfo info) throws HBaseException;
public String toString() {
return "["+this.getClass().getSimpleName()+"]";
}
}
public static abstract class PropertyExpression extends Expression implements Externalizable {
protected String property;
protected Object value;
public PropertyExpression() {
// for Externalizable
}
public PropertyExpression(String prop, Object val) {
this.property = prop;
this.value = val;
}
public String getProperty() { return this.property; }
public Object getValue() { return this.value; }
@Override
public void readExternal( ObjectInput in ) throws IOException,
ClassNotFoundException {
this.property = IOUtil.readString(in);
this.value = IOUtil.readWithType(in);
}
@Override
public void writeExternal( ObjectOutput out ) throws IOException {
IOUtil.writeNullable(out, this.property);
IOUtil.writeNullableWithType(out, this.value);
}
public String toString() {
return "["+this.getClass().getSimpleName()+": property="+this.property+", value="+this.value+"]";
}
}
public static class PropertyComparison extends PropertyExpression {
private ColumnMatchFilter.CompareOp op = null;
public PropertyComparison() {
// for Externalizable
}
public PropertyComparison(String prop, Object val, ColumnMatchFilter.CompareOp op) {
super(prop, val);
this.op = op;
}
public Filter getFilter(EntityInfo entityInfo) throws HBaseException {
FieldMapping mapping = entityInfo.getPropertyMapping(this.property);
if (mapping == null) {
throw new MappingException( entityInfo.getEntityClass(),
String.format("No mapping for criteria! class=%s, property=%s",
entityInfo.getEntityClass().getName(), this.property) );
}
if (log.isDebugEnabled()) {
log.debug(String.format("PropertyComparison(%s, %s, %s): Creating ColumnRowFilter, column=%s",
this.property, this.value, this.op.toString(), mapping.getFieldName()));
}
return new ColumnMatchFilter(Bytes.toBytes(mapping.getFieldName()),
this.op,
PBUtil.toBytes(this.value),
true);
}
public String toString() {
return String.format("[%s: property=%s, value=%s]", this.getClass().getSimpleName(), this.property, this.value.toString());
}
}
public static class RequireExpression extends Expression {
private Expression required;
public RequireExpression() {
// for Externalizable
}
public RequireExpression(Expression required) {
this.required = required;
}
public Filter getFilter(EntityInfo entityInfo) throws HBaseException {
if (log.isDebugEnabled())
log.debug(String.format("Adding filter: WhileMatchFilter for expr %s", this.required.toString()));
Filter newFilter = new WhileMatchFilter(required.getFilter(entityInfo));
return newFilter;
}
public Expression getRequired() {
return this.required;
}
@Override
public void readExternal( ObjectInput in ) throws IOException,
ClassNotFoundException {
this.required = (in.readBoolean() ? null : (Expression)in.readObject());
}
@Override
public void writeExternal( ObjectOutput out ) throws IOException {
IOUtil.writeNullable(out, this.required);
}
public String toString() {
return String.format("[%s: required=%s]", this.getClass().getSimpleName(), this.required.toString());
}
}
public static class CompoundExpression extends Expression {
private FilterList.Operator oper = null;
private List<Expression> subconditions = new ArrayList<Expression>();
public CompoundExpression() {
// for Externalizable
}
public CompoundExpression(boolean reqAll) {
if (reqAll)
this.oper = FilterList.Operator.MUST_PASS_ALL;
else
this.oper = FilterList.Operator.MUST_PASS_ONE;
}
public Filter getFilter(EntityInfo entityInfo) throws HBaseException {
FilterList newFilter = new FilterList(this.oper, new ArrayList<Filter>());
for (Expression expr : this.subconditions) {
newFilter.addFilter(expr.getFilter(entityInfo));
}
return newFilter;
}
public void add(Expression e) { this.subconditions.add(e); }
@Override
public void readExternal( ObjectInput in ) throws IOException,
ClassNotFoundException {
this.oper = (FilterList.Operator)IOUtil.readEnum(in, FilterList.Operator.class);
this.subconditions = (in.readBoolean() ? null : (List<Expression>)in.readObject());
}
@Override
public void writeExternal( ObjectOutput out ) throws IOException {
IOUtil.writeNullable(out, this.oper);
IOUtil.writeNullable(out, this.subconditions);
}
public String toString() {
StringBuilder str = new StringBuilder();
str.append("[").append(this.getClass().getSimpleName()).append("=>");
for (Expression e : this.subconditions) {
str.append("\n\t").append(e.toString());
}
str.append("\n]");
return str.toString();
}
}
}