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