Changeset 4817 in josm for trunk/src/org
- Timestamp:
- 2012-01-18T20:19:02+01:00 (13 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/search/PushbackTokenizer.java
r4346 r4817 48 48 49 49 public enum Token { 50 NOT(marktr("<not>")), OR(marktr("<or>")), LEFT_PARENT(marktr("<left parent>")), 50 NOT(marktr("<not>")), OR(marktr("<or>")), XOR(marktr("<xor>")), LEFT_PARENT(marktr("<left parent>")), 51 51 RIGHT_PARENT(marktr("<right parent>")), COLON(marktr("<colon>")), EQUALS(marktr("<equals>")), 52 52 KEY(marktr("<key>")), QUESTION_MARK(marktr("<question mark>")), … … 74 74 } 75 75 76 private static final List<Character> specialChars = Arrays.asList(new Character[] {'"', ':', '(', ')', '|', '=', '?'}); 76 private static final List<Character> specialChars = Arrays.asList(new Character[] {'"', ':', '(', ')', '|', '^', '=', '?'}); 77 77 private static final List<Character> specialCharsQuoted = Arrays.asList(new Character[] {'"'}); 78 78 … … 102 102 * : for an key. The value is the next token 103 103 * | for "OR" 104 * ^ for "XOR" 104 105 * ' ' for anything else. 105 106 * @return The next token in the stream. … … 134 135 getChar(); 135 136 return Token.OR; 137 case '^': 138 getChar(); 139 return Token.XOR; 136 140 case '&': 137 141 getChar(); … … 156 160 if ("or".equalsIgnoreCase(currentText)) 157 161 return Token.OR; 158 if ("and".equalsIgnoreCase(currentText)) 162 else if ("xor".equalsIgnoreCase(currentText)) 163 return Token.XOR; 164 else if ("and".equalsIgnoreCase(currentText)) 159 165 return nextToken(); 160 166 // try parsing number -
trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java
r4696 r4817 8 8 import java.io.StringReader; 9 9 import java.text.Normalizer; 10 import java.util.Arrays; 10 11 import java.util.Collection; 11 12 import java.util.Date; 13 import java.util.HashMap; 14 import java.util.Map; 12 15 import java.util.regex.Matcher; 13 16 import java.util.regex.Pattern; … … 55 58 private static String rxErrorMsgNoPos = marktr("The regex \"{0}\" had a parse error, full error:\n\n{1}"); 56 59 private PushbackTokenizer tokenizer; 60 private static Map<String, SimpleMatchFactory> simpleMatchFactoryMap = new HashMap<String, SimpleMatchFactory>(); 61 private static Map<String, UnaryMatchFactory> unaryMatchFactoryMap = new HashMap<String, UnaryMatchFactory>(); 62 private static Map<String, BinaryMatchFactory> binaryMatchFactoryMap = new HashMap<String, BinaryMatchFactory>(); 57 63 58 64 public SearchCompiler(boolean caseSensitive, boolean regexSearch, PushbackTokenizer tokenizer) { … … 60 66 this.regexSearch = regexSearch; 61 67 this.tokenizer = tokenizer; 62 } 63 68 69 /* register core match factories at first instance, so plugins should 70 * never be able to generate a NPE 71 */ 72 if (simpleMatchFactoryMap.isEmpty()) { 73 addMatchFactory(new CoreSimpleMatchFactory()); 74 } 75 if (unaryMatchFactoryMap.isEmpty()) { 76 addMatchFactory(new CoreUnaryMatchFactory()); 77 } 78 79 } 80 81 /** 82 * Add (register) MatchFactory with SearchCompiler 83 * @param factory 84 */ 85 public static void addMatchFactory(MatchFactory factory) { 86 for (String keyword : factory.getKeywords()) { 87 // TODO: check for keyword collisions 88 if (factory instanceof SimpleMatchFactory) { 89 simpleMatchFactoryMap.put(keyword, (SimpleMatchFactory)factory); 90 } else if (factory instanceof UnaryMatchFactory) { 91 unaryMatchFactoryMap.put(keyword, (UnaryMatchFactory)factory); 92 } else if (factory instanceof BinaryMatchFactory) { 93 binaryMatchFactoryMap.put(keyword, (BinaryMatchFactory)factory); 94 } else 95 throw new AssertionError("Unknown match factory"); 96 } 97 } 98 99 public class CoreSimpleMatchFactory implements SimpleMatchFactory { 100 private Collection<String> keywords = Arrays.asList("id", "version", 101 "changeset", "nodes", "tags", "areasize", "modified", "selected", 102 "incomplete", "untagged", "closed", "new", "indownloadarea", 103 "allindownloadarea", "inview", "allinview", "timestamp"); 104 105 @Override 106 public Match get(String keyword, PushbackTokenizer tokenizer) throws ParseError { 107 if ("id".equals(keyword)) 108 return new Id(tokenizer); 109 else if ("version".equals(keyword)) 110 return new Version(tokenizer); 111 else if ("changeset".equals(keyword)) 112 return new ChangesetId(tokenizer); 113 else if ("nodes".equals(keyword)) 114 return new NodeCountRange(tokenizer); 115 else if ("tags".equals(keyword)) 116 return new TagCountRange(tokenizer); 117 else if ("areasize".equals(keyword)) 118 return new AreaSize(tokenizer); 119 else if ("modified".equals(keyword)) 120 return new Modified(); 121 else if ("selected".equals(keyword)) 122 return new Selected(); 123 else if ("incomplete".equals(keyword)) 124 return new Incomplete(); 125 else if ("untagged".equals(keyword)) 126 return new Untagged(); 127 else if ("closed".equals(keyword)) 128 return new Closed(); 129 else if ("new".equals(keyword)) 130 return new New(); 131 else if ("indownloadedarea".equals(keyword)) 132 return new InDataSourceArea(false); 133 else if ("allindownloadedarea".equals(keyword)) 134 return new InDataSourceArea(true); 135 else if ("inview".equals(keyword)) 136 return new InView(false); 137 else if ("allinview".equals(keyword)) 138 return new InView(true); 139 else if ("timestamp".equals(keyword)) { 140 String rangeS = " " + tokenizer.readTextOrNumber() + " "; // add leading/trailing space in order to get expected split (e.g. "a--" => {"a", ""}) 141 String[] rangeA = rangeS.split("/"); 142 if (rangeA.length == 1) 143 return new KeyValue(keyword, rangeS, regexSearch, caseSensitive); 144 else if (rangeA.length == 2) { 145 String rangeA1 = rangeA[0].trim(); 146 String rangeA2 = rangeA[1].trim(); 147 long minDate = DateUtils.fromString(rangeA1.isEmpty() ? "1980" : rangeA1).getTime(); // if min timestap is empty: use lowest possible date 148 long maxDate = rangeA2.isEmpty() ? new Date().getTime() : DateUtils.fromString(rangeA2).getTime(); // if max timestamp is empty: use "now" 149 return new TimestampRange(minDate, maxDate); 150 } else 151 /* 152 * I18n: Don't translate timestamp keyword 153 */ throw new ParseError(tr("Expecting <i>min</i>/<i>max</i> after ''timestamp''")); 154 } else 155 return null; 156 } 157 158 @Override 159 public Collection<String> getKeywords() { 160 return keywords; 161 } 162 } 163 164 public static class CoreUnaryMatchFactory implements UnaryMatchFactory { 165 private static Collection<String> keywords = Arrays.asList("parent", "child"); 166 167 @Override 168 public UnaryMatch get(String keyword, Match matchOperand, PushbackTokenizer tokenizer) { 169 if ("parent".equals(keyword)) 170 return new Parent(matchOperand); 171 else if ("child".equals(keyword)) 172 return new Child(matchOperand); 173 return null; 174 } 175 176 @Override 177 public Collection<String> getKeywords() { 178 return keywords; 179 } 180 } 181 182 /** 183 * Classes implementing this interface can provide Match operators. 184 */ 185 private interface MatchFactory { 186 public Collection<String> getKeywords(); 187 } 188 189 public interface SimpleMatchFactory extends MatchFactory { 190 public Match get(String keyword, PushbackTokenizer tokenizer) throws ParseError; 191 } 192 193 public interface UnaryMatchFactory extends MatchFactory { 194 public UnaryMatch get(String keyword, Match matchOperand, PushbackTokenizer tokenizer) throws ParseError; 195 } 196 197 public interface BinaryMatchFactory extends MatchFactory { 198 public BinaryMatch get(String keyword, Match lhs, Match rhs, PushbackTokenizer tokenizer) throws ParseError; 199 } 200 201 /** 202 * Base class for all search operators. 203 */ 64 204 abstract public static class Match { 205 65 206 abstract public boolean match(OsmPrimitive osm); 66 207 … … 88 229 } 89 230 231 /** 232 * A unary search operator which may take data parameters. 233 */ 234 abstract public static class UnaryMatch extends Match { 235 236 protected final Match match; 237 238 public UnaryMatch(Match match) { 239 if (match == null) { 240 // "operator" (null) should mean the same as "operator()" 241 // (Always). I.e. match everything 242 this.match = new Always(); 243 } else { 244 this.match = match; 245 } 246 } 247 248 public Match getOperand() { 249 return match; 250 } 251 } 252 253 /** 254 * A binary search operator which may take data parameters. 255 */ 256 abstract public static class BinaryMatch extends Match { 257 258 protected final Match lhs; 259 protected final Match rhs; 260 261 public BinaryMatch(Match lhs, Match rhs) { 262 this.lhs = lhs; 263 this.rhs = rhs; 264 } 265 266 public Match getLhs() { 267 return lhs; 268 } 269 270 public Match getRhs() { 271 return rhs; 272 } 273 } 274 275 /** 276 * Matches every OsmPrimitive. 277 */ 90 278 public static class Always extends Match { 91 279 public static Always INSTANCE = new Always(); … … 95 283 } 96 284 285 /** 286 * Never matches any OsmPrimitive. 287 */ 97 288 public static class Never extends Match { 98 289 @Override … … 102 293 } 103 294 104 public static class Not extends Match { 105 private final Match match; 106 public Not(Match match) {this.match = match;} 295 /** 296 * Inverts the match. 297 */ 298 public static class Not extends UnaryMatch { 299 public Not(Match match) {super(match);} 107 300 @Override public boolean match(OsmPrimitive osm) { 108 301 return !match.match(osm); … … 114 307 } 115 308 309 /** 310 * Matches if the value of the corresponding key is ''yes'', ''true'', ''1'' or ''on''. 311 */ 116 312 private static class BooleanMatch extends Match { 117 313 private final String key; … … 132 328 } 133 329 134 public static class And extends Match { 135 private final Match lhs; 136 private final Match rhs; 137 public And(Match lhs, Match rhs) {this.lhs = lhs; this.rhs = rhs;} 330 /** 331 * Matches if both left and right expressions match. 332 */ 333 public static class And extends BinaryMatch { 334 public And(Match lhs, Match rhs) {super(lhs, rhs);} 138 335 @Override public boolean match(OsmPrimitive osm) { 139 336 return lhs.match(osm) && rhs.match(osm); 140 337 } 141 @Override public String toString() {return lhs+" && "+rhs;} 142 public Match getLhs() { 143 return lhs; 144 } 145 public Match getRhs() { 146 return rhs; 147 } 148 } 149 150 public static class Or extends Match { 151 private final Match lhs; 152 private final Match rhs; 153 public Or(Match lhs, Match rhs) {this.lhs = lhs; this.rhs = rhs;} 338 @Override public String toString() { 339 return lhs + " && " + rhs; 340 } 341 } 342 343 /** 344 * Matches if the left OR the right expression match. 345 */ 346 public static class Or extends BinaryMatch { 347 public Or(Match lhs, Match rhs) {super(lhs, rhs);} 154 348 @Override public boolean match(OsmPrimitive osm) { 155 349 return lhs.match(osm) || rhs.match(osm); 156 350 } 157 @Override public String toString() {return lhs+" || "+rhs;} 158 public Match getLhs() { 159 return lhs; 160 } 161 public Match getRhs() { 162 return rhs; 163 } 164 } 165 351 @Override public String toString() { 352 return lhs + " || " + rhs; 353 } 354 } 355 356 /** 357 * Matches if the left OR the right expression match, but not both. 358 */ 359 public static class Xor extends BinaryMatch { 360 public Xor(Match lhs, Match rhs) {super(lhs, rhs);} 361 @Override public boolean match(OsmPrimitive osm) { 362 return lhs.match(osm) ^ rhs.match(osm); 363 } 364 @Override public String toString() { 365 return lhs + " ^ " + rhs; 366 } 367 } 368 369 /** 370 * Matches objects with the given object ID. 371 */ 166 372 private static class Id extends Match { 167 373 private long id; … … 169 375 this.id = id; 170 376 } 377 public Id(PushbackTokenizer tokenizer) throws ParseError { 378 this(tokenizer.readNumber(tr("Primitive id expected"))); 379 } 171 380 @Override public boolean match(OsmPrimitive osm) { 172 381 return id == 0?osm.isNew():osm.getUniqueId() == id; … … 175 384 } 176 385 386 /** 387 * Matches objects with the given changeset ID. 388 */ 177 389 private static class ChangesetId extends Match { 178 390 private long changesetid; 179 391 public ChangesetId(long changesetid) {this.changesetid = changesetid;} 392 public ChangesetId(PushbackTokenizer tokenizer) throws ParseError { 393 this(tokenizer.readNumber(tr("Changeset id expected"))); 394 } 180 395 @Override public boolean match(OsmPrimitive osm) { 181 396 return osm.getChangesetId() == changesetid; … … 184 399 } 185 400 401 /** 402 * Matches objects with the given version number. 403 */ 186 404 private static class Version extends Match { 187 405 private long version; 188 406 public Version(long version) {this.version = version;} 407 public Version(PushbackTokenizer tokenizer) throws ParseError { 408 this(tokenizer.readNumber(tr("Version expected"))); 409 } 189 410 @Override public boolean match(OsmPrimitive osm) { 190 411 return osm.getVersion() == version; … … 193 414 } 194 415 416 /** 417 * Matches objects with the given key-value pair. 418 */ 195 419 private static class KeyValue extends Match { 196 420 private final String key; … … 287 511 } 288 512 513 /** 514 * Matches objects with the exact given key-value pair. 515 */ 289 516 public static class ExactKeyValue extends Match { 290 517 … … 415 642 } 416 643 644 /** 645 * Match a string in any tags (key or value), with optional regex and case insensitivity. 646 */ 417 647 private static class Any extends Match { 418 648 private final String search; … … 478 708 } 479 709 710 // TODO: change how we handle this 480 711 private static class ExactType extends Match { 481 712 private final Class<?> type; … … 497 728 } 498 729 730 /** 731 * Matches objects last changed by the given username. 732 */ 499 733 private static class UserMatch extends Match { 500 734 private String user; … … 519 753 } 520 754 755 /** 756 * Matches objects with the given relation role (i.e. "outer"). 757 */ 521 758 private static class RoleMatch extends Match { 522 759 private String role; … … 549 786 } 550 787 788 /** 789 * Matches objects with properties in a certain range. 790 */ 551 791 private abstract static class CountRange extends Match { 552 792 … … 557 797 this.minCount = Math.min(minCount, maxCount); 558 798 this.maxCount = Math.max(minCount, maxCount); 799 } 800 801 public CountRange(Range range) { 802 this(range.getStart(), range.getEnd()); 559 803 } 560 804 … … 579 823 580 824 581 825 /** 826 * Matches ways with a number of nodes in given range 827 */ 582 828 private static class NodeCountRange extends CountRange { 583 584 public NodeCountRange(long minCount, long maxCount) { 585 super(minCount, maxCount); 829 public NodeCountRange(Range range) { 830 super(range); 831 } 832 833 public NodeCountRange(PushbackTokenizer tokenizer) throws ParseError { 834 this(tokenizer.readRange(tr("Range of numbers expected"))); 586 835 } 587 836 … … 600 849 } 601 850 851 /** 852 * Matches objects with a number of tags in given range 853 */ 602 854 private static class TagCountRange extends CountRange { 603 604 public TagCountRange(long minCount, long maxCount) { 605 super(minCount, maxCount); 855 public TagCountRange(Range range) { 856 super(range); 857 } 858 859 public TagCountRange(PushbackTokenizer tokenizer) throws ParseError { 860 this(tokenizer.readRange(tr("Range of numbers expected"))); 606 861 } 607 862 … … 617 872 } 618 873 874 /** 875 * Matches objects with a timestamp in given range 876 */ 619 877 private static class TimestampRange extends CountRange { 620 878 … … 635 893 } 636 894 895 /** 896 * Matches objects that are new (i.e. have not been uploaded to the server) 897 */ 637 898 private static class New extends Match { 638 899 @Override public boolean match(OsmPrimitive osm) { … … 644 905 } 645 906 907 /** 908 * Matches all objects that have been modified, created, or undeleted 909 */ 646 910 private static class Modified extends Match { 647 911 @Override public boolean match(OsmPrimitive osm) { … … 651 915 } 652 916 917 /** 918 * Matches all objects currently selected 919 */ 653 920 private static class Selected extends Match { 654 921 @Override public boolean match(OsmPrimitive osm) { … … 658 925 } 659 926 927 /** 928 * Match objects that are incomplete, where only id and type are known. 929 * Typically some members of a relation are incomplete until they are 930 * fetched from the server. 931 */ 660 932 private static class Incomplete extends Match { 661 933 @Override public boolean match(OsmPrimitive osm) { … … 665 937 } 666 938 939 /** 940 * Matches objects that don't have any interesting tags (i.e. only has source, 941 * FIXME, etc.). The complete list of uninteresting tags can be found here: 942 * org.openstreetmap.josm.data.osm.OsmPrimitive.getUninterestingKeys() 943 */ 667 944 private static class Untagged extends Match { 668 945 @Override public boolean match(OsmPrimitive osm) { … … 672 949 } 673 950 951 /** 952 * Matches ways which are closed (i.e. first and last node are the same) 953 */ 674 954 private static class Closed extends Match { 675 955 @Override public boolean match(OsmPrimitive osm) { … … 679 959 } 680 960 681 public static class Parent extends Match { 682 private final Match child; 961 /** 962 * Matches objects if they are parents of the expression 963 */ 964 public static class Parent extends UnaryMatch { 683 965 public Parent(Match m) { 684 if (m == null) { 685 // "parent" (null) should mean the same as "parent()" 686 // (Always). I.e. match everything 687 child = new Always(); 688 } else { 689 child = m; 690 } 966 super(m); 691 967 } 692 968 @Override public boolean match(OsmPrimitive osm) { … … 695 971 if (osm instanceof Way) { 696 972 for (Node n : ((Way)osm).getNodes()) { 697 isParent |= child.match(n);973 isParent |= match.match(n); 698 974 } 699 975 } else if (osm instanceof Relation) { 700 976 for (RelationMember member : ((Relation)osm).getMembers()) { 701 isParent |= child.match(member.getMember());977 isParent |= match.match(member.getMember()); 702 978 } 703 979 } 704 980 return isParent; 705 981 } 706 @Override public String toString() {return "parent(" + child + ")";} 707 public Match getChild() { 708 return child; 709 } 710 } 711 712 public static class Child extends Match { 713 private final Match parent; 982 @Override public String toString() {return "parent(" + match + ")";} 983 } 984 985 /** 986 * Matches objects if they are children of the expression 987 */ 988 public static class Child extends UnaryMatch { 714 989 715 990 public Child(Match m) { 716 // "child" (null) should mean the same as "child()" 717 // (Always). I.e. match everything 718 if (m == null) { 719 parent = new Always(); 720 } else { 721 parent = m; 722 } 991 super(m); 723 992 } 724 993 … … 726 995 boolean isChild = false; 727 996 for (OsmPrimitive p : osm.getReferrers()) { 728 isChild |= parent.match(p);997 isChild |= match.match(p); 729 998 } 730 999 return isChild; 731 1000 } 732 @Override public String toString() {return "child(" + parent + ")";} 733 734 public Match getParent() { 735 return parent; 736 } 737 } 738 739 /** 740 * Matches on the area of a closed way. 1001 @Override public String toString() {return "child(" + match + ")";} 1002 } 1003 1004 /** 1005 * Matches if the size of the area is within the given range 741 1006 * 742 1007 * @author Ole Jørgen Brønner 743 1008 */ 744 private static class Area extends CountRange { 745 746 public Area(long minCount, long maxCount) { 747 super(minCount, maxCount); 1009 private static class AreaSize extends CountRange { 1010 1011 public AreaSize(Range range) { 1012 super(range); 1013 } 1014 1015 public AreaSize(PushbackTokenizer tokenizer) throws ParseError { 1016 this(tokenizer.readRange(tr("Range of numbers expected"))); 748 1017 } 749 1018 … … 758 1027 @Override 759 1028 protected String getCountString() { 760 return "area"; 761 } 762 } 763 764 /** 765 * Matches data within bounds.1029 return "areasize"; 1030 } 1031 } 1032 1033 /** 1034 * Matches objects within the given bounds. 766 1035 */ 767 1036 private abstract static class InArea extends Match { … … 797 1066 798 1067 /** 799 * Matches datain source area ("downloaded area").1068 * Matches objects within source area ("downloaded area"). 800 1069 */ 801 1070 private static class InDataSourceArea extends InArea { … … 812 1081 813 1082 /** 814 * Matches datain current map view.1083 * Matches objects within current map view. 815 1084 */ 816 1085 private static class InView extends InArea { … … 843 1112 } 844 1113 1114 /** 1115 * Parse search string. 1116 * 1117 * @return match determined by search string 1118 * @throws org.openstreetmap.josm.actions.search.SearchCompiler.ParseError 1119 */ 845 1120 public Match parse() throws ParseError { 846 1121 Match m = parseExpression(); … … 852 1127 } 853 1128 1129 /** 1130 * Parse expression. This is a recursive method. 1131 * 1132 * @return match determined by parsing expression 1133 * @throws org.openstreetmap.josm.actions.search.SearchCompiler.ParseError 1134 */ 854 1135 private Match parseExpression() throws ParseError { 855 1136 Match factor = parseFactor(); 856 1137 if (factor == null) 1138 // empty search string 857 1139 return null; 858 1140 if (tokenizer.readIfEqual(Token.OR)) 859 1141 return new Or(factor, parseExpression(tr("Missing parameter for OR"))); 1142 else if (tokenizer.readIfEqual(Token.XOR)) 1143 return new Xor(factor, parseExpression(tr("Missing parameter for XOR"))); 860 1144 else { 861 1145 Match expression = parseExpression(); 862 1146 if (expression == null) 1147 // reached end of search string, no more recursive calls 863 1148 return factor; 864 1149 else 1150 // the default operator is AND 865 1151 return new And(factor, expression); 866 1152 } 867 1153 } 868 1154 1155 /** 1156 * Parse expression, showing the specified error message if parsing fails. 1157 * 1158 * @param errorMessage to display if parsing error occurs 1159 * @return 1160 * @throws org.openstreetmap.josm.actions.search.SearchCompiler.ParseError 1161 */ 869 1162 private Match parseExpression(String errorMessage) throws ParseError { 870 1163 Match expression = parseExpression(); … … 875 1168 } 876 1169 1170 /** 1171 * Parse next factor (a search operator or search term). 1172 * 1173 * @return match determined by parsing factor string 1174 * @throws org.openstreetmap.josm.actions.search.SearchCompiler.ParseError 1175 */ 877 1176 private Match parseFactor() throws ParseError { 878 1177 if (tokenizer.readIfEqual(Token.LEFT_PARENT)) { … … 881 1180 throw new ParseError(Token.RIGHT_PARENT, tokenizer.nextToken()); 882 1181 return expression; 883 } else if (tokenizer.readIfEqual(Token.NOT)) 1182 } else if (tokenizer.readIfEqual(Token.NOT)) { 884 1183 return new Not(parseFactor(tr("Missing operator for NOT"))); 885 else if (tokenizer.readIfEqual(Token.KEY)) { 1184 } else if (tokenizer.readIfEqual(Token.KEY)) { 1185 // factor consists of key:value or key=value 886 1186 String key = tokenizer.getText(); 887 1187 if (tokenizer.readIfEqual(Token.EQUALS)) 888 1188 return new ExactKeyValue(regexSearch, key, tokenizer.readTextOrNumber()); 889 1189 else if (tokenizer.readIfEqual(Token.COLON)) { 890 if ("id".equals(key)) 891 return new Id(tokenizer.readNumber(tr("Primitive id expected"))); 892 else if ("tags".equals(key)) { 893 Range range = tokenizer.readRange(tr("Range of numbers expected")); 894 return new TagCountRange(range.getStart(), range.getEnd()); 895 } else if ("nodes".equals(key)) { 896 Range range = tokenizer.readRange(tr("Range of numbers expected")); 897 return new NodeCountRange(range.getStart(), range.getEnd()); 898 } else if ("areasize".equals(key)) { 899 Range range = tokenizer.readRange(tr("Range of numbers expected")); 900 return new Area(range.getStart(), range.getEnd()); 901 } else if ("timestamp".equals(key)) { 902 String rangeS = " " + tokenizer.readTextOrNumber() + " "; // add leading/trailing space in order to get expected split (e.g. "a--" => {"a", ""}) 903 String[] rangeA = rangeS.split("/"); 904 if (rangeA.length == 1) { 905 return new KeyValue(key, rangeS, regexSearch, caseSensitive); 906 } else if (rangeA.length == 2) { 907 String rangeA1 = rangeA[0].trim(); 908 String rangeA2 = rangeA[1].trim(); 909 long minDate = DateUtils.fromString(rangeA1.isEmpty() ? "1980" : rangeA1).getTime(); // if min timestap is empty: use lowest possible date 910 long maxDate = rangeA2.isEmpty() ? new Date().getTime() : DateUtils.fromString(rangeA2).getTime(); // if max timestamp is empty: use "now" 911 return new TimestampRange(minDate, maxDate); 912 } else { 913 /* I18n: Don't translate timestamp keyword */ throw new ParseError(tr("Expecting <i>min</i>/<i>max</i> after ''timestamp''")); 914 } 915 } else if ("changeset".equals(key)) 916 return new ChangesetId(tokenizer.readNumber(tr("Changeset id expected"))); 917 else if ("version".equals(key)) 918 return new Version(tokenizer.readNumber(tr("Version expected"))); 919 else 920 return parseKV(key, tokenizer.readTextOrNumber()); 1190 // see if we have a Match that takes a data parameter 1191 SimpleMatchFactory factory = simpleMatchFactoryMap.get(key); 1192 if (factory != null) 1193 return factory.get(key, tokenizer); 1194 1195 UnaryMatchFactory unaryFactory = unaryMatchFactoryMap.get(key); 1196 if (unaryFactory != null) 1197 return unaryFactory.get(key, parseFactor(), tokenizer); 1198 1199 // key:value form where value is a string (may be OSM key search) 1200 return parseKV(key, tokenizer.readTextOrNumber()); 921 1201 } else if (tokenizer.readIfEqual(Token.QUESTION_MARK)) 922 1202 return new BooleanMatch(key, false); 923 else if ("new".equals(key)) 924 return new New(); 925 else if ("modified".equals(key)) 926 return new Modified(); 927 else if ("incomplete".equals(key)) 928 return new Incomplete(); 929 else if ("untagged".equals(key)) 930 return new Untagged(); 931 else if ("selected".equals(key)) 932 return new Selected(); 933 else if ("closed".equals(key)) 934 return new Closed(); 935 else if ("child".equals(key)) 936 return new Child(parseFactor()); 937 else if ("parent".equals(key)) 938 return new Parent(parseFactor()); 939 else if ("indownloadedarea".equals(key)) 940 return new InDataSourceArea(false); 941 else if ("allindownloadedarea".equals(key)) 942 return new InDataSourceArea(true); 943 else if ("inview".equals(key)) 944 return new InView(false); 945 else if ("allinview".equals(key)) 946 return new InView(true); 947 else 1203 else { 1204 SimpleMatchFactory factory = simpleMatchFactoryMap.get(key); 1205 if (factory != null) 1206 return factory.get(key, null); 1207 1208 UnaryMatchFactory unaryFactory = unaryMatchFactoryMap.get(key); 1209 if (unaryFactory != null) 1210 return unaryFactory.get(key, parseFactor(), null); 1211 1212 // match string in any key or value 948 1213 return new Any(key, regexSearch, caseSensitive); 1214 } 949 1215 } else 950 1216 return null; -
trunk/src/org/openstreetmap/josm/tools/template_engine/ContextSwitchTemplate.java
r4725 r4817 184 184 private Match transform(Match m, int searchExpressionPosition) throws ParseError { 185 185 if (m instanceof Parent) { 186 Match child = transform(((Parent) m).get Child(), searchExpressionPosition);186 Match child = transform(((Parent) m).getOperand(), searchExpressionPosition); 187 187 return new ParentSet(child); 188 188 } else if (m instanceof Child) { 189 Match parent = transform(((Child) m).get Parent(), searchExpressionPosition);189 Match parent = transform(((Child) m).getOperand(), searchExpressionPosition); 190 190 return new ChildSet(parent); 191 191 } else if (m instanceof And) {
Note:
See TracChangeset
for help on using the changeset viewer.