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 if (engine == null) { 041 throw new IllegalStateException("Failed to retrieve JavaScript engine"); 042 } 043 try (CachedFile file = new CachedFile("resource://data/overpass-wizard.js"); 044 Reader reader = file.getContentReader()) { 045 engine.eval("var console = {error: " + Main.class.getCanonicalName() + ".warn};"); 046 engine.eval("var global = {};"); 047 engine.eval(reader); 048 engine.eval("var overpassWizard = function(query) {" + 049 " return global.overpassWizard(query, {" + 050 " comment: false," + 051 " outputFormat: 'xml'," + 052 " outputMode: 'recursive_meta'" + 053 " });" + 054 "}"); 055 } catch (ScriptException | IOException ex) { 056 throw new IllegalStateException("Failed to initialize OverpassTurboQueryWizard", ex); 057 } 058 } 059 060 /** 061 * Builds an Overpass QL from a {@link org.openstreetmap.josm.actions.search.SearchAction} like query. 062 * @param search the {@link org.openstreetmap.josm.actions.search.SearchAction} like query 063 * @return an Overpass QL query 064 * @throws UncheckedParseException when the parsing fails 065 */ 066 public String constructQuery(String search) { 067 try { 068 final Object result = ((Invocable) engine).invokeFunction("overpassWizard", search); 069 if (Boolean.FALSE.equals(result)) { 070 throw new UncheckedParseException(); 071 } 072 return (String) result; 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}