Clover coverage report - Clover results for XOM 1.2d1
Coverage timestamp: Wed Feb 8 2006 08:31:33 EST
file stats: LOC: 350   Methods: 11
NCLOC: 172   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
SAXConverter.java 100% 100% 100% 100%
coverage
 1    /* Copyright 2002-2005 Elliotte Rusty Harold
 2   
 3    This library is free software; you can redistribute it and/or modify
 4    it under the terms of version 2.1 of the GNU Lesser General Public
 5    License as published by the Free Software Foundation.
 6   
 7    This library is distributed in the hope that it will be useful,
 8    but WITHOUT ANY WARRANTY; without even the implied warranty of
 9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 10    GNU Lesser General Public License for more details.
 11   
 12    You should have received a copy of the GNU Lesser General Public
 13    License along with this library; if not, write to the
 14    Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 15    Boston, MA 02111-1307 USA
 16   
 17    You can contact Elliotte Rusty Harold by sending e-mail to
 18    elharo@metalab.unc.edu. Please include the word "XOM" in the
 19    subject line. The XOM home page is located at http://www.xom.nu/
 20    */
 21   
 22    package nu.xom.converters;
 23   
 24    import nu.xom.Attribute;
 25    import nu.xom.Comment;
 26    import nu.xom.DocType;
 27    import nu.xom.Document;
 28    import nu.xom.Element;
 29    import nu.xom.Node;
 30    import nu.xom.Nodes;
 31    import nu.xom.ParentNode;
 32    import nu.xom.ProcessingInstruction;
 33    import nu.xom.Text;
 34   
 35    import org.xml.sax.ContentHandler;
 36    import org.xml.sax.SAXException;
 37    import org.xml.sax.ext.LexicalHandler;
 38    import org.xml.sax.helpers.AttributesImpl;
 39    import org.xml.sax.helpers.LocatorImpl;
 40   
 41    /**
 42    * <p>
 43    * Feeds a XOM <code>Document</code> into a
 44    * SAX2 <code>ContentHandler</code>.
 45    * </p>
 46    *
 47    * @author Elliotte Rusty Harold
 48    * @version 1.1b2
 49    */
 50    public class SAXConverter {
 51   
 52   
 53    private ContentHandler contentHandler;
 54    private LexicalHandler lexicalHandler;
 55    private LocatorImpl locator;
 56    private boolean stripBaseAttributes = true;
 57   
 58   
 59    /**
 60    * <p>
 61    * Creates a new <code>SAXConverter</code>.
 62    * </p>
 63    *
 64    * @param handler the SAX2 content handler
 65    * that receives the data
 66    *
 67    * @throws NullPointerException if handler is null
 68    *
 69    */
 70  1003 public SAXConverter(ContentHandler handler) {
 71  1003 setContentHandler(handler);
 72    }
 73   
 74   
 75    /**
 76    * <p>
 77    * Set the content handler for this converter.
 78    * </p>
 79    *
 80    * @param handler SAX2 content handler that
 81    * receives the data
 82    *
 83    * @throws NullPointerException if handler is null
 84    *
 85    */
 86  1993 public void setContentHandler(ContentHandler handler) {
 87   
 88  1993 if (handler == null) {
 89  1 throw new NullPointerException(
 90    "ContentHandler must be non-null."
 91    );
 92    }
 93    // unbelievably skanky hack to allow xml:base attributes
 94    // to be passed to XSL transforms without mucking with the
 95    // public API. This would be so much easier if Java had friend
 96    // functions.
 97  1992 else if ("nu.xom.xslt.XSLTHandler".equals(handler.getClass().getName())) {
 98  972 this.stripBaseAttributes = false;
 99    }
 100    else {
 101  1020 this.contentHandler = handler;
 102    }
 103   
 104    }
 105   
 106   
 107    /**
 108    * <p>
 109    * Returns the content handler.
 110    * </p>
 111    *
 112    * @return SAX2 content handler that receives the data
 113    */
 114  2 public ContentHandler getContentHandler() {
 115  2 return this.contentHandler;
 116    }
 117   
 118   
 119    /**
 120    * <p>
 121    * Sets the optional lexical handler for this converter.
 122    * The only lexical events the converter supplies
 123    * are comments.
 124    * </p>
 125    *
 126    * @param handler the lexical handler;
 127    * may be null to turn off lexical events
 128    */
 129  450 public void setLexicalHandler(LexicalHandler handler) {
 130  450 this.lexicalHandler = handler;
 131    }
 132   
 133   
 134    /**
 135    * <p>
 136    * Returns the <code>LexicalHandler</code> for this
 137    * converter. This is only used for comments.
 138    * </p>
 139    *
 140    * @return SAX2 lexical handler that receives
 141    * lexical events
 142    */
 143  2 public LexicalHandler getLexicalHandler() {
 144  2 return this.lexicalHandler;
 145    }
 146   
 147   
 148    // Not necessary to worry about parser exceptions passed to
 149    // fatalError() because we're starting with a known good document.
 150    // Only exceptions that can arise are thrown by
 151    // the supplied ContentHandler, and we don't want to pass those
 152    // to the ErrorHandler, or call endDocument() if such an exception
 153    // is thrown
 154    /**
 155    * <p>
 156    * Feed a document through this converter.
 157    * </p>
 158    *
 159    * @param doc the document to pass to SAX
 160    *
 161    * @throws SAXException if the content handler
 162    * or lexical handler throws an exception
 163    */
 164  993 public void convert(Document doc) throws SAXException {
 165   
 166  993 locator = new LocatorImpl();
 167  993 locator.setSystemId(doc.getBaseURI());
 168  993 contentHandler.setDocumentLocator(locator);
 169  993 contentHandler.startDocument();
 170  993 for (int i = 0; i < doc.getChildCount(); i++) {
 171  1500 process(doc.getChild(i));
 172    }
 173  893 contentHandler.endDocument();
 174   
 175    }
 176   
 177   
 178  586725 private void process(Node node) throws SAXException {
 179   
 180  586725 if (node instanceof Element) {
 181  222512 convertElement((Element) node);
 182    }
 183  364213 else if (node instanceof Text) {
 184  362169 String data = node.getValue();
 185  362169 contentHandler.characters(
 186    data.toCharArray(), 0, data.length());
 187    }
 188  2044 else if (node instanceof ProcessingInstruction) {
 189  88 ProcessingInstruction instruction
 190    = (ProcessingInstruction) node;
 191   
 192  88 contentHandler.processingInstruction(
 193    instruction.getTarget(), instruction.getValue());
 194    }
 195  1956 else if (node instanceof Comment && lexicalHandler != null) {
 196  744 String data = node.getValue();
 197  744 lexicalHandler.comment(
 198    data.toCharArray(), 0, data.length());
 199    }
 200  1212 else if (node instanceof DocType && lexicalHandler != null) {
 201  97 DocType type = (DocType) node;
 202  97 lexicalHandler.startDTD(type.getRootElementName(),
 203    type.getPublicID(), type.getSystemID());
 204  97 lexicalHandler.endDTD();
 205    }
 206    // all other types are ignored
 207   
 208    }
 209   
 210   
 211    /**
 212    * @param element the context in which the prefix is mapped
 213    * @param prefix the prefix to pass to statPrefixMapping
 214    * @return true if and only if startPrefixMapping was called
 215    * @throws SAXException if the ContentHandler throws an exception
 216    */
 217  222825 private boolean convertNamespace(Element element, String prefix)
 218    throws SAXException {
 219   
 220    // XXX could store a stack of these in a namespaceSupport to avoid going up to the parent
 221  222825 String uri = element.getNamespaceURI(prefix);
 222  222825 ParentNode parentNode = element.getParent();
 223  222825 Element parent = null;
 224  222825 if (parentNode instanceof Element) {
 225  221586 parent = (Element) parentNode;
 226    }
 227   
 228  222825 if (parent != null && uri.equals(parent.getNamespaceURI(prefix))) {
 229  221538 return false;
 230    }
 231  1287 else if (parent == null && "".equals(uri)) {
 232    // Do not fire startPrefixMapping event for no namespace
 233    // on root element
 234  453 return false;
 235    }
 236  834 contentHandler.startPrefixMapping(prefix, uri);
 237  834 return true; // i.e. converted
 238   
 239    }
 240   
 241   
 242  222512 private void convertElement(Element element) throws SAXException {
 243   
 244  222512 locator.setSystemId(element.getBaseURI());
 245   
 246    // start prefix mapping
 247  222512 int namespaceCount = element.getNamespaceDeclarationCount();
 248  222512 String[] prefixes = new String[namespaceCount];
 249  222512 int prefixCount = 0;
 250  222512 for (int i = 0; i < namespaceCount; i++) {
 251  222825 String prefix = element.getNamespacePrefix(i);
 252  222825 boolean converted = convertNamespace(element, prefix);
 253  222825 if (converted) {
 254  834 prefixes[prefixCount] = prefix;
 255  834 prefixCount++;
 256    }
 257    }
 258   
 259    // prepare attributes
 260  222512 AttributesImpl saxAttributes = new AttributesImpl();
 261  222512 int attributeCount = element.getAttributeCount();
 262  222512 for (int i = 0; i < attributeCount; i++) {
 263  10213 Attribute attribute = element.getAttribute(i);
 264    // The base URIs provided by the locator have already
 265    // accounted for any xml:base attributes. We do not
 266    // also pass in xml:base attributes or some relative base
 267    // URIs could be applied twice.
 268  10213 if ("base".equals(attribute.getLocalName())
 269    && "http://www.w3.org/XML/1998/namespace".equals(attribute.getNamespaceURI())
 270    && stripBaseAttributes) {
 271  1 continue;
 272    }
 273  10212 saxAttributes.addAttribute(attribute.getNamespaceURI(),
 274    attribute.getLocalName(),
 275    attribute.getQualifiedName(),
 276    getSAXType(attribute),
 277    attribute.getValue());
 278    }
 279   
 280  222512 contentHandler.startElement(
 281    element.getNamespaceURI(),
 282    element.getLocalName(),
 283    element.getQualifiedName(),
 284    saxAttributes);
 285  222419 int childCount = element.getChildCount();
 286  222419 for (int i = 0; i < childCount; i++) {
 287  585224 process(element.getChild(i));
 288    }
 289  222235 contentHandler.endElement(element.getNamespaceURI(),
 290    element.getLocalName(), element.getQualifiedName());
 291   
 292    // end prefix mappings
 293  222233 for (int i = 0; i < prefixCount; i++) {
 294  731 contentHandler.endPrefixMapping(prefixes[i]);
 295    }
 296   
 297    }
 298   
 299   
 300  10212 private static String getSAXType(Attribute attribute) {
 301   
 302  10212 Attribute.Type type = attribute.getType();
 303  8 if (type.equals(Attribute.Type.UNDECLARED)) return "CDATA";
 304  9862 if (type.equals(Attribute.Type.CDATA)) return "CDATA";
 305  58 if (type.equals(Attribute.Type.ID)) return "ID";
 306  5 if (type.equals(Attribute.Type.IDREF)) return "IDREF";
 307  1 if (type.equals(Attribute.Type.IDREFS)) return "IDREFS";
 308  273 if (type.equals(Attribute.Type.NMTOKEN)) return "NMTOKEN";
 309  1 if (type.equals(Attribute.Type.NMTOKENS)) return "NMTOKENS";
 310  1 if (type.equals(Attribute.Type.ENTITY)) return "ENTITY";
 311  1 if (type.equals(Attribute.Type.ENTITIES)) return "ENTITIES";
 312  1 if (type.equals(Attribute.Type.NOTATION)) return "NOTATION";
 313  1 return "NMTOKEN"; // ENUMERATED
 314   
 315    }
 316   
 317   
 318    /**
 319    * <p>
 320    * Converts a <code>Nodes</code> list into SAX by firing events
 321    * into the registered handlers. This method calls
 322    * <code>startDocument</code> before processing the list
 323    * of nodes, and calls <code>endDocument</code> after processing
 324    * all of them.
 325    * </p>
 326    *
 327    * @param nodes the nodes to pass to SAX
 328    *
 329    * @throws SAXException if the content handler
 330    * or lexical handler throws an exception
 331    */
 332  972 public void convert(Nodes nodes) throws SAXException {
 333   
 334  972 if (nodes.size() == 1 && nodes.get(0) instanceof Document) {
 335  971 convert((Document) nodes.get(0));
 336    }
 337    else {
 338  1 locator = new LocatorImpl();
 339  1 contentHandler.setDocumentLocator(locator);
 340  1 contentHandler.startDocument();
 341  1 for (int i = 0; i < nodes.size(); i++) {
 342  1 process(nodes.get(i));
 343    }
 344  1 contentHandler.endDocument();
 345    }
 346   
 347    }
 348   
 349   
 350    }