package com.itemanalysis.squiggle.base;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import com.itemanalysis.squiggle.criteria.MatchCriteria;
import com.itemanalysis.squiggle.output.Output;
import com.itemanalysis.squiggle.output.Outputable;
import com.itemanalysis.squiggle.output.ToStringer;
/**
* @author <a href="joe@truemesh.com">Joe Walnes</a>
* @author Nat Pryce
*/
public class SelectQuery implements Outputable, ValueSet {
public static final int indentSize = 4;
private final List<Selectable> selection = new ArrayList<Selectable>();
private final List<Criteria> criteria = new ArrayList<Criteria>();
private final List<Order> order = new ArrayList<Order>();
private final List<DerbyOffset> offset = new ArrayList<DerbyOffset>();
private boolean isDistinct = false;
public void addOffset(int offset){
this.offset.clear();//ensure only one element in array list - this is a workaround
DerbyOffset os = new DerbyOffset(offset);
this.offset.add(os);
}
public void addOffset(int offset, int size){
this.offset.clear();//ensure only one element in array list - this is a workaround
DerbyOffset os = new DerbyOffset(offset, size);
this.offset.add(os);
}
public List<Table> listTables() {
LinkedHashSet<Table> tables = new LinkedHashSet<Table>();
addReferencedTablesTo(tables);
return new ArrayList<Table>(tables);
}
public void addToSelection(Selectable selectable) {
selection.add(selectable);
}
/**
* Syntax sugar for addToSelection(Column).
*/
public void addColumn(Table table, String columname) {
addToSelection(table.getColumn(columname));
}
public void removeFromSelection(Selectable selectable) {
selection.remove(selectable);
}
/**
* @return a list of {@link Selectable} objects.
*/
public List<Selectable> listSelection() {
return Collections.unmodifiableList(selection);
}
public boolean isDistinct() {
return isDistinct;
}
public void setDistinct(boolean distinct) {
isDistinct = distinct;
}
public void addCriteria(Criteria criteria) {
this.criteria.add(criteria);
}
public void removeCriteria(Criteria criteria) {
this.criteria.remove(criteria);
}
public List<Criteria> listCriteria() {
return Collections.unmodifiableList(criteria);
}
/**
* Syntax sugar for addCriteria(JoinCriteria)
*/
public void addJoin(Table srcTable, String srcColumnname, Table destTable, String destColumnname) {
addCriteria(new MatchCriteria(srcTable.getColumn(srcColumnname), MatchCriteria.EQUALS, destTable.getColumn(destColumnname)));
}
/**
* Syntax sugar for addCriteria(JoinCriteria)
*/
public void addJoin(Table srcTable, String srcColumnName, String operator, Table destTable, String destColumnName) {
addCriteria(new MatchCriteria(srcTable.getColumn(srcColumnName), operator, destTable.getColumn(destColumnName)));
}
public void addOrder(Order order) {
this.order.add(order);
}
/**
* Syntax sugar for addOrder(Order).
*/
public void addOrder(Table table, String columnname, boolean ascending) {
addOrder(new Order(table.getColumn(columnname), ascending));
}
public void removeOrder(Order order) {
this.order.remove(order);
}
public List<Order> listOrder() {
return Collections.unmodifiableList(order);
}
public String toString() {
return ToStringer.toString(this);
}
public void write(Output out) {
out.print("SELECT");
if (isDistinct) {
out.print(" DISTINCT");
}
out.println();
appendIndentedList(out, selection, ",");
Set<Table> tables = findAllUsedTables();
if (!tables.isEmpty()) {
out.println("FROM");
appendIndentedList(out, tables, ",");
}
// Add criteria
if (criteria.size() > 0) {
out.println("WHERE");
appendIndentedList(out, criteria, "AND");
}
// Add order
if (order.size() > 0) {
out.println("ORDER BY");
appendIndentedList(out, order, ",");
}
//add offset
if(offset.size() > 0){
out.println("OFFSET");
appendIndentedList(out, offset, ",");
}
}
private void appendIndentedList(Output out, Collection<? extends Outputable> things, String seperator) {
out.indent();
appendList(out, things, seperator);
out.unindent();
}
/**
* Iterate through a Collection and append all entries (using .toString()) to
* a StringBuffer.
*/
private void appendList(Output out, Collection<? extends Outputable> collection, String seperator) {
Iterator<? extends Outputable> i = collection.iterator();
boolean hasNext = i.hasNext();
while (hasNext) {
Outputable curr = (Outputable) i.next();
hasNext = i.hasNext();
curr.write(out);
out.print(' ');
if (hasNext) {
out.print(seperator);
}
out.println();
}
}
/**
* Find all the tables used in the query (from columns, criteria and order).
*
* @return Set of {@link Table}s
*/
private Set<Table> findAllUsedTables() {
Set<Table> tables = new LinkedHashSet<Table>();
addReferencedTablesTo(tables);
return tables;
}
public void addReferencedTablesTo(Set<Table> tables) {
for (Selectable s : selection) {
s.addReferencedTablesTo(tables);
}
for (Criteria c : criteria) {
c.addReferencedTablesTo(tables);
}
for (Order o : order) {
o.addReferencedTablesTo(tables);
}
}
}