001: /*
002: * Globus Toolkit Public License (GTPL)
003: *
004: * Copyright (c) 1999 University of Chicago and The University of
005: * Southern California. All Rights Reserved.
006: *
007: * 1) The "Software", below, refers to the Globus Toolkit (in either
008: * source-code, or binary form and accompanying documentation) and a
009: * "work based on the Software" means a work based on either the
010: * Software, on part of the Software, or on any derivative work of
011: * the Software under copyright law: that is, a work containing all
012: * or a portion of the Software either verbatim or with
013: * modifications. Each licensee is addressed as "you" or "Licensee."
014: *
015: * 2) The University of Southern California and the University of
016: * Chicago as Operator of Argonne National Laboratory are copyright
017: * holders in the Software. The copyright holders and their third
018: * party licensors hereby grant Licensee a royalty-free nonexclusive
019: * license, subject to the limitations stated herein and
020: * U.S. Government license rights.
021: *
022: * 3) A copy or copies of the Software may be given to others, if you
023: * meet the following conditions:
024: *
025: * a) Copies in source code must include the copyright notice and
026: * this license.
027: *
028: * b) Copies in binary form must include the copyright notice and
029: * this license in the documentation and/or other materials
030: * provided with the copy.
031: *
032: * 4) All advertising materials, journal articles and documentation
033: * mentioning features derived from or use of the Software must
034: * display the following acknowledgement:
035: *
036: * "This product includes software developed by and/or derived from
037: * the Globus project (http://www.globus.org/)."
038: *
039: * In the event that the product being advertised includes an intact
040: * Globus distribution (with copyright and license included) then
041: * this clause is waived.
042: *
043: * 5) You are encouraged to package modifications to the Software
044: * separately, as patches to the Software.
045: *
046: * 6) You may make modifications to the Software, however, if you
047: * modify a copy or copies of the Software or any portion of it,
048: * thus forming a work based on the Software, and give a copy or
049: * copies of such work to others, either in source code or binary
050: * form, you must meet the following conditions:
051: *
052: * a) The Software must carry prominent notices stating that you
053: * changed specified portions of the Software.
054: *
055: * b) The Software must display the following acknowledgement:
056: *
057: * "This product includes software developed by and/or derived
058: * from the Globus Project (http://www.globus.org/) to which the
059: * U.S. Government retains certain rights."
060: *
061: * 7) You may incorporate the Software or a modified version of the
062: * Software into a commercial product, if you meet the following
063: * conditions:
064: *
065: * a) The commercial product or accompanying documentation must
066: * display the following acknowledgment:
067: *
068: * "This product includes software developed by and/or derived
069: * from the Globus Project (http://www.globus.org/) to which the
070: * U.S. Government retains a paid-up, nonexclusive, irrevocable
071: * worldwide license to reproduce, prepare derivative works, and
072: * perform publicly and display publicly."
073: *
074: * b) The user of the commercial product must be given the following
075: * notice:
076: *
077: * "[Commercial product] was prepared, in part, as an account of
078: * work sponsored by an agency of the United States Government.
079: * Neither the United States, nor the University of Chicago, nor
080: * University of Southern California, nor any contributors to
081: * the Globus Project or Globus Toolkit nor any of their employees,
082: * makes any warranty express or implied, or assumes any legal
083: * liability or responsibility for the accuracy, completeness, or
084: * usefulness of any information, apparatus, product, or process
085: * disclosed, or represents that its use would not infringe
086: * privately owned rights.
087: *
088: * IN NO EVENT WILL THE UNITED STATES, THE UNIVERSITY OF CHICAGO
089: * OR THE UNIVERSITY OF SOUTHERN CALIFORNIA OR ANY CONTRIBUTORS
090: * TO THE GLOBUS PROJECT OR GLOBUS TOOLKIT BE LIABLE FOR ANY
091: * DAMAGES, INCLUDING DIRECT, INCIDENTAL, SPECIAL, OR CONSEQUENTIAL
092: * DAMAGES RESULTING FROM EXERCISE OF THIS LICENSE AGREEMENT OR
093: * THE USE OF THE [COMMERCIAL PRODUCT]."
094: *
095: * 8) LICENSEE AGREES THAT THE EXPORT OF GOODS AND/OR TECHNICAL DATA
096: * FROM THE UNITED STATES MAY REQUIRE SOME FORM OF EXPORT CONTROL
097: * LICENSE FROM THE U.S. GOVERNMENT AND THAT FAILURE TO OBTAIN SUCH
098: * EXPORT CONTROL LICENSE MAY RESULT IN CRIMINAL LIABILITY UNDER U.S.
099: * LAWS.
100: *
101: * 9) Portions of the Software resulted from work developed under a
102: * U.S. Government contract and are subject to the following license:
103: * the Government is granted for itself and others acting on its
104: * behalf a paid-up, nonexclusive, irrevocable worldwide license in
105: * this computer software to reproduce, prepare derivative works, and
106: * perform publicly and display publicly.
107: *
108: * 10) The Software was prepared, in part, as an account of work
109: * sponsored by an agency of the United States Government. Neither
110: * the United States, nor the University of Chicago, nor The
111: * University of Southern California, nor any contributors to the
112: * Globus Project or Globus Toolkit, nor any of their employees,
113: * makes any warranty express or implied, or assumes any legal
114: * liability or responsibility for the accuracy, completeness, or
115: * usefulness of any information, apparatus, product, or process
116: * disclosed, or represents that its use would not infringe privately
117: * owned rights.
118: *
119: * 11) IN NO EVENT WILL THE UNITED STATES, THE UNIVERSITY OF CHICAGO OR
120: * THE UNIVERSITY OF SOUTHERN CALIFORNIA OR ANY CONTRIBUTORS TO THE
121: * GLOBUS PROJECT OR GLOBUS TOOLKIT BE LIABLE FOR ANY DAMAGES,
122: * INCLUDING DIRECT, INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES
123: * RESULTING FROM EXERCISE OF THIS LICENSE AGREEMENT OR THE USE OF
124: * THE SOFTWARE.
125: *
126: * END OF LICENSE
127: */
128: package org.griphyn.vdl.annotation;
129:
130: import java.io.*;
131: import java.util.regex.Pattern;
132: import java.text.SimpleDateFormat;
133: import java.text.ParseException;
134: import org.griphyn.vdl.util.Logging;
135: import org.griphyn.vdl.dbschema.Annotation;
136:
137: /**
138: * Parses the input stream and generates a query tree as
139: * output.
140: *
141: * @author Jens-S. Vöckler
142: * @author Yong Zhao
143: * @version $Revision: 50 $
144: *
145: * @see QueryScanner
146: * @see QueryTree
147: */
148: public class QueryParser {
149: /**
150: * The access to the lexical scanner is stored here.
151: */
152: private QueryScanner m_scanner = null;
153:
154: /**
155: * Stores the look-ahead symbol.
156: */
157: private String m_lookAhead = null;
158:
159: /**
160: * Initializes the parser with an input stream to read from.
161: * @param r is the stream opened for reading.
162: */
163: public QueryParser(java.io.Reader r) throws IOException,
164: QueryParserException {
165: m_scanner = new QueryScanner(r);
166: m_lookAhead = m_scanner.nextToken();
167: }
168:
169: /**
170: * Parses the query stream
171: * @return the root node to the parsed query tree
172: */
173: public QueryTree parse() throws IOException, QueryParserException {
174: QueryTree root = null;
175: while (m_lookAhead != null) {
176: if (root == null) {
177: root = expression();
178: } else
179: expression();
180: }
181: return root;
182: }
183:
184: /**
185: * The main function to test the parser.
186: */
187: public static void main(String[] args) throws IOException,
188: QueryParserException {
189: QueryParser parser;
190: if (args.length == 0)
191: parser = new QueryParser(new InputStreamReader(System.in));
192: else
193: parser = new QueryParser(new FileReader(args[0]));
194:
195: QueryTree root = parser.parse();
196:
197: if (root != null) {
198: String sql = root.toSQL(Annotation.CLASS_FILENAME, null);
199: System.out.println(sql);
200: }
201: }
202:
203: /**
204: * check a string and guess the type of the attribute from its value
205: */
206: protected int checkString(String value) {
207: int type = Predicate.TYPE_STRING;
208: int len = value.length();
209: if (value == null || len == 0)
210: return type;
211: if (len == 1)
212: if (value.equalsIgnoreCase("t")
213: || value.equalsIgnoreCase("f"))
214: return Predicate.TYPE_BOOL;
215: else
216: return type;
217:
218: //if (value.matches("\\d\\d[/.]\\d\\d[/.]\\d\\d(\\d\\d)?"))
219: if (checkDate(value))
220: return Predicate.TYPE_DATE;
221:
222: return type;
223: }
224:
225: /**
226: * check a string to see whether it represents a date value
227: */
228: protected boolean checkDate(String value) {
229: SimpleDateFormat fmt[] = {
230: new SimpleDateFormat("MM/dd/yy HH:mm:ss.SSS"),
231: new SimpleDateFormat("MM/dd/yy HH:mm:ss"),
232: new SimpleDateFormat("MM/dd/yy HH:mm"),
233: new SimpleDateFormat("MM/dd/yy"),
234: new SimpleDateFormat("MM.dd.yy HH:mm:ss.SSS"),
235: new SimpleDateFormat("MM.dd.yy HH:mm:ss"),
236: new SimpleDateFormat("MM.dd.yy HH:mm"),
237: new SimpleDateFormat("MM.dd.yy"),
238: new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"),
239: new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"),
240: new SimpleDateFormat("yyyy-MM-dd HH:mm"),
241: new SimpleDateFormat("yyyy-MM-dd"),
242: new SimpleDateFormat("yyyy.MM.dd HH:mm:ss.SSS"),
243: new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"),
244: new SimpleDateFormat("yyyy.MM.dd HH:mm"),
245: new SimpleDateFormat("yyyy.MM.dd"), };
246: for (int i = 0; i < fmt.length; i++) {
247: try {
248: fmt[i].parse(value);
249: return true;
250: } catch (ParseException e) {
251: continue;
252: }
253: }
254: return false;
255: }
256:
257: /**
258: * check a string to see whether it represents a integer or float value.
259: */
260: protected int checkNumber(String value) {
261: if (value.matches("^[+-]?\\d+$"))
262: return Predicate.TYPE_INT;
263: if (value
264: .matches("^([+-]?)(?=\\d|\\.\\d)\\d*(\\.\\d*)?([Ee]([+-]?\\d+))?$"))
265: return Predicate.TYPE_FLOAT;
266: return -1;
267: }
268:
269: /**
270: * Parses predicates and returns the corresponding Predicate objects
271: */
272: protected Predicate predicates() throws IOException,
273: QueryParserException {
274: Predicate p = null;
275: if (m_lookAhead.startsWith("#")) {
276: //identifier
277: String name = m_lookAhead.substring(1);
278:
279: m_lookAhead = m_scanner.nextToken();
280: if (!m_lookAhead.startsWith("@"))
281: throw new QueryParserException(m_scanner,
282: "A predicate must follow " + name);
283: if (m_lookAhead.equals("@EX")) {
284: //nothing following exists
285: p = new Predicate(Predicate.EXISTS, name);
286: } else if (m_lookAhead.equals("@CT")) {
287: m_lookAhead = m_scanner.nextToken();
288: if (!m_lookAhead.startsWith("$"))
289: throw new QueryParserException(m_scanner,
290: "CONTAINS should be followed by a string value");
291: String value = m_lookAhead.substring(1);
292: p = new Predicate(Predicate.CONTAINS, name, value);
293: } else if (m_lookAhead.equals("@LK")) {
294: m_lookAhead = m_scanner.nextToken();
295: if (!m_lookAhead.startsWith("$"))
296: throw new QueryParserException(m_scanner,
297: "LIKE should be followed by a string value");
298: String value = m_lookAhead.substring(1);
299: p = new Predicate(Predicate.LIKE, name, value);
300: } else if (m_lookAhead.equals("@BT")) {
301: m_lookAhead = m_scanner.nextToken();
302:
303: int type;
304: int numType1 = -1, numType2 = -1;
305: int strType1 = -1, strType2 = -1;
306: String value1;
307: if (m_lookAhead.startsWith("#")) {
308: type = 0;
309: numType1 = -1;
310:
311: value1 = m_lookAhead.substring(1);
312: if ((numType1 = checkNumber(value1)) == -1)
313: throw new QueryParserException(
314: m_scanner,
315: "the value "
316: + value1
317: + " is not numeric, use quotes for a string");
318:
319: } else if (m_lookAhead.startsWith("$")) {
320: type = 1;
321: value1 = m_lookAhead.substring(1);
322: strType1 = checkString(value1);
323: } else
324: throw new QueryParserException(m_scanner,
325: "BETWEEN should be followed by a value");
326: m_lookAhead = m_scanner.nextToken();
327: if (!m_lookAhead.equals("AND"))
328: throw new QueryParserException(m_scanner,
329: "BETWEEN should be followed by AND");
330: m_lookAhead = m_scanner.nextToken();
331: if (!m_lookAhead.startsWith("#")
332: && !m_lookAhead.startsWith("$")) {
333: throw new QueryParserException(m_scanner,
334: "BETWEEN should have another value after AND ");
335: } else if ((m_lookAhead.startsWith("#") && type == 1)
336: || (m_lookAhead.startsWith("$") && type == 0))
337: throw new QueryParserException(m_scanner,
338: "type mismatch in BETWEEN...AND ");
339:
340: String value2 = m_lookAhead.substring(1);
341: numType2 = -1;
342: if (m_lookAhead.startsWith("#")) {
343: if ((numType2 = checkNumber(value2)) == -1)
344: throw new QueryParserException(
345: m_scanner,
346: "the value "
347: + value2
348: + " is not numeric, use quotes for a string");
349: if (numType1 != numType2)
350: type = Predicate.TYPE_FLOAT;
351: else
352: type = numType1;
353: } else {
354: strType2 = checkString(value2);
355: if (strType1 != strType2)
356: type = Predicate.TYPE_STRING;
357: else
358: type = strType1;
359: }
360: p = new Predicate(Predicate.BETWEEN, name, type,
361: value1, value2);
362: } else {
363: String op = m_lookAhead;
364: m_lookAhead = m_scanner.nextToken();
365: if (!m_lookAhead.startsWith("#")
366: && !m_lookAhead.startsWith("$"))
367: throw new QueryParserException(m_scanner,
368: "Predicate should be followed by a value");
369: String value = m_lookAhead.substring(1);
370:
371: int type = -1;
372: if (m_lookAhead.startsWith("#")) {
373: if ((type = checkNumber(value)) == -1)
374: throw new QueryParserException(
375: m_scanner,
376: "the value "
377: + value
378: + " is not numeric, use quotes for a string");
379: } else {
380: type = checkString(value);
381: }
382:
383: int pt = -1;
384: if (op.equals("@EQ"))
385: pt = Predicate.EQ;
386: else if (op.equals("@NE"))
387: pt = Predicate.NE;
388: else if (op.equals("@GT"))
389: pt = Predicate.GT;
390: else if (op.equals("@LT"))
391: pt = Predicate.LT;
392: else if (op.equals("@GE"))
393: pt = Predicate.GE;
394: else if (op.equals("@LE"))
395: pt = Predicate.LE;
396:
397: p = new Predicate(pt, name, type, value);
398: }
399: }
400: return p;
401: }
402:
403: /**
404: * Parses the atoms of the query statement
405: */
406: public QueryTree atom() throws IOException, QueryParserException {
407: QueryTree ret = null;
408: QueryTree current = null;
409:
410: if (m_lookAhead.startsWith("#")) {
411: Predicate p = predicates();
412: current = new QueryTree(p);
413: } else if (m_lookAhead.equals("NOT")) {
414: m_lookAhead = m_scanner.nextToken();
415: Predicate p = new Predicate(Predicate.NOT);
416: current = new QueryTree(p);
417: ret = atom();
418: if (ret.getPredicate() == Predicate.NOT)
419: current = ret.getRchild();
420: else
421: current.setRchild(ret);
422: } else if (m_lookAhead.startsWith("(")) {
423: m_lookAhead = m_scanner.nextToken();
424: current = expression();
425:
426: if (m_lookAhead == null || !m_lookAhead.startsWith(")")) {
427: throw new QueryParserException(m_scanner, "missing )");
428: }
429: } else
430: throw new QueryParserException(m_scanner, "invalid token "
431: + m_lookAhead.substring(1));
432:
433: return current;
434: }
435:
436: /**
437: * Parses the expressions of the query statement
438: */
439: public QueryTree expression() throws IOException,
440: QueryParserException {
441: QueryTree ret = null;
442:
443: ret = atom();
444:
445: m_lookAhead = m_scanner.nextToken();
446: while ((m_lookAhead != null)
447: && (m_lookAhead.equals("AND") || m_lookAhead
448: .equals("OR"))) {
449: Predicate p;
450: if (m_lookAhead.equals("AND")) {
451: p = new Predicate(Predicate.AND);
452: } else {
453: p = new Predicate(Predicate.OR);
454: }
455: QueryTree tmp = new QueryTree(p);
456: tmp.addChild(ret);
457:
458: m_lookAhead = m_scanner.nextToken();
459: ret = atom();
460: tmp.addChild(ret);
461:
462: m_lookAhead = m_scanner.nextToken();
463:
464: ret = tmp;
465: }
466: return ret;
467: }
468: }
|