/* * Copyright (C) 2013, VistaTEC or third-party contributors as indicated * by the @author tags or express copyright attribution statements applied by * the authors. All third-party contributions are distributed under license by * VistaTEC. * * This file is part of Ocelot. * * Ocelot is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Ocelot is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, write to: * * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 * USA * * Also, see the full LGPL text here: <http://www.gnu.org/copyleft/lesser.html> */ package com.vistatec.ocelot.rules; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Matchers { public static RegexMatcher regex(String pattern) { RegexMatcher m = new RegexMatcher(); m.setPattern(pattern); return m; } public static NumericMatcher numeric(double lowerBound, double upperBound) { NumericMatcher m = new NumericMatcher(); m.setLowerBound(lowerBound); m.setUpperBound(upperBound); return m; } public static class RegexMatcher implements DataCategoryField.Matcher { private Pattern pattern; @Override public boolean validatePattern(String s) { return (getPattern(s) != null); } @Override public void setPattern(String s) { pattern = getPattern(s); } private Pattern getPattern(String s) { try { return Pattern.compile(s); } catch (PatternSyntaxException e) { return null; } } @Override public boolean matches(Object value) { if (pattern == null) { throw new IllegalStateException("setPattern() was not called"); } if (!(value instanceof String)) return false; String s = (String)value; return pattern.matcher(s).matches(); } @Override public String toString() { return "RegexMatcher(" + pattern + ")"; } @Override public boolean equals(Object o) { if (o == this) return true; if (o == null || !(o instanceof RegexMatcher)) return false; RegexMatcher m = (RegexMatcher)o; if (pattern == null && m.pattern == null) return true; if (pattern == null || m.pattern == null) return false; // Pattern doesn't override equals()! Compare the string // representations instead. return pattern.toString().equals(m.pattern.toString()); } @Override public int hashCode() { return pattern.toString().hashCode(); } } // Matches bounded ranges within 0-100 // Handles syntax like: // [min]-[max] // Bounds are inclusive. public static class NumericMatcher implements DataCategoryField.Matcher { private static Logger LOG = LoggerFactory.getLogger(NumericMatcher.class); private double lowerBound = -1, upperBound = -1; @Override public boolean validatePattern(String pattern) { Values numValues = null; try { numValues = getValues(pattern); } catch (NumberFormatException e) { LOG.error("Unaccepted Numeric Matcher Syntax: "+pattern, e); } return (numValues != null); } public double getLowerBound() { return lowerBound; } public double getUpperBound() { return upperBound; } public void setLowerBound(double d) { this.lowerBound = d; } public void setUpperBound(double d) { this.upperBound = d; } @Override public void setPattern(String pattern) { Values v = getValues(pattern); if (v == null) { // XXX This is very strange LOG.error("Could not create rule", new IllegalArgumentException( "Unaccepted Numeric Matcher Syntax: "+pattern)); } lowerBound = v.min; upperBound = v.max; } private static final Pattern VALUE_PATTERN = Pattern.compile("^([0-9]+(?:\\.[0-9]+)?)\\s*-\\s*([0-9]+(?:\\.[0-9]+)?)$"); private Values getValues(String pattern) throws NumberFormatException { pattern = pattern.trim(); Matcher m = VALUE_PATTERN.matcher(pattern); if (!m.find()) { return null; } Values v = new Values(); v.min = Double.valueOf(m.group(1)); v.max = Double.valueOf(m.group(2)); return v; } static class Values { double min; double max; } @Override public boolean matches(Object value) { if (lowerBound == -1 || upperBound == -1) { throw new IllegalStateException("setPattern() was not called"); } if (value instanceof Integer) { Integer v = (Integer) value; return lowerBound == upperBound ? v == lowerBound : (v >= lowerBound && v < upperBound); } if (value instanceof Double) { Double v = (Double) value; return lowerBound == upperBound ? v == lowerBound : (v >= lowerBound && v < upperBound); } return false; } @Override public String toString() { return "NumericMatcher(" + lowerBound + ", " + upperBound + ")"; } @Override public boolean equals(Object o) { if (o == this) return true; if (o == null || !(o instanceof NumericMatcher)) return false; NumericMatcher m = (NumericMatcher)o; return lowerBound == m.lowerBound && upperBound == m.upperBound; } @Override public int hashCode() { return Objects.hash(lowerBound, upperBound); } } }