source: josm/trunk/src/org/openstreetmap/josm/gui/io/CustomConfigurator.java@ 14624

Last change on this file since 14624 was 14624, checked in by Don-vip, 5 years ago

fix various SonarQube issues

  • Property svn:eol-style set to native
File size: 30.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.io;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.ByteArrayInputStream;
7import java.io.CharArrayReader;
8import java.io.CharArrayWriter;
9import java.io.File;
10import java.io.IOException;
11import java.io.InputStream;
12import java.nio.charset.StandardCharsets;
13import java.nio.file.Files;
14import java.nio.file.InvalidPathException;
15import java.util.ArrayList;
16import java.util.Collection;
17import java.util.Collections;
18import java.util.HashMap;
19import java.util.HashSet;
20import java.util.List;
21import java.util.Locale;
22import java.util.Map;
23import java.util.Set;
24import java.util.regex.Matcher;
25import java.util.regex.Pattern;
26
27import javax.script.ScriptEngine;
28import javax.script.ScriptException;
29import javax.swing.JOptionPane;
30import javax.swing.SwingUtilities;
31import javax.xml.parsers.DocumentBuilder;
32import javax.xml.parsers.ParserConfigurationException;
33import javax.xml.stream.XMLStreamException;
34import javax.xml.transform.OutputKeys;
35import javax.xml.transform.Transformer;
36import javax.xml.transform.TransformerException;
37import javax.xml.transform.TransformerFactoryConfigurationError;
38import javax.xml.transform.dom.DOMSource;
39import javax.xml.transform.stream.StreamResult;
40
41import org.openstreetmap.josm.data.Preferences;
42import org.openstreetmap.josm.data.PreferencesUtils;
43import org.openstreetmap.josm.data.Version;
44import org.openstreetmap.josm.gui.MainApplication;
45import org.openstreetmap.josm.gui.MainFrame;
46import org.openstreetmap.josm.plugins.PluginDownloadTask;
47import org.openstreetmap.josm.plugins.PluginInformation;
48import org.openstreetmap.josm.plugins.ReadLocalPluginInformationTask;
49import org.openstreetmap.josm.spi.preferences.Config;
50import org.openstreetmap.josm.spi.preferences.Setting;
51import org.openstreetmap.josm.tools.LanguageInfo;
52import org.openstreetmap.josm.tools.Logging;
53import org.openstreetmap.josm.tools.Utils;
54import org.openstreetmap.josm.tools.XmlUtils;
55import org.w3c.dom.DOMException;
56import org.w3c.dom.Document;
57import org.w3c.dom.Element;
58import org.w3c.dom.Node;
59import org.w3c.dom.NodeList;
60import org.xml.sax.SAXException;
61
62/**
63 * Class to process configuration changes stored in XML
64 * can be used to modify preferences, store/delete files in .josm folders etc
65 */
66public final class CustomConfigurator {
67
68 private CustomConfigurator() {
69 // Hide default constructor for utils classes
70 }
71
72 /**
73 * Read configuration script from XML file, modifying main preferences
74 * @param dir - directory
75 * @param fileName - XML file name
76 */
77 public static void readXML(String dir, String fileName) {
78 readXML(new File(dir, fileName));
79 }
80
81 /**
82 * Read configuration script from XML file, modifying given preferences object
83 * @param file - file to open for reading XML
84 * @param prefs - arbitrary Preferences object to modify by script
85 */
86 public static void readXML(final File file, final Preferences prefs) {
87 synchronized (CustomConfigurator.class) {
88 busy = true;
89 }
90 new XMLCommandProcessor(prefs).openAndReadXML(file);
91 synchronized (CustomConfigurator.class) {
92 CustomConfigurator.class.notifyAll();
93 busy = false;
94 }
95 }
96
97 /**
98 * Read configuration script from XML file, modifying main preferences
99 * @param file - file to open for reading XML
100 */
101 public static void readXML(File file) {
102 readXML(file, Preferences.main());
103 }
104
105 /**
106 * Downloads file to one of JOSM standard folders
107 * @param address - URL to download
108 * @param path - file path relative to base where to put downloaded file
109 * @param base - only "prefs", "cache" and "plugins" allowed for standard folders
110 */
111 public static void downloadFile(String address, String path, String base) {
112 processDownloadOperation(address, path, getDirectoryByAbbr(base), true, false);
113 }
114
115 /**
116 * Downloads file to one of JOSM standard folders and unpack it as ZIP/JAR file
117 * @param address - URL to download
118 * @param path - file path relative to base where to put downloaded file
119 * @param base - only "prefs", "cache" and "plugins" allowed for standard folders
120 */
121 public static void downloadAndUnpackFile(String address, String path, String base) {
122 processDownloadOperation(address, path, getDirectoryByAbbr(base), true, true);
123 }
124
125 /**
126 * Downloads file to arbitrary folder
127 * @param address - URL to download
128 * @param path - file path relative to parentDir where to put downloaded file
129 * @param parentDir - folder where to put file
130 * @param mkdir - if true, non-existing directories will be created
131 * @param unzip - if true file wil be unzipped and deleted after download
132 */
133 public static void processDownloadOperation(String address, String path, String parentDir, boolean mkdir, boolean unzip) {
134 String dir = parentDir;
135 if (path.contains("..") || path.startsWith("/") || path.contains(":")) {
136 return; // some basic protection
137 }
138 File fOut = new File(dir, path);
139 DownloadFileTask downloadFileTask = new DownloadFileTask(MainApplication.getMainFrame(), address, fOut, mkdir, unzip);
140
141 MainApplication.worker.submit(downloadFileTask);
142 PreferencesUtils.log("Info: downloading file from %s to %s in background ", parentDir, fOut.getAbsolutePath());
143 if (unzip) PreferencesUtils.log("and unpacking it"); else PreferencesUtils.log("");
144 }
145
146 /**
147 * Simple function to show messageBox, may be used from JS API and from other code
148 * @param type - 'i','w','e','q','p' for Information, Warning, Error, Question, Message
149 * @param text - message to display, HTML allowed
150 */
151 public static void messageBox(String type, String text) {
152 char c = (type == null || type.isEmpty() ? "plain" : type).charAt(0);
153 MainFrame parent = MainApplication.getMainFrame();
154 switch (c) {
155 case 'i': JOptionPane.showMessageDialog(parent, text, tr("Information"), JOptionPane.INFORMATION_MESSAGE); break;
156 case 'w': JOptionPane.showMessageDialog(parent, text, tr("Warning"), JOptionPane.WARNING_MESSAGE); break;
157 case 'e': JOptionPane.showMessageDialog(parent, text, tr("Error"), JOptionPane.ERROR_MESSAGE); break;
158 case 'q': JOptionPane.showMessageDialog(parent, text, tr("Question"), JOptionPane.QUESTION_MESSAGE); break;
159 case 'p': JOptionPane.showMessageDialog(parent, text, tr("Message"), JOptionPane.PLAIN_MESSAGE); break;
160 default: Logging.warn("Unsupported messageBox type: " + c);
161 }
162 }
163
164 /**
165 * Simple function for choose window, may be used from JS API and from other code
166 * @param text - message to show, HTML allowed
167 * @param opts -
168 * @return number of pressed button, -1 if cancelled
169 */
170 public static int askForOption(String text, String opts) {
171 if (!opts.isEmpty()) {
172 return JOptionPane.showOptionDialog(MainApplication.getMainFrame(), text, "Question",
173 JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, opts.split(";"), 0);
174 } else {
175 return JOptionPane.showOptionDialog(MainApplication.getMainFrame(), text, "Question",
176 JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, 2);
177 }
178 }
179
180 public static String askForText(String text) {
181 String s = JOptionPane.showInputDialog(MainApplication.getMainFrame(), text, tr("Enter text"), JOptionPane.QUESTION_MESSAGE);
182 return s != null ? s.trim() : null;
183 }
184
185 /**
186 * This function exports part of user preferences to specified file.
187 * Default values are not saved.
188 * @param filename - where to export
189 * @param append - if true, resulting file cause appending to exuisting preferences
190 * @param keys - which preferences keys you need to export ("imagery.entries", for example)
191 */
192 public static void exportPreferencesKeysToFile(String filename, boolean append, String... keys) {
193 Set<String> keySet = new HashSet<>();
194 Collections.addAll(keySet, keys);
195 exportPreferencesKeysToFile(filename, append, keySet);
196 }
197
198 /**
199 * This function exports part of user preferences to specified file.
200 * Default values are not saved.
201 * Preference keys matching specified pattern are saved
202 * @param fileName - where to export
203 * @param append - if true, resulting file cause appending to exuisting preferences
204 * @param pattern - Regexp pattern forh preferences keys you need to export (".*imagery.*", for example)
205 */
206 public static void exportPreferencesKeysByPatternToFile(String fileName, boolean append, String pattern) {
207 List<String> keySet = new ArrayList<>();
208 Map<String, Setting<?>> allSettings = Preferences.main().getAllSettings();
209 for (String key: allSettings.keySet()) {
210 if (key.matches(pattern))
211 keySet.add(key);
212 }
213 exportPreferencesKeysToFile(fileName, append, keySet);
214 }
215
216 /**
217 * Export specified preferences keys to configuration file
218 * @param filename - name of file
219 * @param append - will the preferences be appended to existing ones when file is imported later.
220 * Elsewhere preferences from file will replace existing keys.
221 * @param keys - collection of preferences key names to save
222 */
223 public static void exportPreferencesKeysToFile(String filename, boolean append, Collection<String> keys) {
224 Element root = null;
225 Document document = null;
226 Document exportDocument = null;
227
228 try {
229 String toXML = Preferences.main().toXML(true);
230 DocumentBuilder builder = XmlUtils.newSafeDOMBuilder();
231 document = builder.parse(new ByteArrayInputStream(toXML.getBytes(StandardCharsets.UTF_8)));
232 exportDocument = builder.newDocument();
233 root = document.getDocumentElement();
234 } catch (SAXException | IOException | ParserConfigurationException ex) {
235 Logging.log(Logging.LEVEL_WARN, "Error getting preferences to save:", ex);
236 }
237 if (root == null || exportDocument == null)
238 return;
239 try {
240 Element newRoot = exportDocument.createElement("config");
241 exportDocument.appendChild(newRoot);
242
243 Element prefElem = exportDocument.createElement("preferences");
244 prefElem.setAttribute("operation", append ? "append" : "replace");
245 newRoot.appendChild(prefElem);
246
247 NodeList childNodes = root.getChildNodes();
248 int n = childNodes.getLength();
249 for (int i = 0; i < n; i++) {
250 Node item = childNodes.item(i);
251 if (item.getNodeType() == Node.ELEMENT_NODE) {
252 String currentKey = ((Element) item).getAttribute("key");
253 if (keys.contains(currentKey)) {
254 Node imported = exportDocument.importNode(item, true);
255 prefElem.appendChild(imported);
256 }
257 }
258 }
259 File f = new File(filename);
260 Transformer ts = XmlUtils.newSafeTransformerFactory().newTransformer();
261 ts.setOutputProperty(OutputKeys.INDENT, "yes");
262 ts.transform(new DOMSource(exportDocument), new StreamResult(f.toURI().getPath()));
263 } catch (DOMException | TransformerFactoryConfigurationError | TransformerException ex) {
264 Logging.warn("Error saving preferences part:");
265 Logging.error(ex);
266 }
267 }
268
269 public static void deleteFile(String path, String base) {
270 String dir = getDirectoryByAbbr(base);
271 if (dir == null) {
272 PreferencesUtils.log("Error: Can not find base, use base=cache, base=prefs or base=plugins attribute.");
273 return;
274 }
275 PreferencesUtils.log("Delete file: %s\n", path);
276 if (path.contains("..") || path.startsWith("/") || path.contains(":")) {
277 return; // some basic protection
278 }
279 File fOut = new File(dir, path);
280 if (fOut.exists()) {
281 deleteFileOrDirectory(fOut);
282 }
283 }
284
285 public static void deleteFileOrDirectory(File f) {
286 if (f.isDirectory()) {
287 File[] files = f.listFiles();
288 if (files != null) {
289 for (File f1: files) {
290 deleteFileOrDirectory(f1);
291 }
292 }
293 }
294 if (!Utils.deleteFile(f)) {
295 PreferencesUtils.log("Warning: Can not delete file "+f.getPath());
296 }
297 }
298
299 private static boolean busy;
300
301 public static void pluginOperation(String install, String uninstall, String delete) {
302 final List<String> installList = new ArrayList<>();
303 final List<String> removeList = new ArrayList<>();
304 final List<String> deleteList = new ArrayList<>();
305 Collections.addAll(installList, install.toLowerCase(Locale.ENGLISH).split(";"));
306 Collections.addAll(removeList, uninstall.toLowerCase(Locale.ENGLISH).split(";"));
307 Collections.addAll(deleteList, delete.toLowerCase(Locale.ENGLISH).split(";"));
308 installList.remove("");
309 removeList.remove("");
310 deleteList.remove("");
311
312 if (!installList.isEmpty()) {
313 PreferencesUtils.log("Plugins install: "+installList);
314 }
315 if (!removeList.isEmpty()) {
316 PreferencesUtils.log("Plugins turn off: "+removeList);
317 }
318 if (!deleteList.isEmpty()) {
319 PreferencesUtils.log("Plugins delete: "+deleteList);
320 }
321
322 final ReadLocalPluginInformationTask task = new ReadLocalPluginInformationTask();
323 Runnable r = () -> {
324 if (task.isCanceled()) return;
325 synchronized (CustomConfigurator.class) {
326 try { // proceed only after all other tasks were finished
327 while (busy) CustomConfigurator.class.wait();
328 } catch (InterruptedException ex) {
329 Logging.log(Logging.LEVEL_WARN, "InterruptedException while reading local plugin information", ex);
330 Thread.currentThread().interrupt();
331 }
332
333 SwingUtilities.invokeLater(() -> {
334 List<PluginInformation> availablePlugins = task.getAvailablePlugins();
335 List<PluginInformation> toInstallPlugins = new ArrayList<>();
336 List<PluginInformation> toRemovePlugins = new ArrayList<>();
337 List<PluginInformation> toDeletePlugins = new ArrayList<>();
338 for (PluginInformation pi1: availablePlugins) {
339 String name = pi1.name.toLowerCase(Locale.ENGLISH);
340 if (installList.contains(name)) toInstallPlugins.add(pi1);
341 if (removeList.contains(name)) toRemovePlugins.add(pi1);
342 if (deleteList.contains(name)) toDeletePlugins.add(pi1);
343 }
344 if (!installList.isEmpty()) {
345 PluginDownloadTask pluginDownloadTask =
346 new PluginDownloadTask(MainApplication.getMainFrame(), toInstallPlugins, tr("Installing plugins"));
347 MainApplication.worker.submit(pluginDownloadTask);
348 }
349 List<String> pls = new ArrayList<>(Config.getPref().getList("plugins"));
350 for (PluginInformation pi2: toInstallPlugins) {
351 if (!pls.contains(pi2.name)) {
352 pls.add(pi2.name);
353 }
354 }
355 for (PluginInformation pi3: toRemovePlugins) {
356 pls.remove(pi3.name);
357 }
358 for (PluginInformation pi4: toDeletePlugins) {
359 pls.remove(pi4.name);
360 Utils.deleteFile(new File(Preferences.main().getPluginsDirectory(), pi4.name+".jar"));
361 }
362 Config.getPref().putList("plugins", pls);
363 });
364 }
365 };
366 MainApplication.worker.submit(task);
367 MainApplication.worker.submit(r);
368 }
369
370 private static String getDirectoryByAbbr(String base) {
371 String dir;
372 if ("prefs".equals(base) || base.isEmpty()) {
373 dir = Config.getDirs().getPreferencesDirectory(false).getAbsolutePath();
374 } else if ("cache".equals(base)) {
375 dir = Config.getDirs().getCacheDirectory(false).getAbsolutePath();
376 } else if ("plugins".equals(base)) {
377 dir = Preferences.main().getPluginsDirectory().getAbsolutePath();
378 } else {
379 dir = null;
380 }
381 return dir;
382 }
383
384 public static class XMLCommandProcessor {
385
386 private Preferences mainPrefs;
387 private final Map<String, Element> tasksMap = new HashMap<>();
388
389 private boolean lastV; // last If condition result
390
391 private ScriptEngine engine;
392
393 public void openAndReadXML(File file) {
394 PreferencesUtils.log("-- Reading custom preferences from " + file.getAbsolutePath() + " --");
395 try {
396 String fileDir = file.getParentFile().getAbsolutePath();
397 if (fileDir != null) engine.eval("scriptDir='"+normalizeDirName(fileDir) +"';");
398 try (InputStream is = Files.newInputStream(file.toPath())) {
399 openAndReadXML(is);
400 }
401 } catch (ScriptException | IOException | SecurityException | InvalidPathException ex) {
402 PreferencesUtils.log(ex, "Error reading custom preferences:");
403 }
404 }
405
406 public void openAndReadXML(InputStream is) {
407 try {
408 Document document = XmlUtils.parseSafeDOM(is);
409 synchronized (CustomConfigurator.class) {
410 processXML(document);
411 }
412 } catch (SAXException | IOException | ParserConfigurationException ex) {
413 PreferencesUtils.log(ex, "Error reading custom preferences:");
414 }
415 PreferencesUtils.log("-- Reading complete --");
416 }
417
418 public XMLCommandProcessor(Preferences mainPrefs) {
419 try {
420 this.mainPrefs = mainPrefs;
421 PreferencesUtils.resetLog();
422 engine = Utils.getJavaScriptEngine();
423 if (engine == null) {
424 throw new ScriptException("Failed to retrieve JavaScript engine");
425 }
426 engine.eval("API={}; API.pref={}; API.fragments={};");
427
428 engine.eval("homeDir='"+normalizeDirName(Config.getDirs().getPreferencesDirectory(false).getAbsolutePath()) +"';");
429 engine.eval("josmVersion="+Version.getInstance().getVersion()+';');
430 String className = CustomConfigurator.class.getName();
431 engine.eval("API.messageBox="+className+".messageBox");
432 engine.eval("API.askText=function(text) { return String("+className+".askForText(text));}");
433 engine.eval("API.askOption="+className+".askForOption");
434 engine.eval("API.downloadFile="+className+".downloadFile");
435 engine.eval("API.downloadAndUnpackFile="+className+".downloadAndUnpackFile");
436 engine.eval("API.deleteFile="+className+".deleteFile");
437 engine.eval("API.plugin ="+className+".pluginOperation");
438 engine.eval("API.pluginInstall = function(names) { "+className+".pluginOperation(names,'','');}");
439 engine.eval("API.pluginUninstall = function(names) { "+className+".pluginOperation('',names,'');}");
440 engine.eval("API.pluginDelete = function(names) { "+className+".pluginOperation('','',names);}");
441 } catch (ScriptException ex) {
442 PreferencesUtils.log("Error: initializing script engine: "+ex.getMessage());
443 Logging.error(ex);
444 }
445 }
446
447 private void processXML(Document document) {
448 processXmlFragment(document.getDocumentElement());
449 }
450
451 private void processXmlFragment(Element root) {
452 NodeList childNodes = root.getChildNodes();
453 int nops = childNodes.getLength();
454 for (int i = 0; i < nops; i++) {
455 Node item = childNodes.item(i);
456 if (item.getNodeType() != Node.ELEMENT_NODE) continue;
457 String elementName = item.getNodeName();
458 Element elem = (Element) item;
459
460 switch(elementName) {
461 case "var":
462 setVar(elem.getAttribute("name"), evalVars(elem.getAttribute("value")));
463 break;
464 case "task":
465 tasksMap.put(elem.getAttribute("name"), elem);
466 break;
467 case "runtask":
468 if (processRunTaskElement(elem)) return;
469 break;
470 case "ask":
471 processAskElement(elem);
472 break;
473 case "if":
474 processIfElement(elem);
475 break;
476 case "else":
477 processElseElement(elem);
478 break;
479 case "break":
480 return;
481 case "plugin":
482 processPluginInstallElement(elem);
483 break;
484 case "messagebox":
485 processMsgBoxElement(elem);
486 break;
487 case "preferences":
488 processPreferencesElement(elem);
489 break;
490 case "download":
491 processDownloadElement(elem);
492 break;
493 case "delete":
494 processDeleteElement(elem);
495 break;
496 case "script":
497 processScriptElement(elem);
498 break;
499 default:
500 PreferencesUtils.log("Error: Unknown element " + elementName);
501 }
502 }
503 }
504
505 private void processPreferencesElement(Element item) {
506 String oper = evalVars(item.getAttribute("operation"));
507 String id = evalVars(item.getAttribute("id"));
508
509 if ("delete-keys".equals(oper)) {
510 String pattern = evalVars(item.getAttribute("pattern"));
511 String key = evalVars(item.getAttribute("key"));
512 PreferencesUtils.deletePreferenceKey(key, mainPrefs);
513 PreferencesUtils.deletePreferenceKeyByPattern(pattern, mainPrefs);
514 return;
515 }
516
517 Preferences tmpPref = readPreferencesFromDOMElement(item);
518 PreferencesUtils.showPrefs(tmpPref);
519
520 if (!id.isEmpty()) {
521 try {
522 String fragmentVar = "API.fragments['"+id+"']";
523 engine.eval(fragmentVar+"={};");
524 PreferencesUtils.loadPrefsToJS(engine, tmpPref, fragmentVar, false);
525 // we store this fragment as API.fragments['id']
526 } catch (ScriptException ex) {
527 PreferencesUtils.log(ex, "Error: can not load preferences fragment:");
528 }
529 }
530
531 if ("replace".equals(oper)) {
532 PreferencesUtils.log("Preferences replace: %d keys: %s\n",
533 tmpPref.getAllSettings().size(), tmpPref.getAllSettings().keySet().toString());
534 PreferencesUtils.replacePreferences(tmpPref, mainPrefs);
535 } else if ("append".equals(oper)) {
536 PreferencesUtils.log("Preferences append: %d keys: %s\n",
537 tmpPref.getAllSettings().size(), tmpPref.getAllSettings().keySet().toString());
538 PreferencesUtils.appendPreferences(tmpPref, mainPrefs);
539 } else if ("delete-values".equals(oper)) {
540 PreferencesUtils.deletePreferenceValues(tmpPref, mainPrefs);
541 }
542 }
543
544 private void processDeleteElement(Element item) {
545 String path = evalVars(item.getAttribute("path"));
546 String base = evalVars(item.getAttribute("base"));
547 deleteFile(path, base);
548 }
549
550 private void processDownloadElement(Element item) {
551 String base = evalVars(item.getAttribute("base"));
552 String dir = getDirectoryByAbbr(base);
553 if (dir == null) {
554 PreferencesUtils.log("Error: Can not find directory to place file, use base=cache, base=prefs or base=plugins attribute.");
555 return;
556 }
557
558 String path = evalVars(item.getAttribute("path"));
559 if (path.contains("..") || path.startsWith("/") || path.contains(":")) {
560 return; // some basic protection
561 }
562
563 String address = evalVars(item.getAttribute("url"));
564 if (address.isEmpty() || path.isEmpty()) {
565 PreferencesUtils.log("Error: Please specify url=\"where to get file\" and path=\"where to place it\"");
566 return;
567 }
568
569 String unzip = evalVars(item.getAttribute("unzip"));
570 String mkdir = evalVars(item.getAttribute("mkdir"));
571 processDownloadOperation(address, path, dir, "true".equals(mkdir), "true".equals(unzip));
572 }
573
574 private static void processPluginInstallElement(Element elem) {
575 String install = elem.getAttribute("install");
576 String uninstall = elem.getAttribute("remove");
577 String delete = elem.getAttribute("delete");
578 pluginOperation(install, uninstall, delete);
579 }
580
581 private void processMsgBoxElement(Element elem) {
582 String text = evalVars(elem.getAttribute("text"));
583 String locText = evalVars(elem.getAttribute(LanguageInfo.getJOSMLocaleCode()+".text"));
584 if (!locText.isEmpty()) text = locText;
585
586 String type = evalVars(elem.getAttribute("type"));
587 messageBox(type, text);
588 }
589
590 private void processAskElement(Element elem) {
591 String text = evalVars(elem.getAttribute("text"));
592 String locText = evalVars(elem.getAttribute(LanguageInfo.getJOSMLocaleCode()+".text"));
593 if (!locText.isEmpty()) text = locText;
594 String var = elem.getAttribute("var");
595 if (var.isEmpty()) var = "result";
596
597 String input = evalVars(elem.getAttribute("input"));
598 if ("true".equals(input)) {
599 setVar(var, askForText(text));
600 } else {
601 String opts = evalVars(elem.getAttribute("options"));
602 String locOpts = evalVars(elem.getAttribute(LanguageInfo.getJOSMLocaleCode()+".options"));
603 if (!locOpts.isEmpty()) opts = locOpts;
604 setVar(var, String.valueOf(askForOption(text, opts)));
605 }
606 }
607
608 public void setVar(String name, String value) {
609 try {
610 engine.eval(name+"='"+value+"';");
611 } catch (ScriptException ex) {
612 PreferencesUtils.log(ex, String.format("Error: Can not assign variable: %s=%s :", name, value));
613 }
614 }
615
616 private void processIfElement(Element elem) {
617 String realValue = evalVars(elem.getAttribute("test"));
618 boolean v = false;
619 if ("true".equals(realValue) || "false".equals(realValue)) {
620 processXmlFragment(elem);
621 v = true;
622 } else {
623 PreferencesUtils.log("Error: Illegal test expression in if: %s=%s\n", elem.getAttribute("test"), realValue);
624 }
625
626 lastV = v;
627 }
628
629 private void processElseElement(Element elem) {
630 if (!lastV) {
631 processXmlFragment(elem);
632 }
633 }
634
635 private boolean processRunTaskElement(Element elem) {
636 String taskName = elem.getAttribute("name");
637 Element task = tasksMap.get(taskName);
638 if (task != null) {
639 PreferencesUtils.log("EXECUTING TASK "+taskName);
640 processXmlFragment(task); // process task recursively
641 } else {
642 PreferencesUtils.log("Error: Can not execute task "+taskName);
643 return true;
644 }
645 return false;
646 }
647
648 private void processScriptElement(Element elem) {
649 String js = elem.getChildNodes().item(0).getTextContent();
650 PreferencesUtils.log("Processing script...");
651 try {
652 PreferencesUtils.modifyPreferencesByScript(engine, mainPrefs, js);
653 } catch (ScriptException ex) {
654 messageBox("e", ex.getMessage());
655 PreferencesUtils.log(ex, "JS error:");
656 }
657 PreferencesUtils.log("Script finished");
658 }
659
660 /**
661 * substitute ${expression} = expression evaluated by JavaScript
662 * @param s string
663 * @return evaluation result
664 */
665 private String evalVars(String s) {
666 Matcher mr = Pattern.compile("\\$\\{([^\\}]*)\\}").matcher(s);
667 StringBuffer sb = new StringBuffer();
668 while (mr.find()) {
669 try {
670 String result = engine.eval(mr.group(1)).toString();
671 mr.appendReplacement(sb, result);
672 } catch (ScriptException ex) {
673 PreferencesUtils.log(ex, String.format("Error: Can not evaluate expression %s :", mr.group(1)));
674 }
675 }
676 mr.appendTail(sb);
677 return sb.toString();
678 }
679
680 private Preferences readPreferencesFromDOMElement(Element item) {
681 Preferences tmpPref = new Preferences();
682 try {
683 Transformer xformer = XmlUtils.newSafeTransformerFactory().newTransformer();
684 CharArrayWriter outputWriter = new CharArrayWriter(8192);
685 StreamResult out = new StreamResult(outputWriter);
686
687 xformer.transform(new DOMSource(item), out);
688
689 String fragmentWithReplacedVars = evalVars(outputWriter.toString());
690
691 CharArrayReader reader = new CharArrayReader(fragmentWithReplacedVars.toCharArray());
692 tmpPref.fromXML(reader);
693 } catch (TransformerException | XMLStreamException | IOException ex) {
694 PreferencesUtils.log(ex, "Error: can not read XML fragment:");
695 }
696
697 return tmpPref;
698 }
699
700 private static String normalizeDirName(String dir) {
701 String s = dir.replace('\\', '/');
702 if (s.endsWith("/")) s = s.substring(0, s.length()-1);
703 return s;
704 }
705 }
706}
Note: See TracBrowser for help on using the repository browser.