/**************************************************************************************
* 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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.*;
import java.io.PrintWriter;
import java.io.StringWriter;
import com.espertech.esper.util.IndentWriter;
import com.espertech.esper.epl.join.plan.NStreamOuterQueryPlanBuilder;
/**
* Builds a tree of assembly nodes given a strategy for how to join streams.
*/
public class AssemblyStrategyTreeBuilder
{
/**
* Builds a tree of {@link BaseAssemblyNode} from join strategy information.
* @param rootStream - the root stream supplying the event to evaluate
* @param streamsJoinedPerStream - a map in which the key is the stream number to supply an event,
* and the value is an array of streams to find events in for the given event
* @param isRequiredPerStream - indicates which streams are required join streams versus optional streams
* @return root assembly node
*/
public static BaseAssemblyNode build(int rootStream, Map<Integer, int[]> streamsJoinedPerStream, boolean isRequiredPerStream[])
{
if (streamsJoinedPerStream.size() < 3)
{
throw new IllegalArgumentException("Not a 3-way join");
}
if ((rootStream < 0) || (rootStream >= streamsJoinedPerStream.size()))
{
throw new IllegalArgumentException("Invalid root stream");
}
if (isRequiredPerStream.length != streamsJoinedPerStream.size())
{
throw new IllegalArgumentException("Arrays not matching up");
}
NStreamOuterQueryPlanBuilder.verifyJoinedPerStream(rootStream, streamsJoinedPerStream);
if (log.isDebugEnabled())
{
log.debug(".build Building node for root stream " + rootStream +
" streamsJoinedPerStream=" + NStreamOuterQueryPlanBuilder.print(streamsJoinedPerStream) +
" isRequiredPerStream=" + Arrays.toString(isRequiredPerStream));
}
BaseAssemblyNode topNode = createNode(true, rootStream, streamsJoinedPerStream.size(), streamsJoinedPerStream.get(rootStream), isRequiredPerStream);
recursiveBuild(rootStream, topNode, streamsJoinedPerStream, isRequiredPerStream);
if (log.isDebugEnabled())
{
StringWriter buf = new StringWriter();
PrintWriter print = new PrintWriter(buf);
IndentWriter indentWriter = new IndentWriter(print, 0, 2);
topNode.printDescendends(indentWriter);
log.debug(".build Dumping root node for stream " + rootStream + ": \n" + buf.toString());
}
return topNode;
}
private static void recursiveBuild(int parentStreamNum, BaseAssemblyNode parentNode,
Map<Integer, int[]> streamsJoinedPerStream, boolean isRequiredPerStream[])
{
int numStreams = streamsJoinedPerStream.size();
for (int i = 0; i < streamsJoinedPerStream.get(parentStreamNum).length; i++)
{
int streamJoined = streamsJoinedPerStream.get(parentStreamNum)[i];
BaseAssemblyNode childNode = createNode(false, streamJoined, numStreams, streamsJoinedPerStream.get(streamJoined), isRequiredPerStream);
parentNode.addChild(childNode);
if (streamsJoinedPerStream.get(streamJoined).length > 0)
{
recursiveBuild(streamJoined, childNode, streamsJoinedPerStream, isRequiredPerStream);
}
}
}
private static BaseAssemblyNode createNode(boolean isRoot, int streamNum, int numStreams, int[] joinedStreams, boolean isRequiredPerStream[])
{
if (joinedStreams.length == 0)
{
return new LeafAssemblyNode(streamNum, numStreams);
}
if (joinedStreams.length == 1)
{
int joinedStream = joinedStreams[0];
boolean isRequired = isRequiredPerStream[joinedStream];
if (isRequired)
{
if (isRoot)
{
return new RootRequiredAssemblyNode(streamNum, numStreams);
}
else
{
return new BranchRequiredAssemblyNode(streamNum, numStreams);
}
}
else
{
if (isRoot)
{
return new RootOptionalAssemblyNode(streamNum, numStreams);
}
else
{
return new BranchOptionalAssemblyNode(streamNum, numStreams);
}
}
}
// Determine if all substream are outer (optional) joins
boolean allSubStreamsOptional = true;
for (int i = 0; i < joinedStreams.length; i++)
{
int stream = joinedStreams[i];
if (isRequiredPerStream[stream])
{
allSubStreamsOptional = false;
}
}
// Make node for building a cartesian product
if (isRoot)
{
return new RootCartProdAssemblyNode(streamNum, numStreams, allSubStreamsOptional);
}
else
{
return new CartesianProdAssemblyNode(streamNum, numStreams, allSubStreamsOptional);
}
}
private static final Log log = LogFactory.getLog(AssemblyStrategyTreeBuilder.class);
}