001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.jdo.query;
012:
013: import com.versant.core.common.Debug;
014: import com.versant.core.jdo.QueryDetails;
015: import com.versant.core.common.CmdBitSet;
016: import com.versant.core.metadata.ClassMetaData;
017: import com.versant.core.metadata.ModelMetaData;
018: import com.versant.core.util.classhelper.ClassHelper;
019:
020: import java.io.StringReader;
021:
022: import com.versant.core.common.BindingSupportImpl;
023:
024: /**
025: * Wrapper around the JavaCC JDOQL parser.
026: */
027: public class QueryParser {
028:
029: private JavaCharStream stream;
030: protected ImportNode[] imports;
031: protected ParamNode[] params;
032: protected VarNode[] vars;
033: protected OrderNode[] orders;
034: protected UnaryNode filter;
035: protected ResultNode resultNode;
036: protected GroupingNode groupingNode;
037:
038: private JDOQLParser parser;
039: private JDOQLParserTokenManager tokenManager;
040:
041: /**
042: * The ClassMetaData of the Candidate class.
043: */
044: protected ClassMetaData cmd;
045: protected ModelMetaData jmd;
046:
047: public QueryParser(ModelMetaData jmd) {
048: this .jmd = jmd;
049: }
050:
051: public ClassMetaData getCmd() {
052: return cmd;
053: }
054:
055: public ImportNode[] getImports() {
056: return imports;
057: }
058:
059: public ParamNode[] getParams() {
060: return params;
061: }
062:
063: public void setParams(ParamNode[] params) {
064: this .params = params;
065: }
066:
067: public VarNode[] getVars() {
068: return vars;
069: }
070:
071: public OrderNode[] getOrders() {
072: return orders;
073: }
074:
075: public UnaryNode getFilter() {
076: return filter;
077: }
078:
079: public ResultNode getResultNode() {
080: return resultNode;
081: }
082:
083: public GroupingNode getGroupingNode() {
084: return groupingNode;
085: }
086:
087: public void setGroupingNode(GroupingNode groupingNode) {
088: this .groupingNode = groupingNode;
089: }
090:
091: public void parse(QueryDetails q) throws Exception {
092:
093: parseJDOQL(q);
094:
095: parseCommon(q);
096: }
097:
098: public void parseJDOQL(QueryDetails q) throws Exception {
099: cmd = jmd.getClassMetaData(q.getCandidateClass());
100: String s = q.getImports();
101: if (s != null) {
102: initParser(s);
103: try {
104: imports = parser.declareImports();
105: } catch (ParseException e) {
106: throw BindingSupportImpl.getInstance()
107: .invalidOperation(
108: "Invalid imports:\n" + s + "\n"
109: + e.getMessage());
110: }
111: if (Debug.DEBUG) {
112: Debug.OUT.println("imports: ");
113: dump(imports);
114: }
115: } else {
116: imports = null;
117: }
118:
119: // parse the parameters
120: s = q.getParameters();
121: if (s != null) {
122: initParser(s);
123: try {
124: params = parser.declareParameters();
125: } catch (ParseException e) {
126: throw BindingSupportImpl.getInstance()
127: .invalidOperation(
128: "Invalid parameter declarations:\n" + s
129: + "\n" + e.getMessage());
130: }
131: for (int i = params.length - 1; i >= 0; i--)
132: params[i].setIndex(i);
133: if (Debug.DEBUG) {
134: Debug.OUT.println("params: ");
135: dump(params);
136: }
137: } else {
138: params = null;
139: }
140:
141: // parse the variables
142: s = q.getVariables();
143: if (s != null) {
144: initParser(s);
145: try {
146: vars = parser.declareVariables();
147: } catch (ParseException e) {
148: throw BindingSupportImpl.getInstance()
149: .invalidOperation(
150: "Invalid variable declarations:\n" + s
151: + "\n" + e.getMessage());
152: }
153: if (Debug.DEBUG) {
154: Debug.OUT.println("vars: ");
155: dump(vars);
156: }
157: } else {
158: vars = null;
159: }
160:
161: // parse the ordering
162: s = q.getOrdering();
163: if (s != null) {
164: initParser(s);
165: try {
166: orders = parser.setOrderings();
167: } catch (ParseException e) {
168: throw BindingSupportImpl.getInstance()
169: .invalidOperation(
170: "Invalid ordering:\n" + s + "\n"
171: + e.getMessage());
172: }
173: for (int i = 0; i < orders.length; i++)
174: orders[i].normalize();
175: if (Debug.DEBUG) {
176: Debug.OUT.println("normalized orders: ");
177: dump(orders);
178: }
179: } else {
180: orders = null;
181: }
182:
183: // parse the filter
184: s = q.getFilter();
185: if (s != null) {
186: initParser(q.getFilter());
187: try {
188: Node e = parser.filterExpression();
189: if (e != null) {
190: filter = new UnaryNode(e);
191: } else {
192: filter = null;
193: }
194: } catch (ParseException x) {
195: throw BindingSupportImpl.getInstance()
196: .invalidOperation(
197: "Invalid filter:\n" + s + "\n"
198: + x.getMessage());
199: }
200: } else {
201: filter = null;
202: }
203:
204: //parse the result
205: s = q.getResult();
206: if (s != null && s.trim().length() > 0) {
207: initParser(s);
208: try {
209: resultNode = parser.setResults();
210: } catch (ParseException x) {
211: throw BindingSupportImpl.getInstance()
212: .invalidOperation(
213: "Invalid result:\n" + s + "\n"
214: + x.getMessage());
215: }
216: } else {
217: resultNode = null;
218: }
219:
220: //parse the grouping
221: s = q.getGrouping();
222: if (s != null) {
223: initParser(s);
224: try {
225: groupingNode = parser.setGrouping();
226: } catch (ParseException x) {
227: throw BindingSupportImpl.getInstance()
228: .invalidOperation(
229: "Invalid grouping:\n" + s + "\n"
230: + x.getMessage());
231: }
232: } else {
233: groupingNode = null;
234: }
235: }
236:
237: private void parseCommon(QueryDetails q) throws Exception {
238:
239: // check the types of all vars and lookup their meta data
240: if (vars != null) {
241: for (int i = vars.length - 1; i >= 0; i--)
242: vars[i].resolve(this );
243: }
244:
245: if (filter != null) {
246: if (Debug.DEBUG) {
247: Debug.OUT.println("\n* Filter: " + filter);
248: Debug.OUT.println("\n* Parsed tree:");
249: filter.dump("");
250: }
251:
252: // simplify some tree constructs
253: filter.normalize();
254: if (Debug.DEBUG) {
255: Debug.OUT.println("\n* Normalized tree:");
256: filter.dump("");
257: }
258:
259: // resolve field and parameter names etc
260: filter.resolve(this , cmd, false);
261: if (Debug.DEBUG) {
262: Debug.OUT.println("\n* Resolved tree:");
263: filter.dump("");
264: }
265:
266: // simplify some tree constructs again as some operations
267: // require resolved nodes
268: filter.normalize();
269: if (Debug.DEBUG) {
270: Debug.OUT.println("\n* Second normalized tree:");
271: filter.dump("");
272: }
273:
274: } else {
275: if (Debug.DEBUG) {
276: Debug.OUT.println("filter is null");
277: }
278: }
279: }
280:
281: /**
282: * Parse just an ordering specification. This is used to parse orderings
283: * on their own in the meta data.
284: */
285: public OrderNode[] parseOrdering(ClassMetaData candidateClass,
286: String s) throws ParseException {
287: try {
288: if (s != null) {
289: cmd = candidateClass;
290: initParser(s);
291: return parser.setOrderings();
292: } else {
293: return null;
294: }
295: } catch (TokenMgrError e) {
296: throw new ParseException(e.toString());
297: }
298: }
299:
300: /**
301: * Find the parameter with name or null if none.
302: */
303: public ParamNode findParam(String name) {
304: if (params == null)
305: return null;
306: for (int i = params.length - 1; i >= 0; i--) {
307: ParamNode p = params[i];
308: if (p.getIdentifier().equals(name))
309: return p;
310: }
311: return null;
312: }
313:
314: /**
315: * Find the variable with name or null if none.
316: */
317: public VarNode findVar(String name) {
318: if (vars == null)
319: return null;
320: for (int i = vars.length - 1; i >= 0; i--) {
321: VarNode v = vars[i];
322: if (v.getIdentifier().equals(name))
323: return v;
324: }
325: return null;
326: }
327:
328: public ClassMetaData getCMD(Class cls) {
329: return jmd.getClassMetaData(cls);
330: }
331:
332: /**
333: * Convert a variable type name into a class. This makes sure it is a
334: * legal type.
335: */
336: public Class resolveVarType(String type) {
337: Class cls;
338: try {
339: cls = resolveType(type);
340: } catch (ClassNotFoundException e) {
341: throw BindingSupportImpl.getInstance().runtime(
342: "Variable class not found: '" + type + "'", e);
343: }
344: return cls;
345: }
346:
347: /**
348: * Convert a parameter type name into a class. This makes sure it is a
349: * legal type.
350: */
351: public Class resolveParamType(String type) {
352: Class cls;
353: try {
354: cls = resolveType(type);
355: } catch (ClassNotFoundException e) {
356: throw BindingSupportImpl.getInstance().runtime(
357: "Parameter class not found: '" + type + "'", e);
358: }
359: return cls;
360: }
361:
362: private Class resolveType(String type)
363: throws ClassNotFoundException {
364: try {
365: return resolveTypeImp(type);
366: } catch (ClassNotFoundException e) {
367: int i = type.lastIndexOf('.');
368: if (i >= 0) {
369: StringBuffer s = new StringBuffer(type);
370: s.setCharAt(i, '$');
371: try {
372: return resolveTypeImp(s.toString());
373: } catch (ClassNotFoundException e1) {
374: // ignore
375: }
376: }
377: throw e;
378: }
379: }
380:
381: private Class resolveTypeImp(String type)
382: throws ClassNotFoundException {
383: ClassLoader loader = cmd.getClassLoader();
384: try {
385: return ClassHelper.get().classForName(type, true, loader);
386: } catch (ClassNotFoundException e) {
387: if (imports != null) {
388: int len = imports.length;
389: for (int i = 0; i < len; i++) {
390: ImportNode im = imports[i];
391: if (im.all) {
392: try {
393: return ClassHelper.get().classForName(
394: im.name + type, true, loader);
395: } catch (ClassNotFoundException x) {
396: // ignore
397: }
398: } else {
399: if (type.equals(im.getClassName())) {
400: return ClassHelper.get().classForName(
401: im.name, true, loader);
402: }
403: }
404: }
405: }
406: try {
407: return ClassHelper.get().classForName(
408: cmd.packageNameWithDot + type, true, loader);
409: } catch (ClassNotFoundException x) {
410: return ClassHelper.get().classForName(
411: "java.lang." + type, true, loader);
412: }
413: }
414: }
415:
416: /**
417: * Convert a cast expression into ClassMetaData or throw a
418: * JDOFatalUserException if not found. If the cast is to an interface
419: * then the array will contain all the possible implementing classes.
420: * TODO: Complete the interface support.
421: */
422: public ClassMetaData[] resolveCastType(String type) {
423: ClassMetaData c = jmd.getClassMetaData(type);
424: if (c == null) {
425: if (imports != null) {
426: int len = imports.length;
427: for (int i = 0; i < len && c == null; i++) {
428: ImportNode im = imports[i];
429: if (im.all) {
430: c = jmd.getClassMetaData(im.name + type);
431: } else if (type.equals(im.getClassName())) {
432: c = jmd.getClassMetaData(im.name);
433: }
434: }
435: }
436: if (c == null) {
437: c = jmd.getClassMetaData(cmd.packageNameWithDot + type);
438: }
439: }
440: if (c == null) {
441: throw BindingSupportImpl.getInstance().runtime(
442: "No persistent class found for cast expression: ("
443: + type + "): check the query imports");
444: }
445: return new ClassMetaData[] { c };
446: }
447:
448: /**
449: * Get the parser ready to parse s.
450: */
451: private void initParser(String s) {
452: StringReader reader = new StringReader(s);
453: if (stream == null) {
454: stream = new JavaCharStream(reader);
455: } else {
456: stream.ReInit(reader);
457: }
458: if (tokenManager == null) {
459: tokenManager = new JDOQLParserTokenManager(stream);
460: } else {
461: tokenManager.ReInit(stream);
462: }
463: if (parser == null) {
464: parser = new JDOQLParser(tokenManager);
465: } else {
466: parser.ReInit(tokenManager);
467: }
468: }
469:
470: private void dump(Node[] a) {
471: for (int i = 0; i < a.length; i++) {
472: Debug.OUT.print("[" + i + "] ");
473: a[i].dump(" ");
474: }
475: }
476:
477: /**
478: * This will return the CmdBitSet filled with the classMetadata's
479: * that this query depends on.
480: */
481: public CmdBitSet getCmds() {
482: final CmdBitSet bitSet = new CmdBitSet(jmd);
483: bitSet.addPlus(cmd);
484: if (filter != null) {
485: doCmdDependency(filter, bitSet);
486: }
487: if (orders != null) {
488: for (int i = 0; i < orders.length; i++) {
489: doCmdDependency(orders[i], bitSet);
490: }
491: }
492: return bitSet;
493: }
494:
495: private void doCmdDependency(Node node, CmdBitSet bitSet) {
496: if (node == null)
497: return;
498: node.updateEvictionDependency(bitSet);
499: for (Node n = node.childList; n != null; n = n.next) {
500: doCmdDependency(n, bitSet);
501: }
502: }
503:
504: }
|