/*
***************************************************************************************
* 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.join.exec.base;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.epl.expression.core.ExprEvaluatorContext;
import com.espertech.esper.epl.join.assemble.BaseAssemblyNode;
import com.espertech.esper.epl.join.assemble.BaseAssemblyNodeFactory;
import com.espertech.esper.epl.join.assemble.ResultAssembler;
import com.espertech.esper.epl.join.rep.Node;
import com.espertech.esper.epl.join.rep.RepositoryImpl;
import com.espertech.esper.util.IndentWriter;
import java.util.*;
/**
* Execution for a set of lookup instructions and for a set of result assemble instructions to perform
* joins and construct a complex result.
*/
@SuppressWarnings({"StringContatenationInLoop"})
public class LookupInstructionExecNode extends ExecNode {
private final int rootStream;
private final String rootStreamName;
private final int numStreams;
private final boolean[] requiredPerStream;
private final LookupInstructionExec[] lookupInstructions;
private final BaseAssemblyNode[] assemblyInstructions;
private final MyResultAssembler myResultAssembler;
private int requireResultsInstruction;
/**
* Ctor.
*
* @param rootStream is the stream supplying the lookup event
* @param rootStreamName is the name of the stream supplying the lookup event
* @param numStreams is the number of streams
* @param lookupInstructions is a list of lookups to perform
* @param requiredPerStream indicates which streams are required and which are optional in the lookup
* @param assemblyInstructionFactories factories for assembly
*/
public LookupInstructionExecNode(int rootStream,
String rootStreamName,
int numStreams,
LookupInstructionExec[] lookupInstructions,
boolean[] requiredPerStream,
List<BaseAssemblyNodeFactory> assemblyInstructionFactories) {
this.rootStream = rootStream;
this.rootStreamName = rootStreamName;
this.numStreams = numStreams;
this.lookupInstructions = lookupInstructions;
this.requiredPerStream = requiredPerStream;
// We have a list of factories that are pointing to each other in a tree, i.e.:
// F1 (->F3), F2 (->F3), F3
Map<BaseAssemblyNodeFactory, BaseAssemblyNode> nodes = new IdentityHashMap<BaseAssemblyNodeFactory, BaseAssemblyNode>();
for (BaseAssemblyNodeFactory factory : assemblyInstructionFactories) {
BaseAssemblyNode node = factory.makeAssemblerUnassociated();
nodes.put(factory, node);
}
// re-associate each node after allocation
for (Map.Entry<BaseAssemblyNodeFactory, BaseAssemblyNode> nodeWithFactory : nodes.entrySet()) {
BaseAssemblyNodeFactory parentFactory = nodeWithFactory.getKey().getParentNode();
if (parentFactory != null) {
BaseAssemblyNode parent = nodes.get(parentFactory);
nodeWithFactory.getValue().setParentAssembler(parent);
}
for (BaseAssemblyNodeFactory childNodeFactory : nodeWithFactory.getKey().getChildNodes()) {
BaseAssemblyNode child = nodes.get(childNodeFactory);
nodeWithFactory.getValue().addChild(child);
}
}
this.assemblyInstructions = new BaseAssemblyNode[assemblyInstructionFactories.size()];
for (int i = 0; i < assemblyInstructionFactories.size(); i++) {
this.assemblyInstructions[i] = nodes.get(assemblyInstructionFactories.get(i));
}
myResultAssembler = new MyResultAssembler(rootStream);
assemblyInstructions[assemblyInstructions.length - 1].setParentAssembler(myResultAssembler);
// Determine up to which instruction we are dealing with optional results.
// When dealing with optional results we don't do fast exists if we find no lookup results
requireResultsInstruction = 1; // we always require results from the very first lookup
for (int i = 1; i < lookupInstructions.length; i++) {
int fromStream = lookupInstructions[i].getFromStream();
if (requiredPerStream[fromStream]) {
requireResultsInstruction = i + 1; // require results as long as the from-stream is a required stream
} else {
break;
}
}
}
public void process(EventBean lookupEvent, EventBean[] prefillPath, Collection<EventBean[]> resultFinalRows, ExprEvaluatorContext exprEvaluatorContext) {
RepositoryImpl repository = new RepositoryImpl(rootStream, lookupEvent, numStreams);
boolean processOptional = true;
for (int i = 0; i < requireResultsInstruction; i++) {
LookupInstructionExec currentInstruction = lookupInstructions[i];
boolean hasResults = currentInstruction.process(repository, exprEvaluatorContext);
// no results, check what to do
if (!hasResults) {
// If there was a required stream, we are done.
if (currentInstruction.hasRequiredStream()) {
return;
}
// If this is the first stream and there are no results, we are done with lookups
if (i == 0) {
processOptional = false; // go to result processing
}
}
}
if (processOptional) {
for (int i = requireResultsInstruction; i < lookupInstructions.length; i++) {
LookupInstructionExec currentInstruction = lookupInstructions[i];
currentInstruction.process(repository, exprEvaluatorContext);
}
}
// go over the assembly instruction set
List<Node>[] results = repository.getNodesPerStream();
// no results - need to execute the very last instruction/top node
if (results == null) {
BaseAssemblyNode lastAssemblyNode = assemblyInstructions[assemblyInstructions.length - 1];
lastAssemblyNode.init(null);
lastAssemblyNode.process(null, resultFinalRows, lookupEvent);
return;
}
// we have results - execute all instructions
BaseAssemblyNode assemblyNode;
for (int i = 0; i < assemblyInstructions.length; i++) {
assemblyNode = assemblyInstructions[i];
assemblyNode.init(results);
}
for (int i = 0; i < assemblyInstructions.length; i++) {
assemblyNode = assemblyInstructions[i];
assemblyNode.process(results, resultFinalRows, lookupEvent);
}
}
public void print(IndentWriter writer) {
writer.println("LookupInstructionExecNode" +
" rootStream=" + rootStream +
" name=" + rootStreamName +
" requiredPerStream=" + Arrays.toString(requiredPerStream));
writer.incrIndent();
for (int i = 0; i < lookupInstructions.length; i++) {
writer.println("lookup inst node " + i);
writer.incrIndent();
lookupInstructions[i].print(writer);
writer.decrIndent();
}
writer.decrIndent();
writer.incrIndent();
for (int i = 0; i < assemblyInstructions.length; i++) {
writer.println("assembly inst node " + i);
writer.incrIndent();
assemblyInstructions[i].print(writer);
writer.decrIndent();
}
writer.decrIndent();
}
/**
* Receives result rows posted by result set assembly nodes.
*/
public static class MyResultAssembler implements ResultAssembler {
private final int rootStream;
/**
* Ctor.
*
* @param rootStream is the root stream for which we get results
*/
public MyResultAssembler(int rootStream) {
this.rootStream = rootStream;
}
public void result(EventBean[] row, int fromStreamNum, EventBean myEvent, Node myNode, Collection<EventBean[]> resultFinalRows, EventBean resultRootEvent) {
row[rootStream] = resultRootEvent;
resultFinalRows.add(row);
}
}
}