@Override
public GenericUDAFEvaluator getEvaluator(GenericUDAFParameterInfo info) throws SemanticException {
ObjectInspector[] parameters = info.getParameterObjectInspectors();
if (parameters.length != 2 && parameters.length != 3) {
throw new UDFArgumentTypeException(parameters.length - 1,
"Please specify either two or three arguments.");
}
// Validate the first parameter, which is the expression to compute over. This should be a
// numeric primitive type.
if (parameters[0].getCategory() != ObjectInspector.Category.PRIMITIVE) {
throw new UDFArgumentTypeException(0,
"Only primitive type arguments are accepted but "
+ parameters[0].getTypeName() + " was passed as parameter 1.");
}
switch (((PrimitiveObjectInspector) parameters[0]).getPrimitiveCategory()) {
case BYTE:
case SHORT:
case INT:
case LONG:
case FLOAT:
case DOUBLE:
case TIMESTAMP:
break;
default:
throw new UDFArgumentTypeException(0,
"Only numeric type arguments are accepted but "
+ parameters[0].getTypeName() + " was passed as parameter 1.");
}
// Validate the second parameter, which is either a solitary double or an array of doubles.
boolean wantManyQuantiles = false;
switch(parameters[1].getCategory()) {
case PRIMITIVE:
// Only a single double was passed as parameter 2, a single quantile is being requested
switch(((PrimitiveObjectInspector) parameters[1]).getPrimitiveCategory()) {
case FLOAT:
case DOUBLE:
break;
default:
throw new UDFArgumentTypeException(1,
"Only a float/double or float/double array argument is accepted as parameter 2, but "
+ parameters[1].getTypeName() + " was passed instead.");
}
break;
case LIST:
// An array was passed as parameter 2, make sure it's an array of primitives
if(((ListObjectInspector) parameters[1]).getListElementObjectInspector().getCategory() !=
ObjectInspector.Category.PRIMITIVE) {
throw new UDFArgumentTypeException(1,
"A float/double array argument may be passed as parameter 2, but "
+ parameters[1].getTypeName() + " was passed instead.");
}
// Now make sure it's an array of doubles or floats. We don't allow integer types here
// because percentile (really, quantile) values should generally be strictly between 0 and 1.
switch(((PrimitiveObjectInspector)((ListObjectInspector) parameters[1]).getListElementObjectInspector()).
getPrimitiveCategory()) {
case FLOAT:
case DOUBLE:
break;
default:
throw new UDFArgumentTypeException(1,
"A float/double array argument may be passed as parameter 2, but "
+ parameters[1].getTypeName() + " was passed instead.");
}
wantManyQuantiles = true;
break;
default:
throw new UDFArgumentTypeException(1,
"Only a float/double or float/double array argument is accepted as parameter 2, but "
+ parameters[1].getTypeName() + " was passed instead.");
}
// Also make sure it is a constant.
if (!ObjectInspectorUtils.isConstantObjectInspector(parameters[1])) {
throw new UDFArgumentTypeException(1,
"The second argument must be a constant, but " + parameters[1].getTypeName() +
" was passed instead.");
}
// If a third parameter has been specified, it should be an integer that specifies the number
// of histogram bins to use in the percentile approximation.
if(parameters.length == 3) {
if(parameters[2].getCategory() != ObjectInspector.Category.PRIMITIVE) {
throw new UDFArgumentTypeException(2, "Only a primitive argument is accepted as "
+ "parameter 3, but " + parameters[2].getTypeName() + " was passed instead.");
}
switch(((PrimitiveObjectInspector) parameters[2]).getPrimitiveCategory()) {
case BYTE:
case SHORT:
case INT:
case LONG:
case TIMESTAMP:
break;
default:
throw new UDFArgumentTypeException(2, "Only an integer argument is accepted as "
+ "parameter 3, but " + parameters[2].getTypeName() + " was passed instead.");
}
// Also make sure it is a constant.
if (!ObjectInspectorUtils.isConstantObjectInspector(parameters[2])) {
throw new UDFArgumentTypeException(2,
"The third argument must be a constant, but " + parameters[2].getTypeName() +
" was passed instead.");
}
}