package nl.ipo.cds.etl.filtering; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import javax.xml.namespace.QName; import nl.ipo.cds.domain.AttributeExpression; import nl.ipo.cds.domain.FeatureType; import nl.ipo.cds.domain.FilterExpression; import nl.ipo.cds.domain.OperatorExpression; import nl.ipo.cds.domain.ValueExpression; import org.deegree.commons.tom.TypedObjectNode; import org.deegree.filter.Expression; import org.deegree.filter.Filter; import org.deegree.filter.MatchAction; import org.deegree.filter.Operator; import org.deegree.filter.OperatorFilter; import org.deegree.filter.comparison.BinaryComparisonOperator; import org.deegree.filter.comparison.PropertyIsEqualTo; import org.deegree.filter.comparison.PropertyIsGreaterThan; import org.deegree.filter.comparison.PropertyIsGreaterThanOrEqualTo; import org.deegree.filter.comparison.PropertyIsLessThan; import org.deegree.filter.comparison.PropertyIsLessThanOrEqualTo; import org.deegree.filter.comparison.PropertyIsLike; import org.deegree.filter.comparison.PropertyIsNotEqualTo; import org.deegree.filter.comparison.PropertyIsNull; import org.deegree.filter.expression.Literal; import org.deegree.filter.expression.ValueReference; import org.deegree.filter.logical.And; import org.deegree.filter.logical.Not; import org.deegree.filter.logical.Or; public class FilterFactory { private final FeatureType featureType; public FilterFactory (final FeatureType featureType) { this.featureType = featureType; } public Filter createFilter (final FilterExpression expression) { if (!(expression instanceof OperatorExpression)) { throw new IllegalArgumentException ("expression must be an instance of FilterExpression"); } return createFilter ((OperatorExpression)expression); } public Filter createFilter (final OperatorExpression inputFilter) { final Operator rootOperator = createOperator (inputFilter); return new OperatorFilter (rootOperator); } private Operator createOperator (final OperatorExpression expression) { final List<FilterExpression> inputs = expression.getInputs (); switch (expression.getOperatorType ()) { case AND: return new And (createChildrenArray (inputs)); case OR: return new Or (createChildrenArray (inputs)); case EQUALS: return createBinaryComparisonOperator (expression, PropertyIsEqualTo.class); case GREATER_THAN: return createBinaryComparisonOperator (expression, PropertyIsGreaterThan.class); case GREATER_THAN_EQUAL: return createBinaryComparisonOperator (expression, PropertyIsGreaterThanOrEqualTo.class); case LESS_THAN: return createBinaryComparisonOperator (expression, PropertyIsLessThan.class); case LESS_THAN_EQUAL: return createBinaryComparisonOperator (expression, PropertyIsLessThanOrEqualTo.class); case NOT_EQUALS: return createBinaryComparisonOperator (expression, PropertyIsNotEqualTo.class); case LIKE: return createLikeOperator (expression); case IN: return createInOperator (expression); case NOT_NULL: return createNotNullOperator (expression); default: throw new IllegalArgumentException (String.format ("Invalid operator type `%s`", expression.getOperatorType ())); } } /** * Turns the given list of filter expressions into an array of Operators. * * @param children The list of filter expressions to convert. * @return An array of operators */ private Operator[] createChildrenArray (final List<FilterExpression> children) { final List<Operator> operators = new ArrayList<Operator> (); for (final FilterExpression childExpression: children) { if (!(childExpression instanceof OperatorExpression)) { throw new IllegalArgumentException ("Only operator expressions can be used as the child of a logical expression"); } operators.add (createOperator ((OperatorExpression)childExpression)); } return operators.toArray (new Operator[0]); } /** * Turns the given operator expression into a binary comparison operator of the given type. * * @param operatorExpression The expression to turn into a comparison. * @return A binary comparison operator of the given type. */ private BinaryComparisonOperator createBinaryComparisonOperator (final OperatorExpression operatorExpression, final Class<? extends BinaryComparisonOperator> operatorClass) { final Constructor<? extends BinaryComparisonOperator> constructor; final List<FilterExpression> inputs = operatorExpression.getInputs (); // Check preconditions: if (inputs == null || inputs.size () != 2) { throw new IllegalArgumentException (String.format ("Binary comparison operator expects exactly two inputs")); } if (!(inputs.get (0) instanceof AttributeExpression)) { throw new IllegalArgumentException (String.format ("Binary comparison operator expects the first input to be of type AttributeExpression")); } if (!(inputs.get (1) instanceof ValueExpression)) { throw new IllegalArgumentException (String.format ("Binary comparison operator expects the second input to be of type ValueExpression")); } // Locate the constructor: try { constructor = operatorClass.getConstructor (Expression.class, Expression.class, Boolean.class, MatchAction.class); } catch (NoSuchMethodException e) { throw new IllegalArgumentException (String.format ("Comparison operator class `%s` doesn't have the expected constructor", operatorClass.getCanonicalName ())); } catch (SecurityException e) { throw new IllegalArgumentException (String.format ("Comparison operator class `%s` is not accessible", operatorClass.getCanonicalName ())); } try { return constructor.newInstance ( createValueReference ((AttributeExpression)inputs.get (0)), createLiteral ((ValueExpression)inputs.get (1)), operatorExpression.isCaseSensitive (), MatchAction.ANY ); } catch (InstantiationException e) { throw new RuntimeException (e); } catch (IllegalAccessException e) { throw new RuntimeException (e); } catch (IllegalArgumentException e) { throw e; } catch (InvocationTargetException e) { throw new RuntimeException (e); } } /** * Turns the given expression into a PropertyIsLike operator. * * @param expression The expression. * @return An instance of PropertyIsLike. */ private PropertyIsLike createLikeOperator (final OperatorExpression expression) { final List<FilterExpression> inputs = expression.getInputs (); // Check preconditions: if (inputs == null || inputs.size () != 2) { throw new IllegalArgumentException ("The like operator requires two inputs"); } if (!(inputs.get (0) instanceof AttributeExpression)) { throw new IllegalArgumentException ("The first argument to the like operator must be a AttributeExpression"); } if (!(inputs.get (1) instanceof ValueExpression)) { throw new IllegalArgumentException ("The second argument to the like operator must be a ValueExpression"); } return new PropertyIsLike ( createValueReference ((AttributeExpression)inputs.get (0)), createLiteral ((ValueExpression)inputs.get (1)), "*", "?", "\\", expression.isCaseSensitive (), MatchAction.ANY ); } /** * Turns the given operator into a sequence of operators that are equivalent in semantics * to the 'in' operator. The first input expression must be an attribute reference and * the second input is assumed to be a list of values. * * @param expression The expression * @return The Or operator representing this expression. */ private Or createInOperator (final OperatorExpression expression) { final List<FilterExpression> inputs = expression.getInputs (); // Check preconditions: if (inputs == null || inputs.size () != 2) { throw new IllegalArgumentException ("The `in` operator requires two inputs"); } if (!(inputs.get (0) instanceof AttributeExpression)) { throw new IllegalArgumentException ("The first argument to the in operator must be a AttributeExpression"); } if (!(inputs.get (1) instanceof ValueExpression)) { throw new IllegalArgumentException ("The second argument to the in operator must be a ValueExpression"); } if (((ValueExpression)inputs.get (1)).getStringValue () == null) { throw new IllegalArgumentException ("In operator needs a value"); } // Create values: final String[] values = ((ValueExpression)inputs.get (1)).getStringValue ().split (","); final List<Operator> valueOperators = new ArrayList<Operator> (); final ValueReference valueReference = createValueReference ((AttributeExpression)inputs.get (0)); for (final String value: values) { final String trimmedValue = value.trim (); valueOperators.add (new PropertyIsEqualTo ( valueReference, new Literal<TypedObjectNode> (trimmedValue), expression.isCaseSensitive (), MatchAction.ANY )); } return new Or (valueOperators.toArray (new Operator[0])); } /** * Turns the given expression into a Not Null expression (combination of not and propertyIsNull). * * @param expression * @return A not expression that contains a propertyIsNull expression. */ private Not createNotNullOperator (final OperatorExpression expression) { final List<FilterExpression> inputs = expression.getInputs (); // Check preconditions: if (inputs == null || inputs.size () < 1 || inputs.size () > 2) { throw new IllegalArgumentException ("The not null operator must have one input"); } if (!(inputs.get (0) instanceof AttributeExpression)) { throw new IllegalArgumentException ("The first argument to the not null operator must be a AttributeExpression"); } // Get the value reference: final ValueReference valueReference = createValueReference ((AttributeExpression)inputs.get (0)); return new Not (new PropertyIsNull (valueReference, MatchAction.ANY)); } /** * Creates a value reference based on the given AttributeExpression. * * @param expression * @return A ValueReference for the requested attributes. */ private ValueReference createValueReference (final AttributeExpression expression) { return new ValueReference (new QName ( featureType.getName ().getNamespace (), expression.getAttributeName () )); } /** * Creates a literal based on the given ValueExpression. * * @param expression The expression * @return A literal instance containg the value in the expression. */ private Literal<?> createLiteral (final ValueExpression expression) { return new Literal<TypedObjectNode> (expression.getStringValue ()); } }