Rewrite lexer to comply with CQL spec
[cql-java-moved-to-github.git] / src / main / java / org / z3950 / zing / cql / CQLLexerSimple.java
1 /*
2  * Copyright (c) 1995-2014, Index Datassss
3  * All rights reserved.
4  * See the file LICENSE for details.
5  */
6 package org.z3950.zing.cql;
7
8 /**
9  *
10  * @author jakub
11  */
12 public class CQLLexerSimple implements CQLLexer {
13   private String qs;
14   private int qi;
15   private int ql;
16   private int what = TT_NOTHING;
17   private String val;
18   private String lval;
19   private StringBuilder buf = new StringBuilder();
20
21   public CQLLexerSimple(String cql, boolean debug) {
22     qs = cql;
23     ql = cql.length();
24   }
25
26   @Override
27   public void move() {
28     //eat whitespace
29     while (qi < ql && strchr(" \t\r\n", qs.charAt(qi)))
30       qi++;
31     //eof
32     if (qi == ql) {
33       what = TT_EOF;
34       return;
35     }
36     //current char
37     char c = qs.charAt(qi);
38     //separators
39     if (strchr("()/", c)) {
40       what = c;
41       qi++;
42     //comparitor
43     } else if (strchr("<>=", c)) {
44       what = c;
45       qi++;
46       //two-char comparitor
47       if (qi < ql) {
48         char d = qs.charAt(qi);
49         String comp = String.valueOf((char) c) + String.valueOf((char) d);
50         if (comp.equals("==")) {
51           what = TT_EQEQ;
52           qi++;
53         }
54         else if (comp.equals("<=")) {
55           what = TT_LE;
56           qi++;
57         }
58         else if (comp.equals(">=")) {
59           what = TT_GE;
60           qi++;
61         }
62         else if (comp.equals("<>")) {
63           what = TT_NE;
64           qi++;
65         }
66       }
67     //quoted string
68     } else if (strchr("\"", c)) { //no single-quotes
69       what = '"';
70       //remember quote char
71       char mark = c;
72       qi++;
73       boolean escaped = false;
74       buf.setLength(0); //reset buffer
75       while (qi < ql) {
76         if (!escaped && qs.charAt(qi) == mark) //terminator
77           break;
78         if (escaped && strchr("*?^\\", qs.charAt(qi))) //no escaping for d-quote
79           buf.append("\\");
80         if (!escaped && qs.charAt(qi) == '\\') { //escape-char
81           escaped = true;
82           qi++;
83           continue;
84         }
85         escaped = false; //reset escape
86         buf.append(qs.charAt(qi));
87         qi++;
88       }
89       val = buf.toString();
90       lval = val.toLowerCase();
91       if (qi < ql)
92         qi++;
93       else //unterminated
94         what = TT_EOF; //notify error
95       //unquoted string
96     } else {
97       what = TT_WORD;
98       buf.setLength(0); //reset buffer
99       while (qi < ql
100         && !strchr("()/<>= \t\r\n", qs.charAt(qi))) {
101         buf.append(qs.charAt(qi));
102         qi++;
103       }
104       val = buf.toString();
105       lval = val.toLowerCase();
106       if (lval.equals("or")) what = TT_OR;
107       else if (lval.equals("and")) what = TT_AND;
108       else if (lval.equals("not")) what = TT_NOT;
109       else if (lval.equals("prox")) what = TT_PROX;
110       else if (lval.equals("sortby")) what = TT_SORTBY;
111     }
112   }
113
114   private boolean strchr(String s, char ch) {
115     return s.indexOf(ch) >= 0;
116   }
117
118   @Override
119   public String value() {
120     return val;
121   }
122
123   @Override
124   public int what() {
125     return what;
126   }
127
128   @Override
129   public String render() {
130     return render(what, true);
131   }
132
133   @Override
134   public String render(int token, boolean quoteChars) {
135     switch (token) {
136       case TT_EOF:
137         return "EOF";
138       case TT_WORD:
139         return "word: " + val;
140       case '"':
141         return "string: \"" + val + "\"";
142       case TT_LE:
143         return "<=";
144       case TT_GE:
145         return ">=";
146       case TT_NE:
147         return "<>";
148       case TT_EQEQ:
149         return "==";
150       case TT_AND:
151         return "and";
152       case TT_NOT:
153         return "not";
154       case TT_OR:
155         return "or";
156       case TT_PROX:
157         return "prox";
158       case TT_SORTBY:
159         return "sortby";
160       default:
161         //a single character, such as '(' or '/' or relation
162         String res = String.valueOf((char) token);
163         if (quoteChars)
164           res = "'" + res + "'";
165         return res;
166     }
167   }
168
169   @Override
170   public int pos() {
171     return qi;
172   }
173 }