/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2008, Open Source Geospatial Foundation (OSGeo) * * This library 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; * version 2.1 of the License. * * This library 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. */ package org.geotools.filter.function; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.geotools.filter.capability.FunctionNameImpl; import org.geotools.util.Converters; import org.opengis.filter.capability.FunctionName; import org.opengis.filter.expression.Expression; import org.opengis.filter.expression.ExpressionVisitor; import org.opengis.filter.expression.Function; import org.opengis.filter.expression.Literal; /** * StringTemplate function, applies a regular expression with capturing groups, and them uses the * groups to build a new string based on the provided template. * <p> * This function expects four expressions in input, the first three of which are mandatory. * <ol> * <li>The string to be matched * <li>A regular expression, possibly using capturing groups * <li>A template using ${0} for the fully matched string, ${n} for the n-th matching group * <li>An optional value returned if the regular expresion does not match the input string * </ol> * * Here are some examples using CQL as the expression language: * <ol> * <li> * <code>stringTemplate('2002-03-01T13:00:00Z', '\d{4}-\d{2}-\d{2}T(\d{2}):\d{2}:\d{2}', '${1}') * will result in <code>13</code> (the only matching group returns the hour of the timestamp) * <li> * <code>stringTemplate('abcd', '\d{4}-\d{2}-\d{2}T(\d{2}):\d{2}:\d{2}', '${1}')</code> will return * <code>null</code> (no match) * <li> * <code>stringTemplate('abcd', '\d{4}-\d{2}-\d{2}T(\d{2}):\d{2}:\d{2}', '${1}', 'default')</code> * will return <code>default</code> (no match, but there is a default value </old> * */ public class StringTemplateFunction implements Function { private final List<Expression> parameters; private Pattern staticPattern; private final Literal fallback; volatile Object[] convertedValues; /** * Make the instance of FunctionName available in * a consistent spot. */ public static final FunctionName NAME = new FunctionNameImpl("stringTemplate", "input", "pattern", "template", "defaultValue"); public StringTemplateFunction() { this.parameters = new ArrayList<Expression>(); this.fallback = null; } public StringTemplateFunction(List<Expression> parameters, Literal fallback) { this.parameters = parameters; this.fallback = fallback; // check for valid structure if (parameters.size() < 3) { throw new IllegalArgumentException( "We need at least 3 input values, the input string, the regular expression, and the template"); } else if (parameters.size() > 4) { throw new IllegalArgumentException("We need at least 3 or 4 input values, " + parameters.size() + " were given instead"); } } public String getName() { return NAME.getName(); } public FunctionName getFunctionName() { return NAME; } public List<Expression> getParameters() { return Collections.unmodifiableList(parameters); } public Object accept(ExpressionVisitor visitor, Object extraData) { return visitor.visit(this, extraData); } public Object evaluate(Object object) { return evaluate(object, Object.class); } public <T> T evaluate(Object object, Class<T> context) { // get the default value String defaultValue; if(parameters.size() == 4) { defaultValue = parameters.get(3).evaluate(object, String.class); } else { defaultValue = null; } // the input string String input = parameters.get(0).evaluate(object, String.class); Pattern pattern = getPattern(object); String template = parameters.get(2).evaluate(object, String.class); String result = defaultValue; if (input != null && template != null && pattern != null) { Matcher matcher = pattern.matcher(input); if (matcher.matches()) { result = applyTemplate(template, matcher); } } if (context != null) { return Converters.convert(result, context); } else { return (T) result; } } private String applyTemplate(String template, Matcher matcher) { String result = template.replace("${0}", matcher.group()); for (int i = 0; i < matcher.groupCount(); i++) { int groupIdx = i + 1; result = result.replace("${" + groupIdx + "}", matcher.group(groupIdx)); } return result; } private Pattern getPattern(Object object) { if (staticPattern != null) { return staticPattern; } // see if we have a static pattern Expression pe = parameters.get(1); if (pe instanceof Literal) { String ps = pe.evaluate(null, String.class); if (ps == null) { staticPattern = null; } else { staticPattern = Pattern.compile(ps); } return staticPattern; } else { // dynamic evaluation String ps = pe.evaluate(object, String.class); if (ps == null) { return null; } else { return Pattern.compile(ps); } } } public Literal getFallbackValue() { return fallback; } }