1/*
2 * Copyright (c) 2005, 2015, 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.io.IOException;
29import java.util.Iterator;
30
31import org.xml.sax.Attributes;
32import org.xml.sax.ContentHandler;
33import org.xml.sax.DTDHandler;
34import org.xml.sax.EntityResolver;
35import org.xml.sax.ErrorHandler;
36import org.xml.sax.InputSource;
37import org.xml.sax.Locator;
38import org.xml.sax.SAXException;
39import org.xml.sax.SAXNotRecognizedException;
40import org.xml.sax.SAXNotSupportedException;
41import org.xml.sax.XMLReader;
42import org.xml.sax.ext.LexicalHandler;
43import org.xml.sax.helpers.AttributesImpl;
44import org.xml.sax.ext.Locator2;
45import com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl;
46
47import javax.xml.namespace.QName;
48import javax.xml.stream.XMLEventReader;
49import javax.xml.stream.XMLStreamConstants;
50import javax.xml.stream.XMLStreamException;
51import javax.xml.stream.events.Attribute;
52import javax.xml.stream.events.Characters;
53import javax.xml.stream.events.EndElement;
54import javax.xml.stream.events.Namespace;
55import javax.xml.stream.events.ProcessingInstruction;
56import javax.xml.stream.events.StartElement;
57import javax.xml.stream.events.XMLEvent;
58import javax.xml.stream.events.StartDocument;
59
60
61/**
62 * @author Suresh Kumar
63 * @author Sunitha Reddy
64 * @since 1.6
65 */
66public class StAXEvent2SAX implements XMLReader, Locator {
67
68    //private final static String EMPTYSTRING = "";
69    //private static final String XMLNS_PREFIX = "xmlns";
70
71    // StAX event source
72    private final XMLEventReader staxEventReader;
73
74    //private Node _dom = null;
75    private ContentHandler _sax = null;
76    private LexicalHandler _lex = null;
77    private SAXImpl _saxImpl = null;
78    private String version = null;
79    private String encoding = null;
80
81
82    public StAXEvent2SAX(XMLEventReader staxCore) {
83        staxEventReader = staxCore;
84    }
85
86    public ContentHandler getContentHandler() {
87        return _sax;
88    }
89
90    public void setContentHandler(ContentHandler handler) throws
91        NullPointerException
92    {
93        _sax = handler;
94        if (handler instanceof LexicalHandler) {
95            _lex = (LexicalHandler) handler;
96        }
97
98        if (handler instanceof SAXImpl) {
99            _saxImpl = (SAXImpl)handler;
100        }
101    }
102
103
104    public void parse(InputSource unused) throws IOException, SAXException {
105       try {
106            bridge();
107        } catch (XMLStreamException e) {
108            throw new SAXException(e);
109        }
110    }
111
112
113    //Main Work Starts Here.
114    public void parse() throws IOException, SAXException, XMLStreamException {
115        bridge();
116    }
117
118
119    /*  public void parse() throws IOException, SAXException {
120        if (_dom != null) {
121            boolean isIncomplete =
122                (_dom.getNodeType() != org.w3c.dom.Node.DOCUMENT_NODE);
123
124            if (isIncomplete) {
125                _sax.startDocument();
126                parse(_dom);
127                _sax.endDocument();
128            }
129            else {
130                parse(_dom);
131            }
132        }
133    }
134    */
135
136    /*
137     * @see StAXReaderToContentHandler#bridge()
138     */
139    private void bridge() throws XMLStreamException {
140
141        try {
142            // remembers the nest level of elements to know when we are done.
143            int depth=0;
144            boolean startedAtDocument = false;
145
146            XMLEvent event = staxEventReader.peek();
147
148            if (!event.isStartDocument() && !event.isStartElement()) {
149                throw new IllegalStateException();
150            }
151
152            if (event.getEventType() == XMLStreamConstants.START_DOCUMENT){
153                startedAtDocument = true;
154                version = ((StartDocument)event).getVersion();
155                if (((StartDocument)event).encodingSet())
156                    encoding = ((StartDocument)event).getCharacterEncodingScheme();
157                event=staxEventReader.nextEvent(); // that gets the one we peeked at
158                event=staxEventReader.nextEvent(); // that really gets the next one
159            }
160
161            handleStartDocument(event);
162
163            // Handle the prolog: http://www.w3.org/TR/REC-xml/#NT-prolog
164            while (event.getEventType() != XMLStreamConstants.START_ELEMENT) {
165                switch (event.getEventType()) {
166                    case XMLStreamConstants.CHARACTERS :
167                        handleCharacters(event.asCharacters());
168                        break;
169                    case XMLStreamConstants.PROCESSING_INSTRUCTION :
170                        handlePI((ProcessingInstruction)event);
171                        break;
172                    case XMLStreamConstants.COMMENT :
173                        handleComment();
174                        break;
175                    case XMLStreamConstants.DTD :
176                        handleDTD();
177                        break;
178                    case XMLStreamConstants.SPACE :
179                        handleSpace();
180                        break;
181                    default :
182                        throw new InternalError("processing prolog event: " + event);
183                }
184                event=staxEventReader.nextEvent();
185            }
186
187            // Process the (document) element
188            do {
189                // These are all of the events listed in the javadoc for
190                // XMLEvent.
191                // The spec only really describes 11 of them.
192                switch (event.getEventType()) {
193                    case XMLStreamConstants.START_ELEMENT :
194                        depth++;
195                        handleStartElement(event.asStartElement());
196                        break;
197                    case XMLStreamConstants.END_ELEMENT :
198                        handleEndElement(event.asEndElement());
199                        depth--;
200                        break;
201                    case XMLStreamConstants.CHARACTERS :
202                        handleCharacters(event.asCharacters());
203                        break;
204                    case XMLStreamConstants.ENTITY_REFERENCE :
205                        handleEntityReference();
206                        break;
207                    case XMLStreamConstants.PROCESSING_INSTRUCTION :
208                        handlePI((ProcessingInstruction)event);
209                        break;
210                    case XMLStreamConstants.COMMENT :
211                        handleComment();
212                        break;
213                    case XMLStreamConstants.DTD :
214                        handleDTD();
215                        break;
216                    case XMLStreamConstants.ATTRIBUTE :
217                        handleAttribute();
218                        break;
219                    case XMLStreamConstants.NAMESPACE :
220                        handleNamespace();
221                        break;
222                    case XMLStreamConstants.CDATA :
223                        handleCDATA();
224                        break;
225                    case XMLStreamConstants.ENTITY_DECLARATION :
226                        handleEntityDecl();
227                        break;
228                    case XMLStreamConstants.NOTATION_DECLARATION :
229                        handleNotationDecl();
230                        break;
231                    case XMLStreamConstants.SPACE :
232                        handleSpace();
233                        break;
234                    default :
235                        throw new InternalError("processing event: " + event);
236                }
237
238                event=staxEventReader.nextEvent();
239            } while (depth!=0);
240
241            if (startedAtDocument) {
242                // Handle the Misc (http://www.w3.org/TR/REC-xml/#NT-Misc) that can follow the document element
243                while (event.getEventType() != XMLStreamConstants.END_DOCUMENT) {
244                    switch (event.getEventType()) {
245                        case XMLStreamConstants.CHARACTERS :
246                            handleCharacters(event.asCharacters());
247                            break;
248                        case XMLStreamConstants.PROCESSING_INSTRUCTION :
249                            handlePI((ProcessingInstruction)event);
250                            break;
251                        case XMLStreamConstants.COMMENT :
252                            handleComment();
253                            break;
254                        case XMLStreamConstants.SPACE :
255                            handleSpace();
256                            break;
257                        default :
258                            throw new InternalError("processing misc event after document element: " + event);
259                    }
260                    event=staxEventReader.nextEvent();
261                }
262            }
263
264            handleEndDocument();
265        } catch (SAXException e) {
266            throw new XMLStreamException(e);
267        }
268    }
269
270
271    private void handleEndDocument() throws SAXException {
272        _sax.endDocument();
273    }
274
275    private void handleStartDocument(final XMLEvent event) throws SAXException {
276        _sax.setDocumentLocator(new Locator2() {
277            public int getColumnNumber() {
278                return event.getLocation().getColumnNumber();
279            }
280            public int getLineNumber() {
281                return event.getLocation().getLineNumber();
282            }
283            public String getPublicId() {
284                return event.getLocation().getPublicId();
285            }
286            public String getSystemId() {
287                return event.getLocation().getSystemId();
288            }
289            public String getXMLVersion(){
290                return version;
291            }
292            public String getEncoding(){
293                return encoding;
294            }
295
296        });
297        _sax.startDocument();
298    }
299
300    private void handlePI(ProcessingInstruction event)
301        throws XMLStreamException {
302        try {
303            _sax.processingInstruction(
304                event.getTarget(),
305                event.getData());
306        } catch (SAXException e) {
307            throw new XMLStreamException(e);
308        }
309    }
310
311    private void handleCharacters(Characters event) throws XMLStreamException {
312        try {
313            _sax.characters(
314                event.getData().toCharArray(),
315                0,
316                event.getData().length());
317        } catch (SAXException e) {
318            throw new XMLStreamException(e);
319        }
320    }
321
322    private void handleEndElement(EndElement event) throws XMLStreamException {
323        QName qName = event.getName();
324
325        //construct prefix:localName from qName
326        String qname = "";
327        if (qName.getPrefix() != null && qName.getPrefix().trim().length() != 0){
328            qname = qName.getPrefix() + ":";
329        }
330        qname += qName.getLocalPart();
331
332        try {
333            // fire endElement
334            _sax.endElement(
335                qName.getNamespaceURI(),
336                qName.getLocalPart(),
337                qname);
338
339            // end namespace bindings
340            for( Iterator i = event.getNamespaces(); i.hasNext();) {
341                String prefix = (String)i.next();
342                if( prefix == null ) { // true for default namespace
343                    prefix = "";
344                }
345                _sax.endPrefixMapping(prefix);
346            }
347        } catch (SAXException e) {
348            throw new XMLStreamException(e);
349        }
350    }
351
352    private void handleStartElement(StartElement event)
353        throws XMLStreamException {
354        try {
355            // start namespace bindings
356            for (Iterator i = event.getNamespaces(); i.hasNext();) {
357                String prefix = ((Namespace)i.next()).getPrefix();
358                if (prefix == null) { // true for default namespace
359                    prefix = "";
360                }
361                _sax.startPrefixMapping(
362                    prefix,
363                    event.getNamespaceURI(prefix));
364            }
365
366            // fire startElement
367            QName qName = event.getName();
368            String prefix = qName.getPrefix();
369            String rawname;
370            if (prefix == null || prefix.length() == 0) {
371                rawname = qName.getLocalPart();
372            } else {
373                rawname = prefix + ':' + qName.getLocalPart();
374            }
375
376            Attributes saxAttrs = getAttributes(event);
377            _sax.startElement(
378                qName.getNamespaceURI(),
379                qName.getLocalPart(),
380                rawname,
381                saxAttrs);
382        } catch (SAXException e) {
383            throw new XMLStreamException(e);
384        }
385    }
386
387    /**
388     * Get the attributes associated with the given START_ELEMENT StAXevent.
389     *
390     * @return the StAX attributes converted to an org.xml.sax.Attributes
391     */
392    private Attributes getAttributes(StartElement event) {
393        AttributesImpl attrs = new AttributesImpl();
394
395        if ( !event.isStartElement() ) {
396            throw new InternalError(
397                "getAttributes() attempting to process: " + event);
398        }
399
400        // in SAX, namespace declarations are not part of attributes by default.
401        // (there's a property to control that, but as far as we are concerned
402        // we don't use it.) So don't add xmlns:* to attributes.
403
404        // gather non-namespace attrs
405        for (Iterator i = event.getAttributes(); i.hasNext();) {
406            Attribute staxAttr = (javax.xml.stream.events.Attribute)i.next();
407
408            String uri = staxAttr.getName().getNamespaceURI();
409            if (uri == null) {
410                uri = "";
411            }
412            String localName = staxAttr.getName().getLocalPart();
413            String prefix = staxAttr.getName().getPrefix();
414            String qName;
415            if (prefix == null || prefix.length() == 0) {
416                qName = localName;
417            } else {
418                qName = prefix + ':' + localName;
419            }
420            String type = staxAttr.getDTDType();
421            String value = staxAttr.getValue();
422
423            attrs.addAttribute(uri, localName, qName, type, value);
424        }
425
426        return attrs;
427    }
428
429    private void handleNamespace() {
430        // no-op ???
431        // namespace events don't normally occur outside of a startElement
432        // or endElement
433    }
434
435    private void handleAttribute() {
436        // no-op ???
437        // attribute events don't normally occur outside of a startElement
438        // or endElement
439    }
440
441    private void handleDTD() {
442        // no-op ???
443        // it seems like we need to pass this info along, but how?
444    }
445
446    private void handleComment() {
447        // no-op ???
448    }
449
450    private void handleEntityReference() {
451        // no-op ???
452    }
453
454    private void handleSpace() {
455        // no-op ???
456        // this event is listed in the javadoc, but not in the spec.
457    }
458
459    private void handleNotationDecl() {
460        // no-op ???
461        // this event is listed in the javadoc, but not in the spec.
462    }
463
464    private void handleEntityDecl() {
465        // no-op ???
466        // this event is listed in the javadoc, but not in the spec.
467    }
468
469    private void handleCDATA() {
470        // no-op ???
471        // this event is listed in the javadoc, but not in the spec.
472    }
473
474
475    /**
476     * This class is only used internally so this method should never
477     * be called.
478     */
479    public DTDHandler getDTDHandler() {
480        return null;
481    }
482
483    /**
484     * This class is only used internally so this method should never
485     * be called.
486     */
487    public ErrorHandler getErrorHandler() {
488        return null;
489    }
490
491    /**
492     * This class is only used internally so this method should never
493     * be called.
494     */
495    public boolean getFeature(String name) throws SAXNotRecognizedException,
496        SAXNotSupportedException
497    {
498        return false;
499    }
500
501    /**
502     * This class is only used internally so this method should never
503     * be called.
504     */
505    public void setFeature(String name, boolean value) throws
506        SAXNotRecognizedException, SAXNotSupportedException
507    {
508    }
509
510    /**
511     * This class is only used internally so this method should never
512     * be called.
513     */
514    public void parse(String sysId) throws IOException, SAXException {
515        throw new IOException("This method is not yet implemented.");
516    }
517
518    /**
519     * This class is only used internally so this method should never
520     * be called.
521     */
522    public void setDTDHandler(DTDHandler handler) throws NullPointerException {
523    }
524
525    /**
526     * This class is only used internally so this method should never
527     * be called.
528     */
529    public void setEntityResolver(EntityResolver resolver) throws
530        NullPointerException
531    {
532    }
533
534    /**
535     * This class is only used internally so this method should never
536     * be called.
537     */
538    public EntityResolver getEntityResolver() {
539        return null;
540    }
541
542    /**
543     * This class is only used internally so this method should never
544     * be called.
545     */
546    public void setErrorHandler(ErrorHandler handler) throws
547        NullPointerException
548    {
549    }
550
551    /**
552     * This class is only used internally so this method should never
553     * be called.
554     */
555    public void setProperty(String name, Object value) throws
556        SAXNotRecognizedException, SAXNotSupportedException {
557    }
558
559    /**
560     * This class is only used internally so this method should never
561     * be called.
562     */
563    public Object getProperty(String name) throws SAXNotRecognizedException,
564        SAXNotSupportedException
565    {
566        return null;
567    }
568
569    /**
570     * This class is only used internally so this method should never
571     * be called.
572     */
573    public int getColumnNumber() {
574        return 0;
575    }
576
577    /**
578     * This class is only used internally so this method should never
579     * be called.
580     */
581    public int getLineNumber() {
582        return 0;
583    }
584
585    /**
586     * This class is only used internally so this method should never
587     * be called.
588     */
589    public String getPublicId() {
590        return null;
591    }
592
593    /**
594     * This class is only used internally so this method should never
595     * be called.
596     */
597    public String getSystemId() {
598        return null;
599    }
600}
601