001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.download; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.event.ActionEvent; 007import java.util.ArrayList; 008import java.util.Collections; 009import java.util.Optional; 010 011import javax.swing.JOptionPane; 012 013import org.openstreetmap.josm.data.osm.search.SearchSetting; 014import org.openstreetmap.josm.data.preferences.ListProperty; 015import org.openstreetmap.josm.gui.dialogs.SearchDialog; 016import org.openstreetmap.josm.gui.download.overpass.OverpassWizardRegistration.OverpassWizardCallbacks; 017import org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBoxModel; 018import org.openstreetmap.josm.tools.Logging; 019import org.openstreetmap.josm.tools.SearchCompilerQueryWizard; 020import org.openstreetmap.josm.tools.UncheckedParseException; 021import org.openstreetmap.josm.tools.Utils; 022 023/** 024 * This dialog provides an easy and fast way to create an overpass query. 025 * @since 12576 026 * @since 12652: Moved here 027 */ 028public final class OverpassQueryWizardDialog extends SearchDialog { 029 030 private static final ListProperty OVERPASS_WIZARD_HISTORY = 031 new ListProperty("download.overpass.wizard", new ArrayList<String>()); 032 private final OverpassWizardCallbacks callbacks; 033 034 // dialog buttons 035 private static final int BUILD_QUERY = 0; 036 private static final int BUILD_AN_EXECUTE_QUERY = 1; 037 private static final int CANCEL = 2; 038 039 private AutoCompComboBoxModel<SearchSetting> model; 040 041 /** preferences reader/writer with automatic transmogrification to and from String */ 042 private AutoCompComboBoxModel<SearchSetting>.Preferences prefs; 043 044 /** 045 * Create a new {@link OverpassQueryWizardDialog} 046 * @param callbacks The Overpass download source panel. 047 */ 048 public OverpassQueryWizardDialog(OverpassWizardCallbacks callbacks) { 049 super(new SearchSetting(), new AutoCompComboBoxModel<>(), new PanelOptions(false, true), callbacks.getParent(), 050 tr("Overpass Query Wizard"), 051 tr("Build query"), tr("Build query and execute"), tr("Cancel")); 052 this.callbacks = callbacks; 053 model = hcbSearchString.getModel(); 054 setButtonIcons("dialogs/magic-wand", "download-overpass", "cancel"); 055 setCancelButton(CANCEL + 1); 056 setDefaultButton(BUILD_AN_EXECUTE_QUERY + 1); 057 prefs = model.prefs(SearchSetting::fromString, SearchSetting::toString); 058 prefs.load(OVERPASS_WIZARD_HISTORY); 059 } 060 061 @Override 062 public void buttonAction(int buttonIndex, ActionEvent evt) { 063 switch (buttonIndex) { 064 case BUILD_QUERY: 065 if (this.buildQueryAction()) { 066 this.saveHistory(); 067 super.buttonAction(BUILD_QUERY, evt); 068 } 069 break; 070 case BUILD_AN_EXECUTE_QUERY: 071 if (this.buildQueryAction()) { 072 this.saveHistory(); 073 super.buttonAction(BUILD_AN_EXECUTE_QUERY, evt); 074 075 DownloadDialog.getInstance().startDownload(); 076 } 077 break; 078 default: 079 super.buttonAction(buttonIndex, evt); 080 } 081 } 082 083 /** 084 * Saves the latest, successfully parsed search term. 085 */ 086 private void saveHistory() { 087 Optional.ofNullable(SearchSetting.fromString(hcbSearchString.getText())) 088 .ifPresent(model::addTopElement); 089 prefs.save(OVERPASS_WIZARD_HISTORY); 090 } 091 092 /** 093 * Tries to process a search term using {@link SearchCompilerQueryWizard}. If the term cannot 094 * be parsed, the the corresponding dialog is shown. 095 * @param searchTerm The search term to parse. 096 * @return {@link Optional#empty()} if an exception was thrown when parsing, meaning 097 * that the term cannot be processed, or non-empty {@link Optional} containing the result 098 * of parsing. 099 */ 100 private Optional<String> tryParseSearchTerm(String searchTerm) { 101 try { 102 return Optional.of(SearchCompilerQueryWizard.constructQuery(searchTerm)); 103 } catch (UncheckedParseException | IllegalStateException ex) { 104 Logging.error(ex); 105 JOptionPane.showMessageDialog( 106 callbacks.getParent(), 107 "<html>" + 108 tr("The Overpass wizard could not parse the following query:") + 109 Utils.joinAsHtmlUnorderedList(Collections.singleton(Utils.escapeReservedCharactersHTML(searchTerm))) + 110 "</html>", 111 tr("Parse error"), 112 JOptionPane.ERROR_MESSAGE 113 ); 114 return Optional.empty(); 115 } 116 } 117 118 /** 119 * Builds an Overpass query out from {@link SearchSetting} contents. 120 * @return {@code true} if the query successfully built, {@code false} otherwise. 121 */ 122 private boolean buildQueryAction() { 123 Optional<String> q = tryParseSearchTerm(getSearchSettings().text); 124 q.ifPresent(callbacks::submitWizardResult); 125 return q.isPresent(); 126 } 127}