/* * Copyright 2011 Future Systems * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * 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.krakenapps.logdb.query.command; import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; import org.krakenapps.logdb.LogQueryCommand.LogMap; import org.krakenapps.logdb.query.ObjectComparator; import org.krakenapps.logdb.query.command.Function.EvaledField.EvalType; import org.krakenapps.logdb.query.command.Function; import org.krakenapps.logdb.query.command.NumberUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class Function { private static Logger logger = LoggerFactory.getLogger(Function.class); private static Map<String, Class<? extends Function>> mapping; private static Pattern p = Pattern.compile("[0-9]+$"); static { mapping = new HashMap<String, Class<? extends Function>>(); mapping.put("avg", Average.class); mapping.put("count", Count.class); mapping.put("c", Count.class); mapping.put("first", First.class); mapping.put("last", Last.class); mapping.put("max", Max.class); mapping.put("min", Min.class); mapping.put("mean", Average.class); mapping.put("range", Range.class); mapping.put("sum", Sum.class); } private String name; private Integer suffix; private String keyName; private String target; private EvaledField evaled; private Map<String, Class<? extends Function>> extClass; private Function() { } public static Function getFunction(String name, String target) { return getFunction(name, target, null, null); } public static Function getFunction(String name, String target, String keyName) { return getFunction(name, target, keyName, null); } public static Function getFunction(String name, String target, Map<String, Class<? extends Function>> extClass) { return getFunction(name, target, null, extClass); } public static Function getFunction(String name, String target, String keyName, Map<String, Class<? extends Function>> extClass) { if (name == null) return null; // String functionName = name.split("[0-9]+$")[0]; String functionName = p.split(name)[0]; Class<? extends Function> cls = mapping.get(functionName.toLowerCase()); if (cls == null && extClass != null) cls = extClass.get(functionName.toLowerCase()); if (cls == null) return null; try { Function f = cls.newInstance(); f.name = name; if (!functionName.equals(name)) f.suffix = Integer.parseInt(name.substring(functionName.length())); f.keyName = keyName; if (target != null && target.startsWith("eval(") && target.endsWith(")")) { String evalString = target.substring(5, target.length() - 1); EvalType type = null; String lh = null; String operator = null; String rh = null; int index = 0; if (evalString.contains("+")) { index = evalString.indexOf("+"); type = EvalType.Arithmetic; operator = "+"; } else if (evalString.contains("-")) { index = evalString.indexOf("-"); type = EvalType.Arithmetic; operator = "-"; } else if (evalString.contains("*")) { index = evalString.indexOf("*"); type = EvalType.Arithmetic; operator = "*"; } else if (evalString.contains("/")) { index = evalString.indexOf("/"); type = EvalType.Arithmetic; operator = "/"; } else if (evalString.contains("%")) { index = evalString.indexOf("%"); type = EvalType.Arithmetic; operator = "%"; } lh = evalString.substring(0, index); rh = evalString.substring(index + 1); f.evaled = new EvaledField(type, lh, operator, rh); } f.target = target; f.extClass = extClass; return f; } catch (Exception e) { logger.error("kraken logdb: failed create function.", e); } return null; } public String getName() { return name; } public Integer getSuffix() { return suffix; } public String getKeyName() { return keyName; } public void setKeyName(String keyName) { this.keyName = keyName; } public String getTarget() { return target; } @Override public Function clone() { return getFunction(name, target, keyName, extClass); } public void put(LogMap row) { Object value = null; if (evaled != null) value = evaled.eval(row); else value = row.get(target); if (value != null || target == null) put(value); } abstract protected void put(Object obj); abstract public Object getResult(); abstract public void clean(); // when save serialized data to disk public Object[] serialize() { Object[] l = new Object[3]; serialize(l); return l; } protected int serialize(Object[] arr) { arr[0] = name; arr[1] = target; arr[2] = keyName; return 3; } // when load swapped data from disk public void load(Object value) { Object[] l = (Object[]) value; name = (String) l[0]; target = (String) l[1]; keyName = (String) l[2]; } abstract public Function merge(Function func); @Override public String toString() { if (keyName == null) return String.format("%s%s", name, (target != null) ? ("(" + target + ")") : ""); else return keyName; } public static class EvaledField { public static enum EvalType { Arithmetic, Boolean } private EvalType type; private String lh; private String operator; private String rh; public EvaledField(EvalType type, String lh, String operator, String rh) { this.type = type; this.lh = lh; this.operator = operator; this.rh = rh; } public Object eval(LogMap row) { Object l = row.get(lh); if (type == EvalType.Arithmetic) { Object r = row.get(rh); if (l == null && r == null) return null; if (operator.equals("+")) return NumberUtil.add(l, r); else if (operator.equals("-")) return NumberUtil.sub(l, r); else if (operator.equals("*")) return NumberUtil.mul(l, r); else if (operator.equals("/")) return NumberUtil.div(l, r); else if (operator.equals("%")) return NumberUtil.mod(l, r); } else if (type == EvalType.Boolean) { // TODO } return null; } } protected static class Average extends Function { private Double d; private int count; @Override protected void put(Object obj) { d = NumberUtil.add(d, obj).doubleValue(); count++; } public Double getD() { return d; } public void setD(Double d) { this.d = d; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } @Override public Object getResult() { if (d == null) return null; return d / count; } @Override public void clean() { d = null; } @Override public Object[] serialize() { Object[] l = new Object[5]; int i = super.serialize(l); l[i++] = d; l[i++] = count; return l; } @Override public void load(Object value) { super.load(value); Object[] l = (Object[]) value; this.d = (Double) l[3]; this.count = (Integer) l[4]; } @Override public Function merge(Function func) { // d should not be null here (do not allow null merge set) Average other = (Average) func; this.d += other.d; this.count += other.count; return this; } } protected static class Count extends Function { private long result = 0; @Override protected void put(Object obj) { result++; } public void setResult(long result) { this.result = result; } @Override public Object getResult() { return result; } @Override public void clean() { } @Override public Object[] serialize() { Object[] l = new Object[4]; int i = super.serialize(l); l[i++] = result; return l; } @Override public void load(Object value) { super.load(value); Object[] l = (Object[]) value; result = (Long) l[3]; } @Override public Function merge(Function func) { Count c = (Count) func; this.result += c.result; return this; } } protected static class First extends Function { private Object first; @Override protected void put(Object obj) { if (first == null && obj != null) first = obj; } public Object getFirst() { return first; } public void setFirst(Object first) { this.first = first; } @Override public Object getResult() { return first; } @Override public void clean() { first = null; } @Override public Function merge(Function func) { // ignore subsequent items return this; } @Override public Object[] serialize() { Object[] l = new Object[4]; int i = super.serialize(l); l[i++] = first; return l; } @Override public void load(Object value) { super.load(value); Object[] l = (Object[]) value; first = l[3]; } } protected static class Last extends Function { private Object last; @Override protected void put(Object obj) { if (obj != null) last = obj; } public Object getLast() { return last; } public void setLast(Object last) { this.last = last; } @Override public Object getResult() { return last; } @Override public void clean() { last = null; } @Override public Object[] serialize() { Object[] l = new Object[4]; int i = super.serialize(l); l[i++] = last; return l; } @Override public void load(Object value) { super.load(value); Object[] l = (Object[]) value; last = l[3]; } @Override public Function merge(Function func) { Last last = (Last) func; this.last = last.last; return this; } } protected static class Max extends Function { private ObjectComparator comp = new ObjectComparator(); private Object max; @Override protected void put(Object obj) { if (max == null || comp.compare(max, obj) < 0) max = obj; } public Object getMax() { return max; } public void setMax(Object max) { this.max = max; } @Override public Object getResult() { return max; } @Override public void clean() { max = null; } @Override public Object[] serialize() { Object[] l = new Object[4]; int i = super.serialize(l); l[i++] = max; return l; } @Override public void load(Object value) { super.load(value); Object[] l = (Object[]) value; this.max = l[3]; } @Override public Function merge(Function func) { Max other = (Max) func; put(other.max); return this; } } protected static class Min extends Function { private ObjectComparator comp = new ObjectComparator(); private Object min; @Override protected void put(Object obj) { if (min == null || (comp.compare(min, obj) > 0 && obj != null)) min = obj; } public Object getMin() { return min; } public void setMin(Object min) { this.min = min; } @Override public Object getResult() { return min; } @Override public void clean() { min = null; } @Override public Object[] serialize() { Object[] l = new Object[4]; int i = super.serialize(l); l[i++] = min; return l; } @Override public void load(Object value) { super.load(value); Object[] l = (Object[]) value; this.min = l[3]; } @Override public Function merge(Function func) { Min other = (Min) func; put(other.min); return this; } } protected static class Range extends Function { private Number min; private Number max; @Override protected void put(Object obj) { min = NumberUtil.min(min, obj); max = NumberUtil.max(max, obj); } public Number getMin() { return min; } public void setMin(Number min) { this.min = min; } public Number getMax() { return max; } public void setMax(Number max) { this.max = max; } @Override public Object getResult() { if (max == null && min == null) return null; return NumberUtil.sub(max, min); } @Override public void clean() { min = null; max = null; } @Override public Object[] serialize() { Object[] l = new Object[5]; int i = super.serialize(l); l[i++] = min; l[i++] = max; return l; } @Override public void load(Object value) { super.load(value); Object[] l = (Object[]) value; min = (Number) l[3]; max = (Number) l[4]; } @Override public Function merge(Function func) { Range other = (Range) func; this.min = NumberUtil.min(min, other.min); this.max = NumberUtil.max(max, other.max); return this; } } protected static class Sum extends Function { private Number sum = 0L; @Override protected void put(Object obj) { sum = NumberUtil.add(sum, obj); } public Number getSum() { return sum; } public void setSum(Number sum) { this.sum = sum; } @Override public Object getResult() { return sum; } @Override public void clean() { sum = null; } @Override public Object[] serialize() { Object[] l = new Object[4]; int i = super.serialize(l); l[i++] = sum; return l; } @Override public void load(Object value) { super.load(value); Object[] l = (Object[]) value; this.sum = (Number) l[3]; } @Override public Function merge(Function func) { Sum other = (Sum) func; this.sum = NumberUtil.add(sum, other.sum); return this; } } }