package net.sourceforge.javautil.database.query;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import net.sourceforge.javautil.database.query.sql.CompositeCriteriaFragment;
import net.sourceforge.javautil.database.query.sql.CriteriaFragmentParameter;
import net.sourceforge.javautil.database.query.sql.QueryBuilderSelect;
/**
* This allows one to easily scroll through the available data via a {@link IQueryExecutor}
* and a {@link IQueryBuilder} and cach the most recent results.
*
* @author elponderador
* @author $Author: ponderator $
* @version $Id: QueryDataScroller.java 2286 2010-06-14 04:18:27Z ponderator $
*/
public class QueryDataScroller<T extends QueryBuilderSelect> {
protected final IQueryExecutor<T, ?> executor;
protected final T builder;
protected int cachedStart = 0;
protected int cachedAttempted = -1;
protected List cachedResults;
protected boolean moreAvailable = false;
protected Map<String, String> where = new LinkedHashMap<String, String>();
protected Map<String, String> having = new LinkedHashMap<String, String>();
protected Map<String, Object> raw = new LinkedHashMap<String, Object>();
protected Map<String, Object> like = new LinkedHashMap<String, Object>();
public QueryDataScroller(IQueryExecutor<T, ?> executor, T builder) {
this.executor = executor;
this.builder = builder;
}
/**
* @param start The starting record, offset
* @param maxrecords The maximum amount of records to retrieve
* @return A list, possibly empty, of the records returned by the execution of the query
*/
public List getResults (int start, int maxrecords) {
if (cachedResults != null) {
if (start == cachedStart && maxrecords == cachedAttempted) {
return cachedResults;
}
}
builder.setResultLimit(this.cachedStart = start, (this.cachedAttempted = maxrecords) + 1);
cachedResults = executor.execute(builder);
this.moreAvailable = cachedResults.size() > maxrecords;
if (this.moreAvailable) cachedResults.remove(cachedResults.size()-1);
return cachedResults = Collections.unmodifiableList( cachedResults );
}
/**
* Clear any cached results
*/
public void clear () {
this.cachedResults = null;
this.cachedAttempted = -1;
this.cachedStart = -1;
}
/**
* @param index The 'real' index of the record
* @return The object corresponding to the relative cached result index, or null if not available
*/
public Object getRelative (int index) {
if (this.cachedResults == null) return null;
int idx = index - cachedStart;
return idx < 0 || idx >= cachedResults.size() ? null : this.cachedResults.get(idx);
}
/**
* @return The results from the last attempt, null if no attempts have successfully been made
*/
public List getCachedResults() { return cachedResults; }
/**
* @return True if this data scroller has already fetched results at least once, otherwise false
*/
public boolean isHasBeenAttempted () { return this.cachedAttempted != -1; }
/**
* @return True if the last result fetch indicated that there are more records available, otherwise false
*/
public boolean isMoreAvailable() { return moreAvailable; }
/**
* @return The executor being used by this scroller
*/
public IQueryExecutor getExecutor() { return executor; }
/**
* @return The builder being used by this scroller
*/
public IQueryBuilder getBuilder() { return builder; }
/**
* @return The where criteria currently available for modifying the results
*/
public Map<String, String> getWhere() { return where; }
/**
* @return The having criteria currently available for modifying the results
*/
public Map<String, String> getHaving() { return having; }
/**
* @return The currently set raw parameters for the corresponding criteria
*/
public Map<String, Object> getParameters() { return raw; }
/**
* @return The currently set LIKE parameters for the corresponding criteria
*/
public Map<String, Object> getLike() { return like; }
/**
* This will force the query to be regenerated and new results to be retreived
*/
public void applyCriteria () {
CompositeCriteriaFragment where = new CompositeCriteriaFragment(this.builder);
CompositeCriteriaFragment having = new CompositeCriteriaFragment(this.builder);
Map<String, Object> parameters = new LinkedHashMap<String, Object>();
this.appendCriteria(this.where, where, parameters);
this.appendCriteria(this.having, having, parameters);
this.builder.setWhere(where);
this.builder.setHaving(having);
this.builder.setParameters(parameters);
this.cachedResults = null;
}
/**
* @param criteriaSet The criteria set to append from
* @param composite The composite to append to
* @param parameters The parameters to check for matches
*/
protected void appendCriteria (Map<String, String> criteriaSet, CompositeCriteriaFragment composite, Map<String, Object> parameters) {
for (String criteria : criteriaSet.keySet()) {
boolean raw = true;
Object value = this.raw.get(criteria);
if (value == null || "".equals(value)) {
raw = false;
value = this.like.get(criteria);
}
if (value == null || "".equals(value)) continue;
parameters.put(criteria, raw ? value : ("%" + value + "%"));
composite.add(new CriteriaFragmentParameter(builder, criteriaSet.get(criteria), criteria));
}
}
}