ExpressionIn.java
package org.microspace.table.query.sql;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.microspace.annotation.IndexType;
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 ExpressionIn<T> implements Expression<T> {
private static final long serialVersionUID = 116424891769864966L;
final Class<T> tableClass;
final Class<?> columnClass;
final String columnName;
final String columnEsc;
final boolean notIn;
List<String> columnValuesString;
List<Object> columnValues;
LiteralValueType columnType;
Select<T> innerSelect;
String innerColumnName;
boolean needsBracket = false;
public void setNeedsBracket (boolean needsBracket) {
this.needsBracket = needsBracket;
}
public ExpressionIn(Class<T> tableClass, String columnName, String columnEsc) {
this(tableClass, columnName, false, columnEsc);
}
public ExpressionIn(Class<T> tableClass, String columnName, boolean notIn, String columnEsc) {
this.tableClass = tableClass;
this.columnName = columnName;
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.notIn = notIn;
}
public void setLiteralColumnValues(List<String> columnValuesString, LiteralValueType columnType) {
this.columnType = columnType;
this.columnValues = new LinkedList<>();
this.columnValuesString = new LinkedList<>();
this.columnValuesString.addAll(columnValuesString);
for (String value : columnValuesString) {
Object columnObject = Util.createColumnObject(value, columnClass, columnType);
columnValues.add(columnObject);
}
}
public void setInnerSelect(Select<T> innerSelect) {
this.innerSelect = innerSelect;
this.innerColumnName = innerSelect.getColumnNames().get(0);
}
public IndexedMap<Object, Entry<T>> apply(Accessor<T> accessor, IndexedMap<Object, Entry<T>> entries,
ColumnReferences<T>[] indexedColumns, InnerSelectContext<?> innerSelectContext) {
if (innerSelect != null) {
if (innerSelectContext == null) {
throw new IllegalArgumentException ("Inner Select Without a Space");
}
// Local inner select.
if (innerSelectContext.getSpace() == null) {
throw new IllegalArgumentException ("No Space Found for " + tableClass.getName());
}
IndexedSet<Object> res = innerSelectContext.get(innerSelect.getId());
if (res == null) {
throw new IllegalArgumentException ("Internal InnerSelect Stack Initialization Error");
}
return applyInternal(accessor, entries, indexedColumns, res.getUnderlyingSet());
} else {
return applyInternal(accessor, entries, indexedColumns, columnValues);
}
}
private IndexedMap<Object, Entry<T>> applyInternal(Accessor<T> accessor, IndexedMap<Object, Entry<T>> entries,
ColumnReferences<T>[] indexedColumns, Collection<Object> values) {
IndexedSet<Object> retKeys = new IndexedSet<Object>(accessor.getPrimaryKeyGetSetPair().getIndexType());
GetSetPair<T> pair = accessor.getGetSetPair(columnName);
ColumnReferences<T> inf = indexedColumns[pair.getIndex()];
if (notIn) {
for (Object key : entries.keySet()) {
retKeys.add(key);
}
}
if (inf != null) {
for (Object value : values) {
IndexedSet<Object> matching = inf.getMatchingKeys(value);
if (notIn) {
retKeys.removeAll(matching);
} else {
retKeys.addAll(matching);
}
}
} else {
for (Entry<T> entry : entries.values()) {
boolean contains = listContainsObject(pair, entry.getSpaceEntry(), values);
if (contains) {
if (notIn) {
retKeys.remove(entry.getPrimaryKey());
} else {
retKeys.add(entry.getPrimaryKey());
}
}
}
}
IndexedMap<Object, Entry<T>> retMap = new IndexedMap<Object, Entry<T>>(
accessor.getPrimaryKeyGetSetPair().getIndexType());
for (Object key : retKeys) {
// Index may contain more values.
Entry<T> entry = entries.get(key);
if (entry == null) {
continue;
}
retMap.put(key, entry);
}
return retMap;
}
/**
* Check if object matches. Inner SELECT is not supported.
*
* @param object
* is the Class Object.
* @param accessor
* is the accessor of the object.
*/
public boolean match(T object, Accessor<T> accessor) {
// Inner select IN not supported.
if (innerSelect != null) {
return false;
}
GetSetPair<T> pair = accessor.getGetSetPair(columnName);
boolean cont = listContainsObject(pair, object, columnValues);
return cont ^ notIn;
}
@SuppressWarnings("unchecked")
private boolean listContainsObject(GetSetPair<T> pair, T object, Collection<Object> values) {
Object value = pair.get(object);
if (value == null)
return false;
for (Object literalValue : values) {
if (pair.getIndexType() == IndexType.HASHED || !(literalValue instanceof Comparable)) {
if (value.equals(literalValue)) {
return true;
}
} else {
@SuppressWarnings("rawtypes")
Comparable c0 = (Comparable) value;
@SuppressWarnings("rawtypes")
Comparable c1 = (Comparable) literalValue;
if (c0.compareTo(c1) == 0) {
return true;
}
}
}
return false;
}
@Override
public String formatSqlQuery(SqlFormat format) {
StringBuffer ret = new StringBuffer();
String esc = columnEsc;
if (format == SqlFormat.GIGASPACES) {
esc = "`";
}
ret.append(esc);
ret.append(columnName);
ret.append(esc);
ret.append(" ");
if (notIn) {
ret.append("NOT ");
}
ret.append("IN ");
if (innerSelect != null) {
ret.append("( " + innerSelect.formatSqlQuery(format) + " )");
return ret.toString();
}
ret.append("( ");
String comma = "";
for (String s : columnValuesString) {
ret.append(comma);
if (columnType == LiteralValueType.STRING) {
ret.append(Util.quote(s));
} else {
ret.append(s);
}
comma = ", ";
}
ret.append(" )");
return ret.toString();
}
}