001package jmri.configurexml; 002 003import java.io.File; 004import java.net.URL; 005import java.util.ArrayList; 006import java.util.Collections; 007import java.util.LinkedHashMap; 008import java.util.List; 009import java.util.Map; 010import jmri.InstanceManager; 011import jmri.jmrit.XmlFile; 012import jmri.jmrit.revhistory.FileHistory; 013import jmri.util.FileUtil; 014import org.jdom2.Attribute; 015import org.jdom2.Document; 016import org.jdom2.Element; 017import org.jdom2.ProcessingInstruction; 018import org.slf4j.Logger; 019import org.slf4j.LoggerFactory; 020 021/** 022 * Provides the mechanisms for storing an entire layout configuration to XML. 023 * "Layout" refers to the hardware: Specific communication systems, etc. 024 * 025 * @see <a href="package-summary.html">Package summary for details of the 026 * overall structure</a> 027 * @author Bob Jacobsen Copyright (c) 2002, 2008 028 */ 029public class ConfigXmlManager extends jmri.jmrit.XmlFile 030 implements jmri.ConfigureManager { 031 032 /** 033 * Define the current schema version string for the layout-config schema. 034 * See the <a href="package-summary.html#schema">Schema versioning 035 * discussion</a>. Also controls the stylesheet file version. 036 */ 037 static final public String schemaVersion = "-5-5-5"; 038 039 public ConfigXmlManager() { 040 } 041 042 /** {@inheritDoc} */ 043 @Override 044 public void registerConfig(Object o) { 045 registerConfig(o, 50); 046 } 047 048 /** {@inheritDoc} */ 049 @Override 050 public void registerPref(Object o) { 051 // skip if already present, leaving in original order 052 if (plist.contains(o)) { 053 return; 054 } 055 confirmAdapterAvailable(o); 056 // and add to list 057 plist.add(o); 058 } 059 060 /** 061 * Common check routine to confirm an adapter is available as part of 062 * registration process. 063 * <p> 064 * Note: Should only be called for debugging purposes, for example, when 065 * Log4J DEBUG level is selected, to load fewer classes at startup. 066 * 067 * @param o object to confirm XML adapter exists for 068 */ 069 void confirmAdapterAvailable(Object o) { 070 if (log.isDebugEnabled()) { 071 String adapter = adapterName(o); 072 log.debug("register {} adapter {}", o, adapter); 073 if (adapter != null) { 074 try { 075 Class.forName(adapter); 076 } catch (ClassNotFoundException | NoClassDefFoundError ex) { 077 locateClassFailed(ex, adapter, o); 078 } 079 } 080 } 081 } 082 083 /** 084 * Handles ConfigureXml classes that have moved to a new package or been 085 * superseded. 086 * 087 * @param name name of the moved or superceded ConfigureXml class 088 * @return name of the ConfigureXml class in newer package or of superseding 089 * class 090 */ 091 static public String currentClassName(String name) { 092 return InstanceManager.getDefault(ClassMigrationManager.class).getClassName(name); 093 } 094 095 /** {@inheritDoc} */ 096 @Override 097 public void removePrefItems() { 098 log.debug("removePrefItems dropped {}", plist.size()); 099 plist.clear(); 100 } 101 102 /** {@inheritDoc} */ 103 @Override 104 public Object findInstance(Class<?> c, int index) { 105 List<Object> temp = new ArrayList<>(plist); 106 temp.addAll(clist.keySet()); 107 temp.addAll(tlist); 108 temp.addAll(ulist); 109 temp.addAll(uplist); 110 for (Object o : temp) { 111 if (c.isInstance(o)) { 112 if (index-- == 0) { 113 return o; 114 } 115 } 116 } 117 return null; 118 } 119 120 /** {@inheritDoc} */ 121 @Override 122 public List<Object> getInstanceList(Class<?> c) { 123 List<Object> result = new ArrayList<>(); 124 125 List<Object> temp = new ArrayList<>(plist); 126 temp.addAll(clist.keySet()); 127 temp.addAll(tlist); 128 temp.addAll(ulist); 129 temp.addAll(uplist); 130 for (Object o : temp) { 131 if (c.isInstance(o)) { 132 result.add(o); 133 } 134 } 135 return result; 136 } 137 138 /** {@inheritDoc} */ 139 @Override 140 public void registerConfig(Object o, int x) { 141 // skip if already present, leaving in original order 142 if (clist.containsKey(o)) { 143 return; 144 } 145 confirmAdapterAvailable(o); 146 // and add to list 147 clist.put(o, x); 148 } 149 150 /** {@inheritDoc} */ 151 @Override 152 public void registerTool(Object o) { 153 // skip if already present, leaving in original order 154 if (tlist.contains(o)) { 155 return; 156 } 157 confirmAdapterAvailable(o); 158 // and add to list 159 tlist.add(o); 160 } 161 162 /** 163 * Register an object whose state is to be tracked. It is not an error if 164 * the original object was already registered. 165 * 166 * @param o The object, which must have an associated adapter class. 167 */ 168 @Override 169 public void registerUser(Object o) { 170 // skip if already present, leaving in original order 171 if (ulist.contains(o)) { 172 return; 173 } 174 confirmAdapterAvailable(o); 175 // and add to list 176 ulist.add(o); 177 } 178 179 /** {@inheritDoc} */ 180 @Override 181 public void registerUserPrefs(Object o) { 182 // skip if already present, leaving in original order 183 if (uplist.contains(o)) { 184 return; 185 } 186 confirmAdapterAvailable(o); 187 // and add to list 188 uplist.add(o); 189 } 190 191 /** {@inheritDoc} */ 192 @Override 193 public void deregister(Object o) { 194 plist.remove(o); 195 if (o != null) { 196 clist.remove(o); 197 } 198 tlist.remove(o); 199 ulist.remove(o); 200 uplist.remove(o); 201 } 202 203 private List<Object> plist = new ArrayList<>(); 204 Map<Object, Integer> clist = Collections.synchronizedMap(new LinkedHashMap<>()); 205 private List<Object> tlist = new ArrayList<>(); 206 private List<Object> ulist = new ArrayList<>(); 207 private List<Object> uplist = new ArrayList<>(); 208 private final List<Element> loadDeferredList = new ArrayList<>(); 209 210 /** 211 * Find the name of the adapter class for an object. 212 * 213 * @param o object of a configurable type 214 * @return class name of adapter 215 */ 216 public static String adapterName(Object o) { 217 String className = o.getClass().getName(); 218 log.trace("handle object of class {}", className); 219 int lastDot = className.lastIndexOf("."); 220 if (lastDot > 0) { 221 // found package-class boundary OK 222 String result = className.substring(0, lastDot) 223 + ".configurexml." 224 + className.substring(lastDot + 1, className.length()) 225 + "Xml"; 226 log.trace("adapter class name is {}", result); 227 return result; 228 } else { 229 // no last dot found! 230 log.error("No package name found, which is not yet handled!"); 231 return null; 232 } 233 } 234 235 /** 236 * Handle failure to load adapter class. Although only a one-liner in this 237 * class, it is a separate member to facilitate testing. 238 * 239 * @param ex the exception throw failing to load adapterName as o 240 * @param adapterName name of the adapter class 241 * @param o adapter object 242 */ 243 void locateClassFailed(Throwable ex, String adapterName, Object o) { 244 log.error("{} could not load adapter class {}", ex, adapterName); 245 log.debug("Stack trace is", ex); 246 } 247 248 protected Element initStore() { 249 Element root = new Element("layout-config"); 250 root.setAttribute("noNamespaceSchemaLocation", 251 "http://jmri.org/xml/schema/layout" + schemaVersion + ".xsd", 252 org.jdom2.Namespace.getNamespace("xsi", 253 "http://www.w3.org/2001/XMLSchema-instance")); 254 return root; 255 } 256 257 protected void addPrefsStore(Element root) { 258 for (int i = 0; i < plist.size(); i++) { 259 Object o = plist.get(i); 260 Element e = elementFromObject(o); 261 if (e != null) { 262 root.addContent(e); 263 } 264 } 265 } 266 267 protected boolean addConfigStore(Element root) { 268 boolean result = true; 269 List<Map.Entry<Object, Integer>> l = new ArrayList<>(clist.entrySet()); 270 Collections.sort(l, (Map.Entry<Object, Integer> o1, Map.Entry<Object, Integer> o2) -> o1.getValue().compareTo(o2.getValue())); 271 for (int i = 0; i < l.size(); i++) { 272 try { 273 Object o = l.get(i).getKey(); 274 Element e = elementFromObject(o); 275 if (e != null) { 276 root.addContent(e); 277 } 278 } catch (Exception e) { 279 storingErrorEncountered(null, "storing to file in addConfigStore", 280 "Exception thrown", null, null, e); 281 result = false; 282 } 283 } 284 return result; 285 } 286 287 protected boolean addToolsStore(Element root) { 288 boolean result = true; 289 for (Object o : tlist) { 290 try { 291 Element e = elementFromObject(o); 292 if (e != null) { 293 root.addContent(e); 294 } 295 } catch (Exception e) { 296 result = false; 297 storingErrorEncountered(null, "storing to file in addToolsStore", 298 "Exception thrown", null, null, e); 299 } 300 } 301 return result; 302 } 303 304 protected boolean addUserStore(Element root) { 305 boolean result = true; 306 for (Object o : ulist) { 307 try { 308 Element e = elementFromObject(o); 309 if (e != null) { 310 root.addContent(e); 311 } 312 } catch (Exception e) { 313 result = false; 314 storingErrorEncountered(null, "storing to file in addUserStore", 315 "Exception thrown", null, null, e); 316 } 317 } 318 return result; 319 } 320 321 protected void addUserPrefsStore(Element root) { 322 for (Object o : uplist) { 323 Element e = elementFromObject(o); 324 if (e != null) { 325 root.addContent(e); 326 } 327 } 328 } 329 330 protected void includeHistory(Element root, File file) { 331 // add history to end of document 332 if (InstanceManager.getNullableDefault(FileHistory.class) != null) { 333 var historyElement = jmri.jmrit.revhistory.configurexml.FileHistoryXml.storeDirectly( 334 InstanceManager.getDefault(FileHistory.class), file.getPath()); 335 if (historyElement != null) { 336 root.addContent(historyElement); 337 } 338 } 339 } 340 341 protected boolean finalStore(Element root, File file) { 342 try { 343 // Document doc = newDocument(root, dtdLocation+"layout-config-"+dtdVersion+".dtd"); 344 Document doc = newDocument(root); 345 346 // add XSLT processing instruction 347 // <?xml-stylesheet type="text/xsl" href="XSLT/panelfile"+schemaVersion+".xsl"?> 348 java.util.Map<String, String> m = new java.util.HashMap<>(); 349 m.put("type", "text/xsl"); 350 m.put("href", xsltLocation + "panelfile" + schemaVersion + ".xsl"); 351 ProcessingInstruction p = new ProcessingInstruction("xml-stylesheet", m); 352 doc.addContent(0, p); 353 354 // add version at front 355 storeVersion(root); 356 357 writeXML(file, doc); 358 } catch (java.io.FileNotFoundException ex3) { 359 storingErrorEncountered(null, "storing to file " + file.getName(), 360 "File not found " + file.getName(), null, null, ex3); 361 log.error("FileNotFound error writing file: {}", ex3.getLocalizedMessage()); 362 return false; 363 } catch (java.io.IOException ex2) { 364 storingErrorEncountered(null, "storing to file " + file.getName(), 365 "IO error writing file " + file.getName(), null, null, ex2); 366 log.error("IO error writing file: {}", ex2.getLocalizedMessage()); 367 return false; 368 } 369 return true; 370 } 371 372 /** {@inheritDoc} */ 373 @Override 374 public void storePrefs() { 375 storePrefs(prefsFile); 376 } 377 378 /** {@inheritDoc} */ 379 @Override 380 public void storePrefs(File file) { 381 synchronized (this) { 382 Element root = initStore(); 383 addPrefsStore(root); 384 finalStore(root, file); 385 } 386 } 387 388 /** {@inheritDoc} */ 389 @Override 390 public void storeUserPrefs(File file) { 391 synchronized (this) { 392 Element root = initStore(); 393 addUserPrefsStore(root); 394 finalStore(root, file); 395 } 396 } 397 398 /** 399 * Set location for preferences file. 400 * <p> 401 * File need not exist, but location must be writable when storePrefs() 402 * called. 403 * 404 * @param prefsFile new location for preferences file 405 */ 406 public void setPrefsLocation(File prefsFile) { 407 this.prefsFile = prefsFile; 408 } 409 File prefsFile; 410 411 /** {@inheritDoc} */ 412 @Override 413 public boolean storeConfig(File file) { 414 boolean result = true; 415 Element root = initStore(); 416 if (!addConfigStore(root)) { 417 result = false; 418 } 419 includeHistory(root, file); 420 if (!finalStore(root, file)) { 421 result = false; 422 } 423 return result; 424 } 425 426 /** {@inheritDoc} */ 427 @Override 428 public boolean storeUser(File file) { 429 boolean result = true; 430 Element root = initStore(); 431 if (!addConfigStore(root)) { 432 result = false; 433 } 434 if (!addUserStore(root)) { 435 result = false; 436 } 437 includeHistory(root, file); 438 if (!finalStore(root, file)) { 439 result = false; 440 } 441 return result; 442 } 443 444 /** {@inheritDoc} */ 445 @Override 446 public boolean makeBackup(File file) { 447 return makeBackupFile(FileUtil.getUserFilesPath() + "backupPanels", file); 448 } 449 450 /** 451 * 452 * @param o The object to get an XML representation of 453 * @return An XML element representing o 454 */ 455 static public Element elementFromObject(Object o) { 456 return ConfigXmlManager.elementFromObject(o, true); 457 } 458 459 /** 460 * 461 * @param object The object to get an XML representation of 462 * @param shared true if the XML should be shared, false if the XML should 463 * be per-node 464 * @return An XML element representing object 465 */ 466 static public Element elementFromObject(Object object, boolean shared) { 467 String aName = adapterName(object); 468 log.debug("store using {}", aName); 469 XmlAdapter adapter = null; 470 try { 471 adapter = (XmlAdapter) Class.forName(adapterName(object)).getDeclaredConstructor().newInstance(); 472 } catch (ClassNotFoundException | IllegalAccessException | InstantiationException 473 | NoSuchMethodException | java.lang.reflect.InvocationTargetException ex) { 474 log.error("Cannot load configuration adapter for {}", object.getClass().getName(), ex); 475 } 476 if (adapter != null) { 477 return adapter.store(object, shared); 478 } else { 479 log.error("Cannot store configuration for {}", object.getClass().getName()); 480 return null; 481 } 482 } 483 484 private void storeVersion(Element root) { 485 // add version at front 486 root.addContent(0, 487 new Element("jmriversion") 488 .addContent(new Element("major").addContent("" + jmri.Version.major)) 489 .addContent(new Element("minor").addContent("" + jmri.Version.minor)) 490 .addContent(new Element("test").addContent("" + jmri.Version.test)) 491 .addContent(new Element("modifier").addContent(jmri.Version.getModifier())) 492 ); 493 } 494 495 /** 496 * Load a file. 497 * <p> 498 * Handles problems locally to the extent that it can, by routing them to 499 * the creationErrorEncountered method. 500 * 501 * @param fi file to load 502 * @return true if no problems during the load 503 * @throws jmri.configurexml.JmriConfigureXmlException if unable to load 504 * file 505 */ 506 @Override 507 public boolean load(File fi) throws JmriConfigureXmlException { 508 return load(fi, false); 509 } 510 511 /** {@inheritDoc} */ 512 @Override 513 public boolean load(URL url) throws JmriConfigureXmlException { 514 return load(url, false); 515 } 516 517 /** 518 * Load a file. 519 * <p> 520 * Handles problems locally to the extent that it can, by routing them to 521 * the creationErrorEncountered method. 522 * 523 * @param fi file to load 524 * @param registerDeferred true to register objects to defer 525 * @return true if no problems during the load 526 * @throws JmriConfigureXmlException if problem during load 527 * @see jmri.configurexml.XmlAdapter#loadDeferred() 528 * @since 2.11.2 529 */ 530 @Override 531 public boolean load(File fi, boolean registerDeferred) throws JmriConfigureXmlException { 532 return this.load(FileUtil.fileToURL(fi), registerDeferred); 533 } 534 535 /** 536 * Load a file. 537 * <p> 538 * Handles problems locally to the extent that it can, by routing them to 539 * the creationErrorEncountered method. 540 * <p> 541 * Always processes on Swing thread 542 * 543 * @param url URL of file to load 544 * @param registerDeferred true to register objects to defer 545 * @return true if no problems during the load 546 * @throws JmriConfigureXmlException if problem during load 547 * @see jmri.configurexml.XmlAdapter#loadDeferred() 548 * @since 3.3.2 549 */ 550 @Override 551 public boolean load(URL url, boolean registerDeferred) throws JmriConfigureXmlException { 552 log.trace("starting load({}, {})", url, registerDeferred); 553 554 // we do the actual load on the Swing thread in case it changes visible windows 555 Boolean retval = jmri.util.ThreadingUtil.runOnGUIwithReturn(() -> { 556 try { 557 Boolean ret = loadOnSwingThread(url, registerDeferred); 558 return ret; 559 } catch (Exception e) { 560 log.trace(" ending load() via JmriConfigureXmlException"); 561 throw new RuntimeException(e); 562 } 563 }); 564 565 log.trace(" ending load({}, {} with {})", url, registerDeferred, retval); 566 return retval; 567 } 568 569 private XmlFile.Validate validate = XmlFile.Validate.CheckDtdThenSchema; 570 571 /** {@inheritDoc} */ 572 @Override 573 public void setValidate(XmlFile.Validate v) { 574 validate = v; 575 } 576 577 /** {@inheritDoc} */ 578 @Override 579 public XmlFile.Validate getValidate() { 580 return validate; 581 } 582 583 // must run on GUI thread only; that's ensured at the using level. 584 private Boolean loadOnSwingThread(URL url, boolean registerDeferred) throws JmriConfigureXmlException { 585 boolean result = true; 586 Element root = null; 587 /* We will put all the elements into a load list, along with the load order 588 As XML files prior to 2.13.1 had no order to the store, beans would be stored/loaded 589 before beans that they were dependant upon had been stored/loaded 590 */ 591 Map<Element, Integer> loadlist = Collections.synchronizedMap(new LinkedHashMap<>()); 592 593 try { 594 setValidate(validate); 595 root = super.rootFromURL(url); 596 // get the objects to load 597 List<Element> items = root.getChildren(); 598 for (Element item : items) { 599 //Put things into an ordered list 600 Attribute a = item.getAttribute("class"); 601 if (a == null) { 602 // this is an element that we're not meant to read 603 log.debug("skipping {}", item); 604 continue; 605 } 606 String adapterName = a.getValue(); 607 log.debug("attempt to get adapter {} for {}", adapterName, item); 608 adapterName = currentClassName(adapterName); 609 XmlAdapter adapter = (XmlAdapter) Class.forName(adapterName).getDeclaredConstructor().newInstance(); 610 int order = adapter.loadOrder(); 611 log.debug("add {} to load list with order id of {}", item, order); 612 loadlist.put(item, order); 613 } 614 615 List<Map.Entry<Element, Integer>> l = new ArrayList<>(loadlist.entrySet()); 616 Collections.sort(l, (Map.Entry<Element, Integer> o1, Map.Entry<Element, Integer> o2) -> o1.getValue().compareTo(o2.getValue())); 617 618 for (Map.Entry<Element, Integer> elementIntegerEntry : l) { 619 Element item = elementIntegerEntry.getKey(); 620 String adapterName = item.getAttribute("class").getValue(); 621 adapterName = currentClassName(adapterName); 622 log.debug("load {} via {}", item, adapterName); 623 XmlAdapter adapter = null; 624 try { 625 adapter = (XmlAdapter) Class.forName(adapterName).getDeclaredConstructor().newInstance(); 626 627 // get version info 628 // loadVersion(root, adapter); 629 // and do it 630 if (adapter.loadDeferred() && registerDeferred) { 631 // register in the list for deferred load 632 loadDeferredList.add(item); 633 log.debug("deferred load registered for {} {}", item, adapterName); 634 } else { 635 boolean loadStatus = adapter.load(item, item); 636 log.debug("load status for {} {} is {}", item, adapterName, loadStatus); 637 638 // if any adaptor load fails, then the entire load has failed 639 if (!loadStatus) { 640 result = false; 641 } 642 } 643 } catch (Exception e) { 644 creationErrorEncountered(adapter, "load(" + url.getFile() + ")", "Unexpected error (Exception)", null, null, e); 645 646 result = false; // keep going, but return false to signal problem 647 } catch (Throwable et) { 648 creationErrorEncountered(adapter, "in load(" + url.getFile() + ")", "Unexpected error (Throwable)", null, null, et); 649 650 result = false; // keep going, but return false to signal problem 651 } 652 } 653 654 } catch (java.io.FileNotFoundException e1) { 655 // this returns false to indicate un-success, but not enough 656 // of an error to require a message 657 creationErrorEncountered(null, "opening file " + url.getFile(), 658 "File not found", null, null, e1); 659 result = false; 660 } catch (org.jdom2.JDOMException e) { 661 creationErrorEncountered(null, "parsing file " + url.getFile(), 662 "Parse error", null, null, e); 663 result = false; 664 } catch (java.io.IOException e) { 665 creationErrorEncountered(null, "loading from file " + url.getFile(), 666 "IOException", null, null, e); 667 result = false; 668 } catch (ClassNotFoundException e) { 669 creationErrorEncountered(null, "loading from file " + url.getFile(), 670 "ClassNotFoundException", null, null, e); 671 result = false; 672 } catch (InstantiationException e) { 673 creationErrorEncountered(null, "loading from file " + url.getFile(), 674 "InstantiationException", null, null, e); 675 result = false; 676 } catch (IllegalAccessException e) { 677 creationErrorEncountered(null, "loading from file " + url.getFile(), 678 "IllegalAccessException", null, null, e); 679 result = false; 680 } catch (NoSuchMethodException e) { 681 creationErrorEncountered(null, "loading from file " + url.getFile(), 682 "NoSuchMethodException", null, null, e); 683 result = false; 684 } catch (java.lang.reflect.InvocationTargetException e) { 685 creationErrorEncountered(null, "loading from file " + url.getFile(), 686 "InvocationTargetException", null, null, e); 687 result = false; 688 } finally { 689 // no matter what, close error reporting 690 handler.done(); 691 } 692 693 // loading complete, as far as it got, make history entry 694 FileHistory r = InstanceManager.getNullableDefault(FileHistory.class); 695 if (r != null) { 696 FileHistory included = null; 697 if (root != null) { 698 Element filehistory = root.getChild("filehistory"); 699 if (filehistory != null) { 700 included = jmri.jmrit.revhistory.configurexml.FileHistoryXml.loadFileHistory(filehistory); 701 } 702 } 703 String friendlyName = url.getFile().replaceAll("%20", " "); 704 r.addOperation((result ? "Load OK" : "Load with errors"), friendlyName, included); 705 } else { 706 log.info("Not recording file history"); 707 } 708 return result; 709 } 710 711 /** {@inheritDoc} */ 712 @Override 713 public boolean loadDeferred(File fi) { 714 return this.loadDeferred(FileUtil.fileToURL(fi)); 715 } 716 717 /** {@inheritDoc} */ 718 @Override 719 public boolean loadDeferred(URL url) { 720 boolean result = true; 721 // Now process the load-later list 722 log.debug("Start processing deferred load list (size): {}", loadDeferredList.size()); 723 if (!loadDeferredList.isEmpty()) { 724 for (Element item : loadDeferredList) { 725 String adapterName = item.getAttribute("class").getValue(); 726 log.debug("deferred load via {}", adapterName); 727 XmlAdapter adapter = null; 728 try { 729 adapter = (XmlAdapter) Class.forName(adapterName).getDeclaredConstructor().newInstance(); 730 boolean loadStatus = adapter.load(item, item); 731 log.debug("deferred load status for {} is {}", adapterName, loadStatus); 732 733 // if any adaptor load fails, then the entire load has failed 734 if (!loadStatus) { 735 result = false; 736 } 737 } catch (Exception e) { 738 creationErrorEncountered(adapter, "deferred load(" + url.getFile() + ")", 739 "Unexpected error (Exception)", null, null, e); 740 result = false; // keep going, but return false to signal problem 741 } catch (Throwable et) { 742 creationErrorEncountered(adapter, "in deferred load(" + url.getFile() + ")", 743 "Unexpected error (Throwable)", null, null, et); 744 result = false; // keep going, but return false to signal problem 745 } 746 } 747 } 748 log.debug("Done processing deferred load list with result: {}", result); 749 return result; 750 } 751 752 /** 753 * Find a file by looking 754 * <ul> 755 * <li> in xml/layout/ in the preferences directory, if that exists 756 * <li> in xml/layout/ in the application directory, if that exists 757 * <li> in xml/ in the preferences directory, if that exists 758 * <li> in xml/ in the application directory, if that exists 759 * <li> at top level in the application directory 760 * </ul> 761 * 762 * @param f Local filename, perhaps without path information 763 * @return Corresponding File object 764 */ 765 @Override 766 public URL find(String f) { 767 URL u = FileUtil.findURL(f, "xml/layout", "xml"); // NOI18N 768 if (u == null) { 769 this.locateFileFailed(f); 770 } 771 return u; 772 } 773 774 /** 775 * Report a failure to find a file. This is a separate member to ease 776 * testing. 777 * 778 * @param f Name of file not located. 779 */ 780 void locateFileFailed(String f) { 781 log.warn("Could not locate file {}", f); 782 } 783 784 /** 785 * Invoke common handling of errors that happen during the "load" process. 786 * <p> 787 * Exceptions passed into this are absorbed. 788 * 789 * @param adapter Object that encountered the error (for reporting), may 790 * be null 791 * @param operation description of the operation being attempted, may be 792 * null 793 * @param description description of error encountered 794 * @param systemName System name of bean being handled, may be null 795 * @param userName used name of the bean being handled, may be null 796 * @param exception Any exception being handled in the processing, may be 797 * null 798 */ 799 static public void creationErrorEncountered( 800 XmlAdapter adapter, 801 String operation, 802 String description, 803 String systemName, 804 String userName, 805 Throwable exception) { 806 // format and log a message (note reordered from arguments) 807// System.out.format("creationErrorEncountered: %s%n", exception.getMessage()); 808// System.out.format("creationErrorEncountered: %s, %s, %s, %s, %s, %s%n", adapter, operation, description, systemName, userName, exception == null ? null : exception.getMessage()); 809 ErrorMemo e = new ErrorMemo( 810 adapter, operation, description, 811 systemName, userName, exception, "loading"); 812 if (adapter != null) { 813 ErrorHandler aeh = adapter.getExceptionHandler(); 814 if (aeh != null) { 815 aeh.handle(e); 816 } 817 } else { 818 handler.handle(e); 819 } 820 } 821 822 /** 823 * Invoke common handling of errors that happen during the "store" process. 824 * <p> 825 * Exceptions passed into this are absorbed. 826 * 827 * @param adapter Object that encountered the error (for reporting), may 828 * be null 829 * @param operation description of the operation being attempted, may be 830 * null 831 * @param description description of error encountered 832 * @param systemName System name of bean being handled, may be null 833 * @param userName used name of the bean being handled, may be null 834 * @param exception Any exception being handled in the processing, may be 835 * null 836 */ 837 static public void storingErrorEncountered( 838 XmlAdapter adapter, 839 String operation, 840 String description, 841 String systemName, 842 String userName, 843 Throwable exception) { 844 // format and log a message (note reordered from arguments) 845 ErrorMemo e = new ErrorMemo( 846 adapter, operation, description, 847 systemName, userName, exception, "storing"); 848 if (adapter != null) { 849 ErrorHandler aeh = adapter.getExceptionHandler(); 850 if (aeh != null) { 851 aeh.handle(e); 852 } 853 } else { 854 handler.handle(e); 855 } 856 } 857 858 private static ErrorHandler handler = new ErrorHandler(); 859 860 static public void setErrorHandler(ErrorHandler handler) { 861 ConfigXmlManager.handler = handler; 862 } 863 864 /** 865 * @return the loadDeferredList 866 */ 867 protected List<Element> getLoadDeferredList() { 868 return loadDeferredList; 869 } 870 871 // initialize logging 872 private final static Logger log = LoggerFactory.getLogger(ConfigXmlManager.class); 873 874}