/* * Copyright (c) 2016, Metron, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Metron, Inc. nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL METRON, INC. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.metsci.glimpse.dnc.geosym; import static com.google.common.base.Objects.equal; import static java.lang.Double.parseDouble; import static java.lang.Float.parseFloat; import static java.lang.Integer.parseInt; import static java.lang.Short.parseShort; import java.util.function.Function; import java.util.regex.Pattern; public class DncGeosymAttributeComparison implements DncGeosymAttributeExpression { public static boolean isNoValueUnparsed(String s) { return s.equals("NULL") || s.isEmpty(); } public static boolean isNoValueString(String v) { return v.isEmpty(); } public static boolean isNoValueDouble(double v) { return Double.isNaN(v); } public static boolean isNoValueFloat(float v) { return Float.isNaN(v); } public static boolean isNoValueInteger(int v) { return v == -2147483648; } public static boolean isNoValueShort(short v) { return v == -32768; } public final String lhsAttr; public final String comparisonOp; public final String rhsUnparsed; public DncGeosymAttributeComparison(String attr, String comparisonOp, String unparsedComparisonValue) { this.lhsAttr = attr; this.comparisonOp = comparisonOp; this.rhsUnparsed = unparsedComparisonValue; } @Override public boolean eval(Function<String,Object> featureAttrs, Function<String,Object> externalAttrs) { Object lhs = (isExternalAttr(lhsAttr) ? externalAttrs.apply(lhsAttr) : featureAttrs.apply(lhsAttr)); if (lhs == null) return evalNull( comparisonOp, isRhsNull(rhsUnparsed, externalAttrs) ); else if (lhs instanceof String) return evalString( (String)lhs, comparisonOp, parseRhsString(rhsUnparsed, externalAttrs) ); else if (lhs instanceof Double) return evalDouble( (Double)lhs, comparisonOp, parseRhsDouble(rhsUnparsed, externalAttrs) ); else if (lhs instanceof Float) return evalFloat( (Float)lhs, comparisonOp, parseRhsFloat(rhsUnparsed, externalAttrs) ); else if (lhs instanceof Integer) return evalInteger( (Integer)lhs, comparisonOp, parseRhsInteger(rhsUnparsed, externalAttrs) ); else if (lhs instanceof Short) return evalShort( (Short)lhs, comparisonOp, parseRhsShort(rhsUnparsed, externalAttrs) ); else throw new RuntimeException("Unrecognized type of lhs: type = " + lhs.getClass().getName()); } public static boolean evalNull(String comparisonOp, boolean isRhsNull) { if ("=".equals(comparisonOp)) return isRhsNull; else if ("<>".equals(comparisonOp)) return !isRhsNull; else return false; } public static boolean evalString(String lhs, String comparisonOp, String rhs) { if ("=".equals(comparisonOp)) return equal(lhs, rhs); else if ("<>".equals(comparisonOp)) return !equal(lhs, rhs); else throw new RuntimeException("Unrecognized string comparison operator: " + comparisonOp); } public static boolean evalDouble(Double lhs, String comparisonOp, Double rhs) { if ("=".equals(comparisonOp)) return equal(lhs, rhs); else if ("<>".equals(comparisonOp)) return !equal(lhs, rhs); else if ("<".equals(comparisonOp)) return lhs < rhs; else if (">".equals(comparisonOp)) return lhs > rhs; else if ("<=".equals(comparisonOp)) return lhs <= rhs; else if (">=".equals(comparisonOp)) return lhs >= rhs; else throw new RuntimeException("Unrecognized double comparison operator: " + comparisonOp); } public static boolean evalFloat(Float lhs, String comparisonOp, Float rhs) { if ("=".equals(comparisonOp)) return equal(lhs, rhs); else if ("<>".equals(comparisonOp)) return !equal(lhs, rhs); else if ("<".equals(comparisonOp)) return lhs < rhs; else if (">".equals(comparisonOp)) return lhs > rhs; else if ("<=".equals(comparisonOp)) return lhs <= rhs; else if (">=".equals(comparisonOp)) return lhs >= rhs; else throw new RuntimeException("Unrecognized float comparison operator: " + comparisonOp); } public static boolean evalInteger(Integer lhs, String comparisonOp, Integer rhs) { if ("=".equals(comparisonOp)) return equal(lhs, rhs); else if ("<>".equals(comparisonOp)) return !equal(lhs, rhs); else if ("<".equals(comparisonOp)) return lhs < rhs; else if (">".equals(comparisonOp)) return lhs > rhs; else if ("<=".equals(comparisonOp)) return lhs <= rhs; else if (">=".equals(comparisonOp)) return lhs >= rhs; else throw new RuntimeException("Unrecognized integer comparison operator: " + comparisonOp); } public static boolean evalShort(Short lhs, String comparisonOp, Short rhs) { if ("=".equals(comparisonOp)) return equal(lhs, rhs); else if ("<>".equals(comparisonOp)) return !equal(lhs, rhs); else if ("<".equals(comparisonOp)) return lhs < rhs; else if (">".equals(comparisonOp)) return lhs > rhs; else if ("<=".equals(comparisonOp)) return lhs <= rhs; else if (">=".equals(comparisonOp)) return lhs >= rhs; else throw new RuntimeException("Unrecognized short comparison operator: " + comparisonOp); } public static boolean isRhsNull(String s, Function<String,Object> externalAttrs) { if (isNoValueUnparsed(s)) { return true; } else if (isExternalAttr(s)) { return (externalAttrs.apply(s) == null); } else if (isNumber(s)) { try { if (isNoValueDouble(parseDouble(s))) return true; } catch (NumberFormatException e) { } try { if (isNoValueFloat(parseFloat(s))) return true; } catch (NumberFormatException e) { } try { if (isNoValueInteger(parseInt(s))) return true; } catch (NumberFormatException e) { } try { if (isNoValueShort(parseShort(s))) return true; } catch (NumberFormatException e) { } return false; } else if (isString(s)) { return isNoValueString(s); } else { throw new RuntimeException("Unrecognized type of rhs: rhs = " + s); } } public static String parseRhsString(String s, Function<String,Object> externalAttrs) { if (isNoValueUnparsed(s)) { return null; } else if (isExternalAttr(s)) { Object v = externalAttrs.apply(s); return (v instanceof String ? (String) v : null); } else { // Strip off the quotes String v = s.substring(1, s.length()-1); return (isNoValueString(v) ? null : v); } } public static Double parseRhsDouble(String s, Function<String,Object> externalAttrs) { if (isNoValueUnparsed(s)) { return null; } else if (isExternalAttr(s)) { Object v = externalAttrs.apply(s); return (v instanceof Number ? ((Number) v).doubleValue() : null); } else { double v = parseDouble(s); return (isNoValueDouble(v) ? null : v); } } public static Float parseRhsFloat(String s, Function<String,Object> externalAttrs) { if (isNoValueUnparsed(s)) { return null; } else if (isExternalAttr(s)) { Object v = externalAttrs.apply(s); return (v instanceof Number ? ((Number) v).floatValue() : null); } else { float v = parseFloat(s); return (isNoValueFloat(v) ? null : v); } } public static Integer parseRhsInteger(String s, Function<String,Object> externalAttrs) { if (isNoValueUnparsed(s)) { return null; } else if (isExternalAttr(s)) { Object v = externalAttrs.apply(s); return (v instanceof Number ? ((Number) v).intValue() : null); } else { int v = parseInt(s); return (isNoValueInteger(v) ? null : v); } } public static Short parseRhsShort(String s, Function<String,Object> externalAttrs) { if (isNoValueUnparsed(s)) { return null; } else if (isExternalAttr(s)) { Object v = externalAttrs.apply(s); return (v instanceof Number ? ((Number) v).shortValue() : null); } else { short v = parseShort(s); return (isNoValueShort(v) ? null : v); } } public static boolean isString(String s) { return (s.startsWith("\"") && s.endsWith("\"")); } public static boolean isExternalAttr(String s) { return (!isNoValueUnparsed(s) && !isNumber(s) && !isString(s) && s.length() == 4); } public static boolean isNumber(String s) { return numberPattern.matcher(s).matches(); } public static final Pattern numberPattern = Pattern.compile(numberRegex()); /** * Regexp from the javadoc of {@link Double#valueOf(String)}. */ public static String numberRegex() { final String Digits = "(\\p{Digit}+)"; final String HexDigits = "(\\p{XDigit}+)"; // An exponent is 'e' or 'E' followed by an optionally signed decimal integer final String Exp = "[eE][+-]?"+Digits; return ("[\\x00-\\x20]*"+ // Optional leading "whitespace" "[+-]?(" + // Optional sign character "NaN|" + // "NaN" string "Infinity|" + // "Infinity" string // A decimal floating-point string representing a finite positive // number without a leading sign has at most five basic pieces: // Digits . Digits ExponentPart FloatTypeSuffix // // Since this method allows integer-only strings as input // in addition to strings of floating-point literals, the // two sub-patterns below are simplifications of the grammar // productions from the Java Language Specification, 2nd // edition, section 3.10.2. // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt "((("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+ // . Digits ExponentPart_opt FloatTypeSuffix_opt "(\\.("+Digits+")("+Exp+")?)|"+ // Hexadecimal strings "((" + // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt "(0[xX]" + HexDigits + "(\\.)?)|" + // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" + ")[pP][+-]?" + Digits + "))" + "[fFdD]?))" + "[\\x00-\\x20]*");// Optional trailing "whitespace" } @Override public String toString() { return lhsAttr + comparisonOp + rhsUnparsed; } }