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.ScriptException; 010 011import org.openstreetmap.josm.io.CachedFile; 012 013/** 014 * Uses <a href="https://github.com/tyrasd/overpass-wizard/">Overpass Turbo query wizard</a> code (MIT Licensed) 015 * to build an Overpass QL from a {@link org.openstreetmap.josm.actions.search.SearchAction} like query. 016 * 017 * Requires a JavaScript {@link ScriptEngine}. 018 * @since 8744 019 */ 020public final class OverpassTurboQueryWizard { 021 022 private static OverpassTurboQueryWizard instance; 023 private final ScriptEngine engine = Utils.getJavaScriptEngine(); 024 025 /** 026 * Replies the unique instance of this class. 027 * 028 * @return the unique instance of this class 029 */ 030 public static synchronized OverpassTurboQueryWizard getInstance() { 031 if (instance == null) { 032 instance = new OverpassTurboQueryWizard(); 033 } 034 return instance; 035 } 036 037 private OverpassTurboQueryWizard() { 038 try (CachedFile file = new CachedFile("resource://data/overpass-wizard.js"); 039 Reader reader = file.getContentReader()) { 040 if (engine != null) { 041 engine.eval("var console = {error: " + Logging.class.getCanonicalName() + ".warn};"); 042 engine.eval("var global = {};"); 043 engine.eval(reader); 044 engine.eval("var overpassWizard = function(query) {" + 045 " return global.overpassWizard(query, {" + 046 " comment: false," + 047 " outputFormat: 'xml'," + 048 " outputMode: 'recursive_meta'" + 049 " });" + 050 "}"); 051 } 052 } catch (ScriptException | IOException ex) { 053 throw new IllegalStateException("Failed to initialize OverpassTurboQueryWizard", ex); 054 } 055 } 056 057 /** 058 * Builds an Overpass QL from a {@link org.openstreetmap.josm.actions.search.SearchAction} like query. 059 * @param search the {@link org.openstreetmap.josm.actions.search.SearchAction} like query 060 * @return an Overpass QL query 061 * @throws UncheckedParseException when the parsing fails 062 */ 063 public String constructQuery(String search) { 064 if (engine == null) { 065 throw new IllegalStateException("Failed to retrieve JavaScript engine"); 066 } 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}