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); }