
toxgene.core.genes.trees.Query Maven / Gradle / Ivy
/**
* Implements a toxgene.core.parser/executor for queries over lists
*
* @author Denilson Barbosa
* @version 0.1
*/
package toxgene.core.genes.trees;
import toxgene.core.ToXgeneErrorException;
import toxgene.core.genes.lists.PathNotFoundException;
import toxgene.core.genes.lists.ToxList;
import toxgene.core.genes.lists.ToxListElement;
import toxgene.core.genes.lists.ToxListElementException;
import toxgene.interfaces.ToXgeneReporter;
import toxgene.util.Date;
import java.util.Vector;
import java.util.Collections;
public class Query implements Expression{
private static final int NOP = 0;
private static final int SUM = 1;
private static final int AVG = 2;
private static final int MIN = 3;
private static final int MAX = 4;
private static final int COUNT = 5;
private static final int LEN = 6;
private static final int CONCAT = 7;
private static final int DISTINCT = 8;
private static final int LIST = 1;
private static final int SCAN = 2;
/**
* Prefix of the path expression; used for referencing external scan loops
* inside a query.
*/
private String scanName;
/**
* Path expression to be queried, relative to the scan operator.
*/
private String path;
/**
* Absolute path expression for this query, i.e.,relative to the root of
* the list. This path is necessary to get the datatype of the node being
* queried from a list.
*/
private String absolutePath;
/**
* Path expression relative to the current element of the given scan
* element associated with the query.
*/
private String relativePath;
/**
* Datatype for the input elements to this query, i.e., the result of
* querying the path expression.
*/
private String type;
/**
* Datatype of the resulting operation of this query.
*/
private int resultType;
private int AGG, DT;
private ToxList list;
private boolean fresh, copy_literal, copy_tag;
private ToxScan scan;
private Vector result;
private int mode;
private String expression;
/**
* Prefix used to get the datatypes for queries defined in where clauses
* for iterator genes.
*/
private String prefix;
/**
* Element node in the DOM tree corresponding to this scan element in the
* template document. Used for error reporting.
*/
private int templateNodeLocation;
private ToXgeneReporter tgReporter;
public Query(String query, ToxList list, String prefix, int nodeLocation,
ToXgeneReporter reporter){
this.list = list;
this.prefix = prefix;
this.templateNodeLocation = nodeLocation;
mode = LIST;
tgReporter = reporter;
initialize(query);
}
public Query(String query, ToxScan scan, String prefix, int nodeLocation,
ToXgeneReporter reporter){
if (scan == null){
throw new ToXgeneErrorException("path expression "+query
+" declared outside the scope \n"+
"of a tox-scan or tox-sample element!",
templateNodeLocation);
}
this.scan = scan;
this.prefix = prefix;
this.templateNodeLocation = nodeLocation;
mode = SCAN;
tgReporter = reporter;
initialize(query);
this.list = scan.list();
}
private void initialize(String query){
//store this for debugging later on
expression = query;
String temp;
int start = 0, end;
//initially all queries are stale
fresh = copy_literal = copy_tag = false;
//find the delimiters of the query
end = query.indexOf('[',0);
if (end == -1){
throw new ToXgeneErrorException("could not parse a query from: "+query, templateNodeLocation);
}
start = end+1;
end = query.indexOf(']', start);
if (end == -1){
throw new ToXgeneErrorException("could not parse a query from: "+query, templateNodeLocation);
}
//get the path expression
path = query.substring(start, end);
if (mode == SCAN){
//check whether this path is prefixed
if (path.charAt(0) == '$'){
int index = path.indexOf('/');
scanName = path.substring(1,index);
path = path.substring(index+1);
}
else{
scanName = "";
}
//the actual query path is updated later. we need to keep this around
//so that we can get the correct path for extracting the type of the
//query
relativePath = path;
//register the query within a scan operator. At this point, the correct
//scan operator invokes the setScan() method
scan.register(this);
if (relativePath.compareTo("!") == 0){
//this is a "copy literal" query
copy_literal = true;
absolutePath = scan.absolutePath();
type = scan.getType();
}
else{
if (relativePath.compareTo("~") == 0){
//this is a "copy tag" query
copy_tag = true;
absolutePath = scan.absolutePath();
type = "string";
}
else{
//this path is used for querying the elements
path = scan.relativePath() + "/" + path;
absolutePath = scan.absolutePath() + "/" + relativePath;
if (path.charAt(path.length()-1) == '/'){
type = "complex";
}
else{
try{
type = scan.list().getType(absolutePath);
}
catch(PathNotFoundException e){
throw new ToXgeneErrorException("invalid path ["+absolutePath+
"] defining query.", templateNodeLocation);
}
}
}
}
}
else{
//in the case of queries over lists we *always* query an absolute path
if (prefix != null){
path = prefix+"/"+path;
}
if (path.length() != 0){
absolutePath = list.name()+"/"+path;
}
else{
absolutePath = list.name();
}
if (path.charAt(path.length()-1) == '/'){
type = "complex";
}
else{
try{
type = list.getType(absolutePath);
}
catch(PathNotFoundException e){
throw new ToXgeneErrorException("invalid path ["+absolutePath+
"] defining query.");
}
}
}
//check the datatype of the expression
if (type.compareTo("string") == 0){
DT = Expression.STRING;
}
else{
if (type.compareTo("integer") == 0){
DT = Expression.INTEGER;
}
else{
if (type.compareTo("real") == 0){
DT = Expression.REAL;
}
else{
if (type.compareTo("date") == 0){
DT = Expression.DATE;
}
else{
//it has got to be complex
if (type.compareTo("complex") != 0){
throw new ToXgeneErrorException("Spurious datatype in Query.java!");
}
DT = Expression.COMPLEX;
AGG = NOP;
}
}
}
}
//get the (optional) operator and set the datatype of the result of
//this query
AGG = NOP;
temp = query.substring(0, start-1);
resultType = DT; //this is the default
if (temp.length() != 0){
// if (resultType == Expression.COMPLEX){
// throw new ToXgeneErrorException("Invalid query expression: cannot apply aggregate "+
// "operator over complex objects!");
// }
if (temp.compareTo("SUM") == 0){
AGG = SUM;
}
else{
if (temp.compareTo("AVG") == 0){
AGG = AVG;
}
else{
if (temp.compareTo("MIN") == 0){
AGG = MIN;
}
else{
if (temp.compareTo("MAX") == 0){
AGG = MAX;
}
else{
if (temp.compareTo("COUNT") == 0){
AGG = COUNT;
resultType = Expression.INTEGER;
}
else{
if (temp.compareTo("LEN") == 0){
AGG = LEN;
resultType = Expression.INTEGER;
}
else{
if (temp.compareTo("CONCAT") == 0){
AGG = CONCAT;
resultType = Expression.STRING;
}
else{
if (temp.compareTo("DISTINCT") == 0){
AGG = DISTINCT;
}
else{
throw new ToXgeneErrorException("invalid aggregation operator: "+temp,
templateNodeLocation);
}
}
}
}
}
}
}
}
}
//check the consistency of this query
switch (AGG){
case SUM:{
if (DT != Expression.INTEGER && DT!=Expression.REAL){
throw new ToXgeneErrorException("in query "+query+"\n SUM() cannot be applied to "
+"non-numerical values", templateNodeLocation);
}
break;
}
case AVG:{
if (DT != Expression.INTEGER && DT!=Expression.REAL){
throw new ToXgeneErrorException("in query "+query+"\n AVG() cannot be applied to "
+"non-numerical values", templateNodeLocation);
}
break;
}
case CONCAT:{
if (DT != Expression.STRING){
tgReporter.explain("input argument of CONCAT() not STRING in "+
query+"\n\tcasting values to STRING");
}
break;
}
case LEN: {
if (DT != Expression.STRING){
tgReporter.explain("input argument of LEN() not STRING in "+
query+"\n\tcasting values to STRING");
}
break;
}
}
}
public String relativePath(){
return relativePath;
}
public String path(){
return path;
}
public String scanName(){
return scanName;
}
public void setScan(ToxScan scan){
this.scan = scan;
}
public int expressionType(){
return resultType;
}
public String expression(){
return expression;
}
/**
* Updates the status of the query and sets the number of elements in the
* corresponding scan
*/
public void stale(){
fresh = false;
}
public boolean fresh(){
return fresh;
}
public int getQtty(){
if (!fresh){
evaluate();
}
if (AGG == NOP){
return result.size();
}
return 1;
}
public String absolutePath(){
return absolutePath;
}
/**
* evaluates the query. This method is called by a @see ToxScan
* object. Since the ToxScan might modify the data (e.g., remove
* data that does not conform to a given where clause), we return a
* copy of the elements in the list.
*/
public Vector evaluateSCAN(){
if (mode == SCAN){
if (!fresh){
ToxListElement element = scan.refresh();
//this is SIMPLY A VERY UGLY HACK!!!!
if (copy_literal){
//since my getType for ToxElements is ridiculous, in some cases I
//have a tox-get whose type is COMPLEX, when in fact it should be
//a literal so I have to use this aberration of mother Nature.
result = new Vector(1);
result.add(element);
}
else{
result = list.query(element, path);
}
fresh = true;
}
}
else{
result = list.querySCAN(path);
}
return result;
}
/**
* evaluates the query. This method is called from a ToxScan object
* that will not modify the elements in the result. So, we can
* return the original data and not a copy.
*/
public Vector evaluateSOFT(){
if (mode == SCAN){
if (!fresh){
ToxListElement element = scan.refresh();
//this is SIMPLY A VERY UGLY HACK!!!!
if (copy_literal){
//since my getType for ToxElements is ridiculous, in some cases I
//have a tox-get whose type is COMPLEX, when in fact it should be
//a literal so I have to use this aberration of mother Nature.
result = new Vector(1);
result.add(element);
}
else{
result = list.query(element, path);
}
fresh = true;
}
}
else{
result = list.querySOFT(path);
}
return result;
}
public Vector evaluateSCAN(ToxListElement element){
return list.query(element, path);
}
public Vector evaluate(){
switch (mode){
case SCAN:{
if (!fresh){
ToxListElement element = scan.refresh();
//these are just shortcuts
if (copy_literal){
result = new Vector(1);
result.add(element.getContent());
}
else{
if (copy_tag){
result = new Vector(1);
result.add(list.getElementTagName(element));
}
else{
result = evaluate(element, path);
fresh = true;
}
}
}
break;
}
case LIST:{
result = list.query(path);
}
}
return result;
}
public Vector evaluateWHERE(){
return null;
}
public Vector evaluate(ToxListElement element){
return evaluate(element, path);
}
private Vector evaluate(ToxListElement element, String path){
Vector temp = list.query(element, path);
int size = temp== null? 0 : temp.size();
Vector result = new Vector(size);
//we need to unpack the string literals from the query result set
try{
for (int i=0; i max){
max = temp;
}
}
result.add(Long.toString(max));
break;
}
case Expression.REAL:{
double max = Double.parseDouble((String) answer.get(0));
result = new Vector(1);
for (int i=1; i max){
max = temp;
}
}
result.add(Double.toString(max));
break;
}
case Expression.DATE:{
Date max = Date.parseDate((String) answer.get(0));
for (int i=1; i 0){
max = temp;
}
}
result.add(max);
break;
}
}
break;
}
case COUNT:{
result = new Vector(1);
result.add(Integer.toString(answer.size()));
break;
}
case LEN:{
if (answer.size() > 1){
throw new ToXgeneErrorException("cannot apply LEN() over a set of strings", templateNodeLocation);
}
result = new Vector(1);
if (answer.size() == 0){
result.add("0");
}
int length = ((String)answer.get(0)).length();
result.add(Integer.toString(length));
break;
}
case CONCAT:{
result = new Vector(1);
String temp = "";
for (int i=0; i
© 2015 - 2025 Weber Informatics LLC | Privacy Policy