Ticket #14923: preset-search-v4.patch
File preset-search-v4.patch, 29.1 KB (added by , 7 years ago) |
---|
-
src/org/openstreetmap/josm/actions/search/SearchAction.java
35 35 import javax.swing.JOptionPane; 36 36 import javax.swing.JPanel; 37 37 import javax.swing.JRadioButton; 38 import javax.swing.SwingUtilities; 38 39 import javax.swing.text.BadLocationException; 40 import javax.swing.text.Document; 39 41 import javax.swing.text.JTextComponent; 40 42 41 43 import org.openstreetmap.josm.Main; … … 54 56 import org.openstreetmap.josm.gui.preferences.ToolbarPreferences; 55 57 import org.openstreetmap.josm.gui.preferences.ToolbarPreferences.ActionParser; 56 58 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 59 import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset; 60 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetSelector; 57 61 import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator; 58 62 import org.openstreetmap.josm.gui.widgets.HistoryComboBox; 59 63 import org.openstreetmap.josm.tools.GBC; … … 231 235 232 236 @Override 233 237 public void mouseClicked(MouseEvent e) { 234 try { 235 JTextComponent tf = hcb.getEditorComponent(); 236 tf.getDocument().insertString(tf.getCaretPosition(), ' ' + insertText, null); 237 } catch (BadLocationException ex) { 238 throw new JosmRuntimeException(ex.getMessage(), ex); 238 JTextComponent tf = hcb.getEditorComponent(); 239 240 /* 241 * Make sure that the focus is transferred to the search text field 242 * from the selector component. 243 */ 244 if (!tf.hasFocus()) { 245 tf.requestFocusInWindow(); 239 246 } 247 248 /* 249 * In order to make interaction with the search dialog simpler, 250 * we make sure that if autocompletion triggers and the text field is 251 * not in focus, the correct area is selected. We first request focus 252 * and then execute the selection logic. invokeLater allows us to 253 * defer the selection until waiting for focus. 254 */ 255 SwingUtilities.invokeLater(() -> { 256 try { 257 tf.getDocument().insertString(tf.getCaretPosition(), ' ' + insertText, null); 258 } catch (BadLocationException ex) { 259 throw new JosmRuntimeException(ex.getMessage(), ex); 260 } 261 }); 240 262 } 241 263 }); 242 264 } … … 244 266 } 245 267 } 246 268 269 /** 270 * Builds and shows the search dialog. 271 * @param initialValues A set of initial values needed in order to initialize the search dialog. 272 * If is {@code null}, then default settings are used. 273 * @return Returns {@link SearchAction} object containing parameters of the search. 274 */ 247 275 public static SearchSetting showSearchDialog(SearchSetting initialValues) { 248 276 if (initialValues == null) { 249 277 initialValues = new SearchSetting(); 250 278 } 251 // -- prepare the combo box with the search expressions 252 // 279 280 // prepare the combo box with the search expressions 253 281 JLabel label = new JLabel(initialValues instanceof Filter ? tr("Filter string:") : tr("Search string:")); 254 finalHistoryComboBox hcbSearchString = new HistoryComboBox();255 finalString tooltip = tr("Enter the search expression");282 HistoryComboBox hcbSearchString = new HistoryComboBox(); 283 String tooltip = tr("Enter the search expression"); 256 284 hcbSearchString.setText(initialValues.text); 257 285 hcbSearchString.setToolTipText(tooltip); 286 258 287 // we have to reverse the history, because ComboBoxHistory will reverse it again in addElement() 259 //260 288 List<String> searchExpressionHistory = getSearchExpressionHistory(); 261 289 Collections.reverse(searchExpressionHistory); 262 290 hcbSearchString.setPossibleItems(searchExpressionHistory); … … 273 301 bg.add(remove); 274 302 bg.add(inSelection); 275 303 276 finalJCheckBox caseSensitive = new JCheckBox(tr("case sensitive"), initialValues.caseSensitive);304 JCheckBox caseSensitive = new JCheckBox(tr("case sensitive"), initialValues.caseSensitive); 277 305 JCheckBox allElements = new JCheckBox(tr("all objects"), initialValues.allElements); 278 306 allElements.setToolTipText(tr("Also include incomplete and deleted objects in search.")); 279 307 JCheckBox addOnToolbar = new JCheckBox(tr("add toolbar button"), false); 280 308 281 finalJRadioButton standardSearch = new JRadioButton(tr("standard"), !initialValues.regexSearch && !initialValues.mapCSSSearch);282 finalJRadioButton regexSearch = new JRadioButton(tr("regular expression"), initialValues.regexSearch);283 finalJRadioButton mapCSSSearch = new JRadioButton(tr("MapCSS selector"), initialValues.mapCSSSearch);284 finalButtonGroup bg2 = new ButtonGroup();309 JRadioButton standardSearch = new JRadioButton(tr("standard"), !initialValues.regexSearch && !initialValues.mapCSSSearch); 310 JRadioButton regexSearch = new JRadioButton(tr("regular expression"), initialValues.regexSearch); 311 JRadioButton mapCSSSearch = new JRadioButton(tr("MapCSS selector"), initialValues.mapCSSSearch); 312 ButtonGroup bg2 = new ButtonGroup(); 285 313 bg2.add(standardSearch); 286 314 bg2.add(regexSearch); 287 315 bg2.add(mapCSSSearch); 288 316 289 JPanel left = new JPanel(new GridBagLayout());290 291 317 JPanel selectionSettings = new JPanel(new GridBagLayout()); 292 318 selectionSettings.setBorder(BorderFactory.createTitledBorder(tr("Selection settings"))); 293 319 selectionSettings.add(replace, GBC.eol().anchor(GBC.WEST).fill(GBC.HORIZONTAL)); … … 299 325 additionalSettings.setBorder(BorderFactory.createTitledBorder(tr("Additional settings"))); 300 326 additionalSettings.add(caseSensitive, GBC.eol().anchor(GBC.WEST).fill(GBC.HORIZONTAL)); 301 327 328 JPanel left = new JPanel(new GridBagLayout()); 329 302 330 left.add(selectionSettings, GBC.eol().fill(GBC.BOTH)); 303 331 left.add(additionalSettings, GBC.eol().fill(GBC.BOTH)); 304 332 … … 315 343 left.add(searchOptions, GBC.eol().fill(GBC.BOTH)); 316 344 } 317 345 318 finalJPanel right = SearchAction.buildHintsSection(hcbSearchString);319 finalJPanel top = new JPanel(new GridBagLayout());346 JPanel right = SearchAction.buildHintsSection(hcbSearchString); 347 JPanel top = new JPanel(new GridBagLayout()); 320 348 top.add(label, GBC.std().insets(0, 0, 5, 0)); 321 349 top.add(hcbSearchString, GBC.eol().fill(GBC.HORIZONTAL)); 322 350 323 finalJTextComponent editorComponent = hcbSearchString.getEditorComponent();324 editorComponent.getDocument().addDocumentListener(new AbstractTextComponentValidator(editorComponent) {351 JTextComponent editorComponent = hcbSearchString.getEditorComponent(); 352 Document document = editorComponent.getDocument(); 325 353 354 /* 355 * Setup the logic to validate the contents of the search text field which is executed 356 * every time the content of the field has changed. If the query is incorrect, then 357 * the text field is colored red. 358 */ 359 document.addDocumentListener(new AbstractTextComponentValidator(editorComponent) { 360 326 361 @Override 327 362 public void validate() { 328 363 if (!isValid()) { … … 348 383 } 349 384 }); 350 385 351 final JPanel p = new JPanel(new GridBagLayout()); 386 /* 387 * Setup the logic to append preset queries to the search text field according to 388 * selected preset by the user. Every query is of the form ' group/sub-group/.../presetName' 389 * if the corresponding group of the preset exists, otherwise it is simply ' presetName'. 390 */ 391 TaggingPresetSelector selector = new TaggingPresetSelector(false, false); 392 selector.setBorder(BorderFactory.createTitledBorder(tr("Search by preset"))); 393 selector.setDblClickListener(ev -> setPresetDblClickListener(selector, editorComponent)); 394 395 JPanel p = new JPanel(new GridBagLayout()); 352 396 p.add(top, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 5, 0)); 353 397 p.add(left, GBC.std().anchor(GBC.NORTH).insets(5, 10, 10, 0).fill(GBC.VERTICAL)); 354 p.add(right, GBC.eol().fill(GBC.BOTH).insets(0, 10, 0, 0)); 398 p.add(right, GBC.std().fill(GBC.BOTH).insets(0, 10, 0, 0)); 399 p.add(selector, GBC.eol().fill(GBC.BOTH).insets(0, 10, 0, 0)); 355 400 356 401 ExtendedDialog dialog = new ExtendedDialog( 357 402 Main.parent, … … 390 435 if (dialog.showDialog().getValue() != 1) return null; 391 436 392 437 // User pressed OK - let's perform the search 393 SearchMode mode = replace.isSelected() ? SearchAction.SearchMode.replace394 : (add.isSelected() ? SearchAction.SearchMode.add395 : (remove.isSelected() ? SearchAction.SearchMode.remove : SearchAction.SearchMode.in_selection));396 438 initialValues.text = hcbSearchString.getText(); 397 initialValues.mode = mode;398 439 initialValues.caseSensitive = caseSensitive.isSelected(); 399 440 initialValues.allElements = allElements.isSelected(); 400 441 initialValues.regexSearch = regexSearch.isSelected(); 401 442 initialValues.mapCSSSearch = mapCSSSearch.isSelected(); 402 443 444 if (inSelection.isSelected()) { 445 initialValues.mode = SearchAction.SearchMode.in_selection; 446 } else if (replace.isSelected()) { 447 initialValues.mode = SearchAction.SearchMode.replace; 448 } else if (add.isSelected()) { 449 initialValues.mode = SearchAction.SearchMode.add; 450 } else { 451 initialValues.mode = SearchAction.SearchMode.remove; 452 } 453 403 454 if (addOnToolbar.isSelected()) { 404 455 ToolbarPreferences.ActionDefinition aDef = 405 456 new ToolbarPreferences.ActionDefinition(Main.main.menu.search); … … 413 464 // add custom search button to toolbar preferences 414 465 Main.toolbar.addCustomButton(res, -1, false); 415 466 } 467 416 468 return initialValues; 417 469 } 418 470 … … 458 510 .addKeyword("untagged", "untagged ", tr("object without useful tags")), 459 511 GBC.eol()); 460 512 hintPanel.add(new SearchKeywordRow(hcbSearchString) 513 .addKeyword("preset:\"Annotation/Address\"", "preset:\"Annotation/Address\"", 514 tr("all objects that use the address preset")) 515 .addKeyword("preset:\"Geography/Nature/*\"", "preset:\"Geography/Nature/*\"", 516 tr("all objects that use any preset under the Geography/Nature group")), 517 GBC.eol().anchor(GBC.CENTER)); 518 hintPanel.add(new SearchKeywordRow(hcbSearchString) 461 519 .addTitle(tr("metadata")) 462 520 .addKeyword("user:", "user:", tr("objects changed by user", "user:anonymous")) 463 521 .addKeyword("id:", "id:", tr("objects with given ID"), "id:0 (new objects)") … … 575 633 } 576 634 577 635 /** 636 * 637 * @param selector Selector component that the user interacts with 638 * @param searchEditor Editor for search queries 639 */ 640 private static void setPresetDblClickListener(TaggingPresetSelector selector, JTextComponent searchEditor) { 641 TaggingPreset selectedPreset = selector.getSelectedPresetAndUpdateClassification(); 642 643 if (selectedPreset == null) { 644 return; 645 } 646 647 /* 648 * Make sure that the focus is transferred to the search text field 649 * from the selector component. 650 */ 651 searchEditor.requestFocusInWindow(); 652 653 /* 654 * In order to make interaction with the search dialog simpler, 655 * we make sure that if autocompletion triggers and the text field is 656 * not in focus, the correct area is selected. We first request focus 657 * and then execute the selection logic. invokeLater allows us to 658 * defer the selection until waiting for focus. 659 */ 660 SwingUtilities.invokeLater(() -> { 661 int textOffset = searchEditor.getCaretPosition(); 662 String presetSearchQuery = " preset:" + 663 "\"" + selectedPreset.getRawName() + "\""; 664 try { 665 searchEditor.getDocument().insertString(textOffset, presetSearchQuery, null); 666 } catch (BadLocationException e1) { 667 throw new JosmRuntimeException(e1.getMessage(), e1); 668 } 669 }); 670 } 671 672 /** 578 673 * Interfaces implementing this may receive the result of the current search. 579 674 * @author Michael Zangl 580 675 * @since 10457 … … 749 844 } 750 845 } 751 846 847 /** 848 * This class defines a set of parameters that is used to 849 * perform search within the search dialog. 850 */ 752 851 public static class SearchSetting { 753 852 public String text; 754 853 public SearchMode mode; … … 810 909 return Objects.hash(text, mode, caseSensitive, regexSearch, mapCSSSearch, allElements); 811 910 } 812 911 912 /** 913 * <p>Transforms a string following a certain format, namely "[R | A | D | S][C?,R?,A?,M?] [a-zA-Z]" 914 * where the first part defines the mode of the search, see {@link SearchMode}, the second defines 915 * a set of attributes within the {@code SearchSetting} class and the second is the search query. 916 * <p> 917 * Attributes are as follows: 918 * <ul> 919 * <li>C - if search is case sensitive 920 * <li>R - if the regex syntax is used 921 * <li>A - if all objects are considered 922 * <li>M - if the mapCSS syntax is used 923 * </ul> 924 * <p>For example, "RC type:node" is a valid string representation of an object that replaces the 925 * current selection, is case sensitive and searches for all objects of type node. 926 * @param s A string representation of a {@code SearchSetting} object 927 * from which the object must be built. 928 * @return A {@code SearchSetting} defined by the input string. 929 */ 813 930 public static SearchSetting readFromString(String s) { 814 931 if (s.isEmpty()) 815 932 return null; … … 851 968 return result; 852 969 } 853 970 971 /** 972 * Builds a string representation of the {@code SearchSetting} object, 973 * see {@link #readFromString(String)} for more details. 974 * @return A string representation of the {@code SearchSetting} object. 975 */ 854 976 public String writeToString() { 855 977 if (text == null || text.isEmpty()) 856 978 return ""; … … 888 1010 public List<ActionParameter<?>> getActionParameters() { 889 1011 return Collections.<ActionParameter<?>>singletonList(new SearchSettingsActionParameter(SEARCH_EXPRESSION)); 890 1012 } 891 } 1013 } 1014 No newline at end of file -
src/org/openstreetmap/josm/actions/search/SearchCompiler.java
20 20 import java.util.regex.Matcher; 21 21 import java.util.regex.Pattern; 22 22 import java.util.regex.PatternSyntaxException; 23 import java.util.stream.Collectors; 23 24 24 25 import org.openstreetmap.josm.Main; 25 26 import org.openstreetmap.josm.actions.search.PushbackTokenizer.Range; … … 38 39 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector; 39 40 import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.MapCSSParser; 40 41 import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.ParseException; 42 import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset; 43 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetMenu; 44 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetSeparator; 45 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets; 41 46 import org.openstreetmap.josm.tools.AlphanumComparator; 42 47 import org.openstreetmap.josm.tools.Geometry; 43 48 import org.openstreetmap.josm.tools.UncheckedParseException; … … 115 120 private final Collection<String> keywords = Arrays.asList("id", "version", "type", "user", "role", 116 121 "changeset", "nodes", "ways", "tags", "areasize", "waylength", "modified", "deleted", "selected", 117 122 "incomplete", "untagged", "closed", "new", "indownloadedarea", 118 "allindownloadedarea", "inview", "allinview", "timestamp", "nth", "nth%", "hasRole" );123 "allindownloadedarea", "inview", "allinview", "timestamp", "nth", "nth%", "hasRole", "preset"); 119 124 120 125 @Override 121 126 public Match get(String keyword, PushbackTokenizer tokenizer) throws ParseError { … … 151 156 return new Version(tokenizer); 152 157 case "type": 153 158 return new ExactType(tokenizer.readTextOrNumber()); 159 case "preset": 160 return new Preset(tokenizer.readTextOrNumber()); 154 161 case "user": 155 162 return new UserMatch(tokenizer.readTextOrNumber()); 156 163 case "role": … … 1554 1561 } 1555 1562 } 1556 1563 1564 /** 1565 * Matches presets. 1566 */ 1567 private static class Preset extends Match { 1568 private final List<TaggingPreset> presets; 1569 1570 Preset(String presetName) throws ParseError { 1571 1572 if (presetName == null || presetName.equals("")) { 1573 throw new ParseError("The name of the preset is required"); 1574 } 1575 1576 int wildCardIdx = presetName.lastIndexOf("*"); 1577 int length = presetName.length() - 1; 1578 1579 /* 1580 * Match strictly (simply comparing the names) if there is no '*' symbol 1581 * at the end of the name or '*' is a part of the preset name. 1582 */ 1583 boolean matchStrictly = wildCardIdx == -1 || wildCardIdx != length; 1584 1585 this.presets = TaggingPresets.getTaggingPresets() 1586 .stream() 1587 .filter(preset -> !(preset instanceof TaggingPresetMenu || preset instanceof TaggingPresetSeparator)) 1588 .filter(preset -> this.presetNameMatch(presetName, preset, matchStrictly)) 1589 .collect(Collectors.toList()); 1590 1591 if (this.presets.isEmpty()) { 1592 throw new ParseError(tr("Unknown preset name: ") + presetName); 1593 } 1594 } 1595 1596 @Override 1597 public boolean match(OsmPrimitive osm) { 1598 for (TaggingPreset preset : this.presets) { 1599 if (preset.test(osm)) { 1600 return true; 1601 } 1602 } 1603 1604 return false; 1605 } 1606 1607 private boolean presetNameMatch(String name, TaggingPreset preset, boolean matchStrictly) { 1608 if (matchStrictly) { 1609 return name.equalsIgnoreCase(preset.getRawName()); 1610 } 1611 1612 try { 1613 String groupSuffix = name.substring(0, name.length() - 2); // try to remove '/*' 1614 TaggingPresetMenu group = preset.group; 1615 1616 return group != null && groupSuffix.equalsIgnoreCase(group.getRawName()); 1617 } catch (StringIndexOutOfBoundsException ex) { 1618 return false; 1619 } 1620 } 1621 } 1622 1557 1623 public static class ParseError extends Exception { 1558 1624 public ParseError(String msg) { 1559 1625 super(msg); … … 1803 1869 return forKey + '"' + escapeStringForSearch(value) + '"'; 1804 1870 } 1805 1871 } 1806 } 1872 } 1873 No newline at end of file -
src/org/openstreetmap/josm/gui/tagging/presets/TaggingPreset.java
634 634 ToolbarPreferences.ActionParser actionParser = new ToolbarPreferences.ActionParser(null); 635 635 return actionParser.saveAction(new ToolbarPreferences.ActionDefinition(this)); 636 636 } 637 } 637 } 638 No newline at end of file -
test/unit/org/openstreetmap/josm/actions/search/SearchCompilerTest.java
7 7 import static org.junit.Assert.assertTrue; 8 8 import static org.junit.Assert.fail; 9 9 10 import java.lang.reflect.Field; 10 11 import java.nio.charset.StandardCharsets; 11 12 import java.nio.file.Files; 12 13 import java.nio.file.Paths; 14 import java.util.Arrays; 15 import java.util.Collection; 16 import java.util.Collections; 13 17 14 18 import org.junit.Rule; 15 19 import org.junit.Test; … … 30 34 import org.openstreetmap.josm.data.osm.User; 31 35 import org.openstreetmap.josm.data.osm.Way; 32 36 import org.openstreetmap.josm.data.osm.WayData; 37 import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset; 38 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType; 39 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets; 40 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetMenu; 41 import org.openstreetmap.josm.gui.tagging.presets.items.Key; 33 42 import org.openstreetmap.josm.testutils.JOSMTestRules; 34 43 import org.openstreetmap.josm.tools.date.DateUtils; 35 44 … … 490 499 public void testEnumExactKeyValueMode() { 491 500 TestUtils.superficialEnumCodeCoverage(ExactKeyValue.Mode.class); 492 501 } 493 } 502 503 /** 504 * Robustness test for preset searching. Ensures that the query 'preset:' is not accepted. 505 * @throws ParseError always 506 */ 507 @Test(expected = ParseError.class) 508 public void testPresetSearchMissingValue() throws ParseError { 509 SearchSetting settings = new SearchSetting(); 510 settings.text = "preset:"; 511 settings.mapCSSSearch = false; 512 513 TaggingPresets.readFromPreferences(); 514 515 SearchCompiler.compile(settings); 516 } 517 518 /** 519 * Robustness test for preset searching. Validates that it is not possible to search for 520 * non existing presets. 521 * @throws ParseError always 522 */ 523 @Test(expected = ParseError.class) 524 public void testPresetNotExist() throws ParseError { 525 String testPresetName = "groupnamethatshouldnotexist/namethatshouldnotexist"; 526 SearchSetting settings = new SearchSetting(); 527 settings.text = "preset:" + testPresetName; 528 settings.mapCSSSearch = false; 529 530 // load presets 531 TaggingPresets.readFromPreferences(); 532 533 SearchCompiler.compile(settings); 534 } 535 536 /** 537 * Robustness tests for preset searching. Ensures that combined preset names (having more than 538 * 1 word) must be enclosed with " . 539 * @throws ParseError always 540 */ 541 @Test(expected = ParseError.class) 542 public void testPresetMultipleWords() throws ParseError { 543 TaggingPreset testPreset = new TaggingPreset(); 544 testPreset.name = "Test Combined Preset Name"; 545 testPreset.group = new TaggingPresetMenu(); 546 testPreset.group.name = "TestGroupName"; 547 548 String combinedPresetname = testPreset.getRawName(); 549 SearchSetting settings = new SearchSetting(); 550 settings.text = "preset:" + combinedPresetname; 551 settings.mapCSSSearch = false; 552 553 // load presets 554 TaggingPresets.readFromPreferences(); 555 556 SearchCompiler.compile(settings); 557 } 558 559 560 /** 561 * Ensures that correct presets are stored in the {@link org.openstreetmap.josm.actions.search.SearchCompiler.Preset} 562 * class against which the osm primitives are tested. 563 * @throws ParseError if an error has been encountered while compiling 564 * @throws NoSuchFieldException if there is no field called 'presets' 565 * @throws IllegalAccessException if cannot access the field where all matching presets are stored 566 */ 567 @Test 568 public void testPresetLookup() throws ParseError, NoSuchFieldException, IllegalAccessException { 569 TaggingPreset testPreset = new TaggingPreset(); 570 testPreset.name = "Test Preset Name"; 571 testPreset.group = new TaggingPresetMenu(); 572 testPreset.group.name = "Test Preset Group Name"; 573 574 String query = "preset:" + 575 "\"" + testPreset.getRawName() + "\""; 576 SearchSetting settings = new SearchSetting(); 577 settings.text = query; 578 settings.mapCSSSearch = false; 579 580 // load presets and add the test preset 581 TaggingPresets.readFromPreferences(); 582 TaggingPresets.addTaggingPresets(Collections.singletonList(testPreset)); 583 584 Match match = SearchCompiler.compile(settings); 585 586 // access the private field where all matching presets are stored 587 // and ensure that indeed the correct ones are there 588 Field field = match.getClass().getDeclaredField("presets"); 589 field.setAccessible(true); 590 Collection<TaggingPreset> foundPresets = (Collection<TaggingPreset>) field.get(match); 591 592 assertEquals(1, foundPresets.size()); 593 assertTrue(foundPresets.contains(testPreset)); 594 } 595 596 /** 597 * Ensures that the wildcard search works and that correct presets are stored in 598 * the {@link org.openstreetmap.josm.actions.search.SearchCompiler.Preset} class against which 599 * the osm primitives are tested. 600 * @throws ParseError if an error has been encountered while compiling 601 * @throws NoSuchFieldException if there is no field called 'presets' 602 * @throws IllegalAccessException if cannot access the field where all matching presets are stored 603 */ 604 @Test 605 public void testPresetLookupWildcard() throws ParseError, NoSuchFieldException, IllegalAccessException { 606 TaggingPresetMenu group = new TaggingPresetMenu(); 607 group.name = "TestPresetGroup"; 608 609 TaggingPreset testPreset1 = new TaggingPreset(); 610 testPreset1.name = "TestPreset1"; 611 testPreset1.group = group; 612 613 TaggingPreset testPreset2 = new TaggingPreset(); 614 testPreset2.name = "TestPreset2"; 615 testPreset2.group = group; 616 617 TaggingPreset testPreset3 = new TaggingPreset(); 618 testPreset3.name = "TestPreset3"; 619 testPreset3.group = group; 620 621 String query = "preset:" + "\"" + group.getRawName() + "/*\""; 622 SearchSetting settings = new SearchSetting(); 623 settings.text = query; 624 settings.mapCSSSearch = false; 625 626 TaggingPresets.readFromPreferences(); 627 TaggingPresets.addTaggingPresets(Arrays.asList(testPreset1, testPreset2, testPreset3)); 628 629 Match match = SearchCompiler.compile(settings); 630 631 // access the private field where all matching presets are stored 632 // and ensure that indeed the correct ones are there 633 Field field = match.getClass().getDeclaredField("presets"); 634 field.setAccessible(true); 635 Collection<TaggingPreset> foundPresets = (Collection<TaggingPreset>) field.get(match); 636 637 assertEquals(3, foundPresets.size()); 638 assertTrue(foundPresets.contains(testPreset1)); 639 assertTrue(foundPresets.contains(testPreset2)); 640 assertTrue(foundPresets.contains(testPreset3)); 641 } 642 643 /** 644 * Ensures that correct primitives are matched against the specified preset. 645 * @throws ParseError if an error has been encountered while compiling 646 */ 647 @Test 648 public void testPreset() throws ParseError { 649 final String presetName = "Test Preset Name"; 650 final String presetGroupName = "Test Preset Group"; 651 final String key = "test_key1"; 652 final String val = "test_val1"; 653 654 Key key1 = new Key(); 655 key1.key = key; 656 key1.value = val; 657 658 TaggingPreset testPreset = new TaggingPreset(); 659 testPreset.name = presetName; 660 testPreset.types = Collections.singleton(TaggingPresetType.NODE); 661 testPreset.data.add(key1); 662 testPreset.group = new TaggingPresetMenu(); 663 testPreset.group.name = presetGroupName; 664 665 TaggingPresets.readFromPreferences(); 666 TaggingPresets.addTaggingPresets(Collections.singleton(testPreset)); 667 668 String query = "preset:" + "\"" + testPreset.getRawName() + "\""; 669 670 SearchContext ctx = new SearchContext(query); 671 ctx.n1.put(key, val); 672 ctx.n2.put(key, val); 673 674 for (OsmPrimitive osm : new OsmPrimitive[] {ctx.n1, ctx.n2}) { 675 ctx.match(osm, true); 676 } 677 678 for (OsmPrimitive osm : new OsmPrimitive[] {ctx.r1, ctx.r2, ctx.w1, ctx.w2}) { 679 ctx.match(osm, false); 680 } 681 } 682 } 683 No newline at end of file