1 /* 2 * Copyright (c) 1997, 2018, 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.xml.internal.bind.v2.runtime.output; 27 28 import java.io.IOException; 29 import java.io.Writer; 30 import java.lang.reflect.Constructor; 31 32 import javax.xml.stream.XMLStreamException; 33 import javax.xml.stream.XMLStreamWriter; 34 35 import com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler; 36 import com.sun.xml.internal.bind.marshaller.NoEscapeHandler; 37 import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; 38 import com.sun.xml.internal.bind.v2.runtime.XMLSerializer; 39 40 import org.xml.sax.SAXException; 41 42 /** 43 * {@link XmlOutput} that writes to StAX {@link XMLStreamWriter}. 44 * <p> 45 * TODO: 46 * Finding the optimized FI implementations is a bit hacky and not very 47 * extensible. Can we use the service provider mechanism in general for 48 * concrete implementations of XmlOutputAbstractImpl. 49 * 50 * @author Kohsuke Kawaguchi 51 */ 52 public class XMLStreamWriterOutput extends XmlOutputAbstractImpl { 53 54 /** 55 * Creates a new {@link XmlOutput} from a {@link XMLStreamWriter}. 56 * This method recognizes an FI StAX writer. 57 */ 58 public static XmlOutput create(XMLStreamWriter out, JAXBContextImpl context, CharacterEscapeHandler escapeHandler) { 59 // try optimized path 60 final Class writerClass = out.getClass(); 61 if (writerClass==FI_STAX_WRITER_CLASS) { 62 try { 63 return FI_OUTPUT_CTOR.newInstance(out, context); 64 } catch (Exception e) { 65 } 66 } 67 if (STAXEX_WRITER_CLASS!=null && STAXEX_WRITER_CLASS.isAssignableFrom(writerClass)) { 68 try { 69 return STAXEX_OUTPUT_CTOR.newInstance(out); 70 } catch (Exception e) { 71 } 72 } 73 74 CharacterEscapeHandler xmlStreamEscapeHandler = escapeHandler != null ? 75 escapeHandler : NoEscapeHandler.theInstance; 76 77 // otherwise the normal writer. 78 return new XMLStreamWriterOutput(out, xmlStreamEscapeHandler); 79 } 80 81 82 private final XMLStreamWriter out; 83 84 private final CharacterEscapeHandler escapeHandler; 85 86 private final XmlStreamOutWriterAdapter writerWrapper; 87 88 protected final char[] buf = new char[256]; 89 90 protected XMLStreamWriterOutput(XMLStreamWriter out, CharacterEscapeHandler escapeHandler) { 91 this.out = out; 92 this.escapeHandler = escapeHandler; 93 this.writerWrapper = new XmlStreamOutWriterAdapter(out); 94 } 95 96 // not called if we are generating fragments 97 @Override 98 public void startDocument(XMLSerializer serializer, boolean fragment, int[] nsUriIndex2prefixIndex, NamespaceContextImpl nsContext) throws IOException, SAXException, XMLStreamException { 99 super.startDocument(serializer, fragment,nsUriIndex2prefixIndex,nsContext); 100 if(!fragment) 101 out.writeStartDocument(); 102 } 103 104 @Override 105 public void endDocument(boolean fragment) throws IOException, SAXException, XMLStreamException { 106 if(!fragment) { 107 out.writeEndDocument(); 108 out.flush(); 109 } 110 super.endDocument(fragment); 111 } 112 113 public void beginStartTag(int prefix, String localName) throws IOException, XMLStreamException { 114 out.writeStartElement( 115 nsContext.getPrefix(prefix), 116 localName, 117 nsContext.getNamespaceURI(prefix)); 118 119 NamespaceContextImpl.Element nse = nsContext.getCurrent(); 120 if(nse.count()>0) { 121 for( int i=nse.count()-1; i>=0; i-- ) { 122 String uri = nse.getNsUri(i); 123 if(uri.length()==0 && nse.getBase()==1) 124 continue; // no point in definint xmlns='' on the root 125 out.writeNamespace(nse.getPrefix(i),uri); 126 } 127 } 128 } 129 130 public void attribute(int prefix, String localName, String value) throws IOException, XMLStreamException { 131 if(prefix==-1) 132 out.writeAttribute(localName,value); 133 else 134 out.writeAttribute( 135 nsContext.getPrefix(prefix), 136 nsContext.getNamespaceURI(prefix), 137 localName, value); 138 } 139 140 public void endStartTag() throws IOException, SAXException { 141 // noop 142 } 143 144 public void endTag(int prefix, String localName) throws IOException, SAXException, XMLStreamException { 145 out.writeEndElement(); 146 } 147 148 public void text(String value, boolean needsSeparatingWhitespace) throws IOException, SAXException, XMLStreamException { 149 if(needsSeparatingWhitespace) 150 out.writeCharacters(" "); 151 escapeHandler.escape(value.toCharArray(), 0, value.length(), false, writerWrapper); 152 } 153 154 public void text(Pcdata value, boolean needsSeparatingWhitespace) throws IOException, SAXException, XMLStreamException { 155 if(needsSeparatingWhitespace) 156 out.writeCharacters(" "); 157 158 int len = value.length(); 159 if(len <buf.length) { 160 value.writeTo(buf,0); 161 out.writeCharacters(buf,0,len); 162 } else { 163 out.writeCharacters(value.toString()); 164 } 165 } 166 167 /** 168 * Reference to FI's XMLStreamWriter class, if FI can be loaded. 169 */ 170 private static final Class FI_STAX_WRITER_CLASS = initFIStAXWriterClass(); 171 private static final Constructor<? extends XmlOutput> FI_OUTPUT_CTOR = initFastInfosetOutputClass(); 172 173 private static Class initFIStAXWriterClass() { 174 try { 175 Class<?> llfisw = Class.forName("com.sun.xml.internal.org.jvnet.fastinfoset.stax.LowLevelFastInfosetStreamWriter"); 176 Class<?> sds = Class.forName("com.sun.xml.internal.fastinfoset.stax.StAXDocumentSerializer"); 177 // Check if StAXDocumentSerializer implements LowLevelFastInfosetStreamWriter 178 if (llfisw.isAssignableFrom(sds)) 179 return sds; 180 else 181 return null; 182 } catch (Throwable e) { 183 return null; 184 } 185 } 186 187 private static Constructor<? extends XmlOutput> initFastInfosetOutputClass() { 188 try { 189 if (FI_STAX_WRITER_CLASS == null) 190 return null; 191 Class c = Class.forName("com.sun.xml.internal.bind.v2.runtime.output.FastInfosetStreamWriterOutput"); 192 return c.getConstructor(FI_STAX_WRITER_CLASS, JAXBContextImpl.class); 193 } catch (Throwable e) { 194 return null; 195 } 196 } 197 198 // 199 // StAX-ex 200 // 201 private static final Class STAXEX_WRITER_CLASS = initStAXExWriterClass(); 202 private static final Constructor<? extends XmlOutput> STAXEX_OUTPUT_CTOR = initStAXExOutputClass(); 203 204 private static Class initStAXExWriterClass() { 205 try { 206 return Class.forName("com.sun.xml.internal.org.jvnet.staxex.XMLStreamWriterEx"); 207 } catch (Throwable e) { 208 return null; 209 } 210 } 211 212 private static Constructor<? extends XmlOutput> initStAXExOutputClass() { 213 try { 214 Class c = Class.forName("com.sun.xml.internal.bind.v2.runtime.output.StAXExStreamWriterOutput"); 215 return c.getConstructor(STAXEX_WRITER_CLASS); 216 } catch (Throwable e) { 217 return null; 218 } 219 } 220 221 private static final class XmlStreamOutWriterAdapter extends Writer { 222 223 private final XMLStreamWriter writer; 224 225 private XmlStreamOutWriterAdapter(XMLStreamWriter writer) { 226 this.writer = writer; 227 } 228 229 @Override 230 public void write(char[] cbuf, int off, int len) throws IOException { 231 try { 232 writer.writeCharacters(cbuf, off, len); 233 } catch (XMLStreamException e) { 234 throw new IOException("Error writing XML stream", e); 235 } 236 } 237 238 public void writeEntityRef(String entityReference) throws XMLStreamException { 239 writer.writeEntityRef(entityReference); 240 } 241 242 @Override 243 public void flush() throws IOException { 244 try { 245 writer.flush(); 246 } catch (XMLStreamException e) { 247 throw new IOException("Error flushing XML stream", e); 248 } 249 } 250 251 @Override 252 public void close() throws IOException { 253 try { 254 writer.close(); 255 } catch (XMLStreamException e) { 256 throw new IOException("Error closing XML stream", e); 257 } 258 } 259 } 260 }