aoqi@0: /*
aoqi@0: * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0: *
aoqi@0: * This code is free software; you can redistribute it and/or modify it
aoqi@0: * under the terms of the GNU General Public License version 2 only, as
aoqi@0: * published by the Free Software Foundation. Oracle designates this
aoqi@0: * particular file as subject to the "Classpath" exception as provided
aoqi@0: * by Oracle in the LICENSE file that accompanied this code.
aoqi@0: *
aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0: * accompanied this code).
aoqi@0: *
aoqi@0: * You should have received a copy of the GNU General Public License version
aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0: *
aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0: * or visit www.oracle.com if you need additional information or have any
aoqi@0: * questions.
aoqi@0: */
aoqi@0:
aoqi@0: package com.sun.xml.internal.bind.unmarshaller;
aoqi@0:
aoqi@0: import java.util.Enumeration;
aoqi@0:
aoqi@0: import javax.xml.bind.ValidationEventLocator;
aoqi@0: import javax.xml.bind.helpers.AbstractUnmarshallerImpl;
aoqi@0: import javax.xml.bind.helpers.ValidationEventLocatorImpl;
aoqi@0:
aoqi@0: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.LocatorEx;
aoqi@0:
aoqi@0: import org.w3c.dom.Attr;
aoqi@0: import org.w3c.dom.Document;
aoqi@0: import org.w3c.dom.Element;
aoqi@0: import org.w3c.dom.NamedNodeMap;
aoqi@0: import org.w3c.dom.Node;
aoqi@0: import org.w3c.dom.NodeList;
aoqi@0: import org.w3c.dom.ProcessingInstruction;
aoqi@0: import org.xml.sax.ContentHandler;
aoqi@0: import org.xml.sax.Locator;
aoqi@0: import org.xml.sax.SAXException;
aoqi@0: import org.xml.sax.helpers.AttributesImpl;
aoqi@0: import org.xml.sax.helpers.NamespaceSupport;
aoqi@0:
aoqi@0: /**
aoqi@0: * Visits a W3C DOM tree and generates SAX2 events from it.
aoqi@0: *
aoqi@0: *
aoqi@0: * This class is just intended to be used by {@link AbstractUnmarshallerImpl}.
aoqi@0: * The javax.xml.bind.helpers package is generally a wrong place to put
aoqi@0: * classes like this.
aoqi@0: *
aoqi@0: * @author
- Kohsuke Kawaguchi, Sun Microsystems, Inc.
aoqi@0: * @since JAXB1.0
aoqi@0: */
aoqi@0: public class DOMScanner implements LocatorEx,InfosetScanner/* --- but can't do this to protect 1.0 clients, or can I? */
aoqi@0: {
aoqi@0:
aoqi@0: /** reference to the current node being scanned - used for determining
aoqi@0: * location info for validation events */
aoqi@0: private Node currentNode = null;
aoqi@0:
aoqi@0: /** To save memory, only one instance of AttributesImpl will be used. */
aoqi@0: private final AttributesImpl atts = new AttributesImpl();
aoqi@0:
aoqi@0: /** This handler will receive SAX2 events. */
aoqi@0: private ContentHandler receiver=null;
aoqi@0:
aoqi@0: private Locator locator=this;
aoqi@0:
aoqi@0: public DOMScanner() {
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Configures the locator object that the SAX {@link ContentHandler} will see.
aoqi@0: */
aoqi@0: public void setLocator( Locator loc ) {
aoqi@0: this.locator = loc;
aoqi@0: }
aoqi@0:
aoqi@0: public void scan(Object node) throws SAXException {
aoqi@0: if( node instanceof Document ) {
aoqi@0: scan( (Document)node );
aoqi@0: } else {
aoqi@0: scan( (Element)node );
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: public void scan( Document doc ) throws SAXException {
aoqi@0: scan( doc.getDocumentElement() );
aoqi@0: }
aoqi@0:
aoqi@0: public void scan( Element e) throws SAXException {
aoqi@0: setCurrentLocation( e );
aoqi@0:
aoqi@0: receiver.setDocumentLocator(locator);
aoqi@0: receiver.startDocument();
aoqi@0:
aoqi@0: NamespaceSupport nss = new NamespaceSupport();
aoqi@0: buildNamespaceSupport( nss, e.getParentNode() );
aoqi@0:
aoqi@0: for( Enumeration en = nss.getPrefixes(); en.hasMoreElements(); ) {
aoqi@0: String prefix = (String)en.nextElement();
aoqi@0: receiver.startPrefixMapping( prefix, nss.getURI(prefix) );
aoqi@0: }
aoqi@0:
aoqi@0: visit(e);
aoqi@0:
aoqi@0: for( Enumeration en = nss.getPrefixes(); en.hasMoreElements(); ) {
aoqi@0: String prefix = (String)en.nextElement();
aoqi@0: receiver.endPrefixMapping( prefix );
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: setCurrentLocation( e );
aoqi@0: receiver.endDocument();
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Parses a subtree starting from the element e and
aoqi@0: * reports SAX2 events to the specified handler.
aoqi@0: *
aoqi@0: * @deprecated in JAXB 2.0
aoqi@0: * Use {@link #scan(Element)}
aoqi@0: */
aoqi@0: public void parse( Element e, ContentHandler handler ) throws SAXException {
aoqi@0: // it might be better to set receiver at the constructor.
aoqi@0: receiver = handler;
aoqi@0:
aoqi@0: setCurrentLocation( e );
aoqi@0: receiver.startDocument();
aoqi@0:
aoqi@0: receiver.setDocumentLocator(locator);
aoqi@0: visit(e);
aoqi@0:
aoqi@0: setCurrentLocation( e );
aoqi@0: receiver.endDocument();
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Similar to the parse method but it visits the ancestor nodes
aoqi@0: * and properly emulate the all in-scope namespace declarations.
aoqi@0: *
aoqi@0: * @deprecated in JAXB 2.0
aoqi@0: * Use {@link #scan(Element)}
aoqi@0: */
aoqi@0: public void parseWithContext( Element e, ContentHandler handler ) throws SAXException {
aoqi@0: setContentHandler(handler);
aoqi@0: scan(e);
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Recursively visit ancestors and build up {@link NamespaceSupport} oject.
aoqi@0: */
aoqi@0: private void buildNamespaceSupport(NamespaceSupport nss, Node node) {
aoqi@0: if(node==null || node.getNodeType()!=Node.ELEMENT_NODE)
aoqi@0: return;
aoqi@0:
aoqi@0: buildNamespaceSupport( nss, node.getParentNode() );
aoqi@0:
aoqi@0: nss.pushContext();
aoqi@0: NamedNodeMap atts = node.getAttributes();
aoqi@0: for( int i=0; i=0; i-- ) {
aoqi@0: Attr a = (Attr)attributes.item(i);
aoqi@0: String name = a.getName();
aoqi@0: // start namespace binding
aoqi@0: if(name.startsWith("xmlns")) {
aoqi@0: if(name.length()==5) {
aoqi@0: receiver.startPrefixMapping( "", a.getValue() );
aoqi@0: } else {
aoqi@0: String localName = a.getLocalName();
aoqi@0: if(localName==null) {
aoqi@0: // DOM built without namespace support has this problem
aoqi@0: localName = name.substring(6);
aoqi@0: }
aoqi@0: receiver.startPrefixMapping( localName, a.getValue() );
aoqi@0: }
aoqi@0: continue;
aoqi@0: }
aoqi@0:
aoqi@0: String uri = a.getNamespaceURI();
aoqi@0: if(uri==null) uri="";
aoqi@0:
aoqi@0: String local = a.getLocalName();
aoqi@0: if(local==null) local = a.getName();
aoqi@0: // add other attributes to the attribute list
aoqi@0: // that we will pass to the ContentHandler
aoqi@0: atts.addAttribute(
aoqi@0: uri,
aoqi@0: local,
aoqi@0: a.getName(),
aoqi@0: "CDATA",
aoqi@0: a.getValue());
aoqi@0: }
aoqi@0:
aoqi@0: String uri = e.getNamespaceURI();
aoqi@0: if(uri==null) uri="";
aoqi@0: String local = e.getLocalName();
aoqi@0: String qname = e.getTagName();
aoqi@0: if(local==null) local = qname;
aoqi@0: receiver.startElement( uri, local, qname, atts );
aoqi@0:
aoqi@0: // visit its children
aoqi@0: NodeList children = e.getChildNodes();
aoqi@0: int clen = children.getLength();
aoqi@0: for( int i=0; i=0; i-- ) {
aoqi@0: Attr a = (Attr)attributes.item(i);
aoqi@0: String name = a.getName();
aoqi@0: if(name.startsWith("xmlns")) {
aoqi@0: if(name.length()==5)
aoqi@0: receiver.endPrefixMapping("");
aoqi@0: else
aoqi@0: receiver.endPrefixMapping(a.getLocalName());
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: private void visit( Node n ) throws SAXException {
aoqi@0: setCurrentLocation( n );
aoqi@0:
aoqi@0: // if a case statement gets too big, it should be made into a separate method.
aoqi@0: switch(n.getNodeType()) {
aoqi@0: case Node.CDATA_SECTION_NODE:
aoqi@0: case Node.TEXT_NODE:
aoqi@0: String value = n.getNodeValue();
aoqi@0: receiver.characters( value.toCharArray(), 0, value.length() );
aoqi@0: break;
aoqi@0: case Node.ELEMENT_NODE:
aoqi@0: visit( (Element)n );
aoqi@0: break;
aoqi@0: case Node.ENTITY_REFERENCE_NODE:
aoqi@0: receiver.skippedEntity(n.getNodeName());
aoqi@0: break;
aoqi@0: case Node.PROCESSING_INSTRUCTION_NODE:
aoqi@0: ProcessingInstruction pi = (ProcessingInstruction)n;
aoqi@0: receiver.processingInstruction(pi.getTarget(),pi.getData());
aoqi@0: break;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: private void setCurrentLocation( Node currNode ) {
aoqi@0: currentNode = currNode;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * The same as {@link #getCurrentElement()} but
aoqi@0: * better typed.
aoqi@0: */
aoqi@0: public Node getCurrentLocation() {
aoqi@0: return currentNode;
aoqi@0: }
aoqi@0:
aoqi@0: public Object getCurrentElement() {
aoqi@0: return currentNode;
aoqi@0: }
aoqi@0:
aoqi@0: public LocatorEx getLocator() {
aoqi@0: return this;
aoqi@0: }
aoqi@0:
aoqi@0: public void setContentHandler(ContentHandler handler) {
aoqi@0: this.receiver = handler;
aoqi@0: }
aoqi@0:
aoqi@0: public ContentHandler getContentHandler() {
aoqi@0: return this.receiver;
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: // LocatorEx implementation
aoqi@0: public String getPublicId() { return null; }
aoqi@0: public String getSystemId() { return null; }
aoqi@0: public int getLineNumber() { return -1; }
aoqi@0: public int getColumnNumber() { return -1; }
aoqi@0:
aoqi@0: public ValidationEventLocator getLocation() {
aoqi@0: return new ValidationEventLocatorImpl(getCurrentLocation());
aoqi@0: }
aoqi@0: }