// This file is part of OpenTSDB.
// Copyright (C) 2015 The OpenTSDB Authors.
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 2.1 of the License, or (at your
// option) any later version. This program is distributed in the hope that it
// will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
// General Public License for more details. You should have received a copy
// of the GNU Lesser General Public License along with this program. If not,
// see <http://www.gnu.org/licenses/>.
package net.opentsdb.query.expression;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.google.common.base.Objects;
import net.opentsdb.core.FillPolicy;
/**
* POJO for serdes of fill policies. It allows the user to pick either policies
* with default values or a scalar that can be supplied with any number.
* @since 2.3
*/
@JsonDeserialize(builder = NumericFillPolicy.Builder.class)
public class NumericFillPolicy {
/** The fill policy to use. This is required */
private FillPolicy policy;
/** The value to store with the fill policy */
private double value;
/**
* CTor to set the policy. Also calls {@link #validate()}
* @param policy The policy to set.
*/
public NumericFillPolicy(final FillPolicy policy) {
this.policy = policy;
validate();
}
/**
* CTor to set the policy and value. Also calls {@link #validate()}
* @param policy The name of the fill policy
* @param value The value to use when filling
* @throws IllegalArgumentException if the policy and value don't gel together
*/
public NumericFillPolicy(final FillPolicy policy, final double value) {
this.policy = policy;
this.value = value;
validate();
}
@Override
public String toString() {
return "policy=" + policy + ", value=" + value;
}
/** @returns a NumericFillPolicy builder */
public static Builder Builder() {
return new Builder();
}
/**
* A builder class for deserialization via Jackson
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
public static final class Builder {
@JsonProperty
private FillPolicy policy;
@JsonProperty
private double value;
public Builder setPolicy(FillPolicy policy) {
this.policy = policy;
return this;
}
public Builder setValue(double value) {
this.value = value;
return this;
}
public NumericFillPolicy build() {
return new NumericFillPolicy(policy, value);
}
}
@Override
public int hashCode() {
return Objects.hashCode(policy, value);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (!(obj instanceof NumericFillPolicy)) {
return false;
}
final NumericFillPolicy nfp = (NumericFillPolicy)obj;
return Objects.equal(policy, nfp.policy) &&
Objects.equal(value, nfp.value);
}
/** @return the fill policy */
public FillPolicy getPolicy() {
return policy;
}
/** @param policy the fill policy to use */
public void setPolicy(final FillPolicy policy) {
this.policy = policy;
}
/** @return the value to use when filling */
public double getValue() {
return value;
}
/** @param value the value to use when filling */
public void setValue(final double value) {
this.value = value;
}
/**
* Makes sure the policy name and value are a suitable combination. If one
* or the other is missing then we set the other with the proper value.
* @throws IllegalArgumentException if the combination is bad
*/
public void validate() {
if (policy == null) {
if (value == 0) {
policy = FillPolicy.ZERO;
} else if (Double.isNaN(value)) {
policy = FillPolicy.NOT_A_NUMBER;
} else {
policy = FillPolicy.SCALAR;
}
} else {
switch (policy) {
case NONE:
case NOT_A_NUMBER:
if (value != 0 && !Double.isNaN(value)) {
throw new IllegalArgumentException(
"The value for NONE and NAN must be NaN");
}
value = Double.NaN;
break;
case ZERO:
if (value != 0) {
throw new IllegalArgumentException("The value for ZERO must be 0");
}
value = 0;
break;
case NULL:
if (value != 0 && !Double.isNaN(value)) {
throw new IllegalArgumentException("The value for NULL must be 0");
}
value = Double.NaN;
break;
case SCALAR: // it CAN be zero
break;
}
}
}
}