/*
* *************************************************************************************
* Copyright (C) 2008 EsperTech, Inc. All rights reserved. *
* http://esper.codehaus.org *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
* *************************************************************************************
*/
package com.espertech.esper.epl.expression;
import com.espertech.esper.client.EPException;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.epl.datetime.eval.TimePeriod;
import com.espertech.esper.metrics.instrumentation.InstrumentationHelper;
import com.espertech.esper.util.JavaClassHelper;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayDeque;
import java.util.Calendar;
/**
* Expression representing a time period.
* <p>
* Child nodes to this expression carry the actual parts and must return a numeric value.
*/
public class ExprTimePeriodImpl extends ExprNodeBase implements ExprTimePeriod, ExprEvaluator
{
private final boolean hasYear;
private final boolean hasMonth;
private final boolean hasWeek;
private final boolean hasDay;
private final boolean hasHour;
private final boolean hasMinute;
private final boolean hasSecond;
private final boolean hasMillisecond;
private boolean hasVariable;
private transient ExprEvaluator[] evaluators;
private transient TimePeriodAdder[] adders;
private static final long serialVersionUID = -7229827032500659319L;
/**
* Ctor.
* @param hasDay true if the expression has that part, false if not
* @param hasHour true if the expression has that part, false if not
* @param hasMinute true if the expression has that part, false if not
* @param hasSecond true if the expression has that part, false if not
* @param hasMillisecond true if the expression has that part, false if not
*/
public ExprTimePeriodImpl(boolean hasYear, boolean hasMonth, boolean hasWeek, boolean hasDay, boolean hasHour, boolean hasMinute, boolean hasSecond, boolean hasMillisecond)
{
this.hasYear = hasYear;
this.hasMonth = hasMonth;
this.hasWeek = hasWeek;
this.hasDay = hasDay;
this.hasHour = hasHour;
this.hasMinute = hasMinute;
this.hasSecond = hasSecond;
this.hasMillisecond = hasMillisecond;
}
public ExprTimePeriodEvalDeltaConst constEvaluator(ExprEvaluatorContext context) {
if (!hasMonth && !hasYear) {
double seconds = evaluateAsSeconds(null, true, context);
long msec = Math.round(seconds * 1000d);
return new ExprTimePeriodEvalDeltaConstMsec(msec);
}
else {
int[] values = new int[adders.length];
for (int i = 0; i < values.length; i++) {
values[i] = ((Number) evaluators[i].evaluate(null, true, context)).intValue();
}
return new ExprTimePeriodEvalDeltaConstCalAdd(adders, values);
}
}
public ExprTimePeriodEvalDeltaNonConst nonconstEvaluator() {
if (!hasMonth && !hasYear) {
return new ExprTimePeriodEvalDeltaNonConstMsec(this);
}
else {
return new ExprTimePeriodEvalDeltaNonConstCalAdd(this);
}
}
public ExprEvaluator getExprEvaluator() {
return this;
}
public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) {
throw new IllegalStateException("Time-Period expression must be evaluated via any of " + ExprTimePeriod.class.getSimpleName() + " interface methods");
}
protected TimePeriodAdder[] getAdders() {
return adders;
}
public ExprEvaluator[] getEvaluators() {
return evaluators;
}
/**
* Indicator whether the time period has a day part child expression.
* @return true for part present, false for not present
*/
public boolean isHasDay()
{
return hasDay;
}
/**
* Indicator whether the time period has a hour part child expression.
* @return true for part present, false for not present
*/
public boolean isHasHour()
{
return hasHour;
}
/**
* Indicator whether the time period has a minute part child expression.
* @return true for part present, false for not present
*/
public boolean isHasMinute()
{
return hasMinute;
}
/**
* Indicator whether the time period has a second part child expression.
* @return true for part present, false for not present
*/
public boolean isHasSecond()
{
return hasSecond;
}
/**
* Indicator whether the time period has a millisecond part child expression.
* @return true for part present, false for not present
*/
public boolean isHasMillisecond()
{
return hasMillisecond;
}
/**
* Indicator whether the time period has a year part child expression.
* @return true for part present, false for not present
*/
public boolean isHasYear() {
return hasYear;
}
/**
* Indicator whether the time period has a month part child expression.
* @return true for part present, false for not present
*/
public boolean isHasMonth() {
return hasMonth;
}
/**
* Indicator whether the time period has a week part child expression.
* @return true for part present, false for not present
*/
public boolean isHasWeek() {
return hasWeek;
}
/**
* Indicator whether the time period has a variable in any of the child expressions.
* @return true for variable present, false for not present
*/
public boolean hasVariable()
{
return hasVariable;
}
public void validate(ExprValidationContext validationContext) throws ExprValidationException
{
evaluators = ExprNodeUtility.getEvaluators(this.getChildNodes());
for (ExprNode childNode : this.getChildNodes())
{
validate(childNode);
}
ArrayDeque<TimePeriodAdder> list = new ArrayDeque<TimePeriodAdder>();
if (hasYear) {
list.add(new TimePeriodAdderYear());
}
if (hasMonth) {
list.add(new TimePeriodAdderMonth());
}
if (hasWeek) {
list.add(new TimePeriodAdderWeek());
}
if (hasDay) {
list.add(new TimePeriodAdderDay());
}
if (hasHour) {
list.add(new TimePeriodAdderHour());
}
if (hasMinute) {
list.add(new TimePeriodAdderMinute());
}
if (hasSecond) {
list.add(new TimePeriodAdderSecond());
}
if (hasMillisecond) {
list.add(new TimePeriodAdderMSec());
}
adders = list.toArray(new TimePeriodAdder[list.size()]);
}
private void validate(ExprNode expression) throws ExprValidationException
{
if (expression == null)
{
return;
}
Class returnType = expression.getExprEvaluator().getType();
if (!JavaClassHelper.isNumeric(returnType)) {
throw new ExprValidationException("Time period expression requires a numeric parameter type");
}
if ((hasMonth || hasYear) && (JavaClassHelper.getBoxedType(returnType) != Integer.class)) {
throw new ExprValidationException("Time period expressions with month or year component require integer values, received a " + returnType.getSimpleName() + " value");
}
if (expression instanceof ExprVariableNode)
{
hasVariable = true;
}
}
public double evaluateAsSeconds(EventBean[] eventsPerStream, boolean newData, ExprEvaluatorContext context) {
if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().qExprTimePeriod(this);}
double seconds = 0;
for (int i = 0; i < adders.length; i++) {
Double result = eval(evaluators[i], eventsPerStream, newData, context);
if (result == null)
{
if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().aExprTimePeriod(null);}
throw new EPException("Failed to evaluate time period, received a null value for '" + ExprNodeUtility.toExpressionStringMinPrecedence(this) + "'");
}
seconds += adders[i].compute(result);
}
if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().aExprTimePeriod(seconds);}
return seconds;
}
private Double eval(ExprEvaluator expr, EventBean[] events, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext)
{
Object value = expr.evaluate(events, isNewData, exprEvaluatorContext);
if (value == null)
{
return null;
}
if (value instanceof BigDecimal)
{
return ((Number) value).doubleValue();
}
if (value instanceof BigInteger)
{
return ((Number) value).doubleValue();
}
return ((Number) value).doubleValue();
}
public TimePeriod evaluateGetTimePeriod(EventBean[] eventsPerStream, boolean newData, ExprEvaluatorContext context) {
int exprCtr = 0;
Integer year = null;
if (hasYear){
year = getInt(evaluators[exprCtr++].evaluate(eventsPerStream, newData, context));
}
Integer month = null;
if (hasMonth) {
month = getInt(evaluators[exprCtr++].evaluate(eventsPerStream, newData, context));
}
Integer week = null;
if (hasWeek) {
week = getInt(evaluators[exprCtr++].evaluate(eventsPerStream, newData, context));
}
Integer day = null;
if (hasDay) {
day = getInt(evaluators[exprCtr++].evaluate(eventsPerStream, newData, context));
}
Integer hours = null;
if (hasHour) {
hours = getInt(evaluators[exprCtr++].evaluate(eventsPerStream, newData, context));
}
Integer minutes = null;
if (hasMinute)
{
minutes = getInt(evaluators[exprCtr++].evaluate(eventsPerStream, newData, context));
}
Integer seconds = null;
if (hasSecond)
{
seconds = getInt(evaluators[exprCtr++].evaluate(eventsPerStream, newData, context));
}
Integer milliseconds = null;
if (hasMillisecond)
{
milliseconds = getInt(evaluators[exprCtr].evaluate(eventsPerStream, newData, context));
}
return new TimePeriod(year, month, week, day, hours, minutes, seconds, milliseconds);
}
private Integer getInt(Object evaluated) {
if (evaluated == null) {
return null;
}
return ((Number) evaluated).intValue();
}
public static interface TimePeriodAdder {
public double compute(Double value);
public void add(Calendar cal, int value);
}
public static class TimePeriodAdderYear implements TimePeriodAdder {
private static final double MULTIPLIER = 365*24*60*60;
public double compute(Double value) {
return value*MULTIPLIER;
}
public void add(Calendar cal, int value) {
cal.add(Calendar.YEAR, value);
}
}
public static class TimePeriodAdderMonth implements TimePeriodAdder {
private static final double MULTIPLIER = 30*24*60*60;
public double compute(Double value) {
return value*MULTIPLIER;
}
public void add(Calendar cal, int value) {
cal.add(Calendar.MONTH, value);
}
}
public static class TimePeriodAdderWeek implements TimePeriodAdder {
private static final double MULTIPLIER = 7*24*60*60;
public double compute(Double value) {
return value*MULTIPLIER;
}
public void add(Calendar cal, int value) {
cal.add(Calendar.WEEK_OF_YEAR, value);
}
}
public static class TimePeriodAdderDay implements TimePeriodAdder {
private static final double MULTIPLIER = 24*60*60;
public double compute(Double value) {
return value*MULTIPLIER;
}
public void add(Calendar cal, int value) {
cal.add(Calendar.DAY_OF_MONTH, value);
}
}
public static class TimePeriodAdderHour implements TimePeriodAdder {
private static final double MULTIPLIER = 60*60;
public double compute(Double value) {
return value*MULTIPLIER;
}
public void add(Calendar cal, int value) {
cal.add(Calendar.HOUR_OF_DAY, value);
}
}
public static class TimePeriodAdderMinute implements TimePeriodAdder {
private static final double MULTIPLIER = 60;
public double compute(Double value) {
return value*MULTIPLIER;
}
public void add(Calendar cal, int value) {
cal.add(Calendar.MINUTE, value);
}
}
public static class TimePeriodAdderSecond implements TimePeriodAdder {
public double compute(Double value) {
return value;
}
public void add(Calendar cal, int value) {
cal.add(Calendar.SECOND, value);
}
}
public static class TimePeriodAdderMSec implements TimePeriodAdder {
public double compute(Double value) {
return value / 1000d;
}
public void add(Calendar cal, int value) {
cal.add(Calendar.MILLISECOND, value);
}
}
public Class getType()
{
return Double.class;
}
public boolean isConstantResult()
{
for (ExprNode child : getChildNodes())
{
if (!child.isConstantResult())
{
return false;
}
}
return true;
}
public void toPrecedenceFreeEPL(StringWriter writer) {
int exprCtr = 0;
String delimiter = "";
if (hasYear)
{
getChildNodes()[exprCtr++].toEPL(writer, getPrecedence());
writer.append(" years");
delimiter = " ";
}
if (hasMonth)
{
writer.append(delimiter);
getChildNodes()[exprCtr++].toEPL(writer, getPrecedence());
writer.append(" months");
delimiter = " ";
}
if (hasWeek)
{
writer.append(delimiter);
getChildNodes()[exprCtr++].toEPL(writer, getPrecedence());
writer.append(" weeks");
delimiter = " ";
}
if (hasDay)
{
writer.append(delimiter);
getChildNodes()[exprCtr++].toEPL(writer, getPrecedence());
writer.append(" days");
delimiter = " ";
}
if (hasHour)
{
writer.append(delimiter);
getChildNodes()[exprCtr++].toEPL(writer, getPrecedence());
writer.append(" hours");
delimiter = " ";
}
if (hasMinute)
{
writer.append(delimiter);
getChildNodes()[exprCtr++].toEPL(writer, getPrecedence());
writer.append(" minutes");
delimiter = " ";
}
if (hasSecond)
{
writer.append(delimiter);
getChildNodes()[exprCtr++].toEPL(writer, getPrecedence());
writer.append(" seconds");
delimiter = " ";
}
if (hasMillisecond)
{
writer.append(delimiter);
getChildNodes()[exprCtr].toEPL(writer, getPrecedence());
writer.append(" milliseconds");
}
}
public ExprPrecedenceEnum getPrecedence() {
return ExprPrecedenceEnum.UNARY;
}
public boolean equalsNode(ExprNode node)
{
if (!(node instanceof ExprTimePeriodImpl))
{
return false;
}
ExprTimePeriodImpl other = (ExprTimePeriodImpl) node;
if (hasYear!= other.hasYear)
{
return false;
}
if (hasMonth != other.hasMonth)
{
return false;
}
if (hasWeek != other.hasWeek)
{
return false;
}
if (hasDay != other.hasDay)
{
return false;
}
if (hasHour != other.hasHour)
{
return false;
}
if (hasMinute != other.hasMinute)
{
return false;
}
if (hasSecond != other.hasSecond)
{
return false;
}
return (hasMillisecond == other.hasMillisecond);
}
}