1 /*
   2  * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * @LastModified: Oct 2017
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.  Oracle designates this
   9  * particular file as subject to the "Classpath" exception as provided
  10  * by Oracle in the LICENSE file that accompanied this code.
  11  *
  12  * This code is distributed in the hope that it will be useful, but WITHOUT
  13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15  * version 2 for more details (a copy is included in the LICENSE file that
  16  * accompanied this code).
  17  *
  18  * You should have received a copy of the GNU General Public License version
  19  * 2 along with this work; if not, write to the Free Software Foundation,
  20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  21  *
  22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  23  * or visit www.oracle.com if you need additional information or have any
  24  * questions.
  25  */
  26 
  27 package com.sun.org.apache.xalan.internal.xsltc.trax;
  28 
  29 import java.util.ArrayList;
  30 import java.util.Collection;
  31 import java.util.Collections;
  32 import java.util.HashMap;
  33 import java.util.Iterator;
  34 import java.util.List;
  35 import java.util.Map;
  36 import javax.xml.stream.XMLEventFactory;
  37 import javax.xml.stream.XMLEventWriter;
  38 import javax.xml.stream.XMLStreamException;
  39 import javax.xml.stream.events.*;
  40 import org.xml.sax.Attributes;
  41 import org.xml.sax.SAXException;
  42 import org.xml.sax.ext.Locator2;
  43 
  44 /**
  45  * @author Sunitha Reddy
  46  */
  47 public class SAX2StAXEventWriter extends SAX2StAXBaseWriter {
  48 
  49 
  50     private XMLEventWriter writer;
  51 
  52 
  53     private XMLEventFactory eventFactory;
  54 
  55 
  56     private List<Collection<Namespace>> namespaceStack = new ArrayList<>();
  57 
  58 
  59     private boolean needToCallStartDocument = false;
  60 
  61 
  62     public SAX2StAXEventWriter() {
  63 
  64         eventFactory = XMLEventFactory.newInstance();
  65 
  66     }
  67 
  68 
  69     public SAX2StAXEventWriter(XMLEventWriter writer) {
  70 
  71         this.writer = writer;
  72         eventFactory = XMLEventFactory.newInstance();
  73 
  74     }
  75 
  76     public SAX2StAXEventWriter(XMLEventWriter writer,
  77             XMLEventFactory factory) {
  78 
  79         this.writer = writer;
  80         if (factory != null) {
  81 
  82             this.eventFactory = factory;
  83 
  84         } else {
  85 
  86             eventFactory = XMLEventFactory.newInstance();
  87 
  88         }
  89 
  90     }
  91 
  92     public XMLEventWriter getEventWriter() {
  93 
  94         return writer;
  95 
  96     }
  97 
  98 
  99     public void setEventWriter(XMLEventWriter writer) {
 100 
 101         this.writer = writer;
 102 
 103     }
 104 
 105 
 106     public XMLEventFactory getEventFactory() {
 107 
 108         return eventFactory;
 109 
 110     }
 111 
 112 
 113     public void setEventFactory(XMLEventFactory factory) {
 114 
 115         this.eventFactory = factory;
 116 
 117     }
 118 
 119     public void startDocument() throws SAXException {
 120 
 121         super.startDocument();
 122 
 123         namespaceStack.clear();
 124 
 125         eventFactory.setLocation(getCurrentLocation());
 126 
 127         // Encoding and version info will be available only after startElement
 128         // is called for first time. So, defer START_DOCUMENT event of StAX till
 129         // that point of time.
 130         needToCallStartDocument = true;
 131     }
 132 
 133     private void writeStartDocument() throws SAXException {
 134         try {
 135             if (docLocator == null)
 136                 writer.add(eventFactory.createStartDocument());
 137             else {
 138                 try{
 139                     writer.add(eventFactory.createStartDocument(((Locator2)docLocator).getEncoding(),((Locator2)docLocator).getXMLVersion()));
 140                 } catch(ClassCastException e){
 141                     writer.add(eventFactory.createStartDocument());
 142                 }
 143             }
 144         } catch (XMLStreamException e) {
 145             throw new SAXException(e);
 146         }
 147         needToCallStartDocument = false;
 148     }
 149 
 150     public void endDocument() throws SAXException {
 151 
 152         eventFactory.setLocation(getCurrentLocation());
 153 
 154         try {
 155 
 156             writer.add(eventFactory.createEndDocument());
 157 
 158         } catch (XMLStreamException e) {
 159 
 160             throw new SAXException(e);
 161 
 162         }
 163 
 164         super.endDocument();
 165 
 166         // clear the namespaces
 167         namespaceStack.clear();
 168 
 169     }
 170 
 171     @SuppressWarnings({"rawtypes", "unchecked"})
 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<Namespace> nsList = namespaceStack.remove(namespaceStack.size() - 1);
 220         Iterator<Namespace> 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     @SuppressWarnings({"rawtypes", "unchecked"})
 331     protected void createStartEvents(Attributes attributes, Collection<Attribute>[] events) {
 332 
 333         Map<String, Attribute> nsMap = null;
 334         List<Attribute> 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.get(i++);
 341                 String uri = (String) namespaces.get(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                     attrs = new ArrayList<>();
 393                 }
 394                 attrs.add(attribute);
 395 
 396             }
 397         }
 398 
 399         events[0] = (nsMap == null ? Collections.EMPTY_LIST : nsMap.values());
 400         events[1] = (attrs == null ? Collections.EMPTY_LIST : attrs);
 401 
 402     }
 403 
 404     protected Namespace createNamespace(String prefix, String uri) {
 405 
 406         if (prefix == null || prefix.length() == 0) {
 407 
 408             return eventFactory.createNamespace(uri);
 409 
 410         } else {
 411 
 412             return eventFactory.createNamespace(prefix, uri);
 413 
 414         }
 415 
 416     }
 417 
 418 }