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

Last change on this file since 4085 was 4085, checked in by bastiK, 13 years ago

applied #6308 (patch by Ole Jørgen Brønner) - Filter/search by closed-way area size

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