44 import java.io.InputStreamReader;
45 import java.io.IOException;
46 import java.io.ObjectInputStream;
47 import java.io.ObjectOutputStream;
48 import java.io.Reader;
49 import java.io.SequenceInputStream;
50 import java.io.StringReader;
51
52 import java.net.URI;
53 import java.net.URISyntaxException;
54
55 import java.nio.ByteBuffer;
56 import java.nio.CharBuffer;
57 import java.nio.charset.Charset;
58 import java.nio.charset.CharsetEncoder;
59 import java.nio.charset.IllegalCharsetNameException;
60 import java.nio.charset.StandardCharsets;
61 import java.nio.charset.UnsupportedCharsetException;
62
63 import java.lang.reflect.Constructor;
64 import java.lang.reflect.InvocationTargetException;
65 import java.lang.reflect.Method;
66 import java.lang.reflect.Modifier;
67
68 import java.security.AccessController;
69 import java.security.PrivilegedAction;
70 import java.security.PrivilegedActionException;
71 import java.security.PrivilegedExceptionAction;
72 import java.security.ProtectionDomain;
73
74 import java.util.*;
75
76 import sun.util.logging.PlatformLogger;
77
78 import sun.awt.AppContext;
79 import sun.awt.SunToolkit;
80
81 import java.awt.image.BufferedImage;
82 import java.awt.image.ImageObserver;
83 import java.awt.image.RenderedImage;
84 import java.awt.image.WritableRaster;
85 import java.awt.image.ColorModel;
86
87 import javax.imageio.ImageIO;
88 import javax.imageio.ImageReader;
89 import javax.imageio.ImageReadParam;
90 import javax.imageio.ImageWriter;
91 import javax.imageio.ImageTypeSpecifier;
92
93 import javax.imageio.spi.ImageWriterSpi;
94
95 import javax.imageio.stream.ImageInputStream;
96 import javax.imageio.stream.ImageOutputStream;
118 * a byte array or InputStream into an Object, given a source format and
119 * a target DataFlavor.
120 *
121 * @author David Mendenhall
122 * @author Danila Sinopalnikov
123 *
124 * @since 1.3.1
125 */
126 public abstract class DataTransferer {
127 /**
128 * The <code>DataFlavor</code> representing a Java text encoding String
129 * encoded in UTF-8, where
130 * <pre>
131 * representationClass = [B
132 * mimeType = "application/x-java-text-encoding"
133 * </pre>
134 */
135 public static final DataFlavor javaTextEncodingFlavor;
136
137 /**
138 * Lazy initialization of Standard Encodings.
139 */
140 private static class StandardEncodingsHolder {
141 private static final SortedSet<String> standardEncodings = load();
142
143 private static SortedSet<String> load() {
144 final Comparator<String> comparator =
145 new CharsetComparator(IndexedComparator.SELECT_WORST);
146 final SortedSet<String> tempSet = new TreeSet<>(comparator);
147 tempSet.add("US-ASCII");
148 tempSet.add("ISO-8859-1");
149 tempSet.add("UTF-8");
150 tempSet.add("UTF-16BE");
151 tempSet.add("UTF-16LE");
152 tempSet.add("UTF-16");
153 tempSet.add(Charset.defaultCharset().name());
154 return Collections.unmodifiableSortedSet(tempSet);
155 }
156 }
157
158 /**
159 * Tracks whether a particular text/* MIME type supports the charset
160 * parameter. The Map is initialized with all of the standard MIME types
161 * listed in the DataFlavor.selectBestTextFlavor method comment. Additional
162 * entries may be added during the life of the JRE for text/<other> types.
163 */
164 private static final Map<String, Boolean> textMIMESubtypeCharsetSupport;
165
166 /**
167 * A collection of all natives listed in flavormap.properties with
168 * a primary MIME type of "text".
169 */
170 private static final Set<Long> textNatives =
171 Collections.synchronizedSet(new HashSet<>());
172
173 /**
174 * The native encodings/charsets for the Set of textNatives.
175 */
176 private static final Map<Long, String> nativeCharsets =
177 Collections.synchronizedMap(new HashMap<>());
178
179 /**
180 * The end-of-line markers for the Set of textNatives.
181 */
182 private static final Map<Long, String> nativeEOLNs =
183 Collections.synchronizedMap(new HashMap<>());
184
185 /**
186 * The number of terminating NUL bytes for the Set of textNatives.
187 */
188 private static final Map<Long, Integer> nativeTerminators =
189 Collections.synchronizedMap(new HashMap<>());
190
191 /**
192 * The key used to store pending data conversion requests for an AppContext.
193 */
194 private static final String DATA_CONVERTER_KEY = "DATA_CONVERTER_KEY";
195
196 private static final PlatformLogger dtLog = PlatformLogger.getLogger("sun.awt.datatransfer.DataTransfer");
197
198 static {
199 DataFlavor tJavaTextEncodingFlavor = null;
200 try {
201 tJavaTextEncodingFlavor = new DataFlavor("application/x-java-text-encoding;class=\"[B\"");
202 } catch (ClassNotFoundException cannotHappen) {
203 }
204 javaTextEncodingFlavor = tJavaTextEncodingFlavor;
205
206 Map<String, Boolean> tempMap = new HashMap<>(17);
207 tempMap.put("sgml", Boolean.TRUE);
208 tempMap.put("xml", Boolean.TRUE);
209 tempMap.put("html", Boolean.TRUE);
210 tempMap.put("enriched", Boolean.TRUE);
211 tempMap.put("richtext", Boolean.TRUE);
212 tempMap.put("uri-list", Boolean.TRUE);
213 tempMap.put("directory", Boolean.TRUE);
214 tempMap.put("css", Boolean.TRUE);
215 tempMap.put("calendar", Boolean.TRUE);
216 tempMap.put("plain", Boolean.TRUE);
217 tempMap.put("rtf", Boolean.FALSE);
218 tempMap.put("tab-separated-values", Boolean.FALSE);
219 tempMap.put("t140", Boolean.FALSE);
220 tempMap.put("rfc822-headers", Boolean.FALSE);
221 tempMap.put("parityfec", Boolean.FALSE);
222 textMIMESubtypeCharsetSupport = Collections.synchronizedMap(tempMap);
223 }
224
225 /**
226 * The accessor method for the singleton DataTransferer instance. Note
227 * that in a headless environment, there may be no DataTransferer instance;
228 * instead, null will be returned.
229 */
230 public static synchronized DataTransferer getInstance() {
231 return ((SunToolkit) Toolkit.getDefaultToolkit()).getDataTransferer();
232 }
233
234 /**
235 * Converts an arbitrary text encoding to its canonical name.
236 */
237 public static String canonicalName(String encoding) {
238 if (encoding == null) {
239 return null;
240 }
241 try {
242 return Charset.forName(encoding).name();
243 } catch (IllegalCharsetNameException icne) {
244 return encoding;
245 } catch (UnsupportedCharsetException uce) {
246 return encoding;
247 }
248 }
249
250 /**
251 * If the specified flavor is a text flavor which supports the "charset"
252 * parameter, then this method returns that parameter, or the default
253 * charset if no such parameter was specified at construction. For non-
254 * text DataFlavors, and for non-charset text flavors, this method returns
255 * null.
256 */
257 public static String getTextCharset(DataFlavor flavor) {
258 if (!isFlavorCharsetTextType(flavor)) {
259 return null;
260 }
261
262 String encoding = flavor.getParameter("charset");
263
264 return (encoding != null) ? encoding : Charset.defaultCharset().name();
265 }
266
267 /**
268 * Tests only whether the flavor's MIME type supports the charset
269 * parameter. Must only be called for flavors with a primary type of
270 * "text".
271 */
272 public static boolean doesSubtypeSupportCharset(DataFlavor flavor) {
273 if (dtLog.isLoggable(PlatformLogger.Level.FINE)) {
274 if (!"text".equals(flavor.getPrimaryType())) {
275 dtLog.fine("Assertion (\"text\".equals(flavor.getPrimaryType())) failed");
276 }
277 }
278
279 String subType = flavor.getSubType();
280 if (subType == null) {
281 return false;
282 }
283
284 Boolean support = textMIMESubtypeCharsetSupport.get(subType);
285
286 if (support != null) {
287 return support;
288 }
289
290 boolean ret_val = (flavor.getParameter("charset") != null);
291 textMIMESubtypeCharsetSupport.put(subType, ret_val);
292 return ret_val;
293 }
294 public static boolean doesSubtypeSupportCharset(String subType,
295 String charset)
296 {
297 Boolean support = textMIMESubtypeCharsetSupport.get(subType);
298
299 if (support != null) {
300 return support;
301 }
302
303 boolean ret_val = (charset != null);
304 textMIMESubtypeCharsetSupport.put(subType, ret_val);
305 return ret_val;
306 }
307
308 /**
309 * Returns whether this flavor is a text type which supports the
310 * 'charset' parameter.
311 */
312 public static boolean isFlavorCharsetTextType(DataFlavor flavor) {
313 // Although stringFlavor doesn't actually support the charset
314 // parameter (because its primary MIME type is not "text"), it should
315 // be treated as though it does. stringFlavor is semantically
316 // equivalent to "text/plain" data.
317 if (DataFlavor.stringFlavor.equals(flavor)) {
318 return true;
319 }
320
321 if (!"text".equals(flavor.getPrimaryType()) ||
322 !doesSubtypeSupportCharset(flavor))
323 {
324 return false;
325 }
326
327 Class<?> rep_class = flavor.getRepresentationClass();
328
329 if (flavor.isRepresentationClassReader() ||
330 String.class.equals(rep_class) ||
331 flavor.isRepresentationClassCharBuffer() ||
332 char[].class.equals(rep_class))
333 {
334 return true;
335 }
336
337 if (!(flavor.isRepresentationClassInputStream() ||
338 flavor.isRepresentationClassByteBuffer() ||
339 byte[].class.equals(rep_class))) {
340 return false;
341 }
342
343 String charset = flavor.getParameter("charset");
344
345 return (charset != null)
346 ? DataTransferer.isEncodingSupported(charset)
347 : true; // null equals default encoding which is always supported
348 }
349
350 /**
351 * Returns whether this flavor is a text type which does not support the
352 * 'charset' parameter.
353 */
354 public static boolean isFlavorNoncharsetTextType(DataFlavor flavor) {
355 if (!"text".equals(flavor.getPrimaryType()) ||
356 doesSubtypeSupportCharset(flavor))
357 {
358 return false;
359 }
360
361 return (flavor.isRepresentationClassInputStream() ||
362 flavor.isRepresentationClassByteBuffer() ||
363 byte[].class.equals(flavor.getRepresentationClass()));
364 }
365
366 /**
367 * Determines whether this JRE can both encode and decode text in the
368 * specified encoding.
369 */
370 private static boolean isEncodingSupported(String encoding) {
371 if (encoding == null) {
372 return false;
373 }
374 try {
375 return Charset.isSupported(encoding);
376 } catch (IllegalCharsetNameException icne) {
377 return false;
378 }
379 }
380
381 /**
382 * Returns {@code true} if the given type is a java.rmi.Remote.
383 */
384 public static boolean isRemote(Class<?> type) {
385 return RMI.isRemote(type);
386 }
387
388 /**
389 * Returns an Iterator which traverses a SortedSet of Strings which are
390 * a total order of the standard character sets supported by the JRE. The
391 * ordering follows the same principles as DataFlavor.selectBestTextFlavor.
392 * So as to avoid loading all available character converters, optional,
393 * non-standard, character sets are not included.
394 */
395 public static Set <String> standardEncodings() {
396 return StandardEncodingsHolder.standardEncodings;
397 }
398
399 /**
400 * Converts a FlavorMap to a FlavorTable.
401 */
402 public static FlavorTable adaptFlavorMap(final FlavorMap map) {
403 if (map instanceof FlavorTable) {
404 return (FlavorTable)map;
405 }
406
407 return new FlavorTable() {
408 @Override
409 public Map<DataFlavor, String> getNativesForFlavors(DataFlavor[] flavors) {
410 return map.getNativesForFlavors(flavors);
411 }
412 @Override
413 public Map<String, DataFlavor> getFlavorsForNatives(String[] natives) {
414 return map.getFlavorsForNatives(natives);
415 }
416 @Override
417 public List<String> getNativesForFlavor(DataFlavor flav) {
418 Map<DataFlavor, String> natives = getNativesForFlavors(new DataFlavor[]{flav});
419 String nat = natives.get(flav);
583 // SystemFlavorMap.getNativesForFlavor will return
584 // text/plain natives for all text/*. While this is good
585 // for a single text/* flavor, we would prefer that
586 // text/plain native data come from a text/plain flavor.
587 if (("text".equals(flavor.getPrimaryType()) &&
588 "plain".equals(flavor.getSubType())) ||
589 flavor.equals(DataFlavor.stringFlavor)) {
590 textPlainMap.put(lFormat, flavor);
591 textPlainIndexMap.put(lFormat, index);
592 }
593 }
594
595 currentIndex += natives.size();
596 }
597 }
598
599 formatMap.putAll(textPlainMap);
600 indexMap.putAll(textPlainIndexMap);
601
602 // Sort the map keys according to the formats preference order.
603 Comparator<Long> comparator =
604 new IndexOrderComparator(indexMap, IndexedComparator.SELECT_WORST);
605 SortedMap<Long, DataFlavor> sortedMap = new TreeMap<>(comparator);
606 sortedMap.putAll(formatMap);
607
608 return sortedMap;
609 }
610
611 /**
612 * Reduces the Map output for the root function to an array of the
613 * Map's keys.
614 */
615 public long[] getFormatsForTransferableAsArray(Transferable contents,
616 FlavorTable map) {
617 return keysToLongArray(getFormatsForTransferable(contents, map));
618 }
619
620 /**
621 * Returns a Map whose keys are all of the possible DataFlavors into which
622 * data in the specified formats can be translated. The value of each key
623 * is the format in which the Clipboard or dropped data should be requested
624 * when converting to the DataFlavor.
955 }
956 if (flavor.equals(DataFlavor.plainTextFlavor) &&
957 !(obj instanceof InputStream))
958 {
959 obj = contents.getTransferData(DataFlavor.stringFlavor);
960 if (obj == null) {
961 return null;
962 }
963 stringSelectionHack = true;
964 } else {
965 stringSelectionHack = false;
966 }
967 } catch (UnsupportedFlavorException e) {
968 throw new IOException(e.getMessage());
969 }
970
971 // Source data is a String. Search-and-replace EOLN. Encode into the
972 // target format. Append terminating NUL bytes.
973 if (stringSelectionHack ||
974 (String.class.equals(flavor.getRepresentationClass()) &&
975 isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
976
977 String str = removeSuspectedData(flavor, contents, (String)obj);
978
979 return translateTransferableString(
980 str,
981 format);
982
983 // Source data is a Reader. Convert to a String and recur. In the
984 // future, we may want to rewrite this so that we encode on demand.
985 } else if (flavor.isRepresentationClassReader()) {
986 if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
987 throw new IOException
988 ("cannot transfer non-text data as Reader");
989 }
990
991 StringBuilder buf = new StringBuilder();
992 try (Reader r = (Reader)obj) {
993 int c;
994 while ((c = r.read()) != -1) {
995 buf.append((char)c);
996 }
997 }
998
999 return translateTransferableString(
1000 buf.toString(),
1001 format);
1002
1003 // Source data is a CharBuffer. Convert to a String and recur.
1004 } else if (flavor.isRepresentationClassCharBuffer()) {
1005 if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
1006 throw new IOException
1007 ("cannot transfer non-text data as CharBuffer");
1008 }
1009
1010 CharBuffer buffer = (CharBuffer)obj;
1011 int size = buffer.remaining();
1012 char[] chars = new char[size];
1013 buffer.get(chars, 0, size);
1014
1015 return translateTransferableString(
1016 new String(chars),
1017 format);
1018
1019 // Source data is a char array. Convert to a String and recur.
1020 } else if (char[].class.equals(flavor.getRepresentationClass())) {
1021 if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
1022 throw new IOException
1023 ("cannot transfer non-text data as char array");
1024 }
1025
1026 return translateTransferableString(
1027 new String((char[])obj),
1028 format);
1029
1030 // Source data is a ByteBuffer. For arbitrary flavors, simply return
1031 // the array. For text flavors, decode back to a String and recur to
1032 // reencode according to the requested format.
1033 } else if (flavor.isRepresentationClassByteBuffer()) {
1034 ByteBuffer buffer = (ByteBuffer)obj;
1035 int size = buffer.remaining();
1036 byte[] bytes = new byte[size];
1037 buffer.get(bytes, 0, size);
1038
1039 if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1040 String sourceEncoding = DataTransferer.getTextCharset(flavor);
1041 return translateTransferableString(
1042 new String(bytes, sourceEncoding),
1043 format);
1044 } else {
1045 return bytes;
1046 }
1047
1048 // Source data is a byte array. For arbitrary flavors, simply return
1049 // the array. For text flavors, decode back to a String and recur to
1050 // reencode according to the requested format.
1051 } else if (byte[].class.equals(flavor.getRepresentationClass())) {
1052 byte[] bytes = (byte[])obj;
1053
1054 if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1055 String sourceEncoding = DataTransferer.getTextCharset(flavor);
1056 return translateTransferableString(
1057 new String(bytes, sourceEncoding),
1058 format);
1059 } else {
1060 return bytes;
1061 }
1062 // Source data is Image
1063 } else if (DataFlavor.imageFlavor.equals(flavor)) {
1064 if (!isImageFormat(format)) {
1065 throw new IOException("Data translation failed: " +
1066 "not an image format");
1067 }
1068
1069 Image image = (Image)obj;
1070 byte[] bytes = imageToPlatformBytes(image, format);
1071
1072 if (bytes == null) {
1073 throw new IOException("Data translation failed: " +
1074 "cannot convert java image to native format");
1075 }
1138 theByteArray = bos.toByteArray();
1139 }
1140
1141 // Source data is an InputStream. For arbitrary flavors, just grab the
1142 // bytes and dump them into a byte array. For text flavors, decode back
1143 // to a String and recur to reencode according to the requested format.
1144 } else if (flavor.isRepresentationClassInputStream()) {
1145 try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
1146 try (InputStream is = (InputStream)obj) {
1147 boolean eof = false;
1148 int avail = is.available();
1149 byte[] tmp = new byte[avail > 8192 ? avail : 8192];
1150 do {
1151 int aValue;
1152 if (!(eof = (aValue = is.read(tmp, 0, tmp.length)) == -1)) {
1153 bos.write(tmp, 0, aValue);
1154 }
1155 } while (!eof);
1156 }
1157
1158 if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1159 byte[] bytes = bos.toByteArray();
1160 String sourceEncoding = DataTransferer.getTextCharset(flavor);
1161 return translateTransferableString(
1162 new String(bytes, sourceEncoding),
1163 format);
1164 }
1165 theByteArray = bos.toByteArray();
1166 }
1167
1168
1169
1170 // Source data is an RMI object
1171 } else if (flavor.isRepresentationClassRemote()) {
1172
1173 Object mo = RMI.newMarshalledObject(obj);
1174 theByteArray = convertObjectToBytes(mo);
1175
1176 // Source data is Serializable
1177 } else if (flavor.isRepresentationClassSerializable()) {
1178
1179 theByteArray = convertObjectToBytes(obj);
1180
1181 } else {
1182 throw new IOException("data translation failed");
1183 }
1184
1185
1186
1187 return theByteArray;
1188 }
1189
1190 private static byte[] convertObjectToBytes(Object object) throws IOException {
1191 try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
1192 ObjectOutputStream oos = new ObjectOutputStream(bos))
1193 {
1194 oos.writeObject(object);
1370 URI uris[] = dragQueryURIs(str, format, localeTransferable);
1371 if (uris == null) {
1372 return null;
1373 }
1374 List<File> files = new ArrayList<>();
1375 for (URI uri : uris) {
1376 try {
1377 files.add(new File(uri));
1378 } catch (IllegalArgumentException illegalArg) {
1379 // When converting from URIs to less generic files,
1380 // common practice (Wine, SWT) seems to be to
1381 // silently drop the URIs that aren't local files.
1382 }
1383 }
1384 theObject = files;
1385 }
1386
1387 // Target data is a String. Strip terminating NUL bytes. Decode bytes
1388 // into characters. Search-and-replace EOLN.
1389 } else if (String.class.equals(flavor.getRepresentationClass()) &&
1390 isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1391
1392 theObject = translateBytesToString(bytes, format, localeTransferable);
1393
1394 // Target data is a Reader. Obtain data in InputStream format, encoded
1395 // as "Unicode" (utf-16be). Then use an InputStreamReader to decode
1396 // back to chars on demand.
1397 } else if (flavor.isRepresentationClassReader()) {
1398 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
1399 theObject = translateStream(bais,
1400 flavor, format, localeTransferable);
1401 }
1402 // Target data is a CharBuffer. Recur to obtain String and wrap.
1403 } else if (flavor.isRepresentationClassCharBuffer()) {
1404 if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
1405 throw new IOException
1406 ("cannot transfer non-text data as CharBuffer");
1407 }
1408
1409 CharBuffer buffer = CharBuffer.wrap(
1410 translateBytesToString(bytes,format, localeTransferable));
1411
1412 theObject = constructFlavoredObject(buffer, flavor, CharBuffer.class);
1413
1414 // Target data is a char array. Recur to obtain String and convert to
1415 // char array.
1416 } else if (char[].class.equals(flavor.getRepresentationClass())) {
1417 if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
1418 throw new IOException
1419 ("cannot transfer non-text data as char array");
1420 }
1421
1422 theObject = translateBytesToString(
1423 bytes, format, localeTransferable).toCharArray();
1424
1425 // Target data is a ByteBuffer. For arbitrary flavors, just return
1426 // the raw bytes. For text flavors, convert to a String to strip
1427 // terminators and search-and-replace EOLN, then reencode according to
1428 // the requested flavor.
1429 } else if (flavor.isRepresentationClassByteBuffer()) {
1430 if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1431 bytes = translateBytesToString(
1432 bytes, format, localeTransferable).getBytes(
1433 DataTransferer.getTextCharset(flavor)
1434 );
1435 }
1436
1437 ByteBuffer buffer = ByteBuffer.wrap(bytes);
1438 theObject = constructFlavoredObject(buffer, flavor, ByteBuffer.class);
1439
1440 // Target data is a byte array. For arbitrary flavors, just return
1441 // the raw bytes. For text flavors, convert to a String to strip
1442 // terminators and search-and-replace EOLN, then reencode according to
1443 // the requested flavor.
1444 } else if (byte[].class.equals(flavor.getRepresentationClass())) {
1445 if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1446 theObject = translateBytesToString(
1447 bytes, format, localeTransferable
1448 ).getBytes(DataTransferer.getTextCharset(flavor));
1449 } else {
1450 theObject = bytes;
1451 }
1452
1453 // Target data is an InputStream. For arbitrary flavors, just return
1454 // the raw bytes. For text flavors, decode to strip terminators and
1455 // search-and-replace EOLN, then reencode according to the requested
1456 // flavor.
1457 } else if (flavor.isRepresentationClassInputStream()) {
1458
1459 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
1460 theObject = translateStream(bais, flavor, format, localeTransferable);
1461 }
1462
1463 } else if (flavor.isRepresentationClassRemote()) {
1464 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
1465 ObjectInputStream ois = new ObjectInputStream(bais))
1466 {
1467 theObject = RMI.getMarshalledObject(ois.readObject());
1468 } catch (Exception e) {
1469 throw new IOException(e.getMessage());
1470 }
1471
1472 // Target data is Serializable
1473 } else if (flavor.isRepresentationClassSerializable()) {
1474
1475 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
1476 theObject = translateStream(bais, flavor, format, localeTransferable);
1477 }
1478
1479 // Target data is Image
1480 } else if (DataFlavor.imageFlavor.equals(flavor)) {
1481 if (!isImageFormat(format)) {
1482 throw new IOException("data translation failed");
1483 }
1484
1485 theObject = platformImageBytesToImage(bytes, format);
1486 }
1487
1512
1513 URI uris[] = dragQueryURIs(str, format, localeTransferable);
1514 if (uris == null) {
1515 return null;
1516 }
1517 List<File> files = new ArrayList<>();
1518 for (URI uri : uris) {
1519 try {
1520 files.add(new File(uri));
1521 } catch (IllegalArgumentException illegalArg) {
1522 // When converting from URIs to less generic files,
1523 // common practice (Wine, SWT) seems to be to
1524 // silently drop the URIs that aren't local files.
1525 }
1526 }
1527 theObject = files;
1528
1529 // Target data is a String. Strip terminating NUL bytes. Decode bytes
1530 // into characters. Search-and-replace EOLN.
1531 } else if (String.class.equals(flavor.getRepresentationClass()) &&
1532 isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1533
1534 return translateBytesToString(inputStreamToByteArray(str),
1535 format, localeTransferable);
1536
1537 // Special hack to maintain backwards-compatibility with the brokenness
1538 // of StringSelection. Return a StringReader instead of an InputStream.
1539 // Recur to obtain String and encapsulate.
1540 } else if (DataFlavor.plainTextFlavor.equals(flavor)) {
1541 theObject = new StringReader(translateBytesToString(
1542 inputStreamToByteArray(str),
1543 format, localeTransferable));
1544
1545 // Target data is an InputStream. For arbitrary flavors, just return
1546 // the raw bytes. For text flavors, decode to strip terminators and
1547 // search-and-replace EOLN, then reencode according to the requested
1548 // flavor.
1549 } else if (flavor.isRepresentationClassInputStream()) {
1550 theObject = translateStreamToInputStream(str, flavor, format,
1551 localeTransferable);
1552
1553 // Target data is a Reader. Obtain data in InputStream format, encoded
1554 // as "Unicode" (utf-16be). Then use an InputStreamReader to decode
1555 // back to chars on demand.
1556 } else if (flavor.isRepresentationClassReader()) {
1557 if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
1558 throw new IOException
1559 ("cannot transfer non-text data as Reader");
1560 }
1561
1562 InputStream is = (InputStream)translateStreamToInputStream(
1563 str, DataFlavor.plainTextFlavor,
1564 format, localeTransferable);
1565
1566 String unicode = DataTransferer.getTextCharset(DataFlavor.plainTextFlavor);
1567
1568 Reader reader = new InputStreamReader(is, unicode);
1569
1570 theObject = constructFlavoredObject(reader, flavor, Reader.class);
1571 // Target data is a byte array
1572 } else if (byte[].class.equals(flavor.getRepresentationClass())) {
1573 if(isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1574 theObject = translateBytesToString(inputStreamToByteArray(str), format, localeTransferable)
1575 .getBytes(DataTransferer.getTextCharset(flavor));
1576 } else {
1577 theObject = inputStreamToByteArray(str);
1578 }
1579 // Target data is an RMI object
1580 } else if (flavor.isRepresentationClassRemote()) {
1581
1582 try (ObjectInputStream ois =
1583 new ObjectInputStream(str))
1584 {
1585 theObject = RMI.getMarshalledObject(ois.readObject());
1586 }catch (Exception e) {
1587 throw new IOException(e.getMessage());
1588 }
1589
1590 // Target data is Serializable
1591 } else if (flavor.isRepresentationClassSerializable()) {
1592 try (ObjectInputStream ois =
1593 new ObjectInputStream(str))
1594 {
1595 theObject = ois.readObject();
1596 } catch (Exception e) {
1597 throw new IOException(e.getMessage());
1598 }
1599 // Target data is Image
1600 } else if (DataFlavor.imageFlavor.equals(flavor)) {
1601 if (!isImageFormat(format)) {
1602 throw new IOException("data translation failed");
1603 }
1604 theObject = platformImageBytesToImage(inputStreamToByteArray(str), format);
1605 }
1606
1607 if (theObject == null) {
1608 throw new IOException("data translation failed");
1609 }
1610
1611 return theObject;
1612
1613 }
1614
1615 /**
1616 * For arbitrary flavors, just use the raw InputStream. For text flavors,
1617 * ReencodingInputStream will decode and reencode the InputStream on demand
1618 * so that we can strip terminators and search-and-replace EOLN.
1619 */
1620 private Object translateStreamToInputStream
1621 (InputStream str, DataFlavor flavor, long format,
1622 Transferable localeTransferable) throws IOException
1623 {
1624 if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1625 str = new ReencodingInputStream
1626 (str, format, DataTransferer.getTextCharset(flavor),
1627 localeTransferable);
1628 }
1629
1630 return constructFlavoredObject(str, flavor, InputStream.class);
1631 }
1632
1633 /**
1634 * We support representations which are exactly of the specified Class,
1635 * and also arbitrary Objects which have a constructor which takes an
1636 * instance of the Class as its sole parameter.
1637 */
1638 private Object constructFlavoredObject(Object arg, DataFlavor flavor,
1639 Class<?> clazz)
1640 throws IOException
1641 {
1642 final Class<?> dfrc = flavor.getRepresentationClass();
1643
1644 if (clazz.equals(dfrc)) {
1645 return arg; // simple case
1646 } else {
1848
1849 /**
1850 * Translates either a byte array or an input stream which contain
1851 * platform-specific image data in the given format into an Image.
1852 */
1853
1854
1855 protected abstract Image platformImageBytesToImage(
1856 byte[] bytes,long format) throws IOException;
1857
1858 /**
1859 * Translates either a byte array or an input stream which contain
1860 * an image data in the given standard format into an Image.
1861 *
1862 * @param mimeType image MIME type, such as: image/png, image/jpeg, image/gif
1863 */
1864 protected Image standardImageBytesToImage(
1865 byte[] bytes, String mimeType) throws IOException
1866 {
1867
1868 Iterator<ImageReader> readerIterator =
1869 ImageIO.getImageReadersByMIMEType(mimeType);
1870
1871 if (!readerIterator.hasNext()) {
1872 throw new IOException("No registered service provider can decode " +
1873 " an image from " + mimeType);
1874 }
1875
1876 IOException ioe = null;
1877
1878 while (readerIterator.hasNext()) {
1879 ImageReader imageReader = readerIterator.next();
1880 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
1881 try (ImageInputStream imageInputStream = ImageIO.createImageInputStream(bais)) {
1882 ImageReadParam param = imageReader.getDefaultReadParam();
1883 imageReader.setInput(imageInputStream, true, true);
1884 BufferedImage bufferedImage = imageReader.read(imageReader.getMinIndex(), param);
1885 if (bufferedImage != null) {
1886 return bufferedImage;
1887 }
1888 } finally {
1889 imageReader.dispose();
1902 throw ioe;
1903 }
1904
1905 /**
1906 * Translates a Java Image into a byte array which contains platform-
1907 * specific image data in the given format.
1908 */
1909 protected abstract byte[] imageToPlatformBytes(Image image, long format)
1910 throws IOException;
1911
1912 /**
1913 * Translates a Java Image into a byte array which contains
1914 * an image data in the given standard format.
1915 *
1916 * @param mimeType image MIME type, such as: image/png, image/jpeg
1917 */
1918 protected byte[] imageToStandardBytes(Image image, String mimeType)
1919 throws IOException {
1920 IOException originalIOE = null;
1921
1922 Iterator<ImageWriter> writerIterator =
1923 ImageIO.getImageWritersByMIMEType(mimeType);
1924
1925 if (!writerIterator.hasNext()) {
1926 throw new IOException("No registered service provider can encode " +
1927 " an image to " + mimeType);
1928 }
1929
1930 if (image instanceof RenderedImage) {
1931 // Try to encode the original image.
1932 try {
1933 return imageToStandardBytesImpl((RenderedImage)image, mimeType);
1934 } catch (IOException ioe) {
1935 originalIOE = ioe;
1936 }
1937 }
1938
1939 // Retry with a BufferedImage.
1940 int width = 0;
1941 int height = 0;
1942 if (image instanceof ToolkitImage) {
1943 ImageRepresentation ir = ((ToolkitImage)image).getImageRep();
1962 g.drawImage(image, 0, 0, width, height, null);
1963 } finally {
1964 g.dispose();
1965 }
1966
1967 try {
1968 return imageToStandardBytesImpl(bufferedImage, mimeType);
1969 } catch (IOException ioe) {
1970 if (originalIOE != null) {
1971 throw originalIOE;
1972 } else {
1973 throw ioe;
1974 }
1975 }
1976 }
1977
1978 byte[] imageToStandardBytesImpl(RenderedImage renderedImage,
1979 String mimeType)
1980 throws IOException {
1981
1982 Iterator<ImageWriter> writerIterator =
1983 ImageIO.getImageWritersByMIMEType(mimeType);
1984
1985 ImageTypeSpecifier typeSpecifier =
1986 new ImageTypeSpecifier(renderedImage);
1987
1988 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1989 IOException ioe = null;
1990
1991 while (writerIterator.hasNext()) {
1992 ImageWriter imageWriter = writerIterator.next();
1993 ImageWriterSpi writerSpi = imageWriter.getOriginatingProvider();
1994
1995 if (!writerSpi.canEncodeImage(typeSpecifier)) {
1996 continue;
1997 }
1998
1999 try {
2000 try (ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(baos)) {
2001 imageWriter.setOutput(imageOutputStream);
2002 imageWriter.write(renderedImage);
2003 imageOutputStream.flush();
2146 return ret;
2147 }
2148
2149 public void processDataConversionRequests() {
2150 if (EventQueue.isDispatchThread()) {
2151 AppContext appContext = AppContext.getAppContext();
2152 getToolkitThreadBlockedHandler().lock();
2153 try {
2154 Runnable dataConverter =
2155 (Runnable)appContext.get(DATA_CONVERTER_KEY);
2156 if (dataConverter != null) {
2157 dataConverter.run();
2158 appContext.remove(DATA_CONVERTER_KEY);
2159 }
2160 } finally {
2161 getToolkitThreadBlockedHandler().unlock();
2162 }
2163 }
2164 }
2165
2166 public abstract ToolkitThreadBlockedHandler
2167 getToolkitThreadBlockedHandler();
2168
2169 /**
2170 * Helper function to reduce a Map with Long keys to a long array.
2171 * <p>
2172 * The map keys are sorted according to the native formats preference
2173 * order.
2174 */
2175 public static long[] keysToLongArray(SortedMap<Long, ?> map) {
2176 Set<Long> keySet = map.keySet();
2177 long[] retval = new long[keySet.size()];
2178 int i = 0;
2179 for (Iterator<Long> iter = keySet.iterator(); iter.hasNext(); i++) {
2180 retval[i] = iter.next();
2181 }
2182 return retval;
2183 }
2184
2185 /**
2186 * Helper function to convert a Set of DataFlavors to a sorted array.
2187 * The array will be sorted according to <code>DataFlavorComparator</code>.
2188 */
2189 public static DataFlavor[] setToSortedDataFlavorArray(Set<DataFlavor> flavorsSet) {
2190 DataFlavor[] flavors = new DataFlavor[flavorsSet.size()];
2191 flavorsSet.toArray(flavors);
2192 final Comparator<DataFlavor> comparator =
2193 new DataFlavorComparator(IndexedComparator.SELECT_WORST);
2194 Arrays.sort(flavors, comparator);
2195 return flavors;
2196 }
2197
2198 /**
2199 * Helper function to convert an InputStream to a byte[] array.
2200 */
2201 protected static byte[] inputStreamToByteArray(InputStream str)
2202 throws IOException
2203 {
2204 try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
2205 int len = 0;
2206 byte[] buf = new byte[8192];
2207
2208 while ((len = str.read(buf)) != -1) {
2209 baos.write(buf, 0, len);
2210 }
2211
2212 return baos.toByteArray();
2213 }
2214 }
2215
2216 /**
2217 * Returns platform-specific mappings for the specified native.
2218 * If there are no platform-specific mappings for this native, the method
2219 * returns an empty <code>List</code>.
2220 */
2221 public LinkedHashSet<DataFlavor> getPlatformMappingsForNative(String nat) {
2222 return new LinkedHashSet<>();
2223 }
2224
2225 /**
2226 * Returns platform-specific mappings for the specified flavor.
2227 * If there are no platform-specific mappings for this flavor, the method
2228 * returns an empty <code>List</code>.
2229 */
2230 public LinkedHashSet<String> getPlatformMappingsForFlavor(DataFlavor df) {
2231 return new LinkedHashSet<>();
2232 }
2233
2234 /**
2235 * A Comparator which includes a helper function for comparing two Objects
2236 * which are likely to be keys in the specified Map.
2237 */
2238 public abstract static class IndexedComparator<T> implements Comparator<T> {
2239
2240 /**
2241 * The best Object (e.g., DataFlavor) will be the last in sequence.
2242 */
2243 public static final boolean SELECT_BEST = true;
2244
2245 /**
2246 * The best Object (e.g., DataFlavor) will be the first in sequence.
2247 */
2248 public static final boolean SELECT_WORST = false;
2249
2250 final boolean order;
2251
2252 public IndexedComparator(boolean order) {
2253 this.order = order;
2254 }
2255
2256 /**
2257 * Helper method to compare two objects by their Integer indices in the
2258 * given map. If the map doesn't contain an entry for either of the
2259 * objects, the fallback index will be used for the object instead.
2260 *
2261 * @param indexMap the map which maps objects into Integer indexes.
2262 * @param obj1 the first object to be compared.
2263 * @param obj2 the second object to be compared.
2264 * @param fallbackIndex the Integer to be used as a fallback index.
2265 * @return a negative integer, zero, or a positive integer as the
2266 * first object is mapped to a less, equal to, or greater
2267 * index than the second.
2268 */
2269 static <T> int compareIndices(Map<T, Integer> indexMap,
2270 T obj1, T obj2,
2271 Integer fallbackIndex) {
2272 Integer index1 = indexMap.getOrDefault(obj1, fallbackIndex);
2273 Integer index2 = indexMap.getOrDefault(obj2, fallbackIndex);
2274 return index1.compareTo(index2);
2275 }
2276 }
2277
2278 /**
2279 * An IndexedComparator which compares two String charsets. The comparison
2280 * follows the rules outlined in DataFlavor.selectBestTextFlavor. In order
2281 * to ensure that non-Unicode, non-ASCII, non-default charsets are sorted
2282 * in alphabetical order, charsets are not automatically converted to their
2283 * canonical forms.
2284 */
2285 public static class CharsetComparator extends IndexedComparator<String> {
2286 private static final Map<String, Integer> charsets;
2287
2288 private static final Integer DEFAULT_CHARSET_INDEX = 2;
2289 private static final Integer OTHER_CHARSET_INDEX = 1;
2290 private static final Integer WORST_CHARSET_INDEX = 0;
2291 private static final Integer UNSUPPORTED_CHARSET_INDEX = Integer.MIN_VALUE;
2292
2293 private static final String UNSUPPORTED_CHARSET = "UNSUPPORTED";
2294
2295 static {
2296 Map<String, Integer> charsetsMap = new HashMap<>(8, 1.0f);
2297
2298 // we prefer Unicode charsets
2299 charsetsMap.put(canonicalName("UTF-16LE"), 4);
2300 charsetsMap.put(canonicalName("UTF-16BE"), 5);
2301 charsetsMap.put(canonicalName("UTF-8"), 6);
2302 charsetsMap.put(canonicalName("UTF-16"), 7);
2303
2304 // US-ASCII is the worst charset supported
2305 charsetsMap.put(canonicalName("US-ASCII"), WORST_CHARSET_INDEX);
2306
2307 charsetsMap.putIfAbsent(Charset.defaultCharset().name(), DEFAULT_CHARSET_INDEX);
2308
2309 charsetsMap.put(UNSUPPORTED_CHARSET, UNSUPPORTED_CHARSET_INDEX);
2310
2311 charsets = Collections.unmodifiableMap(charsetsMap);
2312 }
2313
2314 public CharsetComparator(boolean order) {
2315 super(order);
2316 }
2317
2318 /**
2319 * Compares two String objects. Returns a negative integer, zero,
2320 * or a positive integer as the first charset is worse than, equal to,
2321 * or better than the second.
2322 *
2323 * @param obj1 the first charset to be compared
2324 * @param obj2 the second charset to be compared
2325 * @return a negative integer, zero, or a positive integer as the
2326 * first argument is worse, equal to, or better than the
2327 * second.
2328 * @throws ClassCastException if either of the arguments is not
2329 * instance of String
2330 * @throws NullPointerException if either of the arguments is
2331 * <code>null</code>.
2332 */
2333 public int compare(String obj1, String obj2) {
2334 if (order == SELECT_BEST) {
2335 return compareCharsets(obj1, obj2);
2336 } else {
2337 return compareCharsets(obj2, obj1);
2338 }
2339 }
2340
2341 /**
2342 * Compares charsets. Returns a negative integer, zero, or a positive
2343 * integer as the first charset is worse than, equal to, or better than
2344 * the second.
2345 * <p>
2346 * Charsets are ordered according to the following rules:
2347 * <ul>
2348 * <li>All unsupported charsets are equal.
2349 * <li>Any unsupported charset is worse than any supported charset.
2350 * <li>Unicode charsets, such as "UTF-16", "UTF-8", "UTF-16BE" and
2351 * "UTF-16LE", are considered best.
2352 * <li>After them, platform default charset is selected.
2353 * <li>"US-ASCII" is the worst of supported charsets.
2354 * <li>For all other supported charsets, the lexicographically less
2355 * one is considered the better.
2356 * </ul>
2357 *
2358 * @param charset1 the first charset to be compared
2359 * @param charset2 the second charset to be compared.
2360 * @return a negative integer, zero, or a positive integer as the
2361 * first argument is worse, equal to, or better than the
2362 * second.
2363 */
2364 int compareCharsets(String charset1, String charset2) {
2365 charset1 = getEncoding(charset1);
2366 charset2 = getEncoding(charset2);
2367
2368 int comp = compareIndices(charsets, charset1, charset2,
2369 OTHER_CHARSET_INDEX);
2370
2371 if (comp == 0) {
2372 return charset2.compareTo(charset1);
2373 }
2374
2375 return comp;
2376 }
2377
2378 /**
2379 * Returns encoding for the specified charset according to the
2380 * following rules:
2381 * <ul>
2382 * <li>If the charset is <code>null</code>, then <code>null</code> will
2383 * be returned.
2384 * <li>Iff the charset specifies an encoding unsupported by this JRE,
2385 * <code>UNSUPPORTED_CHARSET</code> will be returned.
2386 * <li>If the charset specifies an alias name, the corresponding
2387 * canonical name will be returned iff the charset is a known
2388 * Unicode, ASCII, or default charset.
2389 * </ul>
2390 *
2391 * @param charset the charset.
2392 * @return an encoding for this charset.
2393 */
2394 static String getEncoding(String charset) {
2395 if (charset == null) {
2396 return null;
2397 } else if (!DataTransferer.isEncodingSupported(charset)) {
2398 return UNSUPPORTED_CHARSET;
2399 } else {
2400 // Only convert to canonical form if the charset is one
2401 // of the charsets explicitly listed in the known charsets
2402 // map. This will happen only for Unicode, ASCII, or default
2403 // charsets.
2404 String canonicalName = DataTransferer.canonicalName(charset);
2405 return (charsets.containsKey(canonicalName))
2406 ? canonicalName
2407 : charset;
2408 }
2409 }
2410 }
2411
2412 /**
2413 * An IndexedComparator which compares two DataFlavors. For text flavors,
2414 * the comparison follows the rules outlined in
2415 * DataFlavor.selectBestTextFlavor. For non-text flavors, unknown
2416 * application MIME types are preferred, followed by known
2417 * application/x-java-* MIME types. Unknown application types are preferred
2418 * because if the user provides his own data flavor, it will likely be the
2419 * most descriptive one. For flavors which are otherwise equal, the
2420 * flavors' string representation are compared in the alphabetical order.
2421 */
2422 public static class DataFlavorComparator extends IndexedComparator<DataFlavor> {
2423
2424 private final CharsetComparator charsetComparator;
2425
2426 private static final Map<String, Integer> exactTypes;
2427 private static final Map<String, Integer> primaryTypes;
2428 private static final Map<Class<?>, Integer> nonTextRepresentations;
2429 private static final Map<String, Integer> textTypes;
2430 private static final Map<Class<?>, Integer> decodedTextRepresentations;
2431 private static final Map<Class<?>, Integer> encodedTextRepresentations;
2432
2433 private static final Integer UNKNOWN_OBJECT_LOSES = Integer.MIN_VALUE;
2434 private static final Integer UNKNOWN_OBJECT_WINS = Integer.MAX_VALUE;
2435
2436 static {
2437 {
2438 Map<String, Integer> exactTypesMap = new HashMap<>(4, 1.0f);
2439
2440 // application/x-java-* MIME types
2441 exactTypesMap.put("application/x-java-file-list", 0);
2442 exactTypesMap.put("application/x-java-serialized-object", 1);
2443 exactTypesMap.put("application/x-java-jvm-local-objectref", 2);
2444 exactTypesMap.put("application/x-java-remote-object", 3);
2445
2446 exactTypes = Collections.unmodifiableMap(exactTypesMap);
2447 }
2448
2449 {
2450 Map<String, Integer> primaryTypesMap = new HashMap<>(1, 1.0f);
2451
2452 primaryTypesMap.put("application", 0);
2453
2454 primaryTypes = Collections.unmodifiableMap(primaryTypesMap);
2455 }
2456
2457 {
2458 Map<Class<?>, Integer> nonTextRepresentationsMap = new HashMap<>(3, 1.0f);
2459
2460 nonTextRepresentationsMap.put(java.io.InputStream.class, 0);
2461 nonTextRepresentationsMap.put(java.io.Serializable.class, 1);
2462
2463 Class<?> remoteClass = RMI.remoteClass();
2464 if (remoteClass != null) {
2465 nonTextRepresentationsMap.put(remoteClass, 2);
2466 }
2467
2468 nonTextRepresentations = Collections.unmodifiableMap(nonTextRepresentationsMap);
2469 }
2470
2471 {
2472 Map<String, Integer> textTypesMap = new HashMap<>(16, 1.0f);
2473
2474 // plain text
2475 textTypesMap.put("text/plain", 0);
2476
2477 // stringFlavor
2478 textTypesMap.put("application/x-java-serialized-object", 1);
2479
2480 // misc
2481 textTypesMap.put("text/calendar", 2);
2482 textTypesMap.put("text/css", 3);
2483 textTypesMap.put("text/directory", 4);
2484 textTypesMap.put("text/parityfec", 5);
2485 textTypesMap.put("text/rfc822-headers", 6);
2486 textTypesMap.put("text/t140", 7);
2487 textTypesMap.put("text/tab-separated-values", 8);
2488 textTypesMap.put("text/uri-list", 9);
2489
2490 // enriched
2491 textTypesMap.put("text/richtext", 10);
2492 textTypesMap.put("text/enriched", 11);
2493 textTypesMap.put("text/rtf", 12);
2494
2495 // markup
2496 textTypesMap.put("text/html", 13);
2497 textTypesMap.put("text/xml", 14);
2498 textTypesMap.put("text/sgml", 15);
2499
2500 textTypes = Collections.unmodifiableMap(textTypesMap);
2501 }
2502
2503 {
2504 Map<Class<?>, Integer> decodedTextRepresentationsMap = new HashMap<>(4, 1.0f);
2505
2506 decodedTextRepresentationsMap.put(char[].class, 0);
2507 decodedTextRepresentationsMap.put(CharBuffer.class, 1);
2508 decodedTextRepresentationsMap.put(String.class, 2);
2509 decodedTextRepresentationsMap.put(Reader.class, 3);
2510
2511 decodedTextRepresentations =
2512 Collections.unmodifiableMap(decodedTextRepresentationsMap);
2513 }
2514
2515 {
2516 Map<Class<?>, Integer> encodedTextRepresentationsMap = new HashMap<>(3, 1.0f);
2517
2518 encodedTextRepresentationsMap.put(byte[].class, 0);
2519 encodedTextRepresentationsMap.put(ByteBuffer.class, 1);
2520 encodedTextRepresentationsMap.put(InputStream.class, 2);
2521
2522 encodedTextRepresentations =
2523 Collections.unmodifiableMap(encodedTextRepresentationsMap);
2524 }
2525 }
2526
2527 public DataFlavorComparator() {
2528 this(SELECT_BEST);
2529 }
2530
2531 public DataFlavorComparator(boolean order) {
2532 super(order);
2533
2534 charsetComparator = new CharsetComparator(order);
2535 }
2536
2537 public int compare(DataFlavor obj1, DataFlavor obj2) {
2538 DataFlavor flavor1 = order == SELECT_BEST ? obj1 : obj2;
2539 DataFlavor flavor2 = order == SELECT_BEST ? obj2 : obj1;
2540
2541 if (flavor1.equals(flavor2)) {
2542 return 0;
2543 }
2544
2545 int comp = 0;
2546
2547 String primaryType1 = flavor1.getPrimaryType();
2548 String subType1 = flavor1.getSubType();
2549 String mimeType1 = primaryType1 + "/" + subType1;
2550 Class<?> class1 = flavor1.getRepresentationClass();
2551
2552 String primaryType2 = flavor2.getPrimaryType();
2553 String subType2 = flavor2.getSubType();
2554 String mimeType2 = primaryType2 + "/" + subType2;
2555 Class<?> class2 = flavor2.getRepresentationClass();
2556
2557 if (flavor1.isFlavorTextType() && flavor2.isFlavorTextType()) {
2558 // First, compare MIME types
2559 comp = compareIndices(textTypes, mimeType1, mimeType2,
2560 UNKNOWN_OBJECT_LOSES);
2561 if (comp != 0) {
2562 return comp;
2563 }
2564
2565 // Only need to test one flavor because they both have the
2566 // same MIME type. Also don't need to worry about accidentally
2567 // passing stringFlavor because either
2568 // 1. Both flavors are stringFlavor, in which case the
2569 // equality test at the top of the function succeeded.
2570 // 2. Only one flavor is stringFlavor, in which case the MIME
2571 // type comparison returned a non-zero value.
2572 if (doesSubtypeSupportCharset(flavor1)) {
2573 // Next, prefer the decoded text representations of Reader,
2574 // String, CharBuffer, and [C, in that order.
2575 comp = compareIndices(decodedTextRepresentations, class1,
2576 class2, UNKNOWN_OBJECT_LOSES);
2577 if (comp != 0) {
2578 return comp;
2579 }
2580
2581 // Next, compare charsets
2582 comp = charsetComparator.compareCharsets
2583 (DataTransferer.getTextCharset(flavor1),
2584 DataTransferer.getTextCharset(flavor2));
2585 if (comp != 0) {
2586 return comp;
2587 }
2588 }
2589
2590 // Finally, prefer the encoded text representations of
2591 // InputStream, ByteBuffer, and [B, in that order.
2592 comp = compareIndices(encodedTextRepresentations, class1,
2593 class2, UNKNOWN_OBJECT_LOSES);
2594 if (comp != 0) {
2595 return comp;
2596 }
2597 } else {
2598 // First, prefer application types.
2599 comp = compareIndices(primaryTypes, primaryType1, primaryType2,
2600 UNKNOWN_OBJECT_LOSES);
2601 if (comp != 0) {
2602 return comp;
2603 }
2604
2605 // Next, look for application/x-java-* types. Prefer unknown
2606 // MIME types because if the user provides his own data flavor,
2607 // it will likely be the most descriptive one.
2608 comp = compareIndices(exactTypes, mimeType1, mimeType2,
2609 UNKNOWN_OBJECT_WINS);
2610 if (comp != 0) {
2611 return comp;
2612 }
2613
2614 // Finally, prefer the representation classes of Remote,
2615 // Serializable, and InputStream, in that order.
2616 comp = compareIndices(nonTextRepresentations, class1, class2,
2617 UNKNOWN_OBJECT_LOSES);
2618 if (comp != 0) {
2619 return comp;
2620 }
2621 }
2622
2623 // The flavours are not equal but still not distinguishable.
2624 // Compare String representations in alphabetical order
2625 return flavor1.getMimeType().compareTo(flavor2.getMimeType());
2626 }
2627 }
2628
2629 /*
2630 * Given the Map that maps objects to Integer indices and a boolean value,
2631 * this Comparator imposes a direct or reverse order on set of objects.
2632 * <p>
2633 * If the specified boolean value is SELECT_BEST, the Comparator imposes the
2634 * direct index-based order: an object A is greater than an object B if and
2635 * only if the index of A is greater than the index of B. An object that
2636 * doesn't have an associated index is less or equal than any other object.
2637 * <p>
2638 * If the specified boolean value is SELECT_WORST, the Comparator imposes the
2639 * reverse index-based order: an object A is greater than an object B if and
2640 * only if A is less than B with the direct index-based order.
2641 */
2642 public static class IndexOrderComparator extends IndexedComparator<Long> {
2643 private final Map<Long, Integer> indexMap;
2644 private static final Integer FALLBACK_INDEX = Integer.MIN_VALUE;
2645
2646 public IndexOrderComparator(Map<Long, Integer> indexMap, boolean order) {
2647 super(order);
2648 this.indexMap = indexMap;
2649 }
2650
2651 public int compare(Long obj1, Long obj2) {
2652 if (order == SELECT_WORST) {
2653 return -compareIndices(indexMap, obj1, obj2, FALLBACK_INDEX);
2654 } else {
2655 return compareIndices(indexMap, obj1, obj2, FALLBACK_INDEX);
2656 }
2657 }
2658 }
2659
2660 /**
2661 * A class that provides access to java.rmi.Remote and java.rmi.MarshalledObject
2662 * without creating a static dependency.
2663 */
2664 private static class RMI {
2665 private static final Class<?> remoteClass = getClass("java.rmi.Remote");
2666 private static final Class<?> marshallObjectClass =
2667 getClass("java.rmi.MarshalledObject");
2668 private static final Constructor<?> marshallCtor =
2669 getConstructor(marshallObjectClass, Object.class);
2670 private static final Method marshallGet =
2671 getMethod(marshallObjectClass, "get");
2672
2673 private static Class<?> getClass(String name) {
2674 try {
2675 return Class.forName(name, true, null);
2676 } catch (ClassNotFoundException e) {
2677 return null;
2678 }
2679 }
2680
2681 private static Constructor<?> getConstructor(Class<?> c, Class<?>... types) {
2682 try {
2683 return (c == null) ? null : c.getDeclaredConstructor(types);
2684 } catch (NoSuchMethodException x) {
2685 throw new AssertionError(x);
2686 }
2687 }
2688
2689 private static Method getMethod(Class<?> c, String name, Class<?>... types) {
2690 try {
2691 return (c == null) ? null : c.getMethod(name, types);
2692 } catch (NoSuchMethodException e) {
2693 throw new AssertionError(e);
2694 }
2695 }
2696
2697 /**
2698 * Returns {@code true} if the given class is java.rmi.Remote.
2699 */
2700 static boolean isRemote(Class<?> c) {
2701 return (remoteClass == null) ? false : remoteClass.isAssignableFrom(c);
2702 }
2703
2704 /**
2705 * Returns java.rmi.Remote.class if RMI is present; otherwise {@code null}.
2706 */
2707 static Class<?> remoteClass() {
2708 return remoteClass;
2709 }
2710
2711 /**
2712 * Returns a new MarshalledObject containing the serialized representation
2713 * of the given object.
2714 */
2715 static Object newMarshalledObject(Object obj) throws IOException {
2716 try {
2717 return marshallCtor.newInstance(obj);
2718 } catch (InstantiationException | IllegalAccessException x) {
2719 throw new AssertionError(x);
2720 } catch (InvocationTargetException x) {
2721 Throwable cause = x.getCause();
2722 if (cause instanceof IOException)
2723 throw (IOException)cause;
2724 throw new AssertionError(x);
2725 }
2726 }
2727
2728 /**
2729 * Returns a new copy of the contained marshalled object.
2730 */
2731 static Object getMarshalledObject(Object obj)
2732 throws IOException, ClassNotFoundException
2733 {
2734 try {
2735 return marshallGet.invoke(obj);
2736 } catch (IllegalAccessException x) {
2737 throw new AssertionError(x);
2738 } catch (InvocationTargetException x) {
2739 Throwable cause = x.getCause();
2740 if (cause instanceof IOException)
2741 throw (IOException)cause;
2742 if (cause instanceof ClassNotFoundException)
2743 throw (ClassNotFoundException)cause;
2744 throw new AssertionError(x);
2745 }
2746 }
2747 }
2748 }
|
44 import java.io.InputStreamReader;
45 import java.io.IOException;
46 import java.io.ObjectInputStream;
47 import java.io.ObjectOutputStream;
48 import java.io.Reader;
49 import java.io.SequenceInputStream;
50 import java.io.StringReader;
51
52 import java.net.URI;
53 import java.net.URISyntaxException;
54
55 import java.nio.ByteBuffer;
56 import java.nio.CharBuffer;
57 import java.nio.charset.Charset;
58 import java.nio.charset.CharsetEncoder;
59 import java.nio.charset.IllegalCharsetNameException;
60 import java.nio.charset.StandardCharsets;
61 import java.nio.charset.UnsupportedCharsetException;
62
63 import java.lang.reflect.Constructor;
64 import java.lang.reflect.Modifier;
65
66 import java.security.AccessController;
67 import java.security.PrivilegedAction;
68 import java.security.PrivilegedActionException;
69 import java.security.PrivilegedExceptionAction;
70 import java.security.ProtectionDomain;
71
72 import java.util.*;
73
74 import sun.datatransfer.DataFlavorUtil;
75
76 import sun.awt.AppContext;
77 import sun.awt.SunToolkit;
78
79 import java.awt.image.BufferedImage;
80 import java.awt.image.ImageObserver;
81 import java.awt.image.RenderedImage;
82 import java.awt.image.WritableRaster;
83 import java.awt.image.ColorModel;
84
85 import javax.imageio.ImageIO;
86 import javax.imageio.ImageReader;
87 import javax.imageio.ImageReadParam;
88 import javax.imageio.ImageWriter;
89 import javax.imageio.ImageTypeSpecifier;
90
91 import javax.imageio.spi.ImageWriterSpi;
92
93 import javax.imageio.stream.ImageInputStream;
94 import javax.imageio.stream.ImageOutputStream;
116 * a byte array or InputStream into an Object, given a source format and
117 * a target DataFlavor.
118 *
119 * @author David Mendenhall
120 * @author Danila Sinopalnikov
121 *
122 * @since 1.3.1
123 */
124 public abstract class DataTransferer {
125 /**
126 * The <code>DataFlavor</code> representing a Java text encoding String
127 * encoded in UTF-8, where
128 * <pre>
129 * representationClass = [B
130 * mimeType = "application/x-java-text-encoding"
131 * </pre>
132 */
133 public static final DataFlavor javaTextEncodingFlavor;
134
135 /**
136 * A collection of all natives listed in flavormap.properties with
137 * a primary MIME type of "text".
138 */
139 private static final Set<Long> textNatives =
140 Collections.synchronizedSet(new HashSet<>());
141
142 /**
143 * The native encodings/charsets for the Set of textNatives.
144 */
145 private static final Map<Long, String> nativeCharsets =
146 Collections.synchronizedMap(new HashMap<>());
147
148 /**
149 * The end-of-line markers for the Set of textNatives.
150 */
151 private static final Map<Long, String> nativeEOLNs =
152 Collections.synchronizedMap(new HashMap<>());
153
154 /**
155 * The number of terminating NUL bytes for the Set of textNatives.
156 */
157 private static final Map<Long, Integer> nativeTerminators =
158 Collections.synchronizedMap(new HashMap<>());
159
160 /**
161 * The key used to store pending data conversion requests for an AppContext.
162 */
163 private static final String DATA_CONVERTER_KEY = "DATA_CONVERTER_KEY";
164
165 static {
166 DataFlavor tJavaTextEncodingFlavor = null;
167 try {
168 tJavaTextEncodingFlavor = new DataFlavor("application/x-java-text-encoding;class=\"[B\"");
169 } catch (ClassNotFoundException cannotHappen) {
170 }
171 javaTextEncodingFlavor = tJavaTextEncodingFlavor;
172 }
173
174 /**
175 * The accessor method for the singleton DataTransferer instance. Note
176 * that in a headless environment, there may be no DataTransferer instance;
177 * instead, null will be returned.
178 */
179 public static synchronized DataTransferer getInstance() {
180 return ((SunToolkit) Toolkit.getDefaultToolkit()).getDataTransferer();
181 }
182
183 /**
184 * Converts a FlavorMap to a FlavorTable.
185 */
186 public static FlavorTable adaptFlavorMap(final FlavorMap map) {
187 if (map instanceof FlavorTable) {
188 return (FlavorTable)map;
189 }
190
191 return new FlavorTable() {
192 @Override
193 public Map<DataFlavor, String> getNativesForFlavors(DataFlavor[] flavors) {
194 return map.getNativesForFlavors(flavors);
195 }
196 @Override
197 public Map<String, DataFlavor> getFlavorsForNatives(String[] natives) {
198 return map.getFlavorsForNatives(natives);
199 }
200 @Override
201 public List<String> getNativesForFlavor(DataFlavor flav) {
202 Map<DataFlavor, String> natives = getNativesForFlavors(new DataFlavor[]{flav});
203 String nat = natives.get(flav);
367 // SystemFlavorMap.getNativesForFlavor will return
368 // text/plain natives for all text/*. While this is good
369 // for a single text/* flavor, we would prefer that
370 // text/plain native data come from a text/plain flavor.
371 if (("text".equals(flavor.getPrimaryType()) &&
372 "plain".equals(flavor.getSubType())) ||
373 flavor.equals(DataFlavor.stringFlavor)) {
374 textPlainMap.put(lFormat, flavor);
375 textPlainIndexMap.put(lFormat, index);
376 }
377 }
378
379 currentIndex += natives.size();
380 }
381 }
382
383 formatMap.putAll(textPlainMap);
384 indexMap.putAll(textPlainIndexMap);
385
386 // Sort the map keys according to the formats preference order.
387 Comparator<Long> comparator = DataFlavorUtil.getIndexOrderComparator(indexMap).reversed();
388 SortedMap<Long, DataFlavor> sortedMap = new TreeMap<>(comparator);
389 sortedMap.putAll(formatMap);
390
391 return sortedMap;
392 }
393
394 /**
395 * Reduces the Map output for the root function to an array of the
396 * Map's keys.
397 */
398 public long[] getFormatsForTransferableAsArray(Transferable contents,
399 FlavorTable map) {
400 return keysToLongArray(getFormatsForTransferable(contents, map));
401 }
402
403 /**
404 * Returns a Map whose keys are all of the possible DataFlavors into which
405 * data in the specified formats can be translated. The value of each key
406 * is the format in which the Clipboard or dropped data should be requested
407 * when converting to the DataFlavor.
738 }
739 if (flavor.equals(DataFlavor.plainTextFlavor) &&
740 !(obj instanceof InputStream))
741 {
742 obj = contents.getTransferData(DataFlavor.stringFlavor);
743 if (obj == null) {
744 return null;
745 }
746 stringSelectionHack = true;
747 } else {
748 stringSelectionHack = false;
749 }
750 } catch (UnsupportedFlavorException e) {
751 throw new IOException(e.getMessage());
752 }
753
754 // Source data is a String. Search-and-replace EOLN. Encode into the
755 // target format. Append terminating NUL bytes.
756 if (stringSelectionHack ||
757 (String.class.equals(flavor.getRepresentationClass()) &&
758 DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
759
760 String str = removeSuspectedData(flavor, contents, (String)obj);
761
762 return translateTransferableString(
763 str,
764 format);
765
766 // Source data is a Reader. Convert to a String and recur. In the
767 // future, we may want to rewrite this so that we encode on demand.
768 } else if (flavor.isRepresentationClassReader()) {
769 if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
770 throw new IOException
771 ("cannot transfer non-text data as Reader");
772 }
773
774 StringBuilder buf = new StringBuilder();
775 try (Reader r = (Reader)obj) {
776 int c;
777 while ((c = r.read()) != -1) {
778 buf.append((char)c);
779 }
780 }
781
782 return translateTransferableString(
783 buf.toString(),
784 format);
785
786 // Source data is a CharBuffer. Convert to a String and recur.
787 } else if (flavor.isRepresentationClassCharBuffer()) {
788 if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
789 throw new IOException
790 ("cannot transfer non-text data as CharBuffer");
791 }
792
793 CharBuffer buffer = (CharBuffer)obj;
794 int size = buffer.remaining();
795 char[] chars = new char[size];
796 buffer.get(chars, 0, size);
797
798 return translateTransferableString(
799 new String(chars),
800 format);
801
802 // Source data is a char array. Convert to a String and recur.
803 } else if (char[].class.equals(flavor.getRepresentationClass())) {
804 if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
805 throw new IOException
806 ("cannot transfer non-text data as char array");
807 }
808
809 return translateTransferableString(
810 new String((char[])obj),
811 format);
812
813 // Source data is a ByteBuffer. For arbitrary flavors, simply return
814 // the array. For text flavors, decode back to a String and recur to
815 // reencode according to the requested format.
816 } else if (flavor.isRepresentationClassByteBuffer()) {
817 ByteBuffer buffer = (ByteBuffer)obj;
818 int size = buffer.remaining();
819 byte[] bytes = new byte[size];
820 buffer.get(bytes, 0, size);
821
822 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
823 String sourceEncoding = DataFlavorUtil.getTextCharset(flavor);
824 return translateTransferableString(
825 new String(bytes, sourceEncoding),
826 format);
827 } else {
828 return bytes;
829 }
830
831 // Source data is a byte array. For arbitrary flavors, simply return
832 // the array. For text flavors, decode back to a String and recur to
833 // reencode according to the requested format.
834 } else if (byte[].class.equals(flavor.getRepresentationClass())) {
835 byte[] bytes = (byte[])obj;
836
837 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
838 String sourceEncoding = DataFlavorUtil.getTextCharset(flavor);
839 return translateTransferableString(
840 new String(bytes, sourceEncoding),
841 format);
842 } else {
843 return bytes;
844 }
845 // Source data is Image
846 } else if (DataFlavor.imageFlavor.equals(flavor)) {
847 if (!isImageFormat(format)) {
848 throw new IOException("Data translation failed: " +
849 "not an image format");
850 }
851
852 Image image = (Image)obj;
853 byte[] bytes = imageToPlatformBytes(image, format);
854
855 if (bytes == null) {
856 throw new IOException("Data translation failed: " +
857 "cannot convert java image to native format");
858 }
921 theByteArray = bos.toByteArray();
922 }
923
924 // Source data is an InputStream. For arbitrary flavors, just grab the
925 // bytes and dump them into a byte array. For text flavors, decode back
926 // to a String and recur to reencode according to the requested format.
927 } else if (flavor.isRepresentationClassInputStream()) {
928 try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
929 try (InputStream is = (InputStream)obj) {
930 boolean eof = false;
931 int avail = is.available();
932 byte[] tmp = new byte[avail > 8192 ? avail : 8192];
933 do {
934 int aValue;
935 if (!(eof = (aValue = is.read(tmp, 0, tmp.length)) == -1)) {
936 bos.write(tmp, 0, aValue);
937 }
938 } while (!eof);
939 }
940
941 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
942 byte[] bytes = bos.toByteArray();
943 String sourceEncoding = DataFlavorUtil.getTextCharset(flavor);
944 return translateTransferableString(
945 new String(bytes, sourceEncoding),
946 format);
947 }
948 theByteArray = bos.toByteArray();
949 }
950
951
952 // Source data is an RMI object
953 } else if (flavor.isRepresentationClassRemote()) {
954 theByteArray = convertObjectToBytes(DataFlavorUtil.RMI.newMarshalledObject(obj));
955
956 // Source data is Serializable
957 } else if (flavor.isRepresentationClassSerializable()) {
958
959 theByteArray = convertObjectToBytes(obj);
960
961 } else {
962 throw new IOException("data translation failed");
963 }
964
965
966
967 return theByteArray;
968 }
969
970 private static byte[] convertObjectToBytes(Object object) throws IOException {
971 try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
972 ObjectOutputStream oos = new ObjectOutputStream(bos))
973 {
974 oos.writeObject(object);
1150 URI uris[] = dragQueryURIs(str, format, localeTransferable);
1151 if (uris == null) {
1152 return null;
1153 }
1154 List<File> files = new ArrayList<>();
1155 for (URI uri : uris) {
1156 try {
1157 files.add(new File(uri));
1158 } catch (IllegalArgumentException illegalArg) {
1159 // When converting from URIs to less generic files,
1160 // common practice (Wine, SWT) seems to be to
1161 // silently drop the URIs that aren't local files.
1162 }
1163 }
1164 theObject = files;
1165 }
1166
1167 // Target data is a String. Strip terminating NUL bytes. Decode bytes
1168 // into characters. Search-and-replace EOLN.
1169 } else if (String.class.equals(flavor.getRepresentationClass()) &&
1170 DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1171
1172 theObject = translateBytesToString(bytes, format, localeTransferable);
1173
1174 // Target data is a Reader. Obtain data in InputStream format, encoded
1175 // as "Unicode" (utf-16be). Then use an InputStreamReader to decode
1176 // back to chars on demand.
1177 } else if (flavor.isRepresentationClassReader()) {
1178 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
1179 theObject = translateStream(bais,
1180 flavor, format, localeTransferable);
1181 }
1182 // Target data is a CharBuffer. Recur to obtain String and wrap.
1183 } else if (flavor.isRepresentationClassCharBuffer()) {
1184 if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
1185 throw new IOException("cannot transfer non-text data as CharBuffer");
1186 }
1187
1188 CharBuffer buffer = CharBuffer.wrap(
1189 translateBytesToString(bytes,format, localeTransferable));
1190
1191 theObject = constructFlavoredObject(buffer, flavor, CharBuffer.class);
1192
1193 // Target data is a char array. Recur to obtain String and convert to
1194 // char array.
1195 } else if (char[].class.equals(flavor.getRepresentationClass())) {
1196 if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
1197 throw new IOException
1198 ("cannot transfer non-text data as char array");
1199 }
1200
1201 theObject = translateBytesToString(
1202 bytes, format, localeTransferable).toCharArray();
1203
1204 // Target data is a ByteBuffer. For arbitrary flavors, just return
1205 // the raw bytes. For text flavors, convert to a String to strip
1206 // terminators and search-and-replace EOLN, then reencode according to
1207 // the requested flavor.
1208 } else if (flavor.isRepresentationClassByteBuffer()) {
1209 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1210 bytes = translateBytesToString(
1211 bytes, format, localeTransferable).getBytes(
1212 DataFlavorUtil.getTextCharset(flavor)
1213 );
1214 }
1215
1216 ByteBuffer buffer = ByteBuffer.wrap(bytes);
1217 theObject = constructFlavoredObject(buffer, flavor, ByteBuffer.class);
1218
1219 // Target data is a byte array. For arbitrary flavors, just return
1220 // the raw bytes. For text flavors, convert to a String to strip
1221 // terminators and search-and-replace EOLN, then reencode according to
1222 // the requested flavor.
1223 } else if (byte[].class.equals(flavor.getRepresentationClass())) {
1224 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1225 theObject = translateBytesToString(
1226 bytes, format, localeTransferable
1227 ).getBytes(DataFlavorUtil.getTextCharset(flavor));
1228 } else {
1229 theObject = bytes;
1230 }
1231
1232 // Target data is an InputStream. For arbitrary flavors, just return
1233 // the raw bytes. For text flavors, decode to strip terminators and
1234 // search-and-replace EOLN, then reencode according to the requested
1235 // flavor.
1236 } else if (flavor.isRepresentationClassInputStream()) {
1237
1238 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
1239 theObject = translateStream(bais, flavor, format, localeTransferable);
1240 }
1241
1242 } else if (flavor.isRepresentationClassRemote()) {
1243 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
1244 ObjectInputStream ois = new ObjectInputStream(bais)) {
1245
1246 DataFlavorUtil.RMI.getMarshalledObject(ois.readObject());
1247 } catch (Exception e) {
1248 throw new IOException(e.getMessage());
1249 }
1250
1251 // Target data is Serializable
1252 } else if (flavor.isRepresentationClassSerializable()) {
1253
1254 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
1255 theObject = translateStream(bais, flavor, format, localeTransferable);
1256 }
1257
1258 // Target data is Image
1259 } else if (DataFlavor.imageFlavor.equals(flavor)) {
1260 if (!isImageFormat(format)) {
1261 throw new IOException("data translation failed");
1262 }
1263
1264 theObject = platformImageBytesToImage(bytes, format);
1265 }
1266
1291
1292 URI uris[] = dragQueryURIs(str, format, localeTransferable);
1293 if (uris == null) {
1294 return null;
1295 }
1296 List<File> files = new ArrayList<>();
1297 for (URI uri : uris) {
1298 try {
1299 files.add(new File(uri));
1300 } catch (IllegalArgumentException illegalArg) {
1301 // When converting from URIs to less generic files,
1302 // common practice (Wine, SWT) seems to be to
1303 // silently drop the URIs that aren't local files.
1304 }
1305 }
1306 theObject = files;
1307
1308 // Target data is a String. Strip terminating NUL bytes. Decode bytes
1309 // into characters. Search-and-replace EOLN.
1310 } else if (String.class.equals(flavor.getRepresentationClass()) &&
1311 DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1312
1313 return translateBytesToString(inputStreamToByteArray(str),
1314 format, localeTransferable);
1315
1316 // Special hack to maintain backwards-compatibility with the brokenness
1317 // of StringSelection. Return a StringReader instead of an InputStream.
1318 // Recur to obtain String and encapsulate.
1319 } else if (DataFlavor.plainTextFlavor.equals(flavor)) {
1320 theObject = new StringReader(translateBytesToString(
1321 inputStreamToByteArray(str),
1322 format, localeTransferable));
1323
1324 // Target data is an InputStream. For arbitrary flavors, just return
1325 // the raw bytes. For text flavors, decode to strip terminators and
1326 // search-and-replace EOLN, then reencode according to the requested
1327 // flavor.
1328 } else if (flavor.isRepresentationClassInputStream()) {
1329 theObject = translateStreamToInputStream(str, flavor, format,
1330 localeTransferable);
1331
1332 // Target data is a Reader. Obtain data in InputStream format, encoded
1333 // as "Unicode" (utf-16be). Then use an InputStreamReader to decode
1334 // back to chars on demand.
1335 } else if (flavor.isRepresentationClassReader()) {
1336 if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
1337 throw new IOException
1338 ("cannot transfer non-text data as Reader");
1339 }
1340
1341 InputStream is = (InputStream)translateStreamToInputStream(
1342 str, DataFlavor.plainTextFlavor,
1343 format, localeTransferable);
1344
1345 String unicode = DataFlavorUtil.getTextCharset(DataFlavor.plainTextFlavor);
1346
1347 Reader reader = new InputStreamReader(is, unicode);
1348
1349 theObject = constructFlavoredObject(reader, flavor, Reader.class);
1350 // Target data is a byte array
1351 } else if (byte[].class.equals(flavor.getRepresentationClass())) {
1352 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1353 theObject = translateBytesToString(inputStreamToByteArray(str), format, localeTransferable)
1354 .getBytes(DataFlavorUtil.getTextCharset(flavor));
1355 } else {
1356 theObject = inputStreamToByteArray(str);
1357 }
1358 // Target data is an RMI object
1359 } else if (flavor.isRepresentationClassRemote()) {
1360 try (ObjectInputStream ois = new ObjectInputStream(str)) {
1361 theObject = DataFlavorUtil.RMI.getMarshalledObject(ois.readObject());
1362 } catch (Exception e) {
1363 throw new IOException(e.getMessage());
1364 }
1365
1366 // Target data is Serializable
1367 } else if (flavor.isRepresentationClassSerializable()) {
1368 try (ObjectInputStream ois =
1369 new ObjectInputStream(str))
1370 {
1371 theObject = ois.readObject();
1372 } catch (Exception e) {
1373 throw new IOException(e.getMessage());
1374 }
1375 // Target data is Image
1376 } else if (DataFlavor.imageFlavor.equals(flavor)) {
1377 if (!isImageFormat(format)) {
1378 throw new IOException("data translation failed");
1379 }
1380 theObject = platformImageBytesToImage(inputStreamToByteArray(str), format);
1381 }
1382
1383 if (theObject == null) {
1384 throw new IOException("data translation failed");
1385 }
1386
1387 return theObject;
1388
1389 }
1390
1391 /**
1392 * For arbitrary flavors, just use the raw InputStream. For text flavors,
1393 * ReencodingInputStream will decode and reencode the InputStream on demand
1394 * so that we can strip terminators and search-and-replace EOLN.
1395 */
1396 private Object translateStreamToInputStream
1397 (InputStream str, DataFlavor flavor, long format,
1398 Transferable localeTransferable) throws IOException
1399 {
1400 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1401 str = new ReencodingInputStream
1402 (str, format, DataFlavorUtil.getTextCharset(flavor),
1403 localeTransferable);
1404 }
1405
1406 return constructFlavoredObject(str, flavor, InputStream.class);
1407 }
1408
1409 /**
1410 * We support representations which are exactly of the specified Class,
1411 * and also arbitrary Objects which have a constructor which takes an
1412 * instance of the Class as its sole parameter.
1413 */
1414 private Object constructFlavoredObject(Object arg, DataFlavor flavor,
1415 Class<?> clazz)
1416 throws IOException
1417 {
1418 final Class<?> dfrc = flavor.getRepresentationClass();
1419
1420 if (clazz.equals(dfrc)) {
1421 return arg; // simple case
1422 } else {
1624
1625 /**
1626 * Translates either a byte array or an input stream which contain
1627 * platform-specific image data in the given format into an Image.
1628 */
1629
1630
1631 protected abstract Image platformImageBytesToImage(
1632 byte[] bytes,long format) throws IOException;
1633
1634 /**
1635 * Translates either a byte array or an input stream which contain
1636 * an image data in the given standard format into an Image.
1637 *
1638 * @param mimeType image MIME type, such as: image/png, image/jpeg, image/gif
1639 */
1640 protected Image standardImageBytesToImage(
1641 byte[] bytes, String mimeType) throws IOException
1642 {
1643
1644 Iterator<ImageReader> readerIterator = ImageIO.getImageReadersByMIMEType(mimeType);
1645
1646 if (!readerIterator.hasNext()) {
1647 throw new IOException("No registered service provider can decode " +
1648 " an image from " + mimeType);
1649 }
1650
1651 IOException ioe = null;
1652
1653 while (readerIterator.hasNext()) {
1654 ImageReader imageReader = readerIterator.next();
1655 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
1656 try (ImageInputStream imageInputStream = ImageIO.createImageInputStream(bais)) {
1657 ImageReadParam param = imageReader.getDefaultReadParam();
1658 imageReader.setInput(imageInputStream, true, true);
1659 BufferedImage bufferedImage = imageReader.read(imageReader.getMinIndex(), param);
1660 if (bufferedImage != null) {
1661 return bufferedImage;
1662 }
1663 } finally {
1664 imageReader.dispose();
1677 throw ioe;
1678 }
1679
1680 /**
1681 * Translates a Java Image into a byte array which contains platform-
1682 * specific image data in the given format.
1683 */
1684 protected abstract byte[] imageToPlatformBytes(Image image, long format)
1685 throws IOException;
1686
1687 /**
1688 * Translates a Java Image into a byte array which contains
1689 * an image data in the given standard format.
1690 *
1691 * @param mimeType image MIME type, such as: image/png, image/jpeg
1692 */
1693 protected byte[] imageToStandardBytes(Image image, String mimeType)
1694 throws IOException {
1695 IOException originalIOE = null;
1696
1697 Iterator<ImageWriter> writerIterator = ImageIO.getImageWritersByMIMEType(mimeType);
1698
1699 if (!writerIterator.hasNext()) {
1700 throw new IOException("No registered service provider can encode " +
1701 " an image to " + mimeType);
1702 }
1703
1704 if (image instanceof RenderedImage) {
1705 // Try to encode the original image.
1706 try {
1707 return imageToStandardBytesImpl((RenderedImage)image, mimeType);
1708 } catch (IOException ioe) {
1709 originalIOE = ioe;
1710 }
1711 }
1712
1713 // Retry with a BufferedImage.
1714 int width = 0;
1715 int height = 0;
1716 if (image instanceof ToolkitImage) {
1717 ImageRepresentation ir = ((ToolkitImage)image).getImageRep();
1736 g.drawImage(image, 0, 0, width, height, null);
1737 } finally {
1738 g.dispose();
1739 }
1740
1741 try {
1742 return imageToStandardBytesImpl(bufferedImage, mimeType);
1743 } catch (IOException ioe) {
1744 if (originalIOE != null) {
1745 throw originalIOE;
1746 } else {
1747 throw ioe;
1748 }
1749 }
1750 }
1751
1752 byte[] imageToStandardBytesImpl(RenderedImage renderedImage,
1753 String mimeType)
1754 throws IOException {
1755
1756 Iterator<ImageWriter> writerIterator = ImageIO.getImageWritersByMIMEType(mimeType);
1757
1758 ImageTypeSpecifier typeSpecifier =
1759 new ImageTypeSpecifier(renderedImage);
1760
1761 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1762 IOException ioe = null;
1763
1764 while (writerIterator.hasNext()) {
1765 ImageWriter imageWriter = writerIterator.next();
1766 ImageWriterSpi writerSpi = imageWriter.getOriginatingProvider();
1767
1768 if (!writerSpi.canEncodeImage(typeSpecifier)) {
1769 continue;
1770 }
1771
1772 try {
1773 try (ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(baos)) {
1774 imageWriter.setOutput(imageOutputStream);
1775 imageWriter.write(renderedImage);
1776 imageOutputStream.flush();
1919 return ret;
1920 }
1921
1922 public void processDataConversionRequests() {
1923 if (EventQueue.isDispatchThread()) {
1924 AppContext appContext = AppContext.getAppContext();
1925 getToolkitThreadBlockedHandler().lock();
1926 try {
1927 Runnable dataConverter =
1928 (Runnable)appContext.get(DATA_CONVERTER_KEY);
1929 if (dataConverter != null) {
1930 dataConverter.run();
1931 appContext.remove(DATA_CONVERTER_KEY);
1932 }
1933 } finally {
1934 getToolkitThreadBlockedHandler().unlock();
1935 }
1936 }
1937 }
1938
1939 public abstract ToolkitThreadBlockedHandler getToolkitThreadBlockedHandler();
1940
1941 /**
1942 * Helper function to reduce a Map with Long keys to a long array.
1943 * <p>
1944 * The map keys are sorted according to the native formats preference
1945 * order.
1946 */
1947 public static long[] keysToLongArray(SortedMap<Long, ?> map) {
1948 Set<Long> keySet = map.keySet();
1949 long[] retval = new long[keySet.size()];
1950 int i = 0;
1951 for (Iterator<Long> iter = keySet.iterator(); iter.hasNext(); i++) {
1952 retval[i] = iter.next();
1953 }
1954 return retval;
1955 }
1956
1957 /**
1958 * Helper function to convert a Set of DataFlavors to a sorted array.
1959 * The array will be sorted according to <code>DataFlavorComparator</code>.
1960 */
1961 public static DataFlavor[] setToSortedDataFlavorArray(Set<DataFlavor> flavorsSet) {
1962 DataFlavor[] flavors = new DataFlavor[flavorsSet.size()];
1963 flavorsSet.toArray(flavors);
1964 final Comparator<DataFlavor> comparator = DataFlavorUtil.getDataFlavorComparator().reversed();
1965 Arrays.sort(flavors, comparator);
1966 return flavors;
1967 }
1968
1969 /**
1970 * Helper function to convert an InputStream to a byte[] array.
1971 */
1972 protected static byte[] inputStreamToByteArray(InputStream str)
1973 throws IOException
1974 {
1975 try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
1976 int len = 0;
1977 byte[] buf = new byte[8192];
1978
1979 while ((len = str.read(buf)) != -1) {
1980 baos.write(buf, 0, len);
1981 }
1982
1983 return baos.toByteArray();
1984 }
1985 }
1986
1987 /**
1988 * Returns platform-specific mappings for the specified native.
1989 * If there are no platform-specific mappings for this native, the method
1990 * returns an empty <code>List</code>.
1991 */
1992 public LinkedHashSet<DataFlavor> getPlatformMappingsForNative(String nat) {
1993 return new LinkedHashSet<>();
1994 }
1995
1996 /**
1997 * Returns platform-specific mappings for the specified flavor.
1998 * If there are no platform-specific mappings for this flavor, the method
1999 * returns an empty <code>List</code>.
2000 */
2001 public LinkedHashSet<String> getPlatformMappingsForFlavor(DataFlavor df) {
2002 return new LinkedHashSet<>();
2003 }
2004 }
|