Start implementing Leviathan aggregates (JENA-507) - Adds initial implementations for all(), any() and none() - Reworks AccumulatorExpr slightly to be more extensible - Adds an AccumulatorShortCircuitExpr for aggregates that support short circuiting
Project: http://git-wip-us.apache.org/repos/asf/jena/repo Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/7da685a9 Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/7da685a9 Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/7da685a9 Branch: refs/heads/JENA-507 Commit: 7da685a9434844653e283efb499d2400800d1fdd Parents: f7a660e Author: Rob Vesse <[email protected]> Authored: Mon Sep 7 14:50:05 2015 +0100 Committer: Rob Vesse <[email protected]> Committed: Mon Sep 7 14:50:05 2015 +0100 ---------------------------------------------------------------------- .../sparql/expr/aggregate/AccumulatorExpr.java | 133 +++++++++++++------ .../aggregate/AccumulatorShortCircuitExpr.java | 85 ++++++++++++ .../jena/sparql/expr/aggregate/AggAvg.java | 3 +- .../library/leviathan/aggregates/AggAll.java | 115 ++++++++++++++++ .../library/leviathan/aggregates/AggAny.java | 112 ++++++++++++++++ .../library/leviathan/aggregates/AggNone.java | 115 ++++++++++++++++ 6 files changed, 520 insertions(+), 43 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jena/blob/7da685a9/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AccumulatorExpr.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AccumulatorExpr.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AccumulatorExpr.java index b94c98b..1203d6a 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AccumulatorExpr.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AccumulatorExpr.java @@ -18,58 +18,109 @@ package org.apache.jena.sparql.expr.aggregate; -import org.apache.jena.sparql.engine.binding.Binding ; -import org.apache.jena.sparql.expr.Expr ; -import org.apache.jena.sparql.expr.ExprEvalException ; -import org.apache.jena.sparql.expr.NodeValue ; -import org.apache.jena.sparql.function.FunctionEnv ; +import org.apache.jena.sparql.engine.binding.Binding; +import org.apache.jena.sparql.expr.Expr; +import org.apache.jena.sparql.expr.ExprEvalException; +import org.apache.jena.sparql.expr.NodeValue; +import org.apache.jena.sparql.function.FunctionEnv; /** Accumulator that passes down every value of an expression */ -abstract class AccumulatorExpr implements Accumulator -{ - private long count = 0 ; - protected long errorCount = 0 ; - private final Expr expr ; - - protected AccumulatorExpr(Expr expr) - { - this.expr = expr ; +public abstract class AccumulatorExpr implements Accumulator { + private long count = 0; + private long errorCount = 0; + private final Expr expr; + + protected AccumulatorExpr(Expr expr) { + this.expr = expr; } - + @Override - final public void accumulate(Binding binding, FunctionEnv functionEnv) - { - try { - NodeValue nv = expr.eval(binding, functionEnv) ; - accumulate(nv, binding, functionEnv) ; - count++ ; - } catch (ExprEvalException ex) - { - errorCount++ ; - accumulateError(binding, functionEnv) ; + public void accumulate(Binding binding, FunctionEnv functionEnv) { + try { + NodeValue nv = expr.eval(binding, functionEnv); + accumulate(nv, binding, functionEnv); + incrementCount(); + } catch (ExprEvalException ex) { + incrementErrorCount(); + accumulateError(binding, functionEnv); } } - - // Count(?v) is different + /** + * Gets the expression + * @return Expression + */ + protected final Expr getExpr() { + return expr; + } + + /** + * Increments the count of valid expression evaluations + */ + protected final void incrementCount() { + count++; + } + + /** + * Increments the count of error expression evaluations + */ + protected final void incrementErrorCount() { + errorCount++; + } + + /** + * Gets the value of the accumulator + */ @Override - public NodeValue getValue() - { - if ( errorCount == 0 ) - return getAccValue() ; - return null ; + public NodeValue getValue() { + if (errorCount == 0) + return getAccValue(); + return getErrorValue(); + } + + /** + * Gets the value that is returned in the event of any errors (defaults to + * {@code null}) + * + * @return Error value + */ + protected NodeValue getErrorValue() { + return null; + } + + /** + * Gets the count of valid expression evaluations + * + * @return Valid expression evaluation count + */ + protected final long getCount() { + return count; + } + + /** + * Gets the count of expression evaluation errors encountered + * + * @return Expression evaluation error count + */ + protected final long getErrorCount() { + return errorCount; } - protected long getErrorCount() { return errorCount ; } - /** Called if no errors to get the accumulated result */ - protected abstract NodeValue getAccValue() ; + protected abstract NodeValue getAccValue(); + + /** + * Called when the expression being aggregated evaluates OK + * <p> + * Can throw {@link ExprEvalException} in which case the + * {@link #accumulateError} method is called + * </p> + */ + protected abstract void accumulate(NodeValue nv, Binding binding, FunctionEnv functionEnv); - /** Called when the expression beeing aggregated evaluates OK. - * Can throw ExprEvalException - in which case the accumulateError is called */ - protected abstract void accumulate(NodeValue nv, Binding binding, FunctionEnv functionEnv) ; - /** Called when an evaluation of the expression causes an error - * or when the accumulation step throws ExprEvalException + /** + * Called when an evaluation of the expression causes an error or when the + * accumulation step throws {@link ExprEvalException} */ - protected abstract void accumulateError(Binding binding, FunctionEnv functionEnv) ; + protected abstract void accumulateError(Binding binding, FunctionEnv functionEnv); } http://git-wip-us.apache.org/repos/asf/jena/blob/7da685a9/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AccumulatorShortCircuitExpr.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AccumulatorShortCircuitExpr.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AccumulatorShortCircuitExpr.java new file mode 100644 index 0000000..cbaf053 --- /dev/null +++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AccumulatorShortCircuitExpr.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.jena.sparql.expr.aggregate; + +import org.apache.jena.sparql.engine.binding.Binding; +import org.apache.jena.sparql.expr.Expr; +import org.apache.jena.sparql.expr.ExprEvalException; +import org.apache.jena.sparql.expr.NodeValue; +import org.apache.jena.sparql.function.FunctionEnv; + +/** + * Accumulator that passes down the value of an expression but that allows for + * short circuiting. + * <p> + * Short circuiting handles the case where an accumulator can determine the + * final value ahead of seeing all the solutions. In this case it can indicate + * as such by calling the {@link #shortCircuit(NodeValue)} method after which + * point the evaluation of the expression is skipped for further solutions + * </p> + * + */ +public abstract class AccumulatorShortCircuitExpr extends AccumulatorExpr { + private boolean shortCircuited = false; + private NodeValue shortCircuitValue; + + protected AccumulatorShortCircuitExpr(Expr expr) { + super(expr); + } + + @Override + public void accumulate(Binding binding, FunctionEnv functionEnv) { + // No need to do any further work if already short circuited + if (shortCircuited) + return; + + try { + NodeValue nv = this.getExpr().eval(binding, functionEnv); + accumulate(nv, binding, functionEnv); + this.incrementCount(); + } catch (ExprEvalException ex) { + this.incrementErrorCount(); + accumulateError(binding, functionEnv); + } + } + + // Count(?v) is different + @Override + public NodeValue getValue() { + if (shortCircuited) + return shortCircuitValue; + if (this.getErrorCount() == 0) + return getAccValue(); + return getErrorValue(); + } + + /** + * Called when the derived class has been able to determine the final + * aggregate value without needing to process further values. Once set + * further expression evaluation is skipped and the final value will be the + * value given here + * + * @param value + * Final aggregate value + */ + protected final void shortCircuit(NodeValue value) { + this.shortCircuited = true; + this.shortCircuitValue = value; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/7da685a9/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggAvg.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggAvg.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggAvg.java index 57c32c7..d16bd63 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggAvg.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/aggregate/AggAvg.java @@ -100,8 +100,7 @@ public class AggAvg extends AggregatorBase public NodeValue getAccValue() { if ( count == 0 ) return noValuesToAvg ; - if ( super.errorCount != 0 ) - //throw new ExprEvalException("avg: error in group") ; + if ( this.getErrorCount() != 0 ) return null ; NodeValue nvCount = NodeValue.makeInteger(count) ; return XSDFuncOp.numDivide(total, nvCount) ; http://git-wip-us.apache.org/repos/asf/jena/blob/7da685a9/jena-arq/src/main/java/org/apache/jena/sparql/function/library/leviathan/aggregates/AggAll.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/leviathan/aggregates/AggAll.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/leviathan/aggregates/AggAll.java new file mode 100644 index 0000000..1f499d6 --- /dev/null +++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/leviathan/aggregates/AggAll.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.jena.sparql.function.library.leviathan.aggregates; + +import org.apache.jena.graph.Node; +import org.apache.jena.sparql.engine.binding.Binding; +import org.apache.jena.sparql.expr.Expr; +import org.apache.jena.sparql.expr.ExprList; +import org.apache.jena.sparql.expr.NodeValue; +import org.apache.jena.sparql.expr.aggregate.Accumulator; +import org.apache.jena.sparql.expr.aggregate.AccumulatorShortCircuitExpr; +import org.apache.jena.sparql.expr.aggregate.Aggregator; +import org.apache.jena.sparql.expr.aggregate.AggregatorBase; +import org.apache.jena.sparql.expr.nodevalue.NodeValueBoolean; +import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp; +import org.apache.jena.sparql.function.FunctionEnv; +import org.apache.jena.sparql.function.library.leviathan.LeviathanConstants; + +/** + * All aggregate, expression must evaluate to true for all solutions + */ +public class AggAll extends AggregatorBase { + + /** + * @param expr + * Expression + */ + protected AggAll(Expr expr) { + super(LeviathanConstants.LeviathanFunctionLibraryURI + "all", false, expr); + } + + @Override + public Aggregator copy(ExprList exprs) { + return new AggAll(exprs.get(0)); + } + + @Override + public boolean equals(Aggregator other, boolean bySyntax) { + if (other == null) + return false; + if (this == other) + return true; + if (!(other instanceof AggAll)) + return false; + AggAll agg = (AggAll) other; + return getExpr().equals(agg.getExpr(), bySyntax); + } + + @Override + public Accumulator createAccumulator() { + return new AccAll(this.getExpr()); + } + + @Override + public Node getValueEmpty() { + return NodeValue.TRUE.asNode(); + } + + @Override + public int hashCode() { + // TODO Auto-generated method stub + return 0; + } + + private static class AccAll extends AccumulatorShortCircuitExpr { + + /** + * @param expr + */ + protected AccAll(Expr expr) { + super(expr); + } + + @Override + protected NodeValue getAccValue() { + return NodeValue.TRUE; + } + + @Override + protected NodeValue getErrorValue() { + return NodeValue.FALSE; + } + + @Override + protected void accumulate(NodeValue nv, Binding binding, FunctionEnv functionEnv) { + // If true can't short circuit yet + if (XSDFuncOp.booleanEffectiveValue(nv)) + return; + + // Otherwise can short circuit + this.shortCircuit(NodeValue.FALSE); + } + + @Override + protected void accumulateError(Binding binding, FunctionEnv functionEnv) { + this.shortCircuit(NodeValue.FALSE); + } + + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/7da685a9/jena-arq/src/main/java/org/apache/jena/sparql/function/library/leviathan/aggregates/AggAny.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/leviathan/aggregates/AggAny.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/leviathan/aggregates/AggAny.java new file mode 100644 index 0000000..a462098 --- /dev/null +++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/leviathan/aggregates/AggAny.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.jena.sparql.function.library.leviathan.aggregates; + +import org.apache.jena.graph.Node; +import org.apache.jena.sparql.engine.binding.Binding; +import org.apache.jena.sparql.expr.Expr; +import org.apache.jena.sparql.expr.ExprList; +import org.apache.jena.sparql.expr.NodeValue; +import org.apache.jena.sparql.expr.aggregate.Accumulator; +import org.apache.jena.sparql.expr.aggregate.AccumulatorShortCircuitExpr; +import org.apache.jena.sparql.expr.aggregate.Aggregator; +import org.apache.jena.sparql.expr.aggregate.AggregatorBase; +import org.apache.jena.sparql.expr.nodevalue.NodeValueBoolean; +import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp; +import org.apache.jena.sparql.function.FunctionEnv; +import org.apache.jena.sparql.function.library.leviathan.LeviathanConstants; + +/** + * All aggregate, expression must evaluate to true for at least one solution + */ +public class AggAny extends AggregatorBase { + + /** + * @param expr + * Expression + */ + protected AggAny(Expr expr) { + super(LeviathanConstants.LeviathanFunctionLibraryURI + "any", false, expr); + } + + @Override + public Aggregator copy(ExprList exprs) { + return new AggAny(exprs.get(0)); + } + + @Override + public boolean equals(Aggregator other, boolean bySyntax) { + if (other == null) + return false; + if (this == other) + return true; + if (!(other instanceof AggAny)) + return false; + AggAny agg = (AggAny) other; + return getExpr().equals(agg.getExpr(), bySyntax); + } + + @Override + public Accumulator createAccumulator() { + return new AccAny(this.getExpr()); + } + + @Override + public Node getValueEmpty() { + return NodeValue.TRUE.asNode(); + } + + @Override + public int hashCode() { + // TODO Auto-generated method stub + return 0; + } + + private static class AccAny extends AccumulatorShortCircuitExpr { + + /** + * @param expr + */ + protected AccAny(Expr expr) { + super(expr); + } + + @Override + protected NodeValue getAccValue() { + return NodeValue.FALSE; + } + + @Override + protected NodeValue getErrorValue() { + return NodeValue.FALSE; + } + + @Override + protected void accumulate(NodeValue nv, Binding binding, FunctionEnv functionEnv) { + // If true can short circuit yet + if (XSDFuncOp.booleanEffectiveValue(nv)) + this.shortCircuit(NodeValue.TRUE); + } + + @Override + protected void accumulateError(Binding binding, FunctionEnv functionEnv) { + // Can't short circuit errors + } + + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/7da685a9/jena-arq/src/main/java/org/apache/jena/sparql/function/library/leviathan/aggregates/AggNone.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/leviathan/aggregates/AggNone.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/leviathan/aggregates/AggNone.java new file mode 100644 index 0000000..a155139 --- /dev/null +++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/leviathan/aggregates/AggNone.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.jena.sparql.function.library.leviathan.aggregates; + +import org.apache.jena.graph.Node; +import org.apache.jena.sparql.engine.binding.Binding; +import org.apache.jena.sparql.expr.Expr; +import org.apache.jena.sparql.expr.ExprList; +import org.apache.jena.sparql.expr.NodeValue; +import org.apache.jena.sparql.expr.aggregate.Accumulator; +import org.apache.jena.sparql.expr.aggregate.AccumulatorShortCircuitExpr; +import org.apache.jena.sparql.expr.aggregate.Aggregator; +import org.apache.jena.sparql.expr.aggregate.AggregatorBase; +import org.apache.jena.sparql.expr.nodevalue.NodeValueBoolean; +import org.apache.jena.sparql.expr.nodevalue.XSDFuncOp; +import org.apache.jena.sparql.function.FunctionEnv; +import org.apache.jena.sparql.function.library.leviathan.LeviathanConstants; + +/** + * All aggregate, expression must evaluate to false for all solutions + */ +public class AggNone extends AggregatorBase { + + /** + * @param expr + * Expression + */ + protected AggNone(Expr expr) { + super(LeviathanConstants.LeviathanFunctionLibraryURI + "none", false, expr); + } + + @Override + public Aggregator copy(ExprList exprs) { + return new AggNone(exprs.get(0)); + } + + @Override + public boolean equals(Aggregator other, boolean bySyntax) { + if (other == null) + return false; + if (this == other) + return true; + if (!(other instanceof AggNone)) + return false; + AggNone agg = (AggNone) other; + return getExpr().equals(agg.getExpr(), bySyntax); + } + + @Override + public Accumulator createAccumulator() { + return new AccNone(this.getExpr()); + } + + @Override + public Node getValueEmpty() { + return NodeValue.TRUE.asNode(); + } + + @Override + public int hashCode() { + // TODO Auto-generated method stub + return 0; + } + + private static class AccNone extends AccumulatorShortCircuitExpr { + + /** + * @param expr + */ + protected AccNone(Expr expr) { + super(expr); + } + + @Override + protected NodeValue getAccValue() { + return NodeValue.TRUE; + } + + @Override + protected NodeValue getErrorValue() { + return NodeValue.FALSE; + } + + @Override + protected void accumulate(NodeValue nv, Binding binding, FunctionEnv functionEnv) { + // If false can't short circuit yet + if (!XSDFuncOp.booleanEffectiveValue(nv)) + return; + + // Otherwise can short circuit + this.shortCircuit(NodeValue.FALSE); + } + + @Override + protected void accumulateError(Binding binding, FunctionEnv functionEnv) { + this.shortCircuit(NodeValue.FALSE); + } + + } +}
