/*
***************************************************************************************
* 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.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
*/
public 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;
}
}