/* *************************************************************************************** * Copyright (C) 2006 EsperTech, Inc. All rights reserved. * * http://www.espertech.com/esper * * 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.datetime.interval; import com.espertech.esper.client.EventBean; import com.espertech.esper.client.EventPropertyGetter; import com.espertech.esper.client.EventType; import com.espertech.esper.collection.Pair; import com.espertech.esper.epl.core.PropertyResolutionDescriptor; import com.espertech.esper.epl.core.StreamTypeService; import com.espertech.esper.epl.datetime.eval.DatetimeLongCoercerLocalDateTime; import com.espertech.esper.epl.datetime.eval.DatetimeLongCoercerZonedDateTime; import com.espertech.esper.epl.datetime.eval.DatetimeMethodEnum; import com.espertech.esper.epl.datetime.eval.FilterExprAnalyzerDTIntervalAffector; import com.espertech.esper.epl.expression.core.*; import com.espertech.esper.epl.expression.dot.ExprDotNodeFilterAnalyzerInput; import com.espertech.esper.epl.expression.dot.ExprDotNodeFilterAnalyzerInputProp; import com.espertech.esper.epl.expression.dot.ExprDotNodeFilterAnalyzerInputStream; import com.espertech.esper.epl.expression.time.TimeAbacus; import com.espertech.esper.util.JavaClassHelper; import java.time.LocalDateTime; import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.TimeZone; public class IntervalOpImpl implements IntervalOp { private ExprEvaluator evaluatorTimestamp; private Integer parameterStreamNum; private String parameterPropertyStart; private String parameterPropertyEnd; private final IntervalOpEval intervalOpEval; public IntervalOpImpl(DatetimeMethodEnum method, String methodNameUse, StreamTypeService streamTypeService, List<ExprNode> expressions, TimeZone timeZone, TimeAbacus timeAbacus) throws ExprValidationException { ExprEvaluator evaluatorEndTimestamp = null; Class timestampType; if (expressions.get(0) instanceof ExprStreamUnderlyingNode) { ExprStreamUnderlyingNode und = (ExprStreamUnderlyingNode) expressions.get(0); parameterStreamNum = und.getStreamId(); EventType type = streamTypeService.getEventTypes()[parameterStreamNum]; parameterPropertyStart = type.getStartTimestampPropertyName(); if (parameterPropertyStart == null) { throw new ExprValidationException("For date-time method '" + methodNameUse + "' the first parameter is event type '" + type.getName() + "', however no timestamp property has been defined for this event type"); } timestampType = type.getPropertyType(parameterPropertyStart); EventPropertyGetter getter = type.getGetter(parameterPropertyStart); evaluatorTimestamp = new ExprEvaluatorStreamLongProp(parameterStreamNum, getter); if (type.getEndTimestampPropertyName() != null) { parameterPropertyEnd = type.getEndTimestampPropertyName(); EventPropertyGetter getterEndTimestamp = type.getGetter(type.getEndTimestampPropertyName()); evaluatorEndTimestamp = new ExprEvaluatorStreamLongProp(parameterStreamNum, getterEndTimestamp); } else { parameterPropertyEnd = parameterPropertyStart; } } else { evaluatorTimestamp = expressions.get(0).getExprEvaluator(); timestampType = evaluatorTimestamp.getType(); String unresolvedPropertyName = null; if (expressions.get(0) instanceof ExprIdentNode) { ExprIdentNode identNode = (ExprIdentNode) expressions.get(0); parameterStreamNum = identNode.getStreamId(); parameterPropertyStart = identNode.getResolvedPropertyName(); parameterPropertyEnd = parameterPropertyStart; unresolvedPropertyName = identNode.getUnresolvedPropertyName(); } if (!JavaClassHelper.isDatetimeClass(evaluatorTimestamp.getType())) { // ident node may represent a fragment if (unresolvedPropertyName != null) { Pair<PropertyResolutionDescriptor, String> propertyDesc = ExprIdentNodeUtil.getTypeFromStream(streamTypeService, unresolvedPropertyName, false, true); if (propertyDesc.getFirst().getFragmentEventType() != null) { EventType type = propertyDesc.getFirst().getFragmentEventType().getFragmentType(); parameterPropertyStart = type.getStartTimestampPropertyName(); if (parameterPropertyStart == null) { throw new ExprValidationException("For date-time method '" + methodNameUse + "' the first parameter is event type '" + type.getName() + "', however no timestamp property has been defined for this event type"); } timestampType = type.getPropertyType(parameterPropertyStart); EventPropertyGetter getterFragment = streamTypeService.getEventTypes()[parameterStreamNum].getGetter(unresolvedPropertyName); EventPropertyGetter getterStartTimestamp = type.getGetter(parameterPropertyStart); evaluatorTimestamp = new ExprEvaluatorStreamLongPropFragment(parameterStreamNum, getterFragment, getterStartTimestamp); if (type.getEndTimestampPropertyName() != null) { parameterPropertyEnd = type.getEndTimestampPropertyName(); EventPropertyGetter getterEndTimestamp = type.getGetter(type.getEndTimestampPropertyName()); evaluatorEndTimestamp = new ExprEvaluatorStreamLongPropFragment(parameterStreamNum, getterFragment, getterEndTimestamp); } else { parameterPropertyEnd = parameterPropertyStart; } } } else { throw new ExprValidationException("For date-time method '" + methodNameUse + "' the first parameter expression returns '" + evaluatorTimestamp.getType() + "', however requires a Date, Calendar, Long-type return value or event (with timestamp)"); } } } IntervalComputer intervalComputer = IntervalComputerFactory.make(method, expressions, timeAbacus); // evaluation without end timestamp if (evaluatorEndTimestamp == null) { if (JavaClassHelper.isSubclassOrImplementsInterface(timestampType, Calendar.class)) { intervalOpEval = new IntervalOpEvalCal(intervalComputer); } else if (JavaClassHelper.isSubclassOrImplementsInterface(timestampType, Date.class)) { intervalOpEval = new IntervalOpEvalDate(intervalComputer); } else if (JavaClassHelper.getBoxedType(timestampType) == Long.class) { intervalOpEval = new IntervalOpEvalLong(intervalComputer); } else if (JavaClassHelper.isSubclassOrImplementsInterface(timestampType, LocalDateTime.class)) { intervalOpEval = new IntervalOpEvalLocalDateTime(intervalComputer, timeZone); } else if (JavaClassHelper.isSubclassOrImplementsInterface(timestampType, ZonedDateTime.class)) { intervalOpEval = new IntervalOpEvalZonedDateTime(intervalComputer); } else { throw new IllegalArgumentException("Invalid interval first parameter type '" + timestampType + "'"); } } else { if (JavaClassHelper.isSubclassOrImplementsInterface(timestampType, Calendar.class)) { intervalOpEval = new IntervalOpEvalCalWithEnd(intervalComputer, evaluatorEndTimestamp); } else if (JavaClassHelper.isSubclassOrImplementsInterface(timestampType, Date.class)) { intervalOpEval = new IntervalOpEvalDateWithEnd(intervalComputer, evaluatorEndTimestamp); } else if (JavaClassHelper.getBoxedType(timestampType) == Long.class) { intervalOpEval = new IntervalOpEvalLongWithEnd(intervalComputer, evaluatorEndTimestamp); } else if (JavaClassHelper.isSubclassOrImplementsInterface(timestampType, LocalDateTime.class)) { intervalOpEval = new IntervalOpEvalLocalDateTimeWithEnd(intervalComputer, evaluatorEndTimestamp, timeZone); } else if (JavaClassHelper.isSubclassOrImplementsInterface(timestampType, ZonedDateTime.class)) { intervalOpEval = new IntervalOpEvalZonedDateTimeWithEnd(intervalComputer, evaluatorEndTimestamp); } else { throw new IllegalArgumentException("Invalid interval first parameter type '" + timestampType + "'"); } } } /** * Obtain information used by filter analyzer to handle this dot-method invocation as part of query planning/indexing. * * @param typesPerStream event types * @param currentMethod current method * @param currentParameters current params * @param inputDesc descriptor of what the input to this interval method is */ public FilterExprAnalyzerDTIntervalAffector getFilterDesc(EventType[] typesPerStream, DatetimeMethodEnum currentMethod, List<ExprNode> currentParameters, ExprDotNodeFilterAnalyzerInput inputDesc) { // with intervals is not currently query planned if (currentParameters.size() > 1) { return null; } // Get input (target) int targetStreamNum; String targetPropertyStart; String targetPropertyEnd; if (inputDesc instanceof ExprDotNodeFilterAnalyzerInputStream) { ExprDotNodeFilterAnalyzerInputStream targetStream = (ExprDotNodeFilterAnalyzerInputStream) inputDesc; targetStreamNum = targetStream.getStreamNum(); EventType targetType = typesPerStream[targetStreamNum]; targetPropertyStart = targetType.getStartTimestampPropertyName(); targetPropertyEnd = targetType.getEndTimestampPropertyName() != null ? targetType.getEndTimestampPropertyName() : targetPropertyStart; } else if (inputDesc instanceof ExprDotNodeFilterAnalyzerInputProp) { ExprDotNodeFilterAnalyzerInputProp targetStream = (ExprDotNodeFilterAnalyzerInputProp) inputDesc; targetStreamNum = targetStream.getStreamNum(); targetPropertyStart = targetStream.getPropertyName(); targetPropertyEnd = targetStream.getPropertyName(); } else { return null; } // check parameter info if (parameterPropertyStart == null) { return null; } return new FilterExprAnalyzerDTIntervalAffector(currentMethod, typesPerStream, targetStreamNum, targetPropertyStart, targetPropertyEnd, parameterStreamNum, parameterPropertyStart, parameterPropertyEnd); } public Object evaluate(long startTs, long endTs, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { Object parameter = evaluatorTimestamp.evaluate(eventsPerStream, isNewData, context); if (parameter == null) { return parameter; } return intervalOpEval.evaluate(startTs, endTs, parameter, eventsPerStream, isNewData, context); } public static interface IntervalOpEval { public Object evaluate(long startTs, long endTs, Object parameter, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context); } public abstract static class IntervalOpEvalDateBase implements IntervalOpEval { protected final IntervalComputer intervalComputer; public IntervalOpEvalDateBase(IntervalComputer intervalComputer) { this.intervalComputer = intervalComputer; } } public static class IntervalOpEvalDate extends IntervalOpEvalDateBase { public IntervalOpEvalDate(IntervalComputer intervalComputer) { super(intervalComputer); } public Object evaluate(long startTs, long endTs, Object parameter, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { long time = ((Date) parameter).getTime(); return intervalComputer.compute(startTs, endTs, time, time, eventsPerStream, isNewData, context); } } public static class IntervalOpEvalLong extends IntervalOpEvalDateBase { public IntervalOpEvalLong(IntervalComputer intervalComputer) { super(intervalComputer); } public Object evaluate(long startTs, long endTs, Object parameter, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { long time = (Long) parameter; return intervalComputer.compute(startTs, endTs, time, time, eventsPerStream, isNewData, context); } } public static class IntervalOpEvalCal extends IntervalOpEvalDateBase { public IntervalOpEvalCal(IntervalComputer intervalComputer) { super(intervalComputer); } public Object evaluate(long startTs, long endTs, Object parameter, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { long time = ((Calendar) parameter).getTimeInMillis(); return intervalComputer.compute(startTs, endTs, time, time, eventsPerStream, isNewData, context); } } public static class IntervalOpEvalLocalDateTime extends IntervalOpEvalDateBase { private final TimeZone timeZone; public IntervalOpEvalLocalDateTime(IntervalComputer intervalComputer, TimeZone timeZone) { super(intervalComputer); this.timeZone = timeZone; } public Object evaluate(long startTs, long endTs, Object parameter, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { long time = DatetimeLongCoercerLocalDateTime.coerce((LocalDateTime) parameter, timeZone); return intervalComputer.compute(startTs, endTs, time, time, eventsPerStream, isNewData, context); } } public static class IntervalOpEvalZonedDateTime extends IntervalOpEvalDateBase { public IntervalOpEvalZonedDateTime(IntervalComputer intervalComputer) { super(intervalComputer); } public Object evaluate(long startTs, long endTs, Object parameter, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { long time = DatetimeLongCoercerZonedDateTime.coerce((ZonedDateTime) parameter); return intervalComputer.compute(startTs, endTs, time, time, eventsPerStream, isNewData, context); } } public abstract static class IntervalOpEvalDateWithEndBase implements IntervalOpEval { protected final IntervalComputer intervalComputer; private final ExprEvaluator evaluatorEndTimestamp; protected IntervalOpEvalDateWithEndBase(IntervalComputer intervalComputer, ExprEvaluator evaluatorEndTimestamp) { this.intervalComputer = intervalComputer; this.evaluatorEndTimestamp = evaluatorEndTimestamp; } public abstract Object evaluate(long startTs, long endTs, Object parameterStartTs, Object parameterEndTs, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context); public Object evaluate(long startTs, long endTs, Object parameterStartTs, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { Object paramEndTs = evaluatorEndTimestamp.evaluate(eventsPerStream, isNewData, context); if (paramEndTs == null) { return null; } return evaluate(startTs, endTs, parameterStartTs, paramEndTs, eventsPerStream, isNewData, context); } } public static class IntervalOpEvalDateWithEnd extends IntervalOpEvalDateWithEndBase { public IntervalOpEvalDateWithEnd(IntervalComputer intervalComputer, ExprEvaluator evaluatorEndTimestamp) { super(intervalComputer, evaluatorEndTimestamp); } public Object evaluate(long startTs, long endTs, Object parameterStartTs, Object parameterEndTs, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { return intervalComputer.compute(startTs, endTs, ((Date) parameterStartTs).getTime(), ((Date) parameterEndTs).getTime(), eventsPerStream, isNewData, context); } } public static class IntervalOpEvalLongWithEnd extends IntervalOpEvalDateWithEndBase { public IntervalOpEvalLongWithEnd(IntervalComputer intervalComputer, ExprEvaluator evaluatorEndTimestamp) { super(intervalComputer, evaluatorEndTimestamp); } public Object evaluate(long startTs, long endTs, Object parameterStartTs, Object parameterEndTs, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { return intervalComputer.compute(startTs, endTs, (Long) parameterStartTs, (Long) parameterEndTs, eventsPerStream, isNewData, context); } } public static class IntervalOpEvalCalWithEnd extends IntervalOpEvalDateWithEndBase { public IntervalOpEvalCalWithEnd(IntervalComputer intervalComputer, ExprEvaluator evaluatorEndTimestamp) { super(intervalComputer, evaluatorEndTimestamp); } public Object evaluate(long startTs, long endTs, Object parameterStartTs, Object parameterEndTs, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { return intervalComputer.compute(startTs, endTs, ((Calendar) parameterStartTs).getTimeInMillis(), ((Calendar) parameterEndTs).getTimeInMillis(), eventsPerStream, isNewData, context); } } public static class IntervalOpEvalLocalDateTimeWithEnd extends IntervalOpEvalDateWithEndBase { private final TimeZone timeZone; public IntervalOpEvalLocalDateTimeWithEnd(IntervalComputer intervalComputer, ExprEvaluator evaluatorEndTimestamp, TimeZone timeZone) { super(intervalComputer, evaluatorEndTimestamp); this.timeZone = timeZone; } public Object evaluate(long startTs, long endTs, Object parameterStartTs, Object parameterEndTs, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { return intervalComputer.compute(startTs, endTs, DatetimeLongCoercerLocalDateTime.coerce((LocalDateTime) parameterStartTs, timeZone), DatetimeLongCoercerLocalDateTime.coerce((LocalDateTime) parameterEndTs, timeZone), eventsPerStream, isNewData, context); } } public static class IntervalOpEvalZonedDateTimeWithEnd extends IntervalOpEvalDateWithEndBase { public IntervalOpEvalZonedDateTimeWithEnd(IntervalComputer intervalComputer, ExprEvaluator evaluatorEndTimestamp) { super(intervalComputer, evaluatorEndTimestamp); } public Object evaluate(long startTs, long endTs, Object parameterStartTs, Object parameterEndTs, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { return intervalComputer.compute(startTs, endTs, DatetimeLongCoercerZonedDateTime.coerce((ZonedDateTime) parameterStartTs), DatetimeLongCoercerZonedDateTime.coerce((ZonedDateTime) parameterEndTs), eventsPerStream, isNewData, context); } } }