/* (c) 2014 LinkedIn Corp. All rights reserved.
*
* 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.
*/
package com.linkedin.cubert.operator.cube;
import com.linkedin.cubert.block.DataType;
import com.linkedin.cubert.operator.PreconditionException;
import com.linkedin.cubert.operator.PreconditionExceptionType;
/**
* Factory class for creating {@link ValueAggregator}.
*
* @author Maneesh Varshney
*
*/
public class ValueAggregatorFactory
{
/**
* Creates {@link ValueAggregator} objects.
* <p>
* This method can throw {@link PreconditionException} if the data type of the input
* field is not valid (e.g. SUM of string fields).
*
* @param type
* the type of aggregator
* @param inputType
* the data type of the input values
* @param colName
* the name of the input column
* @return ValueAggregator object
* @throws PreconditionException
*/
public static ValueAggregator get(ValueAggregationType type,
DataType inputType,
String colName) throws PreconditionException
{
switch (type)
{
case COUNT:
return new CountAggregator();
case COUNT_TO_ONE:
return new CountToOneAggregator();
case MAX:
assertNumericalType("MAX", colName, inputType);
return new MaxAggregator(inputType);
case MIN:
assertNumericalType("MIN", colName, inputType);
return new MinAggregator(inputType);
case SUM:
assertNumericalType("SUM", colName, inputType);
return new SumAggregator(inputType);
}
return null;
}
private static void assertNumericalType(String aggregator,
String colName,
DataType inputType) throws PreconditionException
{
if (inputType.isIntOrLong() || inputType.isReal())
return;
String msg =
String.format("Expected type for %s(%s): int, long, float or double. Found: %s",
aggregator,
colName,
inputType);
throw new PreconditionException(PreconditionExceptionType.INVALID_SCHEMA, msg);
}
private ValueAggregatorFactory()
{
}
/**
* Counts the number of values.
*
* @author Maneesh Varshney
*
*/
private static final class CountAggregator implements ValueAggregator
{
@Override
public long initialValue()
{
return 0L;
}
@Override
public long aggregate(long lastAggregate, Object currentValue)
{
return lastAggregate + 1;
}
@Override
public Object output(long value)
{
return value;
}
@Override
public DataType outputType()
{
return DataType.LONG;
}
}
/**
* Reports 0 if no value is seen, or 1 if one or more value are seen.
*
* @author Maneesh Varshney
*
*/
private static final class CountToOneAggregator implements ValueAggregator
{
@Override
public long initialValue()
{
return 0L;
}
@Override
public long aggregate(long lastAggregate, Object currentValue)
{
return 1L;
}
@Override
public Object output(long value)
{
return value;
}
@Override
public DataType outputType()
{
return DataType.LONG;
}
}
/**
* Computes sum of the values.
*
* @author Maneesh Varshney
*
*/
private static final class SumAggregator implements ValueAggregator
{
private final DataType type;
SumAggregator(DataType type)
{
this.type = type;
}
@Override
public long initialValue()
{
switch (type)
{
case INT:
case LONG:
return 0L;
default:
return Double.doubleToLongBits(0.0);
}
}
@Override
public long aggregate(long lastAggregate, Object currentValue)
{
switch (type)
{
case INT:
case LONG:
return lastAggregate + ((Number) currentValue).longValue();
default:
return Double.doubleToLongBits(Double.longBitsToDouble(lastAggregate)
+ ((Number) currentValue).doubleValue());
}
}
@Override
public Object output(long value)
{
switch (type)
{
case INT:
return (int) value;
case LONG:
return value;
case FLOAT:
return (float) Double.longBitsToDouble(value);
default:
return Double.longBitsToDouble(value);
}
}
@Override
public DataType outputType()
{
return type;
}
}
/**
* Computes minimum of the values.
*
* @author Maneesh Varshney
*
*/
private static final class MinAggregator implements ValueAggregator
{
private final DataType type;
MinAggregator(DataType type)
{
this.type = type;
}
@Override
public long initialValue()
{
switch (type)
{
case INT:
case LONG:
return Long.MAX_VALUE;
default:
return Double.doubleToLongBits(Double.MAX_VALUE);
}
}
@Override
public long aggregate(long lastAggregate, Object currentValue)
{
switch (type)
{
case INT:
case LONG:
{
long current = ((Number) currentValue).longValue();
return current < lastAggregate ? current : lastAggregate;
}
default:
{
double last = Double.longBitsToDouble(lastAggregate);
double current = ((Number) currentValue).doubleValue();
double min = current < last ? current : last;
return Double.doubleToLongBits(min);
}
}
}
@Override
public Object output(long value)
{
switch (type)
{
case INT:
return (value == Long.MAX_VALUE) ? null : (int) value;
case LONG:
return (value == Long.MAX_VALUE) ? null : value;
case FLOAT:
{
double dvalue = Double.longBitsToDouble(value);
return (dvalue == Double.MAX_VALUE) ? null : (float) dvalue;
}
default:
{
double dvalue = Double.longBitsToDouble(value);
return (dvalue == Double.MAX_VALUE) ? null : dvalue;
}
}
}
@Override
public DataType outputType()
{
return type;
}
}
/**
* Computes maximum of the values.
*
* @author Maneesh Varshney
*
*/
private static final class MaxAggregator implements ValueAggregator
{
private final DataType type;
MaxAggregator(DataType type)
{
this.type = type;
}
@Override
public long initialValue()
{
switch (type)
{
case INT:
case LONG:
return Long.MIN_VALUE;
default:
return Double.doubleToLongBits(Double.MIN_VALUE);
}
}
@Override
public long aggregate(long lastAggregate, Object currentValue)
{
switch (type)
{
case INT:
case LONG:
{
long current = ((Number) currentValue).longValue();
return current > lastAggregate ? current : lastAggregate;
}
default:
{
double last = Double.longBitsToDouble(lastAggregate);
double current = ((Number) currentValue).doubleValue();
double min = current > last ? current : last;
return Double.doubleToLongBits(min);
}
}
}
@Override
public Object output(long value)
{
switch (type)
{
case INT:
return (value == Long.MAX_VALUE) ? null : (int) value;
case LONG:
return (value == Long.MAX_VALUE) ? null : value;
case FLOAT:
{
double dvalue = Double.longBitsToDouble(value);
return (dvalue == Double.MAX_VALUE) ? null : (float) dvalue;
}
default:
{
double dvalue = Double.longBitsToDouble(value);
return (dvalue == Double.MAX_VALUE) ? null : dvalue;
}
}
}
@Override
public DataType outputType()
{
return type;
}
}
}