/*
$Header: /cvsroot/xorm/xorm/src/org/xorm/query/QueryImpl.java,v 1.29 2003/08/29 16:42:31 wbiggs Exp $
This file is part of XORM.
XORM is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
XORM 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with XORM; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.xorm.query;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.util.StringTokenizer;
import javax.jdo.Query;
import javax.jdo.Extent;
import javax.jdo.PersistenceManager;
import javax.jdo.JDOUserException;
import org.xorm.ClassMapping;
import org.xorm.CollectionProxy;
import org.xorm.FetchGroupManager;
import org.xorm.InterfaceManagerFactory;
import org.xorm.I15d;
import org.xorm.XORM;
import org.xorm.query.jdoql.JDOQL;
/**
* Implementation of JDO query interface. This is a wrapper around
* a QueryLanguage object, which returns an Expression.
*/
public class QueryImpl implements Query, I15d {
private static HashMap knownClasses = new HashMap();
static {
knownClasses.put("Character", Character.class);
knownClasses.put("char", Character.TYPE);
knownClasses.put("Integer", Integer.class);
knownClasses.put("int", Integer.TYPE);
knownClasses.put("Long", Long.class);
knownClasses.put("long", Long.TYPE);
knownClasses.put("Float", Float.class);
knownClasses.put("float", Float.TYPE);
knownClasses.put("Double", Double.class);
knownClasses.put("double", Double.TYPE);
knownClasses.put("Boolean", Boolean.class);
knownClasses.put("boolean", Boolean.TYPE);
knownClasses.put("Short", Short.class);
knownClasses.put("short", Short.TYPE);
knownClasses.put("String", String.class);
// This one's not in the spec, but it's rather convenient.
knownClasses.put("Collection", Collection.class);
}
// Instance variables
protected PersistenceManager mgr;
protected QueryLanguage query;
protected boolean compiled;
private List imports;
private List openResults = new ArrayList();
public QueryImpl(PersistenceManager mgr) {
this.mgr = mgr;
query = new JDOQL();
}
public QueryImpl(PersistenceManager mgr, QueryLanguage query) {
this.mgr = mgr;
this.query = query;
}
// TODO FIXME
public QueryImpl(PersistenceManager mgr, QueryImpl other) {
this(mgr);
/*
NEED query = (QueryLanguage) other.query.clone()
this.compiled = other.compiled;
*/
if (other.imports != null) {
this.imports = new ArrayList(other.imports);
}
}
public PersistenceManager getPersistenceManager() {
return mgr;
}
public void setClass(Class clazz) {
if (query instanceof AbstractQueryLanguage) {
((AbstractQueryLanguage) query).setClass(clazz);
}
}
public Class getCandidateClass() {
return query.getCandidateClass();
}
public void setFilter(String filter) {
if (query instanceof AbstractQueryLanguage) {
((AbstractQueryLanguage) query).setFilter(filter);
}
}
// Needed by ExpressionValidator
Class getParameterType(String name) {
if (query instanceof AbstractQueryLanguage) {
return ((AbstractQueryLanguage) query).getParameterType(name);
}
return null;
}
Class getVariableType(String name) {
if (query instanceof AbstractQueryLanguage) {
return ((AbstractQueryLanguage) query).getVariableType(name);
}
return null;
}
public void declareVariables(String variables) {
if (query instanceof AbstractQueryLanguage) {
declareImpl(variables, ((AbstractQueryLanguage) query).varNameToType, ";");
} else {
throw new UnsupportedOperationException();
}
}
public void declareParameters(String parameters) {
if (query instanceof AbstractQueryLanguage) {
declareImpl(parameters, ((AbstractQueryLanguage) query).paramNameToType, ",");
} else {
throw new UnsupportedOperationException();
}
}
// Shared code for variables and parameters
// TODO more sanity checking
private void declareImpl(String input, HashMap nameMap, String token) {
StringTokenizer toke = new StringTokenizer(input, token);
while (toke.hasMoreTokens()) {
String var = toke.nextToken().trim();
int pos = var.indexOf(' ');
String typeName = var.substring(0,pos);
String varName = var.substring(pos+1);
Class typeClass = typeNameToClass(typeName);
if (typeClass == null) {
throw new JDOUserException(I18N.msg("E_query_class", typeName));
}
nameMap.put(varName, typeClass);
} // for each parameter
}
public void compile() {
if (!compiled) {
try {
query.compile();
compiled = true;
} catch (QuerySyntaxException e) {
throw new JDOUserException(I18N.msg("E_parse_query"), e);
}
ExpressionValidator ev = new ExpressionValidator(this);
if (!ev.isValid()) {
throw new JDOUserException(I18N.msg("E_parse_query"));
}
}
}
Expression getExpression() {
return query.getExpression();
}
private Object getCollectionProxy(BoundExpression bound) {
FetchGroupManager fgm = XORM.getFetchGroupManager(mgr);
ClassMapping mapping = XORM.getModelMapping(mgr).getClassMapping(query.getCandidateClass());
Selector selector = bound.getSelector();
selector.require(fgm.getDataFetchGroup(mapping));
CollectionProxy cp = new CollectionProxy(mgr, mapping, selector);
openResults.add(cp);
return cp;
}
public BoundExpression getBoundExpression() {
if (!compiled) {
compile();
}
return new BoundExpression(query, mgr);
}
public Object execute() {
return getCollectionProxy(getBoundExpression());
}
public Object execute(Object param) {
BoundExpression bound = getBoundExpression();
bound.bindParameter(0, param);
return getCollectionProxy(bound);
}
public Object execute(Object param0, Object param1) {
BoundExpression bound = getBoundExpression();
bound.bindParameter(0, param0);
bound.bindParameter(1, param1);
return getCollectionProxy(bound);
}
public Object execute(Object param0, Object param1, Object param2) {
BoundExpression bound = getBoundExpression();
bound.bindParameter(0, param0);
bound.bindParameter(1, param1);
bound.bindParameter(2, param2);
return getCollectionProxy(bound);
}
public Object executeWithMap(Map map) {
BoundExpression bound = getBoundExpression();
bound.setMap(map);
return getCollectionProxy(bound);
}
public Object executeWithArray(Object[] array) {
BoundExpression bound = getBoundExpression();
for (int i = 0; i < array.length; i++) {
bound.bindParameter(i, array[i]);
}
return getCollectionProxy(bound);
}
public void setCandidates(Extent extent) {
// TODO this does not account for subclasses = false,
// but at present subclasses are not supported anyway.
if (query instanceof AbstractQueryLanguage) {
((AbstractQueryLanguage) query).setClass(extent.getCandidateClass());
}
}
public void setCandidates(Collection collection) {
throw new UnsupportedOperationException();
}
public void declareImports(String imports) {
this.imports = new ArrayList();
StringTokenizer toke = new StringTokenizer(imports, ";");
while (toke.hasMoreTokens()) {
String var = toke.nextToken().trim();
int pos = var.indexOf(' ');
String importStr = var.substring(0,pos);
if (!"import".equals(importStr)) {
throw new JDOUserException("Bad syntax for import");
}
String target = var.substring(pos+1);
this.imports.add(target);
}
}
// TODO: Check spec on the order in which we check these things
private Class typeNameToClass(String typeName) {
if (typeName.indexOf('.') != -1) {
// Fully specified name.
try {
return Class.forName(typeName);
} catch (ClassNotFoundException e2) {
// silently ignore
}
} else {
// Might be a well known java.lang class
Class typeClass = (Class) knownClasses.get(typeName);
if (typeClass != null) return typeClass;
}
// Not fully specified or java.lang; try imports
if (imports != null) {
String dotName = "." + typeName;
Iterator i = imports.iterator();
while (i.hasNext()) {
String importStr = (String) i.next();
if (importStr.endsWith(dotName)) {
try {
return Class.forName(importStr);
} catch (ClassNotFoundException e) {
// silently ignore
}
} else if (importStr.endsWith("*")) {
try {
return Class.forName(importStr.substring(0, importStr.length() - 1) + typeName);
} catch (ClassNotFoundException e) {
// silently ignore
}
}
} // for each import
} // if there are imports
// Assume same package as the thing we're selecting
try {
return Class.forName(query.getCandidateClass().getPackage().getName() + "." + typeName);
} catch (ClassNotFoundException e) {
// silently ignore
}
return null;
}
public void setOrdering(String ordering) {
if (query instanceof AbstractQueryLanguage) {
StringTokenizer toke = new StringTokenizer(ordering, ",");
while (toke.hasMoreTokens()) {
String var = toke.nextToken().trim();
int pos = var.indexOf(' ');
if (pos < 0) {
throw new JDOUserException(I18N.msg("E_query_ordering"));
}
String field = var.substring(0,pos);
String orderStr = var.substring(pos+1);
int order;
if ("ascending".equalsIgnoreCase(orderStr)) {
order = QueryOrdering.ASCENDING;
} else if ("descending".equalsIgnoreCase(orderStr)) {
order = QueryOrdering.DESCENDING;
} else {
throw new JDOUserException(I18N.msg("E_query_ordering"));
}
((AbstractQueryLanguage) query)
.addOrdering(new QueryOrdering(field, order));
}
}
}
public QueryOrdering[] getOrdering() {
return query.getOrdering();
}
public void setIgnoreCache(boolean ignoreCache) {
throw new UnsupportedOperationException();
}
public boolean getIgnoreCache() {
throw new UnsupportedOperationException();
}
public void close(Object object) {
if (openResults.contains(object)) {
((CollectionProxy) object).close();
openResults.remove(object);
}
}
public void closeAll() {
Iterator it = openResults.iterator();
while (it.hasNext()) {
((CollectionProxy) it.next()).close();
}
openResults.clear();
}
public String toString() {
return getExpression().toString();
}
}