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