001: /*
002: $Header: /cvsroot/xorm/xorm/src/org/xorm/query/QueryImpl.java,v 1.29 2003/08/29 16:42:31 wbiggs Exp $
003:
004: This file is part of XORM.
005:
006: XORM is free software; you can redistribute it and/or modify
007: it under the terms of the GNU General Public License as published by
008: the Free Software Foundation; either version 2 of the License, or
009: (at your option) any later version.
010:
011: XORM is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU General Public License for more details.
015:
016: You should have received a copy of the GNU General Public License
017: along with XORM; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package org.xorm.query;
021:
022: import java.io.StringReader;
023: import java.util.HashMap;
024: import java.util.Collection;
025: import java.util.Iterator;
026: import java.util.List;
027: import java.util.Map;
028: import java.util.ArrayList;
029: import java.util.StringTokenizer;
030:
031: import javax.jdo.Query;
032: import javax.jdo.Extent;
033: import javax.jdo.PersistenceManager;
034: import javax.jdo.JDOUserException;
035:
036: import org.xorm.ClassMapping;
037: import org.xorm.CollectionProxy;
038: import org.xorm.FetchGroupManager;
039: import org.xorm.InterfaceManagerFactory;
040: import org.xorm.I15d;
041: import org.xorm.XORM;
042: import org.xorm.query.jdoql.JDOQL;
043:
044: /**
045: * Implementation of JDO query interface. This is a wrapper around
046: * a QueryLanguage object, which returns an Expression.
047: */
048: public class QueryImpl implements Query, I15d {
049: private static HashMap knownClasses = new HashMap();
050: static {
051: knownClasses.put("Character", Character.class);
052: knownClasses.put("char", Character.TYPE);
053: knownClasses.put("Integer", Integer.class);
054: knownClasses.put("int", Integer.TYPE);
055: knownClasses.put("Long", Long.class);
056: knownClasses.put("long", Long.TYPE);
057: knownClasses.put("Float", Float.class);
058: knownClasses.put("float", Float.TYPE);
059: knownClasses.put("Double", Double.class);
060: knownClasses.put("double", Double.TYPE);
061: knownClasses.put("Boolean", Boolean.class);
062: knownClasses.put("boolean", Boolean.TYPE);
063: knownClasses.put("Short", Short.class);
064: knownClasses.put("short", Short.TYPE);
065: knownClasses.put("String", String.class);
066:
067: // This one's not in the spec, but it's rather convenient.
068: knownClasses.put("Collection", Collection.class);
069: }
070:
071: // Instance variables
072: protected PersistenceManager mgr;
073: protected QueryLanguage query;
074:
075: protected boolean compiled;
076: private List imports;
077: private List openResults = new ArrayList();
078:
079: public QueryImpl(PersistenceManager mgr) {
080: this .mgr = mgr;
081: query = new JDOQL();
082: }
083:
084: public QueryImpl(PersistenceManager mgr, QueryLanguage query) {
085: this .mgr = mgr;
086: this .query = query;
087: }
088:
089: // TODO FIXME
090: public QueryImpl(PersistenceManager mgr, QueryImpl other) {
091: this (mgr);
092: /*
093: NEED query = (QueryLanguage) other.query.clone()
094: this.compiled = other.compiled;
095: */
096: if (other.imports != null) {
097: this .imports = new ArrayList(other.imports);
098: }
099: }
100:
101: public PersistenceManager getPersistenceManager() {
102: return mgr;
103: }
104:
105: public void setClass(Class clazz) {
106: if (query instanceof AbstractQueryLanguage) {
107: ((AbstractQueryLanguage) query).setClass(clazz);
108: }
109: }
110:
111: public Class getCandidateClass() {
112: return query.getCandidateClass();
113: }
114:
115: public void setFilter(String filter) {
116: if (query instanceof AbstractQueryLanguage) {
117: ((AbstractQueryLanguage) query).setFilter(filter);
118: }
119: }
120:
121: // Needed by ExpressionValidator
122: Class getParameterType(String name) {
123: if (query instanceof AbstractQueryLanguage) {
124: return ((AbstractQueryLanguage) query)
125: .getParameterType(name);
126: }
127: return null;
128: }
129:
130: Class getVariableType(String name) {
131: if (query instanceof AbstractQueryLanguage) {
132: return ((AbstractQueryLanguage) query)
133: .getVariableType(name);
134: }
135: return null;
136: }
137:
138: public void declareVariables(String variables) {
139: if (query instanceof AbstractQueryLanguage) {
140: declareImpl(variables,
141: ((AbstractQueryLanguage) query).varNameToType, ";");
142: } else {
143: throw new UnsupportedOperationException();
144: }
145: }
146:
147: public void declareParameters(String parameters) {
148: if (query instanceof AbstractQueryLanguage) {
149: declareImpl(parameters,
150: ((AbstractQueryLanguage) query).paramNameToType,
151: ",");
152: } else {
153: throw new UnsupportedOperationException();
154: }
155: }
156:
157: // Shared code for variables and parameters
158: // TODO more sanity checking
159: private void declareImpl(String input, HashMap nameMap, String token) {
160: StringTokenizer toke = new StringTokenizer(input, token);
161: while (toke.hasMoreTokens()) {
162: String var = toke.nextToken().trim();
163: int pos = var.indexOf(' ');
164: String typeName = var.substring(0, pos);
165: String varName = var.substring(pos + 1);
166: Class typeClass = typeNameToClass(typeName);
167: if (typeClass == null) {
168: throw new JDOUserException(I18N.msg("E_query_class",
169: typeName));
170: }
171: nameMap.put(varName, typeClass);
172: } // for each parameter
173: }
174:
175: public void compile() {
176: if (!compiled) {
177: try {
178: query.compile();
179: compiled = true;
180: } catch (QuerySyntaxException e) {
181: throw new JDOUserException(I18N.msg("E_parse_query"), e);
182: }
183: ExpressionValidator ev = new ExpressionValidator(this );
184: if (!ev.isValid()) {
185: throw new JDOUserException(I18N.msg("E_parse_query"));
186: }
187: }
188: }
189:
190: Expression getExpression() {
191: return query.getExpression();
192: }
193:
194: private Object getCollectionProxy(BoundExpression bound) {
195: FetchGroupManager fgm = XORM.getFetchGroupManager(mgr);
196: ClassMapping mapping = XORM.getModelMapping(mgr)
197: .getClassMapping(query.getCandidateClass());
198: Selector selector = bound.getSelector();
199: selector.require(fgm.getDataFetchGroup(mapping));
200: CollectionProxy cp = new CollectionProxy(mgr, mapping, selector);
201: openResults.add(cp);
202: return cp;
203: }
204:
205: public BoundExpression getBoundExpression() {
206: if (!compiled) {
207: compile();
208: }
209: return new BoundExpression(query, mgr);
210: }
211:
212: public Object execute() {
213: return getCollectionProxy(getBoundExpression());
214: }
215:
216: public Object execute(Object param) {
217: BoundExpression bound = getBoundExpression();
218: bound.bindParameter(0, param);
219: return getCollectionProxy(bound);
220: }
221:
222: public Object execute(Object param0, Object param1) {
223: BoundExpression bound = getBoundExpression();
224: bound.bindParameter(0, param0);
225: bound.bindParameter(1, param1);
226: return getCollectionProxy(bound);
227: }
228:
229: public Object execute(Object param0, Object param1, Object param2) {
230: BoundExpression bound = getBoundExpression();
231: bound.bindParameter(0, param0);
232: bound.bindParameter(1, param1);
233: bound.bindParameter(2, param2);
234: return getCollectionProxy(bound);
235: }
236:
237: public Object executeWithMap(Map map) {
238: BoundExpression bound = getBoundExpression();
239: bound.setMap(map);
240: return getCollectionProxy(bound);
241: }
242:
243: public Object executeWithArray(Object[] array) {
244: BoundExpression bound = getBoundExpression();
245: for (int i = 0; i < array.length; i++) {
246: bound.bindParameter(i, array[i]);
247: }
248: return getCollectionProxy(bound);
249: }
250:
251: public void setCandidates(Extent extent) {
252: // TODO this does not account for subclasses = false,
253: // but at present subclasses are not supported anyway.
254: if (query instanceof AbstractQueryLanguage) {
255: ((AbstractQueryLanguage) query).setClass(extent
256: .getCandidateClass());
257: }
258: }
259:
260: public void setCandidates(Collection collection) {
261: throw new UnsupportedOperationException();
262: }
263:
264: public void declareImports(String imports) {
265: this .imports = new ArrayList();
266:
267: StringTokenizer toke = new StringTokenizer(imports, ";");
268: while (toke.hasMoreTokens()) {
269: String var = toke.nextToken().trim();
270: int pos = var.indexOf(' ');
271: String importStr = var.substring(0, pos);
272: if (!"import".equals(importStr)) {
273: throw new JDOUserException("Bad syntax for import");
274: }
275: String target = var.substring(pos + 1);
276: this .imports.add(target);
277: }
278: }
279:
280: // TODO: Check spec on the order in which we check these things
281: private Class typeNameToClass(String typeName) {
282: if (typeName.indexOf('.') != -1) {
283: // Fully specified name.
284: try {
285: return Class.forName(typeName);
286: } catch (ClassNotFoundException e2) {
287: // silently ignore
288: }
289: } else {
290: // Might be a well known java.lang class
291: Class typeClass = (Class) knownClasses.get(typeName);
292: if (typeClass != null)
293: return typeClass;
294: }
295:
296: // Not fully specified or java.lang; try imports
297: if (imports != null) {
298: String dotName = "." + typeName;
299: Iterator i = imports.iterator();
300: while (i.hasNext()) {
301: String importStr = (String) i.next();
302: if (importStr.endsWith(dotName)) {
303: try {
304: return Class.forName(importStr);
305: } catch (ClassNotFoundException e) {
306: // silently ignore
307: }
308: } else if (importStr.endsWith("*")) {
309: try {
310: return Class.forName(importStr.substring(0,
311: importStr.length() - 1)
312: + typeName);
313: } catch (ClassNotFoundException e) {
314: // silently ignore
315: }
316: }
317: } // for each import
318: } // if there are imports
319:
320: // Assume same package as the thing we're selecting
321: try {
322: return Class.forName(query.getCandidateClass().getPackage()
323: .getName()
324: + "." + typeName);
325: } catch (ClassNotFoundException e) {
326: // silently ignore
327: }
328: return null;
329: }
330:
331: public void setOrdering(String ordering) {
332: if (query instanceof AbstractQueryLanguage) {
333: StringTokenizer toke = new StringTokenizer(ordering, ",");
334: while (toke.hasMoreTokens()) {
335: String var = toke.nextToken().trim();
336: int pos = var.indexOf(' ');
337: if (pos < 0) {
338: throw new JDOUserException(I18N
339: .msg("E_query_ordering"));
340: }
341: String field = var.substring(0, pos);
342: String orderStr = var.substring(pos + 1);
343: int order;
344: if ("ascending".equalsIgnoreCase(orderStr)) {
345: order = QueryOrdering.ASCENDING;
346: } else if ("descending".equalsIgnoreCase(orderStr)) {
347: order = QueryOrdering.DESCENDING;
348: } else {
349: throw new JDOUserException(I18N
350: .msg("E_query_ordering"));
351: }
352: ((AbstractQueryLanguage) query)
353: .addOrdering(new QueryOrdering(field, order));
354: }
355: }
356: }
357:
358: public QueryOrdering[] getOrdering() {
359: return query.getOrdering();
360: }
361:
362: public void setIgnoreCache(boolean ignoreCache) {
363: throw new UnsupportedOperationException();
364: }
365:
366: public boolean getIgnoreCache() {
367: throw new UnsupportedOperationException();
368: }
369:
370: public void close(Object object) {
371: if (openResults.contains(object)) {
372: ((CollectionProxy) object).close();
373: openResults.remove(object);
374: }
375: }
376:
377: public void closeAll() {
378: Iterator it = openResults.iterator();
379: while (it.hasNext()) {
380: ((CollectionProxy) it.next()).close();
381: }
382: openResults.clear();
383: }
384:
385: public String toString() {
386: return getExpression().toString();
387: }
388: }
|