/*
***************************************************************************************
* 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.rowregex;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.collection.MultiKeyUntyped;
import com.espertech.esper.epl.expression.core.ExprEvaluator;
import com.espertech.esper.epl.expression.core.ExprEvaluatorContext;
import com.espertech.esper.metrics.instrumentation.InstrumentationHelper;
import java.util.*;
/**
* Partition-by implementation for partition state.
*/
public class RegexPartitionStateRepoGroup implements RegexPartitionStateRepo {
/**
* Empty state collection initial threshold.
*/
public final static int INITIAL_COLLECTION_MIN = 100;
private final RegexPartitionStateRepoGroupMeta meta;
private final RegexPartitionStateRandomAccessGetter getter;
private final Map<Object, RegexPartitionStateImpl> states;
private final RegexPartitionStateRepoScheduleStateImpl optionalIntervalSchedules;
private int currentCollectionSize = INITIAL_COLLECTION_MIN;
private int eventSequenceNumber;
public RegexPartitionStateRepoGroup(RegexPartitionStateRandomAccessGetter getter,
RegexPartitionStateRepoGroupMeta meta,
boolean keepScheduleState,
RegexPartitionTerminationStateComparator terminationStateCompare) {
this.getter = getter;
this.meta = meta;
this.states = new HashMap<Object, RegexPartitionStateImpl>();
this.optionalIntervalSchedules = keepScheduleState ? new RegexPartitionStateRepoScheduleStateImpl(terminationStateCompare) : null;
}
public int incrementAndGetEventSequenceNum() {
++eventSequenceNumber;
return eventSequenceNumber;
}
public void setEventSequenceNum(int num) {
this.eventSequenceNumber = num;
}
public RegexPartitionStateRepoScheduleState getScheduleState() {
return optionalIntervalSchedules;
}
public void removeState(Object partitionKey) {
states.remove(partitionKey);
}
public RegexPartitionStateRepo copyForIterate(boolean forOutOfOrderReprocessing) {
RegexPartitionStateRepoGroup copy = new RegexPartitionStateRepoGroup(getter, meta, false, null);
for (Map.Entry<Object, RegexPartitionStateImpl> entry : states.entrySet()) {
copy.states.put(entry.getKey(), new RegexPartitionStateImpl(entry.getValue().getRandomAccess(), entry.getKey()));
}
return copy;
}
public int removeOld(EventBean[] oldData, boolean isEmpty, boolean[] found) {
if (isEmpty) {
int countRemoved;
if (getter == null) {
// no "prev" used, clear all state
countRemoved = getStateCount();
states.clear();
} else {
countRemoved = 0;
for (Map.Entry<Object, RegexPartitionStateImpl> entry : states.entrySet()) {
countRemoved += entry.getValue().getNumStates();
entry.getValue().setCurrentStates(Collections.<RegexNFAStateEntry>emptyList());
}
}
// clear "prev" state
if (getter != null) {
// we will need to remove event-by-event
for (int i = 0; i < oldData.length; i++) {
RegexPartitionStateImpl partitionState = getState(oldData[i], true);
if (partitionState == null) {
continue;
}
partitionState.removeEventFromPrev(oldData);
}
}
return countRemoved;
}
// we will need to remove event-by-event
int countRemoved = 0;
for (int i = 0; i < oldData.length; i++) {
RegexPartitionStateImpl partitionState = getState(oldData[i], true);
if (partitionState == null) {
continue;
}
if (found[i]) {
countRemoved += partitionState.removeEventFromState(oldData[i]);
boolean cleared = partitionState.getNumStates() == 0;
if (cleared) {
if (getter == null) {
states.remove(partitionState.getOptionalKeys());
}
}
}
partitionState.removeEventFromPrev(oldData[i]);
}
return countRemoved;
}
public RegexPartitionState getState(Object key) {
return states.get(key);
}
public RegexPartitionStateImpl getState(EventBean theEvent, boolean isCollect) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qRegExPartition(meta.getPartitionExpressionNodes());
}
// collect unused states
if (isCollect && (states.size() >= currentCollectionSize)) {
List<Object> removeList = new ArrayList<Object>();
for (Map.Entry<Object, RegexPartitionStateImpl> entry : states.entrySet()) {
if ((entry.getValue().isEmptyCurrentState()) &&
(entry.getValue().getRandomAccess() == null || entry.getValue().getRandomAccess().isEmpty())) {
removeList.add(entry.getKey());
}
}
for (Object removeKey : removeList) {
states.remove(removeKey);
}
if (removeList.size() < (currentCollectionSize / 5)) {
currentCollectionSize *= 2;
}
}
Object key = getKeys(theEvent, meta);
RegexPartitionStateImpl state = states.get(key);
if (state != null) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aRegExPartition(true, state);
}
return state;
}
state = new RegexPartitionStateImpl(getter, new ArrayList<RegexNFAStateEntry>(), key);
states.put(key, state);
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aRegExPartition(false, state);
}
return state;
}
public void accept(EventRowRegexNFAViewServiceVisitor visitor) {
visitor.visitPartitioned((Map) states);
}
public boolean isPartitioned() {
return true;
}
public Map<Object, RegexPartitionStateImpl> getStates() {
return states;
}
public int getStateCount() {
int total = 0;
for (Map.Entry<Object, RegexPartitionStateImpl> entry : states.entrySet()) {
total += entry.getValue().getNumStates();
}
return total;
}
public static Object getKeys(EventBean theEvent, RegexPartitionStateRepoGroupMeta meta) {
EventBean[] eventsPerStream = meta.getEventsPerStream();
eventsPerStream[0] = theEvent;
ExprEvaluator[] partitionExpressions = meta.getPartitionExpressions();
if (partitionExpressions.length == 1) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qExprValue(meta.getPartitionExpressionNodes()[0], eventsPerStream);
Object value = partitionExpressions[0].evaluate(eventsPerStream, true, meta.getExprEvaluatorContext());
InstrumentationHelper.get().aExprValue(value);
return value;
} else {
return partitionExpressions[0].evaluate(eventsPerStream, true, meta.getExprEvaluatorContext());
}
}
Object[] keys = new Object[partitionExpressions.length];
int count = 0;
ExprEvaluatorContext exprEvaluatorContext = meta.getExprEvaluatorContext();
for (ExprEvaluator node : partitionExpressions) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qExprValue(meta.getPartitionExpressionNodes()[count], eventsPerStream);
}
keys[count] = node.evaluate(eventsPerStream, true, exprEvaluatorContext);
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aExprValue(keys[count]);
}
count++;
}
return new MultiKeyUntyped(keys);
}
public void destroy() {
}
}