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 }