package jadex.javaparser.javaccimpl;
import jadex.commons.SReflect;
import jadex.commons.collection.SCollection;
import jadex.commons.collection.SortedList;
import jadex.javaparser.IValueFetcher;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
/**
* Node for OQL like select statements.
*/
public class SelectNode extends ExpressionNode
{
//-------- constants --------
/** The selection mode for returning a set of elements (default). */
public static final int ALL = 1;
/** The selection mode for returning the first matching element. */
public static final int ANY = 2;
/** The selection mode for returning a single matching element. */
public static final int IOTA = 3;
/** The ascending order direction. */
public static final int ASC = 1;
/** The descending order direction. */
public static final int DESC = 2;
//-------- attributes --------
/** The selection mode. */
protected int mode;
/** The variable names. */
protected String[] vars;
/** The flag indicating presence of a where clause. */
protected boolean where;
/** The flag indicating presence of an order by clause. */
protected boolean orderby;
/** The order direction. */
protected int order;
//-------- constructors --------
/**
* Create a node.
* @param p The parser.
* @param id The id.
*/
public SelectNode(ParserImpl p, int id)
{
super(p, id);
this.mode = ALL;
this.order = ASC;
}
//-------- attribute accessors --------
/**
* Set the token text.
* @param text The token text.
*/
public void setText(String text)
{
super.setText(text);
this.mode = fromString(text);
}
/**
* Set the variable names.
*/
public void setVariables(String[] vars)
{
this.vars = vars;
}
/**
* Set the where clause flag.
*/
public void setWhere(boolean where)
{
this.where = where;
}
/**
* Set the where order by flag.
*/
public void setOrderBy(boolean orderby)
{
this.orderby = orderby;
}
/**
* Set the ordering direction.
*/
public void setOrder(String order)
{
this.order = orderFromString(order);
}
//-------- evaluation --------
/**
* Precompile the node.
*/
public void precompile()
{
// Todo: ???
}
/**
* Evaluate the expression in the given state
* with respect to given parameters.
* @param params The parameters (string, value pairs), if any.
* @return The value of the term.
*/
public Object getValue(final IValueFetcher fetcher) //throws Exception
{
// Create result set container.
AbstractList results;
SortedList orders;
if(orderby)
{
orders = new SortedList(order==ASC);
results = new LinkedList();
}
else
{
results = new ArrayList();
orders = null;
}
// Evaluate all subnodes.
int subnode = 0;
// Ignore return type declaration. (hack!!!)
if(jjtGetChild(subnode) instanceof TypeNode)
subnode++;
// The element (select clause).
ExpressionNode element = (ExpressionNode)jjtGetChild(subnode++);
// Check if select clause depends on at least one variable,
// otherwise print warning.
String select = element.toPlainString();
boolean contained = false;
for(int i=0; !contained && i<vars.length; i++)
contained = select.indexOf(vars[i])!=-1;
if(!contained)
{
System.out.println("Warning: Selection expression does not refer to any variables: "+toPlainString());
}
// The collections (from clause).
Object[] collections = new Object[vars.length];
Iterator[] iterators = new Iterator[vars.length];
// Map params2 = SCollection.createNestedMap(params); // Local parameters.
final Map params2 = SCollection.createHashMap();
for(int i=0; i<vars.length; i++)
{
// Ignore return type declaration. (hack!!!)
if(jjtGetChild(subnode) instanceof TypeNode)
subnode++;
// Evaluate collection expression.
collections[i] = ((ExpressionNode)jjtGetChild(subnode++))
.getValue(fetcher);
// Create iterator over collection.
iterators[i] = SReflect.getIterator(collections[i]);
if(iterators[i].hasNext())
{
// Initial local parameters.
params2.put(vars[i], iterators[i].next());
}
else
{
// When one collection is empty, empty result is returned.
// Hack !!!
if(mode==ALL)
{
return results;
}
else
{
return null;
}
}
}
IValueFetcher f2 = new IValueFetcher()
{
public Object fetchValue(String name)
{
Object ret = params2.get(name);
if(ret==null && !params2.containsKey(name))
ret = fetcher.fetchValue(name);
return ret;
}
public Object fetchValue(String name, Object object)
{
return fetcher.fetchValue(name, object);
}
};
// The condition (where clause, if any).
ExpressionNode condition = where ? (ExpressionNode)jjtGetChild(subnode++) : null;
// The order expression (if any).
ExpressionNode orderexp = orderby ? (ExpressionNode)jjtGetChild(subnode++) : null;
while(true)
{
// Evaluate where clause, if any.
if(!where ||
((Boolean)condition.getValue(f2)).booleanValue())
{
// Check unique match for iota.
if(mode==IOTA && results.size()>0)
{
throw new RuntimeException("No unique element for iota selection: "+this);
}
// Evaluate result element.
Object result = element.getValue(f2);
if(orderby)
{
// Use insertion order index for adding elements.
// For ASC add insert from end of list,
// for DESC add insert from start of list.
int index = orders.insertElement(
order==ASC ? orders.size() : 0,
orderexp.getValue(f2));
results.add(index, result);
}
else
{
results.add(result);
}
// For any selects, return with first matching element.
if(mode==ANY && !orderby)
{
break;
}
}
// Calculate next join tuple (stored as local parameters).
int i=0;
for(; i<iterators.length && !iterators[i].hasNext(); i++)
{
// Overflow: Re-init iterator.
iterators[i] = SReflect.getIterator(collections[i]);
params2.put(vars[i], iterators[i].next());
}
if(i<iterators.length)
{
params2.put(vars[i], iterators[i].next());
}
else
{
// Overflow in last iterator: done.
// Hack: Unnecessarily re-inits all iterators before break ?
break;
}
}
if(mode==ALL)
{
return results;
}
else
{
if(results.size()>0)
return results.iterator().next();
else
return null;
}
}
/**
* Create a string representation of this node and its subnodes.
* @return A string representation of this node.
*/
public String toPlainString()
{
int subnode = 0;
String ret = "SELECT " + toString(mode) + " " + subnodeToString(subnode++)
+ " FROM ";
for(int i=0; i<vars.length; i++)
{
if(i!=0)
ret += ", ";
ret += vars[i] + " IN " + subnodeToString(subnode++);
}
if(where)
ret += " WHERE " + subnodeToString(subnode++);
if(orderby)
{
ret += " ORDER BY " + subnodeToString(subnode++);
if(order==DESC) ret+=" DESC";
}
return ret;
}
/**
* Get unbound parameter nodes.
* @return The unbound parameter nodes.
*/
public ParameterNode[] getUnboundParameterNodes()
{
// Exclude nodes from select variables.
ParameterNode[] param = super.getUnboundParameterNodes();
ArrayList ret = new ArrayList();
for(int i=0; i<param.length; i++)
{
boolean found = false;
for(int j=0; !found && j<vars.length; j++)
{
found = param[i].getText().equals(vars[j]);
}
if(!found)
{
ret.add(param[i]);
}
}
return (ParameterNode[])ret.toArray(new ParameterNode[ret.size()]);
}
//-------- static part --------
/**
* Convert a selection mode to a string representation.
* @param mode The mode
* @return A string representation of the mode.
*/
public static String toString(int mode)
{
switch(mode)
{
case ALL:
return "ALL";
case ANY:
return "ANY";
case IOTA:
return "IOTA";
default:
return ""+mode;
}
}
/**
* Convert an ordering direction to a string representation.
* @param order The ordering direction
* @return A string representation of the ordering direction.
*/
public static String orderToString(int order)
{
switch(order)
{
case ASC:
return "ASC";
case DESC:
return "DESC";
default:
return ""+order;
}
}
/**
* Convert a selection mode from a string representation.
* @param mode The mode as string.
* @return The int value of the mode.
*/
public static int fromString(String mode)
{
if("ALL".equalsIgnoreCase(mode))
{
return ALL;
}
else if("ANY".equalsIgnoreCase(mode))
{
return ANY;
}
else if("IOTA".equalsIgnoreCase(mode))
{
return IOTA;
}
else
{
throw new ParseException("Unknown selection mode: "+mode);
}
}
/**
* Convert an ordering direction from a string representation.
* @param order The ordering direction as string.
* @return The int value of the ordering direction.
*/
public static int orderFromString(String order)
{
if("ASC".equalsIgnoreCase(order))
{
return ASC;
}
else if("DESC".equalsIgnoreCase(order))
{
return DESC;
}
else
{
throw new ParseException("Unknown ordering direction: "+order);
}
}
}