001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.tools; 003 004import java.io.IOException; 005import java.io.Reader; 006 007import javax.script.Invocable; 008import javax.script.ScriptEngine; 009import javax.script.ScriptEngineManager; 010import javax.script.ScriptException; 011 012import org.openstreetmap.josm.Main; 013import org.openstreetmap.josm.io.CachedFile; 014 015/** 016 * Uses <a href="https://github.com/tyrasd/overpass-wizard/">Overpass Turbo query wizard</a> code (MIT Licensed) 017 * to build an Overpass QL from a {@link org.openstreetmap.josm.actions.search.SearchAction} like query. 018 * 019 * Requires a JavaScript {@link ScriptEngine}. 020 * @since 8744 021 */ 022public final class OverpassTurboQueryWizard { 023 024 private static OverpassTurboQueryWizard instance; 025 private final ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript"); 026 027 /** 028 * Replies the unique instance of this class. 029 * 030 * @return the unique instance of this class 031 */ 032 public static synchronized OverpassTurboQueryWizard getInstance() { 033 if (instance == null) { 034 instance = new OverpassTurboQueryWizard(); 035 } 036 return instance; 037 } 038 039 private OverpassTurboQueryWizard() { 040 041 try (final CachedFile file = new CachedFile("resource://data/overpass-wizard.js"); 042 final Reader reader = file.getContentReader()) { 043 engine.eval("var console = {error: " + Main.class.getCanonicalName() + ".warn};"); 044 engine.eval("var global = {};"); 045 engine.eval(reader); 046 engine.eval("var overpassWizard = function(query) {" + 047 " return global.overpassWizard(query, {" + 048 " comment: false," + 049 " outputFormat: 'xml'," + 050 " outputMode: 'recursive_meta'" + 051 " });" + 052 "}"); 053 } catch (ScriptException | IOException ex) { 054 throw new IllegalStateException("Failed to initialize OverpassTurboQueryWizard", ex); 055 } 056 } 057 058 /** 059 * Builds an Overpass QL from a {@link org.openstreetmap.josm.actions.search.SearchAction} like query. 060 * @param search the {@link org.openstreetmap.josm.actions.search.SearchAction} like query 061 * @return an Overpass QL query 062 * @throws UncheckedParseException when the parsing fails 063 */ 064 public String constructQuery(String search) { 065 try { 066 final Object result = ((Invocable) engine).invokeFunction("overpassWizard", search); 067 if (Boolean.FALSE.equals(result)) { 068 throw new UncheckedParseException(); 069 } 070 String query = (String) result; 071 query = query.replace("[bbox:{{bbox}}]", ""); 072 return query; 073 } catch (NoSuchMethodException e) { 074 throw new IllegalStateException(e); 075 } catch (ScriptException e) { 076 throw new UncheckedParseException("Failed to execute OverpassTurboQueryWizard", e); 077 } 078 } 079}