/**
* Copyright 2013-2014 Recruit Technologies Co., Ltd. and contributors
* (see CONTRIBUTORS.md)
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. A copy of the
* License is distributed with this work in the LICENSE.md file. You may
* also obtain a copy of the License from
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gennai.gungnir.topology;
import static org.gennai.gungnir.GungnirConst.*;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Map;
import org.gennai.gungnir.tuple.ComplexCondition;
import org.gennai.gungnir.tuple.Condition;
import org.gennai.gungnir.tuple.Field;
import org.gennai.gungnir.tuple.GungnirTuple;
import org.gennai.gungnir.tuple.SimpleCondition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Maps;
public final class ConditionEvaluator implements Serializable {
private static final long serialVersionUID = SERIAL_VERSION_UID;
private static final Logger LOG = LoggerFactory.getLogger(ConditionEvaluator.class);
private static final Map<Class<? extends Number>, Integer> NUMERIC_TYPES_MAP = Maps.newHashMap();
static {
NUMERIC_TYPES_MAP.put(Byte.class, 1);
NUMERIC_TYPES_MAP.put(Short.class, 2);
NUMERIC_TYPES_MAP.put(Integer.class, 3);
NUMERIC_TYPES_MAP.put(Long.class, 4);
NUMERIC_TYPES_MAP.put(Float.class, 5);
NUMERIC_TYPES_MAP.put(Double.class, 6);
}
private ConditionEvaluator() {
}
public static String likeRegex(String likeString) {
StringBuilder sb = new StringBuilder(likeString);
sb.insert(0, '^');
sb.append('$');
int len = sb.length();
int us = 0;
for (int i = 0; i < len;) {
switch (sb.charAt(i)) {
case '_':
us++;
i++;
break;
case '%':
if (us > 0) {
String n = String.valueOf(us);
int l = n.length();
sb.delete(i - us, i);
sb.insert(i - us, ".{");
sb.insert(i - us + 2, n);
sb.insert(i - us + 2 + l, '}');
i += -us + 3 + l;
len += -us + 3 + l;
us = 0;
}
sb.replace(i, i + 1, ".*");
i += 2;
len++;
break;
default:
if (us > 0) {
String n = String.valueOf(us);
int l = n.length();
sb.delete(i - us, i);
sb.insert(i - us, ".{");
sb.insert(i - us + 2, n);
sb.insert(i - us + 2 + l, '}');
i += -us + 3 + l;
len += -us + 3 + l;
us = 0;
}
i++;
}
}
return sb.toString();
}
public static int compareNumber(Number v1, Number v2) {
Integer t1 = NUMERIC_TYPES_MAP.get(v1.getClass());
Integer t2 = NUMERIC_TYPES_MAP.get(v2.getClass());
if (t1 != null && t2 != null) {
Integer t = (t1 > t2) ? t1 : t2;
if (t == 5) {
return Float.compare(v1.floatValue(), v2.floatValue());
} else if (t == 6) {
return Double.compare(v1.doubleValue(), v2.doubleValue());
} else {
Long l = v1.longValue();
return l.compareTo(v2.longValue());
}
} else {
throw new IllegalArgumentException(
"Failed to compare. Arguments isn't TINYINT, SMALLINT, INT, BIGINT, FLOAT, DOUBLE"
+ " compare(" + v1 + ", " + v2 + ")");
}
}
@SuppressWarnings("unchecked")
public static int compare(Object v1, Object v2) {
if (v1.getClass() == v2.getClass()) {
if (v1 instanceof Comparable) {
return ((Comparable<Object>) v1).compareTo(v2);
} else {
throw new IllegalArgumentException("Failed to compare. Arguments isn't comparable. "
+ "compare(" + v1 + ", " + v2 + ")");
}
} else {
if (v1 instanceof Number && v2 instanceof Number) {
return compareNumber((Number) v1, (Number) v2);
} else {
throw new IllegalArgumentException("Failed to compare. Arguments isn't same type."
+ " compare(" + v1 + ", " + v2 + ")");
}
}
}
// TODO No11
private static boolean compare(SimpleCondition.Type type, Object v1, Object v2) {
switch (type) {
case IS_NULL:
return (v1 == null);
case IS_NOT_NULL:
return (v1 != null);
default:
if (v1 == null) {
return false;
}
}
if (v1 instanceof Collection) {
switch (type) {
case ALL:
Collection<?> values = (Collection<?>) v1;
if (v2.getClass().isArray()) {
int len = Array.getLength(v2);
for (int i = 0; i < len; i++) {
boolean ret = false;
for (Object value : values) {
if (compare(value, Array.get(v2, i)) == 0) {
ret = true;
break;
}
}
if (!ret) {
return false;
}
}
return true;
} else {
for (Object value : values) {
if (compare(value, v2) == 0) {
return true;
}
}
return false;
}
case IN:
values = (Collection<?>) v1;
if (v2.getClass().isArray()) {
int len = Array.getLength(v2);
for (int i = 0; i < len; i++) {
for (Object value : values) {
if (compare(value, Array.get(v2, i)) == 0) {
return true;
}
}
}
} else {
for (Object value : values) {
if (compare(value, v2) == 0) {
return true;
}
}
}
return false;
default:
return false;
}
} else {
try {
switch (type) {
case EQ:
if (compare(v1, v2) == 0) {
return true;
}
break;
case NE:
if (compare(v1, v2) != 0) {
return true;
}
break;
case GT:
if (compare(v1, v2) > 0) {
return true;
}
break;
case GE:
if (compare(v1, v2) >= 0) {
return true;
}
break;
case LT:
if (compare(v1, v2) < 0) {
return true;
}
break;
case LE:
if (compare(v1, v2) <= 0) {
return true;
}
break;
case LIKE:
if (v1 instanceof String) {
if (((String) v1).matches(likeRegex((String) v2))) {
return true;
}
}
break;
case REGEXP:
if (v1 instanceof String) {
String regex = ((String) v2);
if (((String) v1).matches(regex)) {
return true;
}
}
break;
case IN:
if (v2.getClass().isArray()) {
int len = Array.getLength(v2);
for (int i = 0; i < len; i++) {
if (compare(v1, Array.get(v2, i)) == 0) {
return true;
}
}
} else {
if (compare(v1, v2) == 0) {
return true;
}
}
break;
case BETWEEN:
Object from = Array.get(v2, 0);
Object to = Array.get(v2, 1);
if (compare(v1, from) >= 0 && compare(v1, to) <= 0) {
return true;
}
break;
default:
return false;
}
return false;
} catch (IllegalArgumentException e) {
if (LOG.isInfoEnabled()) {
LOG.info(e.getMessage());
}
return false;
}
}
}
public static boolean isKeep(Condition condition, GungnirTuple tuple) {
if (condition instanceof ComplexCondition) {
ComplexCondition complexCondition = (ComplexCondition) condition;
switch (complexCondition.getType()) {
case AND:
for (Condition cond : complexCondition.getConditions()) {
if (!isKeep(cond, tuple)) {
return false;
}
}
return true;
case OR:
for (Condition cond : complexCondition.getConditions()) {
if (isKeep(cond, tuple)) {
return true;
}
}
return false;
case NOT:
for (Condition cond : complexCondition.getConditions()) {
if (isKeep(cond, tuple)) {
return false;
}
}
return true;
default:
return false;
}
} else {
SimpleCondition simpleCondition = (SimpleCondition) condition;
Object v1 = simpleCondition.getField().getValue(tuple);
Object v2 = null;
if (simpleCondition.getValue() instanceof Field) {
v2 = ((Field) simpleCondition.getValue()).getValue(tuple);
if (v2 == null) {
return false;
}
} else {
v2 = simpleCondition.getValue();
}
return compare(simpleCondition.getType(), v1, v2);
}
}
}