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

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

fix #3942 - patch by simon04 - improve range handling for search

  • Property svn:eol-style set to native
File size: 28.4 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 Modified extends Match {
582 @Override public boolean match(OsmPrimitive osm) {
583 return osm.isModified() || osm.isNewOrUndeleted();
584 }
585 @Override public String toString() {return "modified";}
586 }
587
588 private static class Selected extends Match {
589 @Override public boolean match(OsmPrimitive osm) {
590 return Main.main.getCurrentDataSet().isSelected(osm);
591 }
592 @Override public String toString() {return "selected";}
593 }
594
595 private static class Incomplete extends Match {
596 @Override public boolean match(OsmPrimitive osm) {
597 return osm.isIncomplete();
598 }
599 @Override public String toString() {return "incomplete";}
600 }
601
602 private static class Untagged extends Match {
603 @Override public boolean match(OsmPrimitive osm) {
604 return !osm.isTagged() && !osm.isIncomplete();
605 }
606 @Override public String toString() {return "untagged";}
607 }
608
609 private static class Closed extends Match {
610 @Override public boolean match(OsmPrimitive osm) {
611 return osm instanceof Way && ((Way) osm).isClosed();
612 }
613 @Override public String toString() {return "closed";}
614 }
615
616 private static class Parent extends Match {
617 private Match child;
618 public Parent(Match m) { child = m; }
619 @Override public boolean match(OsmPrimitive osm) {
620 boolean isParent = false;
621
622 // "parent" (null) should mean the same as "parent()"
623 // (Always). I.e. match everything
624 if (child == null) {
625 child = new Always();
626 }
627
628 if (osm instanceof Way) {
629 for (Node n : ((Way)osm).getNodes()) {
630 isParent |= child.match(n);
631 }
632 } else if (osm instanceof Relation) {
633 for (RelationMember member : ((Relation)osm).getMembers()) {
634 isParent |= child.match(member.getMember());
635 }
636 }
637 return isParent;
638 }
639 @Override public String toString() {return "parent(" + child + ")";}
640 }
641
642 private static class Child extends Match {
643 private final Match parent;
644
645 public Child(Match m) {
646 // "child" (null) should mean the same as "child()"
647 // (Always). I.e. match everything
648 if (m == null) {
649 parent = new Always();
650 } else {
651 parent = m;
652 }
653 }
654
655 @Override public boolean match(OsmPrimitive osm) {
656 boolean isChild = false;
657 for (OsmPrimitive p : osm.getReferrers()) {
658 isChild |= parent.match(p);
659 }
660 return isChild;
661 }
662 @Override public String toString() {return "child(" + parent + ")";}
663 }
664
665 /**
666 * Matches on the area of a closed way.
667 *
668 * @author Ole Jørgen Brønner
669 */
670 private static class Area extends CountRange {
671
672 public Area(int minCount, int maxCount) {
673 super(minCount, maxCount);
674 }
675
676 @Override
677 protected Integer getCount(OsmPrimitive osm) {
678 if (!(osm instanceof Way && ((Way) osm).isClosed())) {
679 return null;
680 }
681 Way way = (Way) osm;
682 return (int) Geometry.closedWayArea(way);
683 }
684
685 @Override
686 protected String getCountString() {
687 return "area";
688 }
689 }
690
691 public static class ParseError extends Exception {
692 public ParseError(String msg) {
693 super(msg);
694 }
695 public ParseError(Token expected, Token found) {
696 this(tr("Unexpected token. Expected {0}, found {1}", expected, found));
697 }
698 }
699
700 public static Match compile(String searchStr, boolean caseSensitive, boolean regexSearch)
701 throws ParseError {
702 return new SearchCompiler(caseSensitive, regexSearch,
703 new PushbackTokenizer(
704 new PushbackReader(new StringReader(searchStr))))
705 .parse();
706 }
707
708 public Match parse() throws ParseError {
709 Match m = parseExpression();
710 if (!tokenizer.readIfEqual(Token.EOF))
711 throw new ParseError(tr("Unexpected token: {0}", tokenizer.nextToken()));
712 if (m == null)
713 return new Always();
714 return m;
715 }
716
717 private Match parseExpression() throws ParseError {
718 Match factor = parseFactor();
719 if (factor == null)
720 return null;
721 if (tokenizer.readIfEqual(Token.OR))
722 return new Or(factor, parseExpression(tr("Missing parameter for OR")));
723 else {
724 Match expression = parseExpression();
725 if (expression == null)
726 return factor;
727 else
728 return new And(factor, expression);
729 }
730 }
731
732 private Match parseExpression(String errorMessage) throws ParseError {
733 Match expression = parseExpression();
734 if (expression == null)
735 throw new ParseError(errorMessage);
736 else
737 return expression;
738 }
739
740 private Match parseFactor() throws ParseError {
741 if (tokenizer.readIfEqual(Token.LEFT_PARENT)) {
742 Match expression = parseExpression();
743 if (!tokenizer.readIfEqual(Token.RIGHT_PARENT))
744 throw new ParseError(Token.RIGHT_PARENT, tokenizer.nextToken());
745 return expression;
746 } else if (tokenizer.readIfEqual(Token.NOT))
747 return new Not(parseFactor(tr("Missing operator for NOT")));
748 else if (tokenizer.readIfEqual(Token.KEY)) {
749 String key = tokenizer.getText();
750 if (tokenizer.readIfEqual(Token.EQUALS))
751 return new ExactKeyValue(regexSearch, key, tokenizer.readTextOrNumber());
752 else if (tokenizer.readIfEqual(Token.COLON)) {
753 if ("id".equals(key))
754 return new Id(tokenizer.readNumber(tr("Primitive id expected")));
755 else if ("tags".equals(key)) {
756 Range range = tokenizer.readRange(tr("Range of numbers expected"));
757 return new TagCountRange((int)range.getStart(), (int)range.getEnd());
758 } else if ("nodes".equals(key)) {
759 Range range = tokenizer.readRange(tr("Range of numbers expected"));
760 return new NodeCountRange((int)range.getStart(), (int)range.getEnd());
761 } else if ("areasize".equals(key)) {
762 Range range = tokenizer.readRange(tr("Range of numbers expected"));
763 return new Area((int)range.getStart(), (int)range.getEnd());
764 } else if ("changeset".equals(key))
765 return new ChangesetId(tokenizer.readNumber(tr("Changeset id expected")));
766 else if ("version".equals(key))
767 return new Version(tokenizer.readNumber(tr("Version expected")));
768 else
769 return parseKV(key, tokenizer.readTextOrNumber());
770 } else if (tokenizer.readIfEqual(Token.QUESTION_MARK))
771 return new BooleanMatch(key, false);
772 else if ("modified".equals(key))
773 return new Modified();
774 else if ("incomplete".equals(key))
775 return new Incomplete();
776 else if ("untagged".equals(key))
777 return new Untagged();
778 else if ("selected".equals(key))
779 return new Selected();
780 else if ("closed".equals(key))
781 return new Closed();
782 else if ("child".equals(key))
783 return new Child(parseFactor());
784 else if ("parent".equals(key))
785 return new Parent(parseFactor());
786 else
787 return new Any(key, regexSearch, caseSensitive);
788 } else
789 return null;
790 }
791
792 private Match parseFactor(String errorMessage) throws ParseError {
793 Match fact = parseFactor();
794 if (fact == null)
795 throw new ParseError(errorMessage);
796 else
797 return fact;
798 }
799
800 private Match parseKV(String key, String value) throws ParseError {
801 if (value == null) {
802 value = "";
803 }
804 if (key.equals("type"))
805 return new ExactType(value);
806 else if (key.equals("user"))
807 return new UserMatch(value);
808 else if (key.equals("role"))
809 return new RoleMatch(value);
810 else
811 return new KeyValue(key, value, regexSearch, caseSensitive);
812 }
813
814 private static int regexFlags(boolean caseSensitive) {
815 int searchFlags = 0;
816
817 // Enables canonical Unicode equivalence so that e.g. the two
818 // forms of "\u00e9gal" and "e\u0301gal" will match.
819 //
820 // It makes sense to match no matter how the character
821 // happened to be constructed.
822 searchFlags |= Pattern.CANON_EQ;
823
824 // Make "." match any character including newline (/s in Perl)
825 searchFlags |= Pattern.DOTALL;
826
827 // CASE_INSENSITIVE by itself only matches US-ASCII case
828 // insensitively, but the OSM data is in Unicode. With
829 // UNICODE_CASE casefolding is made Unicode-aware.
830 if (!caseSensitive) {
831 searchFlags |= (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
832 }
833
834 return searchFlags;
835 }
836}
Note: See TracBrowser for help on using the repository browser.