1 /* 2 * Copyright (c) 2005, 2020, 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 42 /** 43 * @author Sunitha Reddy 44 */ 45 public class SAX2StAXEventWriter extends SAX2StAXBaseWriter { 46 47 private XMLEventWriter writer; 48 49 private XMLEventFactory eventFactory; 50 51 private List<Collection<Namespace>> namespaceStack = new ArrayList<>(); 52 53 private boolean needToCallStartDocument = false; 54 55 public SAX2StAXEventWriter() { 56 eventFactory = XMLEventFactory.newInstance(); 57 } 58 59 public SAX2StAXEventWriter(XMLEventWriter writer) { 60 this.writer = writer; 61 eventFactory = XMLEventFactory.newInstance(); 62 } 63 64 public SAX2StAXEventWriter(XMLEventWriter writer, 65 XMLEventFactory factory) { 66 67 this.writer = writer; 68 if (factory != null) { 69 this.eventFactory = factory; 70 } else { 71 eventFactory = XMLEventFactory.newInstance(); 72 } 73 } 74 75 public XMLEventWriter getEventWriter() { 76 return writer; 77 } 78 79 public void setEventWriter(XMLEventWriter writer) { 80 this.writer = writer; 81 } 82 83 public XMLEventFactory getEventFactory() { 84 return eventFactory; 85 } 86 87 public void setEventFactory(XMLEventFactory factory) { 88 this.eventFactory = factory; 89 } 90 91 public void startDocument() throws SAXException { 92 super.startDocument(); 93 namespaceStack.clear(); 94 eventFactory.setLocation(getCurrentLocation()); 95 96 // Encoding and version info will be available only after startElement 97 // is called for first time. So, defer START_DOCUMENT event of StAX till 98 // that point of time. 99 needToCallStartDocument = true; 100 } 101 102 void writeStartDocument() throws SAXException { 103 super.writeStartDocument(); 104 try { 105 writer.add(eventFactory.createStartDocument(encoding, xmlVersion)); 106 } catch (XMLStreamException e) { 107 throw new SAXException(e); 108 } 109 needToCallStartDocument = false; 110 } 111 112 public void endDocument() throws SAXException { 113 eventFactory.setLocation(getCurrentLocation()); 114 115 try { 116 writer.add(eventFactory.createEndDocument()); 117 } catch (XMLStreamException e) { 118 throw new SAXException(e); 119 } 120 121 super.endDocument(); 122 123 // clear the namespaces 124 namespaceStack.clear(); 125 126 } 127 128 @SuppressWarnings({"rawtypes", "unchecked"}) 129 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 130 if (needToCallStartDocument) { 131 writeStartDocument(); 132 } 133 134 // set document location 135 eventFactory.setLocation(getCurrentLocation()); 136 137 // create attribute and namespace events 138 Collection[] events = {null, null}; 139 createStartEvents(attributes, events); 140 141 namespaceStack.add(events[0]); 142 143 try { 144 String[] qname = {null, null}; 145 parseQName(qName, qname); 146 147 writer.add(eventFactory.createStartElement(qname[0], uri, 148 qname[1], events[1].iterator(), events[0].iterator())); 149 } catch (XMLStreamException e) { 150 throw new SAXException(e); 151 } finally { 152 super.startElement(uri, localName, qName, attributes); 153 } 154 155 } 156 157 public void endElement(String uri, String localName, String qName) 158 throws SAXException { 159 160 super.endElement(uri, localName, qName); 161 162 eventFactory.setLocation(getCurrentLocation()); 163 164 // parse name 165 String[] qname = {null, null}; 166 parseQName(qName, qname); 167 168 // get namespaces 169 Collection<Namespace> nsList = namespaceStack.remove(namespaceStack.size() - 1); 170 Iterator<Namespace> nsIter = nsList.iterator(); 171 172 try { 173 writer.add(eventFactory.createEndElement(qname[0], uri, qname[1], 174 nsIter)); 175 } catch (XMLStreamException e) { 176 throw new SAXException(e); 177 } 178 } 179 180 public void comment(char[] ch, int start, int length) throws SAXException { 181 if (needToCallStartDocument) { 182 // Drat. We were trying to postpone this until the first element so that we could get 183 // the locator, but we can't output a comment before the start document, so we're just 184 // going to have to do without the locator if it hasn't been set yet. 185 writeStartDocument(); 186 } 187 188 super.comment(ch, start, length); 189 190 eventFactory.setLocation(getCurrentLocation()); 191 try { 192 writer.add(eventFactory.createComment(new String(ch, start, 193 length))); 194 } catch (XMLStreamException e) { 195 throw new SAXException(e); 196 } 197 } 198 199 public void characters(char[] ch, int start, int length) 200 throws SAXException { 201 202 super.characters(ch, start, length); 203 204 try { 205 if (!isCDATA) { 206 eventFactory.setLocation(getCurrentLocation()); 207 writer.add(eventFactory.createCharacters(new String(ch, 208 start, length))); 209 } 210 211 } catch (XMLStreamException e) { 212 throw new SAXException(e); 213 } 214 } 215 216 public void ignorableWhitespace(char[] ch, int start, int length) 217 throws SAXException { 218 219 super.ignorableWhitespace(ch, start, length); 220 characters(ch, start, length); 221 } 222 223 public void processingInstruction(String target, String data) 224 throws SAXException { 225 226 if (needToCallStartDocument) { 227 // Drat. We were trying to postpone this until the first element so that we could get 228 // the locator, but we can't output a PI before the start document, so we're just 229 // going to have to do without the locator if it hasn't been set yet. 230 writeStartDocument(); 231 } 232 233 super.processingInstruction(target, data); 234 try { 235 writer.add(eventFactory.createProcessingInstruction(target, data)); 236 } catch (XMLStreamException e) { 237 throw new SAXException(e); 238 } 239 } 240 241 public void endCDATA() throws SAXException { 242 243 eventFactory.setLocation(getCurrentLocation()); 244 try { 245 writer.add(eventFactory.createCData(CDATABuffer.toString())); 246 } catch (XMLStreamException e) { 247 throw new SAXException(e); 248 } 249 250 super.endCDATA(); 251 } 252 253 @SuppressWarnings({"rawtypes", "unchecked"}) 254 protected void createStartEvents(Attributes attributes, Collection<Attribute>[] events) { 255 256 Map<String, Attribute> nsMap = null; 257 List<Attribute> attrs = null; 258 259 // create namespaces 260 if (namespaces != null) { 261 final int nDecls = namespaces.size(); 262 for (int i = 0; i < nDecls; i++) { 263 final String prefix = namespaces.get(i++); 264 String uri = namespaces.get(i); 265 Namespace ns = createNamespace(prefix, uri); 266 if (nsMap == null) { 267 nsMap = new HashMap<>(); 268 } 269 nsMap.put(prefix, ns); 270 } 271 } 272 273 // create attributes 274 String[] qname = {null, null}; 275 for (int i = 0, s = attributes.getLength(); i < s; i++) { 276 277 parseQName(attributes.getQName(i), qname); 278 279 String attrPrefix = qname[0]; 280 String attrLocal = qname[1]; 281 282 String attrQName = attributes.getQName(i); 283 String attrValue = attributes.getValue(i); 284 String attrURI = attributes.getURI(i); 285 286 if ("xmlns".equals(attrQName) || "xmlns".equals(attrPrefix)) { 287 // namespace declaration disguised as an attribute. If the 288 // namespace has already been declared, skip it, otherwise 289 // write it as an namespace 290 if (nsMap == null) { 291 nsMap = new HashMap<>(); 292 } 293 294 if (!nsMap.containsKey(attrLocal)) { 295 Namespace ns = createNamespace(attrLocal, attrValue); 296 nsMap.put(attrLocal, ns); 297 } 298 299 } else { 300 Attribute attribute; 301 if (attrPrefix.length() > 0) { 302 attribute = eventFactory.createAttribute(attrPrefix, 303 attrURI, attrLocal, attrValue); 304 } else { 305 attribute = eventFactory.createAttribute(attrLocal, 306 attrValue); 307 } 308 309 if (attrs == null) { 310 attrs = new ArrayList<>(); 311 } 312 attrs.add(attribute); 313 } 314 } 315 316 events[0] = (nsMap == null ? Collections.EMPTY_LIST : nsMap.values()); 317 events[1] = (attrs == null ? Collections.EMPTY_LIST : attrs); 318 } 319 320 protected Namespace createNamespace(String prefix, String uri) { 321 if (prefix == null || prefix.length() == 0) { 322 return eventFactory.createNamespace(uri); 323 } else { 324 return eventFactory.createNamespace(prefix, uri); 325 } 326 } 327 }