/**
* Copyright (C) 2001-2006 France Telecom R&D
*/
package org.objectweb.speedo.query.jdo;
import org.objectweb.jorm.api.PMapper;
import org.objectweb.jorm.lib.JormPathHelper;
import org.objectweb.jorm.type.api.PType;
import org.objectweb.medor.api.EvaluationException;
import org.objectweb.medor.api.MedorException;
import org.objectweb.medor.eval.prefetch.api.PrefetchBufferFactory;
import org.objectweb.medor.expression.api.ExpressionException;
import org.objectweb.medor.expression.api.ParameterOperand;
import org.objectweb.medor.expression.lib.BasicParameterOperand;
import org.objectweb.medor.optim.api.QueryTransformer;
import org.objectweb.medor.optim.jorm.Jorm2Rdb;
import org.objectweb.medor.optim.lib.BasicQueryRewriter;
import org.objectweb.medor.optim.lib.FlattenQueryTreeRule;
import org.objectweb.medor.optim.lib.IndexesGenerator;
import org.objectweb.medor.optim.lib.PushNotInExpressionRule;
import org.objectweb.medor.optim.rdb.Like2SQL;
import org.objectweb.medor.query.api.QueryTree;
import org.objectweb.medor.query.jorm.lib.JormQueryTreeHelper;
import org.objectweb.medor.query.lib.QueryTreePrinter;
import org.objectweb.medor.type.lib.PTypeSpaceMedor;
import org.objectweb.medor.type.lib.QType;
import org.objectweb.perseus.persistence.api.PersistenceException;
import org.objectweb.speedo.api.SpeedoException;
import org.objectweb.speedo.lib.Personality;
import org.objectweb.speedo.mapper.api.JormFactory;
import org.objectweb.speedo.mapper.lib.DelegatePMapper;
import org.objectweb.speedo.mim.api.PersistentObjectItf;
import org.objectweb.speedo.pm.api.POManagerItf;
import org.objectweb.speedo.pm.jdo.api.JDOPOManagerItf;
import org.objectweb.speedo.query.api.QueryDefinition;
import org.objectweb.speedo.query.lib.AbstractCompiledQuery;
import org.objectweb.speedo.workingset.jdo.api.JDOTransactionItf;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
import javax.jdo.JDOException;
import javax.jdo.JDOUserException;
public abstract class JDOAbstractCompiledQuery extends AbstractCompiledQuery {
/**
* Fields from javax.jdo.Query
*/
protected JDOQueryDefinitionImpl qd = null;
/**
* vparams, hparams and hvars are internal objects to manipulate the list of
* parameters, and the list of variables.
*/
protected Map hparams = null;
protected Map paramName2paramClass = null;
protected Map hvars = null;
protected Logger varParserlogger = null;
protected Logger filterParserLogger = null;
protected abstract Object executeQT(JDOPOManagerItf pm,
ParameterOperand[] pos,
QueryDefinition userqd)
throws EvaluationException, MedorException, SpeedoException ;
public void init(Logger l, Logger logParserVar, Logger logParserFil,
PMapper m, PrefetchBufferFactory pbf, JormFactory _jf) {
init(l, m, pbf, _jf);
varParserlogger = logParserVar;
filterParserLogger = logParserFil;
}
public void setMapper(PMapper m) {
mapper = new DelegatePMapper(m, Personality.JDO);
if (jf != null) {
mapper.setJormFactory(jf);
}
}
public boolean isPrefetchResult() {
return qd.withPrefetch;
}
public void defineQuery(JDOQueryDefinitionImpl _qd) {
this.qd = new JDOQueryDefinitionImpl(_qd);
classLoader = _qd.getCandidateClass().getClassLoader();
if (classLoader == null) {
classLoader = getClass().getClassLoader();
if (classLoader == null) {
classLoader = ClassLoader.getSystemClassLoader();
logger.log(BasicLevel.DEBUG, "The system classLoader of the "
+ "class is assigned to the query: " + classLoader);
} else {
logger.log(BasicLevel.DEBUG, "The classLoader of Speedo is"
+ " assigned the query: " + classLoader);
}
} else {
logger.log(BasicLevel.DEBUG, "The classLoader of the class is "
+ "assigned to the query: " + classLoader);
}
mapper.setClassLoader(classLoader);
status = DEFINED;
}
// IMPLEMENTATION OF ReplaceableCacheEntry INTERFACE //
// ---------------------------------------------------//
public Object getCeIdentifier() {
return qd;
}
// IMPLEMENTATION OF CompiledQuery INTERFACE //
// -------------------------------------------//
public synchronized QueryDefinition getDefinition() {
switch (status) {
case DEFINED:
case COMPILED:
return qd;
case UNDEFINED:
default:
return null;
}
}
protected QueryTree optimize(QueryTree qt, boolean debug)
throws MedorException, ExpressionException {
ArrayList rules = new ArrayList();
rules.add(new PushNotInExpressionRule());
if (mapper.getMapperName().startsWith("rdb")) {
rules.add(new FlattenQueryTreeRule());
rules.add(new Jorm2Rdb());
rules.add(new Like2SQL());
} else {
}
QueryTree optimizedQT = qt;
QueryTransformer queryTransformer = new BasicQueryRewriter(rules);
IndexesGenerator indexesGenerator = new IndexesGenerator();
try {
optimizedQT = queryTransformer.transform(optimizedQT);
if (debug) {
logger.log(BasicLevel.DEBUG, "Query optimized");
QueryTreePrinter.printQueryTree(optimizedQT, logger);
}
optimizedQT = indexesGenerator.transform(optimizedQT);
} catch (Exception e) {
throw new MedorException("Impossible to optimize the query", e);
}
pncParams = JormQueryTreeHelper.getRequiredPNameManagers(optimizedQT);
for (Iterator it = pncParams.iterator(); it.hasNext();) {
ParameterOperand po = (ParameterOperand) it.next();
String name = po.getName();
if (debug) {
logger.log(BasicLevel.DEBUG, "ParameterOperand " + name
+ " is expected.");
}
po.setValue(JormPathHelper.getPNameCoder(name, mapper));
}
return optimizedQT;
}
/**
* evaluate the query with a single parameter which is a array of object
* parameters.
* @param pm the persistence manager object
* @param a the array parameter of the query
* @return a Collection of result objects
* @throws org.objectweb.medor.api.EvaluationException
* @throws org.objectweb.medor.api.MedorException
*/
public Object execute(Object[] a, POManagerItf pm, QueryDefinition userqd)
throws SpeedoException, MedorException, ExpressionException {
if (status != COMPILED)
throw new EvaluationException(
"Impossible to execute a query if it has not been defined and compiled before");
ParameterOperand[] pos = null;
if (a != null) {
pos = new ParameterOperand[a.length + pncParams.size()];
for(Iterator it = hparams.values().iterator(); it.hasNext();) {
Object[] o = (Object[]) it.next();
int idx = ((Integer) o[0]).intValue();
pos[idx] = new BasicParameterOperand(
(BasicParameterOperand) o[1]);
treatParameter(pos[idx], a[idx]);
}
} else {
pos = new ParameterOperand[pncParams.size()];
}
if (pncParams.size() >0) {
int i = (a == null ? 0 : a.length);
for (Iterator it = pncParams.iterator(); it.hasNext(); i++) {
pos[i++] = (ParameterOperand) it.next();
}
}
return executeQT((JDOPOManagerItf) pm, pos, userqd);
}
/**
* evaluate the query with a single parameter which is a Map of object parameters.
* @param pm the persistence manager object
* @param m the map parameter of the query
* @return a Collection of result objects
* @throws org.objectweb.medor.api.EvaluationException
* @throws org.objectweb.medor.api.MedorException
*/
public Object execute(Map m, POManagerItf pm, QueryDefinition userqd)
throws SpeedoException, MedorException, ExpressionException {
if (status != COMPILED)
throw new EvaluationException(
"Impossible to execute a query if it has not been defined and compiled before");
ParameterOperand[] pos =
new BasicParameterOperand[m.size() + pncParams.size()];
for(Iterator it = hparams.values().iterator(); it.hasNext();) {
Object[] o = (Object[]) it.next();
int idx = ((Integer) o[0]).intValue();
pos[idx] = new BasicParameterOperand(
(BasicParameterOperand) o[1]);
treatParameter(pos[idx], m.get(pos[idx].getName()));
}
if (pncParams.size() >0) {
int i = (m == null ? 0 : m.size());
for (Iterator it = pncParams.iterator(); it.hasNext(); i++) {
pos[i++] = (ParameterOperand) it.next();
}
}
return executeQT((JDOPOManagerItf) pm, pos, userqd);
}
protected void treatParameter(ParameterOperand po, Object value)
throws SpeedoException, ExpressionException {
Object val = value;
if (po.getType().getTypeCode() == QType.TYPECODE_PNAME) {
//Assign the pname of the parameter
if (val == null) {
try {
val =
jf.getPClassMapping(
(Class) paramName2paramClass.get(po.getName()))
.getPBinder().getNull();
} catch (Exception e) {
throw new SpeedoException(
"Impossible to find the null PName of the parameter (name= "
+ po.getName()
+ " / type= " + po.getType().getJormName(),
e);
}
} else if (val instanceof PersistentObjectItf) {
val = ((PersistentObjectItf) value).getPName();
} else {
throw new JDOUserException("The parameter '"
+ po.getName()
+ "' must be a persistent object: " + val);
}
}
po.setValue(val);
}
// Private Methods //
//-----------------//
/**
* Hash a String, and compute a Hashtable
* example:
* ("String name, Float salary, Employee boss", ",")
* keys | values
* ---------------------
* "name" | "String"
* "salary" | "Float"
* "boss" | "Employee"
*
* @param stringToHash the String to hash
* @param separator the separator to tokenize
*/
protected void toHashtableParams(String stringToHash, String separator) {
hparams = new HashMap();
paramName2paramClass = new HashMap();
if (stringToHash != null) {
StringTokenizer tok = new StringTokenizer(stringToHash, separator);
int idx = 0;
while (tok.hasMoreTokens()) {
String tuple = tok.nextToken().trim();
int i = tuple.indexOf(' ');
String name = tuple.substring(i + 1, tuple.length());
String paramType = tuple.substring(0, i).trim();
PType type = getPType(paramType);
hparams.put(name, new Object[]{
new Integer(idx),
new BasicParameterOperand(type, name)});
idx ++;
if (type == PTypeSpaceMedor.PNAME) {
paramName2paramClass.put(name, getClass(paramType));
}
}
}
}
protected void toHashtableVars(String stringToHash, String separator) {
hvars = new HashMap();
if (stringToHash != null) {
StringTokenizer tok = new StringTokenizer(stringToHash, separator);
while (tok.hasMoreTokens()) {
String tuple = tok.nextToken().trim();
int i = tuple.indexOf(' ');
String type = tuple.substring(0, i);
hvars.put(tuple.substring(i + 1, tuple.length()), type);
}
}
}
protected Class getClass(String classname) {
if (classLoader != null) {
try {
return classLoader.loadClass(classname);
} catch (ClassNotFoundException e) {
try {
return classLoader.loadClass(
qd.candidateClass.getPackage().getName() + "." + classname);
} catch (ClassNotFoundException e2) {
return null;
}
}
} else {
return null;
}
}
protected void flushCache(JDOPOManagerItf pm) {
if (!qd.ignoreCache || !pm.getIgnoreCache()) {
if (logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG,
"Flush dirty instances of the working set ...");
}
try {
pm.getTransactionalPersistenceManager()
.flush((JDOTransactionItf) pm.currentTransaction(), this);
} catch (PersistenceException e) {
throw new JDOException("Impossible to use the cache into a " +
"query: Error during the flushing of data on the support", e);
}
}
}
}