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