Sun, 18 Jun 2017 23:18:45 +0100
8172297: In java 8, the marshalling with JAX-WS does not escape carriage return
Reviewed-by: lancea
1 /*
2 * Copyright (c) 1997, 2011, 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 */
26 package com.sun.xml.internal.bind.v2.runtime.unmarshaller;
28 import java.io.ByteArrayInputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.OutputStream;
33 import javax.activation.DataHandler;
34 import javax.activation.DataSource;
35 import javax.xml.stream.XMLStreamException;
37 import javax.xml.stream.XMLStreamWriter;
39 import com.sun.xml.internal.bind.DatatypeConverterImpl;
40 import com.sun.xml.internal.bind.v2.runtime.XMLSerializer;
41 import com.sun.xml.internal.bind.v2.runtime.output.Pcdata;
42 import com.sun.xml.internal.bind.v2.runtime.output.UTF8XmlOutput;
43 import com.sun.xml.internal.bind.v2.util.ByteArrayOutputStreamEx;
44 import com.sun.istack.internal.Nullable;
46 /**
47 * Fed to unmarshaller when the 'text' data is actually
48 * a virtual image of base64 encoding of the binary data
49 * transferred on the wire.
50 *
51 * Used for the MTOM support.
52 *
53 * This object is mutable and the owner of this object can
54 * reuse it with new data.
55 *
56 * Also used by the marshaller to write out the binary data
57 * that could be possibly attached.
58 *
59 * @see XmlVisitor#text(CharSequence)
60 * @see XMLSerializer#text(Pcdata,String)
61 *
62 * @author Kohsuke Kawaguchi, Martin Grebac
63 */
64 public final class Base64Data extends Pcdata {
66 // either dataHandler or (data,dataLen,mimeType?) must be present
67 private DataHandler dataHandler;
68 private byte[] data;
69 /**
70 * Length of the valid data in {@link #data}.
71 */
72 private int dataLen;
73 /**
74 * Optional MIME type of {@link #data}.
75 *
76 * Unused when {@link #dataHandler} is set.
77 * Use {@link DataHandler#getContentType()} in that case.
78 */
79 private @Nullable
80 String mimeType;
82 /**
83 * Fills in the data object by a portion of the byte[].
84 *
85 * @param len
86 * data[0] to data[len-1] are treated as the data.
87 */
88 public void set(byte[] data, int len, @Nullable String mimeType) {
89 this.data = data;
90 this.dataLen = len;
91 this.dataHandler = null;
92 this.mimeType = mimeType;
93 }
95 /**
96 * Fills in the data object by the byte[] of the exact length.
97 *
98 * @param data
99 * this buffer may be owned directly by the unmarshaleld JAXB object.
100 */
101 public void set(byte[] data, @Nullable String mimeType) {
102 set(data, data.length, mimeType);
103 }
105 /**
106 * Fills in the data object by a {@link DataHandler}.
107 */
108 public void set(DataHandler data) {
109 assert data != null;
110 this.dataHandler = data;
111 this.data = null;
112 }
114 /**
115 * Gets the raw data.
116 */
117 public DataHandler getDataHandler() {
118 if (dataHandler == null) {
119 dataHandler = new DataHandler(new DataSource() {
121 public String getContentType() {
122 return getMimeType();
123 }
125 public InputStream getInputStream() {
126 return new ByteArrayInputStream(data, 0, dataLen);
127 }
129 public String getName() {
130 return null;
131 }
133 public OutputStream getOutputStream() {
134 throw new UnsupportedOperationException();
135 }
136 });
137 }
139 return dataHandler;
140 }
142 /**
143 * Gets the byte[] of the exact length.
144 */
145 public byte[] getExact() {
146 get();
147 if (dataLen != data.length) {
148 byte[] buf = new byte[dataLen];
149 System.arraycopy(data, 0, buf, 0, dataLen);
150 data = buf;
151 }
152 return data;
153 }
155 /**
156 * Gets the data as an {@link InputStream}.
157 */
158 public InputStream getInputStream() throws IOException {
159 if (dataHandler != null) {
160 return dataHandler.getInputStream();
161 } else {
162 return new ByteArrayInputStream(data, 0, dataLen);
163 }
164 }
166 /**
167 * Returns false if this object only has {@link DataHandler} and therefore
168 * {@link #get()} operation is likely going to be expensive.
169 */
170 public boolean hasData() {
171 return data != null;
172 }
174 /**
175 * Gets the raw data. The size of the byte array maybe larger than the actual length.
176 */
177 public byte[] get() {
178 if (data == null) {
179 try {
180 ByteArrayOutputStreamEx baos = new ByteArrayOutputStreamEx(1024);
181 InputStream is = dataHandler.getDataSource().getInputStream();
182 baos.readFrom(is);
183 is.close();
184 data = baos.getBuffer();
185 dataLen = baos.size();
186 } catch (IOException e) {
187 // TODO: report the error to the unmarshaller
188 dataLen = 0; // recover by assuming length-0 data
189 }
190 }
191 return data;
192 }
194 public int getDataLen() {
195 return dataLen;
196 }
198 public String getMimeType() {
199 if (mimeType == null) {
200 return "application/octet-stream";
201 }
202 return mimeType;
203 }
205 /**
206 * Gets the number of characters needed to represent
207 * this binary data in the base64 encoding.
208 */
209 public int length() {
210 // for each 3 bytes you use 4 chars
211 // if the remainder is 1 or 2 there will be 4 more
212 get(); // fill in the buffer if necessary
213 return ((dataLen + 2) / 3) * 4;
214 }
216 /**
217 * Encode this binary data in the base64 encoding
218 * and returns the character at the specified position.
219 */
220 public char charAt(int index) {
221 // we assume that the length() method is called before this method
222 // (otherwise how would the caller know that the index is valid?)
223 // so we assume that the byte[] is already populated
225 int offset = index % 4;
226 int base = (index / 4) * 3;
228 byte b1, b2;
230 switch (offset) {
231 case 0:
232 return DatatypeConverterImpl.encode(data[base] >> 2);
233 case 1:
234 if (base + 1 < dataLen) {
235 b1 = data[base + 1];
236 } else {
237 b1 = 0;
238 }
239 return DatatypeConverterImpl.encode(
240 ((data[base] & 0x3) << 4)
241 | ((b1 >> 4) & 0xF));
242 case 2:
243 if (base + 1 < dataLen) {
244 b1 = data[base + 1];
245 if (base + 2 < dataLen) {
246 b2 = data[base + 2];
247 } else {
248 b2 = 0;
249 }
251 return DatatypeConverterImpl.encode(
252 ((b1 & 0xF) << 2)
253 | ((b2 >> 6) & 0x3));
254 } else {
255 return '=';
256 }
257 case 3:
258 if (base + 2 < dataLen) {
259 return DatatypeConverterImpl.encode(data[base + 2] & 0x3F);
260 } else {
261 return '=';
262 }
263 }
265 throw new IllegalStateException();
266 }
268 /**
269 * Internally this is only used to split a text to a list,
270 * which doesn't happen that much for base64.
271 * So this method should be smaller than faster.
272 */
273 public CharSequence subSequence(int start, int end) {
274 StringBuilder buf = new StringBuilder();
275 get(); // fill in the buffer if we haven't done so
276 for (int i = start; i < end; i++) {
277 buf.append(charAt(i));
278 }
279 return buf;
280 }
282 /**
283 * Returns the base64 encoded string of this data.
284 */
285 public String toString() {
286 get(); // fill in the buffer
287 return DatatypeConverterImpl._printBase64Binary(data, 0, dataLen);
288 }
290 @Override
291 public void writeTo(char[] buf, int start) {
292 get();
293 DatatypeConverterImpl._printBase64Binary(data, 0, dataLen, buf, start);
294 }
296 public void writeTo(UTF8XmlOutput output) throws IOException {
297 // TODO: this is inefficient if the data source is note byte[] but DataHandler
298 get();
299 output.text(data, dataLen);
300 }
302 public void writeTo(XMLStreamWriter output) throws IOException, XMLStreamException {
303 get();
304 DatatypeConverterImpl._printBase64Binary(data, 0, dataLen, output);
305 }
307 }