1 /* |
1 /* |
2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. |
2 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 * |
4 * |
5 * This code is free software; you can redistribute it and/or modify it |
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 |
6 * under the terms of the GNU General Public License version 2 only, as |
7 * published by the Free Software Foundation. Oracle designates this |
7 * published by the Free Software Foundation. Oracle designates this |
24 */ |
24 */ |
25 |
25 |
26 package com.sun.xml.internal.bind.v2.runtime.output; |
26 package com.sun.xml.internal.bind.v2.runtime.output; |
27 |
27 |
28 import java.io.IOException; |
28 import java.io.IOException; |
|
29 import java.io.Writer; |
29 import java.lang.reflect.Constructor; |
30 import java.lang.reflect.Constructor; |
30 |
31 |
31 import javax.xml.stream.XMLStreamException; |
32 import javax.xml.stream.XMLStreamException; |
32 import javax.xml.stream.XMLStreamWriter; |
33 import javax.xml.stream.XMLStreamWriter; |
33 |
34 |
|
35 import com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler; |
34 import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; |
36 import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; |
35 import com.sun.xml.internal.bind.v2.runtime.XMLSerializer; |
37 import com.sun.xml.internal.bind.v2.runtime.XMLSerializer; |
36 |
38 |
37 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl; |
|
38 import org.xml.sax.SAXException; |
39 import org.xml.sax.SAXException; |
39 |
40 |
40 /** |
41 /** |
41 * {@link XmlOutput} that writes to StAX {@link XMLStreamWriter}. |
42 * {@link XmlOutput} that writes to StAX {@link XMLStreamWriter}. |
42 * <p> |
43 * <p> |
51 |
52 |
52 /** |
53 /** |
53 * Creates a new {@link XmlOutput} from a {@link XMLStreamWriter}. |
54 * Creates a new {@link XmlOutput} from a {@link XMLStreamWriter}. |
54 * This method recognizes an FI StAX writer. |
55 * This method recognizes an FI StAX writer. |
55 */ |
56 */ |
56 public static XmlOutput create(XMLStreamWriter out, JAXBContextImpl context) { |
57 public static XmlOutput create(XMLStreamWriter out, JAXBContextImpl context, CharacterEscapeHandler escapeHandler) { |
57 // try optimized path |
58 // try optimized path |
58 final Class writerClass = out.getClass(); |
59 final Class writerClass = out.getClass(); |
59 if (writerClass==FI_STAX_WRITER_CLASS) { |
60 if (writerClass==FI_STAX_WRITER_CLASS) { |
60 try { |
61 try { |
61 return FI_OUTPUT_CTOR.newInstance(out, context); |
62 return FI_OUTPUT_CTOR.newInstance(out, context); |
67 return STAXEX_OUTPUT_CTOR.newInstance(out); |
68 return STAXEX_OUTPUT_CTOR.newInstance(out); |
68 } catch (Exception e) { |
69 } catch (Exception e) { |
69 } |
70 } |
70 } |
71 } |
71 |
72 |
|
73 CharacterEscapeHandler xmlStreamEscapeHandler = escapeHandler != null ? |
|
74 escapeHandler : NewLineEscapeHandler.theInstance; |
|
75 |
72 // otherwise the normal writer. |
76 // otherwise the normal writer. |
73 return new XMLStreamWriterOutput(out); |
77 return new XMLStreamWriterOutput(out, xmlStreamEscapeHandler); |
74 } |
78 } |
75 |
79 |
76 |
80 |
77 private final XMLStreamWriter out; |
81 private final XMLStreamWriter out; |
78 |
82 |
|
83 private final CharacterEscapeHandler escapeHandler; |
|
84 |
|
85 private final XmlStreamOutWriterAdapter writerWrapper; |
|
86 |
79 protected final char[] buf = new char[256]; |
87 protected final char[] buf = new char[256]; |
80 |
88 |
81 protected XMLStreamWriterOutput(XMLStreamWriter out) { |
89 protected XMLStreamWriterOutput(XMLStreamWriter out, CharacterEscapeHandler escapeHandler) { |
82 this.out = out; |
90 this.out = out; |
|
91 this.escapeHandler = escapeHandler; |
|
92 this.writerWrapper = new XmlStreamOutWriterAdapter(out); |
83 } |
93 } |
84 |
94 |
85 // not called if we are generating fragments |
95 // not called if we are generating fragments |
86 @Override |
96 @Override |
87 public void startDocument(XMLSerializer serializer, boolean fragment, int[] nsUriIndex2prefixIndex, NamespaceContextImpl nsContext) throws IOException, SAXException, XMLStreamException { |
97 public void startDocument(XMLSerializer serializer, boolean fragment, int[] nsUriIndex2prefixIndex, NamespaceContextImpl nsContext) throws IOException, SAXException, XMLStreamException { |
135 } |
145 } |
136 |
146 |
137 public void text(String value, boolean needsSeparatingWhitespace) throws IOException, SAXException, XMLStreamException { |
147 public void text(String value, boolean needsSeparatingWhitespace) throws IOException, SAXException, XMLStreamException { |
138 if(needsSeparatingWhitespace) |
148 if(needsSeparatingWhitespace) |
139 out.writeCharacters(" "); |
149 out.writeCharacters(" "); |
140 out.writeCharacters(value); |
150 escapeHandler.escape(value.toCharArray(), 0, value.length(), false, writerWrapper); |
141 } |
151 } |
142 |
152 |
143 public void text(Pcdata value, boolean needsSeparatingWhitespace) throws IOException, SAXException, XMLStreamException { |
153 public void text(Pcdata value, boolean needsSeparatingWhitespace) throws IOException, SAXException, XMLStreamException { |
144 if(needsSeparatingWhitespace) |
154 if(needsSeparatingWhitespace) |
145 out.writeCharacters(" "); |
155 out.writeCharacters(" "); |
205 } catch (Throwable e) { |
215 } catch (Throwable e) { |
206 return null; |
216 return null; |
207 } |
217 } |
208 } |
218 } |
209 |
219 |
|
220 |
|
221 /** |
|
222 * Performs character escaping only for new lines. |
|
223 */ |
|
224 private static class NewLineEscapeHandler implements CharacterEscapeHandler { |
|
225 |
|
226 public static final NewLineEscapeHandler theInstance = new NewLineEscapeHandler(); |
|
227 |
|
228 @Override |
|
229 public void escape(char[] ch, int start, int length, boolean isAttVal, Writer out) throws IOException { |
|
230 int limit = start+length; |
|
231 int lastEscaped = start; |
|
232 |
|
233 for (int i = start; i < limit; i++) { |
|
234 char c = ch[i]; |
|
235 if (c == '\r' || c == '\n') { |
|
236 if (i != lastEscaped) { |
|
237 out.write(ch, lastEscaped, i - lastEscaped); |
|
238 } |
|
239 lastEscaped = i + 1; |
|
240 if (out instanceof XmlStreamOutWriterAdapter) { |
|
241 try { |
|
242 ((XmlStreamOutWriterAdapter)out).writeEntityRef("#x" + Integer.toHexString(c)); |
|
243 } catch (XMLStreamException e) { |
|
244 throw new IOException("Error writing xml stream", e); |
|
245 } |
|
246 } else { |
|
247 out.write("&#x"); |
|
248 out.write(Integer.toHexString(c)); |
|
249 out.write(';'); |
|
250 } |
|
251 } |
|
252 } |
|
253 if (lastEscaped != limit) { |
|
254 out.write(ch, lastEscaped, length - lastEscaped); |
|
255 } |
|
256 } |
|
257 } |
|
258 |
|
259 private static final class XmlStreamOutWriterAdapter extends Writer { |
|
260 |
|
261 private final XMLStreamWriter writer; |
|
262 |
|
263 private XmlStreamOutWriterAdapter(XMLStreamWriter writer) { |
|
264 this.writer = writer; |
|
265 } |
|
266 |
|
267 @Override |
|
268 public void write(char[] cbuf, int off, int len) throws IOException { |
|
269 try { |
|
270 writer.writeCharacters(cbuf, off, len); |
|
271 } catch (XMLStreamException e) { |
|
272 throw new IOException("Error writing XML stream", e); |
|
273 } |
|
274 } |
|
275 |
|
276 public void writeEntityRef(String entityReference) throws XMLStreamException { |
|
277 writer.writeEntityRef(entityReference); |
|
278 } |
|
279 |
|
280 @Override |
|
281 public void flush() throws IOException { |
|
282 try { |
|
283 writer.flush(); |
|
284 } catch (XMLStreamException e) { |
|
285 throw new IOException("Error flushing XML stream", e); |
|
286 } |
|
287 } |
|
288 |
|
289 @Override |
|
290 public void close() throws IOException { |
|
291 try { |
|
292 writer.close(); |
|
293 } catch (XMLStreamException e) { |
|
294 throw new IOException("Error closing XML stream", e); |
|
295 } |
|
296 } |
|
297 } |
210 } |
298 } |