1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.modeshape.graph.query.model;
25
26 import java.text.CharacterIterator;
27 import java.text.StringCharacterIterator;
28 import java.util.Iterator;
29 import java.util.List;
30 import net.jcip.annotations.Immutable;
31 import org.modeshape.common.text.ParsingException;
32 import org.modeshape.common.util.CheckArg;
33 import org.modeshape.common.util.HashCode;
34 import org.modeshape.common.util.ObjectUtil;
35 import org.modeshape.graph.query.parse.FullTextSearchParser;
36
37
38
39
40
41 @Immutable
42 public class FullTextSearch implements Constraint {
43 private static final long serialVersionUID = 1L;
44
45 private final SelectorName selectorName;
46 private final String propertyName;
47 private final String fullTextSearchExpression;
48 private Term term;
49 private final int hc;
50
51
52
53
54
55
56
57
58
59 public FullTextSearch( SelectorName selectorName,
60 String propertyName,
61 String fullTextSearchExpression,
62 Term term ) {
63 CheckArg.isNotNull(selectorName, "selectorName");
64 CheckArg.isNotEmpty(fullTextSearchExpression, "fullTextSearchExpression");
65 this.selectorName = selectorName;
66 this.propertyName = propertyName;
67 this.term = term;
68 this.fullTextSearchExpression = fullTextSearchExpression;
69 this.hc = HashCode.compute(this.selectorName, this.propertyName, this.fullTextSearchExpression);
70 }
71
72
73
74
75
76
77
78
79 public FullTextSearch( SelectorName selectorName,
80 String propertyName,
81 String fullTextSearchExpression ) {
82 CheckArg.isNotNull(selectorName, "selectorName");
83 CheckArg.isNotEmpty(fullTextSearchExpression, "fullTextSearchExpression");
84 this.selectorName = selectorName;
85 this.propertyName = propertyName;
86 this.fullTextSearchExpression = fullTextSearchExpression;
87 this.term = null;
88 this.hc = HashCode.compute(this.selectorName, this.propertyName, this.fullTextSearchExpression);
89 }
90
91
92
93
94
95
96
97 public FullTextSearch( SelectorName selectorName,
98 String fullTextSearchExpression ) {
99 CheckArg.isNotNull(selectorName, "selectorName");
100 CheckArg.isNotEmpty(fullTextSearchExpression, "fullTextSearchExpression");
101 this.selectorName = selectorName;
102 this.propertyName = null;
103 this.term = null;
104 this.fullTextSearchExpression = fullTextSearchExpression;
105 this.hc = HashCode.compute(this.selectorName, this.propertyName, this.fullTextSearchExpression);
106 }
107
108
109
110
111
112
113 public final SelectorName selectorName() {
114 return selectorName;
115 }
116
117
118
119
120
121
122 public final String propertyName() {
123 return propertyName;
124 }
125
126
127
128
129
130
131 public final String fullTextSearchExpression() {
132 return fullTextSearchExpression;
133 }
134
135
136
137
138
139
140
141 public Term getTerm() {
142
143 if (term == null) {
144 term = new FullTextSearchParser().parse(fullTextSearchExpression);
145 }
146 return term;
147 }
148
149
150
151
152
153
154 @Override
155 public String toString() {
156 return Visitors.readable(this);
157 }
158
159
160
161
162
163
164 @Override
165 public int hashCode() {
166 return hc;
167 }
168
169
170
171
172
173
174 @Override
175 public boolean equals( Object obj ) {
176 if (obj == this) return true;
177 if (obj instanceof FullTextSearch) {
178 FullTextSearch that = (FullTextSearch)obj;
179 if (this.hc != that.hc) return false;
180 if (!this.selectorName.equals(that.selectorName)) return false;
181 if (!ObjectUtil.isEqualWithNulls(this.propertyName, that.propertyName)) return false;
182 if (!this.fullTextSearchExpression.equals(that.fullTextSearchExpression)) return false;
183 return true;
184 }
185 return false;
186 }
187
188
189
190
191
192
193 public void accept( Visitor visitor ) {
194 visitor.visit(this);
195 }
196
197
198
199
200 public static interface Term {
201 }
202
203
204
205
206 public static class NegationTerm implements Term {
207 private final Term negated;
208
209 public NegationTerm( Term negatedTerm ) {
210 assert negatedTerm != null;
211 this.negated = negatedTerm;
212 }
213
214
215
216
217
218
219 public Term getNegatedTerm() {
220 return negated;
221 }
222
223
224
225
226
227
228 @Override
229 public int hashCode() {
230 return negated.hashCode();
231 }
232
233
234
235
236
237
238 @Override
239 public boolean equals( Object obj ) {
240 if (obj == this) return true;
241 if (obj instanceof NegationTerm) {
242 NegationTerm that = (NegationTerm)obj;
243 return this.getNegatedTerm().equals(that.getNegatedTerm());
244 }
245 return false;
246 }
247
248
249
250
251
252
253 @Override
254 public String toString() {
255 return "-" + negated.toString();
256 }
257 }
258
259
260
261
262 public static class SimpleTerm implements Term {
263 private final String value;
264 private final boolean quoted;
265
266
267
268
269
270
271 public SimpleTerm( String value ) {
272 assert value != null;
273 assert value.trim().length() > 0;
274 this.value = value;
275 this.quoted = this.value.indexOf(' ') != -1;
276 }
277
278
279
280
281
282
283
284 public String getValue() {
285 return value;
286 }
287
288
289
290
291
292
293 public String[] getValues() {
294 return value.split("/w");
295 }
296
297
298
299
300
301
302 public boolean isQuotingRequired() {
303 return quoted;
304 }
305
306
307
308
309
310
311 public boolean containsWildcards() {
312 if (this.value.length() == 0) return false;
313 CharacterIterator iter = new StringCharacterIterator(this.value);
314 boolean skipNext = false;
315 for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
316 if (skipNext) {
317 skipNext = false;
318 continue;
319 }
320 if (c == '*' || c == '?' || c == '%' || c == '_') return true;
321 if (c == '\\') skipNext = true;
322 }
323 return false;
324 }
325
326
327
328
329
330
331 @Override
332 public int hashCode() {
333 return value.hashCode();
334 }
335
336
337
338
339
340
341 @Override
342 public boolean equals( Object obj ) {
343 if (obj == this) return true;
344 if (obj instanceof SimpleTerm) {
345 SimpleTerm that = (SimpleTerm)obj;
346 return this.getValue().equals(that.getValue());
347 }
348 return false;
349 }
350
351
352
353
354
355
356 @Override
357 public String toString() {
358 return quoted ? "\"" + this.value + "\"" : this.value;
359 }
360 }
361
362
363
364
365 public static abstract class CompoundTerm implements Term, Iterable<Term> {
366 private final List<Term> terms;
367
368
369
370
371
372
373 protected CompoundTerm( List<Term> terms ) {
374 this.terms = terms;
375 }
376
377
378
379
380
381
382 public List<Term> getTerms() {
383 return terms;
384 }
385
386
387
388
389
390
391 public Iterator<Term> iterator() {
392 return terms.iterator();
393 }
394
395
396
397
398
399
400 @Override
401 public int hashCode() {
402 return terms.hashCode();
403 }
404
405
406
407
408
409
410 @Override
411 public boolean equals( Object obj ) {
412 if (obj == this) return true;
413 if (this.getClass().isInstance(obj)) {
414 CompoundTerm that = (CompoundTerm)obj;
415 return this.getTerms().equals(that.getTerms());
416 }
417 return false;
418 }
419
420 protected String toString( String delimiter ) {
421 if (terms.size() == 1) return terms.iterator().next().toString();
422 StringBuilder sb = new StringBuilder();
423 sb.append("( ");
424 boolean first = true;
425 for (Term term : terms) {
426 if (first) first = false;
427 else sb.append(' ').append(delimiter).append(' ');
428 sb.append(term);
429 }
430 sb.append(" )");
431 return sb.toString();
432 }
433 }
434
435
436
437
438 public static class Disjunction extends CompoundTerm {
439
440
441
442
443
444
445 public Disjunction( List<Term> terms ) {
446 super(terms);
447 }
448
449
450
451
452
453
454 @Override
455 public String toString() {
456 return toString("OR");
457 }
458 }
459
460
461
462
463 public static class Conjunction extends CompoundTerm {
464
465
466
467
468
469
470 public Conjunction( List<Term> terms ) {
471 super(terms);
472 }
473
474
475
476
477
478
479 @Override
480 public String toString() {
481 return toString("AND");
482 }
483 }
484
485 }