ExpressionBoolean.java
package org.microspace.table.query.sql;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.microspace.table.Entry;
import org.microspace.table.column.Accessor;
import org.microspace.table.column.ColumnReferences;
import org.microspace.table.column.GetSetPair;
import org.microspace.table.column.IndexedMap;
import org.microspace.table.column.IndexedSet;
import org.microspace.table.query.SqlFormat;
import org.microspace.util.PojoUtil;
/**
*
* @author Gaspar Sinai - {@literal gaspar.sinai@microspace.org}
* @version 2017-09-29
* @param <T> The type of the Table.
*/
public class ExpressionBoolean<T> implements Expression<T> {
private static final long serialVersionUID = 1L;
final Class<T> tableClass;
final Class<?> columnClass;
final String columnName;
final OperatorType operator;
final LiteralValueType type;
final String value;
final Object columnObject;
final String columnEsc;
boolean needsBracket = false;
public void setNeedsBracket (boolean needsBracket) {
this.needsBracket = needsBracket;
}
public ExpressionBoolean(Class<T> tableClass,
OperatorType operator, String columnName,
LiteralValueType type, String value, String columnEsc) {
this.tableClass = tableClass;
this.columnEsc = columnEsc;
Accessor<T> accessor = PojoUtil.getUnannotateAccessor(tableClass);
GetSetPair<T> gettter = accessor.getGetSetPair(columnName);
if (gettter == null) {
throw new IllegalArgumentException("Unkown Column: " + columnName);
}
this.columnClass = PojoUtil.getColumnClass(tableClass, columnName);
this.operator = operator;
this.columnName = columnName;
this.value = value;
this.type = type;
if (columnClass == null) {
throw new IllegalArgumentException("Unkown Column");
}
if (operator != OperatorType.IS_NULL && operator != OperatorType.IS_NOT_NULL) {
columnObject = Util.createColumnObject(value, columnClass, type);
} else {
columnObject = value;
}
}
public IndexedMap<Object, Entry<T>> apply(Accessor<T> accessor,
IndexedMap<Object, Entry<T>> entries,
ColumnReferences<T>[] indexedColumns, InnerSelectContext<?> innerSelectContext) {
IndexedMap<Object, Entry<T>> retMap = new IndexedMap<Object, Entry<T>>(
accessor.getPrimaryKeyGetSetPair().getIndexType());
GetSetPair<T> getSetPair = accessor.getGetSetPair(columnName);
int columnNumber = getSetPair.getIndex();
ColumnReferences<T> index = indexedColumns[columnNumber];
switch (operator) {
case EQ:
if (index == null) {
for (Entry<T> entry : entries.values()) {
Object field = entry.getField(columnNumber);
if (field == null)
continue;
if (equals(field, columnObject)) {
retMap.put(entry.getPrimaryKey(), entry);
}
}
} else {
IndexedSet<Object> matchingSet = new IndexedSet<Object>(
accessor.getPrimaryKeyGetSetPair().getIndexType());
IndexedSet<Object> workingSet = entries.keySet();
IndexedSet<Object> indexSet = index.getMatchingKeys(columnObject);
intersection(workingSet, indexSet, matchingSet);
for (Object key : matchingSet) {
Entry<T> entry = entries.get(key);
// Indexes are for the whole input.
if (entry != null) {
retMap.put(entry.getPrimaryKey(), entry);
}
}
}
break;
case NOT_EQ:
if (index == null) {
for (Entry<T> entry : entries.values()) {
Object field = entry.getField(columnNumber);
if (field == null || !equals(field, columnObject)) {
retMap.put(entry.getPrimaryKey(), entry);
}
}
} else {
IndexedSet<Object> notPresentSet = new IndexedSet<Object>(
accessor.getPrimaryKeyGetSetPair().getIndexType());
IndexedSet<Object> workingSet = entries.keySet();
IndexedSet<Object> indexSet = index.getMatchingKeys(columnObject);
remove(workingSet, indexSet, notPresentSet);
for (Object key : notPresentSet) {
Entry<T> entry = entries.get(key);
retMap.put(entry.getPrimaryKey(), entry);
}
}
break;
case GTH:
for (Entry<T> entry : entries.values()) {
Object field = entry.getField(columnNumber);
if (field == null)
continue;
if (compare(field, columnObject) > 0) {
retMap.put(entry.getPrimaryKey(), entry);
}
}
break;
case GET:
for (Entry<T> entry : entries.values()) {
Object field = entry.getField(columnNumber);
if (field == null)
continue;
if (compare(field, columnObject) >= 0) {
retMap.put(entry.getPrimaryKey(), entry);
}
}
break;
case LET:
for (Entry<T> entry : entries.values()) {
Object field = entry.getField(columnNumber);
if (field == null)
continue;
if (compare(field, columnObject) <= 0) {
retMap.put(entry.getPrimaryKey(), entry);
}
}
break;
case LTH:
for (Entry<T> entry : entries.values()) {
Object field = entry.getField(columnNumber);
if (field == null)
continue;
if (compare(field, columnObject) < 0) {
retMap.put(entry.getPrimaryKey(), entry);
}
}
break;
case IS_NULL:
for (Entry<T> entry : entries.values()) {
Object field = entry.getField(columnNumber);
if (field != null)
continue;
retMap.put(entry.getPrimaryKey(), entry);
}
break;
case IS_NOT_NULL:
for (Entry<T> entry : entries.values()) {
Object field = entry.getField(columnNumber);
if (field == null)
continue;
retMap.put(entry.getPrimaryKey(), entry);
}
break;
case LIKE:
case NOT_LIKE:
{
String expr = value;
expr = expr.toUpperCase();
expr = expr.replace(".", "\\.");
// TODO Add more.
expr = expr.replace("?", ".");
expr = expr.replace("%", ".*");
Pattern pattern = Pattern.compile(expr);
for (Entry<T> entry : entries.values()) {
Object field = entry.getField(columnNumber);
if (field == null)
continue;
String str = field.toString().toUpperCase();
Matcher m = pattern.matcher(str);
if (m.matches() == (operator == OperatorType.LIKE)) {
retMap.put(entry.getPrimaryKey(), entry);
}
}
}
break;
default:
break;
}
return retMap;
}
public boolean match(T object, Accessor<T> accessor) {
GetSetPair<T> getSetPair = accessor.getGetSetPair(columnName);
Object field = getSetPair.get(object);
if (field == null)
return false;
return equals(field, columnObject);
}
/**
* Parse % string as like.
* <p>
* %endswidth
* <p>
* startswith%
* <p>
* start%end
* <p>
* percent%%
*
* @param expr
* The pattern.
* @param str
* The input.
* @return true if pattern matches.
*/
public static boolean like(String expr, String str) {
expr = expr.toLowerCase();
expr = expr.replace(".", "\\.");
// ... escape any other potentially problematic characters here
expr = expr.replace("?", ".");
expr = expr.replace("%", ".*");
str = str.toLowerCase();
return str.matches(expr);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public static boolean equals(Object field, Object value) {
if (value instanceof Comparable) {
Comparable cmp0 = (Comparable) field;
Comparable cmp1 = (Comparable) value;
return cmp0.compareTo(cmp1) == 0;
}
return field.equals(value);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public static int compare(Object field, Object value) {
if (value instanceof Comparable) {
Comparable cmp0 = (Comparable) field;
Comparable cmp1 = (Comparable) value;
return cmp0.compareTo(cmp1);
}
System.err.println("Comparing " + value);
return (field.toString().compareTo(value.toString()));
}
private void remove(IndexedSet<Object> from, IndexedSet<Object> present, IndexedSet<Object> target) {
for (Object key : from) {
if (!present.contains(key)) {
target.add(key);
}
}
}
/**
* Get the intersection of two sets.
*
* @param set0
* One set.
* @param set1
* The other set.
* @param target
* The intersection is placed here.
*/
private void intersection(IndexedSet<Object> set0, IndexedSet<Object> set1, IndexedSet<Object> target) {
if (set0.size() > set1.size()) {
intersection(set1, set0, target);
return;
}
for (Object key : set0) {
if (set1.contains(key)) {
target.add(key);
}
}
}
@Override
public String formatSqlQuery (SqlFormat format) {
String esc = columnEsc;
if (format == SqlFormat.GIGASPACES) {
esc = "`";
}
switch (type) {
case BOOLEAN:
case NUMBER:
return esc + columnName + esc + " " + operator.constructSqlString(format) + " " + value;
case NULL:
return esc + columnName + esc + " " + operator.constructSqlString(format);
case STRING:
return esc + columnName + esc + " " + operator.constructSqlString(format) + " " + Util.quote(value);
}
return "";
}
}