Clover coverage report - groovy - 1.0-beta-6
Coverage timestamp: Thu Jul 15 2004 13:18:22 BST
file stats: LOC: 562   Methods: 43
NCLOC: 362   Classes: 5
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
XmlSlurper.java 0% 0% 0% 0%
coverage
 1   
 package org.codehaus.groovy.sandbox.util;
 2   
 import groovy.lang.GroovyObjectSupport;
 3   
 
 4   
 import java.io.File;
 5   
 import java.io.FileInputStream;
 6   
 import java.io.IOException;
 7   
 import java.io.InputStream;
 8   
 import java.io.Reader;
 9   
 import java.io.StringReader;
 10   
 import java.security.AccessController;
 11   
 import java.security.PrivilegedActionException;
 12   
 import java.security.PrivilegedExceptionAction;
 13   
 import java.util.HashMap;
 14   
 import java.util.Iterator;
 15   
 import java.util.LinkedList;
 16   
 import java.util.List;
 17   
 import java.util.Map;
 18   
 
 19   
 import javax.xml.parsers.ParserConfigurationException;
 20   
 import javax.xml.parsers.SAXParser;
 21   
 import javax.xml.parsers.SAXParserFactory;
 22   
 
 23   
 import org.xml.sax.Attributes;
 24   
 import org.xml.sax.InputSource;
 25   
 import org.xml.sax.SAXException;
 26   
 import org.xml.sax.XMLReader;
 27   
 import org.xml.sax.helpers.DefaultHandler;
 28   
 
 29   
 
 30   
 public class XmlSlurper extends DefaultHandler {
 31   
     private final XMLReader reader;
 32   
     private List result = null;
 33   
     private List body = null;
 34   
     private final StringBuffer charBuffer = new StringBuffer();
 35   
 
 36  0
     public XmlSlurper() throws ParserConfigurationException, SAXException {
 37  0
         this(false, true);
 38   
     }
 39   
 
 40  0
     public XmlSlurper(final boolean validating, final boolean namespaceAware) throws ParserConfigurationException, SAXException {
 41  0
         SAXParserFactory factory = null;
 42   
         
 43  0
             try {
 44  0
                 factory = (SAXParserFactory) AccessController.doPrivileged(new PrivilegedExceptionAction() {
 45  0
                     public Object run() throws ParserConfigurationException {
 46  0
                         return SAXParserFactory.newInstance();
 47   
                     }
 48   
                 });
 49   
             } catch (final PrivilegedActionException pae) {
 50  0
             final Exception e = pae.getException();
 51   
                 
 52  0
                 if (e instanceof ParserConfigurationException) {
 53  0
                     throw (ParserConfigurationException) e;
 54   
                 } else {
 55  0
                     throw new RuntimeException(e);
 56   
                 }
 57   
             }
 58  0
         factory.setNamespaceAware(namespaceAware);
 59  0
         factory.setValidating(validating);
 60   
 
 61  0
         final SAXParser parser = factory.newSAXParser();
 62  0
         this.reader = parser.getXMLReader();
 63   
     }
 64   
 
 65  0
     public XmlSlurper(final XMLReader reader) {
 66  0
         this.reader = reader;
 67   
     }
 68   
 
 69  0
     public XmlSlurper(final SAXParser parser) throws SAXException {
 70  0
         this(parser.getXMLReader());
 71   
     }
 72   
 
 73   
     /**
 74   
      * Parse the content of the specified input source into a List
 75   
      */
 76  0
     public XmlList parse(final InputSource input) throws IOException, SAXException {
 77  0
             this.reader.setContentHandler(this);
 78  0
             this.reader.parse(input);
 79   
         
 80  0
         return (XmlList)this.result.get(0);
 81   
     }
 82   
     
 83   
     /**
 84   
      * Parses the content of the given file as XML turning it into a List
 85   
      */
 86  0
     public XmlList parse(final File file) throws IOException, SAXException {
 87  0
     final InputSource input = new InputSource(new FileInputStream(file));
 88   
     
 89  0
         input.setSystemId("file://" + file.getAbsolutePath());
 90   
         
 91  0
         return parse(input);
 92   
 
 93   
     }
 94   
 
 95   
     /**
 96   
      * Parse the content of the specified input stream into a List.
 97   
      * Note that using this method will not provide the parser with any URI
 98   
      * for which to find DTDs etc
 99   
      */
 100  0
     public XmlList parse(final InputStream input) throws IOException, SAXException {
 101  0
         return parse(new InputSource(input));
 102   
     }
 103   
 
 104   
     /**
 105   
      * Parse the content of the specified reader into a List.
 106   
      * Note that using this method will not provide the parser with any URI
 107   
      * for which to find DTDs etc
 108   
      */
 109  0
     public XmlList parse(final Reader in) throws IOException, SAXException {
 110  0
         return parse(new InputSource(in));
 111   
     }
 112   
 
 113   
     /**
 114   
      * Parse the content of the specified URI into a List
 115   
      */
 116  0
     public XmlList parse(final String uri) throws IOException, SAXException {
 117  0
         return parse(new InputSource(uri));
 118   
     }
 119   
 
 120   
     /**
 121   
      * A helper method to parse the given text as XML
 122   
      * 
 123   
      * @param text
 124   
      * @return
 125   
      */
 126  0
     public XmlList parseText(final String text) throws IOException, SAXException {
 127  0
         return parse(new StringReader(text));
 128   
     }
 129   
     
 130   
 
 131   
     // ContentHandler interface
 132   
     //-------------------------------------------------------------------------                    
 133   
     
 134   
     /* (non-Javadoc)
 135   
      * @see org.xml.sax.ContentHandler#startDocument()
 136   
      */
 137  0
     public void startDocument() throws SAXException {
 138  0
         this.result = null;
 139  0
         this.body = new LinkedList();
 140  0
         this.charBuffer.setLength(0);
 141   
     }
 142   
     
 143   
     /* (non-Javadoc)
 144   
      * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
 145   
      */
 146  0
     public void startElement(final String namespaceURI, final String localName, final String qName, final Attributes atts) throws SAXException {
 147  0
         addNonWhitespaceCdata();
 148   
         
 149  0
         final Map attributes = new HashMap();
 150   
         
 151  0
         for (int i = atts.getLength() - 1; i != -1; i--) {
 152  0
             if (atts.getURI(i).length() == 0) {
 153  0
                 attributes.put(atts.getQName(i), atts.getValue(i));
 154   
             } else {
 155   
                 //
 156   
                 // Note this is strictly incorrect the name is really localname + URI
 157   
                 // We need to figure out what to do with paramenters in namespaces
 158   
                 //
 159  0
                 attributes.put(atts.getLocalName(i), atts.getValue(i));
 160   
             }
 161   
             
 162   
         }
 163   
         
 164  0
         final List newBody = new LinkedList();
 165   
 
 166  0
         newBody.add(attributes);
 167   
         
 168  0
         newBody.add(this.body);
 169   
 
 170  0
         this.body = newBody;
 171   
     }
 172   
 
 173   
     /* (non-Javadoc)
 174   
      * @see org.xml.sax.ContentHandler#characters(char[], int, int)
 175   
      */
 176  0
     public void characters(final char[] ch, final int start, final int length) throws SAXException {
 177  0
         this.charBuffer.append(ch, start, length);
 178   
     }
 179   
     
 180   
     /* (non-Javadoc)
 181   
      * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
 182   
      */
 183  0
     public void endElement(final String namespaceURI, final String localName, final String qName) throws SAXException {
 184  0
         addNonWhitespaceCdata();
 185   
         
 186  0
         final List children = this.body;
 187   
         
 188  0
         final Map attributes = (Map)this.body.remove(0);
 189   
         
 190  0
         this.body = (List)this.body.remove(0);
 191   
         
 192  0
         if (namespaceURI.length() == 0) {
 193  0
             this.body.add(new XmlList(qName, attributes, children, namespaceURI));
 194   
         } else {
 195  0
             this.body.add(new XmlList(localName, attributes, children, namespaceURI));
 196   
         }
 197   
     }
 198   
     
 199   
     /* (non-Javadoc)
 200   
      * @see org.xml.sax.ContentHandler#endDocument()
 201   
      */
 202  0
     public void endDocument() throws SAXException {
 203  0
         this.result = this.body;
 204  0
         this.body = null;
 205   
     }
 206   
 
 207   
     // Implementation methods
 208   
     //-------------------------------------------------------------------------           
 209   
 
 210   
     /**
 211   
      * 
 212   
      */
 213  0
     private void addNonWhitespaceCdata() {
 214  0
         if (this.charBuffer.length() != 0) {
 215   
             //
 216   
             // This element is preceeded by CDATA if it's not whitespace add it to the body
 217   
             // Note that, according to the XML spec, we should preserve the CDATA if it's all whitespace
 218   
             // but for the sort of work I'm doing ignoring the whitespace is preferable
 219   
             //
 220  0
             final String cdata = this.charBuffer.toString();
 221   
             
 222  0
             this.charBuffer.setLength(0);
 223  0
             if (cdata.trim().length() != 0) {
 224  0
                 this.body.add(cdata);
 225   
             }
 226   
         }        
 227   
     }
 228   
 }
 229   
 
 230   
 class XmlList extends GroovyObjectSupport {
 231   
     final String name;
 232   
     final Map attributes;
 233   
     final Object[] children;
 234   
     final String namespaceURI;
 235   
     
 236  0
     public XmlList(final String name, final Map attributes, final List body, final String namespaceURI) {
 237  0
         super();
 238   
         
 239  0
         this.name = name;
 240  0
         this.attributes = attributes;
 241  0
         this.children = body.toArray();
 242  0
         this.namespaceURI = namespaceURI;
 243   
     }
 244   
     
 245  0
     public Object getProperty(final String elementName) {
 246  0
             if (elementName.startsWith("@")) {
 247  0
                 return this.attributes.get(elementName.substring(1));
 248   
             } else {
 249  0
             final int indexOfFirst = getNextXmlElement(elementName, -1);
 250   
             
 251  0
                 if (indexOfFirst == -1) { // no elements match the element name
 252  0
                     return new ElementCollection() {
 253  0
                         protected ElementCollection getResult(final String property) {
 254  0
                             return this;
 255   
                         }
 256   
 
 257   
                                 /**
 258   
                                  * 
 259   
                                  * Used by the Invoker when it wants to iterate over this object
 260   
                                  * 
 261   
                                  * @return
 262   
                                  */
 263  0
                                 public ElementIterator iterator() {
 264  0
                                     return new ElementIterator(new XmlList[]{XmlList.this}, new int[]{-1}) {
 265   
                                         {
 266  0
                                             findNextChild();        // set up the element indexes
 267   
                                         }
 268   
                                         
 269  0
                                     protected void findNextChild() {
 270  0
                                         this.nextParentElements[0] = -1;
 271   
                                     }
 272   
                                     };
 273   
                                 }
 274   
                     };
 275   
                 }
 276   
                 
 277  0
                 if (getNextXmlElement(elementName, indexOfFirst) == -1) {    // one element matches the element name
 278  0
                     return this.children[indexOfFirst];
 279   
                 } else {        // > 1 element matches the element name
 280  0
                         return new ElementCollection() {
 281  0
                             protected ElementCollection getResult(final String property) {
 282  0
                                 return new ComplexElementCollection(new XmlList[]{XmlList.this},
 283   
                                                                  new int[] {indexOfFirst},
 284   
                                                                 new String[] {elementName},
 285   
                                                                 property);
 286   
                             }
 287   
     
 288   
                                 /**
 289   
                                  * 
 290   
                                  * Used by the Invoker when it wants to iterate over this object
 291   
                                  * 
 292   
                                  * @return
 293   
                                  */
 294  0
                                 public ElementIterator iterator() {
 295  0
                                     return new ElementIterator(new XmlList[]{XmlList.this}, new int[]{indexOfFirst}) {
 296  0
                                     protected void findNextChild() {
 297  0
                                         this.nextParentElements[0] = XmlList.this.getNextXmlElement(elementName, this.nextParentElements[0]);
 298   
                                     }
 299   
                                     };
 300   
                                 }
 301   
                         };
 302   
                 }
 303   
             }
 304   
     }
 305   
     
 306  0
     public Object getAt(final int index) {
 307  0
             if (index == 0) {
 308  0
                 return this;
 309   
             } else {
 310  0
                 throw new ArrayIndexOutOfBoundsException(index);
 311   
             }
 312   
         }
 313   
     
 314  0
     public int size() {
 315  0
             return 1;
 316   
     }
 317   
 
 318  0
     public Object invokeMethod(final String name, final Object args) {
 319  0
         if ("attributes".equals(name)) {
 320  0
             return this.attributes;
 321  0
         } else if ("name".equals(name)) {
 322  0
             return this.name;
 323  0
         } else if ("children".equals(name)) {
 324  0
             return this.children;
 325  0
         } else if ("text".equals(name)) {
 326  0
         final StringBuffer buff = new StringBuffer();
 327   
         
 328  0
             for (int i = 0; i != this.children.length; i++) {
 329  0
             final Object child = this.children[i];
 330   
             
 331  0
                 if (child instanceof String) {
 332  0
                     buff.append(child);
 333   
                 }
 334   
             }    
 335   
         
 336  0
             return buff.toString();
 337  0
         } else if ("getAt".equals(name) && ((Object[])args)[0] instanceof String) {
 338  0
             return getProperty((String)((Object[])args)[0]);
 339  0
         } else if ("depthFirst".equals(name)) {
 340   
             //
 341   
             // TODO: replace this with an iterator
 342   
             //
 343   
             
 344  0
             return new GroovyObjectSupport() {
 345  0
                 public Object invokeMethod(final String name, final Object args) {
 346  0
                     if ("getAt".equals(name) && ((Object[])args)[0] instanceof String) {
 347  0
                         return getProperty((String)((Object[])args)[0]);
 348   
                     } else {
 349  0
                         return XmlList.this.invokeMethod(name, args);
 350   
                     }
 351   
                 }
 352   
                 
 353  0
                 public Object getProperty(final String property) {
 354  0
                     if (property.startsWith("@")) {
 355  0
                         return XmlList.this.getProperty(property);
 356   
                     } else {
 357  0
                     final List result = new LinkedList();
 358   
 
 359  0
                         depthFirstGetProperty(property, XmlList.this.children, result);
 360   
                         
 361  0
                         return result;
 362   
                     }
 363   
                 }
 364   
                 
 365  0
                 private void depthFirstGetProperty(final String property, final Object[] contents, final List result) {
 366  0
                         for (int i = 0; i != contents.length; i++) {
 367  0
                         final Object item = contents[i];
 368   
                         
 369  0
                             if (item instanceof XmlList) {
 370  0
                                 if (((XmlList)item).name.equals(property)) {
 371  0
                                     result.add(item);
 372   
                                 }
 373   
                                 
 374  0
                                 depthFirstGetProperty(property, ((XmlList)item).children, result);
 375   
                             }
 376   
                     }
 377   
                 }
 378   
             };
 379   
             } else {
 380  0
                 return getMetaClass().invokeMethod(this, name, args);
 381   
             }
 382   
     }
 383   
 
 384  0
         protected int getNextXmlElement(final String name, final int lastFound) {
 385  0
             for (int i = lastFound + 1; i < this.children.length; i++) {
 386  0
             final Object item = this.children[i];
 387   
                 
 388  0
                 if (item instanceof XmlList && ((XmlList)item).name.equals(name)) {
 389  0
                     return i;
 390   
                 }
 391   
             }
 392   
             
 393  0
             return -1;
 394   
         }
 395   
 }
 396   
 
 397   
 abstract class ElementIterator implements Iterator {
 398   
     protected final XmlList[] parents;
 399   
     protected final int[] nextParentElements;
 400   
     
 401  0
     protected ElementIterator(final XmlList[] parents, int[] nextParentElements) {
 402  0
         this.parents = new XmlList[parents.length];
 403  0
         System.arraycopy(parents, 0, this.parents, 0, parents.length);
 404   
         
 405  0
         this.nextParentElements = new int[nextParentElements.length];
 406  0
         System.arraycopy(nextParentElements, 0, this.nextParentElements, 0, nextParentElements.length);
 407   
     }
 408   
     
 409   
     /* (non-Javadoc)
 410   
      * @see java.util.Enumeration#hasMoreElements()
 411   
     */
 412  0
     public boolean hasNext() {
 413  0
         return this.nextParentElements[0] != -1;
 414   
     }
 415   
     
 416   
     /* (non-Javadoc)
 417   
      * @see java.util.Enumeration#nextElement()
 418   
      */
 419  0
     public Object next() {
 420  0
     final Object result = this.parents[0].children[this.nextParentElements[0]];
 421   
             
 422  0
         findNextChild();
 423   
     
 424  0
         return result;
 425   
     }
 426   
     
 427   
     /* (non-Javadoc)
 428   
      * @see java.util.Iterator#remove()
 429   
      */
 430  0
     public void remove() {
 431  0
         throw new UnsupportedOperationException();
 432   
     }
 433   
     
 434   
     protected abstract void findNextChild();
 435   
 }
 436   
 
 437   
 abstract class ElementCollection extends GroovyObjectSupport {
 438   
     private int count = -1;
 439   
     
 440   
     public abstract ElementIterator iterator();
 441   
     
 442   
     /* (non-Javadoc)
 443   
      * @see groovy.lang.GroovyObject#getProperty(java.lang.String)
 444   
      */
 445  0
     public Object getProperty(final String property) {
 446  0
     final ElementCollection result = getResult(property);
 447  0
     final Iterator iterator = result.iterator();
 448   
 
 449  0
         if (iterator.hasNext()) {                
 450   
             //
 451   
             // See if there's only one available
 452   
             //
 453  0
             final Object first = iterator.next();
 454   
             
 455  0
             if (!iterator.hasNext()) {
 456  0
                 return first;
 457   
             }
 458   
         }
 459   
         
 460  0
         return result;
 461   
     }
 462   
     
 463   
     protected abstract ElementCollection getResult(String property);
 464   
     
 465  0
     public synchronized int size() {
 466  0
         if (this.count == -1) {
 467  0
         final Iterator iter = iterator();
 468   
         
 469  0
             this.count = 0;
 470   
             
 471  0
             while (iter.hasNext()) {
 472  0
                 this.count++;
 473  0
                 iter.next();
 474   
             }
 475   
         }
 476  0
         return this.count;
 477   
     }
 478   
 }
 479   
 
 480   
 class ComplexElementCollection extends ElementCollection {
 481   
     private final XmlList[] parents;
 482   
     private final int[] nextParentElements;
 483   
     private final String[] parentElementNames;
 484   
     
 485  0
     public ComplexElementCollection(final XmlList[] parents,
 486   
                                       final int[] nextParentElements,
 487   
                                   final String[] parentElementNames,
 488   
                                   final String childElementName)
 489   
     {
 490  0
         this.parents = new XmlList[parents.length + 1];
 491  0
         this.parents[0] = (XmlList)parents[0].children[nextParentElements[0]];
 492  0
         System.arraycopy(parents, 0, this.parents, 1, parents.length);
 493   
         
 494  0
         this.nextParentElements = new int[nextParentElements.length + 1];
 495  0
         this.nextParentElements[0] = -1;    
 496  0
         System.arraycopy(nextParentElements, 0, this.nextParentElements, 1, nextParentElements.length);
 497   
         
 498  0
         this.parentElementNames = new String[parentElementNames.length + 1];
 499  0
         this.parentElementNames[0] = childElementName;
 500  0
         System.arraycopy(parentElementNames, 0, this.parentElementNames, 1, parentElementNames.length);
 501   
         
 502   
         //
 503   
         // Use the iterator to get the index of the first elemeny
 504   
         //
 505   
         
 506  0
         final ElementIterator iter = this.iterator();
 507   
         
 508  0
         iter.findNextChild();
 509   
         
 510  0
         this.nextParentElements[0] = iter.nextParentElements[0];
 511   
     }
 512   
     
 513  0
     protected ElementCollection getResult(final String property) {
 514  0
         return new ComplexElementCollection(this.parents,
 515   
                                                this.nextParentElements,
 516   
                                             this.parentElementNames,
 517   
                                             property);
 518   
     }
 519   
     
 520   
     /**
 521   
      * 
 522   
      * Used by the Invoker when it wants to iterate over this object
 523   
      * 
 524   
      * @return
 525   
      */
 526  0
     public ElementIterator iterator() {
 527  0
         return new ElementIterator(this.parents, this.nextParentElements) {
 528  0
                         protected void findNextChild() {    
 529  0
                             this.nextParentElements[0] = this.parents[0].getNextXmlElement(ComplexElementCollection.this.parentElementNames[0], this.nextParentElements[0]);
 530   
                             
 531  0
                             while (this.nextParentElements[0] == -1) {
 532  0
                                 this.parents[0] = findNextParent(1);
 533   
                                 
 534  0
                                 if (this.parents[0] == null) {
 535  0
                                     return;
 536   
                                 } else {
 537  0
                                     this.nextParentElements[0] = this.parents[0].getNextXmlElement(ComplexElementCollection.this.parentElementNames[0], -1);
 538   
                                 }
 539   
                             }
 540   
                         }
 541   
                         
 542  0
                         private XmlList findNextParent(final int i) {
 543  0
                             if (i == this.nextParentElements.length) return null;
 544   
                             
 545  0
                             this.nextParentElements[i] = this.parents[i].getNextXmlElement(ComplexElementCollection.this.parentElementNames[i], this.nextParentElements[i]);
 546   
                             
 547  0
                             while (this.nextParentElements[i] == -1) {
 548  0
                                 this.parents[i] = findNextParent(i + 1);
 549   
                                 
 550  0
                                 if (this.parents[i] == null) {
 551  0
                                     return null;
 552   
                                 } else {
 553  0
                                     this.nextParentElements[i] = this.parents[i].getNextXmlElement(ComplexElementCollection.this.parentElementNames[i], -1);
 554   
                                 }
 555   
                             }
 556   
                         
 557  0
                             return (XmlList)this.parents[i].children[this.nextParentElements[i]];
 558   
                         }
 559   
         };
 560   
     }
 561   
 }
 562