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    }