/**************************************************************************************
* 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.join.assemble;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.epl.join.rep.Node;
import com.espertech.esper.util.IndentWriter;
import java.util.*;
/**
* Represents a node in a tree responsible for assembling outer join query results.
* <p>
* The tree is double-linked, child nodes know each parent and parent know all child nodes.
* <p>
* Each specific subclass of this abstract assembly node is dedicated to assembling results for
* a certain event stream.
*/
public abstract class BaseAssemblyNode implements ResultAssembler
{
/**
* Parent node.
*/
protected ResultAssembler parentNode;
/**
* Child nodes.
*/
protected final List<BaseAssemblyNode> childNodes;
/**
* Stream number.
*/
protected final int streamNum;
/**
* Number of streams in statement.
*/
protected final int numStreams;
/**
* Ctor.
* @param streamNum - stream number of the event stream that this node assembles results for.
* @param numStreams - number of streams
*/
protected BaseAssemblyNode(int streamNum, int numStreams)
{
this.streamNum = streamNum;
this.numStreams = numStreams;
childNodes = new LinkedList<BaseAssemblyNode>();
}
/**
* Provides results to assembly nodes for initialization.
* @param result is a list of result nodes per stream
*/
public abstract void init(List<Node>[] result);
/**
* Process results.
* @param result is a list of result nodes per stream
* @param resultFinalRows
* @param resultRootEvent
*/
public abstract void process(List<Node>[] result, Collection<EventBean[]> resultFinalRows, EventBean resultRootEvent);
/**
* Output this node using writer, not outputting child nodes.
* @param indentWriter to use for output
*/
public abstract void print(IndentWriter indentWriter);
/**
* Set parent node.
* @param resultAssembler is the parent node
*/
public void setParentAssembler(ResultAssembler resultAssembler)
{
this.parentNode = resultAssembler;
}
/**
* Add a child node.
* @param childNode to add
*/
public void addChild(BaseAssemblyNode childNode)
{
childNode.parentNode = this;
childNodes.add(childNode);
}
/**
* Returns the stream number.
* @return stream number
*/
protected int getStreamNum()
{
return streamNum;
}
/**
* Returns child nodes.
* @return child nodes
*/
protected List<BaseAssemblyNode> getChildNodes()
{
return childNodes;
}
/**
* Returns parent node.
* @return parent node
*/
protected ResultAssembler getParentAssembler()
{
return parentNode;
}
/**
* Returns an array of stream numbers that lists all child node's stream numbers.
* @return child node stream numbers
*/
protected int[] getSubstreams()
{
List<Integer> substreams = new LinkedList<Integer>();
recusiveAddSubstreams(substreams);
// copy to array
int[] substreamArr = new int[substreams.size()];
int count = 0;
for (Integer stream : substreams)
{
substreamArr[count++] = stream;
}
return substreamArr;
}
private void recusiveAddSubstreams(List<Integer> substreams)
{
substreams.add(streamNum);
for (BaseAssemblyNode child : childNodes)
{
child.recusiveAddSubstreams(substreams);
}
}
/**
* Output this node and all descendent nodes using writer, outputting child nodes.
* @param indentWriter to output to
*/
public void printDescendends(IndentWriter indentWriter)
{
this.print(indentWriter);
for (BaseAssemblyNode child : childNodes)
{
indentWriter.incrIndent();
child.print(indentWriter);
indentWriter.decrIndent();
}
}
/**
* Returns all descendent nodes to the top node in a list in which the utmost descendants are
* listed first and the top node itself is listed last.
* @param topNode is the root node of a tree structure
* @return list of nodes with utmost descendants first ordered by level of depth in tree with top node last
*/
public static List<BaseAssemblyNode> getDescendentNodesBottomUp(BaseAssemblyNode topNode)
{
List<BaseAssemblyNode> result = new LinkedList<BaseAssemblyNode>();
// Map to hold per level of the node (1 to N depth) of node a list of nodes, if any
// exist at that level
TreeMap<Integer, List<BaseAssemblyNode>> nodesPerLevel = new TreeMap<Integer, List<BaseAssemblyNode>>();
// Recursively enter all aggregate functions and their level into map
recursiveAggregateEnter(topNode, nodesPerLevel, 1);
// Done if none found
if (nodesPerLevel.isEmpty())
{
throw new IllegalStateException("Empty collection for nodes per level");
}
// From the deepest (highest) level to the lowest, add aggregates to list
int deepLevel = nodesPerLevel.lastKey();
for (int i = deepLevel; i >= 1; i--)
{
List<BaseAssemblyNode> list = nodesPerLevel.get(i);
if (list == null)
{
continue;
}
result.addAll(list);
}
return result;
}
private static void recursiveAggregateEnter(BaseAssemblyNode currentNode, Map<Integer, List<BaseAssemblyNode>> nodesPerLevel, int currentLevel)
{
// ask all child nodes to enter themselves
for (BaseAssemblyNode node : currentNode.childNodes)
{
recursiveAggregateEnter(node, nodesPerLevel, currentLevel + 1);
}
// Add myself to list
List<BaseAssemblyNode> aggregates = nodesPerLevel.get(currentLevel);
if (aggregates == null)
{
aggregates = new LinkedList<BaseAssemblyNode>();
nodesPerLevel.put(currentLevel, aggregates);
}
aggregates.add(currentNode);
}
}