Files
redkale/src/org/redkale/source/EntityCache.java
Redkale 90ab302667
2017-01-18 22:55:29 +08:00

730 lines
33 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.source;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.logging.*;
import java.util.stream.*;
import javax.persistence.*;
import static org.redkale.source.FilterFunc.*;
import org.redkale.util.*;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <T> Entity类的泛型
*/
@SuppressWarnings("unchecked")
public final class EntityCache<T> {
private static final Logger logger = Logger.getLogger(EntityCache.class.getName());
private final ConcurrentHashMap<Serializable, T> map = new ConcurrentHashMap();
// CopyOnWriteArrayList 插入慢、查询快; 10w数据插入需要3.2秒; ConcurrentLinkedQueue 插入快、查询慢10w数据查询需要 0.062秒, 查询慢40%;
private final Collection<T> list = new ConcurrentLinkedQueue();
private final Map<String, Comparator<T>> sortComparators = new ConcurrentHashMap<>();
private final Class<T> type;
private final boolean needcopy;
private final Creator<T> creator;
private final Attribute<T, Serializable> primary;
private final Reproduce<T, T> newReproduce;
private final Reproduce<T, T> chgReproduce;
private volatile boolean fullloaded;
final EntityInfo<T> info;
public EntityCache(final EntityInfo<T> info) {
this.info = info;
this.type = info.getType();
this.creator = info.getCreator();
this.primary = info.primary;
VirtualEntity ve = info.getType().getAnnotation(VirtualEntity.class);
this.needcopy = ve == null || !ve.direct();
this.newReproduce = Reproduce.create(type, type, (m) -> {
try {
return type.getDeclaredField(m).getAnnotation(Transient.class) == null;
} catch (Exception e) {
return true;
}
});
this.chgReproduce = Reproduce.create(type, type, (m) -> {
try {
java.lang.reflect.Field field = type.getDeclaredField(m);
if (field.getAnnotation(Transient.class) != null) return false;
Column column = field.getAnnotation(Column.class);
return (column == null || column.updatable());
} catch (Exception e) {
return true;
}
});
}
public void fullLoad() {
if (info.fullloader == null) return;
clear();
List<T> all = info.fullloader.apply(info.source, type);
if (all != null) {
all.stream().filter(x -> x != null).forEach(x -> {
this.map.put(this.primary.get(x), x);
});
this.list.addAll(all);
}
this.fullloaded = true;
}
public Class<T> getType() {
return type;
}
public void clear() {
this.fullloaded = false;
this.list.clear();
this.map.clear();
}
public boolean isFullLoaded() {
return fullloaded;
}
public T find(Serializable id) {
if (id == null) return null;
T rs = map.get(id);
return rs == null ? null : (needcopy ? newReproduce.apply(this.creator.create(), rs) : rs);
}
public T find(final SelectColumn selects, final Serializable id) {
if (id == null) return null;
T rs = map.get(id);
if (rs == null) return null;
if (selects == null) return (needcopy ? newReproduce.apply(this.creator.create(), rs) : rs);
T t = this.creator.create();
for (Attribute attr : this.info.attributes) {
if (selects.test(attr.field())) attr.set(t, attr.get(rs));
}
return t;
}
public T find(final SelectColumn selects, FilterNode node) {
final Predicate<T> filter = node == null ? null : node.createPredicate(this);
Stream<T> stream = this.list.stream();
if (filter != null) stream = stream.filter(filter);
Optional<T> opt = stream.findFirst();
if (!opt.isPresent()) return null;
if (selects == null) return (needcopy ? newReproduce.apply(this.creator.create(), opt.get()) : opt.get());
T rs = opt.get();
T t = this.creator.create();
for (Attribute attr : this.info.attributes) {
if (selects.test(attr.field())) attr.set(t, attr.get(rs));
}
return t;
}
public Serializable findColumn(final String column, final Serializable defValue, final Serializable id) {
if (id == null) return defValue;
T rs = map.get(id);
if (rs == null) return defValue;
for (Attribute attr : this.info.attributes) {
if (column.equals(attr.field())) {
Serializable val = (Serializable) attr.get(rs);
return val == null ? defValue : val;
}
}
return defValue;
}
public Serializable findColumn(final String column, final Serializable defValue, FilterNode node) {
final Predicate<T> filter = node == null ? null : node.createPredicate(this);
Stream<T> stream = this.list.stream();
if (filter != null) stream = stream.filter(filter);
Optional<T> opt = stream.findFirst();
if (!opt.isPresent()) return defValue;
T rs = opt.get();
for (Attribute attr : this.info.attributes) {
if (column.equals(attr.field())) {
Serializable val = (Serializable) attr.get(rs);
return val == null ? defValue : val;
}
}
return defValue;
}
public boolean exists(Serializable id) {
if (id == null) return false;
final Class atype = this.primary.type();
if (id.getClass() != atype && id instanceof Number) {
if (atype == int.class || atype == Integer.class) {
id = ((Number) id).intValue();
} else if (atype == long.class || atype == Long.class) {
id = ((Number) id).longValue();
} else if (atype == short.class || atype == Short.class) {
id = ((Number) id).shortValue();
} else if (atype == float.class || atype == Float.class) {
id = ((Number) id).floatValue();
} else if (atype == byte.class || atype == Byte.class) {
id = ((Number) id).byteValue();
} else if (atype == double.class || atype == Double.class) {
id = ((Number) id).doubleValue();
}
}
return this.map.containsKey(id);
}
public boolean exists(FilterNode node) {
final Predicate<T> filter = node == null ? null : node.createPredicate(this);
Stream<T> stream = this.list.stream();
if (filter != null) stream = stream.filter(filter);
return stream.findFirst().isPresent();
}
public boolean exists(final Predicate<T> filter) {
return (filter != null) && this.list.stream().filter(filter).findFirst().isPresent();
}
public <K, V> Map<Serializable, Number> queryColumnMap(final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) {
final Attribute<T, Serializable> keyAttr = info.getAttribute(keyColumn);
final Predicate filter = node == null ? null : node.createPredicate(this);
final Attribute funcAttr = funcColumn == null ? null : info.getAttribute(funcColumn);
Stream<T> stream = this.list.stream();
if (filter != null) stream = stream.filter(filter);
Collector<T, Map, ?> collector = null;
final Class valtype = funcAttr == null ? null : funcAttr.type();
switch (func) {
case AVG:
if (valtype == float.class || valtype == Float.class || valtype == double.class || valtype == Double.class) {
collector = (Collector<T, Map, ?>) Collectors.averagingDouble((T t) -> ((Number) funcAttr.get(t)).doubleValue());
} else {
collector = (Collector<T, Map, ?>) Collectors.averagingLong((T t) -> ((Number) funcAttr.get(t)).longValue());
}
break;
case COUNT:
collector = (Collector<T, Map, ?>) Collectors.counting();
break;
case DISTINCTCOUNT:
collector = (Collector<T, Map, ?>) Collectors.mapping((t) -> funcAttr.get(t), Collectors.toSet());
break;
case MAX:
case MIN:
Comparator<T> comp = (o1, o2) -> o1 == null ? (o2 == null ? 0 : -1) : ((Comparable) funcAttr.get(o1)).compareTo(funcAttr.get(o2));
collector = (Collector<T, Map, ?>) ((func == MAX) ? Collectors.maxBy(comp) : Collectors.minBy(comp));
break;
case SUM:
if (valtype == float.class || valtype == Float.class || valtype == double.class || valtype == Double.class) {
collector = (Collector<T, Map, ?>) Collectors.summingDouble((T t) -> ((Number) funcAttr.get(t)).doubleValue());
} else {
collector = (Collector<T, Map, ?>) Collectors.summingLong((T t) -> ((Number) funcAttr.get(t)).longValue());
}
break;
}
Map rs = stream.collect(Collectors.groupingBy(t -> keyAttr.get(t), LinkedHashMap::new, collector));
if (func == MAX || func == MIN) {
Map rs2 = new LinkedHashMap();
rs.forEach((x, y) -> {
if (((Optional) y).isPresent()) rs2.put(x, funcAttr.get((T) ((Optional) y).get()));
});
rs = rs2;
} else if (func == DISTINCTCOUNT) {
Map rs2 = new LinkedHashMap();
rs.forEach((x, y) -> rs2.put(x, ((Set) y).size()));
rs = rs2;
}
return rs;
}
public <V> Number getNumberResult(final FilterFunc func, final Number defResult, final String column, final FilterNode node) {
final Attribute<T, Serializable> attr = column == null ? null : info.getAttribute(column);
final Predicate<T> filter = node == null ? null : node.createPredicate(this);
Stream<T> stream = this.list.stream();
if (filter != null) stream = stream.filter(filter);
switch (func) {
case AVG:
if (attr.type() == int.class || attr.type() == Integer.class) {
OptionalDouble rs = stream.mapToInt(x -> (Integer) attr.get(x)).average();
return rs.isPresent() ? (int) rs.getAsDouble() : defResult;
} else if (attr.type() == long.class || attr.type() == Long.class) {
OptionalDouble rs = stream.mapToLong(x -> (Long) attr.get(x)).average();
return rs.isPresent() ? (long) rs.getAsDouble() : defResult;
} else if (attr.type() == short.class || attr.type() == Short.class) {
OptionalDouble rs = stream.mapToInt(x -> ((Short) attr.get(x)).intValue()).average();
return rs.isPresent() ? (short) rs.getAsDouble() : defResult;
} else if (attr.type() == float.class || attr.type() == Float.class) {
OptionalDouble rs = stream.mapToDouble(x -> ((Float) attr.get(x)).doubleValue()).average();
return rs.isPresent() ? (float) rs.getAsDouble() : defResult;
} else if (attr.type() == double.class || attr.type() == Double.class) {
OptionalDouble rs = stream.mapToDouble(x -> (Double) attr.get(x)).average();
return rs.isPresent() ? rs.getAsDouble() : defResult;
}
throw new RuntimeException("getNumberResult error(type:" + type + ", attr.declaringClass: " + attr.declaringClass() + ", attr.field: " + attr.field() + ", attr.type: " + attr.type());
case COUNT:
return stream.count();
case DISTINCTCOUNT:
return stream.map(x -> attr.get(x)).distinct().count();
case MAX:
if (attr.type() == int.class || attr.type() == Integer.class) {
OptionalInt rs = stream.mapToInt(x -> (Integer) attr.get(x)).max();
return rs.isPresent() ? rs.getAsInt() : defResult;
} else if (attr.type() == long.class || attr.type() == Long.class) {
OptionalLong rs = stream.mapToLong(x -> (Long) attr.get(x)).max();
return rs.isPresent() ? rs.getAsLong() : defResult;
} else if (attr.type() == short.class || attr.type() == Short.class) {
OptionalInt rs = stream.mapToInt(x -> ((Short) attr.get(x)).intValue()).max();
return rs.isPresent() ? (short) rs.getAsInt() : defResult;
} else if (attr.type() == float.class || attr.type() == Float.class) {
OptionalDouble rs = stream.mapToDouble(x -> ((Float) attr.get(x)).doubleValue()).max();
return rs.isPresent() ? (float) rs.getAsDouble() : defResult;
} else if (attr.type() == double.class || attr.type() == Double.class) {
OptionalDouble rs = stream.mapToDouble(x -> (Double) attr.get(x)).max();
return rs.isPresent() ? rs.getAsDouble() : defResult;
}
throw new RuntimeException("getNumberResult error(type:" + type + ", attr.declaringClass: " + attr.declaringClass() + ", attr.field: " + attr.field() + ", attr.type: " + attr.type());
case MIN:
if (attr.type() == int.class || attr.type() == Integer.class) {
OptionalInt rs = stream.mapToInt(x -> (Integer) attr.get(x)).min();
return rs.isPresent() ? rs.getAsInt() : defResult;
} else if (attr.type() == long.class || attr.type() == Long.class) {
OptionalLong rs = stream.mapToLong(x -> (Long) attr.get(x)).min();
return rs.isPresent() ? rs.getAsLong() : defResult;
} else if (attr.type() == short.class || attr.type() == Short.class) {
OptionalInt rs = stream.mapToInt(x -> ((Short) attr.get(x)).intValue()).min();
return rs.isPresent() ? (short) rs.getAsInt() : defResult;
} else if (attr.type() == float.class || attr.type() == Float.class) {
OptionalDouble rs = stream.mapToDouble(x -> ((Float) attr.get(x)).doubleValue()).min();
return rs.isPresent() ? (float) rs.getAsDouble() : defResult;
} else if (attr.type() == double.class || attr.type() == Double.class) {
OptionalDouble rs = stream.mapToDouble(x -> (Double) attr.get(x)).min();
return rs.isPresent() ? rs.getAsDouble() : defResult;
}
throw new RuntimeException("getNumberResult error(type:" + type + ", attr.declaringClass: " + attr.declaringClass() + ", attr.field: " + attr.field() + ", attr.type: " + attr.type());
case SUM:
if (attr.type() == int.class || attr.type() == Integer.class) {
return stream.mapToInt(x -> (Integer) attr.get(x)).sum();
} else if (attr.type() == long.class || attr.type() == Long.class) {
return stream.mapToLong(x -> (Long) attr.get(x)).sum();
} else if (attr.type() == short.class || attr.type() == Short.class) {
return (short) stream.mapToInt(x -> ((Short) attr.get(x)).intValue()).sum();
} else if (attr.type() == float.class || attr.type() == Float.class) {
return (float) stream.mapToDouble(x -> ((Float) attr.get(x)).doubleValue()).sum();
} else if (attr.type() == double.class || attr.type() == Double.class) {
return stream.mapToDouble(x -> (Double) attr.get(x)).sum();
}
throw new RuntimeException("getNumberResult error(type:" + type + ", attr.declaringClass: " + attr.declaringClass() + ", attr.field: " + attr.field() + ", attr.type: " + attr.type());
}
return defResult;
}
public Sheet<T> querySheet(final SelectColumn selects, final Flipper flipper, final FilterNode node) {
return querySheet(true, selects, flipper, node);
}
public Sheet<T> querySheet(final boolean needtotal, final SelectColumn selects, final Flipper flipper, FilterNode node) {
final Predicate<T> filter = node == null ? null : node.createPredicate(this);
final Comparator<T> comparator = createComparator(flipper);
long total = 0;
if (needtotal) {
Stream<T> stream = this.list.stream();
if (filter != null) stream = stream.filter(filter);
total = stream.count();
}
if (needtotal && total == 0) return new Sheet<>();
Stream<T> stream = this.list.stream();
if (filter != null) stream = stream.filter(filter);
if (comparator != null) stream = stream.sorted(comparator);
if (flipper != null && flipper.getOffset() > 0) stream = stream.skip(flipper.getOffset());
if (flipper != null && flipper.getLimit() > 0) stream = stream.limit(flipper.getLimit());
final List<T> rs = new ArrayList<>();
if (selects == null) {
Consumer<? super T> action = x -> rs.add(needcopy ? newReproduce.apply(creator.create(), x) : x);
if (comparator != null) {
stream.forEachOrdered(action);
} else {
stream.forEach(action);
}
} else {
final List<Attribute<T, Serializable>> attrs = new ArrayList<>();
info.forEachAttribute((k, v) -> {
if (selects.test(k)) attrs.add(v);
});
Consumer<? super T> action = x -> {
final T item = creator.create();
for (Attribute attr : attrs) {
attr.set(item, attr.get(x));
}
rs.add(item);
};
if (comparator != null) {
stream.forEachOrdered(action);
} else {
stream.forEach(action);
}
}
if (!needtotal) total = rs.size();
return new Sheet<>(total, rs);
}
public void insert(T value) {
if (value == null) return;
final T rs = newReproduce.apply(this.creator.create(), value); //确保同一主键值的map与list中的对象必须共用。
T old = this.map.put(this.primary.get(rs), rs);
if (old == null) {
this.list.add(rs);
} else {
logger.log(Level.WARNING, this.type + " cache repeat insert data: " + value);
}
}
public int delete(final Serializable id) {
if (id == null) return 0;
final T rs = this.map.remove(id);
if (rs == null) return 0;
this.list.remove(rs);
return 1;
}
public Serializable[] delete(final Flipper flipper, final FilterNode node) {
if (node == null || this.list.isEmpty()) return new Serializable[0];
final Comparator<T> comparator = createComparator(flipper);
Stream<T> stream = this.list.stream().filter(node.createPredicate(this));
if (comparator != null) stream = stream.sorted(comparator);
if (flipper != null && flipper.getOffset() > 0) stream = stream.skip(flipper.getOffset());
if (flipper != null && flipper.getLimit() > 0) stream = stream.limit(flipper.getLimit());
Object[] rms = stream.toArray();
Serializable[] ids = new Serializable[rms.length];
int i = -1;
for (Object o : rms) {
final T t = (T) o;
ids[++i] = this.primary.get(t);
this.map.remove(ids[i]);
this.list.remove(t);
}
return ids;
}
public int update(final T value) {
if (value == null) return 0;
T rs = this.map.get(this.primary.get(value));
if (rs == null) return 0;
synchronized (rs) {
this.chgReproduce.apply(rs, value);
}
return 1;
}
public T update(final T value, Collection<Attribute<T, Serializable>> attrs) {
if (value == null) return value;
T rs = this.map.get(this.primary.get(value));
if (rs == null) return rs;
synchronized (rs) {
for (Attribute attr : attrs) {
attr.set(rs, attr.get(value));
}
}
return rs;
}
public T[] update(final T value, final Collection<Attribute<T, Serializable>> attrs, final FilterNode node) {
if (value == null || node == null) return (T[]) Array.newInstance(type, 0);
T[] rms = this.list.stream().filter(node.createPredicate(this)).toArray(len -> (T[]) Array.newInstance(type, len));
for (T rs : rms) {
synchronized (rs) {
for (Attribute attr : attrs) {
attr.set(rs, attr.get(value));
}
}
}
return rms;
}
public <V> T update(final Serializable id, Attribute<T, V> attr, final V fieldValue) {
if (id == null) return null;
T rs = this.map.get(id);
if (rs != null) attr.set(rs, fieldValue);
return rs;
}
public <V> T[] update(Attribute<T, V> attr, final V fieldValue, final FilterNode node) {
if (attr == null || node == null) return (T[]) Array.newInstance(type, 0);
T[] rms = this.list.stream().filter(node.createPredicate(this)).toArray(len -> (T[]) Array.newInstance(type, len));
for (T rs : rms) {
attr.set(rs, fieldValue);
}
return rms;
}
public <V> T updateColumn(final Serializable id, List<Attribute<T, Serializable>> attrs, final List<ColumnValue> values) {
if (id == null || attrs == null || attrs.isEmpty()) return null;
T rs = this.map.get(id);
if (rs == null) return rs;
synchronized (rs) {
for (int i = 0; i < attrs.size(); i++) {
ColumnValue cv = values.get(i);
updateColumn(attrs.get(i), rs, cv.getExpress(), cv.getValue());
}
}
return rs;
}
public <V> T[] updateColumn(final FilterNode node, final Flipper flipper, List<Attribute<T, Serializable>> attrs, final List<ColumnValue> values) {
if (attrs == null || attrs.isEmpty() || node == null) return (T[]) Array.newInstance(type, 0);
Stream<T> stream = this.list.stream();
final Comparator<T> comparator = createComparator(flipper);
if (comparator != null) stream = stream.sorted(comparator);
if (flipper != null && flipper.getLimit() > 0) stream = stream.limit(flipper.getLimit());
T[] rms = stream.filter(node.createPredicate(this)).toArray(len -> (T[]) Array.newInstance(type, len));
for (T rs : rms) {
synchronized (rs) {
for (int i = 0; i < attrs.size(); i++) {
ColumnValue cv = values.get(i);
updateColumn(attrs.get(i), rs, cv.getExpress(), cv.getValue());
}
}
}
return rms;
}
public <V> T updateColumnOr(final Serializable id, Attribute<T, V> attr, final long orvalue) {
if (id == null) return null;
T rs = this.map.get(id);
if (rs == null) return rs;
synchronized (rs) {
return updateColumn(attr, rs, ColumnExpress.ORR, orvalue);
}
}
public <V> T updateColumnAnd(final Serializable id, Attribute<T, V> attr, final long andvalue) {
if (id == null) return null;
T rs = this.map.get(id);
if (rs == null) return rs;
synchronized (rs) {
return updateColumn(attr, rs, ColumnExpress.AND, andvalue);
}
}
public <V> T updateColumnIncrement(final Serializable id, Attribute<T, V> attr, final long incvalue) {
if (id == null) return null;
T rs = this.map.get(id);
if (rs == null) return rs;
synchronized (rs) {
return updateColumn(attr, rs, ColumnExpress.INC, incvalue);
}
}
private <V> T updateColumn(Attribute<T, V> attr, final T rs, final ColumnExpress express, Serializable val) {
final Class ft = attr.type();
Number numb = null;
Serializable newval = null;
switch (express) {
case INC:
numb = (Number) attr.get(rs);
if (numb == null) {
numb = (Number) val;
} else {
numb = numb.longValue() + ((Number) val).longValue();
}
break;
case MUL:
numb = (Number) attr.get(rs);
if (numb == null) {
numb = 0;
} else {
numb = numb.longValue() * ((Number) val).floatValue();
}
break;
case AND:
numb = (Number) attr.get(rs);
if (numb == null) {
numb = 0;
} else {
numb = numb.longValue() & ((Number) val).longValue();
}
break;
case ORR:
numb = (Number) attr.get(rs);
if (numb == null) {
numb = 0;
} else {
numb = numb.longValue() | ((Number) val).longValue();
}
break;
case MOV:
newval = val;
break;
}
if (numb != null) {
if (ft == int.class || ft == Integer.class) {
newval = numb.intValue();
} else if (ft == long.class || ft == Long.class) {
newval = numb.longValue();
} else if (ft == short.class || ft == Short.class) {
newval = numb.shortValue();
} else if (ft == float.class || ft == Float.class) {
newval = numb.floatValue();
} else if (ft == double.class || ft == Double.class) {
newval = numb.doubleValue();
} else if (ft == byte.class || ft == Byte.class) {
newval = numb.byteValue();
}
}
attr.set(rs, (V) newval);
return rs;
}
public Attribute<T, Serializable> getAttribute(String fieldname) {
return info.getAttribute(fieldname);
}
//-------------------------------------------------------------------------------------------------------------------------------
protected Comparator<T> createComparator(Flipper flipper) {
if (flipper == null || flipper.getSort() == null || flipper.getSort().isEmpty() || flipper.getSort().indexOf(';') >= 0 || flipper.getSort().indexOf('\n') >= 0) return null;
final String sort = flipper.getSort();
Comparator<T> comparator = this.sortComparators.get(sort);
if (comparator != null) return comparator;
for (String item : sort.split(",")) {
if (item.trim().isEmpty()) continue;
String[] sub = item.trim().split("\\s+");
int pos = sub[0].indexOf('(');
Attribute<T, Serializable> attr;
if (pos <= 0) {
attr = getAttribute(sub[0]);
} else { //含SQL函数
int pos2 = sub[0].lastIndexOf(')');
final Attribute<T, Serializable> pattr = getAttribute(sub[0].substring(pos + 1, pos2));
final String func = sub[0].substring(0, pos);
if ("ABS".equalsIgnoreCase(func)) {
Function getter = null;
if (pattr.type() == int.class || pattr.type() == Integer.class) {
getter = x -> Math.abs(((Number) pattr.get((T) x)).intValue());
} else if (pattr.type() == long.class || pattr.type() == Long.class) {
getter = x -> Math.abs(((Number) pattr.get((T) x)).longValue());
} else if (pattr.type() == float.class || pattr.type() == Float.class) {
getter = x -> Math.abs(((Number) pattr.get((T) x)).floatValue());
} else if (pattr.type() == double.class || pattr.type() == Double.class) {
getter = x -> Math.abs(((Number) pattr.get((T) x)).doubleValue());
} else {
throw new RuntimeException("Flipper not supported sort illegal type by ABS (" + flipper.getSort() + ")");
}
attr = (Attribute<T, Serializable>) Attribute.create(pattr.declaringClass(), pattr.field(), pattr.type(), getter, (o, v) -> pattr.set(o, v));
} else if (func.isEmpty()) {
attr = pattr;
} else {
throw new RuntimeException("Flipper not supported sort illegal function (" + flipper.getSort() + ")");
}
}
Comparator<T> c = (sub.length > 1 && sub[1].equalsIgnoreCase("DESC")) ? (T o1, T o2) -> {
Comparable c1 = (Comparable) attr.get(o1);
Comparable c2 = (Comparable) attr.get(o2);
return c2 == null ? -1 : c2.compareTo(c1);
} : (T o1, T o2) -> {
Comparable c1 = (Comparable) attr.get(o1);
Comparable c2 = (Comparable) attr.get(o2);
return c1 == null ? -1 : c1.compareTo(c2);
};
if (comparator == null) {
comparator = c;
} else {
comparator = comparator.thenComparing(c);
}
}
this.sortComparators.put(sort, comparator);
return comparator;
}
private static class UniqueSequence implements Serializable {
private final Serializable[] value;
public UniqueSequence(Serializable[] val) {
this.value = val;
}
@Override
public int hashCode() {
return Arrays.deepHashCode(this.value);
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
final UniqueSequence other = (UniqueSequence) obj;
if (value.length != other.value.length) return false;
for (int i = 0; i < value.length; i++) {
if (!value[i].equals(other.value[i])) return false;
}
return true;
}
}
private static interface UniqueAttribute<T> extends Predicate<FilterNode> {
public Serializable getValue(T bean);
@Override
public boolean test(FilterNode node);
public static <T> UniqueAttribute<T> create(final Attribute<T, Serializable>[] attributes) {
if (attributes.length == 1) {
final Attribute<T, Serializable> attribute = attributes[0];
return new UniqueAttribute<T>() {
@Override
public Serializable getValue(T bean) {
return attribute.get(bean);
}
@Override
public boolean test(FilterNode node) {
if (node == null || node.isOr()) return false;
if (!attribute.field().equals(node.column)) return false;
if (node.nodes == null) return true;
for (FilterNode n : node.nodes) {
if (!test(n)) return false;
}
return true;
}
};
} else {
return new UniqueAttribute<T>() {
@Override
public Serializable getValue(T bean) {
final Serializable[] rs = new Serializable[attributes.length];
for (int i = 0; i < rs.length; i++) {
rs[i] = attributes[i].get(bean);
}
return new UniqueSequence(rs);
}
@Override
public boolean test(FilterNode node) {
return true;
}
};
}
}
}
}