/**************************************************************************************
* 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.client.soda;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* The from-clause names the streams to select upon.
* <p>
* The most common projected stream is a filter-based stream which is created by {@link FilterStream}.
* <p>
* Multiple streams can be joined by adding each stream individually.
* <p>
* Outer joins are also handled by this class. To create an outer join consisting of 2 streams,
* add one {@link OuterJoinQualifier} that defines the outer join relationship between the 2 streams. The outer joins between
* N streams, add N-1 {@link OuterJoinQualifier} qualifiers.
*/
public class FromClause implements Serializable
{
private static final long serialVersionUID = 0L;
private List<Stream> streams;
private List<OuterJoinQualifier> outerJoinQualifiers;
/**
* Ctor.
*/
public FromClause() {
streams = new ArrayList<Stream>();
outerJoinQualifiers = new ArrayList<OuterJoinQualifier>();
}
/**
* Creates an empty from-clause to which one adds streams via the add methods.
* @return empty from clause
*/
public static FromClause create()
{
return new FromClause();
}
/**
* Creates a from-clause that lists 2 projected streams joined via outer join.
* @param stream first stream in outer join
* @param outerJoinQualifier qualifies the outer join
* @param streamSecond second stream in outer join
* @return from clause
*/
public static FromClause create(Stream stream, OuterJoinQualifier outerJoinQualifier, Stream streamSecond)
{
return new FromClause(stream, outerJoinQualifier, streamSecond);
}
/**
* Creates a from clause that selects from a single stream.
* <p>
* Use {@link FilterStream} to create filter-based streams to add.
* @param streams is one or more streams to add to the from clause.
* @return from clause
*/
public static FromClause create(Stream ...streams)
{
return new FromClause(streams);
}
/**
* Ctor for an outer join between two streams.
* @param streamOne first stream in outer join
* @param outerJoinQualifier type of outer join and fields joined on
* @param streamTwo second stream in outer join
*/
public FromClause(Stream streamOne, OuterJoinQualifier outerJoinQualifier, Stream streamTwo)
{
this(streamOne);
add(streamTwo);
outerJoinQualifiers.add(outerJoinQualifier);
}
/**
* Ctor.
* @param streamsList is zero or more streams in the from-clause.
*/
public FromClause(Stream ...streamsList)
{
streams = new ArrayList<Stream>();
outerJoinQualifiers = new ArrayList<OuterJoinQualifier>();
streams.addAll(Arrays.asList(streamsList));
}
/**
* Adds a stream.
* <p>
* Use {@link FilterStream} to add filter-based streams.
* @param stream to add
* @return from clause
*/
public FromClause add(Stream stream)
{
streams.add(stream);
return this;
}
/**
* Adds an outer join descriptor that defines how the streams are related via outer joins.
* <P>
* For joining N streams, add N-1 outer join qualifiers.
* @param outerJoinQualifier is the type of outer join and the fields in the outer join
* @return from clause
*/
public FromClause add(OuterJoinQualifier outerJoinQualifier)
{
outerJoinQualifiers.add(outerJoinQualifier);
return this;
}
/**
* Returns the list of streams in the from-clause.
* @return list of streams
*/
public List<Stream> getStreams()
{
return streams;
}
/**
* Renders the from-clause in textual representation.
* @param writer to output to
* @param formatter for newline-whitespace formatting
*/
public void toEPL(StringWriter writer, EPStatementFormatter formatter)
{
toEPLOptions(writer, formatter, true);
}
/**
* Renders the from-clause in textual representation.
* @param writer to output to
* @param includeFrom flag whether to add the "from" literal
* @param formatter for newline-whitespace formatting
*/
protected void toEPLOptions(StringWriter writer, EPStatementFormatter formatter, boolean includeFrom)
{
String delimiter = "";
if (includeFrom)
{
formatter.beginFrom(writer);
writer.write("from");
}
if ((outerJoinQualifiers == null) || (outerJoinQualifiers.size() == 0))
{
boolean first = true;
for (Stream stream : streams)
{
writer.write(delimiter);
formatter.beginFromStream(writer, first);
first = false;
stream.toEPL(writer, formatter);
delimiter = ",";
}
}
else
{
if (outerJoinQualifiers.size() != (streams.size() - 1))
{
throw new IllegalArgumentException("Number of outer join qualifiers must be one less then the number of streams.");
}
boolean first = true;
for (int i = 0; i < streams.size(); i++)
{
Stream stream = streams.get(i);
formatter.beginFromStream(writer, first);
first = false;
stream.toEPL(writer, formatter);
if (i > 0)
{
OuterJoinQualifier qualCond = outerJoinQualifiers.get(i - 1);
if (qualCond.getLeft() != null) {
writer.write(" on ");
qualCond.getLeft().toEPL(writer, ExpressionPrecedenceEnum.MINIMUM);
writer.write(" = ");
qualCond.getRight().toEPL(writer, ExpressionPrecedenceEnum.MINIMUM);
if (qualCond.getAdditionalProperties().size() > 0)
{
for (PropertyValueExpressionPair pair : qualCond.getAdditionalProperties())
{
writer.write(" and ");
pair.getLeft().toEPL(writer, ExpressionPrecedenceEnum.MINIMUM);
writer.write(" = ");
pair.getRight().toEPL(writer, ExpressionPrecedenceEnum.MINIMUM);
}
}
}
}
if (i < streams.size() - 1)
{
OuterJoinQualifier qualType = outerJoinQualifiers.get(i);
writer.write(" ");
writer.write(qualType.getType().getText());
writer.write(" outer join");
}
}
}
}
/**
* Returns the outer join descriptors, if this is an outer join, or an empty list if
* none of the streams are outer joined.
* @return list of outer join qualifiers
*/
public List<OuterJoinQualifier> getOuterJoinQualifiers()
{
return outerJoinQualifiers;
}
/**
* Set the streams.
* @param streams to set
*/
public void setStreams(List<Stream> streams) {
this.streams = streams;
}
/**
* Set outer joins.
* @param outerJoinQualifiers to set
*/
public void setOuterJoinQualifiers(List<OuterJoinQualifier> outerJoinQualifiers) {
this.outerJoinQualifiers = outerJoinQualifiers;
}
}