All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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