1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.allanbank.mongodb.bson.element;
21
22 import static com.allanbank.mongodb.util.Assertions.assertNotNull;
23
24 import java.util.regex.Pattern;
25
26 import com.allanbank.mongodb.bson.Element;
27 import com.allanbank.mongodb.bson.ElementType;
28 import com.allanbank.mongodb.bson.Visitor;
29 import com.allanbank.mongodb.bson.io.StringEncoder;
30
31
32
33
34
35
36
37
38
39
40 public class RegularExpressionElement extends AbstractElement {
41
42
43 public static final int CASE_INSENSITIVE;
44
45
46 public static final int DOT_ALL;
47
48
49 public static final int LOCALE_DEPENDENT;
50
51
52 public static final int MULTILINE;
53
54
55 public static final int OPTION_I;
56
57
58 public static final int OPTION_L;
59
60
61 public static final int OPTION_M;
62
63
64 public static final int OPTION_MASK;
65
66
67 public static final int OPTION_S;
68
69
70 public static final int OPTION_U;
71
72
73 public static final int OPTION_X;
74
75
76 public static final ElementType TYPE = ElementType.REGEX;
77
78
79 public static final int UNICODE;
80
81
82 public static final int VERBOSE;
83
84
85
86
87
88 protected static final int PATTERN_UNICODE;
89
90
91 private static final String[] OPTIONS;
92
93
94 private static final long serialVersionUID = 7842839168833403380L;
95
96 static {
97
98 OPTION_I = 0x01;
99 OPTION_L = 0x02;
100 OPTION_M = 0x04;
101 OPTION_S = 0x08;
102 OPTION_U = 0x10;
103 OPTION_X = 0x20;
104 OPTION_MASK = 0x3F;
105
106 CASE_INSENSITIVE = OPTION_I;
107 LOCALE_DEPENDENT = OPTION_L;
108 MULTILINE = OPTION_M;
109 DOT_ALL = OPTION_S;
110 UNICODE = OPTION_U;
111 VERBOSE = OPTION_X;
112
113 final String[] options = new String[OPTION_MASK + 1];
114
115 final StringBuilder builder = new StringBuilder();
116 for (int i = 0; i < (OPTION_MASK + 1); ++i) {
117 builder.setLength(0);
118
119
120 if ((i & OPTION_I) == OPTION_I) {
121 builder.append('i');
122 }
123 if ((i & OPTION_L) == OPTION_L) {
124 builder.append('l');
125 }
126 if ((i & OPTION_M) == OPTION_M) {
127 builder.append('m');
128 }
129 if ((i & OPTION_S) == OPTION_S) {
130 builder.append('s');
131 }
132 if ((i & OPTION_U) == OPTION_U) {
133 builder.append('u');
134 }
135 if ((i & OPTION_X) == OPTION_X) {
136 builder.append('x');
137 }
138 options[i] = builder.toString();
139 }
140
141 OPTIONS = options;
142
143
144 PATTERN_UNICODE = 0x100;
145 }
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169 @SuppressWarnings("javadoc")
170 protected static int optionsAsInt(final Pattern pattern) {
171 int optInt = 0;
172
173 if (pattern != null) {
174 final int flags = pattern.flags();
175 if ((flags & Pattern.CASE_INSENSITIVE) == Pattern.CASE_INSENSITIVE) {
176 optInt |= CASE_INSENSITIVE;
177 }
178 if ((flags & Pattern.MULTILINE) == Pattern.MULTILINE) {
179 optInt |= MULTILINE;
180 }
181 if ((flags & Pattern.DOTALL) == Pattern.DOTALL) {
182 optInt |= DOT_ALL;
183 }
184 if ((flags & PATTERN_UNICODE) == PATTERN_UNICODE) {
185 optInt |= UNICODE;
186 }
187 }
188
189 return optInt;
190 }
191
192
193
194
195
196
197
198
199 protected static int optionsAsInt(final String options) {
200 int optInt = 0;
201
202 if (options != null) {
203 for (final char c : options.toCharArray()) {
204 if ((c == 'i') || (c == 'I')) {
205 optInt |= OPTION_I;
206 }
207 else if ((c == 'l') || (c == 'L')) {
208 optInt |= OPTION_L;
209 }
210 else if ((c == 'm') || (c == 'M')) {
211 optInt |= OPTION_M;
212 }
213 else if ((c == 's') || (c == 'S')) {
214 optInt |= OPTION_S;
215 }
216 else if ((c == 'u') || (c == 'U')) {
217 optInt |= OPTION_U;
218 }
219 else if ((c == 'x') || (c == 'X')) {
220 optInt |= OPTION_X;
221 }
222 else {
223 throw new IllegalArgumentException(
224 "Invalid regular expression option '" + c
225 + "' in options '" + options + "'.");
226 }
227 }
228 }
229
230 return optInt;
231 }
232
233
234
235
236
237
238
239
240
241
242
243
244
245 private static long computeSize(final String name, final String pattern,
246 final int options) {
247 long result = 4;
248
249 result += StringEncoder.utf8Size(name);
250 result += StringEncoder.utf8Size(pattern);
251 result += OPTIONS[options & OPTION_MASK].length();
252
253 return result;
254 }
255
256
257 private final int myOptions;
258
259
260 private final String myPattern;
261
262
263
264
265
266
267
268
269
270
271
272 public RegularExpressionElement(final String name, final Pattern pattern) {
273 this(name, (pattern != null) ? pattern.pattern() : null,
274 optionsAsInt(pattern));
275 }
276
277
278
279
280
281
282
283
284
285
286
287
288
289 public RegularExpressionElement(final String name, final String pattern,
290 final int options) {
291 super(name, computeSize(name, pattern, options));
292
293 assertNotNull(pattern,
294 "Regular Expression element's pattern cannot be null.");
295
296 myPattern = pattern;
297 myOptions = options;
298 }
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317 public RegularExpressionElement(final String name, final String pattern,
318 final int options, final long size) {
319 super(name, size);
320
321 assertNotNull(pattern,
322 "Regular Expression element's pattern cannot be null.");
323
324 myPattern = pattern;
325 myOptions = options;
326 }
327
328
329
330
331
332
333
334
335
336
337
338
339
340 public RegularExpressionElement(final String name, final String pattern,
341 final String options) {
342 this(name, pattern, optionsAsInt(options));
343 }
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362 public RegularExpressionElement(final String name, final String pattern,
363 final String options, final long size) {
364 this(name, pattern, optionsAsInt(options), size);
365 }
366
367
368
369
370
371
372
373 @Override
374 public void accept(final Visitor visitor) {
375 visitor.visitRegularExpression(getName(), getPattern(),
376 OPTIONS[getOptions() & OPTION_MASK]);
377 }
378
379
380
381
382
383
384
385
386 @Override
387 public int compareTo(final Element otherElement) {
388 int result = super.compareTo(otherElement);
389
390 if (result == 0) {
391 final RegularExpressionElement other = (RegularExpressionElement) otherElement;
392
393 result = myPattern.compareTo(other.myPattern);
394 if (result == 0) {
395 result = compare(myOptions, other.myOptions);
396 }
397 }
398
399 return result;
400 }
401
402
403
404
405
406
407
408
409
410
411 @Override
412 public boolean equals(final Object object) {
413 boolean result = false;
414 if (this == object) {
415 result = true;
416 }
417 else if ((object != null) && (getClass() == object.getClass())) {
418 final RegularExpressionElement other = (RegularExpressionElement) object;
419
420 result = (myOptions == other.myOptions) && super.equals(object)
421 && nullSafeEquals(myPattern, other.myPattern);
422 }
423 return result;
424 }
425
426
427
428
429
430
431 public int getOptions() {
432 return myOptions;
433 }
434
435
436
437
438
439
440 public String getPattern() {
441 return myPattern;
442 }
443
444
445
446
447 @Override
448 public ElementType getType() {
449 return TYPE;
450 }
451
452
453
454
455
456
457
458 @Override
459 public Pattern getValueAsObject() {
460
461 int options = 0;
462 if ((myOptions & CASE_INSENSITIVE) == CASE_INSENSITIVE) {
463 options |= Pattern.CASE_INSENSITIVE;
464 }
465 if ((myOptions & MULTILINE) == MULTILINE) {
466 options |= Pattern.MULTILINE;
467 }
468 if ((myOptions & DOT_ALL) == DOT_ALL) {
469 options |= Pattern.DOTALL;
470 }
471 if ((myOptions & UNICODE) == UNICODE) {
472 options |= PATTERN_UNICODE;
473 }
474
475 return Pattern.compile(myPattern, options);
476 }
477
478
479
480
481
482
483 @Override
484 public int hashCode() {
485 int result = 1;
486 result = (31 * result) + super.hashCode();
487 result = (31 * result)
488 + ((myPattern != null) ? myPattern.hashCode() : 3);
489 result = (31 * result) + myOptions;
490 return result;
491 }
492
493
494
495
496
497
498
499 @Override
500 public RegularExpressionElement withName(final String name) {
501 if (getName().equals(name)) {
502 return this;
503 }
504 return new RegularExpressionElement(name, myPattern, myOptions);
505 }
506 }