package org.deephacks.confit.internal.mapdb.query;
import org.deephacks.confit.admin.query.BeanQuery;
import org.deephacks.confit.admin.query.BeanQueryBuilder;
import org.deephacks.confit.admin.query.BeanQueryBuilder.BeanRestriction;
import org.deephacks.confit.admin.query.BeanQueryBuilder.Between;
import org.deephacks.confit.admin.query.BeanQueryBuilder.Equals;
import org.deephacks.confit.admin.query.BeanQueryBuilder.GreaterThan;
import org.deephacks.confit.admin.query.BeanQueryBuilder.Has;
import org.deephacks.confit.admin.query.BeanQueryBuilder.In;
import org.deephacks.confit.admin.query.BeanQueryBuilder.LessThan;
import org.deephacks.confit.admin.query.BeanQueryBuilder.Not;
import org.deephacks.confit.admin.query.BeanQueryBuilder.StringContains;
import org.deephacks.confit.admin.query.BeanQueryResult;
import org.deephacks.confit.internal.mapdb.query.RestrictionBuilder.DefaultBetween;
import org.deephacks.confit.internal.mapdb.query.RestrictionBuilder.DefaultEquals;
import org.deephacks.confit.internal.mapdb.query.RestrictionBuilder.DefaultGreaterThan;
import org.deephacks.confit.internal.mapdb.query.RestrictionBuilder.DefaultLessThan;
import org.deephacks.confit.internal.mapdb.query.RestrictionBuilder.DefaultStringContains;
import org.deephacks.confit.internal.mapdb.query.RestrictionBuilder.PropertyRestriction;
import org.deephacks.confit.model.Bean;
import org.deephacks.confit.model.BeanId;
import org.deephacks.confit.model.Schema;
import org.deephacks.confit.serialization.UniqueIds;
import org.deephacks.confit.serialization.ValueSerialization.ValueReader;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import static org.deephacks.confit.admin.query.BeanQueryBuilder.equal;
public class DefaultBeanQuery implements BeanQuery {
private final LinkedHashMap<BeanId, byte[]> beans;
private final Schema schema;
private final UniqueIds uniqueIds = UniqueIds.lookup();
private int maxResults = Integer.MAX_VALUE;
private int firstResult;
private ArrayList<PropertyRestriction> restrictions = new ArrayList<>();
public DefaultBeanQuery(Schema schema, LinkedHashMap<BeanId, byte[]> beans) {
this.beans = beans;
this.schema = schema;
}
@Override
public BeanQuery add(BeanRestriction restriction) {
if(restriction instanceof BeanQueryBuilder.PropertyRestriction) {
BeanQueryBuilder.PropertyRestriction propertyRestriction = ((BeanQueryBuilder.PropertyRestriction) restriction);
String property = propertyRestriction.getProperty();
if (restriction instanceof Equals) {
Equals specific = (Equals) restriction;
DefaultEquals defaultRestriction = new DefaultEquals(property, specific.getValue());
if(propertyRestriction.isNot()) {
defaultRestriction.setNot();
}
restrictions.add(defaultRestriction);
return this;
} else if (restriction instanceof StringContains) {
StringContains specific = (StringContains) restriction;
DefaultStringContains defaultRestriction = new DefaultStringContains(property, specific.getValue());
if(propertyRestriction.isNot()) {
defaultRestriction.setNot();
}
restrictions.add(defaultRestriction);
return this;
} else if (restriction instanceof Between) {
Between specific = (Between) restriction;
DefaultBetween defaultRestriction = new DefaultBetween(property, specific.getLower(), specific.getUpper());
restrictions.add(defaultRestriction);
return this;
} else if (restriction instanceof GreaterThan) {
GreaterThan specific = (GreaterThan) restriction;
DefaultGreaterThan defaultRestriction = new DefaultGreaterThan(property, specific.getValue());
restrictions.add(defaultRestriction);
return this;
} else if (restriction instanceof LessThan) {
LessThan specific = (LessThan) restriction;
DefaultLessThan defaultRestriction = new DefaultLessThan(property, specific.getValue());
restrictions.add(defaultRestriction);
return this;
} else if (restriction instanceof Has) {
throw new UnsupportedOperationException("'Has' not implemented yet");
} else if (restriction instanceof In) {
In in = ((In) restriction);
List<Object> values = in.getValues();
for (Object value : values) {
BeanQueryBuilder.PropertyRestriction equal = (BeanQueryBuilder.PropertyRestriction) equal(property, value);
if(in.isNot()) {
equal.setNot();
}
add(equal);
}
return this;
} else {
throw new IllegalArgumentException("Could not identify restriction: " + restriction);
}
} else if(restriction instanceof BeanQueryBuilder.LogicalRestriction) {
if (restriction instanceof Not) {
// support only one logical NOT statement at the moment
BeanQueryBuilder.PropertyRestriction not = (BeanQueryBuilder.PropertyRestriction) ((Not) restriction).getRestrictions().get(0);
not.setNot();
add(not);
return this;
}
throw new UnsupportedOperationException("logical restriction not supported " + restriction);
}
throw new UnsupportedOperationException("Could not identify restriction: " + restriction);
}
@Override
public BeanQuery setFirstResult(String firstResult) {
try {
this.firstResult = Integer.parseInt(firstResult);
} catch (Exception e) {
throw new IllegalArgumentException("Could not parse firstResult into an integer.");
}
return this;
}
@Override
public BeanQuery setMaxResults(int maxResults) {
this.maxResults = maxResults;
return this;
}
@Override
public BeanQueryResult retrieve() {
final ArrayList<Bean> result = new ArrayList<>();
int iterations = 0;
for (BeanId id : beans.keySet()) {
if (iterations >= firstResult && result.size() < maxResults) {
id.set(schema);
byte[] current = beans.get(id);
if (matches(current)) {
Bean match = Bean.read(id, current);
result.add(match);
}
}
if (result.size() > maxResults) {
break;
}
iterations++;
}
final String nextFirstResult = Integer.toString(firstResult + result.size());
return new BeanQueryResult() {
@Override
public List<Bean> get() {
return result;
}
@Override
public String nextFirstResult() {
return nextFirstResult;
}
};
}
private boolean matches(byte[] current) {
for (PropertyRestriction restriction : restrictions) {
String propertyName = restriction.getPropertyName();
int id = uniqueIds.getSchemaId(propertyName);
ValueReader reader = new ValueReader(current);
if (schema.isProperty(propertyName)) {
if (!restriction.evaluate(reader.getValue(id))) {
return false;
}
} else if (schema.isReference(propertyName)) {
if (!restriction.evaluate(reader.getValue(id))) {
return false;
}
} else {
throw new IllegalArgumentException("Property not recognized " + propertyName);
}
}
return true;
}
}