/******************************************************************************* * Copyright (c) 2014 Open Door Logistics (www.opendoorlogistics.com) * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser Public License v3 * which accompanies this distribution, and is available at http://www.gnu.org/licenses/lgpl.txt ******************************************************************************/ package com.opendoorlogistics.core.formulae; import java.awt.Color; import java.awt.Image; import java.awt.Toolkit; import java.awt.image.BufferedImage; import java.awt.image.FilteredImageSource; import java.awt.image.ImageFilter; import java.awt.image.ImageProducer; import java.awt.image.RGBImageFilter; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.Random; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.opendoorlogistics.api.geometry.ODLGeom; import com.opendoorlogistics.api.tables.ODLColumnType; import com.opendoorlogistics.api.tables.ODLTime; import com.opendoorlogistics.core.AppProperties; import com.opendoorlogistics.core.geometry.ODLGeomImpl; import com.opendoorlogistics.core.geometry.ODLLoadedGeometry; import com.opendoorlogistics.core.geometry.operations.LinestringFraction; import com.opendoorlogistics.core.gis.map.Symbols.SymbolType; import com.opendoorlogistics.core.gis.map.background.BackgroundMapConfig; import com.opendoorlogistics.core.gis.map.background.BackgroundTileFactorySingleton; import com.opendoorlogistics.core.gis.postcodes.UKPostcodes; import com.opendoorlogistics.core.gis.postcodes.UKPostcodes.UKPostcodeLevel; import com.opendoorlogistics.core.tables.ColumnValueProcessor; import com.opendoorlogistics.core.tables.utils.ExampleData; import com.opendoorlogistics.core.utils.Colours; import com.opendoorlogistics.core.utils.Numbers; import com.opendoorlogistics.core.utils.images.ImageUtils; import com.opendoorlogistics.core.utils.strings.StandardisedStringTreeMap; import com.opendoorlogistics.core.utils.strings.Strings; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; public class Functions { public static final Object EXECUTION_ERROR = new Object() { @Override public String toString() { return "Formula execution error"; } }; private static abstract class Fm2ParamBase extends FunctionImpl { public Fm2ParamBase(Function a, Function b) { super(a, b); } @Override public Function deepCopy() { throw new UnsupportedOperationException(); } protected abstract Object execute(Object a, Object b); @Override public Object execute(FunctionParameters parameters) { Object a = child(0).execute(parameters); Object b = child(1).execute(parameters); if (a == EXECUTION_ERROR || b == EXECUTION_ERROR) { return EXECUTION_ERROR; } return execute(a, b); } } public static class FmDecimalHours extends FunctionImpl{ public FmDecimalHours(Function time){ super(time); } @Override public Object execute(FunctionParameters parameters) { Object v = child(0).execute(parameters); if(v==Functions.EXECUTION_ERROR){ return Functions.EXECUTION_ERROR; } v = ColumnValueProcessor.convertToMe(ODLColumnType.LONG, v); if (v==null){ return null; } Long l = (Long)v; return ((double)l) / ODLTime.MILLIS_IN_HOUR; } @Override public Function deepCopy() { return new FmDecimalHours(child(0).deepCopy()); } } public static class FmTime extends FunctionImpl { public FmTime(Function... components) { super(components); if (components.length > 5) { throw new RuntimeException("Too many arguments for the function time: " + components.length); } } /** * Non-var args construction for the function definition library. */ public FmTime() { super(); } /** * Non-var args construction for the function definition library. * * @param p1 */ public FmTime(Function p1) { super(p1); } /** * Non-var args construction for the function definition library. * * @param p1 * @param p2 */ public FmTime(Function p1, Function p2) { super(p1, p2); } /** * Define non-var args construction for the function definition library. * * @param p1 * @param p2 * @param p3 */ public FmTime(Function p1, Function p2, Function p3) { super(p1, p2, p3); } /** * Define non-var args construction for the function definition library. * * @param p1 * @param p2 * @param p3 * @param p4 */ public FmTime(Function p1, Function p2, Function p3, Function p4) { super(p1, p2, p3, p4); } /** * Define non-var args construction for the function definition library. * * @param p1 * @param p2 * @param p3 * @param p4 * @param p5 */ public FmTime(Function p1, Function p2, Function p3, Function p4, Function p5) { super(p1, p2, p3, p4, p5); } // private FmTime(Function... children) { // super(children); // // TODO Auto-generated constructor stub // } @Override public Object execute(FunctionParameters parameters) { if (children == null || children.length == 0) { return new ODLTime(); } Object[] res = executeChildFormulae(parameters, true); Long[] longs = new Long[res.length]; for (int i = 0; i < res.length; i++) { longs[i] = Numbers.toLong(res[i]); if (longs[i] == null) { return EXECUTION_ERROR; } } try { switch (res.length) { case 1: return new ODLTime(longs[0]); case 2: return new ODLTime(longs[0], longs[1]); case 3: return new ODLTime(longs[0], longs[1], longs[2]); case 4: return new ODLTime(longs[0], longs[1], longs[2], longs[3]); case 5: return new ODLTime(longs[0], longs[1], longs[2], longs[3], longs[4]); default: break; } } catch (Exception e) { return EXECUTION_ERROR; } return EXECUTION_ERROR; } @Override public Function deepCopy() { return new FmTime(deepCopy(children)); } @Override public String toString() { return super.toString("time"); } } private static abstract class Fm1ParamBase extends FunctionImpl { public Fm1ParamBase(Function a) { super(a); } @Override public Function deepCopy() { throw new UnsupportedOperationException(); } protected abstract double execute(double d); protected abstract long execute(long l); @Override public Object execute(FunctionParameters parameters) { Object a = child(0).execute(parameters); if (a == EXECUTION_ERROR || a == null) { return EXECUTION_ERROR; } // try treating as long first to preserve integer numbers where possible Long l = Numbers.toLongIfNotFloatingPoint(a); if (l != null) { return execute(l); } // then try double Double d = Numbers.toDouble(a); if (d != null) { return execute(d); } return EXECUTION_ERROR; } } public static final class FmAbs extends Fm1ParamBase { public FmAbs(Function a) { super(a); } @Override public Function deepCopy() { return new FmAbs(child(0).deepCopy()); } @Override public String toString() { return "abs(" + child(0).toString() + ")"; } @Override protected double execute(double d) { return Math.abs(d); } @Override protected long execute(long l) { return Math.abs(l); } } public static abstract class Fm1DoubleParam extends Fm1ParamBase { public Fm1DoubleParam(Function a) { super(a); } @Override protected long execute(long l) { throw new UnsupportedOperationException(); } @Override public Object execute(FunctionParameters parameters) { Object a = child(0).execute(parameters); if (a == EXECUTION_ERROR || a == null) { return EXECUTION_ERROR; } // always treat as double Double d = Numbers.toDouble(a); if (d != null) { return execute(d); } return EXECUTION_ERROR; } } public static final class FmLn extends Fm1DoubleParam { public FmLn(Function a) { super(a); } @Override public Function deepCopy() { return new FmLn(child(0).deepCopy()); } @Override public String toString() { return "ln(" + child(0).toString() + ")"; } @Override protected double execute(double d) { return Math.log(d); } } public static final class FmDecimalFormat extends FunctionImpl{ private final DecimalFormat format; public FmDecimalFormat(Function pattern,Function number){ super(pattern,number); format = new DecimalFormat(pattern.toString()); } @Override public Object execute(FunctionParameters parameters) { Object child = child(1).execute(parameters); if(child==EXECUTION_ERROR){ return EXECUTION_ERROR; } Double d = Numbers.toDouble(child); if(d!=null){ return format.format(d); } return null; } @Override public Function deepCopy() { return new FmDecimalFormat(child(0).deepCopy(), child(1).deepCopy()); } } public static final class FmLog10 extends Fm1DoubleParam { public FmLog10(Function a) { super(a); } @Override public Function deepCopy() { return new FmLog10(child(0).deepCopy()); } @Override public String toString() { return "log10(" + child(0).toString() + ")"; } @Override protected double execute(double d) { return Math.log10(d); } } public static final class FmSin extends Fm1DoubleParam { public FmSin(Function a) { super(a); } @Override public Function deepCopy() { return new FmSin(child(0).deepCopy()); } @Override public String toString() { return "sin(" + child(0).toString() + ")"; } @Override protected double execute(double d) { return Math.sin(d); } } public static final class FmAsin extends Fm1DoubleParam { public FmAsin(Function a) { super(a); } @Override public Function deepCopy() { return new FmAsin(child(0).deepCopy()); } @Override public String toString() { return "asin(" + child(0).toString() + ")"; } @Override protected double execute(double d) { return Math.asin(d); } } public static final class FmCos extends Fm1DoubleParam { public FmCos(Function a) { super(a); } @Override public Function deepCopy() { return new FmCos(child(0).deepCopy()); } @Override public String toString() { return "cos(" + child(0).toString() + ")"; } @Override protected double execute(double d) { return Math.cos(d); } } public static final class FmSqrt extends Fm1DoubleParam { public FmSqrt(Function a) { super(a); } @Override public Function deepCopy() { return new FmSqrt(child(0).deepCopy()); } @Override public String toString() { return super.toString("sqrt"); } @Override protected double execute(double d) { return Math.sqrt(d); } } public static final class FmTemperatureColours extends FunctionImpl { public FmTemperatureColours(Function f) { super(f); } @Override public Object execute(FunctionParameters parameters) { Double val = Numbers.toDouble(child(0).execute(parameters)); if (val == null) { return EXECUTION_ERROR; } return Colours.temperature(val); } @Override public Function deepCopy() { return new FmTemperatureColours(child(0).deepCopy()); } } public static final class FmLerp extends FunctionImpl { public FmLerp(Function from, Function to, Function fraction) { super(from, to, fraction); } @Override public Function deepCopy() { return new FmLerp(child(0).deepCopy(), child(1).deepCopy(), child(2).deepCopy()); } @Override public String toString() { return super.toString("lerp"); } private double lerp(double from, double to, double fraction) { if (fraction <= 0) { return from; } if (fraction >= 1) { return to; } return from * (1.0 - fraction) + to * fraction; } @Override public Object execute(FunctionParameters parameters) { Object[] children = executeChildFormulae(parameters, true); if (children == null) { return EXECUTION_ERROR; } Double fraction = Numbers.toDouble(children[2]); if (fraction == null) { return EXECUTION_ERROR; } // check for colour if (Color.class.isInstance(children[0]) || Color.class.isInstance(children[1])) { Color c1 =(Color) ColumnValueProcessor.convertToMe(ODLColumnType.COLOUR, children[0]); Color c2 =(Color) ColumnValueProcessor.convertToMe(ODLColumnType.COLOUR, children[1]); if(c1 == null || c2 == null){ return null; } return Colours.lerp(c1, c2, fraction); } Double low = Numbers.toDouble(children[0]); Double high = Numbers.toDouble(children[1]); if (low == null || high == null) { return EXECUTION_ERROR; } return lerp(low, high, fraction); } } public static final class FmAcos extends Fm1DoubleParam { public FmAcos(Function a) { super(a); } @Override public Function deepCopy() { return new FmAcos(child(0).deepCopy()); } @Override public String toString() { return "acos(" + child(0).toString() + ")"; } @Override protected double execute(double d) { return Math.acos(d); } } public static final class FmTan extends Fm1DoubleParam { public FmTan(Function a) { super(a); } @Override public Function deepCopy() { return new FmTan(child(0).deepCopy()); } @Override public String toString() { return "tan(" + child(0).toString() + ")"; } @Override protected double execute(double d) { return Math.tan(d); } } public static final class FmAtan extends Fm1DoubleParam { public FmAtan(Function a) { super(a); } @Override public Function deepCopy() { return new FmAtan(child(0).deepCopy()); } @Override public String toString() { return "atan(" + child(0).toString() + ")"; } @Override protected double execute(double d) { return Math.atan(d); } } public static final class FmCeil extends Fm1ParamBase { public FmCeil(Function a) { super(a); } @Override public Function deepCopy() { return new FmCeil(child(0).deepCopy()); } @Override public String toString() { return "ceil(" + child(0).toString() + ")"; } @Override protected double execute(double d) { return Math.ceil(d); } @Override protected long execute(long l) { return l; } } public static final class FmFloor extends Fm1ParamBase { public FmFloor(Function a) { super(a); } @Override public Function deepCopy() { return new FmFloor(child(0).deepCopy()); } @Override public String toString() { return "floor(" + child(0).toString() + ")"; } @Override protected double execute(double a) { return Math.floor(a); } @Override protected long execute(long l) { return l; } } public static final class FmRound extends FunctionImpl { public FmRound(Function a) { super(a); } @Override public Function deepCopy() { return new FmRound(child(0).deepCopy()); } @Override public String toString() { return "round(" + child(0).toString() + ")"; } @Override public boolean hasBrackets() { return true; } @Override public Object execute(FunctionParameters parameters) { Object a = child(0).execute(parameters); if (a == EXECUTION_ERROR ) { return EXECUTION_ERROR; } Double d = Numbers.toDouble(a); if (d != null) { return (long) Math.round(d); } return null; } } public static final class FmAnd extends FunctionImpl { public FmAnd(Function a, Function b) { super(a, b); } @Override public Function deepCopy() { return new FmAnd(child(0).deepCopy(), child(1).deepCopy()); } @Override public Object execute(FunctionParameters parameters) { int nbChildren = nbChildren(); for (int j = 0; j < nbChildren; j++) { Object val = child(j).execute(parameters); if (val == EXECUTION_ERROR) { return EXECUTION_ERROR; } Double d = Numbers.toDouble(val); if (d == null) { // treat null as false d= 0.0; } if (d != 1) { // booleans should treated as long.. return 0L; } } return 1L; } @Override public String toString() { return toStringWithChildOp("&&"); } } public static final class FmIndexOf extends FunctionImpl { public FmIndexOf(Function findText, Function findWithin) { super(findText, findWithin); } @Override public Function deepCopy() { return new FmIndexOf(child(0).deepCopy(), child(1).deepCopy()); } @Override public String toString() { return super.toString("indexof"); } @Override public Object execute(FunctionParameters parameters) { return execute(child(0).execute(parameters), child(1).execute(parameters)); } static Object execute(Object a, Object b) { if (a == null || a == EXECUTION_ERROR || b == null || b == EXECUTION_ERROR) { return EXECUTION_ERROR; } String sa = (String) ColumnValueProcessor.convertToMe(ODLColumnType.STRING, a); String sb = (String) ColumnValueProcessor.convertToMe(ODLColumnType.STRING, b); if (a == null || b == null) { return EXECUTION_ERROR; } sa = sa.toLowerCase(); sb = sb.toLowerCase(); return sb.indexOf(sa); } } public static final class FmContains extends FunctionImpl { public FmContains(Function findText, Function findWithin) { super(findText, findWithin); } @Override public Function deepCopy() { return new FmContains(child(0).deepCopy(), child(1).deepCopy()); } @Override public String toString() { return super.toString("contains"); } @Override public Object execute(FunctionParameters parameters) { Object o = FmIndexOf.execute(child(0).execute(parameters), child(1).execute(parameters)); if (o == EXECUTION_ERROR) { return EXECUTION_ERROR; } return ((Integer) o) != -1 ? 1 : 0; } } /** * Parent class for formula which return a single string * * @author Phil * */ public abstract static class FmSingleString extends FunctionImpl { protected FmSingleString(Function child) { super(child); } @Override public Object execute(FunctionParameters parameters) { Object a = child(0).execute(parameters); if (a == null || a == EXECUTION_ERROR) { return EXECUTION_ERROR; } String sa = (String) ColumnValueProcessor.convertToMe(ODLColumnType.STRING, a); if (sa == null) { return null; } return execute(sa); } protected abstract Object execute(String s); } public static final class FmUpper extends FmSingleString { public FmUpper(Function child) { super(child); } @Override public Function deepCopy() { return new FmUpper(child(0).deepCopy()); } @Override protected Object execute(String s) { return s.toUpperCase(); } @Override public String toString() { return super.toString("upper"); } } public static final class FmLower extends FmSingleString { public FmLower(Function child) { super(child); } @Override public Function deepCopy() { return new FmLower(child(0).deepCopy()); } @Override protected Object execute(String s) { return s.toLowerCase(); } @Override public String toString() { return super.toString("lower"); } } public static final class FmCreateUUID extends FunctionImpl{ public FmCreateUUID(Function text) { super(text); } @Override public Object execute(FunctionParameters parameters) { Object val = child(0).execute(parameters); if(val == Functions.EXECUTION_ERROR || val==null){ return Functions.EXECUTION_ERROR; } return UUID.nameUUIDFromBytes(ColumnValueProcessor.convertToMe(ODLColumnType.STRING , val).toString().getBytes()); } @Override public Function deepCopy() { return new FmCreateUUID(child(0).deepCopy()); } } public static final class FmLeft extends FunctionImpl { public FmLeft(Function text, Function number_of_characters) { super(text, number_of_characters); } @Override public Function deepCopy() { return new FmLeft(child(0).deepCopy(), child(1).deepCopy()); } @Override public String toString() { return super.toString("left"); } @Override public Object execute(FunctionParameters parameters) { Object a = child(0).execute(parameters); Object b = child(1).execute(parameters); if (a == null || a == EXECUTION_ERROR || b == null || b == EXECUTION_ERROR) { return EXECUTION_ERROR; } String sa = (String) ColumnValueProcessor.convertToMe(ODLColumnType.STRING, a); Double l = Numbers.toDouble(b); if (l == null) { return EXECUTION_ERROR; } return sa.substring(0, Math.min(l.intValue(), sa.length())); } } public static final class FmReplace extends FunctionImpl { public FmReplace(Function findWithin, Function oldText, Function newText) { super(findWithin, oldText, newText); } @Override public Function deepCopy() { return new FmReplace(child(0).deepCopy(), child(1).deepCopy(), child(2).deepCopy()); } @Override public String toString() { return super.toString("replace"); } @Override public Object execute(FunctionParameters parameters) { Object a = child(0).execute(parameters); Object b = child(1).execute(parameters); Object c = child(2).execute(parameters); if (a == null || a == EXECUTION_ERROR || b == null || b == EXECUTION_ERROR || c == null || c == EXECUTION_ERROR) { return EXECUTION_ERROR; } String sa = (String) ColumnValueProcessor.convertToMe(ODLColumnType.STRING, a); String sb = (String) ColumnValueProcessor.convertToMe(ODLColumnType.STRING, b); String sc = (String) ColumnValueProcessor.convertToMe(ODLColumnType.STRING, c); if (sa == null || sb == null || sc == null) { return EXECUTION_ERROR; } // always case-insensitive and be sure to process the string so it can be matched literally sb = Pattern.quote(sb); return Pattern.compile(sb, Pattern.CASE_INSENSITIVE).matcher(sa).replaceAll(sc); } } public static final class FmRound2Second extends FunctionImpl { public FmRound2Second(Function time) { super(time); } @Override public Function deepCopy() { return new FmRound2Second(child(0).deepCopy()); } @Override public String toString() { return super.toString("roundmilliseconds"); } @Override public Object execute(FunctionParameters parameters) { Object a = child(0).execute(parameters); if (a == EXECUTION_ERROR) { return EXECUTION_ERROR; } Long l = Numbers.toLong(a); if(l==null){ return EXECUTION_ERROR; } ODLTime time = new ODLTime(l); if(time.getMilliseconds()>=500){ time = new ODLTime(time.getTotalMilliseconds() + 1000 - time.getMilliseconds()); }else{ time = new ODLTime(time.getTotalMilliseconds() - time.getMilliseconds()); } return time; } } public static final class FmLen extends FunctionImpl { public FmLen(Function text) { super(text); } @Override public Function deepCopy() { return new FmLen(child(0).deepCopy()); } @Override public String toString() { return super.toString("len"); } @Override public Object execute(FunctionParameters parameters) { Object a = child(0).execute(parameters); if (a == EXECUTION_ERROR) { return EXECUTION_ERROR; } String sa = (String) ColumnValueProcessor.convertToMe(ODLColumnType.STRING, a); if (sa == null) { return new Long(0); } else { return new Long(sa.length()); } } } public static final class FmStringFormat extends FunctionImpl{ public FmStringFormat(Function format, Function ...args){ super(FunctionUtils.toSingleArray(format, args)); } private FmStringFormat(Function ...fncs){ super(fncs); } @Override public Object execute(FunctionParameters parameters) { Object [] chdl = executeChildFormulae(parameters, false); if(chdl==null || chdl[0] == null){ return Functions.EXECUTION_ERROR; } String s = ColumnValueProcessor.convertToMe(ODLColumnType.STRING, chdl[0]).toString(); Object [] args = Arrays.copyOfRange(chdl, 1, chdl.length); String ret = String.format(s, args); return ret; } @Override public Function deepCopy() { return new FmStringFormat(deepCopy(children)); } } /** * Base class for all comparisons, e.g. <, <=, >, >= * * @author Phil * */ public static abstract class FmRelativeComparisonBase extends FunctionImpl { public FmRelativeComparisonBase(Function a, Function b) { super(a, b); } protected abstract Object compare(double a, double b); @Override public final Object execute(FunctionParameters parameters) { Object a = child(0).execute(parameters); Object b = child(1).execute(parameters); if (a == EXECUTION_ERROR || b == EXECUTION_ERROR) { return EXECUTION_ERROR; } Double da = Numbers.toDouble(a); Double db = Numbers.toDouble(b); if (da == null || db == null) { // comparing nulls always gives false return false; } return compare(da, db); } public abstract String operator(); @Override public String toString() { return toStringWithChildOp(operator()); } } public static final class FmConst extends FunctionImpl { private final Object val; public static final FmConst NULL = new FmConst((Object)null); public FmConst(Object val) { super(); this.val = val; } /** * Create a constant out of the input function's toString value; used to qualify when a formula containing a string i.e. "name" should refer * to a table's field or a string constant. * * @param function */ public FmConst(Function function) { this(function.toString()); } @Override public Function deepCopy() { return new FmConst(val); } @Override public Object execute(FunctionParameters parameters) { return val; } @Override public String toString() { if (val != null) { if (Color.class.isInstance(val)) { Color col = (Color) val; return new FmColour(new FmConst(col.getRed() / 255.0), new FmConst(col.getGreen() / 255.0), new FmConst(col.getBlue() / 255.0)).toString(); } else { return val.toString(); } } return "null"; } public Object value() { return val; } } public static class FmRand extends FunctionImpl { protected final Random random = new Random(); public FmRand() { super(); } @Override public Function deepCopy() { return new FmRand(); } @Override public Object execute(FunctionParameters parameters) { return random.nextDouble(); } @Override public String toString() { return "rand()"; } } public static final class FmRandData extends FmRand{ private final RandDataType rdt; public enum RandDataType{ PERSON_NAME, COMPANY_NAME, STREET_NAME } public FmRandData(RandDataType rdt){ this.rdt = rdt; } @Override public Object execute(FunctionParameters parameters) { switch(rdt){ case PERSON_NAME: return ExampleData.getRandomPersonName(random); case COMPANY_NAME: return ExampleData.getRandomBusinessName(random); case STREET_NAME: return ExampleData.getRandomStreet(random); default: return null; } } @Override public Function deepCopy() { return new FmRandData(rdt); } } public static final class FmRandomSymbol extends FunctionImpl { private final Random random = new Random(); public FmRandomSymbol() { super(); } @Override public Function deepCopy() { return new FmRandomSymbol(); } @Override public Object execute(FunctionParameters parameters) { int index = random.nextInt(SymbolType.values().length); return SymbolType.values()[index].getKeyword(); } @Override public String toString() { return "randomsymbol()"; } } public static final class FmDivide extends Fm2ParamBase { public FmDivide(Function a, Function b) { super(a, b); } @Override public Function deepCopy() { return new FmDivide(child(0).deepCopy(), child(1).deepCopy()); } @Override protected Object execute(Object a, Object b) { Double da = Numbers.toDouble(a); Double db = Numbers.toDouble(b); if (da == null || db == null) { return EXECUTION_ERROR; } return da / db; } @Override public String toString() { return toStringWithChildOp("/"); } } public abstract static class FmImageFilter extends FunctionImpl { public FmImageFilter(Function... parameters) { super(parameters); } @Override public Object execute(FunctionParameters parameters) { Object[] executed = executeChildFormulae(parameters, true); if (executed == null) { return Functions.EXECUTION_ERROR; } if (BufferedImage.class.isInstance(executed[0]) == false) { return Functions.EXECUTION_ERROR; } BufferedImage img = (BufferedImage) executed[0]; ImageFilter filter = createFilter(executed); if (filter == null) { return Functions.EXECUTION_ERROR; } ImageProducer ip = new FilteredImageSource(img.getSource(), filter); Image ret = Toolkit.getDefaultToolkit().createImage(ip); return ImageUtils.toBufferedImage(ret); } protected abstract ImageFilter createFilter(Object[] executed); } /** * Fade the image, making it transparent by multiplying its alpha channel by the fade value. * * @author Phil * */ public static final class FmFadeImage extends FmImageFilter { public FmFadeImage(Function image, Function fadeValue) { super(image, fadeValue); } @Override protected ImageFilter createFilter(Object[] executed) { Double alpha = Numbers.toDouble(executed[1]); if (alpha == null) { return null; } if (alpha < 0) { alpha = 0.0; } if (alpha > 1) { alpha = 1.0; } final double finalAlpha = alpha; ImageFilter filter = new RGBImageFilter() { @Override public int filterRGB(int x, int y, int rgb) { // pass directly if completely transparent Color pixelCol = new Color(rgb); int alpha = pixelCol.getAlpha(); if (alpha == 0) { return rgb; } double dAlpha = alpha * (1.0 / 255); dAlpha *= finalAlpha; int finalAlpha = Colours.ensureRange((int) Math.round(dAlpha * 255)); Color ret = new Color(pixelCol.getRed(), pixelCol.getGreen(), pixelCol.getBlue(), finalAlpha); return ret.getRGB(); } }; return filter; } @Override public Function deepCopy() { return new FmFadeImage(child(0).deepCopy(), child(1).deepCopy()); } } /** * Apply the colour filter to the image. All non-transparent pixels are linearly interpolated (lerped) between their original colour and the input * colour, according to the lerp fraction. * * @author Phil * */ public static final class FmColourImage extends FmImageFilter { public FmColourImage(Function image, Function col, Function lerp) { super(image, col, lerp); } @Override protected ImageFilter createFilter(Object[] executed) { final Color filterCol = Colours.toColour(executed[1]); if (filterCol == null) { return null; } final Double lerpValue = Numbers.toDouble(executed[2]); if (lerpValue == null) { return null; } ImageFilter filter = new RGBImageFilter() { @Override public int filterRGB(int x, int y, int rgb) { // pass directly if completely transparent Color pixelCol = new Color(rgb, true); if (pixelCol.getAlpha() == 0) { return rgb; } // lerp between the two according to the lerp value Color lerped = Colours.lerp(pixelCol, filterCol, lerpValue); return lerped.getRGB(); } }; return filter; } @Override public Function deepCopy() { return new FmColourImage(child(0).deepCopy(), child(1).deepCopy(), child(2).deepCopy()); } } public static final class FmEquals extends FunctionImpl { public FmEquals(Function a, Function b) { super(a, b); } @Override public final Object execute(FunctionParameters parameters) { Object a = child(0).execute(parameters); Object b = child(1).execute(parameters); if (a == EXECUTION_ERROR || b == EXECUTION_ERROR) { return EXECUTION_ERROR; } boolean equals = ColumnValueProcessor.isEqual(a, b); return equals ? 1L : 0L; } @Override public Function deepCopy() { return new FmEquals(child(0).deepCopy(), child(1).deepCopy()); } @Override public String toString() { return toStringWithChildOp("="); } } public static final class FmNotEqual extends FunctionImpl { public FmNotEqual(Function a, Function b) { super(a, b); } @Override public final Object execute(FunctionParameters parameters) { Object a = child(0).execute(parameters); Object b = child(1).execute(parameters); if (a == EXECUTION_ERROR || b == EXECUTION_ERROR) { return EXECUTION_ERROR; } boolean equals = ColumnValueProcessor.isEqual(a, b); return equals ? 0L : 1L; } @Override public Function deepCopy() { return new FmNotEqual(child(0).deepCopy(), child(1).deepCopy()); } @Override public String toString() { return toStringWithChildOp("!="); } } public static final class FmGreaterThan extends FmRelativeComparisonBase { public FmGreaterThan(Function a, Function b) { super(a, b); } @Override protected Object compare(double a, double b) { return a > b ? 1L : 0L; } @Override public Function deepCopy() { return new FmGreaterThan(child(0).deepCopy(), child(1).deepCopy()); } @Override public String operator() { return ">"; } } public static final class FmGreaterThanEqualTo extends FmRelativeComparisonBase { public FmGreaterThanEqualTo(Function a, Function b) { super(a, b); } @Override protected Object compare(double a, double b) { return a >= b ? 1L : 0L; } @Override public Function deepCopy() { return new FmGreaterThanEqualTo(child(0).deepCopy(), child(1).deepCopy()); } @Override public String operator() { return ">="; } } public static final class FmIfThenElse extends FunctionImpl { public FmIfThenElse(Function fmIf, Function fmThen, Function fmElse) { super(fmIf, fmThen, fmElse); } @Override public Function deepCopy() { return new FmIfThenElse(child(0).deepCopy(), child(1).deepCopy(), child(2).deepCopy()); } @Override public Object execute(FunctionParameters parameters) { Object o = child(0).execute(parameters); if (o == null || o == EXECUTION_ERROR) { return EXECUTION_ERROR; } Double d = Numbers.toDouble(o); if (d == null) { return EXECUTION_ERROR; } if (d == 1) { return child(1).execute(parameters); } else { return child(2).execute(parameters); } } @Override public String toString() { return super.toString("if"); } @Override public boolean hasBrackets() { return true; } } public static final class FmLessThan extends FmRelativeComparisonBase { public FmLessThan(Function a, Function b) { super(a, b); } @Override protected Object compare(double a, double b) { return a < b ? 1L : 0L; } @Override public Function deepCopy() { return new FmLessThan(child(0).deepCopy(), child(1).deepCopy()); } @Override public String operator() { return "<"; } } public static final class FmLessThanEqualTo extends FmRelativeComparisonBase { public FmLessThanEqualTo(Function a, Function b) { super(a, b); } @Override protected Object compare(double a, double b) { return a <= b ? 1L : 0L; } @Override public Function deepCopy() { return new FmLessThanEqualTo(child(0).deepCopy(), child(1).deepCopy()); } @Override public String operator() { return "<="; } } public static final class FmSwitch extends FunctionImpl { public FmSwitch(Function... formula) { super(formula); } @Override public Object execute(FunctionParameters parameters) { Object expression = child(0).execute(parameters); if (expression == EXECUTION_ERROR) { return EXECUTION_ERROR; } int n = nbChildren(); for (int i = 1; (i + 1) < n; i += 2) { Object oCase = child(i).execute(parameters); if (oCase == EXECUTION_ERROR) { return EXECUTION_ERROR; } if (ColumnValueProcessor.isEqual(expression, oCase)) { return child(i + 1).execute(parameters); } } // Return the else at the end if we have one if(n%2==0){ return child(n-1).execute(parameters); } return null; } @Override public Function deepCopy() { return new FmSwitch(deepCopy(children)); } } public static final class FmFirstNonNull extends FunctionImpl { public FmFirstNonNull(Function... formula) { super(formula); } @Override public Function deepCopy() { return new FmFirstNonNull(deepCopy(children)); } @Override public Object execute(FunctionParameters parameters) { Object [] children = executeChildFormulae(parameters, false); if(children==null){ return Functions.EXECUTION_ERROR; } for(Object child:children){ if(child!=null){ return child; } } return null; } @Override public boolean hasBrackets() { return true; } @Override public String toString() { return toString("firstNonNull"); } } public static final class FmMax extends FunctionImpl { public FmMax(Function... formula) { super(formula); } @Override public Function deepCopy() { return new FmMax(deepCopy(children)); } @Override public Object execute(FunctionParameters parameters) { return maxMin(children, parameters, true); } static Object maxMin(Function[] children, FunctionParameters parameters, boolean isMax) { Object ret = children[0].execute(parameters); if (ret == null || ret == EXECUTION_ERROR) { return EXECUTION_ERROR; } for (int j = 1; j < children.length; j++) { Object val = children[j].execute(parameters); if (val == null || val == EXECUTION_ERROR) { return EXECUTION_ERROR; } Double dCurrent = Numbers.toDouble(ret); Double dVal = Numbers.toDouble(val); if (dCurrent == null || dVal == null) { return EXECUTION_ERROR; } if (isMax) { if (dVal > dCurrent) { ret = val; } } else { if (dVal < dCurrent) { ret = val; } } } return ret; } @Override public boolean hasBrackets() { return true; } @Override public String toString() { return toString("max"); } } public static final class FmMin extends FunctionImpl { public FmMin(Function... formula) { super(formula); } @Override public Function deepCopy() { return new FmMin(deepCopy(children)); } @Override public Object execute(FunctionParameters parameters) { return FmMax.maxMin(children, parameters, false); } @Override public boolean hasBrackets() { return true; } @Override public String toString() { return toString("min"); } } public static final class FmMod extends Fm2ParamBase { public FmMod(Function a, Function b) { super(a, b); } @Override public Function deepCopy() { return new FmMod(child(0).deepCopy(), child(1).deepCopy()); } @Override protected Object execute(Object a, Object b) { Double da = Numbers.toDouble(a); Double db = Numbers.toDouble(b); if (da == null || db == null) { return EXECUTION_ERROR; } long la = da.longValue(); long lb = db.longValue(); if (lb == 0) { return EXECUTION_ERROR; } return la % lb; } @Override public String toString() { return toStringWithChildOp("/"); } } public static final class FmPow extends Fm2ParamBase { public FmPow(Function a, Function b) { super(a, b); } @Override public Function deepCopy() { return new FmMod(child(0).deepCopy(), child(1).deepCopy()); } @Override protected Object execute(Object a, Object b) { Double da = Numbers.toDouble(a); Double db = Numbers.toDouble(b); if (da == null || db == null) { return EXECUTION_ERROR; } return Math.pow(da, db); } @Override public String toString() { return toString("pow"); } } public static final class FmMultiply extends FunctionImpl { public FmMultiply(Function... formulae) { super(formulae); } @Override public Function deepCopy() { return new FmMultiply(child(0).deepCopy(), child(1).deepCopy()); } @Override public Object execute(FunctionParameters parameters) { // treat all as doubles double ret = 1; for (int i = 0; i < children.length; i++) { Object o = child(i).execute(parameters); if (o == EXECUTION_ERROR || o == null) { return EXECUTION_ERROR; } Double d = Numbers.toDouble(o); if (d == null) { return EXECUTION_ERROR; } ret *= d; } return ret; } @Override public String toString() { return toStringWithChildOp("*"); } } public static final class FmNot extends Fm1ParamBase { public FmNot(Function a) { super(a); } @Override public Function deepCopy() { return new FmNot(child(0).deepCopy()); } @Override public String toString() { return "!" + child(0).toString(); } @Override protected double execute(double d) { return d == 1 ? 1 : 0; } @Override protected long execute(long l) { return l == 1 ? 1 : 0; } } public static final class FmNegate extends Fm1ParamBase { public FmNegate(Function a) { super(a); } @Override public Function deepCopy() { return new FmNegate(child(0).deepCopy()); } @Override public String toString() { return "-" + child(0).toString(); } @Override protected double execute(double d) { return -d; } @Override protected long execute(long l) { return -l; } } public static final class FmRandColour extends FunctionImpl { public FmRandColour(Function seed) { super(seed); } @Override public Object execute(FunctionParameters parameters) { Object o = child(0).execute(parameters); if (o == Functions.EXECUTION_ERROR) { return Functions.EXECUTION_ERROR; } if (o == null) { o = new Double(0); } Double d = Numbers.toDouble(o); if (d != null && d.longValue() == d.doubleValue()) { return Colours.getRandomColour(d.longValue()); } // get case-insensitive random colour return Colours.getRandomColour(o.toString()); } @Override public Function deepCopy() { return new FmRandColour(child(0).deepCopy()); } @Override public String toString() { return "randcolour(" + child(0).toString() + ")"; } } public static final class FmRandPalletColour extends FunctionImpl{ private final Random r = new Random(); @Override public Object execute(FunctionParameters parameters) { return Colours.getRandomColorFromPredefinedPallet(r); } @Override public Function deepCopy() { return new FmRandPalletColour(); } } public static final class FmOr extends FunctionImpl { public FmOr(Function a, Function b) { super(a, b); } @Override public Function deepCopy() { return new FmOr(child(0).deepCopy(), child(1).deepCopy()); } @Override public Object execute(FunctionParameters parameters) { // treat all as doubles for (int i = 0; i < children.length; i++) { Object o = child(i).execute(parameters); if (o == EXECUTION_ERROR || o == null) { return EXECUTION_ERROR; } Double d = Numbers.toDouble(o); if (d == null) { // treat null as false d=0.0; } if (d == 1) { return 1L; } } return 0L; } @Override public String toString() { return toStringWithChildOp("||"); } } public static final class FmBitwiseOr extends FunctionImpl { public FmBitwiseOr(Function a, Function b) { super(a, b); } @Override public Function deepCopy() { return new FmBitwiseOr(child(0).deepCopy(), child(1).deepCopy()); } @Override public Object execute(FunctionParameters parameters) { Object [] childVals = executeChildFormulae(parameters, false); if(childVals==null){ return Functions.EXECUTION_ERROR; } Long l1 = Numbers.toLong(childVals[0]); Long l2 = Numbers.toLong(childVals[1]); l1 = l1!=null ? l1:0L; l2 = l2!=null ? l2:0L; return l1 | l2; } @Override public String toString() { return toStringWithChildOp("|"); } } public static final class FmSubtract extends Fm2ParamBase { public FmSubtract(Function a, Function b) { super(a, b); } @Override public Function deepCopy() { return new FmSubtract(child(0).deepCopy(), child(1).deepCopy()); } @Override public String toString() { return toStringWithChildOp("-"); } @Override protected Object execute(Object a, Object b) { Double da = Numbers.toDouble(a); Double db = Numbers.toDouble(b); if (da == null || db == null) { return EXECUTION_ERROR; } return da - db; } } public static final class FmSum extends FunctionImpl { public FmSum(Function... formula) { super(formula); } @Override public Function deepCopy() { return new FmSum(deepCopy(children)); } @Override public Object execute(FunctionParameters parameters) { if (children.length == 0) { return 0.0; } // execute child formulae checking if any null Object[] executed = executeChildFormulae(parameters, true); if (executed == null) { return EXECUTION_ERROR; } // look out for the case where we have images int nbImages = 0; for (int i = 0; i < children.length; i++) { if (BufferedImage.class.isInstance(executed[0])) { nbImages++; } } if (nbImages > 0) { if (nbImages != children.length) { return EXECUTION_ERROR; } // combine images BufferedImage[] images = new BufferedImage[executed.length]; for (int i = 0; i < images.length; i++) { images[i] = (BufferedImage) executed[i]; } BufferedImage ret = ImageUtils.addImages(images); return ret; } else { // treat all as doubles double ret = 0; for (int i = 0; i < children.length; i++) { Double d = Numbers.toDouble(executed[i]); if (d == null) { return EXECUTION_ERROR; } ret += d; } return ret; } } @Override public String toString() { return toStringWithChildOp("+"); } } public static final class FmConcatenate extends FunctionImpl { public FmConcatenate(Function... formula) { super(formula); } @Override public Function deepCopy() { return new FmConcatenate(deepCopy(children)); } @Override public Object execute(FunctionParameters parameters) { if (children.length == 0) { return 0.0; } StringBuilder builder = new StringBuilder(); for (int i = 0; i < children.length; i++) { Object o = child(i).execute(parameters); if (o == EXECUTION_ERROR) { return EXECUTION_ERROR; } // treat null as empty if (o == null) { o = ""; } String s = (String) ColumnValueProcessor.convertToMe(ODLColumnType.STRING, o); if (s != null) { builder.append(s); } } return builder.toString(); } @Override public String toString() { return toStringWithChildOp("&"); } } public static final class FmColourMultiply extends FunctionImpl { public FmColourMultiply(Function colour, Function factor) { super(colour, factor); } @Override public Object execute(FunctionParameters parameters) { Object [] executed =executeChildFormulae(parameters, true); if(executed==null){ return EXECUTION_ERROR; } Color col = Colours.toColour(executed[0]); if (col == null) { return EXECUTION_ERROR; } Double factor = Numbers.toDouble(executed[1]); if (factor == null) { return EXECUTION_ERROR; } return Colours.multiplyNonAlpha(col, factor.floatValue()); } @Override public Function deepCopy() { return new FmColourMultiply(child(0).deepCopy(), child(1).deepCopy()); } } public static class FmAppProperty extends FunctionImpl{ public FmAppProperty(Function param){ super(param); } @Override public Object execute(FunctionParameters parameters) { Object o = child(0).execute(parameters); if(o==Functions.EXECUTION_ERROR){ return Functions.EXECUTION_ERROR; } if(o==null){ return null; } String key = (String)ColumnValueProcessor.convertToMe(ODLColumnType.STRING, o); if(key!=null){ return AppProperties.getString(key); } return null; } @Override public Function deepCopy() { throw new UnsupportedOperationException(); } } public static abstract class FmLerpToDefinedColour extends FunctionImpl{ public FmLerpToDefinedColour(Function colour, Function ammount){ super(colour, ammount); } @Override public Object execute(FunctionParameters parameters) { Object [] vals = executeChildFormulae(parameters, true); if(vals == null){ return Functions.EXECUTION_ERROR; } Color col = (Color)ColumnValueProcessor.convertToMe(ODLColumnType.COLOUR, vals[0]); Double lerp = (Double)ColumnValueProcessor.convertToMe(ODLColumnType.DOUBLE, vals[1]); if(col== null || lerp == null){ return Functions.EXECUTION_ERROR; } Color other = getColour(col); return Colours.lerp(col, other, lerp); } protected abstract Color getColour(Color source); @Override public Function deepCopy() { throw new UnsupportedOperationException(); } } public static final class FmGreyscale extends FmLerpToDefinedColour{ public FmGreyscale(Function colour, Function ammount){ super(colour, ammount); } @Override protected Color getColour(Color source) { return Colours.toGrey(source); } } public static final class FmLighten extends FmLerpToDefinedColour{ public FmLighten(Function colour, Function ammount){ super(colour, ammount); } @Override protected Color getColour(Color source) { return Color.WHITE; } } public static final class FmDarken extends FmLerpToDefinedColour{ public FmDarken(Function colour, Function ammount){ super(colour, ammount); } @Override protected Color getColour(Color source) { return Color.BLACK; } } public static final class FmColour extends FunctionImpl { public FmColour(Function red, Function green, Function blue) { super(red, green, blue); } public FmColour(Function red, Function green, Function blue, Function alpha) { super(red, green, blue, alpha); } @Override public Object execute(FunctionParameters parameters) { int n = children.length; Object[] oarr = new Object[n]; float[] cols = new float[n]; for (int i = 0; i < oarr.length; i++) { oarr[i] = child(i).execute(parameters); if (oarr[i] == null || oarr[i] == EXECUTION_ERROR) { return EXECUTION_ERROR; } Double d = Numbers.toDouble(oarr[i]); if (d == null) { return EXECUTION_ERROR; } if (d < 0) d = 0.0; if (d > 1) d = 1.0; cols[i] = d.floatValue(); } if (n == 3) { return new Color(cols[0], cols[1], cols[2]); } else { return new Color(cols[0], cols[1], cols[2], cols[3]); } } @Override public Function deepCopy() { if (children.length == 3) { return new FmColour(child(0).deepCopy(), child(1).deepCopy(), child(2).deepCopy()); } return new FmColour(child(0).deepCopy(), child(1).deepCopy(), child(2).deepCopy(), child(3).deepCopy()); } } public static abstract class Fm1GeometryParam extends FunctionImpl { public Fm1GeometryParam(Function geometry) { super(geometry); } @Override public Object execute(FunctionParameters parameters) { Object child = child(0).execute(parameters); if (child == null || child == Functions.EXECUTION_ERROR) { return Functions.EXECUTION_ERROR; } ODLGeomImpl geom = (ODLGeomImpl) ColumnValueProcessor.convertToMe(ODLColumnType.GEOM, child); if (geom == null) { // if there is no geometry, return null as (a) the property of a null geometry must obviously // be null and (b) it allows to avoid having to use formula like this: if(geom!=null, centroid(geom),null) return null; } Geometry geometry = geom.getJTSGeometry(); if (geometry == null) { return Functions.EXECUTION_ERROR; } return execute(geometry); } protected abstract Object execute(Geometry geometry); } public static final class FmPostcodeUKFormatUnit extends FmSingleString{ public FmPostcodeUKFormatUnit(Function child) { super(child); } @Override public Function deepCopy() { return new FmPostcodeUKFormatUnit(child(0).deepCopy()); } @Override protected Object execute(String s) { Matcher matcher = UKPostcodes.unitWithWithoutSpaceGroupedForSpace.matcher(s); if(matcher.matches()){ return matcher.group(1) + " " + matcher.group(2); } return null; } } public static abstract class FmAbstractRegExp extends FunctionImpl{ public FmAbstractRegExp(Function...functions) { super(functions); } @Override public Object execute(FunctionParameters parameters) { Object [] childs = executeChildFormulae(parameters, false); if(childs==null){ return Functions.EXECUTION_ERROR; } if(childs[0]==null){ return Functions.EXECUTION_ERROR; } String regExp = (String)ColumnValueProcessor.convertToMe(ODLColumnType.STRING, childs[0]); if(regExp==null){ return Functions.EXECUTION_ERROR; } String str = (String)ColumnValueProcessor.convertToMe(ODLColumnType.STRING, childs[1]); if(str==null){ // no match return 0L; } try { Pattern pattern = Pattern.compile(regExp, Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(str); return execute(parameters,childs, matcher); } catch (Exception e) { return Functions.EXECUTION_ERROR; } } protected abstract Object execute(FunctionParameters parameters, Object [] childs,Matcher matcher); } // public static final class FmRegExpMatchedText extends FmAbstractRegExp{ // // public FmRegExpMatchedText(Function regExp, Function str) { // super(regExp, str); // } // // @Override // public Function deepCopy() { // return new FmRegExpMatches(child(0).deepCopy(), child(1).deepCopy()); // } // // @Override // protected Object execute(FunctionParameters parameters,Object [] childs, Matcher matcher){ // if( matcher.find() ){ // return matcher.group(); // } // else{ // return null; // } // } // // } public static final class FmRegExpMatchedGroup extends FmAbstractRegExp{ public FmRegExpMatchedGroup(Function regExp, Function str, Function indx) { super(regExp, str, indx); } @Override public Function deepCopy() { return new FmRegExpMatches(child(0).deepCopy(), child(1).deepCopy()); } @Override protected Object execute(FunctionParameters parameters,Object [] childs, Matcher matcher){ Long indx = Numbers.toLong(childs[2],false); if(indx==null){ return Functions.EXECUTION_ERROR; } if( matcher.matches() ){ if(indx.intValue() < matcher.groupCount()){ return matcher.group(indx.intValue()+1); } return Functions.EXECUTION_ERROR; } else{ return null; } } } public static final class FmRegExpMatches extends FmAbstractRegExp{ public FmRegExpMatches(Function regExp, Function str) { super(regExp, str); } @Override public Function deepCopy() { return new FmRegExpMatches(child(0).deepCopy(), child(1).deepCopy()); } @Override protected Object execute(FunctionParameters parameters,Object [] childs, Matcher matcher){ return matcher.matches() ? 1L : 0L; } } public static final class FmPostcodeUk extends FmSingleString { private static final Pattern unit = UKPostcodes.unitWithWithoutSpaceGroupedForSpace; // Pattern.compile("(" + UKPostcodes.allUnit + ")", Pattern.CASE_INSENSITIVE); private static final Pattern sector = Pattern.compile("(" + UKPostcodes.allSector + ")", Pattern.CASE_INSENSITIVE); private static final Pattern district = Pattern.compile("(" + UKPostcodes.allDistrict + ")", Pattern.CASE_INSENSITIVE); private static final Pattern area = Pattern.compile("(" + UKPostcodes.area + ")", Pattern.CASE_INSENSITIVE); private final UKPostcodeLevel level; public FmPostcodeUk(UKPostcodeLevel level, Function child) { super(child); this.level = level; } @Override public Function deepCopy() { return new FmPostcodeUk(level, child(0).deepCopy()); } @Override protected Object execute(String s) { if (s == null) { return null; } // try cleaning to readd to the space first if not already unit if(level != UKPostcodeLevel.Unit){ s = Strings.std(s); Matcher ukUnit = unit.matcher(s); if(ukUnit.matches()){ s = ukUnit.group(1) + " " + ukUnit.group(2); } s = s.toUpperCase(); } Pattern pattern = null; switch (level) { case Unit: pattern = unit; break; case Sector: pattern = sector; break; case District: pattern = district; break; case Area: pattern = area; break; } Matcher matcher = pattern.matcher(s); String ret = null; if (matcher.find()) { ret = matcher.group(); // call standardising on the UK postcode ret = UKPostcodes.standardisePostcode(ret, true); } return ret; } @Override public String toString() { return toString("postcodeuk" + level.name().toLowerCase()); } } public static class FmStringDateTimeStamp extends FunctionImpl{ private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss") ; @Override public Object execute(FunctionParameters parameters) { Date date = new Date() ; return dateFormat.format(date); } @Override public Function deepCopy() { return new FmStringDateTimeStamp(); } } public static class FmLineStringFraction extends FunctionImpl{ public FmLineStringFraction(Function geom, Function fraction) { super(geom,fraction); } @Override public Object execute(FunctionParameters parameters) { Object []vals = executeChildFormulae(parameters, true); if(vals==null){ return Functions.EXECUTION_ERROR; } ODLGeom geom = (ODLGeom)ColumnValueProcessor.convertToMe(ODLColumnType.GEOM, vals[0]); Double fraction = (Double)ColumnValueProcessor.convertToMe(ODLColumnType.DOUBLE, vals[1]); if(geom== null || fraction == null){ return Functions.EXECUTION_ERROR; } ODLGeomImpl geomImpl = (ODLGeomImpl)geom; Geometry jtsGeometry = geomImpl.getJTSGeometry(); if(jtsGeometry!=null && LineString.class.isInstance(jtsGeometry)){ return new ODLLoadedGeometry(LinestringFraction.calculateFraction((LineString)jtsGeometry, fraction)); } return null; } @Override public Function deepCopy() { return new FmLineStringFraction(child(0).deepCopy(), child(1).deepCopy()); } } public static class FmLineStringEnd extends FunctionImpl{ public FmLineStringEnd(Function geom) { super(geom); } @Override public Object execute(FunctionParameters parameters) { Object val = child(0).execute(parameters); if(val==null){ return null; } ODLGeom geom = (ODLGeom)ColumnValueProcessor.convertToMe(ODLColumnType.GEOM, val); if(geom== null ){ return null; } ODLGeomImpl geomImpl = (ODLGeomImpl)geom; Geometry jtsGeometry = geomImpl.getJTSGeometry(); if(jtsGeometry!=null && LineString.class.isInstance(jtsGeometry)){ LineString ls = (LineString)jtsGeometry; Coordinate coord = ls.getCoordinateN(ls.getNumPoints()-1); Geometry pnt = new GeometryFactory().createPoint(coord); return new ODLLoadedGeometry(pnt); } return null; } @Override public Function deepCopy() { return new FmLineStringFraction(child(0).deepCopy(), child(1).deepCopy()); } } public static class FmTileFactory extends FunctionImpl{ public FmTileFactory(Function param) { super(param); } @Override public Object execute(FunctionParameters parameters) { Object val = child(0).execute(parameters); if(val == null || val == Functions.EXECUTION_ERROR){ return Functions.EXECUTION_ERROR; } String s =(String) ColumnValueProcessor.convertToMe(ODLColumnType.STRING, val); if(s==null){ return Functions.EXECUTION_ERROR; } StandardisedStringTreeMap<String> map = new StandardisedStringTreeMap<String>(false); String [] split = s.split(","); for(String keyvalue : split){ String[] extraSplit = keyvalue.split("="); for(int i =0 ; i < extraSplit.length ; i++){ extraSplit[i] = extraSplit[i].trim(); } if(extraSplit.length!=2 || extraSplit[0].length()==0){ return Functions.EXECUTION_ERROR; } map.put(extraSplit[0], extraSplit[1]); } BackgroundMapConfig config = new BackgroundMapConfig(map); return BackgroundTileFactorySingleton.createTileFactory(config); } @Override public Function deepCopy() { throw new UnsupportedOperationException(); } } }