/*
* Copyright (C) 2010 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xcmis.search.query.plan;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.xcmis.search.content.Schema.Column;
import org.xcmis.search.model.Limit;
import org.xcmis.search.model.constraint.Constraint;
import org.xcmis.search.model.ordering.Ordering;
import org.xcmis.search.model.source.SelectorName;
import org.xcmis.search.model.source.join.JoinCondition;
import org.xcmis.search.model.source.join.JoinType;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* One step from query execution plan.
*/
public abstract class QueryExecutionPlan implements Iterator<QueryExecutionPlan>
{
/** The set of named selectors (e.g., tables) that this node deals with. */
private Set<SelectorName> selectors;
private final Type type;
/**
*/
public QueryExecutionPlan(Type type)
{
this.type = type;
this.selectors = new HashSet<SelectorName>();
}
/**
* @see java.util.Iterator#hasNext()
*/
public boolean hasNext()
{
return false;
}
/**
* @see java.util.Iterator#next()
*/
public QueryExecutionPlan next()
{
return null;
}
/**
* @see java.util.Iterator#remove()
*/
public void remove()
{
throw new UnsupportedOperationException();
}
/**
* Add a selector to this plan node. This method does nothing if the supplied selector is null.
*
* @param symbol the symbol of the selector
*/
public void addSelector(SelectorName symbol)
{
if (symbol != null)
{
selectors.add(symbol);
}
}
/**
* Add the selectors to this execution step. This method does nothing for any supplied selector that is null.
*
* @param first the first symbol to be added
* @param second the second symbol to be added
*/
public void addSelector(SelectorName first, SelectorName second)
{
if (first != null)
{
selectors.add(first);
}
if (second != null)
{
selectors.add(second);
}
}
/**
* Add the selectors to this execution step. This method does nothing for any supplied selector that is null.
*
* @param names the symbols to be added
*/
public void addSelectors(Iterable<SelectorName> names)
{
for (SelectorName name : names)
{
if (name != null)
{
selectors.add(name);
}
}
}
/**
* Return plan by type
* @param type
* @return
*/
public QueryExecutionPlan findPlanByType(Type type)
{
if (this.type.equals(type))
{
return this;
}
return null;
}
/**
* Get the selectors that are referenced by this execution step.
*
* @return the names of the selectors; never null but possibly empty
*/
public Set<SelectorName> getSelectors()
{
return selectors;
}
/**
* @return the type of the step
*/
public Type getType()
{
return type;
}
/**
*
* @return size;
*/
public int getSize()
{
return 1;
}
protected void getRecursiveString(StringBuilder str, int indentLevel)
{
str.append("\n");
str.append(StringUtils.repeat("\t", indentLevel));
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
getRecursiveString(sb, 0);
return sb.toString();
}
/**
* Plan what join two sources.
*
*/
public static class JoinExecutionPlan extends SourceExecutionPlan
{
/**
* Join type.
*/
private JoinType joinType;
/**
* Join algorithm.
*/
private JoinAlgorithm joinAlgorithm;
/**
* Join condition.
*/
private JoinCondition joinCondition;
/**
* Execution step for left source.
*/
private SourceExecutionPlan leftPlan;
/**
* Execution step for right source.
*/
private SourceExecutionPlan rightPlan;
/**
* @param type
*/
public JoinExecutionPlan()
{
super(Type.JOIN);
}
/**
* @see org.xcmis.search.query.plan.QueryExecutionPlan#getSize()
*/
@Override
public int getSize()
{
return 1 + leftPlan.getSize() + rightPlan.getSize();
}
/**
* @return the joinAlgorithm
*/
public JoinAlgorithm getJoinAlgorithm()
{
return joinAlgorithm;
}
/**
* @return the joinCondition
*/
public JoinCondition getJoinCondition()
{
return joinCondition;
}
/**
* @return the joinType
*/
public JoinType getJoinType()
{
return joinType;
}
/**
* @return the leftPlan
*/
public SourceExecutionPlan getLeftPlan()
{
return leftPlan;
}
/**
* @return the rightPlan
*/
public SourceExecutionPlan getRightPlan()
{
return rightPlan;
}
/**
* @param joinAlgorithm the joinAlgorithm to set
*/
public void setJoinAlgorithm(JoinAlgorithm joinAlgorithm)
{
this.joinAlgorithm = joinAlgorithm;
}
/**
* @param joinCondition the joinCondition to set
*/
public void setJoinCondition(JoinCondition joinCondition)
{
this.joinCondition = joinCondition;
}
/**
* @param joinType the joinType to set
*/
public void setJoinType(JoinType joinType)
{
this.joinType = joinType;
}
/**
* @param leftPlan the leftPlan to set
*/
public void setLeftPlan(SourceExecutionPlan leftPlan)
{
this.leftPlan = leftPlan;
}
/**
* @param rightPlan the rightPlan to set
*/
public void setRightPlan(SourceExecutionPlan rightPlan)
{
this.rightPlan = rightPlan;
}
/**
* @see org.xcmis.search.query.plan.QueryExecutionPlan#getRecursiveString(java.lang.StringBuilder, int)
*/
@Override
protected void getRecursiveString(StringBuilder str, int indentLevel)
{
super.getRecursiveString(str, indentLevel);
str.append("Left:" + leftPlan.toString());
str.append("Right:" + rightPlan.toString());
}
}
/**
* Execution step for limit phase.
*
*/
public static class LimitExecutionPlan extends NestedExecutionPlan
{
private Limit limit;
/**
* @param type
*/
public LimitExecutionPlan()
{
super(Type.LIMIT);
}
/**
* @param type
*/
public LimitExecutionPlan(QueryExecutionPlan childPlan)
{
super(Type.LIMIT, childPlan);
}
/**
* @return the limit
*/
public Limit getLimit()
{
return limit;
}
/**
* @param limit the limit to set
*/
public void setLimit(Limit limit)
{
this.limit = limit;
}
/**
* @see org.xcmis.search.query.plan.QueryExecutionPlan#getRecursiveString(java.lang.StringBuilder, int)
*/
@Override
protected void getRecursiveString(StringBuilder str, int indentLevel)
{
super.getRecursiveString(str, indentLevel);
str.append(getType().getSymbol() + "=" + limit);
childPlan.getRecursiveString(str, indentLevel++);
}
}
public static class NestedExecutionPlan extends QueryExecutionPlan
{
/**
* @see org.xcmis.search.query.plan.QueryExecutionPlan#hasNext()
*/
@Override
public boolean hasNext()
{
return next != null;
}
/**
* @see org.xcmis.search.query.plan.QueryExecutionPlan#next()
*/
@Override
public QueryExecutionPlan next()
{
if (next != null)
{
QueryExecutionPlan result = next;
next = null;
return result;
}
return null;
}
protected QueryExecutionPlan childPlan;
private QueryExecutionPlan next;
/**
* @param type
*/
public NestedExecutionPlan(Type type)
{
super(type);
}
/**
* @param type
*/
public NestedExecutionPlan(Type type, QueryExecutionPlan childPlan)
{
super(type);
this.childPlan = childPlan;
this.next = childPlan;
}
/**
* @see org.xcmis.search.query.plan.QueryExecutionPlan#getSize()
*/
@Override
public int getSize()
{
return 1 + childPlan.getSize();
}
/**
* @see org.xcmis.search.query.plan.QueryExecutionPlan#findPlanByType(org.xcmis.search.query.plan.QueryExecutionPlan.Type)
*/
@Override
public QueryExecutionPlan findPlanByType(Type type)
{
if (getType().equals(type))
{
return this;
}
return childPlan.findPlanByType(type);
}
/**
* @return the childPlan
*/
public QueryExecutionPlan getChildPlan()
{
return childPlan;
}
/**
* @param childPlan the childPlan to set
*/
public void setChildPlan(QueryExecutionPlan childPlan)
{
this.childPlan = childPlan;
}
}
/**
* Execution step for project phase.
*
*/
public static class ProjectExecutionPlan extends NestedExecutionPlan
{
private List<org.xcmis.search.model.column.Column> columns;
/**
* @param type
*/
public ProjectExecutionPlan()
{
super(Type.PROJECT);
}
/**
* @param type
*/
public ProjectExecutionPlan(QueryExecutionPlan childPlan)
{
super(Type.PROJECT, childPlan);
}
/**
* @see org.xcmis.search.query.plan.QueryExecutionPlan#getRecursiveString(java.lang.StringBuilder, int)
*/
@Override
protected void getRecursiveString(StringBuilder str, int indentLevel)
{
super.getRecursiveString(str, indentLevel);
str.append(getType().getSymbol() + "=" + columns);
childPlan.getRecursiveString(str, ++indentLevel);
}
/**
* @return the columns
*/
public List<org.xcmis.search.model.column.Column> getColumns()
{
return columns;
}
/**
* @param columns2 the columns to set
*/
public void setColumns(List<org.xcmis.search.model.column.Column> columns2)
{
this.columns = columns2;
}
}
/**
* Plan for what accumulate information about source
*/
public static class SelectorExecutionPlan extends SourceExecutionPlan
{
/**
* Source alias.
*/
private SelectorName alias;
/**
* Source name
*/
private SelectorName name;
/**
* Desire columns
*/
private List<Column> columns;
/**
* @param type
*/
public SelectorExecutionPlan()
{
super(Type.SELECTOR);
}
/**
* @return the alias
*/
public SelectorName getAlias()
{
return alias;
}
/**
* @return the columns
*/
public List<Column> getColumns()
{
return columns;
}
/**
* @return the name
*/
public SelectorName getName()
{
return name;
}
/**
* @param alias the alias to set
*/
public void setAlias(SelectorName alias)
{
this.alias = alias;
}
/**
* @param columns the columns to set
*/
public void setColumns(List<Column> columns)
{
this.columns = columns;
}
/**
* @param name the name to set
*/
public void setName(SelectorName name)
{
this.name = name;
}
/**
* @see org.xcmis.search.query.plan.QueryExecutionPlan#getRecursiveString(java.lang.StringBuilder, int)
*/
@Override
protected void getRecursiveString(StringBuilder str, int indentLevel)
{
super.getRecursiveString(str, indentLevel);
str.append(getType().getSymbol() + "[" + name + "=" + alias + "{" + columns + "}]");
}
}
/**
* Execution step for sort phase.
*
*/
public static class SortExecutionPlan extends NestedExecutionPlan
{
private List<Ordering> orderings;
/**
* @param type
*/
public SortExecutionPlan()
{
super(Type.SORT);
}
/**
* @param type
*/
public SortExecutionPlan(QueryExecutionPlan childPlan)
{
super(Type.SORT, childPlan);
}
/**
* @return the orderings
*/
public List<Ordering> getOrderings()
{
return orderings;
}
/**
* @param orderings the orderings to set
*/
public void setOrderings(List<Ordering> orderings)
{
this.orderings = orderings;
}
/**
* @see org.xcmis.search.query.plan.QueryExecutionPlan#getRecursiveString(java.lang.StringBuilder, int)
*/
@Override
protected void getRecursiveString(StringBuilder str, int indentLevel)
{
super.getRecursiveString(str, indentLevel);
str.append(getType().getSymbol() + "=" + orderings);
childPlan.getRecursiveString(str, indentLevel++);
}
}
/**
* Ancestor for Selector and Join step.
*
*/
public static abstract class SourceExecutionPlan extends QueryExecutionPlan
{
/**
* @param type
*/
public SourceExecutionPlan(Type type)
{
super(type);
}
}
/**
* Execution step for constraint.
*
*/
public static class WhereExecutionPlan extends NestedExecutionPlan
{
/**
* Constraint for step.
*/
private Constraint constraint;
/**
* @param type
*/
public WhereExecutionPlan()
{
super(Type.WHERE);
}
/**
* @param type
*/
public WhereExecutionPlan(QueryExecutionPlan childPlan)
{
super(Type.WHERE, childPlan);
}
/**
* @return the constraint
*/
public Constraint getConstraint()
{
return constraint;
}
/**
* @param constraint the constraint to set
*/
public void setConstraint(Constraint constraint)
{
this.constraint = constraint;
}
/**
* @see org.xcmis.search.query.plan.QueryExecutionPlan#getRecursiveString(java.lang.StringBuilder, int)
*/
@Override
protected void getRecursiveString(StringBuilder str, int indentLevel)
{
super.getRecursiveString(str, indentLevel);
str.append("Type.WHERE=" + constraint);
childPlan.getRecursiveString(str, indentLevel++);
}
}
/**
* An enumeration dictating the type of plan tree nodes.
*/
public static enum Type {
JOIN("Join"), // A node that defines the join type, join criteria, and join strategy
SELECTOR("Selector"), //A node that defines the 'table' from which the tuples are being obtained
PROJECT("Project"), //A node that defines the columns returned from the node.
WHERE("Where"), //A node that selects a filters the tuples by applying a criteria evaluation filter node (WHERE )
SORT("Sort"), //A node that defines the columns to sort on, the sort direction for each column, and whether to remove duplicates.
LIMIT("Limit"); //A node that limits the number of tuples returned
private static final Map<String, Type> TYPE_BY_SYMBOL;
static
{
Map<String, Type> typesBySymbol = new HashMap<String, Type>();
for (Type type : Type.values())
{
typesBySymbol.put(type.getSymbol().toUpperCase(), type);
}
TYPE_BY_SYMBOL = Collections.unmodifiableMap(typesBySymbol);
}
private final String symbol;
private Type(String symbol)
{
this.symbol = symbol;
}
/**
* Get the symbol representation of this node type.
*
* @return the symbol; never null and never empty
*/
public String getSymbol()
{
return symbol;
}
/**
* {@inheritDoc}
*
* @see java.lang.Enum#toString()
*/
@Override
public String toString()
{
return symbol;
}
/**
* Attempt to find the Type given a symbol. The matching is done independent of case.
*
* @param symbol the symbol
* @return the Type having the supplied symbol, or null if there is no Type with the supplied symbol
* @throws IllegalArgumentException if the symbol is null
*/
public static Type forSymbol(String symbol)
{
Validate.notNull(symbol, "The symbol argument may not be null");
return TYPE_BY_SYMBOL.get(symbol.toUpperCase().trim());
}
}
}