1 /* 2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab 3 * Copyright (C) 2012 - Scilab Enterprises - Calixte DENIZET 4 * 5 * Copyright (C) 2012 - 2016 - Scilab Enterprises 6 * 7 * This file is hereby licensed under the terms of the GNU GPL v2.0, 8 * pursuant to article 5.3.4 of the CeCILL v.2.1. 9 * This file was originally licensed under the terms of the CeCILL v2.1, 10 * and continues to be available under such terms. 11 * For more information, see the COPYING file which you should have received 12 * along with this program. 13 * 14 */ 15 16 package org.scilab.modules.commons.xml; 17 18 import java.awt.Color; 19 import java.io.BufferedOutputStream; 20 import java.io.ByteArrayOutputStream; 21 import java.io.File; 22 import java.io.FilenameFilter; 23 import java.io.FileFilter; 24 import java.io.IOException; 25 import java.io.UnsupportedEncodingException; 26 import java.lang.annotation.Annotation; 27 import java.lang.annotation.Retention; 28 import java.lang.annotation.RetentionPolicy; 29 import java.lang.reflect.Array; 30 import java.lang.reflect.Constructor; 31 import java.lang.reflect.InvocationTargetException; 32 import java.lang.reflect.Method; 33 import java.util.ArrayList; 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.HashSet; 38 import java.util.Set; 39 40 import javax.swing.KeyStroke; 41 import javax.swing.event.EventListenerList; 42 import javax.xml.parsers.DocumentBuilder; 43 import javax.xml.parsers.DocumentBuilderFactory; 44 import javax.xml.parsers.ParserConfigurationException; 45 import javax.xml.transform.OutputKeys; 46 import javax.xml.transform.Transformer; 47 import javax.xml.transform.TransformerConfigurationException; 48 import javax.xml.transform.TransformerException; 49 import javax.xml.transform.TransformerFactoryConfigurationError; 50 import javax.xml.transform.dom.DOMSource; 51 import javax.xml.transform.stream.StreamResult; 52 import javax.xml.xpath.XPath; 53 import javax.xml.xpath.XPathConstants; 54 import javax.xml.xpath.XPathExpressionException; 55 import javax.xml.xpath.XPathFactory; 56 57 import org.xml.sax.SAXException; 58 import org.w3c.dom.Document; 59 import org.w3c.dom.Element; 60 import org.w3c.dom.NamedNodeMap; 61 import org.w3c.dom.Node; 62 import org.w3c.dom.NodeList; 63 64 import org.scilab.modules.commons.ScilabConstants; 65 import org.scilab.modules.commons.ScilabGeneralPrefs; 66 import org.scilab.modules.commons.gui.ScilabKeyStroke; 67 import org.scilab.modules.localization.Messages; 68 69 /** 70 * Class to retrieve object from the xml configuration file 71 * 72 * @author Calixte DENIZET 73 * 74 */ 75 public class XConfiguration { 76 77 // User configuration file 78 private static final String SCI = System.getenv("SCI"); 79 private static final String SCILAB_CONFIG_FILE = SCI + "/modules/preferences/etc/XConfiguration.xml"; 80 81 private static final String ERROR_READ = Messages.gettext("Could not load file: "); 82 private static final String ERROR_WRITE = Messages.gettext("Could not write the file: "); 83 private static final String SEVERE_ERROR = Messages.gettext("A severe error occurred: cannot load the preferences file."); 84 private static final String PARSING_ERROR = Messages.gettext("An error occurred when loading the preferences file, try to reload the default one."); 85 86 private static final XPathFactory xpathFactory = ScilabXPathFactory.newInstance(); 87 private static final Map < Class<?>, StringParser > conv = new HashMap < Class<?>, StringParser > (); 88 89 private static final EventListenerList listenerList = new EventListenerList(); 90 private static final Set<String> modifiedPaths = new HashSet<String>(); 91 92 private static Document doc; 93 private static boolean hasBeenRead; 94 private static boolean mustSave = true;; 95 private static String USER_CONFIG_FILE = ScilabConstants.SCIHOME.toString() + "/XConfiguration.xml"; 96 97 static { 98 if (ScilabConstants.SCIHOME != null && ScilabConstants.SCIHOME.canRead() && ScilabConstants.SCIHOME.canWrite()) { 99 USER_CONFIG_FILE = ScilabConstants.SCIHOME.toString() + "/XConfiguration.xml"; 100 } else { 101 USER_CONFIG_FILE = SCILAB_CONFIG_FILE; 102 mustSave = false; 103 } 104 ScilabGeneralPrefs.getInstance()105 addXConfigurationListener(ScilabGeneralPrefs.getInstance()); 106 107 try { 108 Class histoprefs = ClassLoader.getSystemClassLoader().loadClass("org.scilab.modules.history_manager.HistoryPrefs"); 109 Method getinstance = histoprefs.getDeclaredMethod("getInstance"); addXConfigurationListener(XConfigurationListener)getinstance.invoke(null)110 addXConfigurationListener((XConfigurationListener)getinstance.invoke(null)); 111 } catch (ClassNotFoundException e) { 112 // Nothing displayed (always occurs in MN mode) 113 } catch (Exception e) { 114 System.err.println(e); 115 } 116 } 117 118 /** 119 * Get the document in SCIHOME corresponding to the configuration file. 120 * @return the configuration document. 121 */ getXConfigurationDocument()122 public static Document getXConfigurationDocument() { 123 if (doc == null) { 124 boolean error = false; 125 File xml = new File(USER_CONFIG_FILE); 126 if (!xml.exists() && mustSave) { 127 ScilabXMLUtilities.writeDocument(createDocument(), USER_CONFIG_FILE); 128 } 129 130 DocumentBuilder docBuilder = null; 131 132 if (mustSave) { 133 try { 134 DocumentBuilderFactory factory = ScilabDocumentBuilderFactory.newInstance(); 135 docBuilder = factory.newDocumentBuilder(); 136 doc = docBuilder.parse(xml); 137 float version = getDocumentVersion(doc); 138 float defaultVersion = getDocumentVersion(getDefaultDocument()); 139 if (defaultVersion != version) { 140 xml.delete(); 141 doc = null; 142 return getXConfigurationDocument(); 143 } else { 144 return doc; 145 } 146 } catch (ParserConfigurationException pce) { 147 error = true; 148 } catch (SAXException se) { 149 error = true; 150 } catch (IOException ioe) { 151 error = true; 152 } 153 154 if (error) { 155 if (hasBeenRead) { 156 System.err.println(SEVERE_ERROR); 157 doc = null; 158 xml.delete(); 159 return docBuilder.newDocument(); 160 } 161 162 hasBeenRead = true; 163 doc = null; 164 xml.delete(); 165 System.err.println(PARSING_ERROR); 166 return getXConfigurationDocument(); 167 } 168 169 return docBuilder.newDocument(); 170 } else { 171 doc = createDocument(); 172 } 173 } 174 175 return doc; 176 } 177 178 /** 179 * Save the modifications 180 */ writeDocument(String filename, Node written)181 public static void writeDocument(String filename, Node written) { 182 if (mustSave) { 183 Transformer transformer = null; 184 try { 185 transformer = ScilabTransformerFactory.newInstance().newTransformer(); 186 } catch (TransformerConfigurationException e1) { 187 System.err.println(ERROR_WRITE + filename); 188 return; 189 } catch (TransformerFactoryConfigurationError e1) { 190 System.err.println(ERROR_WRITE + filename); 191 return; 192 } 193 transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 194 195 StreamResult result = new StreamResult(new File(filename)); 196 DOMSource source = new DOMSource(written); 197 try { 198 transformer.transform(source, result); 199 } catch (TransformerException e) { 200 System.err.println(ERROR_WRITE + filename); 201 return; 202 } 203 204 // Invalidate the current document 205 if (filename.equals(USER_CONFIG_FILE)) { 206 doc = null; 207 } 208 } 209 } 210 211 /** 212 * Save the modifications 213 */ dumpNode(Node written)214 public static String dumpNode(Node written) { 215 Transformer transformer = null; 216 try { 217 transformer = ScilabTransformerFactory.newInstance().newTransformer(); 218 } catch (TransformerConfigurationException e1) { 219 System.err.println("Cannot dump xml"); 220 return ""; 221 } catch (TransformerFactoryConfigurationError e1) { 222 System.err.println("Cannot dump xml"); 223 return ""; 224 } 225 transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 226 227 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 228 StreamResult result = new StreamResult(new BufferedOutputStream(stream)); 229 DOMSource source = new DOMSource(written); 230 String str = ""; 231 try { 232 transformer.transform(source, result); 233 str = stream.toString("UTF-8"); 234 } catch (TransformerException e) { 235 System.err.println("Cannot dump xml"); 236 return str; 237 } catch (UnsupportedEncodingException e) { 238 e.printStackTrace(); 239 } finally { 240 try { 241 stream.close(); 242 } catch (Exception e) { } 243 } 244 245 return str; 246 } 247 248 /** 249 * Get the document version 250 * @param doc the document 251 * @return the version 252 */ getDocumentVersion(Document doc)253 private static float getDocumentVersion(Document doc) { 254 if (doc != null) { 255 Element root = doc.getDocumentElement(); 256 String version = root.getAttribute("version"); 257 258 try { 259 return Float.parseFloat(version); 260 } catch (NumberFormatException e) { } 261 } 262 263 return 0.0f; 264 } 265 266 /** 267 * Get the default document 268 * @return the document 269 */ getDefaultDocument()270 private static Document getDefaultDocument() { 271 DocumentBuilder docBuilder; 272 DocumentBuilderFactory factory; 273 Document mainDoc = null; 274 275 try { 276 factory = ScilabDocumentBuilderFactory.newInstance(); 277 docBuilder = factory.newDocumentBuilder(); 278 mainDoc = docBuilder.parse(SCILAB_CONFIG_FILE); 279 } catch (ParserConfigurationException pce) { 280 System.err.println("Cannot create a XML DocumentBuilder:\n" + pce); 281 return null; 282 } catch (SAXException se) { 283 System.err.println("Weird... Cannot parse basic file:\n" + se); 284 return null; 285 } catch (IOException ioe) { 286 System.err.println("Weird... Cannot parse basic file:\n" + ioe); 287 return null; 288 } 289 290 return mainDoc; 291 } 292 293 /** 294 * Create a document in using the XConfiguration-*.xml found in SCI/modules/MODULE_NAME/etc/ 295 * @return the built document 296 */ createDocument()297 public static Document createDocument() { 298 DocumentBuilder docBuilder; 299 DocumentBuilderFactory factory; 300 Document mainDoc = getDefaultDocument(); 301 if (mainDoc == null) { 302 return null; 303 } 304 305 Element root = mainDoc.getDocumentElement(); 306 307 factory = ScilabDocumentBuilderFactory.newInstance(); 308 309 try { 310 docBuilder = factory.newDocumentBuilder(); 311 } catch (ParserConfigurationException pce) { 312 System.err.println("Cannot create a XML DocumentBuilder:\n" + pce); 313 return null; 314 } 315 316 List<File> etcs = getEtcDir(); 317 for (File etc : etcs) { 318 File[] xmls = etc.listFiles(new FilenameFilter() { 319 public boolean accept(File dir, String name) { 320 return name.endsWith(".xml") && name.startsWith("XConfiguration-"); 321 } 322 }); 323 for (File xml : xmls) { 324 try { 325 Document doc = docBuilder.parse(xml); 326 if (xml.getName().equals("XConfiguration-general.xml")) { 327 try { 328 XPath xp = xpathFactory.newXPath(); 329 NodeList nodes = (NodeList) xp.compile("//shortcuts/body/actions/action-folder/action[contains(@key, 'OSSCKEY')]").evaluate(doc, XPathConstants.NODESET); 330 for (int i = 0; i < nodes.getLength(); i++) { 331 Element e = (Element) nodes.item(i); 332 e.setAttribute("key", e.getAttribute("key").replace("OSSCKEY", ScilabKeyStroke.getOSMetaKey())); 333 } 334 } catch (XPathExpressionException e) { 335 System.err.println(e); 336 } 337 } 338 Node node = mainDoc.importNode(doc.getDocumentElement(), true); 339 NodeList list = root.getElementsByTagName(node.getNodeName()); 340 if (list.getLength() != 0) { 341 root.replaceChild(node, list.item(0)); 342 } 343 } catch (SAXException se) { 344 System.err.println(ERROR_READ + xml.getName()); 345 } catch (IOException ioe) { 346 System.err.println(ERROR_READ + xml.getName()); 347 } 348 } 349 } 350 351 return mainDoc; 352 } 353 354 /** 355 * Get the list of the etc dirs in modules dirs 356 * @return the lit of etc dirs 357 */ getEtcDir()358 public static List<File> getEtcDir() { 359 List<File> list = new ArrayList<File>(); 360 File modulesDir = new File(SCI + "/modules/"); 361 File[] modules = modulesDir.listFiles(new FileFilter() { 362 public boolean accept(File f) { 363 return f.isDirectory(); 364 } 365 }); 366 367 for (File module : modules) { 368 File etc = new File(module, "/etc/"); 369 if (etc.exists() && etc.isDirectory()) { 370 list.add(etc); 371 } 372 } 373 374 return list; 375 } 376 addModifiedPath(String path)377 public static void addModifiedPath(String path) { 378 if (path != null && !path.isEmpty()) { 379 modifiedPaths.add(path); 380 } 381 } 382 invalidate()383 public static void invalidate() { 384 modifiedPaths.clear(); 385 doc = null; 386 } 387 addXConfigurationListener(XConfigurationListener listener)388 public static void addXConfigurationListener(XConfigurationListener listener) { 389 listenerList.add(XConfigurationListener.class, listener); 390 } 391 removeXConfigurationListener(XConfigurationListener listener)392 public static void removeXConfigurationListener(XConfigurationListener listener) { 393 listenerList.remove(XConfigurationListener.class, listener); 394 } 395 getXConfigurationListeners()396 public static XConfigurationListener[] getXConfigurationListeners() { 397 return listenerList.getListeners(XConfigurationListener.class); 398 } 399 fireXConfigurationEvent()400 public static void fireXConfigurationEvent() { 401 if (!modifiedPaths.isEmpty()) { 402 XConfigurationEvent event = null; 403 Object[] listeners = listenerList.getListenerList(); 404 for (int i = listeners.length - 2; i >= 0; i -= 2) { 405 if (listeners[i] == XConfigurationListener.class) { 406 if (event == null) { 407 event = new XConfigurationEvent(modifiedPaths); 408 } 409 ((XConfigurationListener) listeners[i + 1]).configurationChanged(event); 410 } 411 } 412 413 modifiedPaths.clear(); 414 } 415 } 416 417 /** 418 * Register a StringParser for a given Class 419 * @param type the class type 420 * @param parser the StringParser 421 */ registerStringParser(Class<?> type, StringParser parser)422 public static void registerStringParser(Class<?> type, StringParser parser) { 423 conv.put(type, parser); 424 } 425 426 /** 427 * Get a StringParser for a given Class 428 * @param type the class type 429 * @return the corresponding parser 430 */ getStringParser(Class<?> type)431 public static StringParser getStringParser(Class<?> type) { 432 return conv.get(type); 433 } 434 set(final Document doc, final String path, String value)435 public static void set(final Document doc, final String path, String value) { 436 XPath xp = xpathFactory.newXPath(); 437 NodeList nodes; 438 try { 439 nodes = (NodeList) xp.compile(path).evaluate(doc, XPathConstants.NODESET); 440 } catch (XPathExpressionException e) { 441 System.err.println(e); 442 return; 443 } 444 445 for (int i = 0; i < nodes.getLength() ; i++) { 446 Node n = nodes.item(i); 447 if (n != null && n.getNodeType() == Node.ATTRIBUTE_NODE) { 448 n.setNodeValue(value); 449 } 450 } 451 452 writeDocument(USER_CONFIG_FILE, doc); 453 } 454 455 /** 456 * Save the current file 457 */ save()458 public static void save() { 459 if (doc != null) { 460 writeDocument(USER_CONFIG_FILE, doc); 461 } 462 } 463 464 /** 465 * Get all the nodes with the given path. 466 * All the get nodes are serialized into an object (generic paramater) which must have 467 * a constructor without argument and with methods named set<Attribute Name> with 468 * one argument and no returned value. 469 * For example a node <foo aaa="1" bbb="true" ccc-ddd="#001122"/> could be retrieved with something like 470 * XConfiguration.get(MyObject.class, doc, "//path/to/node") where MyObject should be something like 471 * <code> 472 * public class MyObject { 473 * 474 * public MyObject() { 475 * // ... 476 * } 477 * 478 * public void setAaa(int a) { 479 * // ... 480 * } 481 * 482 * public void setBbb(boolean b) { 483 * // ... 484 * } 485 * 486 * public void setCccDdd(Color c) { 487 * // ... 488 * } 489 * } 490 * </code> 491 * If an attribute must not be retrieved, just remove the setter. 492 * 493 * It is possible to use the annotation @XConfAttribute to make easier the retrievement. 494 * For example 495 * <code> 496 * @XConfAttribute 497 * public class MyObject { 498 * 499 * public MyObject() { 500 * // ... 501 * } 502 * 503 * @XConfAttribute(attributes={"aaa", "bbb", "ccc-ddd"}) 504 * // the contents of aaa will be converted into int and passed as first argument 505 * // the contents of bbb will be converted into boolean and passed as second argument 506 * // the contents of ccc-ddd will be converted into Color and passed as third argument 507 * public void set(int n, boolean b, Color c) { 508 * // ... 509 * } 510 * } 511 * </code> 512 * 513 * @param type the Class type to retrieve 514 * @param doc the document to explore 515 * @param path the xpath query to retrieve the corresponding nodeset. 516 * @return an array of instance of class type. 517 */ get(final Class<T> type, final Document doc, final String path)518 public static final <T> T[] get(final Class<T> type, final Document doc, final String path) { 519 XPath xp = xpathFactory.newXPath(); 520 NodeList nodes; 521 try { 522 nodes = (NodeList) xp.compile(path).evaluate(doc, XPathConstants.NODESET); 523 } catch (XPathExpressionException e) { 524 System.err.println(e); 525 return (T[]) Array.newInstance(type, 0); 526 } 527 528 if (type.getAnnotation(XConfAttribute.class) != null) { 529 T[] arr = get(type, nodes); 530 if (arr != null) { 531 return arr; 532 } 533 } 534 535 Method[] meths = type.getDeclaredMethods(); 536 Map<String, Method> mapMethods = new HashMap<String, Method>(); 537 for (Method m : meths) { 538 String name = m.getName(); 539 if (name.startsWith("set") && m.getParameterTypes().length == 1 && m.getReturnType().equals(Void.TYPE)) { 540 mapMethods.put(m.getName(), m); 541 m.setAccessible(true); 542 } 543 } 544 545 Map<String, String> names = new HashMap<String, String>(); 546 547 T[] values = (T[]) Array.newInstance(type, nodes.getLength()); 548 for (int i = 0; i < values.length; i++) { 549 NamedNodeMap map = nodes.item(i).getAttributes(); 550 try { 551 Constructor<T> constructor = type.getDeclaredConstructor(new Class[] {}); 552 constructor.setAccessible(true); 553 values[i] = constructor.newInstance(); 554 } catch (InstantiationException e) { 555 System.err.println(e); 556 break; 557 } catch (IllegalAccessException e) { 558 System.err.println(e); 559 break; 560 } catch (NoSuchMethodException e) { 561 System.err.println(e); 562 break; 563 } catch (InvocationTargetException e) { 564 System.err.println(e.getTargetException()); 565 } 566 567 for (int j = 0; j < map.getLength(); j++) { 568 Node n = map.item(j); 569 String name = n.getNodeName(); 570 String methName = names.get(name); 571 if (methName == null) { 572 StringBuilder buffer = new StringBuilder("set"); 573 String[] parts = name.split("-"); 574 for (String part : parts) { 575 if (part != null && part.length() > 0) { 576 buffer.append(part.substring(0, 1).toUpperCase()); 577 buffer.append(part.substring(1).toLowerCase()); 578 } 579 } 580 methName = buffer.toString(); 581 names.put(name, methName); 582 } 583 String value = n.getNodeValue(); 584 Method method = mapMethods.get(methName); 585 if (method != null) { 586 Class[] paramsClass = method.getParameterTypes(); 587 StringParser parser = conv.get(paramsClass[0]); 588 if (parser != null) { 589 Object[] params = new Object[] {parser.parse(value)}; 590 try { 591 method.invoke(values[i], params); 592 } catch (IllegalAccessException e) { 593 System.err.println(e); 594 } catch (IllegalArgumentException e) { 595 System.err.println(e); 596 } catch (InvocationTargetException e) { 597 System.err.println(e.getTargetException()); 598 } 599 } 600 } 601 } 602 } 603 604 return values; 605 } 606 607 /** 608 * Get a Map with where the key is the converted value (according to keyType) of the attribute named key 609 * and the value is given in the same way. 610 * @param doc the document to read 611 * @param key the attribute name where its value will be converted and used as a key in the map 612 * @param keyType the Class of the key 613 * @param value the attribute name where its value will be converted and used as a value in the map 614 * @param valueType the Class of the value 615 * @return the corresponding map. 616 */ get(final Document doc, final String key, final Class<T> keyType, final String value, final Class<U> valueType, final String path)617 public static final <T, U> Map<T, U> get(final Document doc, final String key, final Class<T> keyType, final String value, final Class<U> valueType, final String path) { 618 XPath xp = xpathFactory.newXPath(); 619 Map<T, U> map = new HashMap<T, U>(); 620 NodeList nodes; 621 try { 622 nodes = (NodeList) xp.compile(path).evaluate(doc, XPathConstants.NODESET); 623 } catch (XPathExpressionException e) { 624 System.err.println(e); 625 return map; 626 } 627 628 int len = nodes.getLength(); 629 for (int i = 0; i < len; i++) { 630 NamedNodeMap nmap = nodes.item(i).getAttributes(); 631 Node k = nmap.getNamedItem(key); 632 Node v = nmap.getNamedItem(value); 633 if (k == null || v == null) { 634 return map; 635 } 636 637 String kk = k.getNodeValue(); 638 String vv = v.getNodeValue(); 639 640 StringParser convK = conv.get(keyType); 641 StringParser convV = conv.get(valueType); 642 if (convK == null || convV == null) { 643 return map; 644 } 645 646 map.put((T) convK.parse(kk), (U) convV.parse(vv)); 647 } 648 649 return map; 650 } 651 652 /** 653 * Getter for annoted class (with @XConfAttribute) 654 * @param type the class type 655 * @param nodes the nodes to read 656 * @return an array of instances of type, if the class is annoted with @XConfAttribute(isStatic=true), 657 * the returned array is empty. 658 */ get(final Class<T> type, NodeList nodes)659 private static final <T> T[] get(final Class<T> type, NodeList nodes) { 660 Method[] meths = type.getDeclaredMethods(); 661 List<String[]> attrs = new ArrayList<String[]>(); 662 List<Method> methods = new ArrayList<Method>(); 663 for (Method m : meths) { 664 String name = m.getName(); 665 Annotation ann = m.getAnnotation(XConfAttribute.class); 666 if (ann != null) { 667 String[] attributes = ((XConfAttribute) ann).attributes(); 668 if (attributes.length == m.getParameterTypes().length) { 669 m.setAccessible(true); 670 attrs.add(attributes); 671 methods.add(m); 672 } else { 673 return null; 674 } 675 } 676 } 677 678 Annotation ann = type.getAnnotation(XConfAttribute.class); 679 boolean mustInstantiate = !((XConfAttribute) ann).isStatic(); 680 681 T[] values = null; 682 int len = nodes.getLength(); 683 if (mustInstantiate) { 684 values = (T[]) Array.newInstance(type, len); 685 } 686 687 for (int i = 0; i < len; i++) { 688 NamedNodeMap map = nodes.item(i).getAttributes(); 689 String nodeName = nodes.item(i).getNodeName(); 690 691 if (mustInstantiate) { 692 try { 693 Constructor<T> constructor = type.getDeclaredConstructor(new Class[] {}); 694 constructor.setAccessible(true); 695 values[i] = constructor.newInstance(); 696 } catch (InstantiationException e) { 697 System.err.println(e); 698 break; 699 } catch (IllegalAccessException e) { 700 System.err.println(e); 701 break; 702 } catch (NoSuchMethodException e) { 703 System.err.println(e); 704 break; 705 } catch (InvocationTargetException e) { 706 System.err.println(e.getTargetException()); 707 } 708 } 709 710 for (int j = 0; j < methods.size(); j++) { 711 Method method = methods.get(j); 712 ann = method.getAnnotation(XConfAttribute.class); 713 String tag = ((XConfAttribute) ann).tag(); 714 if (tag.isEmpty() || tag.equals(nodeName)) { 715 String[] attributes = attrs.get(j); 716 Object[] params = new Object[attributes.length]; 717 Class[] paramsClass = method.getParameterTypes(); 718 for (int k = 0; k < attributes.length; k++) { 719 String p = ""; 720 Node node = null; 721 if (map != null) { 722 node = map.getNamedItem(attributes[k]); 723 } 724 if (node != null) { 725 p = node.getNodeValue(); 726 } 727 728 StringParser parser = conv.get(paramsClass[k]); 729 if (parser != null) { 730 params[k] = parser.parse(p); 731 } 732 } 733 734 try { 735 if (mustInstantiate) { 736 method.invoke(values[i], params); 737 } else { 738 method.invoke(null, params); 739 } 740 } catch (IllegalAccessException e) { 741 System.err.println(e); 742 } catch (IllegalArgumentException e) { 743 System.err.println(e); 744 } catch (InvocationTargetException e) { 745 System.err.println(e.getTargetException()); 746 } 747 } 748 } 749 } 750 751 if (mustInstantiate) { 752 return values; 753 } else { 754 return (T[]) Array.newInstance(type, 0); 755 } 756 } 757 758 /** 759 * Interface to implement to parse an attribute String into a Java object 760 */ 761 public static interface StringParser { 762 763 /** 764 * Parse a string 765 * @param str the string to parse 766 * @return the corresponding Object 767 */ parse(String str)768 public Object parse(String str); 769 } 770 771 static { conv.put(int.class, new StringParser() { public Integer parse(String str) { try { return Integer.parseInt(str); } catch (NumberFormatException e) { try { return (int) Double.parseDouble(str); } catch (NumberFormatException ee) { return new Integer(0); } } } })772 conv.put(int.class, new StringParser() { 773 public Integer parse(String str) { 774 try { 775 return Integer.parseInt(str); 776 } catch (NumberFormatException e) { 777 try { 778 return (int) Double.parseDouble(str); 779 } catch (NumberFormatException ee) { 780 return new Integer(0); 781 } 782 } 783 } 784 }); conv.put(char.class, new StringParser() { public Object parse(String str) { if (str.length() > 0) { return str.charAt(0); } else { return new Character((char) 0); } } })785 conv.put(char.class, new StringParser() { 786 public Object parse(String str) { 787 if (str.length() > 0) { 788 return str.charAt(0); 789 } else { 790 return new Character((char) 0); 791 } 792 } 793 }); conv.put(byte.class, new StringParser() { public Object parse(String str) { try { return Byte.parseByte(str); } catch (NumberFormatException e) { try { return (byte) Double.parseDouble(str); } catch (NumberFormatException ee) { return new Byte((byte) 0); } } } })794 conv.put(byte.class, new StringParser() { 795 public Object parse(String str) { 796 try { 797 return Byte.parseByte(str); 798 } catch (NumberFormatException e) { 799 try { 800 return (byte) Double.parseDouble(str); 801 } catch (NumberFormatException ee) { 802 return new Byte((byte) 0); 803 } 804 } 805 } 806 }); conv.put(short.class, new StringParser() { public Object parse(String str) { try { return Short.parseShort(str); } catch (NumberFormatException e) { try { return (short) Double.parseDouble(str); } catch (NumberFormatException ee) { return new Short((short) 0); } } } })807 conv.put(short.class, new StringParser() { 808 public Object parse(String str) { 809 try { 810 return Short.parseShort(str); 811 } catch (NumberFormatException e) { 812 try { 813 return (short) Double.parseDouble(str); 814 } catch (NumberFormatException ee) { 815 return new Short((short) 0); 816 } 817 } 818 } 819 }); conv.put(double.class, new StringParser() { public Object parse(String str) { try { return Double.parseDouble(str); } catch (NumberFormatException ee) { return new Double((double) 0); } } })820 conv.put(double.class, new StringParser() { 821 public Object parse(String str) { 822 try { 823 return Double.parseDouble(str); 824 } catch (NumberFormatException ee) { 825 return new Double((double) 0); 826 } 827 } 828 }); conv.put(float.class, new StringParser() { public Object parse(String str) { try { return Float.parseFloat(str); } catch (NumberFormatException ee) { return new Float((float) 0); } } })829 conv.put(float.class, new StringParser() { 830 public Object parse(String str) { 831 try { 832 return Float.parseFloat(str); 833 } catch (NumberFormatException ee) { 834 return new Float((float) 0); 835 } 836 } 837 }); conv.put(boolean.class, new StringParser() { public Object parse(String str) { return Boolean.parseBoolean(str); } })838 conv.put(boolean.class, new StringParser() { 839 public Object parse(String str) { 840 return Boolean.parseBoolean(str); 841 } 842 }); conv.put(long.class, new StringParser() { public Object parse(String str) { try { return Long.parseLong(str); } catch (NumberFormatException e) { try { return (long) Double.parseDouble(str); } catch (NumberFormatException ee) { return new Long((long) 0); } } } })843 conv.put(long.class, new StringParser() { 844 public Object parse(String str) { 845 try { 846 return Long.parseLong(str); 847 } catch (NumberFormatException e) { 848 try { 849 return (long) Double.parseDouble(str); 850 } catch (NumberFormatException ee) { 851 return new Long((long) 0); 852 } 853 } 854 } 855 }); conv.put(String.class, new StringParser() { public Object parse(String str) { return str; } })856 conv.put(String.class, new StringParser() { 857 public Object parse(String str) { 858 return str; 859 } 860 }); conv.put(Color.class, new StringParser() { public Object parse(String str) { try { return Color.decode(str); } catch (NumberFormatException e) { return Color.BLACK; } } })861 conv.put(Color.class, new StringParser() { 862 public Object parse(String str) { 863 try { 864 return Color.decode(str); 865 } catch (NumberFormatException e) { 866 return Color.BLACK; 867 } 868 } 869 }); conv.put(KeyStroke.class, new StringParser() { public Object parse(String str) { String[] toks = str.split(R); StringBuilder buffer = new StringBuilder(); for (int i = 0; i < toks.length - 1; i++) { buffer.append(toks[i].toLowerCase()); buffer.append(R); } buffer.append(toks[toks.length - 1].toUpperCase()); return KeyStroke.getKeyStroke(buffer.toString()); } })870 conv.put(KeyStroke.class, new StringParser() { 871 public Object parse(String str) { 872 String[] toks = str.split(" +"); 873 StringBuilder buffer = new StringBuilder(); 874 for (int i = 0; i < toks.length - 1; i++) { 875 buffer.append(toks[i].toLowerCase()); 876 buffer.append(" "); 877 } 878 buffer.append(toks[toks.length - 1].toUpperCase()); 879 return KeyStroke.getKeyStroke(buffer.toString()); 880 } 881 }); 882 } 883 884 @Retention(RetentionPolicy.RUNTIME) 885 public @interface XConfAttribute { 886 887 /** 888 * Map method arguments with attributes name 889 * For example, 890 * <code> 891 * @XConfAttribute(tag="mytag", attributes={"a", "b"}) 892 * void foo(String one, int tow) { ... } 893 * </code> 894 * The value of attribute "a" is converted into a String and passed as "one" argument,... 895 */ attributes()896 public String[] attributes() default { 897 "" 898 }; 899 tag()900 public String tag() default ""; 901 902 /** 903 * Used to avoid object instanciation so the differents annotated methods must be static. 904 */ isStatic()905 public boolean isStatic() default false; 906 } 907 } 908