1/* 2 * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.xml.internal.bind.unmarshaller; 27 28import java.util.Enumeration; 29 30import javax.xml.bind.ValidationEventLocator; 31import javax.xml.bind.helpers.AbstractUnmarshallerImpl; 32import javax.xml.bind.helpers.ValidationEventLocatorImpl; 33 34import com.sun.xml.internal.bind.v2.runtime.unmarshaller.LocatorEx; 35 36import org.w3c.dom.Attr; 37import org.w3c.dom.Document; 38import org.w3c.dom.Element; 39import org.w3c.dom.NamedNodeMap; 40import org.w3c.dom.Node; 41import org.w3c.dom.NodeList; 42import org.w3c.dom.ProcessingInstruction; 43import org.xml.sax.ContentHandler; 44import org.xml.sax.Locator; 45import org.xml.sax.SAXException; 46import org.xml.sax.helpers.AttributesImpl; 47import org.xml.sax.helpers.NamespaceSupport; 48 49/** 50 * Visits a W3C DOM tree and generates SAX2 events from it. 51 * 52 * <p> 53 * This class is just intended to be used by {@link AbstractUnmarshallerImpl}. 54 * The javax.xml.bind.helpers package is generally a wrong place to put 55 * classes like this. 56 * 57 * @author <ul><li>Kohsuke Kawaguchi, Sun Microsystems, Inc.</li></ul> 58 * @since JAXB 1.0 59 */ 60public class DOMScanner implements LocatorEx,InfosetScanner/*<Node> --- but can't do this to protect 1.0 clients, or can I? */ 61{ 62 63 /** reference to the current node being scanned - used for determining 64 * location info for validation events */ 65 private Node currentNode = null; 66 67 /** To save memory, only one instance of AttributesImpl will be used. */ 68 private final AttributesImpl atts = new AttributesImpl(); 69 70 /** This handler will receive SAX2 events. */ 71 private ContentHandler receiver=null; 72 73 private Locator locator=this; 74 75 public DOMScanner() { 76 } 77 78 79 /** 80 * Configures the locator object that the SAX {@link ContentHandler} will see. 81 */ 82 public void setLocator( Locator loc ) { 83 this.locator = loc; 84 } 85 86 public void scan(Object node) throws SAXException { 87 if( node instanceof Document ) { 88 scan( (Document)node ); 89 } else { 90 scan( (Element)node ); 91 } 92 } 93 94 public void scan( Document doc ) throws SAXException { 95 scan( doc.getDocumentElement() ); 96 } 97 98 public void scan( Element e) throws SAXException { 99 setCurrentLocation( e ); 100 101 receiver.setDocumentLocator(locator); 102 receiver.startDocument(); 103 104 NamespaceSupport nss = new NamespaceSupport(); 105 buildNamespaceSupport( nss, e.getParentNode() ); 106 107 for( Enumeration en = nss.getPrefixes(); en.hasMoreElements(); ) { 108 String prefix = (String)en.nextElement(); 109 receiver.startPrefixMapping( prefix, nss.getURI(prefix) ); 110 } 111 112 visit(e); 113 114 for( Enumeration en = nss.getPrefixes(); en.hasMoreElements(); ) { 115 String prefix = (String)en.nextElement(); 116 receiver.endPrefixMapping( prefix ); 117 } 118 119 120 setCurrentLocation( e ); 121 receiver.endDocument(); 122 } 123 124 /** 125 * Parses a subtree starting from the element e and 126 * reports SAX2 events to the specified handler. 127 * 128 * @deprecated in JAXB 2.0 129 * Use {@link #scan(Element)} 130 */ 131 public void parse( Element e, ContentHandler handler ) throws SAXException { 132 // it might be better to set receiver at the constructor. 133 receiver = handler; 134 135 setCurrentLocation( e ); 136 receiver.startDocument(); 137 138 receiver.setDocumentLocator(locator); 139 visit(e); 140 141 setCurrentLocation( e ); 142 receiver.endDocument(); 143 } 144 145 /** 146 * Similar to the parse method but it visits the ancestor nodes 147 * and properly emulate the all in-scope namespace declarations. 148 * 149 * @deprecated in JAXB 2.0 150 * Use {@link #scan(Element)} 151 */ 152 public void parseWithContext( Element e, ContentHandler handler ) throws SAXException { 153 setContentHandler(handler); 154 scan(e); 155 } 156 157 /** 158 * Recursively visit ancestors and build up {@link NamespaceSupport} oject. 159 */ 160 private void buildNamespaceSupport(NamespaceSupport nss, Node node) { 161 if(node==null || node.getNodeType()!=Node.ELEMENT_NODE) 162 return; 163 164 buildNamespaceSupport( nss, node.getParentNode() ); 165 166 nss.pushContext(); 167 NamedNodeMap atts = node.getAttributes(); 168 for( int i=0; i<atts.getLength(); i++ ) { 169 Attr a = (Attr)atts.item(i); 170 if( "xmlns".equals(a.getPrefix()) ) { 171 nss.declarePrefix( a.getLocalName(), a.getValue() ); 172 continue; 173 } 174 if( "xmlns".equals(a.getName()) ) { 175 nss.declarePrefix( "", a.getValue() ); 176 continue; 177 } 178 } 179 } 180 181 /** 182 * Visits an element and its subtree. 183 */ 184 public void visit( Element e ) throws SAXException { 185 setCurrentLocation( e ); 186 final NamedNodeMap attributes = e.getAttributes(); 187 188 atts.clear(); 189 int len = attributes==null ? 0: attributes.getLength(); 190 191 for( int i=len-1; i>=0; i-- ) { 192 Attr a = (Attr)attributes.item(i); 193 String name = a.getName(); 194 // start namespace binding 195 if(name.startsWith("xmlns")) { 196 if(name.length()==5) { 197 receiver.startPrefixMapping( "", a.getValue() ); 198 } else { 199 String localName = a.getLocalName(); 200 if(localName==null) { 201 // DOM built without namespace support has this problem 202 localName = name.substring(6); 203 } 204 receiver.startPrefixMapping( localName, a.getValue() ); 205 } 206 continue; 207 } 208 209 String uri = a.getNamespaceURI(); 210 if(uri==null) uri=""; 211 212 String local = a.getLocalName(); 213 if(local==null) local = a.getName(); 214 // add other attributes to the attribute list 215 // that we will pass to the ContentHandler 216 atts.addAttribute( 217 uri, 218 local, 219 a.getName(), 220 "CDATA", 221 a.getValue()); 222 } 223 224 String uri = e.getNamespaceURI(); 225 if(uri==null) uri=""; 226 String local = e.getLocalName(); 227 String qname = e.getTagName(); 228 if(local==null) local = qname; 229 receiver.startElement( uri, local, qname, atts ); 230 231 // visit its children 232 NodeList children = e.getChildNodes(); 233 int clen = children.getLength(); 234 for( int i=0; i<clen; i++ ) 235 visit(children.item(i)); 236 237 238 239 setCurrentLocation( e ); 240 receiver.endElement( uri, local, qname ); 241 242 // call the endPrefixMapping method 243 for( int i=len-1; i>=0; i-- ) { 244 Attr a = (Attr)attributes.item(i); 245 String name = a.getName(); 246 if(name.startsWith("xmlns")) { 247 if(name.length()==5) 248 receiver.endPrefixMapping(""); 249 else 250 receiver.endPrefixMapping(a.getLocalName()); 251 } 252 } 253 } 254 255 private void visit( Node n ) throws SAXException { 256 setCurrentLocation( n ); 257 258 // if a case statement gets too big, it should be made into a separate method. 259 switch(n.getNodeType()) { 260 case Node.CDATA_SECTION_NODE: 261 case Node.TEXT_NODE: 262 String value = n.getNodeValue(); 263 receiver.characters( value.toCharArray(), 0, value.length() ); 264 break; 265 case Node.ELEMENT_NODE: 266 visit( (Element)n ); 267 break; 268 case Node.ENTITY_REFERENCE_NODE: 269 receiver.skippedEntity(n.getNodeName()); 270 break; 271 case Node.PROCESSING_INSTRUCTION_NODE: 272 ProcessingInstruction pi = (ProcessingInstruction)n; 273 receiver.processingInstruction(pi.getTarget(),pi.getData()); 274 break; 275 } 276 } 277 278 private void setCurrentLocation( Node currNode ) { 279 currentNode = currNode; 280 } 281 282 /** 283 * The same as {@link #getCurrentElement()} but 284 * better typed. 285 */ 286 public Node getCurrentLocation() { 287 return currentNode; 288 } 289 290 public Object getCurrentElement() { 291 return currentNode; 292 } 293 294 public LocatorEx getLocator() { 295 return this; 296 } 297 298 public void setContentHandler(ContentHandler handler) { 299 this.receiver = handler; 300 } 301 302 public ContentHandler getContentHandler() { 303 return this.receiver; 304 } 305 306 307 // LocatorEx implementation 308 public String getPublicId() { return null; } 309 public String getSystemId() { return null; } 310 public int getLineNumber() { return -1; } 311 public int getColumnNumber() { return -1; } 312 313 public ValidationEventLocator getLocation() { 314 return new ValidationEventLocatorImpl(getCurrentLocation()); 315 } 316} 317