1/*
2 * Copyright (c) 2005, 2006, 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.org.apache.xalan.internal.xsltc.trax;
27
28import java.util.ArrayList;
29import java.util.Collection;
30import java.util.Collections;
31import java.util.HashMap;
32import java.util.Iterator;
33import java.util.List;
34import java.util.Map;
35
36import javax.xml.stream.XMLEventFactory;
37import javax.xml.stream.XMLStreamException;
38import javax.xml.stream.events.*;
39import javax.xml.stream.XMLEventWriter;
40
41import org.xml.sax.Attributes;
42import org.xml.sax.SAXException;
43import org.xml.sax.ext.Locator2;
44
45/**
46 * @author Sunitha Reddy
47 */
48public class SAX2StAXEventWriter extends SAX2StAXBaseWriter {
49
50
51    private XMLEventWriter writer;
52
53
54    private XMLEventFactory eventFactory;
55
56
57    private List namespaceStack = new ArrayList();
58
59
60    private boolean needToCallStartDocument = false;
61
62
63    public SAX2StAXEventWriter() {
64
65        eventFactory = XMLEventFactory.newInstance();
66
67    }
68
69
70    public SAX2StAXEventWriter(XMLEventWriter writer) {
71
72        this.writer = writer;
73        eventFactory = XMLEventFactory.newInstance();
74
75    }
76
77    public SAX2StAXEventWriter(XMLEventWriter writer,
78            XMLEventFactory factory) {
79
80        this.writer = writer;
81        if (factory != null) {
82
83            this.eventFactory = factory;
84
85        } else {
86
87            eventFactory = XMLEventFactory.newInstance();
88
89        }
90
91    }
92
93    public XMLEventWriter getEventWriter() {
94
95        return writer;
96
97    }
98
99
100    public void setEventWriter(XMLEventWriter writer) {
101
102        this.writer = writer;
103
104    }
105
106
107    public XMLEventFactory getEventFactory() {
108
109        return eventFactory;
110
111    }
112
113
114    public void setEventFactory(XMLEventFactory factory) {
115
116        this.eventFactory = factory;
117
118    }
119
120    public void startDocument() throws SAXException {
121
122        super.startDocument();
123
124        namespaceStack.clear();
125
126        eventFactory.setLocation(getCurrentLocation());
127
128        // Encoding and version info will be available only after startElement
129        // is called for first time. So, defer START_DOCUMENT event of StAX till
130        // that point of time.
131        needToCallStartDocument = true;
132    }
133
134    private void writeStartDocument() throws SAXException {
135        try {
136            if (docLocator == null)
137                writer.add(eventFactory.createStartDocument());
138            else {
139                try{
140                    writer.add(eventFactory.createStartDocument(((Locator2)docLocator).getEncoding(),((Locator2)docLocator).getXMLVersion()));
141                } catch(ClassCastException e){
142                    writer.add(eventFactory.createStartDocument());
143                }
144            }
145        } catch (XMLStreamException e) {
146            throw new SAXException(e);
147        }
148        needToCallStartDocument = false;
149    }
150
151    public void endDocument() throws SAXException {
152
153        eventFactory.setLocation(getCurrentLocation());
154
155        try {
156
157            writer.add(eventFactory.createEndDocument());
158
159        } catch (XMLStreamException e) {
160
161            throw new SAXException(e);
162
163        }
164
165        super.endDocument();
166
167        // clear the namespaces
168        namespaceStack.clear();
169
170    }
171
172    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
173
174        if (needToCallStartDocument) {
175            writeStartDocument();
176        }
177
178        // set document location
179        eventFactory.setLocation(getCurrentLocation());
180
181        // create attribute and namespace events
182        Collection[] events = {null, null};
183        createStartEvents(attributes, events);
184
185        namespaceStack.add(events[0]);
186
187        try {
188
189            String[] qname = {null, null};
190            parseQName(qName, qname);
191
192            writer.add(eventFactory.createStartElement(qname[0], uri,
193                    qname[1], events[1].iterator(), events[0].iterator()));
194
195        } catch (XMLStreamException e) {
196
197            throw new SAXException(e);
198
199        } finally {
200
201            super.startElement(uri, localName, qName, attributes);
202
203        }
204
205    }
206
207    public void endElement(String uri, String localName, String qName)
208            throws SAXException {
209
210        super.endElement(uri, localName, qName);
211
212        eventFactory.setLocation(getCurrentLocation());
213
214        // parse name
215        String[] qname = {null, null};
216        parseQName(qName, qname);
217
218        // get namespaces
219        Collection nsList = (Collection) namespaceStack.remove(namespaceStack.size() - 1);
220        Iterator nsIter = nsList.iterator();
221
222        try {
223
224            writer.add(eventFactory.createEndElement(qname[0], uri, qname[1],
225                    nsIter));
226
227        } catch (XMLStreamException e) {
228
229            throw new SAXException(e);
230
231        }
232
233    }
234
235    public void comment(char[] ch, int start, int length) throws SAXException {
236        if (needToCallStartDocument) {
237            // Drat. We were trying to postpone this until the first element so that we could get
238            // the locator, but we can't output a comment before the start document, so we're just
239            // going to have to do without the locator if it hasn't been set yet.
240            writeStartDocument();
241        }
242
243        super.comment(ch, start, length);
244
245        eventFactory.setLocation(getCurrentLocation());
246        try {
247
248            writer.add(eventFactory.createComment(new String(ch, start,
249                    length)));
250
251        } catch (XMLStreamException e) {
252
253            throw new SAXException(e);
254
255        }
256
257    }
258
259    public void characters(char[] ch, int start, int length)
260            throws SAXException {
261
262        super.characters(ch, start, length);
263
264        try {
265
266            if (!isCDATA) {
267
268                eventFactory.setLocation(getCurrentLocation());
269                writer.add(eventFactory.createCharacters(new String(ch,
270                        start, length)));
271
272            }
273
274        } catch (XMLStreamException e) {
275
276            throw new SAXException(e);
277
278        }
279
280    }
281
282    public void ignorableWhitespace(char[] ch, int start, int length)
283            throws SAXException {
284
285        super.ignorableWhitespace(ch, start, length);
286        characters(ch, start, length);
287
288    }
289
290    public void processingInstruction(String target, String data)
291            throws SAXException {
292
293        if (needToCallStartDocument) {
294            // Drat. We were trying to postpone this until the first element so that we could get
295            // the locator, but we can't output a PI before the start document, so we're just
296            // going to have to do without the locator if it hasn't been set yet.
297            writeStartDocument();
298        }
299
300        super.processingInstruction(target, data);
301        try {
302
303            writer.add(eventFactory.createProcessingInstruction(target, data));
304
305        } catch (XMLStreamException e) {
306
307            throw new SAXException(e);
308
309        }
310
311    }
312
313    public void endCDATA() throws SAXException {
314
315        eventFactory.setLocation(getCurrentLocation());
316        try {
317
318            writer.add(eventFactory.createCData(CDATABuffer.toString()));
319
320        } catch (XMLStreamException e) {
321
322            throw new SAXException(e);
323
324        }
325
326        super.endCDATA();
327
328    }
329
330
331    protected void createStartEvents(Attributes attributes, Collection[] events) {
332
333        Map nsMap = null;
334        List attrs = null;
335
336        // create namespaces
337        if (namespaces != null) {
338            final int nDecls = namespaces.size();
339            for (int i = 0; i < nDecls; i++) {
340                final String prefix = (String) namespaces.elementAt(i++);
341                String uri = (String) namespaces.elementAt(i);
342                Namespace ns = createNamespace(prefix, uri);
343                if (nsMap == null) {
344                    nsMap = new HashMap();
345                }
346                nsMap.put(prefix, ns);
347            }
348        }
349
350        // create attributes
351        String[] qname = {null, null};
352        for (int i = 0, s = attributes.getLength(); i < s; i++) {
353
354            parseQName(attributes.getQName(i), qname);
355
356            String attrPrefix = qname[0];
357            String attrLocal = qname[1];
358
359            String attrQName = attributes.getQName(i);
360            String attrValue = attributes.getValue(i);
361            String attrURI = attributes.getURI(i);
362
363            if ("xmlns".equals(attrQName) || "xmlns".equals(attrPrefix)) {
364                // namespace declaration disguised as an attribute. If the
365                // namespace has already been declared, skip it, otherwise
366                // write it as an namespace
367                if (nsMap == null) {
368                    nsMap = new HashMap();
369                }
370
371                if (!nsMap.containsKey(attrLocal)) {
372                    Namespace ns = createNamespace(attrLocal, attrValue);
373                    nsMap.put(attrLocal, ns);
374                }
375
376            } else {
377
378                Attribute attribute;
379                if (attrPrefix.length() > 0) {
380
381                    attribute = eventFactory.createAttribute(attrPrefix,
382                            attrURI, attrLocal, attrValue);
383
384                } else {
385
386                    attribute = eventFactory.createAttribute(attrLocal,
387                            attrValue);
388
389                }
390
391                if (attrs == null) {
392
393                    attrs = new ArrayList();
394
395                }
396                attrs.add(attribute);
397
398            }
399        }
400
401        events[0] = (nsMap == null ? Collections.EMPTY_LIST : nsMap.values());
402        events[1] = (attrs == null ? Collections.EMPTY_LIST : attrs);
403
404    }
405
406    protected Namespace createNamespace(String prefix, String uri) {
407
408        if (prefix == null || prefix.length() == 0) {
409
410            return eventFactory.createNamespace(uri);
411
412        } else {
413
414            return eventFactory.createNamespace(prefix, uri);
415
416        }
417
418    }
419
420}
421