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 }