001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.xml.internal.bind.unmarshaller;
027:
028: import java.util.Enumeration;
029:
030: import javax.xml.bind.ValidationEventLocator;
031: import javax.xml.bind.helpers.AbstractUnmarshallerImpl;
032: import javax.xml.bind.helpers.ValidationEventLocatorImpl;
033:
034: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.LocatorEx;
035:
036: import org.w3c.dom.Attr;
037: import org.w3c.dom.Document;
038: import org.w3c.dom.Element;
039: import org.w3c.dom.NamedNodeMap;
040: import org.w3c.dom.Node;
041: import org.w3c.dom.NodeList;
042: import org.w3c.dom.ProcessingInstruction;
043: import org.xml.sax.ContentHandler;
044: import org.xml.sax.Locator;
045: import org.xml.sax.SAXException;
046: import org.xml.sax.helpers.AttributesImpl;
047: import org.xml.sax.helpers.NamespaceSupport;
048:
049: /**
050: * Visits a W3C DOM tree and generates SAX2 events from it.
051: *
052: * <p>
053: * This class is just intended to be used by {@link AbstractUnmarshallerImpl}.
054: * The javax.xml.bind.helpers package is generally a wrong place to put
055: * classes like this.
056: *
057: * @author <ul><li>Kohsuke Kawaguchi, Sun Microsystems, Inc.</li></ul>
058: * @version $Revision: 1.5 $ $Date: 2006/04/18 00:03:46 $
059: * @since JAXB1.0
060: */
061: public class DOMScanner implements LocatorEx, InfosetScanner/*<Node> --- but can't do this to protect 1.0 clients, or can I? */
062: {
063:
064: /** reference to the current node being scanned - used for determining
065: * location info for validation events */
066: private Node currentNode = null;
067:
068: /** To save memory, only one instance of AttributesImpl will be used. */
069: private final AttributesImpl atts = new AttributesImpl();
070:
071: /** This handler will receive SAX2 events. */
072: private ContentHandler receiver = null;
073:
074: private Locator locator = this ;
075:
076: public DOMScanner() {
077: }
078:
079: /**
080: * Configures the locator object that the SAX {@link ContentHandler} will see.
081: */
082: public void setLocator(Locator loc) {
083: this .locator = loc;
084: }
085:
086: public void scan(Object node) throws SAXException {
087: if (node instanceof Document) {
088: scan((Document) node);
089: } else {
090: scan((Element) node);
091: }
092: }
093:
094: public void scan(Document doc) throws SAXException {
095: scan(doc.getDocumentElement());
096: }
097:
098: public void scan(Element e) throws SAXException {
099: setCurrentLocation(e);
100: receiver.startDocument();
101:
102: receiver.setDocumentLocator(locator);
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: setCurrentLocation(e);
120: receiver.endDocument();
121: }
122:
123: /**
124: * Parses a subtree starting from the element e and
125: * reports SAX2 events to the specified handler.
126: *
127: * @deprecated in JAXB 2.0
128: * Use {@link #scan(Element)}
129: */
130: public void parse(Element e, ContentHandler handler)
131: 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)
153: throws SAXException {
154: setContentHandler(handler);
155: scan(e);
156: }
157:
158: /**
159: * Recursively visit ancestors and build up {@link NamespaceSupport} oject.
160: */
161: private void buildNamespaceSupport(NamespaceSupport nss, Node node) {
162: if (node == null || node.getNodeType() != Node.ELEMENT_NODE)
163: return;
164:
165: buildNamespaceSupport(nss, node.getParentNode());
166:
167: nss.pushContext();
168: NamedNodeMap atts = node.getAttributes();
169: for (int i = 0; i < atts.getLength(); i++) {
170: Attr a = (Attr) atts.item(i);
171: if ("xmlns".equals(a.getPrefix())) {
172: nss.declarePrefix(a.getLocalName(), a.getValue());
173: continue;
174: }
175: if ("xmlns".equals(a.getName())) {
176: nss.declarePrefix("", a.getValue());
177: continue;
178: }
179: }
180: }
181:
182: /**
183: * Visits an element and its subtree.
184: */
185: public void visit(Element e) throws SAXException {
186: setCurrentLocation(e);
187: final NamedNodeMap attributes = e.getAttributes();
188:
189: atts.clear();
190: int len = attributes == null ? 0 : attributes.getLength();
191:
192: for (int i = len - 1; i >= 0; i--) {
193: Attr a = (Attr) attributes.item(i);
194: String name = a.getName();
195: // start namespace binding
196: if (name.startsWith("xmlns")) {
197: if (name.length() == 5) {
198: receiver.startPrefixMapping("", a.getValue());
199: } else {
200: String localName = a.getLocalName();
201: if (localName == null) {
202: // DOM built without namespace support has this problem
203: localName = name.substring(6);
204: }
205: receiver
206: .startPrefixMapping(localName, a.getValue());
207: }
208: continue;
209: }
210:
211: String uri = a.getNamespaceURI();
212: if (uri == null)
213: uri = "";
214:
215: String local = a.getLocalName();
216: if (local == null)
217: local = a.getName();
218: // add other attributes to the attribute list
219: // that we will pass to the ContentHandler
220: atts.addAttribute(uri, local, a.getName(), "CDATA", a
221: .getValue());
222: }
223:
224: String uri = e.getNamespaceURI();
225: if (uri == null)
226: uri = "";
227: String local = e.getLocalName();
228: String qname = e.getTagName();
229: if (local == null)
230: local = qname;
231: receiver.startElement(uri, local, qname, atts);
232:
233: // visit its children
234: NodeList children = e.getChildNodes();
235: int clen = children.getLength();
236: for (int i = 0; i < clen; i++)
237: visit(children.item(i));
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
274: .processingInstruction(pi.getTarget(), pi.getData());
275: break;
276: }
277: }
278:
279: private void setCurrentLocation(Node currNode) {
280: currentNode = currNode;
281: }
282:
283: /**
284: * The same as {@link #getCurrentElement()} but
285: * better typed.
286: */
287: public Node getCurrentLocation() {
288: return currentNode;
289: }
290:
291: public Object getCurrentElement() {
292: return currentNode;
293: }
294:
295: public LocatorEx getLocator() {
296: return this ;
297: }
298:
299: public void setContentHandler(ContentHandler handler) {
300: this .receiver = handler;
301: }
302:
303: public ContentHandler getContentHandler() {
304: return this .receiver;
305: }
306:
307: // LocatorEx implementation
308: public String getPublicId() {
309: return null;
310: }
311:
312: public String getSystemId() {
313: return null;
314: }
315:
316: public int getLineNumber() {
317: return -1;
318: }
319:
320: public int getColumnNumber() {
321: return -1;
322: }
323:
324: public ValidationEventLocator getLocation() {
325: return new ValidationEventLocatorImpl(getCurrentLocation());
326: }
327: }
|