B. ADQL tree Part 2

In order to help the navigation and the manipulation in the tree generated by the parser, the library provides two useful features...

ADQLIterator

Any object into ADQLQuery (including itself and its clauses) has a function called adqlIterator(). This function lets iterating on all ADQL objects (ADQLObject) contained into the target object of the function. It is particularly true for the clauses where this function returns the same items as their function iterator():

ADQLIterator it = query.getSelect().adqlIterator();
System.out.println("ADQL objects into the SELECT clause:");
while(it.hasNext())
	System.out.println("  * "+it.next().toADQL());
warning:

However, in the case of the clause FROM, adqlIterator() returns all ADQLTable, ADQLJoin, ADQLColumn and ADQLQuery.

Thanks to adqlIterator(), it is now possible to browse the tree node by node so that returning objects that match a given condition...

UML class diagram of ADQLIterator.

Search handlers

The interface ISearchHandler defines the main API for any search handler:

The library provides a simple abstract implementation of this interface so that managing by default the matching objects and the navigation into the tree from the given node: SimpleSearchHandler. This implementation, uses the function adqlIterator() to browse all the ADQL objects contained into the given object. It collects all ADQL objects for which the function match(ADQLObject) returns true. All matching items are collected into an array list so that being returned after the call of search(ADQLObject) thanks to iterator().

With SimpleSearchHandler you can decide to stop the research at the first matching object to make a recursive research. "Recursive research" means that the research will continue into any intern ADQLQuery. For instance, if you search all ADQL objects into the clause FROM of this query:

SELECT *
FROM A JOIN (SELECT id FROM B WHERE foo BETWEEN 1 AND 3.7) C ON A.oid = C.oid
WHERE bar = 'stuff';

The research will return: A, id, B, foo, A.oid, C.oid, (SELECT id FROM B WHERE foo BETWEEN 1 AND 3.7), SELECT id, ...

Why "Simple"SearchHandler?

This abstract implementation is qualified Simple because results are simply collected into an array list. But we could image a TreeSearchHandler which keeps the whole path from the given object to a matching object. Thus, results would be accessible through a tree, hence TreeSearchHandler.

UML class diagram of the interface ISearchHandler.

Make your own SearchHandler: SearchColumnHandler & SearchPointHandler

To make your own search handlers, you can either implement the interface ISearchHandler or extend the class SimpleSearchHandler. The second option is the most suitable if you do not need more information about the matching objects than the matching objects themselves. Indeed, you just have to override the function match(ADQLObject) to give the search condition.

For instance, if you need to search all column references:

public class SearchColumnHandler extends SimpleSearchHandler {

	public SearchColumnHandler() {
		super();
	}

	public SearchColumnHandler(boolean recursive) {
		super(recursive);
	}

	public SearchColumnHandler(boolean recursive, boolean onlyFirstMatch) {
		super(recursive, onlyFirstMatch);
	}

	public boolean match(ADQLObject obj) {
		return (obj instanceof ADQLColumn);
	}

}
Class already in the library!

SearchColumnHandler is already provided in the library.

UML class diagram of SearchColumnHandler.

Obviously you can image research more complex, as in the following example. Indeed, we research all POINT functions which correspond to the position of astronomical objects. That's to say all the positions whose the coordinates are "data.ra" and "data.dec":

public class SearchPointHandler extends SimpleSearchHandler {
	public SearchPointHandler() { super(); }
	public SearchPointHandler(boolean recursive) { super(recursive); }
	public SearchPointHandler(boolean recursive, boolean onlyFirstMatch) { super(recursive, onlyFirstMatch); }

	/* Only the POINT functions which use the 2 columns of the table "data": "ra" and "dec".*/
	public boolean match(ADQLObject obj) {
		try{
			PointFunction point = (PointFunction)obj;
			ADQLColumn col1 = (ADQLColumn)point.getCoord1(), col2 = (ADQLColumn)point.getCoord2();
			if (col1.getDBLink() != null && col2.getDBLink() != null){
				// if their table is "data":
				if (col1.getDBLink().getTable().getDBName().equalsIgnoreCase("data")
				    && col1.getDBLink().getTable().getDBName().equals(col2.getDBLink().getTable().getDBName())){
					// "true" if the 1st coordinate is "ra" and the 2nd "dec":
					return col1.getColumnName().equalsIgnoreCase("ra")
					       && col2.getColumnName().equalsIgnoreCase("dec");
				}
			}
		}catch(ClassCastException cce){;}
		return false;
	}
}

In addition to let us to browse the ADQL tree, ADQLIterator has the function replace(ADQLObject) which is able to replace objects inside the target object.

Replace handlers

Given that, the interface ISearchHandler has been extended so that adding the function searchAndReplace(ADQLQuery): IReplaceHandler. Consequently, the class SimpleSearchHandler has been extended to get SimpleReplaceHandler. This implementation has one additional abstract function: getReplacer(ADQLObject). This function must return the ADQL object which should replace the given one.

Don't want to replace a match?

If you do not want to replace an ADQL object, getReplacer(ADQLObject) must return the object given in parameter.

How to get the parent of an object?

There is no way to get the parent of a given ADQL object ! Thus your search condition must be the object which will be modified or replaced. See the following examples.

RemoveHandler

If getReplacer(ADQLObject) returns null, the given object should be removed from its parent. It is exactly what does the class RemoveHandler.

UML class diagram of the interface IReplaceHandler.

Make your own ReplaceHandler: ReplacePointHandler

By extending SimpleReplaceHandler, you have now to override two functions: match(ADQLObject) and getReplacer(ADQLObject).

Now, let's transform the previous example - SearchPointHandler - in ReplacePointHandler in order to replace all POINT(..., ra, dec) by the column coord:

public class SearchReplaceHandler extends SimpleReplaceHandler {
	public SearchReplaceHandler() { super(); }
	public SearchReplaceHandler(boolean recursive) { super(recursive); }
	public SearchReplaceHandler(boolean recursive, boolean onlyFirstMatch) { super(recursive, onlyFirstMatch); }

	/* Only the POINT functions which use the 2 columns of the table "data": "ra" and "dec".*/
	public boolean match(ADQLObject obj) {
		... // see SearchColumnHandler.match(ADQLObject)
	}
	
	/* Replace by: "data.coord" (or <tableAlias>".coord").  */
	public ADQLObject getReplacer(ADQLObject objToReplace) {
		try{
			PointFunction point = (PointFunction)objToReplace;
			ADQLColumn col1 = (ADQLColumn)point.getCoord1();
			if (col1.getAdqlTable() != null){
				String tableAlias = col1.getAdqlTable().getAlias();
				if (tableAlias != null)
					return new ADQLColumn(tableAlias, "coord");
				else
					return new ADQLColumn("basic_data", "coord");
			}else
				return new ADQLColumn("basic_data", "coord");
		}catch(ClassCastException cce){;}
		return null;
	}
}