/*
* 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 java.util.HashMap ;
import java.util.Locale ;
import java.util.Map ;
import org.apache.jena.atlas.io.IndentedLineBuffer ;
import org.apache.jena.graph.Node ;
import org.apache.jena.sparql.ARQInternalErrorException ;
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.graph.NodeTransform;
import org.apache.jena.sparql.serializer.SerializationContext ;
import org.apache.jena.sparql.sse.writers.WriterExpr ;
import org.apache.jena.sparql.util.ExprUtils ;
/** Aggregate that does everything except the per-group aggregation that is needed for each operation */
public abstract class AggregatorBase implements Aggregator
{
// Aggregator -- handles one aggregation over one group, and is the syntax unit.
// AggregateFactory -- delays the creating of Aggregator so multiple mentions over the same group gives the same Aggregator
// Accumulator -- the per-group, per-key accumulator for the aggregate
// queries track their aggregators so if one is used twice, the calculataion is only done once.
// For distinct, that means only uniquefier.
// Built-ins: COUNT, SUM, MIN, MAX, AVG, GROUP_CONCAT, and SAMPLE
// but COUNT(*) and COUNT(Expr) are different beasts
// each in DISTINCT and non-DISTINCT versions
protected final String name ;
protected final boolean isDistinct ;
protected final ExprList exprList ;
protected AggregatorBase(String name, boolean isDistinct, Expr expr) {
this(name, isDistinct, new ExprList(expr)) ;
}
protected AggregatorBase(String name, boolean isDistinct, ExprList exprList) {
this.name = name ;
this.isDistinct = isDistinct ;
this.exprList = exprList ;
}
private Map<Binding, Accumulator> buckets = new HashMap<>() ; // Bindingkey => Accumulator
@Override
public abstract Accumulator createAccumulator() ;
@Override
public abstract Node getValueEmpty() ;
public Node getValue(Binding key)
{
Accumulator acc = buckets.get(key) ;
if ( acc == null )
throw new ARQInternalErrorException("Null for accumulator") ;
NodeValue nv = acc.getValue();
if ( nv == null )
return null ;
return nv.asNode() ;
}
@Override
public String key() { return toPrefixString() ; }
@Override
public final Aggregator copyTransform(NodeTransform transform)
{
ExprList e = getExprList() ;
if ( e != null )
e = e.applyNodeTransform(transform) ;
return copy(e) ;
}
/** Many aggergate use a single expression.
* This convebnience operation gets the expression if there is exactly one.
*/
protected Expr getExpr() {
if ( exprList != null && exprList.size() == 1 )
return getExprList().get(0) ;
return null ;
}
@Override
public ExprList getExprList() { return exprList ; }
@Override
public String getName() { return name ; }
@Override
public String toString() { return asSparqlExpr(null) ; }
@Override
public String asSparqlExpr(SerializationContext sCxt) {
IndentedLineBuffer x = new IndentedLineBuffer() ;
x.append(getName()) ;
x.append("(") ;
if ( isDistinct )
x.append("DISTINCT ") ;
if ( getExprList() != null )
ExprUtils.fmtSPARQL(x, getExprList(), sCxt) ;
x.append(")") ;
return x.asString() ;
}
@Override
public String toPrefixString() {
IndentedLineBuffer x = new IndentedLineBuffer() ;
x.append("(") ;
x.append(getName().toLowerCase(Locale.ROOT)) ;
x.incIndent();
if ( isDistinct )
x.append(" distinct") ;
for ( Expr e : getExprList() ) {
x.append(" ");
WriterExpr.output(x, e, null) ;
}
x.decIndent();
x.append(")") ;
return x.asString() ;
}
@Override
public abstract int hashCode() ;
@Override
public final boolean equals(Object other) {
if ( other == null ) return false ;
if ( this == other ) return true ;
if ( ! ( other instanceof Aggregator ) ) return false ;
return equals((Aggregator)other, false) ;
}
protected static final int HC_AggAvg = 0x170 ;
protected static final int HC_AggAvgDistinct = 0x171 ;
protected static final int HC_AggCount = 0x172 ;
protected static final int HC_AggCountDistinct = 0x173 ;
protected static final int HC_AggCountVar = 0x174 ;
protected static final int HC_AggCountVarDistinct = 0x175 ;
protected static final int HC_AggMin = 0x176 ;
protected static final int HC_AggMinDistinct = 0x177 ;
protected static final int HC_AggMax = 0x178 ;
protected static final int HC_AggMaxDistinct = 0x179 ;
protected static final int HC_AggSample = 0x17A ;
protected static final int HC_AggSampleDistinct = 0x17B ;
protected static final int HC_AggSum = 0x17C ;
protected static final int HC_AggSumDistinct = 0x17D ;
protected static final int HC_AggGroupConcat = 0x17E ;
protected static final int HC_AggGroupConcatDistinct = 0x17F ;
protected static final int HC_AggNull = 0x180 ;
protected static final int HC_AggCustom = 0x181 ;
}