source: josm/trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java@ 4347

Last change on this file since 4347 was 4347, checked in by stoecker, 13 years ago

see #3345 - patch by simon04 - add search for new objects

  • Property svn:eol-style set to native
File size: 28.7 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.actions.search;
3
4import static org.openstreetmap.josm.tools.I18n.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.io.PushbackReader;
8import java.io.StringReader;
9import java.text.Normalizer;
10import java.util.regex.Matcher;
11import java.util.regex.Pattern;
12import java.util.regex.PatternSyntaxException;
13
14import org.openstreetmap.josm.Main;
15import org.openstreetmap.josm.actions.search.PushbackTokenizer.Range;
16import org.openstreetmap.josm.actions.search.PushbackTokenizer.Token;
17import org.openstreetmap.josm.data.osm.Node;
18import org.openstreetmap.josm.data.osm.OsmPrimitive;
19import org.openstreetmap.josm.data.osm.OsmUtils;
20import org.openstreetmap.josm.data.osm.Relation;
21import org.openstreetmap.josm.data.osm.RelationMember;
22import org.openstreetmap.josm.data.osm.Way;
23import org.openstreetmap.josm.tools.DateUtils;
24import org.openstreetmap.josm.tools.Geometry;
25
26/**
27 Implements a google-like search.
28 <br>
29 Grammar:
30<pre>
31expression =
32 fact | expression
33 fact expression
34 fact
35
36fact =
37 ( expression )
38 -fact
39 term?
40 term=term
41 term:term
42 term
43 </pre>
44
45 @author Imi
46 */
47public class SearchCompiler {
48
49 private boolean caseSensitive = false;
50 private boolean regexSearch = false;
51 private static String rxErrorMsg = marktr("The regex \"{0}\" had a parse error at offset {1}, full error:\n\n{2}");
52 private static String rxErrorMsgNoPos = marktr("The regex \"{0}\" had a parse error, full error:\n\n{1}");
53 private PushbackTokenizer tokenizer;
54
55 public SearchCompiler(boolean caseSensitive, boolean regexSearch, PushbackTokenizer tokenizer) {
56 this.caseSensitive = caseSensitive;
57 this.regexSearch = regexSearch;
58 this.tokenizer = tokenizer;
59 }
60
61 abstract public static class Match {
62 abstract public boolean match(OsmPrimitive osm);
63 }
64
65 public static class Always extends Match {
66 public static Always INSTANCE = new Always();
67 @Override public boolean match(OsmPrimitive osm) {
68 return true;
69 }
70 }
71
72 public static class Never extends Match {
73 @Override
74 public boolean match(OsmPrimitive osm) {
75 return false;
76 }
77 }
78
79 public static class Not extends Match {
80 private final Match match;
81 public Not(Match match) {this.match = match;}
82 @Override public boolean match(OsmPrimitive osm) {
83 return !match.match(osm);
84 }
85 @Override public String toString() {return "!"+match;}
86 }
87
88 private static class BooleanMatch extends Match {
89 private final String key;
90 private final boolean defaultValue;
91
92 public BooleanMatch(String key, boolean defaultValue) {
93 this.key = key;
94 this.defaultValue = defaultValue;
95 }
96 @Override
97 public boolean match(OsmPrimitive osm) {
98 Boolean ret = OsmUtils.getOsmBoolean(osm.get(key));
99 if (ret == null)
100 return defaultValue;
101 else
102 return ret;
103 }
104 }
105
106 private static class And extends Match {
107 private Match lhs;
108 private Match rhs;
109 public And(Match lhs, Match rhs) {this.lhs = lhs; this.rhs = rhs;}
110 @Override public boolean match(OsmPrimitive osm) {
111 return lhs.match(osm) && rhs.match(osm);
112 }
113 @Override public String toString() {return lhs+" && "+rhs;}
114 }
115
116 private static class Or extends Match {
117 private Match lhs;
118 private Match rhs;
119 public Or(Match lhs, Match rhs) {this.lhs = lhs; this.rhs = rhs;}
120 @Override public boolean match(OsmPrimitive osm) {
121 return lhs.match(osm) || rhs.match(osm);
122 }
123 @Override public String toString() {return lhs+" || "+rhs;}
124 }
125
126 private static class Id extends Match {
127 private long id;
128 public Id(long id) {
129 this.id = id;
130 }
131 @Override public boolean match(OsmPrimitive osm) {
132 return id == 0?osm.isNew():osm.getUniqueId() == id;
133 }
134 @Override public String toString() {return "id="+id;}
135 }
136
137 private static class ChangesetId extends Match {
138 private long changesetid;
139 public ChangesetId(long changesetid) {this.changesetid = changesetid;}
140 @Override public boolean match(OsmPrimitive osm) {
141 return osm.getChangesetId() == changesetid;
142 }
143 @Override public String toString() {return "changeset="+changesetid;}
144 }
145
146 private static class Version extends Match {
147 private long version;
148 public Version(long version) {this.version = version;}
149 @Override public boolean match(OsmPrimitive osm) {
150 return osm.getVersion() == version;
151 }
152 @Override public String toString() {return "version="+version;}
153 }
154
155 private static class KeyValue extends Match {
156 private final String key;
157 private final Pattern keyPattern;
158 private final String value;
159 private final Pattern valuePattern;
160 private final boolean caseSensitive;
161
162 public KeyValue(String key, String value, boolean regexSearch, boolean caseSensitive) throws ParseError {
163 this.caseSensitive = caseSensitive;
164 if (regexSearch) {
165 int searchFlags = regexFlags(caseSensitive);
166
167 try {
168 this.keyPattern = Pattern.compile(key, searchFlags);
169 } catch (PatternSyntaxException e) {
170 throw new ParseError(tr(rxErrorMsg, e.getPattern(), e.getIndex(), e.getMessage()));
171 } catch (Exception e) {
172 throw new ParseError(tr(rxErrorMsgNoPos, key, e.getMessage()));
173 }
174 try {
175 this.valuePattern = Pattern.compile(value, searchFlags);
176 } catch (PatternSyntaxException e) {
177 throw new ParseError(tr(rxErrorMsg, e.getPattern(), e.getIndex(), e.getMessage()));
178 } catch (Exception e) {
179 throw new ParseError(tr(rxErrorMsgNoPos, value, e.getMessage()));
180 }
181 this.key = key;
182 this.value = value;
183
184 } else if (caseSensitive) {
185 this.key = key;
186 this.value = value;
187 this.keyPattern = null;
188 this.valuePattern = null;
189 } else {
190 this.key = key.toLowerCase();
191 this.value = value;
192 this.keyPattern = null;
193 this.valuePattern = null;
194 }
195 }
196
197 @Override public boolean match(OsmPrimitive osm) {
198
199 if (keyPattern != null) {
200 if (!osm.hasKeys())
201 return false;
202
203 /* The string search will just get a key like
204 * 'highway' and look that up as osm.get(key). But
205 * since we're doing a regex match we'll have to loop
206 * over all the keys to see if they match our regex,
207 * and only then try to match against the value
208 */
209
210 for (String k: osm.keySet()) {
211 String v = osm.get(k);
212
213 Matcher matcherKey = keyPattern.matcher(k);
214 boolean matchedKey = matcherKey.find();
215
216 if (matchedKey) {
217 Matcher matcherValue = valuePattern.matcher(v);
218 boolean matchedValue = matcherValue.find();
219
220 if (matchedValue)
221 return true;
222 }
223 }
224 } else {
225 String mv = null;
226
227 if (key.equals("timestamp")) {
228 mv = DateUtils.fromDate(osm.getTimestamp());
229 } else {
230 mv = osm.get(key);
231 }
232
233 if (mv == null)
234 return false;
235
236 String v1 = caseSensitive ? mv : mv.toLowerCase();
237 String v2 = caseSensitive ? value : value.toLowerCase();
238
239 v1 = Normalizer.normalize(v1, Normalizer.Form.NFC);
240 v2 = Normalizer.normalize(v2, Normalizer.Form.NFC);
241 return v1.indexOf(v2) != -1;
242 }
243
244 return false;
245 }
246 @Override public String toString() {return key+"="+value;}
247 }
248
249 public static class ExactKeyValue extends Match {
250
251 private enum Mode {
252 ANY, ANY_KEY, ANY_VALUE, EXACT, NONE, MISSING_KEY,
253 ANY_KEY_REGEXP, ANY_VALUE_REGEXP, EXACT_REGEXP, MISSING_KEY_REGEXP;
254 }
255
256 private final String key;
257 private final String value;
258 private final Pattern keyPattern;
259 private final Pattern valuePattern;
260 private final Mode mode;
261
262 public ExactKeyValue(boolean regexp, String key, String value) throws ParseError {
263 if ("".equals(key))
264 throw new ParseError(tr("Key cannot be empty when tag operator is used. Sample use: key=value"));
265 this.key = key;
266 this.value = value == null?"":value;
267 if ("".equals(this.value) && "*".equals(key)) {
268 mode = Mode.NONE;
269 } else if ("".equals(this.value)) {
270 if (regexp) {
271 mode = Mode.MISSING_KEY_REGEXP;
272 } else {
273 mode = Mode.MISSING_KEY;
274 }
275 } else if ("*".equals(key) && "*".equals(this.value)) {
276 mode = Mode.ANY;
277 } else if ("*".equals(key)) {
278 if (regexp) {
279 mode = Mode.ANY_KEY_REGEXP;
280 } else {
281 mode = Mode.ANY_KEY;
282 }
283 } else if ("*".equals(this.value)) {
284 if (regexp) {
285 mode = Mode.ANY_VALUE_REGEXP;
286 } else {
287 mode = Mode.ANY_VALUE;
288 }
289 } else {
290 if (regexp) {
291 mode = Mode.EXACT_REGEXP;
292 } else {
293 mode = Mode.EXACT;
294 }
295 }
296
297 if (regexp && key.length() > 0 && !key.equals("*")) {
298 try {
299 keyPattern = Pattern.compile(key, regexFlags(false));
300 } catch (PatternSyntaxException e) {
301 throw new ParseError(tr(rxErrorMsg, e.getPattern(), e.getIndex(), e.getMessage()));
302 } catch (Exception e) {
303 throw new ParseError(tr(rxErrorMsgNoPos, key, e.getMessage()));
304 }
305 } else {
306 keyPattern = null;
307 }
308 if (regexp && this.value.length() > 0 && !this.value.equals("*")) {
309 try {
310 valuePattern = Pattern.compile(this.value, regexFlags(false));
311 } catch (PatternSyntaxException e) {
312 throw new ParseError(tr(rxErrorMsg, e.getPattern(), e.getIndex(), e.getMessage()));
313 } catch (Exception e) {
314 throw new ParseError(tr(rxErrorMsgNoPos, value, e.getMessage()));
315 }
316 } else {
317 valuePattern = null;
318 }
319 }
320
321 @Override
322 public boolean match(OsmPrimitive osm) {
323
324 if (!osm.hasKeys())
325 return mode == Mode.NONE;
326
327 switch (mode) {
328 case NONE:
329 return false;
330 case MISSING_KEY:
331 return osm.get(key) == null;
332 case ANY:
333 return true;
334 case ANY_VALUE:
335 return osm.get(key) != null;
336 case ANY_KEY:
337 for (String v:osm.getKeys().values()) {
338 if (v.equals(value))
339 return true;
340 }
341 return false;
342 case EXACT:
343 return value.equals(osm.get(key));
344 case ANY_KEY_REGEXP:
345 for (String v:osm.getKeys().values()) {
346 if (valuePattern.matcher(v).matches())
347 return true;
348 }
349 return false;
350 case ANY_VALUE_REGEXP:
351 case EXACT_REGEXP:
352 for (String key: osm.keySet()) {
353 if (keyPattern.matcher(key).matches()) {
354 if (mode == Mode.ANY_VALUE_REGEXP
355 || valuePattern.matcher(osm.get(key)).matches())
356 return true;
357 }
358 }
359 return false;
360 case MISSING_KEY_REGEXP:
361 for (String k:osm.keySet()) {
362 if (keyPattern.matcher(k).matches())
363 return false;
364 }
365 return true;
366 }
367 throw new AssertionError("Missed state");
368 }
369
370 @Override
371 public String toString() {
372 return key + '=' + value;
373 }
374
375 }
376
377 private static class Any extends Match {
378 private final String search;
379 private final Pattern searchRegex;
380 private final boolean caseSensitive;
381
382 public Any(String s, boolean regexSearch, boolean caseSensitive) throws ParseError {
383 s = Normalizer.normalize(s, Normalizer.Form.NFC);
384 this.caseSensitive = caseSensitive;
385 if (regexSearch) {
386 try {
387 this.searchRegex = Pattern.compile(s, regexFlags(caseSensitive));
388 } catch (PatternSyntaxException e) {
389 throw new ParseError(tr(rxErrorMsg, e.getPattern(), e.getIndex(), e.getMessage()));
390 } catch (Exception e) {
391 throw new ParseError(tr(rxErrorMsgNoPos, s, e.getMessage()));
392 }
393 this.search = s;
394 } else if (caseSensitive) {
395 this.search = s;
396 this.searchRegex = null;
397 } else {
398 this.search = s.toLowerCase();
399 this.searchRegex = null;
400 }
401 }
402
403 @Override public boolean match(OsmPrimitive osm) {
404 if (!osm.hasKeys() && osm.getUser() == null)
405 return search.equals("");
406
407 for (String key: osm.keySet()) {
408 String value = osm.get(key);
409 if (searchRegex != null) {
410
411 value = Normalizer.normalize(value, Normalizer.Form.NFC);
412
413 Matcher keyMatcher = searchRegex.matcher(key);
414 Matcher valMatcher = searchRegex.matcher(value);
415
416 boolean keyMatchFound = keyMatcher.find();
417 boolean valMatchFound = valMatcher.find();
418
419 if (keyMatchFound || valMatchFound)
420 return true;
421 } else {
422 if (!caseSensitive) {
423 key = key.toLowerCase();
424 value = value.toLowerCase();
425 }
426
427 value = Normalizer.normalize(value, Normalizer.Form.NFC);
428
429 if (key.indexOf(search) != -1 || value.indexOf(search) != -1)
430 return true;
431 }
432 }
433 return false;
434 }
435 @Override public String toString() {
436 return search;
437 }
438 }
439
440 private static class ExactType extends Match {
441 private final Class<?> type;
442 public ExactType(String type) throws ParseError {
443 if ("node".equals(type)) {
444 this.type = Node.class;
445 } else if ("way".equals(type)) {
446 this.type = Way.class;
447 } else if ("relation".equals(type)) {
448 this.type = Relation.class;
449 } else
450 throw new ParseError(tr("Unknown primitive type: {0}. Allowed values are node, way or relation",
451 type));
452 }
453 @Override public boolean match(OsmPrimitive osm) {
454 return osm.getClass() == type;
455 }
456 @Override public String toString() {return "type="+type;}
457 }
458
459 private static class UserMatch extends Match {
460 private String user;
461 public UserMatch(String user) {
462 if (user.equals("anonymous")) {
463 this.user = null;
464 } else {
465 this.user = user;
466 }
467 }
468
469 @Override public boolean match(OsmPrimitive osm) {
470 if (osm.getUser() == null)
471 return user == null;
472 else
473 return osm.getUser().hasName(user);
474 }
475
476 @Override public String toString() {
477 return "user=" + user == null ? "" : user;
478 }
479 }
480
481 private static class RoleMatch extends Match {
482 private String role;
483 public RoleMatch(String role) {
484 if (role == null) {
485 this.role = "";
486 } else {
487 this.role = role;
488 }
489 }
490
491 @Override public boolean match(OsmPrimitive osm) {
492 for (OsmPrimitive ref: osm.getReferrers()) {
493 if (ref instanceof Relation && !ref.isIncomplete() && !ref.isDeleted()) {
494 for (RelationMember m : ((Relation) ref).getMembers()) {
495 if (m.getMember() == osm) {
496 String testRole = m.getRole();
497 if(role.equals(testRole == null ? "" : testRole))
498 return true;
499 }
500 }
501 }
502 }
503 return false;
504 }
505
506 @Override public String toString() {
507 return "role=" + role;
508 }
509 }
510
511 private abstract static class CountRange extends Match {
512
513 private int minCount;
514 private int maxCount;
515
516 public CountRange(int minCount, int maxCount) {
517 this.minCount = Math.min(minCount, maxCount);
518 this.maxCount = Math.max(minCount, maxCount);
519 }
520
521 protected abstract Integer getCount(OsmPrimitive osm);
522
523 protected abstract String getCountString();
524
525 @Override
526 public boolean match(OsmPrimitive osm) {
527 Integer count = getCount(osm);
528 if (count == null) {
529 return false;
530 } else {
531 return (count >= minCount) && (count <= maxCount);
532 }
533 }
534
535 @Override
536 public String toString() {
537 return getCountString() + "=" + minCount + "-" + maxCount;
538 }
539 }
540
541
542
543 private static class NodeCountRange extends CountRange {
544
545 public NodeCountRange(int minCount, int maxCount) {
546 super(minCount, maxCount);
547 }
548
549 @Override
550 protected Integer getCount(OsmPrimitive osm) {
551 if (!(osm instanceof Way)) {
552 return null;
553 } else {
554 return ((Way) osm).getNodesCount();
555 }
556 }
557
558 @Override
559 protected String getCountString() {
560 return "nodes";
561 }
562 }
563
564 private static class TagCountRange extends CountRange {
565
566 public TagCountRange(int minCount, int maxCount) {
567 super(minCount, maxCount);
568 }
569
570 @Override
571 protected Integer getCount(OsmPrimitive osm) {
572 return osm.getKeys().size();
573 }
574
575 @Override
576 protected String getCountString() {
577 return "tags";
578 }
579 }
580
581 private static class New extends Match {
582 @Override public boolean match(OsmPrimitive osm) {
583 return osm.isNew();
584 }
585 @Override public String toString() {
586 return "new";
587 }
588 }
589
590 private static class Modified extends Match {
591 @Override public boolean match(OsmPrimitive osm) {
592 return osm.isModified() || osm.isNewOrUndeleted();
593 }
594 @Override public String toString() {return "modified";}
595 }
596
597 private static class Selected extends Match {
598 @Override public boolean match(OsmPrimitive osm) {
599 return Main.main.getCurrentDataSet().isSelected(osm);
600 }
601 @Override public String toString() {return "selected";}
602 }
603
604 private static class Incomplete extends Match {
605 @Override public boolean match(OsmPrimitive osm) {
606 return osm.isIncomplete();
607 }
608 @Override public String toString() {return "incomplete";}
609 }
610
611 private static class Untagged extends Match {
612 @Override public boolean match(OsmPrimitive osm) {
613 return !osm.isTagged() && !osm.isIncomplete();
614 }
615 @Override public String toString() {return "untagged";}
616 }
617
618 private static class Closed extends Match {
619 @Override public boolean match(OsmPrimitive osm) {
620 return osm instanceof Way && ((Way) osm).isClosed();
621 }
622 @Override public String toString() {return "closed";}
623 }
624
625 private static class Parent extends Match {
626 private Match child;
627 public Parent(Match m) { child = m; }
628 @Override public boolean match(OsmPrimitive osm) {
629 boolean isParent = false;
630
631 // "parent" (null) should mean the same as "parent()"
632 // (Always). I.e. match everything
633 if (child == null) {
634 child = new Always();
635 }
636
637 if (osm instanceof Way) {
638 for (Node n : ((Way)osm).getNodes()) {
639 isParent |= child.match(n);
640 }
641 } else if (osm instanceof Relation) {
642 for (RelationMember member : ((Relation)osm).getMembers()) {
643 isParent |= child.match(member.getMember());
644 }
645 }
646 return isParent;
647 }
648 @Override public String toString() {return "parent(" + child + ")";}
649 }
650
651 private static class Child extends Match {
652 private final Match parent;
653
654 public Child(Match m) {
655 // "child" (null) should mean the same as "child()"
656 // (Always). I.e. match everything
657 if (m == null) {
658 parent = new Always();
659 } else {
660 parent = m;
661 }
662 }
663
664 @Override public boolean match(OsmPrimitive osm) {
665 boolean isChild = false;
666 for (OsmPrimitive p : osm.getReferrers()) {
667 isChild |= parent.match(p);
668 }
669 return isChild;
670 }
671 @Override public String toString() {return "child(" + parent + ")";}
672 }
673
674 /**
675 * Matches on the area of a closed way.
676 *
677 * @author Ole Jørgen Brønner
678 */
679 private static class Area extends CountRange {
680
681 public Area(int minCount, int maxCount) {
682 super(minCount, maxCount);
683 }
684
685 @Override
686 protected Integer getCount(OsmPrimitive osm) {
687 if (!(osm instanceof Way && ((Way) osm).isClosed())) {
688 return null;
689 }
690 Way way = (Way) osm;
691 return (int) Geometry.closedWayArea(way);
692 }
693
694 @Override
695 protected String getCountString() {
696 return "area";
697 }
698 }
699
700 public static class ParseError extends Exception {
701 public ParseError(String msg) {
702 super(msg);
703 }
704 public ParseError(Token expected, Token found) {
705 this(tr("Unexpected token. Expected {0}, found {1}", expected, found));
706 }
707 }
708
709 public static Match compile(String searchStr, boolean caseSensitive, boolean regexSearch)
710 throws ParseError {
711 return new SearchCompiler(caseSensitive, regexSearch,
712 new PushbackTokenizer(
713 new PushbackReader(new StringReader(searchStr))))
714 .parse();
715 }
716
717 public Match parse() throws ParseError {
718 Match m = parseExpression();
719 if (!tokenizer.readIfEqual(Token.EOF))
720 throw new ParseError(tr("Unexpected token: {0}", tokenizer.nextToken()));
721 if (m == null)
722 return new Always();
723 return m;
724 }
725
726 private Match parseExpression() throws ParseError {
727 Match factor = parseFactor();
728 if (factor == null)
729 return null;
730 if (tokenizer.readIfEqual(Token.OR))
731 return new Or(factor, parseExpression(tr("Missing parameter for OR")));
732 else {
733 Match expression = parseExpression();
734 if (expression == null)
735 return factor;
736 else
737 return new And(factor, expression);
738 }
739 }
740
741 private Match parseExpression(String errorMessage) throws ParseError {
742 Match expression = parseExpression();
743 if (expression == null)
744 throw new ParseError(errorMessage);
745 else
746 return expression;
747 }
748
749 private Match parseFactor() throws ParseError {
750 if (tokenizer.readIfEqual(Token.LEFT_PARENT)) {
751 Match expression = parseExpression();
752 if (!tokenizer.readIfEqual(Token.RIGHT_PARENT))
753 throw new ParseError(Token.RIGHT_PARENT, tokenizer.nextToken());
754 return expression;
755 } else if (tokenizer.readIfEqual(Token.NOT))
756 return new Not(parseFactor(tr("Missing operator for NOT")));
757 else if (tokenizer.readIfEqual(Token.KEY)) {
758 String key = tokenizer.getText();
759 if (tokenizer.readIfEqual(Token.EQUALS))
760 return new ExactKeyValue(regexSearch, key, tokenizer.readTextOrNumber());
761 else if (tokenizer.readIfEqual(Token.COLON)) {
762 if ("id".equals(key))
763 return new Id(tokenizer.readNumber(tr("Primitive id expected")));
764 else if ("tags".equals(key)) {
765 Range range = tokenizer.readRange(tr("Range of numbers expected"));
766 return new TagCountRange((int)range.getStart(), (int)range.getEnd());
767 } else if ("nodes".equals(key)) {
768 Range range = tokenizer.readRange(tr("Range of numbers expected"));
769 return new NodeCountRange((int)range.getStart(), (int)range.getEnd());
770 } else if ("areasize".equals(key)) {
771 Range range = tokenizer.readRange(tr("Range of numbers expected"));
772 return new Area((int)range.getStart(), (int)range.getEnd());
773 } else if ("changeset".equals(key))
774 return new ChangesetId(tokenizer.readNumber(tr("Changeset id expected")));
775 else if ("version".equals(key))
776 return new Version(tokenizer.readNumber(tr("Version expected")));
777 else
778 return parseKV(key, tokenizer.readTextOrNumber());
779 } else if (tokenizer.readIfEqual(Token.QUESTION_MARK))
780 return new BooleanMatch(key, false);
781 else if ("new".equals(key))
782 return new New();
783 else if ("modified".equals(key))
784 return new Modified();
785 else if ("incomplete".equals(key))
786 return new Incomplete();
787 else if ("untagged".equals(key))
788 return new Untagged();
789 else if ("selected".equals(key))
790 return new Selected();
791 else if ("closed".equals(key))
792 return new Closed();
793 else if ("child".equals(key))
794 return new Child(parseFactor());
795 else if ("parent".equals(key))
796 return new Parent(parseFactor());
797 else
798 return new Any(key, regexSearch, caseSensitive);
799 } else
800 return null;
801 }
802
803 private Match parseFactor(String errorMessage) throws ParseError {
804 Match fact = parseFactor();
805 if (fact == null)
806 throw new ParseError(errorMessage);
807 else
808 return fact;
809 }
810
811 private Match parseKV(String key, String value) throws ParseError {
812 if (value == null) {
813 value = "";
814 }
815 if (key.equals("type"))
816 return new ExactType(value);
817 else if (key.equals("user"))
818 return new UserMatch(value);
819 else if (key.equals("role"))
820 return new RoleMatch(value);
821 else
822 return new KeyValue(key, value, regexSearch, caseSensitive);
823 }
824
825 private static int regexFlags(boolean caseSensitive) {
826 int searchFlags = 0;
827
828 // Enables canonical Unicode equivalence so that e.g. the two
829 // forms of "\u00e9gal" and "e\u0301gal" will match.
830 //
831 // It makes sense to match no matter how the character
832 // happened to be constructed.
833 searchFlags |= Pattern.CANON_EQ;
834
835 // Make "." match any character including newline (/s in Perl)
836 searchFlags |= Pattern.DOTALL;
837
838 // CASE_INSENSITIVE by itself only matches US-ASCII case
839 // insensitively, but the OSM data is in Unicode. With
840 // UNICODE_CASE casefolding is made Unicode-aware.
841 if (!caseSensitive) {
842 searchFlags |= (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
843 }
844
845 return searchFlags;
846 }
847}
Note: See TracBrowser for help on using the repository browser.