package adql.search;
/*
* This file is part of ADQLLibrary.
*
* ADQLLibrary 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 3 of the License, or
* (at your option) any later version.
*
* ADQLLibrary 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 ADQLLibrary. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2012-2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
* Astronomisches Rechen Institut (ARI)
*/
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Stack;
import adql.query.ADQLIterator;
import adql.query.ADQLObject;
import adql.query.ADQLQuery;
/**
* <p>Lets searching ADQL objects which match with the condition defined in the function {@link #match(ADQLObject)}.</p>
* <ul>
* <li>By default, this search handler does not search recursively (that's to say, it does not search in sub-queries).</li>
* <li>By default, this search handler does not stop to the first matching object.</li>
* <li>The matching objects are simply collected in an ArrayList.</li>
* </ul>
*
* @author Grégory Mantelet (CDS;ARI)
* @version 1.2 (12/2013)
*
* @see SearchColumnHandler
*/
public abstract class SimpleSearchHandler implements ISearchHandler {
/** Indicates whether this handler must search also in sub-queries. */
private boolean recursive = false;
/** Indicates whether this handler must stop at the first match. */
private boolean firstMatch = false;
/** List of all matching ADQL objects. */
protected final ArrayList<ADQLObject> results;
/**
* <p>Builds a SimpleSearchHandler:</p>
* <ul>
* <li>not recursive,</li>
* <li>and which does not stop at the first match.</li>
* </ul>
*/
public SimpleSearchHandler(){
results = new ArrayList<ADQLObject>();
}
/**
* Builds a SimpleSearchHandler which does not stop at the first match.
*
* @param recursive <i>true</i> to search also in sub-queries, <i>false</i> otherwise.
*/
public SimpleSearchHandler(boolean recursive){
this();
this.recursive = recursive;
}
/**
* Builds a SimpleSearchHandler.
*
* @param recursive <i>true</i> to search also in sub-queries, <i>false</i> otherwise.
* @param onlyFirstMatch <i>true</i> to stop at the first match, <i>false</i> otherwise.
*/
public SimpleSearchHandler(boolean recursive, boolean onlyFirstMatch){
this(recursive);
this.firstMatch = onlyFirstMatch;
}
/**
* Tells whether this handler must search also in sub-queries.
*
* @return <i>true</i> if recursive, <i>false</i> otherwise.
*/
public final boolean isRecursive(){
return recursive;
}
/**
* Lets configuring this handler so that it must search also in sub-queries or not.
*
* @param recursive <i>true</i> to search recursively, <i>false</i> otherwise.
*/
public final void setRecursive(boolean recursive){
this.recursive = recursive;
}
/**
* Tells whether this handler must stop at the first match.
*
* @return <i>true</i> if it stops at the first match, <i>false</i> otherwise.
*/
public final boolean onlyFirstMatch(){
return firstMatch;
}
/**
* Lets configuring this handler so that it must stop at the first match.
*
* @param firstMatch <i>true</i> if it must stop at the first match, <i>false</i> otherwise.
*/
public final void setOnlyFirstMatch(boolean firstMatch){
this.firstMatch = firstMatch;
}
@Override
public final Iterator<ADQLObject> iterator(){
return results.iterator();
}
@Override
public final int getNbMatch(){
return results.size();
}
/**
* Indicates whether the research must finish now or not:
* by default, the research ends only at the first match if it has been asked (see {@link #setOnlyFirstMatch(boolean)}).
*
* @return <i>true</i> if the research must end now, <i>false</i> otherwise.
*/
protected boolean isFinished(){
return firstMatch && !results.isEmpty();
}
/**
* Indicates whether the research must continue inside the given ADQL object or not:
* by default, it returns always <i>true</i> except if the given object is an ADQL query while the research must not be recursive.
*
* @param obj An ADQL object.
*
* @return <i>true</i> if the research must continue inside the given ADQL object, <i>false</i> otherwise.
*/
protected boolean goInto(ADQLObject obj){
return recursive || !(obj instanceof ADQLQuery);
}
/**
* Adds the given ADQL object as one result of the research.
*
* <p><b><u>Warning:</u> the second parameter (<i>it</i>) may be <i>null</i> if the given match is the root search object itself.</b></p>
*
* @param matchObj An ADQL object which has matched to the research criteria.
* @param it The iterator from which the matched ADQL object has been extracted.
*/
protected void addMatch(ADQLObject matchObj, ADQLIterator it){
results.add(matchObj);
}
/**
* Resets this handler before the beginning of the research:
* by default, the list of results is cleared.
*/
protected void reset(){
results.clear();
}
@Override
public final void search(final ADQLObject startObj){
reset();
if (startObj == null)
return;
// Test the root search object:
if (match(startObj))
addMatch(startObj, null);
Stack<ADQLIterator> stackIt = new Stack<ADQLIterator>();
ADQLObject obj = null;
ADQLIterator it = startObj.adqlIterator();
while(!isFinished()){
// Fetch the next ADQL object to test:
do{
if (it != null && it.hasNext())
obj = it.next();
else if (!stackIt.isEmpty())
it = stackIt.pop();
else
return;
}while(obj == null);
// Add the current object if it is matching:
if (match(obj))
addMatch(obj, it);
// Continue the research inside the current object:
if (goInto(obj)){
stackIt.push(it);
it = obj.adqlIterator();
}
obj = null;
}
}
/**
* Only tests whether the given ADQL object checks the search conditions.
*
* @param obj The ADQL object to test. (warning: this object may be <i>null</i> !)
*
* @return <i>true</i> if the given object checks the search conditions, <i>false</i> otherwise.
*/
protected abstract boolean match(ADQLObject obj);
}