/* * Copyright 2015 by Thomas Lottermann * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package notaql.model.predicate; import notaql.datamodel.Value; import notaql.datamodel.fixation.Fixation; import notaql.evaluation.ValueEvaluationResult; import notaql.evaluation.EvaluatorService; import notaql.model.EvaluationException; import notaql.model.path.InputPath; import notaql.model.vdata.InputVData; import notaql.model.vdata.VData; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; /** * Created by thomas on 03.12.14. */ public class RelationalPredicate implements Predicate { private static final long serialVersionUID = 2348526499584146893L; private VData left; private VData right; private Operator operator; public RelationalPredicate(VData left, VData right, Operator operator) { this.left = left; this.right = right; this.operator = operator; } /** * Evaluates the relative predicate by evaluating left and right. If there is a single result, evaluate next * level for no group. * <p> * TODO: some of these assumptions might be too strict * * @param step * @param contextFixation * @return */ @Override public boolean evaluate(ValueEvaluationResult step, Fixation contextFixation) { // TODO: Q&D hack // we must look for a inputVData since the path there needs the current step in order to evaluate relative paths final List<ValueEvaluationResult> leftResults; if (left instanceof InputVData) { final InputPath path = ((InputVData) left).getPath(); if (path.isRelative()) { leftResults = path.evaluate(step, contextFixation); } else { leftResults = path.evaluate(contextFixation); } } else { leftResults = EvaluatorService.getInstance().evaluate(left, contextFixation); } if (leftResults.size() == 0) return false; // we must look for a inputVData since the path there needs the current step in order to evaluate relative paths final List<ValueEvaluationResult> rightResults; if (right instanceof InputVData) { final InputPath path = ((InputVData) right).getPath(); if (path.isRelative()) { rightResults = path.evaluate(step, contextFixation); } else { rightResults = path.evaluate(contextFixation); } } else { rightResults = EvaluatorService.getInstance().evaluate(right, contextFixation); } if (rightResults.size() == 0) return false; if (leftResults.size() > 1 && rightResults.size() > 1) throw new EvaluationException("Relative predicate evaluation resulted in multiple values being compared. This is not supported (YET?)"); final List<Value> leftValues; if(EvaluatorService.getInstance().canReduce(left)) { leftValues = leftResults .stream() .map(l -> EvaluatorService.getInstance() .reduce( left, EvaluatorService.getInstance().createIdentity(left), l.getValue() ) ) .collect(Collectors.toList()); } else { leftValues = leftResults.stream().map(ValueEvaluationResult::getValue).collect(Collectors.toList()); } final List<Value> rightValues; if(EvaluatorService.getInstance().canReduce(right)) { rightValues = rightResults .stream() .map(r -> EvaluatorService.getInstance() .reduce( right, EvaluatorService.getInstance().createIdentity(right), r.getValue() ) ) .collect(Collectors.toList()); } else { rightValues = rightResults.stream().map(ValueEvaluationResult::getValue).collect(Collectors.toList()); } return compare(leftValues, rightValues); } private boolean compare(List<Value> leftValues, List<Value> rightValues) { // extract the things that shall be compared // TODO: This behaviour is an "any" behaviour. in case there are multiple results: if any of them fit it's considered true. final boolean leftScalar = leftValues.size() == 1; final Value scalar = leftScalar ? leftValues.get(0) : rightValues.get(0); final List<Value> values = leftScalar ? rightValues : leftValues; boolean comparable = false; final List<Comparable<?>> comparableValues = new LinkedList<>(); if (scalar instanceof Comparable<?>) { values .stream() .filter(v -> v instanceof Comparable<?> && v.getClass().equals(scalar.getClass())) .forEach(v -> comparableValues.add((Comparable<?>) v)); if (comparableValues.size() > 0) comparable = true; } if (!comparable && (operator == Operator.LT || operator == Operator.LTEQ || operator == Operator.GT || operator == Operator.GTEQ)) { return false; } if ((operator == Operator.LT && leftScalar) || (operator == Operator.GT && !leftScalar)) { return comparableValues.stream().anyMatch(v -> ((Comparable) scalar).compareTo(v) < 0); } if ((operator == Operator.LTEQ && leftScalar) || (operator == Operator.GTEQ && !leftScalar)) { return comparableValues.stream().anyMatch(v -> ((Comparable) scalar).compareTo(v) <= 0); } if ((operator == Operator.GT) || (operator == Operator.LT)) { return comparableValues.stream().anyMatch(v -> ((Comparable) scalar).compareTo(v) > 0); } if ((operator == Operator.GTEQ) || (operator == Operator.LTEQ)) { return comparableValues.stream().anyMatch(v -> ((Comparable) scalar).compareTo(v) >= 0); } if (operator == Operator.EQ) { return values.stream().anyMatch(scalar::equals); } if (operator == Operator.NEQ) { return values.stream().anyMatch(v -> !scalar.equals(v)); } throw new EvaluationException("Unknown operator: " + operator); } public VData getLeft() { return left; } public VData getRight() { return right; } public Operator getOperator() { return operator; } @Override public String toString() { return left.toString() + " " + operator.toString() + " " + right.toString(); } public enum Operator { LT, LTEQ, GT, GTEQ, EQ, NEQ } }