/*
* 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 com.addthis.hydra.data.tree.prop;
import com.addthis.bundle.core.BundleField;
import com.addthis.bundle.util.ValueUtil;
import com.addthis.bundle.value.ValueFactory;
import com.addthis.bundle.value.ValueObject;
import com.addthis.codec.annotations.FieldConfig;
import com.addthis.hydra.data.tree.DataTreeNode;
import com.addthis.hydra.data.tree.DataTreeNodeUpdater;
import com.addthis.hydra.data.tree.TreeDataParameters;
import com.addthis.hydra.data.tree.TreeNodeData;
public class DataSumFloat extends TreeNodeData<DataSumFloat.Config> {
/**
* This data attachment <span class="hydra-summary">computes the sum, mean value, and
* number of instances for a specified floating-point field</span>.
* <p/>
* <p>By default null values are treated as zero values and included in the calculation
* of the sum, mean value, and number of instances. The null values
* can be treated as non-zero values by using the 'defaultValue' field.
* <p/>
* <p>Job Configuration Example:</p>
* <pre>
* {field:"ID", data.shares.sumf.key"IS_SHARE"}
* </pre>
*
* <p><b>Query Path Directives</b>
*
* <p>${attachment}=sum returns the sum.
* <p>${attachment}=count returns the number of instances.
* <p>${attachment}=avg returns the sum divided by the number of instances.
*
* <p>% operations are not supported</p>
*
* <p>Query Path Example:</p>
* <pre>
* /+$+shares=sum
* </pre>
*
* @user-reference
*/
public static final class Config extends TreeDataParameters<DataSumFloat> {
@FieldConfig(codable = true)
private String key;
@FieldConfig(codable = true)
private double defaultValue = 0;
@FieldConfig(codable = true)
private boolean warnOnError;
@Override
public DataSumFloat newInstance() {
DataSumFloat ds = new DataSumFloat();
return ds;
}
}
@FieldConfig(codable = true)
private double sum;
@FieldConfig(codable = true)
private long num;
private BundleField keyAccess;
@Override
public boolean updateChildData(DataTreeNodeUpdater state, DataTreeNode tn, DataSumFloat.Config conf) {
if (keyAccess == null && conf.key != null) {
keyAccess = state.getBundle().getFormat().getField(conf.key);
}
if (keyAccess != null) {
ValueObject kv = state.getBundle().getValue(keyAccess);
if (kv != null) {
try {
double value = ValueUtil.asNumberOrParseDouble(kv).asDouble().getDouble();
if (!Double.isNaN(value)) {
sum += value;
num++;
}
} catch (NumberFormatException ex) {
if (conf.warnOnError) {
System.out.println("uparseable " + kv + " in " + state.getBundle());
}
}
} else {
sum += conf.defaultValue;
}
} else {
num++;
sum += conf.defaultValue;
}
return true;
}
@Override
public ValueObject getValue(String key) {
if (key.equals("sum")) {
return ValueFactory.create(sum);
} else if (key.equals("avg")) {
return ValueFactory.create(num != 0 ? sum / num : 0);
} else if (key.equals("count")) {
return ValueFactory.create(num);
} else {
return null;
}
}
}