001 package edu.rice.cs.cunit.util; 002 003 import edu.rice.cs.cunit.instrumentors.threadCheck.AAddThreadCheckStrategy; 004 import edu.rice.cs.cunit.threadCheck.ThreadCheckException; 005 import junit.framework.Assert; 006 import junit.framework.TestCase; 007 import org.w3c.dom.*; 008 import org.xml.sax.InputSource; 009 010 import javax.xml.parsers.DocumentBuilder; 011 import javax.xml.parsers.DocumentBuilderFactory; 012 import javax.xml.parsers.ParserConfigurationException; 013 import javax.xml.transform.*; 014 import javax.xml.transform.dom.DOMSource; 015 import javax.xml.transform.stream.StreamResult; 016 import java.io.*; 017 import java.util.*; 018 019 /** 020 * XML configuration management. 021 * <p/> 022 * This class uses DOM paths of a specific form to refer to nodes in the XML document. 023 * Consider this XML structure: 024 * <foo a="foo.a"> 025 * <bar>abc</bar> 026 * <fum fee="xyz">def</fum> 027 * </foo> 028 * The path "foo/bar" refers to the value "abc". 029 * The path "foo/fum" refers to the value "def". 030 * If this form is used, there may be only #text or #comment nodes in the node. All #text nodes will be 031 * concatenated and then stripped of whitespace at the beginning and the end. 032 * The path "foo/fum.fee" refers to the value "xyz". 033 * The path "foo.a" refers to the value "foo.a". 034 * 035 * When using getMultiple, any node or attribute name can be substituted with "*" to get all elements: 036 * The path "foo/*" returns both the value "abc" and "def". 037 * @author Mathias Ricken 038 */ 039 public class XMLConfig { 040 /** 041 * Newline string. 042 */ 043 public static final String NL = System.getProperty("line.separator"); 044 045 /** 046 * XML document. 047 */ 048 private Document _document; 049 050 /** 051 * Creates an empty configuration. 052 */ 053 public XMLConfig() { 054 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 055 try { 056 DocumentBuilder builder = factory.newDocumentBuilder(); 057 _document = builder.newDocument(); // Create from whole cloth 058 } 059 catch(ParserConfigurationException e) { 060 e.printStackTrace(); 061 } 062 } 063 064 /** 065 * Creates a configuration from an input stream. 066 * @param is input stream 067 */ 068 public XMLConfig(InputStream is) { 069 init(new InputSource(is)); 070 } 071 072 /** 073 * Creates a configuration from a reader. 074 * @param r reader 075 */ 076 public XMLConfig(Reader r) { 077 init(new InputSource(r)); 078 } 079 080 /** 081 * Initialize this XML configuration. 082 * @param is the XML input source 083 */ 084 private void init(InputSource is) { 085 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 086 DocumentBuilder builder = null; 087 try { 088 builder = factory.newDocumentBuilder(); 089 _document = builder.parse(is); 090 } 091 catch(Exception e) { 092 throw new XMLConfigException("Error in ctor", e); 093 } 094 _document.normalize(); 095 } 096 097 /** 098 * Creates a configuration from a file. 099 * @param f file 100 */ 101 public XMLConfig(File f) { 102 try { 103 init(new InputSource(new FileInputStream(f))); 104 } 105 catch(FileNotFoundException e) { 106 throw new XMLConfigException("Error in ctor", e); 107 } 108 } 109 110 /** 111 * Creates a configuration from a file name. 112 * @param filename file name 113 */ 114 public XMLConfig(String filename) { 115 try { 116 init(new InputSource(new FileInputStream(filename))); 117 } 118 catch(FileNotFoundException e) { 119 throw new XMLConfigException("Error in ctor", e); 120 } 121 } 122 123 /** 124 * Saves configuration to an output stream 125 * @param os output stream 126 */ 127 public void save(OutputStream os) { 128 // Prepare the DOM document for writing 129 Source source = new DOMSource(_document); 130 /* 131 // Prepare the output file 132 Result result = new StreamResult(os); 133 */ 134 // Write the DOM document to the file 135 try { 136 TransformerFactory tf = TransformerFactory.newInstance(); 137 tf.setAttribute("indent-number", new Integer(2)); 138 Transformer t = tf.newTransformer(); 139 t.setOutputProperty(OutputKeys.INDENT, "yes"); 140 t.transform(source, new StreamResult(new OutputStreamWriter(os, "utf-8"))); 141 /* 142 Transformer xformer = TransformerFactory.newInstance().newTransformer(); 143 xformer.setOutputProperty(OutputKeys.INDENT, "yes"); 144 xformer.transform(source, result); 145 */ 146 } 147 catch(TransformerException e) { 148 throw new XMLConfigException("Error in save", e); 149 } 150 catch(UnsupportedEncodingException e) { 151 throw new XMLConfigException("Error in save", e); 152 } 153 } 154 155 /** 156 * Saves configuration to a file. 157 * @param f file 158 */ 159 public void save(File f) { 160 try { 161 save(new FileOutputStream(f)); 162 } 163 catch(FileNotFoundException e) { 164 throw new XMLConfigException("Error in save", e); 165 } 166 } 167 168 /** 169 * Saves configuration to a file specified by a file name. 170 * @param filename file name 171 */ 172 public void save(String filename) { 173 save(new File(filename)); 174 } 175 176 /** 177 * Returns the value as specified by the DOM path. 178 * @param path DOM path 179 * @return value. 180 */ 181 public String get(String path) { 182 List<String> r = getMultiple(path); 183 if (r.size()!=1) throw new XMLConfigException("Number of results != 1"); 184 return r.get(0); 185 } 186 187 /** 188 * Returns the value as specified by the DOM path. 189 * @param path DOM path 190 * @return list of values. 191 */ 192 public List<String> getMultiple(String path) { 193 return getMultiple(path, _document); 194 } 195 196 /** 197 * Returns the value as specified by the DOM path. 198 * @param path DOM path 199 * @param root node where the search should start 200 * @return list of values. 201 */ 202 public List<String> getMultiple(String path, Node root) { 203 List<Node> accum = getNodes(path, root); 204 List<String> strings = new LinkedList<String>(); 205 for(Node n: accum) { 206 if (n instanceof Attr) { 207 strings.add(n.getNodeValue()); 208 } 209 else { 210 Node child; 211 String acc = ""; 212 child = n.getFirstChild(); 213 while(child!=null) { 214 if (child.getNodeName().equals("#text")) { 215 acc += " " + child.getNodeValue(); 216 } 217 else if (child.getNodeName().equals("#comment")) { 218 // ignore 219 } 220 else { 221 throw new XMLConfigException("Node "+n.getNodeName()+" contained node "+child.getNodeName()+", but should only contain #text and #comment."); 222 } 223 child = child.getNextSibling(); 224 } 225 strings.add(acc.trim()); 226 } 227 } 228 return strings; 229 } 230 231 232 /** 233 * Returns the nodes as specified by the DOM path. 234 * @param path DOM path 235 * @return list of nodes. 236 */ 237 public List<Node> getNodes(String path) { 238 return getNodes(path, _document); 239 } 240 241 /** 242 * Returns the nodes as specified by the DOM path. 243 * @param path DOM path 244 * @param root node where the search should start 245 * @return list of nodes. 246 */ 247 public List<Node> getNodes(String path, Node root) { 248 List<Node> accum = new LinkedList<Node>(); 249 getMultipleHelper(path, root, accum, false); 250 return accum; 251 } 252 253 /** 254 * Returns the value as specified by the DOM path. 255 * @param path DOM path 256 * @param n node where the search begins 257 * @param accum accumulator 258 * @param dotRead whether a dot has been read 259 */ 260 private void getMultipleHelper(String path, Node n, List<Node> accum, boolean dotRead) { 261 int dotPos = path.indexOf('.'); 262 boolean initialDot = (dotPos==0); 263 if ((path.length()>0) && (dotPos == -1) && (!path.endsWith("/"))) { 264 path = path + "/"; 265 } 266 int slashPos = path.indexOf('/'); 267 if (((slashPos > -1) || (dotPos > -1)) && !dotRead || initialDot) { 268 String nodeName; 269 if ((slashPos > -1) && ((dotPos == -1) || (slashPos < dotPos))) { 270 nodeName = path.substring(0, slashPos); 271 path = path.substring(slashPos+1); 272 } 273 else { 274 if (slashPos > -1) { 275 throw new XMLConfigException("An attribute cannot have subparts (foo.bar.fum and foo.bar/fum not allowed)"); 276 } 277 if (!initialDot) { 278 nodeName = path.substring(0, dotPos); 279 path = path.substring(dotPos+1); 280 dotRead = true; 281 } 282 else { 283 path = nodeName = path.substring(1); 284 getMultipleAddAttributesHelper(path, n, accum); 285 return; 286 } 287 } 288 Node child = n.getFirstChild(); 289 if (nodeName.equals("*")) { 290 while(child!=null) { 291 if (!child.getNodeName().equals("#text") && !child.getNodeName().equals("#comment")) { 292 if (dotRead) { 293 getMultipleAddAttributesHelper(path, child, accum); 294 } 295 else { 296 getMultipleHelper(path, child, accum, false); 297 } 298 } 299 child = child.getNextSibling(); 300 } 301 return; 302 } 303 else { 304 while(child!=null) { 305 if (child.getNodeName().equals(nodeName)) { 306 // found 307 if (dotRead) { 308 getMultipleAddAttributesHelper(path, child, accum); 309 } 310 else { 311 getMultipleHelper(path, child, accum, false); 312 } 313 } 314 child = child.getNextSibling(); 315 } 316 return; 317 } 318 } 319 else { 320 accum.add(n); 321 } 322 } 323 324 private void getMultipleAddAttributesHelper(String path, Node n, List<Node> accum) { 325 if ((path.indexOf('.') > -1) || (path.indexOf('/') > -1)) { 326 throw new XMLConfigException("An attribute cannot have subparts (foo.bar.fum and foo.bar/fum not allowed)"); 327 } 328 NamedNodeMap attrMap = n.getAttributes(); 329 if (path.equals("*")) { 330 for(int i=0; i<attrMap.getLength(); ++i) { 331 accum.add(attrMap.item(i)); 332 } 333 } 334 else { 335 Node attr = attrMap.getNamedItem(path); 336 accum.add(attr); 337 } 338 } 339 340 /** 341 * Returns the value as specified by the DOM path, or the default value if the value could not be found. 342 * @param path DOM path 343 * @param defaultVal default value in case value is not in DOM 344 * @return value. 345 */ 346 public String get(String path, String defaultVal) { 347 try { 348 return get(path); 349 } 350 catch(XMLConfigException e) { 351 return defaultVal; 352 } 353 } 354 355 /** 356 * Set the value of the node or attribute specified by the DOM path. 357 * @param path DOM path 358 * @param value node or attribute value 359 * @return the node that was created, or the parent node of the attribute if it was an attribute 360 */ 361 public Node set(String path, String value) { 362 return set(path, value, _document, true); 363 } 364 365 /** 366 * Set the value of the node or attribute specified by the DOM path. 367 * @param path DOM path 368 * @param value node or attribute value 369 * @param overwrite whether to overwrite (true) or add (false) 370 * @return the node that was created, or the parent node of the attribute if it was an attribute 371 */ 372 public Node set(String path, String value, boolean overwrite) { 373 return set(path, value, _document, overwrite); 374 } 375 376 377 /** 378 * Set the value of the node or attribute specified by the DOM path. 379 * @param path DOM path 380 * @param value node or attribute value 381 * @param n node where the search should start 382 * @param overwrite whether to overwrite (true) or add (false) -- only applies for last node! 383 * @return the node that was created, or the parent node of the attribute if it was an attribute 384 */ 385 public Node set(String path, String value, Node n, boolean overwrite) { 386 int dotPos = path.lastIndexOf('.'); 387 Node node; 388 if (dotPos==0) { 389 node = n; 390 } 391 else { 392 node = createNode(path, n, overwrite); 393 } 394 if (dotPos>=0) { 395 Element e = (Element)node; 396 e.setAttribute(path.substring(dotPos+1),value); 397 } 398 else { 399 node.appendChild(_document.createTextNode(value)); 400 } 401 return node; 402 } 403 404 /** 405 * Create the node specified by the DOM path. 406 * @param path DOM path 407 * @return the node that was created, or the parent node of the attribute if it was an attribute 408 */ 409 public Node createNode(String path) { 410 return createNode(path, _document, true); 411 } 412 413 /** 414 * Create the node specified by the DOM path. 415 * @param path DOM path 416 * @param n node where the search should start, or null for the root 417 * @return the node that was created, or the parent node of the attribute if it was an attribute 418 */ 419 public Node createNode(String path, Node n) { 420 return createNode(path, n, true); 421 } 422 423 /** 424 * Create the node specified by the DOM path. 425 * @param path DOM path 426 * @param n node where the search should start, or null for the root 427 * @param overwrite whether to overwrite (true) or add (false) -- only applies for last node! 428 * @return the node that was created, or the parent node of the attribute if it was an attribute 429 */ 430 public Node createNode(String path, Node n, boolean overwrite) { 431 if (n==null) { n = _document; } 432 while(path.indexOf('/') > -1) { 433 Node child = null; 434 String nodeName = path.substring(0, path.indexOf('/')); 435 path = path.substring(path.indexOf('/')+1); 436 child = n.getFirstChild(); 437 while(child!=null) { 438 if (child.getNodeName().equals(nodeName)) { 439 // found 440 n = child; 441 break; 442 } 443 child = child.getNextSibling(); 444 } 445 if (child==null) { 446 // not found 447 child = _document.createElement(nodeName); 448 n.appendChild(child); 449 n = child; 450 } 451 } 452 453 String nodeName; 454 if (path.indexOf('.') > -1) { 455 nodeName = path.substring(0, path.indexOf('.')); 456 } 457 else { 458 if (path.length()==0) { 459 throw new XMLConfigException("Cannot set node with empty name"); 460 } 461 nodeName = path; 462 } 463 Node child = null; 464 if (nodeName.length()>0) { 465 if (overwrite) { 466 child = n.getFirstChild(); 467 while(child!=null) { 468 if (child.getNodeName().equals(nodeName)) { 469 // found 470 n = child; 471 break; 472 } 473 child = child.getNextSibling(); 474 } 475 if (child==null) { 476 child = _document.createElement(nodeName); 477 n.appendChild(child); 478 n = child; 479 } 480 } 481 else { 482 child = _document.createElement(nodeName); 483 n.appendChild(child); 484 n = child; 485 } 486 } 487 488 if (path.indexOf('.') > -1) { 489 if (!(n instanceof Element)) { 490 throw new XMLConfigException("Node "+n.getNodeName()+" should be an element so it can contain attributes"); 491 } 492 return n; 493 } 494 else { 495 if (overwrite) { 496 child = n.getFirstChild(); 497 // remove all children 498 while(child!=null) { 499 Node temp = child.getNextSibling(); 500 n.removeChild(child); 501 child = temp; 502 } 503 return n; 504 } 505 else { 506 return child; 507 } 508 } 509 } 510 511 512 /** 513 * Returns a string representation of the object. 514 * @return a string representation of the object. 515 */ 516 public String toString() { 517 ByteArrayOutputStream os = new ByteArrayOutputStream(); 518 save(os); 519 return os.toString(); 520 } 521 522 /** 523 * Return the path of a node as it is used in XMLConfig. 524 * @param n node 525 * @return path 526 */ 527 public static String getNodePath(Node n) { 528 if (n==null) { return ""; } 529 String path = ""; 530 while(n.getParentNode()!=null) { 531 path = n.getNodeName()+"/"+path; 532 n = n.getParentNode(); 533 } 534 535 return path.substring(0,path.length()-1); 536 } 537 538 /** 539 * Exception in XMLConfig methods. 540 */ 541 public static class XMLConfigException extends RuntimeException { 542 public XMLConfigException() { 543 super(); 544 } 545 546 public XMLConfigException(String message) { 547 super(message); 548 } 549 550 public XMLConfigException(String message, Throwable cause) { 551 super(message, cause); 552 } 553 554 public XMLConfigException(Throwable cause) { 555 super(cause); 556 } 557 } 558 559 /** 560 * Unit tests. 561 */ 562 public static class XMLConfigTest extends TestCase { 563 public void testNodes() throws Exception { 564 XMLConfig xc = new XMLConfig(new StringReader( 565 "<?xml version=\"1.0\" encoding=\"UTF-8\"?><foo a=\"foo.a\">\n" 566 + " <bar>abc</bar>\n" 567 + " <fum fee=\"xyz\">def</fum>\n" 568 + "</foo>")); 569 assertEquals("abc", xc.get("foo/bar")); 570 assertEquals("def", xc.get("foo/fum")); 571 } 572 public void testAttrs() throws Exception { 573 XMLConfig xc = new XMLConfig(new StringReader( 574 "<?xml version=\"1.0\" encoding=\"UTF-8\"?><foo a=\"foo.a\">\n" 575 + " <bar>abc</bar>\n" 576 + " <fum fee=\"xyz\">def</fum>\n" 577 + "</foo>")); 578 assertEquals("foo.a", xc.get("foo.a")); 579 assertEquals("xyz", xc.get("foo/fum.fee")); 580 } 581 public void testExceptions() throws Exception { 582 XMLConfig xc = new XMLConfig(new StringReader( 583 "<?xml version=\"1.0\" encoding=\"UTF-8\"?><foo a=\"foo.a\">\n" 584 + " <bar>abc</bar>\n" 585 + " <fum fee=\"xyz\">def</fum>\n" 586 + "</foo>")); 587 try { 588 xc.get(""); 589 fail("Should throw 'no node' exception"); 590 } 591 catch (RuntimeException e) { 592 // ignore 593 } 594 try { 595 xc.get("xyz"); 596 fail("Should throw 'no node' exception"); 597 } 598 catch (RuntimeException e) { 599 // ignore 600 } 601 try { 602 xc.get("foo.xyz"); 603 fail("Should throw 'no node' exception"); 604 } 605 catch (RuntimeException e) { 606 // ignore 607 } 608 try { 609 xc.get("foo.b"); 610 fail("Should throw 'no attribute' exception"); 611 } 612 catch (RuntimeException e) { 613 // ignore 614 } 615 } 616 private String remove16XML(String s) { 617 if ((System.getProperty("java.version").startsWith("1.5")) || 618 (System.getProperty("java.version").startsWith("1.4"))) { 619 int pos1 = s.indexOf(" standalone="); 620 int pos2 = s.indexOf("?>", pos1); 621 return s.substring(0, pos1) + s.substring(pos2); 622 } 623 else { 624 return s; 625 } 626 } 627 public void testSave() throws Exception { 628 XMLConfig xc = new XMLConfig(new StringReader( 629 "<?xml version=\"1.0\" encoding=\"UTF-8\"?><foo a=\"foo.a\">\n" 630 + " <bar>abc</bar>\n" 631 + " <fum fee=\"xyz\">def</fum>\n" 632 + "</foo>")); 633 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL + 634 "<foo a=\"foo.a\">"+NL + 635 " <bar>abc</bar>"+NL + 636 " <fum fee=\"xyz\">def</fum>"+NL + 637 "</foo>"+NL), xc.toString()); 638 } 639 public void testSetNodeFromEmpty() throws Exception { 640 XMLConfig xc = new XMLConfig(); 641 xc.set("foo/bar", "abc"); 642 643 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo>"+NL+" <bar>abc</bar>"+NL+"</foo>"+NL), xc.toString()); 644 assertEquals("abc", xc.get("foo/bar")); 645 646 xc.set("foo/fum", "def"); 647 648 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo>"+NL+" <bar>abc</bar>"+NL+" <fum>def</fum>"+NL+"</foo>"+NL), xc.toString()); 649 assertEquals("def", xc.get("foo/fum")); 650 } 651 public void testSetNodeOverwrite() throws Exception { 652 XMLConfig xc = new XMLConfig(); 653 xc.set("foo/bar", "abc"); 654 655 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo>"+NL+" <bar>abc</bar>"+NL+"</foo>"+NL), xc.toString()); 656 assertEquals("abc", xc.get("foo/bar")); 657 658 xc.set("foo/bar", "def"); 659 660 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo>"+NL+" <bar>def</bar>"+NL+"</foo>"+NL), xc.toString()); 661 assertEquals("def", xc.get("foo/bar")); 662 663 xc.set("foo", "xyz"); 664 665 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo>xyz</foo>"+NL), xc.toString()); 666 assertEquals("xyz", xc.get("foo")); 667 } 668 public void testSetAttrFromEmpty() throws Exception { 669 XMLConfig xc = new XMLConfig(); 670 xc.set("foo.bar", "abc"); 671 672 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo bar=\"abc\"/>"+NL), xc.toString()); 673 assertEquals("abc", xc.get("foo.bar")); 674 675 xc.set("foo/fum.fee", "def"); 676 677 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo bar=\"abc\">"+NL+" <fum fee=\"def\"/>"+NL+"</foo>"+NL), xc.toString()); 678 assertEquals("def", xc.get("foo/fum.fee")); 679 } 680 public void testSetAttrOverwrite() throws Exception { 681 XMLConfig xc = new XMLConfig(); 682 xc.set("foo.bar", "abc"); 683 684 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo bar=\"abc\"/>"+NL), xc.toString()); 685 assertEquals("abc", xc.get("foo.bar")); 686 687 xc.set("foo.bar", "def"); 688 689 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo bar=\"def\"/>"+NL), xc.toString()); 690 assertEquals("def", xc.get("foo.bar")); 691 } 692 public void testSetNodeNoOverwrite() throws Exception { 693 XMLConfig xc = new XMLConfig(); 694 xc.set("foo/bar", "abc", false); 695 696 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo>"+NL+" <bar>abc</bar>"+NL+"</foo>"+NL), xc.toString()); 697 assertEquals("abc", xc.get("foo/bar")); 698 699 xc.set("foo/bar", "def", false); 700 701 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo>"+NL+" <bar>abc</bar>"+NL+" <bar>def</bar>"+NL+"</foo>"+NL), xc.toString()); 702 List<String> r = xc.getMultiple("foo/bar"); 703 assertEquals(2, r.size()); 704 assertEquals("abc", r.get(0)); 705 assertEquals("def", r.get(1)); 706 } 707 public void testSetAttrNoOverwrite() throws Exception { 708 XMLConfig xc = new XMLConfig(); 709 xc.set("foo/bar.fee", "abc", false); 710 711 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo>"+NL+" <bar fee=\"abc\"/>"+NL+"</foo>"+NL), xc.toString()); 712 assertEquals("abc", xc.get("foo/bar.fee")); 713 714 xc.set("foo/bar.fee", "def", false); 715 716 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo>"+NL+" <bar fee=\"abc\"/>"+NL+" <bar fee=\"def\"/>"+NL+"</foo>"+NL), xc.toString()); 717 List<String> r = xc.getMultiple("foo/bar.fee"); 718 assertEquals(2, r.size()); 719 assertEquals("abc", r.get(0)); 720 assertEquals("def", r.get(1)); 721 } 722 public void testSetFromNode() throws Exception { 723 XMLConfig xc = new XMLConfig(); 724 Node n = xc.set("foo/bar", "abc", false); 725 726 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo>"+NL+" <bar>abc</bar>"+NL+"</foo>"+NL), xc.toString()); 727 assertEquals("abc", xc.get("foo/bar")); 728 729 xc.set(".fuz", "def", n, false); 730 731 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo>"+NL+" <bar fuz=\"def\">abc</bar>"+NL+"</foo>"+NL), xc.toString()); 732 assertEquals("abc", xc.get("foo/bar")); 733 734 n = xc.set("fum", "", n.getParentNode(), false); 735 736 if (System.getProperty("java.version").startsWith("1.5")) { 737 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo>"+NL+" <bar fuz=\"def\">abc</bar>"+NL+" <fum></fum>"+NL+"</foo>"+NL), 738 xc.toString()); 739 } 740 else { 741 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo>"+NL+" <bar fuz=\"def\">abc</bar>"+NL+" <fum/>"+NL+"</foo>"+NL), 742 xc.toString()); 743 } 744 assertEquals("", xc.get("foo/fum")); 745 746 xc.set("file", "test1.txt", n, false); 747 xc.set("file", "test2.txt", n, false); 748 749 if (System.getProperty("java.version").startsWith("1.5")) { 750 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo>"+NL+" <bar fuz=\"def\">abc</bar>"+NL 751 + " <fum><file>test1.txt</file>"+NL+" <file>test2.txt</file>"+NL+" </fum>"+NL+"</foo>"+NL), 752 xc.toString()); 753 } 754 else { 755 assertEquals(remove16XML("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+NL+"<foo>"+NL+" <bar fuz=\"def\">abc</bar>"+NL 756 + " <fum>"+NL+" <file>test1.txt</file>"+NL+" <file>test2.txt</file>"+NL+" </fum>"+NL+"</foo>"+NL), 757 xc.toString()); 758 } 759 List<String> r = xc.getMultiple("foo/fum/file"); 760 assertEquals(2, r.size()); 761 assertEquals("test1.txt", r.get(0)); 762 assertEquals("test2.txt", r.get(1)); 763 } 764 765 public void testMultipleNodes() throws Exception { 766 XMLConfig xc = new XMLConfig(new StringReader( 767 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><foo a=\"foo.a\">\n" 768 + " <bar>abc</bar>\n" 769 + " <bar>ghi</bar>\n" 770 + " <fum fee=\"xyz\">def</fum>\n" 771 + "</foo>")); 772 List<String> r = xc.getMultiple("foo/bar"); 773 assertEquals(2, r.size()); 774 assertEquals("abc", r.get(0)); 775 assertEquals("ghi", r.get(1)); 776 } 777 public void testMultipleNodesAttr() throws Exception { 778 XMLConfig xc = new XMLConfig(new StringReader( 779 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><foo a=\"foo.a\">\n" 780 + " <bar fuz=\"aaa\">abc</bar>\n" 781 + " <bar fuz=\"bbb\">ghi</bar>\n" 782 + " <fum fee=\"xyz\">def</fum>\n" 783 + "</foo>")); 784 List<String> r = xc.getMultiple("foo/bar.fuz"); 785 assertEquals(2, r.size()); 786 assertEquals("aaa", r.get(0)); 787 assertEquals("bbb", r.get(1)); 788 } 789 public void testNodesStarEnd() throws Exception { 790 XMLConfig xc = new XMLConfig(new StringReader( 791 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><foo a=\"foo.a\">\n" 792 + " <bar>abc</bar>\n" 793 + " <bar>ghi</bar>\n" 794 + " <fum fee=\"xyz\">def</fum>\n" 795 + "</foo>")); 796 List<String> r = xc.getMultiple("foo/*"); 797 assertEquals(3, r.size()); 798 assertEquals("abc", r.get(0)); 799 assertEquals("ghi", r.get(1)); 800 assertEquals("def", r.get(2)); 801 } 802 public void testNodesStarMiddle() throws Exception { 803 XMLConfig xc = new XMLConfig(new StringReader( 804 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><foo a=\"foo.a\">\n" 805 + " <bar fee=\"xxx\" fuz=\"aaa\">abc</bar>\n" 806 + " <bar fee=\"yyy\" fuz=\"bbb\">ghi</bar>\n" 807 + " <fum fee=\"zzz\" fuz=\"ccc\">def</fum>\n" 808 + "</foo>")); 809 List<String> r = xc.getMultiple("foo/*.fee"); 810 assertEquals(3, r.size()); 811 assertEquals("xxx", r.get(0)); 812 assertEquals("yyy", r.get(1)); 813 assertEquals("zzz", r.get(2)); 814 815 r = xc.getMultiple("foo/*.*"); 816 assertEquals(6, r.size()); 817 assertEquals("xxx", r.get(0)); 818 assertEquals("aaa", r.get(1)); 819 assertEquals("yyy", r.get(2)); 820 assertEquals("bbb", r.get(3)); 821 assertEquals("zzz", r.get(4)); 822 assertEquals("ccc", r.get(5)); 823 824 xc = new XMLConfig(new StringReader("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><flub>\n" 825 + " <foo/>\n" 826 + " <fee/>\n" 827 + " <fum foz=\"abc\"/>" 828 + "</flub>")); 829 r = xc.getMultiple("flub/*"); 830 assertEquals(3, r.size()); 831 } 832 public void testAttrsStarEnd() throws Exception { 833 XMLConfig xc = new XMLConfig(new StringReader( 834 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><foo a=\"foo.a\">\n" 835 + " <bar>abc</bar>\n" 836 + " <fum fee=\"xyz\" fuz=\"zzz\" fiz=\"aaa\">def</fum>\n" 837 + "</foo>")); 838 List<String> r = xc.getMultiple("foo.*"); 839 assertEquals(1, r.size()); 840 assertEquals("foo.a", r.get(0)); 841 842 r = xc.getMultiple("foo/fum.*"); 843 assertEquals(3, r.size()); 844 assertEquals("xyz", r.get(0)); 845 assertEquals("aaa", r.get(1)); 846 assertEquals("zzz", r.get(2)); 847 } 848 public void testNodeStarAttrsStar() throws Exception { 849 XMLConfig xc = new XMLConfig(new StringReader( 850 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><foo a=\"foo.a\">\n" 851 + " <bar flubb=\"mno\">abc</bar>\n" 852 + " <fum fee=\"xyz\" fuz=\"zzz\" fiz=\"aaa\">def</fum>\n" 853 + "</foo>")); 854 List<String> r = xc.getMultiple("*.*"); 855 assertEquals(1, r.size()); 856 assertEquals("foo.a", r.get(0)); 857 858 r = xc.getMultiple("foo/*.*"); 859 assertEquals(4, r.size()); 860 assertEquals("mno", r.get(0)); 861 assertEquals("xyz", r.get(1)); 862 assertEquals("aaa", r.get(2)); 863 assertEquals("zzz", r.get(3)); 864 } 865 866 public void testGetNodePath1() throws Exception { 867 XMLConfig xc = new XMLConfig(new StringReader( 868 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><concutest>\n" 869 +" <threadcheck:def>\n" 870 +" <invariant>\n" 871 +" <name type=\"only\" value=\"childclass1\"/>\n" 872 +" </invariant>\n" 873 +" <class name=\"sample.threadCheck.ThreadCheckSample4\"/>\n" 874 +" </threadcheck:def>\n" 875 +" <threadcheck:def>\n" 876 +" <invariant>\n" 877 +" <name type=\"only\" value=\"childclass-method1\"/>\n" 878 +" </invariant>\n" 879 +" <method name=\"sample.threadCheck.ThreadCheckSample4\" sig=\"run()V\"/>\n" 880 +" <method name=\"sample.threadCheck.ThreadCheckSample4\" sig=\"run2()V\"/>\n" 881 +" </threadcheck:def>\n" 882 +"</concutest>")); 883 assertEquals("Path of null is wrong", "", getNodePath(null)); 884 885 List<Node> roots = xc.getNodes("concutest"); 886 Assert.assertEquals(1, roots.size()); 887 assertEquals("Path of "+roots.get(0).getNodeName()+" is wrong", "concutest", getNodePath(roots.get(0))); 888 889 List<Node> defs = xc.getNodes("concutest/threadcheck:def"); 890 Assert.assertEquals(2, defs.size()); 891 892 for(Node def: defs) { 893 assertEquals("Path of "+def.getNodeName()+" is wrong", "concutest/threadcheck:def", getNodePath(def)); 894 List<Node> invs = xc.getNodes("invariant", def); 895 Assert.assertEquals(1, invs.size()); 896 Node inv = invs.get(0); 897 assertEquals("Path of "+inv.getNodeName()+" is wrong", "concutest/threadcheck:def/invariant", getNodePath(inv)); 898 List<Node> annots = xc.getNodes("*", inv); 899 Assert.assertEquals(1, annots.size()); 900 assertEquals("Path of "+annots.get(0).getNodeName()+" is wrong", "concutest/threadcheck:def/invariant/name", getNodePath(annots.get(0))); 901 List<Node> classes = xc.getNodes("class", def); 902 List<Node> methods = xc.getNodes("method", def); 903 Assert.assertTrue("There must be at least one class or method per definition", (classes.size()+methods.size()>0)); 904 List<Node> all = xc.getNodes("*", def); 905 Assert.assertEquals(0, all.size()-invs.size()-classes.size()-methods.size()); 906 for(Node target: classes) { 907 assertEquals("Path of "+target.getNodeName()+" is wrong", "concutest/threadcheck:def/class", getNodePath(target)); 908 } 909 for(Node target: methods) { 910 assertEquals("Path of "+target.getNodeName()+" is wrong", "concutest/threadcheck:def/method", getNodePath(target)); 911 } 912 } 913 } 914 915 public void testXMLConcDef() throws Exception { 916 XMLConfig xc = new XMLConfig(new StringReader( 917 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><concutest>\n" 918 +" <threadcheck:def>\n" 919 +" <invariant>\n" 920 +" <name type=\"only\" value=\"childclass1\"/>\n" 921 +" </invariant>\n" 922 +" <class name=\"sample.threadCheck.ThreadCheckSample4\"/>\n" 923 +" </threadcheck:def>\n" 924 +" <threadcheck:def>\n" 925 +" <invariant>\n" 926 +" <name type=\"only\" value=\"childclass-method1\"/>\n" 927 +" </invariant>\n" 928 +" <method name=\"sample.threadCheck.ThreadCheckSample4\" sig=\"run()V\"/>\n" 929 +" <method name=\"sample.threadCheck.ThreadCheckSample4\" sig=\"run2()V\"/>\n" 930 +" <class name=\"sample.threadCheck.ThreadCheckSample4\"/>\n" 931 +" </threadcheck:def>\n" 932 +"</concutest>")); 933 List<Node> defs = xc.getNodes("concutest/threadcheck:def"); 934 Assert.assertEquals(2, defs.size()); 935 Set<ThreadCheckException> tces = AAddThreadCheckStrategy.checkXMLConcDef(xc); 936 assertEquals(0, tces.size()); 937 } 938 939 public void testXMLConcDefWrong1() throws Exception { 940 XMLConfig xc = new XMLConfig(new StringReader( 941 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><concutest>\n" 942 +" <threadcheck:def>\n" 943 +" <invariant>\n" 944 +" <name type=\"only\" value=\"childclass1\"/>\n" 945 +" <name type=\"only\" value=\"childclass2\"/>\n" 946 +" </invariant>\n" 947 +" <class name=\"sample.threadCheck.ThreadCheckSample4\"/>\n" 948 +" </threadcheck:def>\n" 949 +" <threadcheck:def>\n" 950 +" <invariant>\n" 951 +" <name type=\"only\" value=\"childclass-method1\"/>\n" 952 +" </invariant>\n" 953 +" <method name=\"sample.threadCheck.ThreadCheckSample4\" sig=\"run()V\"/>\n" 954 +" <method name=\"sample.threadCheck.ThreadCheckSample4\" sig=\"run2()V\"/>\n" 955 +" </threadcheck:def>\n" 956 +"</concutest>")); 957 List<Node> defs = xc.getNodes("concutest/threadcheck:def"); 958 Assert.assertEquals(2, defs.size()); 959 Set<ThreadCheckException> tces = AAddThreadCheckStrategy.checkXMLConcDef(xc); 960 assertEquals(1, tces.size()); 961 assertTrue("Wrong message", 962 tces.contains(new ThreadCheckException("There must be exactly one annotation per invariant, found 2 (path: concutest/threadcheck:def/invariant/name)"))); 963 } 964 965 public void testXMLConcDefWrong2() throws Exception { 966 XMLConfig xc = new XMLConfig(new StringReader( 967 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><concutest>\n" 968 +" <threadcheck:def>\n" 969 +" <invariant>\n" 970 +" <name type=\"only\" value=\"childclass1\"/>\n" 971 +" </invariant>\n" 972 +" <invariant>\n" 973 +" <name type=\"only\" value=\"childclass1\"/>\n" 974 +" </invariant>\n" 975 +" <class name=\"sample.threadCheck.ThreadCheckSample4\"/>\n" 976 +" </threadcheck:def>\n" 977 +" <threadcheck:def>\n" 978 +" <invariant>\n" 979 +" <name type=\"only\" value=\"childclass-method1\"/>\n" 980 +" </invariant>\n" 981 +" <method name=\"sample.threadCheck.ThreadCheckSample4\" sig=\"run()V\"/>\n" 982 +" <method name=\"sample.threadCheck.ThreadCheckSample4\" sig=\"run2()V\"/>\n" 983 +" </threadcheck:def>\n" 984 +"</concutest>")); 985 List<Node> defs = xc.getNodes("concutest/threadcheck:def"); 986 Assert.assertEquals(2, defs.size()); 987 Set<ThreadCheckException> tces = AAddThreadCheckStrategy.checkXMLConcDef(xc); 988 assertEquals(1, tces.size()); 989 assertTrue("Wrong message", 990 tces.contains(new ThreadCheckException("There must be exactly one invariant per definition, found 2 (path: concutest/threadcheck:def/invariant)"))); 991 } 992 993 public void testXMLConcDefWrong3() throws Exception { 994 XMLConfig xc = new XMLConfig(new StringReader( 995 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><concutest>\n" 996 +" <threadcheck:def>\n" 997 +" <class name=\"sample.threadCheck.ThreadCheckSample4\"/>\n" 998 +" </threadcheck:def>\n" 999 +" <threadcheck:def>\n" 1000 +" <invariant>\n" 1001 +" <name type=\"only\" value=\"childclass-method1\"/>\n" 1002 +" </invariant>\n" 1003 +" <method name=\"sample.threadCheck.ThreadCheckSample4\" sig=\"run()V\"/>\n" 1004 +" <method name=\"sample.threadCheck.ThreadCheckSample4\" sig=\"run2()V\"/>\n" 1005 +" </threadcheck:def>\n" 1006 +"</concutest>")); 1007 List<Node> defs = xc.getNodes("concutest/threadcheck:def"); 1008 Assert.assertEquals(2, defs.size()); 1009 Set<ThreadCheckException> tces = AAddThreadCheckStrategy.checkXMLConcDef(xc); 1010 assertEquals(1, tces.size()); 1011 assertTrue("Wrong message", 1012 tces.contains(new ThreadCheckException("There must be exactly one invariant per definition, found 0 (path: concutest/threadcheck:def/invariant)"))); 1013 } 1014 1015 public void testXMLConcDefWrong4() throws Exception { 1016 XMLConfig xc = new XMLConfig(new StringReader( 1017 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><concutest>\n" 1018 +" <threadcheck:def>\n" 1019 +" <invariant>\n" 1020 +" <name type=\"only\" value=\"childclass2\"/>\n" 1021 +" </invariant>\n" 1022 +" </threadcheck:def>\n" 1023 +" <threadcheck:def>\n" 1024 +" <invariant>\n" 1025 +" <name type=\"only\" value=\"childclass-method1\"/>\n" 1026 +" </invariant>\n" 1027 +" <method name=\"sample.threadCheck.ThreadCheckSample4\" sig=\"run()V\"/>\n" 1028 +" <method name=\"sample.threadCheck.ThreadCheckSample4\" sig=\"run2()V\"/>\n" 1029 +" </threadcheck:def>\n" 1030 +"</concutest>")); 1031 List<Node> defs = xc.getNodes("concutest/threadcheck:def"); 1032 Assert.assertEquals(2, defs.size()); 1033 Set<ThreadCheckException> tces = AAddThreadCheckStrategy.checkXMLConcDef(xc); 1034 assertEquals(1, tces.size()); 1035 assertTrue("Wrong message", 1036 tces.contains(new ThreadCheckException("There must be at least one class or method per definition, found 0 <class> and 0 <method> (path: concutest/threadcheck:def/)"))); 1037 } 1038 1039 public void testXMLConcDefWrong5() throws Exception { 1040 XMLConfig xc = new XMLConfig(new StringReader( 1041 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><concutest>\n" 1042 +" <threadcheck:def>\n" 1043 +" <invariant>\n" 1044 +" <name type=\"only\" value=\"childclass2\"/>\n" 1045 +" </invariant>\n" 1046 +" <class name=\"sample.threadCheck.ThreadCheckSample4\"/>\n" 1047 +" </threadcheck:def>\n" 1048 +" <threadcheck:def>\n" 1049 +" <invariant>\n" 1050 +" <name type=\"only\" value=\"childclass-method1\"/>\n" 1051 +" </invariant>\n" 1052 +" </threadcheck:def>\n" 1053 +"</concutest>")); 1054 List<Node> defs = xc.getNodes("concutest/threadcheck:def"); 1055 Assert.assertEquals(2, defs.size()); 1056 Set<ThreadCheckException> tces = AAddThreadCheckStrategy.checkXMLConcDef(xc); 1057 assertEquals(1, tces.size()); 1058 assertTrue("Wrong message", 1059 tces.contains(new ThreadCheckException("There must be at least one class or method per definition, found 0 <class> and 0 <method> (path: concutest/threadcheck:def/)"))); 1060 } 1061 1062 public void testXMLConcDefWrong6() throws Exception { 1063 XMLConfig xc = new XMLConfig(new StringReader( 1064 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><concutest>\n" 1065 +" <threadcheck:def>\n" 1066 +" <invariant>\n" 1067 +" <name type=\"only\" value=\"childclass2\"/>\n" 1068 +" </invariant>\n" 1069 +" <class name=\"sample.threadCheck.ThreadCheckSample4\">\n" 1070 +" <foo/>\n" 1071 +" </class>\n" 1072 +" </threadcheck:def>\n" 1073 +" <threadcheck:def>\n" 1074 +" <invariant>\n" 1075 +" <name type=\"only\" value=\"childclass-method1\"/>\n" 1076 +" </invariant>\n" 1077 +" </threadcheck:def>\n" 1078 +"</concutest>")); 1079 List<Node> defs = xc.getNodes("concutest/threadcheck:def"); 1080 Assert.assertEquals(2, defs.size()); 1081 Set<ThreadCheckException> tces = AAddThreadCheckStrategy.checkXMLConcDef(xc); 1082 assertEquals(2, tces.size()); 1083 assertTrue("Wrong message", 1084 tces.contains(new ThreadCheckException("A class node should have no child nodes, found 1 (path: concutest/threadcheck:def/class)"))); 1085 assertTrue("Wrong message", 1086 tces.contains(new ThreadCheckException("There must be at least one class or method per definition, found 0 <class> and 0 <method> (path: concutest/threadcheck:def/)"))); 1087 } 1088 1089 public void testXMLConcDefWrong7() throws Exception { 1090 XMLConfig xc = new XMLConfig(new StringReader( 1091 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><concutest>\n" 1092 +" <threadcheck:def>\n" 1093 +" <invariant>\n" 1094 +" <name type=\"only\" value=\"childclass2\"/>\n" 1095 +" </invariant>\n" 1096 +" <class name=\"sample.threadCheck.ThreadCheckSample4\"/>\n" 1097 +" </threadcheck:def>\n" 1098 +" <threadcheck:def>\n" 1099 +" <invariant>\n" 1100 +" <name type=\"only\" value=\"childclass-method1\"/>\n" 1101 +" </invariant>\n" 1102 +" <method name=\"sample.threadCheck.ThreadCheckSample4\" sig=\"run2()V\">\n" 1103 +" <foo/>\n" 1104 +" </method>\n" 1105 +" </threadcheck:def>\n" 1106 +"</concutest>")); 1107 List<Node> defs = xc.getNodes("concutest/threadcheck:def"); 1108 Assert.assertEquals(2, defs.size()); 1109 Set<ThreadCheckException> tces = AAddThreadCheckStrategy.checkXMLConcDef(xc); 1110 assertEquals(1, tces.size()); 1111 assertTrue("Wrong message", 1112 tces.contains(new ThreadCheckException("A method node should have no child nodes, there were 1 (path: concutest/threadcheck:def/method)"))); 1113 } 1114 1115 public void testXMLConcDefWrong8() throws Exception { 1116 XMLConfig xc = new XMLConfig(new StringReader( 1117 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><concutest>\n" 1118 +" <threadcheck:def>\n" 1119 +" <invariant>\n" 1120 +" <name type=\"only\" value=\"childclass2\"/>\n" 1121 +" </invariant>\n" 1122 +" <class name=\"sample.threadCheck.ThreadCheckSample4\"/>\n" 1123 +" </threadcheck:def>\n" 1124 +" <threadcheck:def>\n" 1125 +" <invariant>\n" 1126 +" <name type=\"only\" value=\"childclass-method1\"/>\n" 1127 +" </invariant>\n" 1128 +" <method name=\"sample.threadCheck.ThreadCheckSample4\" sig=\"run2()V\"/>\n" 1129 +" <foo/>\n" 1130 +" </threadcheck:def>\n" 1131 +"</concutest>")); 1132 List<Node> defs = xc.getNodes("concutest/threadcheck:def"); 1133 Assert.assertEquals(2, defs.size()); 1134 Set<ThreadCheckException> tces = AAddThreadCheckStrategy.checkXMLConcDef(xc); 1135 assertEquals(1, tces.size()); 1136 assertTrue("Wrong message", 1137 tces.contains(new ThreadCheckException("There must be exactly one invariant and at least one class or method per definition, only <invariant>, <class> and <method> are allowed , found 0 <class> and 1 <method>, 1 <invariant>, but 3 children (path: concutest/threadcheck:def/)"))); 1138 } 1139 } 1140 }