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

Last change on this file since 13198 was 13173, checked in by Don-vip, 6 years ago

see #15310 - remove most of deprecated APIs

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