1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.fatwire.gst.foundation.facade.assetapi;
18
19 import java.util.Arrays;
20 import java.util.LinkedList;
21 import java.util.List;
22
23 import com.fatwire.assetapi.common.AssetAccessException;
24 import com.fatwire.assetapi.query.Condition;
25 import com.fatwire.assetapi.query.ConditionFactory;
26 import com.fatwire.assetapi.query.OpTypeEnum;
27
28 import org.apache.commons.lang.StringUtils;
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 public class ConditionParser {
50
51 enum Operator {
52 EQUALS("="), NOT_EQUALS("!="), LESS_THAN("<"), LESS_THAN_EQUALS("<="), GREATER_THAN(">"), GREATER_THAN_EQUALS(
53 ">="), BETWEEN("={"), BETWEEN_EXCLUDING("=!{"), LIKE("~"), RICHTEXT("#");
54 private final String op;
55
56 Operator(final String op) {
57 this.op = op;
58 }
59 };
60
61 private enum State {
62 ATTRIBUTE, OP, VALUE
63
64 };
65
66 interface ParserState {
67 ParserState parse(char c);
68
69 String toValue();
70 }
71
72 static class AttributeState implements ParserState {
73 private final StringBuilder value = new StringBuilder();
74 private boolean quoted = false;
75 private final ParserState next;
76
77 AttributeState(final ParserState next) {
78 this.next = next;
79 }
80
81 public ParserState parse(final char c) {
82 if (Character.isWhitespace(c)) {
83 if (quoted) {
84 value.append(c);
85 } else {
86 return next;
87 }
88 } else if (c == '"' || c == '\'') {
89 if (quoted) {
90 quoted = false;
91 return next;
92 } else {
93 quoted = true;
94 }
95 } else if ("=!<>~{}".indexOf(c) != -1) {
96 if (quoted) {
97 value.append(c);
98 } else if (value.length() > 0) {
99 next.parse(c);
100 return next;
101 }
102 } else {
103 value.append(c);
104
105 }
106 return this;
107
108 }
109
110 public String toValue() {
111 return value.toString();
112 }
113 }
114
115 static class OperatorState implements ParserState {
116 private final StringBuilder value = new StringBuilder();
117 private final ParserState next;
118
119 OperatorState(final ParserState next) {
120 this.next = next;
121 }
122
123 public ParserState parse(final char c) {
124 if ("=!<>~{".indexOf(c) != -1) {
125 value.append(c);
126 } else {
127 next.parse(c);
128 return next;
129 }
130 return this;
131 }
132
133 public String toValue() {
134 return value.toString();
135 }
136
137 }
138
139 static class ValueState implements ParserState {
140 private final StringBuilder value = new StringBuilder();
141 private boolean quoted = false;
142
143 public ParserState parse(final char c) {
144 if (Character.isWhitespace(c)) {
145 if (quoted) {
146 value.append(c);
147 } else if (value.length() > 0) {
148 value.append(c);
149 }
150 } else if (c == '"' || c == '\'') {
151 quoted = !quoted;
152 value.append(c);
153 } else if ("{}".indexOf(c) != -1) {
154
155 if (quoted) {
156 value.append(c);
157 }
158 } else {
159 value.append(c);
160
161 }
162 return this;
163 }
164
165 public String toValue() {
166 return value.toString();
167 }
168
169 }
170
171 public Condition parse(final String s) {
172 final ValueState valueState = new ValueState();
173 final OperatorState operatorState = new OperatorState(valueState);
174 final AttributeState attributeState = new AttributeState(operatorState);
175 ParserState state = attributeState;
176
177 final char[] c = s.trim().toCharArray();
178 for (int i = 0; i < c.length; i++) {
179 state = state.parse(c[i]);
180 }
181 final String attName = attributeState.toValue();
182 if (StringUtils.isBlank(attName)) {
183 throw new IllegalArgumentException("No attribute name found in '" + s + "'.");
184 }
185
186 OpTypeEnum opType;
187 try {
188 opType = toOpType(operatorState.toValue());
189 } catch (final Exception e) {
190 final IllegalArgumentException e2 = new IllegalArgumentException("No operator found in '" + s + "'. "
191 + e.getMessage());
192 e2.initCause(e);
193 throw e2;
194 }
195
196 final String value = valueState.toValue();
197 if (opType == OpTypeEnum.BETWEEN) {
198
199 final String[] parts = valueSplit(value);
200 if (parts.length != 2) {
201 throw new IllegalArgumentException("Between condition does not two comma-seperated values in '" + s
202 + "'. ");
203 }
204 try {
205 return new ConditionFactory().createBetweenCondition(attName, parts[0], parts[1]);
206 } catch (final AssetAccessException e) {
207 final RuntimeAssetAccessException e1 = new RuntimeAssetAccessException(e.getMessage());
208 e1.initCause(e);
209 throw e1;
210 }
211 } else if (opType == OpTypeEnum.EQUALS && value.startsWith("[") && value.endsWith("]")) {
212
213 final String[] parts = valueSplit(value.substring(1, value.length() - 1));
214 if (parts.length < 1) {
215 throw new IllegalArgumentException("Equals condition with multiple values does have any values: '" + s
216 + "'. ");
217 }
218
219 return ConditionFactory.createCondition(attName, opType, Arrays.asList(parts));
220 } else if (opType == OpTypeEnum.NOT_EQUALS && value.startsWith("[") && value.endsWith("]")) {
221
222 final String[] parts = valueSplit(value.substring(1, value.length() - 1));
223 if (parts.length < 1) {
224 throw new IllegalArgumentException("Equals condition with multiple values does have any values: '" + s
225 + "'. ");
226 }
227 Condition condition = null;
228 for (final String part : parts) {
229 final Condition cc = ConditionFactory.createCondition(attName, opType, part);
230 if (condition == null) {
231 condition = cc;
232 } else {
233 condition = condition.and(cc);
234 }
235 }
236 return condition;
237 }
238
239 return ConditionFactory.createCondition(attName, opType, unquote(value));
240
241 }
242
243 private String unquote(final String value) {
244 if (StringUtils.isBlank(value)) {
245 return value;
246 }
247 final char c = value.charAt(0);
248 if (c == '\'' || c == '"') {
249 if (value.length() < 3) {
250 return "";
251 }
252 return value.substring(1, value.length() - 1);
253 }
254 return value;
255 }
256
257 public String[] valueSplit(final String s) {
258 final List<String> list = new LinkedList<String>();
259 boolean quoted = false;
260 final char[] c = s.toCharArray();
261 final StringBuilder cur = new StringBuilder();
262 for (int i = 0; i < c.length; i++) {
263 if (c[i] == ',') {
264 if (quoted) {
265 cur.append(c[i]);
266 } else {
267 list.add(cur.toString());
268 cur.setLength(0);
269 }
270 } else if (c[i] == '\'') {
271 quoted = !quoted;
272 } else {
273 cur.append(c[i]);
274 }
275
276 }
277 list.add(cur.toString());
278 return list.toArray(new String[0]);
279 }
280
281 OpTypeEnum toOpType(final StringBuilder op) {
282 return toOpType(op.toString().toLowerCase());
283 }
284
285 OpTypeEnum toOpType(final String op) {
286 if (StringUtils.isBlank(op)) {
287 throw new IllegalArgumentException("Operator can not be blank.");
288 }
289 if ("=".equals(op)) {
290 return OpTypeEnum.EQUALS;
291 } else if ("!=".equals(op)) {
292 return OpTypeEnum.NOT_EQUALS;
293 } else if ("<".equals(op)) {
294 return OpTypeEnum.LESS_THAN;
295 } else if (">".equals(op)) {
296 return OpTypeEnum.GREATER_THAN;
297 } else if ("{".equals(op)) {
298 return OpTypeEnum.BETWEEN;
299 } else if ("~".equals(op)) {
300 return OpTypeEnum.LIKE;
301 } else if ("#".equals(op)) {
302 return OpTypeEnum.RICHTEXT;
303
304 }
305 throw new IllegalArgumentException("Can't decode operator in " + op);
306 }
307
308 }