1 // $Id: CQLParser.java,v 1.3 2002-10-24 16:06:34 mike Exp $
3 package org.z3950.zing.cql;
4 import java.util.Properties;
5 import java.io.InputStream;
6 import java.io.FileNotFoundException;
7 import java.io.IOException;
8 import java.io.StringReader;
9 import java.io.StreamTokenizer;
13 * Compiles a CQL string into a parse tree ...
16 * @version $Id: CQLParser.java,v 1.3 2002-10-24 16:06:34 mike Exp $
17 * @see <A href="http://zing.z3950.org/cql/index.html"
18 * >http://zing.z3950.org/cql/index.html</A>
22 private String qualset;
23 private Properties qualsetProperties;
24 private StreamTokenizer st;
26 private class CQLParseException extends Exception {
27 CQLParseException(String s) { super(s); }
30 public CQLCompiler(String cql, String qualset) {
32 this.qualset = qualset;
35 public String convertToPQN()
36 throws FileNotFoundException, IOException {
38 if (qualsetProperties == null) {
39 // ### Could think about caching named qualifier sets
40 // across compilations (i.e. shared, in a static
41 // Hashtable, between multiple CQLCompiler
42 // instances.) Probably not worth it.
43 InputStream is = this.getClass().getResourceAsStream(qualset);
45 throw new FileNotFoundException("getResourceAsStream(" +
47 qualsetProperties = new Properties();
48 qualsetProperties.load(is);
51 st = new StreamTokenizer(new StringReader(cql));
52 st.wordChars('/', '/');
53 st.wordChars('0', '9'); // ### but 1 is still recognised as TT_NUM
54 st.wordChars('.', '.');
55 st.wordChars('-', '-');
62 // while ((token = st.nextToken()) != st.TT_EOF) {
63 // System.out.println("token=" + token + ", " +
64 // "nval=" + st.nval + ", " +
65 // "sval=" + st.sval);
71 ret = parse_expression();
72 } catch (CQLParseException ex) {
73 System.err.println("### Oops: " + ex);
77 if (st.ttype != st.TT_EOF) {
78 System.err.println("### Extra bits: " + render(st));
82 // Interpret attributes as BIB-1 unless otherwise specified
83 return "@attrset bib-1 " + ret;
86 private String parse_expression()
87 throws CQLParseException, IOException {
88 String term = parse_term();
90 while (st.ttype == st.TT_WORD) {
91 String op = st.sval.toLowerCase();
92 if (!st.sval.equals("and") &&
93 !st.sval.equals("or") &&
94 !st.sval.equals("not"))
97 String term2 = parse_term();
98 term = "@" + op + " " + term + " " + term2;
104 private String parse_term()
105 throws CQLParseException, IOException {
106 if (st.ttype == '(') {
108 String expr = parse_expression();
116 // ### We treat ',' and '=' equivalently here, which isn't quite right.
117 while (st.ttype == st.TT_WORD) {
120 if (st.ttype != '=' && st.ttype != ',') {
121 // end of qualifer list
125 String attr = qualsetProperties.getProperty(word);
127 throw new CQLParseException("unrecognised qualifier: " + word);
129 attrs = attrs + attr + " ";
131 word = null; // mark as not-yet-read
135 // got to the end of a "foo,bar=" sequence
137 if (st.ttype != '\'' || st.ttype != '"') {
138 word = "\"" + word + "\"";
148 private void match(int token)
149 throws CQLParseException, IOException {
150 if (st.ttype != token)
151 throw new CQLParseException("expected " + render(st, token, null) +
152 ", " + "got " + render(st));
156 // ### This utility should surely be a method of the StreamTokenizer class
157 private static String render(StreamTokenizer st) {
158 return render(st, st.ttype, null);
161 private static String render(StreamTokenizer st, int token, String str) {
165 case st.TT_EOF: return "EOF";
166 case st.TT_EOL: return "EOL";
167 case st.TT_NUMBER: return "number";
168 case st.TT_WORD: ret = "word"; break;
169 case '"': case '\'': ret = "string"; break;
170 default: return "'" + String.valueOf((char) token) + "'";
174 ret += "(\"" + str + "\")";
178 // ### Not really the right place for this test harness.
180 // e.g. java uk.org.miketaylor.zoom.CQLCompiler
181 // '(au=Kerninghan or au=Ritchie) and ti=Unix' qualset.properties
185 // @attr 1=1 @attr 4=1 Kerninghan
186 // @attr 1=1 @attr 4=1 Ritchie
187 // @attr 1=4 @attr 4=1 Unix
189 public static void main (String[] args) {
190 if (args.length != 2) {
191 System.err.println("Usage: CQLQuery <cql> <qualset>");
195 CQLCompiler cc = new CQLCompiler(args[0], args[1]);
197 String pqn = cc.convertToPQN();
198 System.out.println(pqn);
199 } catch (FileNotFoundException ex) {
200 System.err.println("Can't find qualifier set: " + ex);
202 } catch (IOException ex) {
203 System.err.println("Can't read qualifier set: " + ex);