package edu.stanford.nlp.ling.tokensregex.types; import edu.stanford.nlp.ling.CoreLabel; import edu.stanford.nlp.ling.tokensregex.*; import edu.stanford.nlp.pipeline.ChunkAnnotationUtils; import edu.stanford.nlp.process.CoreLabelTokenFactory; import edu.stanford.nlp.util.*; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.*; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * ValueFunctions supported by tokensregex. * * @author Angel Chang */ public class ValueFunctions { private ValueFunctions() {} // static methods protected static Object lookupFunctionObject(Env env, String name) { if (env != null) { Object obj = env.get(name); if (obj != null) { return obj; } } return registeredFunctions.get(name); } public abstract static class NamedValueFunction implements ValueFunction { protected String name; protected String signature; public NamedValueFunction(String name) { this.name = name; } @Override public String getDescription() { return ""; } public String getParamDesc() { return "..."; } protected static String getParamDesc(String type, int nargs) { if (nargs < 0) { return type + "..."; } else if (nargs <= 3) { String[] tmp = new String[nargs]; Arrays.fill(tmp, type); return StringUtils.join(tmp, ","); } else return type + "[" + nargs + "]"; } protected static String getTypeName(Class c) { return c.getCanonicalName(); } public String toString() { if (signature == null) { signature = name + '(' + getParamDesc() + ')'; } return signature; } } // end static class NamedValueFunction public static class ParamInfo { public final String name; public final String typeName; public final Class className; public final boolean nullable; public ParamInfo(String name, String typeName, Class className, boolean nullable) { this.name = name; this.typeName = typeName; this.className = className; this.nullable = nullable; } } public abstract static class TypeCheckedFunction extends NamedValueFunction { List<ParamInfo> paramInfos; int nargs; public TypeCheckedFunction(String name, List<ParamInfo> paramInfos) { super(name); this.paramInfos = paramInfos; nargs = (paramInfos != null)? paramInfos.size():0; } public TypeCheckedFunction(String name, ParamInfo... paramInfos) { super(name); this.paramInfos = Arrays.asList(paramInfos); nargs = paramInfos.length; } @Override public String getParamDesc() { StringBuilder sb = new StringBuilder(); for (ParamInfo p:paramInfos) { if (sb.length() > 0) { sb.append(", "); } if (p.typeName != null) { sb.append(p.typeName); } else { sb.append(getTypeName(p.className)); } } return sb.toString(); } @Override public boolean checkArgs(List<Value> in) { if (in.size() != nargs) { return false; } for (int i = 0; i < in.size(); i++) { Value v = in.get(i); ParamInfo p = paramInfos.get(i); if (v == null) { if (!p.nullable) { return false; } } else { if (p.typeName != null && !p.typeName.equals(v.getType())) { return false; } if (v.get() != null) { if (p.className != null && !(p.className.isAssignableFrom(v.get().getClass()))) { return false; } } } } return true; } } public abstract static class NumericFunction extends NamedValueFunction { protected String resultTypeName = Expressions.TYPE_NUMBER; protected int nargs = 2; protected NumericFunction(String name, int nargs) { super(name); this.nargs = nargs; } protected NumericFunction(String name, int nargs, String resultTypeName) { super(name); this.resultTypeName = resultTypeName; this.nargs = nargs; } @Override public String getParamDesc() { return getParamDesc(Expressions.TYPE_NUMBER, nargs); } public abstract Number compute(Number...ns); @Override public boolean checkArgs(List<Value> in) { if (nargs > 0 && in.size() != nargs) { return false; } for (Value v : in) { if (v == null || !(v.get() instanceof Number)) { return false; } } return true; } @Override public Value apply(Env env, List<Value> in) { if (nargs > 0 && in.size() != nargs) { throw new IllegalArgumentException(nargs + " arguments expected, got " + in.size()); } Number[] numbers = new Number[in.size()]; for (int i = 0; i < in.size(); i++) { numbers[i] = (Number) in.get(i).get(); } Number res = compute(numbers); return new Expressions.PrimitiveValue(resultTypeName, res); } } public static final ValueFunction ADD_FUNCTION = new NumericFunction("ADD", 2) { @Override public Number compute(Number... in) { if (isInteger(in[0]) && isInteger(in[1])) { return in[0].longValue() + in[1].longValue(); } else { return in[0].doubleValue() + in[1].doubleValue(); } } }; public static final ValueFunction SUBTRACT_FUNCTION = new NumericFunction("SUBTRACT", 2) { @Override public Number compute(Number... in) { if (isInteger(in[0]) && isInteger(in[1])) { return in[0].longValue() - in[1].longValue(); } else { return in[0].doubleValue() - in[1].doubleValue(); } } }; public static final ValueFunction MULTIPLY_FUNCTION = new NumericFunction("MULTIPLY", 2) { @Override public Number compute(Number... in) { if (isInteger(in[0]) && isInteger(in[1])) { return in[0].longValue() * in[1].longValue(); } else { return in[0].doubleValue() * in[1].doubleValue(); } } }; public static final ValueFunction DIVIDE_FUNCTION = new NumericFunction("DIVIDE", 2) { @Override public Number compute(Number... in) { if (isInteger(in[0]) && isInteger(in[1])) { if ( in[0].longValue() % in[1].longValue() == 0) return in[0].longValue() / in[1].longValue(); else return in[0].doubleValue() / in[1].doubleValue(); } else { return in[0].doubleValue() / in[1].doubleValue(); } } }; public static final ValueFunction MOD_FUNCTION = new NumericFunction("MOD", 2) { @Override public Number compute(Number... in) { if (isInteger(in[0]) && isInteger(in[1])) { return in[0].longValue() % in[1].longValue(); } else { return in[0].doubleValue() % in[1].doubleValue(); } } }; public static final ValueFunction MAX_FUNCTION = new NumericFunction("MAX", 2) { @Override public Number compute(Number... in) { if (isInteger(in[0]) && isInteger(in[1])) { return Math.max(in[0].longValue(), in[1].longValue()); } else { return Math.max(in[0].doubleValue(), in[1].doubleValue()); } } }; public static final ValueFunction MIN_FUNCTION = new NumericFunction("MIN", 2) { @Override public Number compute(Number... in) { if (isInteger(in[0]) && isInteger(in[1])) { return Math.min(in[0].longValue(), in[1].longValue()); } else { return Math.min(in[0].doubleValue(), in[1].doubleValue()); } } }; public static final ValueFunction POW_FUNCTION = new NumericFunction("POW", 2) { @Override public Number compute(Number... in) { return Math.pow(in[0].doubleValue(), in[1].doubleValue()); } }; public static final ValueFunction NEGATE_FUNCTION = new NumericFunction("NEGATE", 1) { @Override public Number compute(Number... in) { if (isInteger(in[0])) { return - in[0].longValue(); } else { return - in[0].doubleValue(); } } }; public abstract static class BooleanFunction extends NamedValueFunction { protected String resultTypeName = Expressions.TYPE_BOOLEAN; protected int nargs = 2; protected BooleanFunction(String name, int nargs) { super(name); this.nargs = nargs; } protected BooleanFunction(String name, int nargs, String resultTypeName) { super(name); this.resultTypeName = resultTypeName; this.nargs = nargs; } public abstract Boolean compute(Boolean...ns); @Override public String getParamDesc() { return getParamDesc(Expressions.TYPE_BOOLEAN, nargs); } @Override public boolean checkArgs(List<Value> in) { if (nargs > 0 && in.size() != nargs) { return false; } for (Value v : in) { if (v == null || !(v.get() instanceof Boolean)) { return false; } } return true; } @Override public Value apply(Env env, List<Value> in) { if (nargs > 0 && in.size() != nargs) { throw new IllegalArgumentException(nargs + " arguments expected, got " + in.size()); } Boolean[] bools = new Boolean[in.size()]; for (int i = 0; i < in.size(); i++) { bools[i] = (Boolean) in.get(i).get(); } Boolean res = compute(bools); return new Expressions.PrimitiveValue(resultTypeName, res); } } public static final ValueFunction AND_FUNCTION = new BooleanFunction("AND", -1) { @Override public Boolean compute(Boolean... in) { for (Boolean b:in) { if (!b) return false; } return true; } }; public static final ValueFunction OR_FUNCTION = new BooleanFunction("OR", -1) { @Override public Boolean compute(Boolean... in) { for (Boolean b:in) { if (b) return true; } return false; } }; public static final ValueFunction NOT_FUNCTION = new BooleanFunction("NOT", 1) { @Override public Boolean compute(Boolean... in) { Boolean res = !in[0]; return res; } }; private static String join(Object[] args, String glue) { String res = null; if (args.length == 1) { // Only one element - check if it is a list or array and do join on that if (args[0] instanceof Iterable) { res = StringUtils.join((Iterable) args[0], glue); } else { res = StringUtils.join(args, glue); } } else { res = StringUtils.join(args, glue); } return res; } public abstract static class StringFunction extends NamedValueFunction { protected String resultTypeName = Expressions.TYPE_STRING; protected int nargs = 2; protected StringFunction(String name, int nargs) { super(name); this.nargs = nargs; } protected StringFunction(String name, int nargs, String resultTypeName) { super(name); this.resultTypeName = resultTypeName; this.nargs = nargs; } public abstract String compute(String... strs); @Override public String getParamDesc() { return getParamDesc(Expressions.TYPE_STRING, nargs); } @Override public boolean checkArgs(List<Value> in) { if (nargs > 0 && in.size() != nargs) { return false; } for (Value v : in) { if (v == null /*|| !(v.get() instanceof String) */) { return false; } } return true; } @Override public Value apply(Env env, List<Value> in) { if (nargs > 0 && in.size() != nargs) { throw new IllegalArgumentException(nargs + " arguments expected, got " + in.size()); } String[] strs = new String[in.size()]; for (int i = 0; i < in.size(); i++) { if (in.get(i).get() instanceof String) { strs[i] = (String) in.get(i).get(); } else if (in.get(i).get() != null) { strs[i] = in.get(i).get().toString(); } else { strs[i] = null; } } String res = compute(strs); return new Expressions.PrimitiveValue(resultTypeName, res); } } public static final ValueFunction CONCAT_FUNCTION = new StringFunction("CONCAT", -1) { @Override public String compute(String... in) { return join(in, ""); } }; public static final ValueFunction UPPERCASE_FUNCTION = new StringFunction("UPPERCASE", 1) { @Override public String compute(String... in) { return in[0].toUpperCase(); } }; public static final ValueFunction LOWERCASE_FUNCTION = new StringFunction("LOWERCASE", 1) { @Override public String compute(String... in) { return in[0].toLowerCase(); } }; public static final ValueFunction PRINT_FUNCTION = new NamedValueFunction("PRINT") { @Override public String getParamDesc() { return "..."; } @Override public boolean checkArgs(List<Value> in) { if (in.size() < 1) { return false; } if (in.size() > 1 && (in.get(0) == null || !(in.get(0).get() instanceof String))) { return false; } return true; } @Override public Value apply(Env env, List<Value> in) { if (in.size() > 1) { String format = (String) in.get(0).get(); Object[] args = new Object[in.size()-1]; for (int i = 1; i < in.size(); i++) { args[i-1] = in.get(i).get(); } String res = String.format(format, args); System.out.print(res); } else { System.out.print(in.get(0)); } return null; } }; public static final ValueFunction FORMAT_FUNCTION = new NamedValueFunction("FORMAT") { @Override public String getParamDesc() { return Expressions.TYPE_STRING + ",..."; } @Override public boolean checkArgs(List<Value> in) { if (in.size() < 1) { return false; } if (in.get(0) == null || !(in.get(0).get() instanceof String)) { return false; } return true; } @Override public Value apply(Env env, List<Value> in) { String format = (String) in.get(0).get(); Object[] args = new Object[in.size()-1]; for (int i = 1; i < in.size(); i++) { args[i-1] = in.get(i).get(); } String res = String.format(format, args); return new Expressions.PrimitiveValue(Expressions.TYPE_STRING, res); } }; public static final ValueFunction JOIN_FUNCTION = new NamedValueFunction("JOIN") { @Override public String getParamDesc() { return "String glue,..."; } @Override public boolean checkArgs(List<Value> in) { if (in.size() < 1) { return false; } if (in.get(0) == null || !(in.get(0).get() instanceof String)) { return false; } return true; } @Override public Value apply(Env env, List<Value> in) { String glue = (String) in.get(0).get(); Object[] args = new Object[in.size()-1]; for (int i = 1; i < in.size(); i++) { args[i-1] = in.get(i).get(); } String res = join(args, glue); return new Expressions.PrimitiveValue(Expressions.TYPE_STRING, res); } }; public static final ValueFunction CREATE_REGEX_FUNCTION = new NamedValueFunction("CREATE_REGEX") { @Override public boolean checkArgs(List<Value> in) { if (in.size() < 1) { return false; } if (in.get(0) == null || !(in.get(0).get() instanceof List)) { return false; } return true; } @Override public Value apply(Env env, List<Value> in) { List list = (List) in.get(0).get(); String[] args = new String[list.size()]; for (int i = 0; i < list.size(); i++) { args[i] = list.get(i).toString(); } MultiWordStringMatcher matcher = new MultiWordStringMatcher("EXCTWS"); String regex = matcher.getRegex(args); return new Expressions.PrimitiveValue(Expressions.TYPE_REGEX, regex); } }; private static final ParamInfo PARAM_INFO_VALUE_FUNCTION = new ParamInfo("FUNCTION", Expressions.TYPE_FUNCTION, ValueFunction.class, false); private static final ParamInfo PARAM_INFO_LIST = new ParamInfo("LIST", null, List.class, true); public static final ValueFunction MAP_VALUES_FUNCTION = new TypeCheckedFunction("MAP_VALUES", PARAM_INFO_LIST, PARAM_INFO_VALUE_FUNCTION) { // First argument is list of elements to apply function to // Second argument is function to apply @Override public Value apply(Env env, List<Value> in) { if (in.get(0) == null) return null; List list = (List) in.get(0).get(); ValueFunction func = (ValueFunction) in.get(1).get(); List<Value> res = new ArrayList<>(list.size()); for (Object elem:list) { List<Value> args = new ArrayList<>(1); args.add(Expressions.createValue(Expressions.TYPE_LIST, elem)); res.add(func.apply(env, args)); } return new Expressions.PrimitiveValue<>(Expressions.TYPE_LIST, res); } }; private static final ParamInfo PARAM_INFO_FUNCTION = new ParamInfo("FUNCTION", Expressions.TYPE_FUNCTION, Function.class, false); public static final ValueFunction MAP_FUNCTION = new TypeCheckedFunction("MAP", PARAM_INFO_LIST, PARAM_INFO_FUNCTION) { // First argument is list of elements to apply function to // Second argument is function to apply @Override public Value apply(Env env, List<Value> in) { if (in.get(0) == null) return null; List list = (List) in.get(0).get(); Function func = (Function) in.get(1).get(); List<Object> res = new ArrayList<>(list.size()); for (Object elem:list) { res.add(func.apply(elem)); } return new Expressions.PrimitiveValue<>(null, res); } }; private static final ParamInfo PARAM_INFO_TOKEN_REGEX = new ParamInfo("TOKEN_REGEX", Expressions.TYPE_TOKEN_REGEX, TokenSequencePattern.class, false); private static final ParamInfo PARAM_INFO_TOKEN_LIST = new ParamInfo("TOKEN_LIST", null, List.class, true); private static final ParamInfo PARAM_INFO_TOKEN_LIST_REPLACE = new ParamInfo("TOKEN_LIST_REPLACEMENT", null, List.class, true); public static final ValueFunction TOKENS_MATCH_FUNCTION = new TypeCheckedFunction("TOKENS_MATCH", PARAM_INFO_TOKEN_LIST, PARAM_INFO_TOKEN_REGEX) { // First argument is list of tokens to match // Second argument is pattern to match @Override public Value apply(Env env, List<Value> in) { if (in.get(0) == null || in.get(0).get() == null) return Expressions.FALSE; List<CoreMap> cms = (List<CoreMap>) in.get(0).get(); TokenSequencePattern pattern = (TokenSequencePattern) in.get(1).get(); TokenSequenceMatcher matcher = pattern.getMatcher(cms); boolean matches = matcher.matches(); return (matches)? Expressions.TRUE: Expressions.FALSE; } }; public static final ValueFunction TOKENS_REPLACE_FUNCTION = new TypeCheckedFunction("TOKENS_REPLACE", PARAM_INFO_TOKEN_LIST, PARAM_INFO_TOKEN_REGEX, PARAM_INFO_TOKEN_LIST_REPLACE) { // First argument is list of tokens to match // Second argument is pattern to match // Third argument is replacement tokens @Override public Value apply(Env env, List<Value> in) { if (in.get(0) == null || in.get(0).get() == null) return Expressions.FALSE; List<CoreMap> cms = (List<CoreMap>) in.get(0).get(); List<CoreMap> replacement = (List<CoreMap>) in.get(2).get(); TokenSequencePattern pattern = (TokenSequencePattern) in.get(1).get(); TokenSequenceMatcher matcher = pattern.getMatcher(cms); List<CoreMap> replaced = matcher.replaceAll(replacement); return new Expressions.PrimitiveValue(Expressions.TYPE_TOKENS, replaced); } }; private static final ParamInfo PARAM_INFO_STRING_REGEX = new ParamInfo("REGEX", Expressions.TYPE_REGEX, null, false); private static final ParamInfo PARAM_INFO_STRING = new ParamInfo("STRING", null, String.class, true); private static final ParamInfo PARAM_INFO_STRING_REPLACE = new ParamInfo("STRING_REPLACEMENT", null, String.class, true); public static final ValueFunction STRING_MATCH_FUNCTION = new TypeCheckedFunction("STRING_MATCH", PARAM_INFO_STRING, PARAM_INFO_STRING_REGEX) { // First argument is string to match // Second argument is pattern to match @Override public Value apply(Env env, List<Value> in) { if (in.get(0) == null || in.get(0).get() == null) return Expressions.FALSE; String str = (String) in.get(0).get(); String regex = (String) in.get(1).get(); Pattern pattern = env.getStringPattern(regex); Matcher matcher = pattern.matcher(str); boolean matches = matcher.matches(); return (matches)? Expressions.TRUE: Expressions.FALSE; } }; public static final ValueFunction STRING_REPLACE_FUNCTION = new TypeCheckedFunction("STRING_REPLACE", PARAM_INFO_STRING, PARAM_INFO_STRING_REGEX, PARAM_INFO_STRING_REPLACE) { // First argument is string to match // Second argument is pattern to match // Third argument is replacement string @Override public Value apply(Env env, List<Value> in) { if (in.get(0) == null || in.get(0).get() == null) return Expressions.FALSE; String str = (String) in.get(0).get(); String replacement = (String) in.get(2).get(); String regex = (String) in.get(1).get(); Pattern pattern = env.getStringPattern(regex); Matcher matcher = pattern.matcher(str); String replaced = matcher.replaceAll(replacement); return new Expressions.PrimitiveValue(Expressions.TYPE_STRING, replaced); } }; private static final CoreLabelTokenFactory CORELABEL_FACTORY = new CoreLabelTokenFactory(); private static final ParamInfo PARAM_INFO_TOKEN = new ParamInfo("TOKEN", null, CoreMap.class, false); public static final ValueFunction TOKEN_STRING_SPLIT_FUNCTION = new TypeCheckedFunction("TOKEN_STRING_SPLIT", PARAM_INFO_TOKEN, PARAM_INFO_STRING_REGEX, new ParamInfo("INCLUDE_MATCHED", null, Boolean.class, false)) { // First argument is token to split // Second argument is pattern to split on @Override public Value apply(Env env, List<Value> in) { CoreMap cm = (CoreMap) in.get(0).get(); String regex = (String) in.get(1).get(); Boolean includeMatchedAsTokens = (Boolean) in.get(2).get(); Pattern pattern = env.getStringPattern(regex); List<CoreLabel> res = ChunkAnnotationUtils.splitCoreMap(pattern, includeMatchedAsTokens, cm, CORELABEL_FACTORY); return new Expressions.PrimitiveValue(Expressions.TYPE_TOKENS, res); } }; public static boolean isInteger(Number n) { return (n instanceof Long || n instanceof Integer || n instanceof Short); } public static final NumericComparator NUMBER_COMPARATOR = new NumericComparator(); public static class NumericComparator implements Comparator<Number> { @Override public int compare(Number o1, Number o2) { if (isInteger(o1) && isInteger(o2)) { return Long.compare(o1.longValue(), o2.longValue()); } else { return Double.compare(o1.doubleValue(),o2.doubleValue()); } } } public static class ComparableComparator<T extends Comparable<T>> implements Comparator<T> { @Override public int compare(T o1, T o2) { return o1.compareTo(o2); } } public enum CompareType { GT, LT, GE, LE, EQ, NE } public static class CompareFunction<T> extends NamedValueFunction { Comparator<T> comparator; CompareType compType; Class clazz; public CompareFunction(String name, Comparator<T> comparator, CompareType compType, Class clazz) { super(name); this.comparator = comparator; this.compType = compType; this.clazz = clazz; } @Override public String getParamDesc() { return "(" + getTypeName(clazz) + "," + getTypeName(clazz) + ")"; } public Boolean compare(T o1, T o2) { int res = comparator.compare(o1,o2); switch (compType) { case GT: return res > 0; case LT: return res < 0; case GE: return res >= 0; case LE: return res <= 0; case EQ: return res == 0; case NE: return res != 0; default: throw new UnsupportedOperationException("Unknown compType: " + compType); } } @Override public boolean checkArgs(List<Value> in) { if (in.size() != 2) { return false; } if (clazz != null) { if (in.get(0) == null || in.get(0).get() == null || !(clazz.isAssignableFrom(in.get(0).get().getClass()))) { return false; } if (in.get(1) == null || in.get(1).get() == null || !(clazz.isAssignableFrom(in.get(1).get().getClass()))) { return false; } } return true; } @Override public Value apply(Env env, List<Value> in) { if (in.size() != 2) { throw new IllegalArgumentException("2 arguments expected, got " + in.size()); } if (in.get(0) == null || in.get(1) == null || in.get(0).get() == null || in.get(1).get() == null) { return null; // Can't compare... } Boolean res = compare((T) in.get(0).get(), (T) in.get(1).get()); return (res)? Expressions.TRUE: Expressions.FALSE; } } public static final ValueFunction NOT_EQUALS_FUNCTION = new NamedValueFunction("EQUALS") { @Override public String getParamDesc() { return "Object,Object"; } @Override public boolean checkArgs(List<Value> in) { return in.size() == 2; } @Override public Value apply(Env env, List<Value> in) { if (in.size() != 2) { throw new IllegalArgumentException("2 arguments expected, got " + in.size()); } boolean res = false; if (in.get(0) == null || in.get(1) == null) { res = (in.get(0) == in.get(1)); } else if (in.get(0).get() == null || in.get(1).get() == null) { res = (in.get(0).get() == in.get(1).get()); } else { res = in.get(0).get().equals(in.get(1).get()); } return (res)? Expressions.FALSE: Expressions.TRUE; } }; public static final ValueFunction EQUALS_FUNCTION = new NamedValueFunction("EQUALS") { @Override public String getParamDesc() { return "Object,Object"; } @Override public boolean checkArgs(List<Value> in) { return in.size() == 2; } @Override public Value apply(Env env, List<Value> in) { if (in.size() != 2) { throw new IllegalArgumentException("2 arguments expected, got " + in.size()); } boolean res = false; if (in.get(0) == null || in.get(1) == null) { res = (in.get(0) == in.get(1)); } else if (in.get(0).get() == null || in.get(1).get() == null) { res = (in.get(0).get() == in.get(1).get()); } else { res = in.get(0).get().equals(in.get(1).get()); } return (res)? Expressions.TRUE: Expressions.FALSE; } }; public static final ValueFunction ANNOTATION_FUNCTION = new NamedValueFunction("ANNOTATION_VALUE") { @Override public String getParamDesc() { return "CoreMap coremap,String fieldName|Class field,[Object value]"; } // First argument is what (CoreMap) to get annotation for // Second argument is field (Class or String) to get annotation for // Third argument (optional) is annotation value to set @Override public boolean checkArgs(List<Value> in) { if (in.size() != 2 && in.size() != 3) { return false; } if (in.get(0) == null || (!(in.get(0).get() instanceof CoreMap) && !(in.get(0).get() instanceof List))) { return false; } if (in.get(1) == null || (!(in.get(1).get() instanceof Class) && !(in.get(1).get() instanceof String))) { return false; } return true; } @Override public Value apply(Env env, List<Value> in) { Value cmv = in.get(0); Object field = in.get(1).get(); Class annotationFieldClass = null; if (field instanceof String) { annotationFieldClass = EnvLookup.lookupAnnotationKey(env, (String) field); if (annotationFieldClass == null) { throw new IllegalArgumentException("Cannot get annotation field " + field); } } else if (field instanceof Class) { annotationFieldClass = (Class) field; } else { throw new IllegalArgumentException("Type mismatch on arg1: Cannot apply " + this + " to " + in); } if (cmv.get() instanceof CoreMap) { CoreMap cm = (CoreMap) cmv.get(); if (in.size() >= 3) { Value v = in.get(2); Object annotationObject = (v != null)? v.get():null; cm.set(annotationFieldClass, annotationObject); } Object obj = cm.get(annotationFieldClass); return Expressions.createValue(annotationFieldClass.getName(), obj); } else if (cmv.get() instanceof List) { List<CoreMap> cmList = (List<CoreMap>) cmv.get(); if (in.size() >= 3) { Value v = in.get(2); Object annotationObject = (v != null)? v.get():null; for (CoreMap cm:cmList) { cm.set(annotationFieldClass, annotationObject); } } List<Object> list = new ArrayList<>(); Value res = new Expressions.PrimitiveValue(Expressions.TYPE_LIST, list); for (CoreMap cm:cmList) { list.add(cm.get(annotationFieldClass)); } return res; } else { throw new IllegalArgumentException("Type mismatch on arg0: Cannot apply " + this + " to " + in); } } }; public static final ValueFunction GET_ANNOTATION_TAG_FUNCTION = new NamedValueFunction("GET_ANNOTATION_TAG") { @Override public String getParamDesc() { return "CoreMap or List<CoreMap>,String tag"; } // First argument is what (CoreMap or List<CoreMap>) to tag // Second argument is tag @Override public boolean checkArgs(List<Value> in) { if (in.size() != 2) { return false; } if (in.get(0) == null || in.get(0).get() == null) return true; // Allow for NULL if (in.get(0) == null || (!(in.get(0).get() instanceof CoreMap) && !(in.get(0).get() instanceof List))) { return false; } if (in.get(1) == null || !(in.get(1).get() instanceof String)) { return false; } return true; } public Value getTag(CoreMap cm, String tag) { Tags tags = cm.get(Tags.TagsAnnotation.class); return (tags != null)? tags.getTag(tag): null; } @Override public Value apply(Env env, List<Value> in) { if (in.get(0) == null || in.get(0).get() == null) return null; Value v = in.get(0); Value res = null; String tag = (String) in.get(1).get(); if (v.get() instanceof CoreMap) { res = getTag((CoreMap) v.get(), tag); } else if (v.get() instanceof List) { List<CoreMap> cmList = (List<CoreMap>) v.get(); List<Value> list = new ArrayList<>(); res = new Expressions.PrimitiveValue(Expressions.TYPE_LIST, list); for (CoreMap cm:cmList) { list.add(getTag(cm, tag)); } } else { throw new IllegalArgumentException("Type mismatch on arg0: Cannot apply " + this + " to " + in); } return res; } }; public static final ValueFunction SET_ANNOTATION_TAG_FUNCTION = new NamedValueFunction("SET_ANNOTATION_TAG") { @Override public String getParamDesc() { return "CoreMap or List<CoreMap>,String tag,[Object value]"; } // First argument is what (CoreMap or List<CoreMap>) to tag // Second argument is tag // Third argument is tag value @Override public boolean checkArgs(List<Value> in) { if (in.size() != 2 && in.size() != 3) { return false; } if (in.get(0) == null || (!(in.get(0).get() instanceof CoreMap) && !(in.get(0).get() instanceof List))) { return false; } if (in.get(1) == null || !(in.get(1).get() instanceof String)) { return false; } return true; } public void setTag(CoreMap cm, String tag, Value tagValue) { Tags tags = cm.get(Tags.TagsAnnotation.class); if (tags == null) { cm.set(Tags.TagsAnnotation.class, tags = new Tags()); } tags.setTag(tag, tagValue); } @Override public Value apply(Env env, List<Value> in) { Value v = in.get(0); String tag = (String) in.get(1).get(); Value tagValue = (in.size() >= 3)? in.get(2):null; if (v.get() instanceof CoreMap) { setTag((CoreMap) v.get(), tag, tagValue); } else if (v.get() instanceof List) { List<CoreMap> cmList = (List<CoreMap>) v.get(); for (CoreMap cm:cmList) { setTag(cm, tag, tagValue); } } else { throw new IllegalArgumentException("Type mismatch on arg0: Cannot apply " + this + " to " + in); } return v; } }; public static final ValueFunction REMOVE_ANNOTATION_TAG_FUNCTION = new NamedValueFunction("REMOVE_ANNOTATION_TAG") { @Override public String getParamDesc() { return "CoreMap or List<CoreMap>,String tag"; } // First argument is what (CoreMap) to tag // Second argument is tag @Override public boolean checkArgs(List<Value> in) { if (in.size() != 2) { return false; } if (in.get(0) == null || (!(in.get(0).get() instanceof CoreMap) && !(in.get(0).get() instanceof List))) { return false; } if (in.get(1) == null || !(in.get(1).get() instanceof String)) { return false; } return true; } public void removeTag(CoreMap cm, String tag) { Tags tags = cm.get(Tags.TagsAnnotation.class); if (tags != null) { tags.removeTag(tag); } } @Override public Value apply(Env env, List<Value> in) { Value v = in.get(0); String tag = (String) in.get(1).get(); if (v.get() instanceof CoreMap) { removeTag((CoreMap) v.get(), tag); } else if (v.get() instanceof List) { List<CoreMap> cmList = (List<CoreMap>) v.get(); for (CoreMap cm:cmList) { removeTag(cm, tag); } } else { throw new IllegalArgumentException("Type mismatch on arg0: Cannot apply " + this + " to " + in); } return v; } }; public static final ValueFunction TAGS_VALUE_FUNCTION = new NamedValueFunction("TAGS_VALUE") { @Override public String getParamDesc() { return getTypeName(Tags.class) + " tags,String field,[Object value]"; } // First argument is tags object // Second argument is tag // Third argument (optional) is tag value @Override public boolean checkArgs(List<Value> in) { if (in.size() != 2 && in.size() != 3) { return false; } if (in.get(0) == null || !(in.get(0).get() instanceof Tags)) { return false; } if (in.get(1) == null || !(in.get(1).get() instanceof String)) { return false; } return true; } @Override public Value apply(Env env, List<Value> in) { Value v = in.get(0); Tags tags = (Tags) v.get(); String tag = (String) in.get(1).get(); if (in.size() >= 3) { Value tagValue = in.get(2); tags.setTag(tag, tagValue); } return tags.getTag(tag); } }; public static final ValueFunction SET_VALUE_TAG_FUNCTION = new NamedValueFunction("VALUE_TAG") { @Override public String getParamDesc() { return "Value,String tag,[Object value]"; } // First argument is what to tag // Second argument is tag // Third argument is tag value @Override public boolean checkArgs(List<Value> in) { if (in.size() != 2 && in.size() != 3) { return false; } if (in.get(0) == null) { return false; } if (in.get(1) == null || !(in.get(1).get() instanceof String)) { return false; } return true; } @Override public Value apply(Env env, List<Value> in) { Value v = in.get(0); Tags tags = v.getTags(); if (tags == null) { v.setTags(tags = new Tags()); } String tag = (String) in.get(1).get(); Value tagValue = (in.size() >= 3)? in.get(2):null; tags.setTag(tag, tagValue); return v; } }; public static final ValueFunction GET_VALUE_TAG_FUNCTION = new NamedValueFunction("GET_VALUE_TAG") { @Override public String getParamDesc() { return "Value,String tag"; } // First argument is what to tag // Second argument is tag @Override public boolean checkArgs(List<Value> in) { if (in.size() != 2) { return false; } if (in.get(0) == null) { return false; } if (in.get(1) == null || !(in.get(1).get() instanceof String)) { return false; } return true; } @Override public Value apply(Env env, List<Value> in) { Value v = in.get(0); Tags tags = v.getTags(); String tag = (String) in.get(1).get(); return (tags != null)? tags.getTag(tag):null; } }; public static final ValueFunction REMOVE_VALUE_TAG_FUNCTION = new NamedValueFunction("REMOVE_VALUE_TAG") { @Override public String getParamDesc() { return "Value,String tag"; } // First argument is what to tag // Second argument is tag @Override public boolean checkArgs(List<Value> in) { if (in.size() != 2) { return false; } if (in.get(0) == null) { return false; } if (in.get(1) == null || !(in.get(1).get() instanceof String)) { return false; } return true; } @Override public Value apply(Env env, List<Value> in) { Value v = in.get(0); Tags tags = v.getTags(); if (tags == null) { v.setTags(tags = new Tags()); } String tag = (String) in.get(1).get(); tags.removeTag(tag); return v; } }; public static final ValueFunction COMPOSITE_VALUE_FUNCTION = new NamedValueFunction("COMPOSITE_VALUE") { @Override public String getParamDesc() { return Expressions.TYPE_COMPOSITE + " obj,String field,[Object value]"; } // First argument is composite value // Second argument is field to select // Third argument (optional) is value to set composite value field to @Override public boolean checkArgs(List<Value> in) { if (in.size() != 2 && in.size() != 3) { return false; } if (in.get(0) == null || in.get(0).get() == null) return true; // Allow for null if (in.get(0) == null || !(in.get(0) instanceof Expressions.CompositeValue)) { return false; } if (in.get(1) == null || !(in.get(1).get() instanceof String)) { return false; } return true; } @Override public Value apply(Env env, List<Value> in) { if (in.get(0) == null || in.get(0).get() == null ) return null; // Allow for null Expressions.CompositeValue v = (Expressions.CompositeValue) in.get(0); String fieldName = (String) in.get(1).get(); if (in.size() >= 3) { v.set(fieldName, in.get(2)); } return v.getValue(fieldName); } }; public static final ValueFunction COMPOSITE_KEYS_FUNCTION = new NamedValueFunction("COMPOSITE_KEYS") { @Override public String getParamDesc() { return Expressions.TYPE_COMPOSITE; } // First argument is composite value @Override public boolean checkArgs(List<Value> in) { if (in.size() != 1) { return false; } if (in.get(0) == null || in.get(0).get() == null) return true; // Allow for null if (in.get(0) == null || !(in.get(0) instanceof Expressions.CompositeValue)) { return false; } return true; } @Override public Value apply(Env env, List<Value> in) { if (in.get(0) == null || in.get(0).get() == null ) return null; // Allow for null Expressions.CompositeValue v = (Expressions.CompositeValue) in.get(0); List<String> res = new ArrayList<>(v.getAttributes()); return Expressions.createValue(Expressions.TYPE_LIST, res); } }; public static final ValueFunction OBJECT_FIELD_FUNCTION = new NamedValueFunction("OBJECT_FIELD") { @Override public String getParamDesc() { return "Object obj,String fieldName,[Object value]"; } // First argument is object // Second argument is field to select // Third argument (optional) is value to assign to object field @Override public boolean checkArgs(List<Value> in) { if (in.size() != 2 && in.size() != 3) { return false; } if (in.get(0) == null || in.get(0).get() == null) return true; // Allow for null if (in.get(0) == null || !(in.get(0) instanceof Object)) { return false; } if (in.get(1) == null || !(in.get(1).get() instanceof String)) { return false; } return true; } @Override public Value apply(Env env, List<Value> in) { if (in.get(0) == null || in.get(0).get() == null ) return null; // Allow for null Value v = in.get(0); String fieldName = (String) in.get(1).get(); try { Object obj = v.get(); Field f = obj.getClass().getField(fieldName); if (in.size() >= 3) { Value fieldValue = in.get(2); if (fieldValue == null) { f.set(obj, null); } else if (f.getType().isAssignableFrom(Value.class)) { f.set(obj, fieldValue); } else { if (fieldValue.get() == null) { f.set(obj, null); } else if (f.getType().isAssignableFrom(List.class)) { if (fieldValue.get() instanceof List) { List list = (List) fieldValue.get(); Type[] fieldParamTypes = ((ParameterizedType) f.getGenericType()).getActualTypeArguments(); if (fieldParamTypes[0] instanceof Value) { List<Value> list2 = new ArrayList<>(list.size()); for (Object elem:list) { list2.add(Expressions.asValue(env, elem)); } f.set(obj, list2); } else { List list2 = new ArrayList(list.size()); for (Object elem:list) { if (elem instanceof Value) { list2.add(((Value) elem).get()); } else { list2.add(elem); } } f.set(obj, list2); } } else { f.set(obj, Arrays.asList(fieldValue.get())); } } else { f.set(obj, fieldValue.get()); } } } return Expressions.createValue(null, f.get(obj)); } catch (NoSuchFieldException ex) { throw new RuntimeException("Cannot get field " + fieldName + " from " + v, ex); } catch (IllegalAccessException ex) { throw new RuntimeException("Cannot get field " + fieldName + " from " + v, ex); } } }; public static final ValueFunction LIST_VALUE_FUNCTION = new NamedValueFunction("LIST_VALUE") { @Override public String getParamDesc() { return "List list,int index,[Object value]"; } // First argument is List // Second argument is index of element to select // Third argument (optional) is value to assign list element @Override public boolean checkArgs(List<Value> in) { if (in.size() != 2 && in.size() != 3) { return false; } if (in.get(0) == null || in.get(0).get() == null) return true; // Allow for null if (in.get(0) == null || !(in.get(0).get() instanceof List)) { return false; } if (in.get(1) == null || !(in.get(1).get() instanceof Integer)) { return false; } return true; } @Override public Value apply(Env env, List<Value> in) { if (in.get(0) == null || in.get(0).get() == null ) return null; // Allow for null List list = (List) in.get(0).get(); Integer index = (Integer) in.get(1).get(); if (index < 0) { index = list.size() + index; } if (index >= list.size() || index < 0) { // index out of bounds return null; } if (in.size() >= 3) { Value fieldValue = in.get(2); if (fieldValue != null) { list.set(index, fieldValue.get()); } else { list.set(index, null); } } Object obj = list.get(index); return Expressions.asValue(env, obj); // return Expressions.PrimitiveValue.create(null, obj); } }; public static final ValueFunction MAP_VALUE_FUNCTION = new NamedValueFunction("MAP_VALUE") { @Override public String getParamDesc() { return "Map map,Object key,[Object value]"; } // First argument is Map // Second argument is key of element to select // Third argument (optional) is value to assign to element @Override public boolean checkArgs(List<Value> in) { if (in.size() != 2 && in.size() != 3) { return false; } if (in.get(0) == null || in.get(0).get() == null) return true; // Allow for null if (in.get(0) == null || !(in.get(0).get() instanceof Map)) { return false; } if (in.get(1) == null || !(in.get(1).get() instanceof Object)) { return false; } return true; } @Override public Value apply(Env env, List<Value> in) { if (in.get(0) == null || in.get(0).get() == null ) return null; // Allow for null Map map = (Map) in.get(0).get(); Object key = in.get(1).get(); if (in.size() >= 3) { Value fieldValue = in.get(2); if (fieldValue != null) { map.put(key, fieldValue.get()); } else { map.remove(key); } } Object obj = map.get(key); if (in.size() == 2 && obj == null && key instanceof String) { Class annotationFieldClass = null; annotationFieldClass = EnvLookup.lookupAnnotationKey(env, (String) key); if (annotationFieldClass != null) { obj = map.get(annotationFieldClass); } } return Expressions.asValue(env, obj); // return Expressions.PrimitiveValue.create(null, obj); } }; public static final ValueFunction MAP_KEYS_FUNCTION = new NamedValueFunction("MAP_KEYS") { @Override public String getParamDesc() { return "Map"; } // First argument is Map @Override public boolean checkArgs(List<Value> in) { if (in.size() != 1) { return false; } if (in.get(0) == null || in.get(0).get() == null) return true; // Allow for null if (in.get(0) == null || !(in.get(0).get() instanceof Map)) { return false; } return true; } @Override public Value apply(Env env, List<Value> in) { if (in.get(0) == null || in.get(0).get() == null ) return null; // Allow for null Map map = (Map) in.get(0).get(); List<Object> res = new ArrayList<>(map.keySet()); return Expressions.createValue(Expressions.TYPE_LIST, res); } }; public static final ValueFunction AGGREGATE_FUNCTION = new NamedValueFunction("AGGREGATE") { @Override public String getParamDesc() { return "ValueFunction func,Object initialValue,..."; } // First argument is function to apply // Second argument is initial value @Override public boolean checkArgs(List<Value> in) { if (in.size() < 2) { return false; } if (in.get(0) == null || !(in.get(0).get() instanceof ValueFunction)) { return false; } if (in.get(1) == null) { return false; } return true; } @Override public Value apply(Env env, List<Value> in) { ValueFunction func = (ValueFunction) in.get(0).get(); Value res = in.get(1); List<Value> args = new ArrayList<>(2); for (int i = 2; i < in.size(); i++) { args.set(0, res); args.set(1, in.get(i)); res = func.apply(env, args); } return res; } }; public static final ValueFunction CALL_FUNCTION = new NamedValueFunction("CALL") { @Override public String getParamDesc() { return "ValueFunction func or String funcname,..."; } // First argument is function to apply @Override public boolean checkArgs(List<Value> in) { if (in.size() < 1) { return false; } if (in.get(0) == null || !(in.get(0).get() instanceof ValueFunction || in.get(0).get() instanceof String)) { return false; } return true; } @Override public Value apply(Env env, List<Value> in) { Value res; List<Value> args = new ArrayList<>(in.size() - 1); for (int i = 1; i < in.size(); i++) { args.add(in.get(i)); } if (in.get(0).get() instanceof ValueFunction) { ValueFunction func = (ValueFunction) in.get(0).get(); res = func.apply(env, args); } else if (in.get(0).get() instanceof String) { Expressions.FunctionCallExpression func = new Expressions.FunctionCallExpression((String) in.get(0).get(), args); res = func.evaluate(env); } else { throw new IllegalArgumentException("Type mismatch on arg0: Cannot apply " + this + " to " + in); } return res; } }; static final CollectionValuedMap<String, ValueFunction> registeredFunctions = new CollectionValuedMap<>( MapFactory.<String, Collection<ValueFunction>>linkedHashMapFactory(), CollectionFactory.<ValueFunction>arrayListFactory(), false); static { registeredFunctions.add("Add", ADD_FUNCTION); registeredFunctions.add("Subtract", SUBTRACT_FUNCTION); registeredFunctions.add("Multiply", MULTIPLY_FUNCTION); registeredFunctions.add("Divide", DIVIDE_FUNCTION); registeredFunctions.add("Mod", MOD_FUNCTION); registeredFunctions.add("Min", MIN_FUNCTION); registeredFunctions.add("Max", MAX_FUNCTION); registeredFunctions.add("Pow", POW_FUNCTION); registeredFunctions.add("Negate", NEGATE_FUNCTION); registeredFunctions.add("And", AND_FUNCTION); registeredFunctions.add("Or", OR_FUNCTION); registeredFunctions.add("Not", NOT_FUNCTION); registeredFunctions.add("Format", FORMAT_FUNCTION); registeredFunctions.add("Concat", CONCAT_FUNCTION); registeredFunctions.add("Join", JOIN_FUNCTION); registeredFunctions.add("Lowercase", LOWERCASE_FUNCTION); registeredFunctions.add("Uppercase", UPPERCASE_FUNCTION); registeredFunctions.add("Map", MAP_VALUES_FUNCTION); registeredFunctions.add("Map", MAP_FUNCTION); registeredFunctions.add("Match", TOKENS_MATCH_FUNCTION); registeredFunctions.add("Match", STRING_MATCH_FUNCTION); registeredFunctions.add("Replace", TOKENS_REPLACE_FUNCTION); registeredFunctions.add("Replace", STRING_REPLACE_FUNCTION); registeredFunctions.add("GE", new CompareFunction<>("GE", NUMBER_COMPARATOR, CompareType.GE, Number.class) ); registeredFunctions.add("GT", new CompareFunction<>("GT", NUMBER_COMPARATOR, CompareType.GT, Number.class) ); registeredFunctions.add("LE", new CompareFunction<>("LE", NUMBER_COMPARATOR, CompareType.LE, Number.class) ); registeredFunctions.add("LT", new CompareFunction<>("LT", NUMBER_COMPARATOR, CompareType.LT, Number.class) ); registeredFunctions.add("EQ", new CompareFunction<>("EQ", NUMBER_COMPARATOR, CompareType.EQ, Number.class) ); registeredFunctions.add("NE", new CompareFunction<>("NE", NUMBER_COMPARATOR, CompareType.NE, Number.class) ); registeredFunctions.add("EQ", EQUALS_FUNCTION ); registeredFunctions.add("NE", NOT_EQUALS_FUNCTION ); registeredFunctions.add("VTag", SET_VALUE_TAG_FUNCTION); registeredFunctions.add("GetVTag", GET_VALUE_TAG_FUNCTION); registeredFunctions.add("RemoveVTag", REMOVE_VALUE_TAG_FUNCTION); registeredFunctions.add("Tag", SET_ANNOTATION_TAG_FUNCTION); registeredFunctions.add("GetTag", GET_ANNOTATION_TAG_FUNCTION); registeredFunctions.add("RemoveTag", REMOVE_ANNOTATION_TAG_FUNCTION); registeredFunctions.add("Split", TOKEN_STRING_SPLIT_FUNCTION); registeredFunctions.add("Annotate", ANNOTATION_FUNCTION); registeredFunctions.add("Aggregate", AGGREGATE_FUNCTION); registeredFunctions.add("Call", CALL_FUNCTION); registeredFunctions.add("CreateRegex", CREATE_REGEX_FUNCTION); registeredFunctions.add("Select", COMPOSITE_VALUE_FUNCTION); registeredFunctions.add("Select", MAP_VALUE_FUNCTION); registeredFunctions.add("Select", TAGS_VALUE_FUNCTION); registeredFunctions.add("Select", ANNOTATION_FUNCTION); registeredFunctions.add("Select", OBJECT_FIELD_FUNCTION); registeredFunctions.add("ListSelect", LIST_VALUE_FUNCTION); registeredFunctions.add("Keys", MAP_KEYS_FUNCTION); registeredFunctions.add("Keys", COMPOSITE_KEYS_FUNCTION); registeredFunctions.add("Set", TAGS_VALUE_FUNCTION); registeredFunctions.add("Set", COMPOSITE_VALUE_FUNCTION); registeredFunctions.add("Set", MAP_VALUE_FUNCTION); registeredFunctions.add("Set", ANNOTATION_FUNCTION); registeredFunctions.add("Set", OBJECT_FIELD_FUNCTION); registeredFunctions.add("Set", LIST_VALUE_FUNCTION); registeredFunctions.add("Get", TAGS_VALUE_FUNCTION); registeredFunctions.add("Get", COMPOSITE_VALUE_FUNCTION); registeredFunctions.add("Get", MAP_VALUE_FUNCTION); registeredFunctions.add("Get", ANNOTATION_FUNCTION); registeredFunctions.add("Get", OBJECT_FIELD_FUNCTION); registeredFunctions.add("Get", LIST_VALUE_FUNCTION); // For debugging registeredFunctions.add("Print", PRINT_FUNCTION); } public static void main(String[] args) { // Dumps the registered functions for (Map.Entry<String, Collection<ValueFunction>> entry : registeredFunctions.entrySet()) { for (ValueFunction vf: entry.getValue()) { System.out.println(entry.getKey() + ": " + vf); } } } }