1 /* 2 * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.awt.datatransfer; 27 28 import java.awt.EventQueue; 29 import java.awt.Graphics; 30 import java.awt.Image; 31 import java.awt.Toolkit; 32 33 import java.awt.datatransfer.DataFlavor; 34 import java.awt.datatransfer.FlavorMap; 35 import java.awt.datatransfer.FlavorTable; 36 import java.awt.datatransfer.Transferable; 37 import java.awt.datatransfer.UnsupportedFlavorException; 38 39 import java.io.BufferedReader; 40 import java.io.ByteArrayInputStream; 41 import java.io.ByteArrayOutputStream; 42 import java.io.File; 43 import java.io.InputStream; 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.ComponentFactory; 78 import sun.awt.SunToolkit; 79 80 import java.awt.image.BufferedImage; 81 import java.awt.image.ImageObserver; 82 import java.awt.image.RenderedImage; 83 import java.awt.image.WritableRaster; 84 import java.awt.image.ColorModel; 85 86 import javax.imageio.ImageIO; 87 import javax.imageio.ImageReader; 88 import javax.imageio.ImageReadParam; 89 import javax.imageio.ImageWriter; 90 import javax.imageio.ImageTypeSpecifier; 91 92 import javax.imageio.spi.ImageWriterSpi; 93 94 import javax.imageio.stream.ImageInputStream; 95 import javax.imageio.stream.ImageOutputStream; 96 97 import sun.awt.image.ImageRepresentation; 98 import sun.awt.image.ToolkitImage; 99 100 import java.io.FilePermission; 101 import java.util.stream.Stream; 102 103 104 /** 105 * Provides a set of functions to be shared among the DataFlavor class and 106 * platform-specific data transfer implementations. 107 * 108 * The concept of "flavors" and "natives" is extended to include "formats", 109 * which are the numeric values Win32 and X11 use to express particular data 110 * types. Like FlavorMap, which provides getNativesForFlavors(DataFlavor[]) and 111 * getFlavorsForNatives(String[]) functions, DataTransferer provides a set 112 * of getFormatsFor(Transferable|Flavor|Flavors) and 113 * getFlavorsFor(Format|Formats) functions. 114 * 115 * Also provided are functions for translating a Transferable into a byte 116 * array, given a source DataFlavor and a target format, and for translating 117 * a byte array or InputStream into an Object, given a source format and 118 * a target DataFlavor. 119 * 120 * @author David Mendenhall 121 * @author Danila Sinopalnikov 122 * 123 * @since 1.3.1 124 */ 125 public abstract class DataTransferer { 126 /** 127 * The <code>DataFlavor</code> representing a Java text encoding String 128 * encoded in UTF-8, where 129 * <pre> 130 * representationClass = [B 131 * mimeType = "application/x-java-text-encoding" 132 * </pre> 133 */ 134 public static final DataFlavor javaTextEncodingFlavor; 135 136 /** 137 * A collection of all natives listed in flavormap.properties with 138 * a primary MIME type of "text". 139 */ 140 private static final Set<Long> textNatives = 141 Collections.synchronizedSet(new HashSet<>()); 142 143 /** 144 * The native encodings/charsets for the Set of textNatives. 145 */ 146 private static final Map<Long, String> nativeCharsets = 147 Collections.synchronizedMap(new HashMap<>()); 148 149 /** 150 * The end-of-line markers for the Set of textNatives. 151 */ 152 private static final Map<Long, String> nativeEOLNs = 153 Collections.synchronizedMap(new HashMap<>()); 154 155 /** 156 * The number of terminating NUL bytes for the Set of textNatives. 157 */ 158 private static final Map<Long, Integer> nativeTerminators = 159 Collections.synchronizedMap(new HashMap<>()); 160 161 /** 162 * The key used to store pending data conversion requests for an AppContext. 163 */ 164 private static final String DATA_CONVERTER_KEY = "DATA_CONVERTER_KEY"; 165 166 static { 167 DataFlavor tJavaTextEncodingFlavor = null; 168 try { 169 tJavaTextEncodingFlavor = new DataFlavor("application/x-java-text-encoding;class=\"[B\""); 170 } catch (ClassNotFoundException cannotHappen) { 171 } 172 javaTextEncodingFlavor = tJavaTextEncodingFlavor; 173 } 174 175 /** 176 * The accessor method for the singleton DataTransferer instance. Note 177 * that in a headless environment, there may be no DataTransferer instance; 178 * instead, null will be returned. 179 */ 180 public static synchronized DataTransferer getInstance() { 181 return ((ComponentFactory) Toolkit.getDefaultToolkit()).getDataTransferer(); 182 } 183 184 /** 185 * Converts a FlavorMap to a FlavorTable. 186 */ 187 public static FlavorTable adaptFlavorMap(final FlavorMap map) { 188 if (map instanceof FlavorTable) { 189 return (FlavorTable)map; 190 } 191 192 return new FlavorTable() { 193 @Override 194 public Map<DataFlavor, String> getNativesForFlavors(DataFlavor[] flavors) { 195 return map.getNativesForFlavors(flavors); 196 } 197 @Override 198 public Map<String, DataFlavor> getFlavorsForNatives(String[] natives) { 199 return map.getFlavorsForNatives(natives); 200 } 201 @Override 202 public List<String> getNativesForFlavor(DataFlavor flav) { 203 Map<DataFlavor, String> natives = getNativesForFlavors(new DataFlavor[]{flav}); 204 String nat = natives.get(flav); 205 if (nat != null) { 206 return Collections.singletonList(nat); 207 } else { 208 return Collections.emptyList(); 209 } 210 } 211 @Override 212 public List<DataFlavor> getFlavorsForNative(String nat) { 213 Map<String, DataFlavor> flavors = getFlavorsForNatives(new String[]{nat}); 214 DataFlavor flavor = flavors.get(nat); 215 if (flavor != null) { 216 return Collections.singletonList(flavor); 217 } else { 218 return Collections.emptyList(); 219 } 220 } 221 }; 222 } 223 224 /** 225 * Returns the default Unicode encoding for the platform. The encoding 226 * need not be canonical. This method is only used by the archaic function 227 * DataFlavor.getTextPlainUnicodeFlavor(). 228 */ 229 public abstract String getDefaultUnicodeEncoding(); 230 231 /** 232 * This method is called for text flavor mappings established while parsing 233 * the flavormap.properties file. It stores the "eoln" and "terminators" 234 * parameters which are not officially part of the MIME type. They are 235 * MIME parameters specific to the flavormap.properties file format. 236 */ 237 public void registerTextFlavorProperties(String nat, String charset, 238 String eoln, String terminators) { 239 Long format = getFormatForNativeAsLong(nat); 240 241 textNatives.add(format); 242 nativeCharsets.put(format, (charset != null && charset.length() != 0) 243 ? charset : Charset.defaultCharset().name()); 244 if (eoln != null && eoln.length() != 0 && !eoln.equals("\n")) { 245 nativeEOLNs.put(format, eoln); 246 } 247 if (terminators != null && terminators.length() != 0) { 248 Integer iTerminators = Integer.valueOf(terminators); 249 if (iTerminators > 0) { 250 nativeTerminators.put(format, iTerminators); 251 } 252 } 253 } 254 255 /** 256 * Determines whether the native corresponding to the specified long format 257 * was listed in the flavormap.properties file. 258 */ 259 protected boolean isTextFormat(long format) { 260 return textNatives.contains(Long.valueOf(format)); 261 } 262 263 protected String getCharsetForTextFormat(Long lFormat) { 264 return nativeCharsets.get(lFormat); 265 } 266 267 /** 268 * Specifies whether text imported from the native system in the specified 269 * format is locale-dependent. If so, when decoding such text, 270 * 'nativeCharsets' should be ignored, and instead, the Transferable should 271 * be queried for its javaTextEncodingFlavor data for the correct encoding. 272 */ 273 public abstract boolean isLocaleDependentTextFormat(long format); 274 275 /** 276 * Determines whether the DataFlavor corresponding to the specified long 277 * format is DataFlavor.javaFileListFlavor. 278 */ 279 public abstract boolean isFileFormat(long format); 280 281 /** 282 * Determines whether the DataFlavor corresponding to the specified long 283 * format is DataFlavor.imageFlavor. 284 */ 285 public abstract boolean isImageFormat(long format); 286 287 /** 288 * Determines whether the format is a URI list we can convert to 289 * a DataFlavor.javaFileListFlavor. 290 */ 291 protected boolean isURIListFormat(long format) { 292 return false; 293 } 294 295 /** 296 * Returns a Map whose keys are all of the possible formats into which the 297 * Transferable's transfer data flavors can be translated. The value of 298 * each key is the DataFlavor in which the Transferable's data should be 299 * requested when converting to the format. 300 * <p> 301 * The map keys are sorted according to the native formats preference 302 * order. 303 */ 304 public SortedMap<Long,DataFlavor> getFormatsForTransferable(Transferable contents, 305 FlavorTable map) 306 { 307 DataFlavor[] flavors = contents.getTransferDataFlavors(); 308 if (flavors == null) { 309 return Collections.emptySortedMap(); 310 } 311 return getFormatsForFlavors(flavors, map); 312 } 313 314 /** 315 * Returns a Map whose keys are all of the possible formats into which data 316 * in the specified DataFlavors can be translated. The value of each key 317 * is the DataFlavor in which the Transferable's data should be requested 318 * when converting to the format. 319 * <p> 320 * The map keys are sorted according to the native formats preference 321 * order. 322 * 323 * @param flavors the data flavors 324 * @param map the FlavorTable which contains mappings between 325 * DataFlavors and data formats 326 * @throws NullPointerException if flavors or map is <code>null</code> 327 */ 328 public SortedMap<Long, DataFlavor> getFormatsForFlavors(DataFlavor[] flavors, 329 FlavorTable map) 330 { 331 Map<Long,DataFlavor> formatMap = new HashMap<>(flavors.length); 332 Map<Long,DataFlavor> textPlainMap = new HashMap<>(flavors.length); 333 // Maps formats to indices that will be used to sort the formats 334 // according to the preference order. 335 // Larger index value corresponds to the more preferable format. 336 Map<Long, Integer> indexMap = new HashMap<>(flavors.length); 337 Map<Long, Integer> textPlainIndexMap = new HashMap<>(flavors.length); 338 339 int currentIndex = 0; 340 341 // Iterate backwards so that preferred DataFlavors are used over 342 // other DataFlavors. (See javadoc for 343 // Transferable.getTransferDataFlavors.) 344 for (int i = flavors.length - 1; i >= 0; i--) { 345 DataFlavor flavor = flavors[i]; 346 if (flavor == null) continue; 347 348 // Don't explicitly test for String, since it is just a special 349 // case of Serializable 350 if (flavor.isFlavorTextType() || 351 flavor.isFlavorJavaFileListType() || 352 DataFlavor.imageFlavor.equals(flavor) || 353 flavor.isRepresentationClassSerializable() || 354 flavor.isRepresentationClassInputStream() || 355 flavor.isRepresentationClassRemote()) 356 { 357 List<String> natives = map.getNativesForFlavor(flavor); 358 359 currentIndex += natives.size(); 360 361 for (String aNative : natives) { 362 Long lFormat = getFormatForNativeAsLong(aNative); 363 Integer index = currentIndex--; 364 365 formatMap.put(lFormat, flavor); 366 indexMap.put(lFormat, index); 367 368 // SystemFlavorMap.getNativesForFlavor will return 369 // text/plain natives for all text/*. While this is good 370 // for a single text/* flavor, we would prefer that 371 // text/plain native data come from a text/plain flavor. 372 if (("text".equals(flavor.getPrimaryType()) && 373 "plain".equals(flavor.getSubType())) || 374 flavor.equals(DataFlavor.stringFlavor)) { 375 textPlainMap.put(lFormat, flavor); 376 textPlainIndexMap.put(lFormat, index); 377 } 378 } 379 380 currentIndex += natives.size(); 381 } 382 } 383 384 formatMap.putAll(textPlainMap); 385 indexMap.putAll(textPlainIndexMap); 386 387 // Sort the map keys according to the formats preference order. 388 Comparator<Long> comparator = DataFlavorUtil.getIndexOrderComparator(indexMap).reversed(); 389 SortedMap<Long, DataFlavor> sortedMap = new TreeMap<>(comparator); 390 sortedMap.putAll(formatMap); 391 392 return sortedMap; 393 } 394 395 /** 396 * Reduces the Map output for the root function to an array of the 397 * Map's keys. 398 */ 399 public long[] getFormatsForTransferableAsArray(Transferable contents, 400 FlavorTable map) { 401 return keysToLongArray(getFormatsForTransferable(contents, map)); 402 } 403 404 /** 405 * Returns a Map whose keys are all of the possible DataFlavors into which 406 * data in the specified formats can be translated. The value of each key 407 * is the format in which the Clipboard or dropped data should be requested 408 * when converting to the DataFlavor. 409 */ 410 public Map<DataFlavor, Long> getFlavorsForFormats(long[] formats, FlavorTable map) { 411 Map<DataFlavor, Long> flavorMap = new HashMap<>(formats.length); 412 Set<AbstractMap.SimpleEntry<Long, DataFlavor>> mappingSet = new HashSet<>(formats.length); 413 Set<DataFlavor> flavorSet = new HashSet<>(formats.length); 414 415 // First step: build flavorSet, mappingSet and initial flavorMap 416 // flavorSet - the set of all the DataFlavors into which 417 // data in the specified formats can be translated; 418 // mappingSet - the set of all the mappings from the specified formats 419 // into any DataFlavor; 420 // flavorMap - after this step, this map maps each of the DataFlavors 421 // from flavorSet to any of the specified formats. 422 for (long format : formats) { 423 String nat = getNativeForFormat(format); 424 List<DataFlavor> flavors = map.getFlavorsForNative(nat); 425 for (DataFlavor flavor : flavors) { 426 // Don't explicitly test for String, since it is just a special 427 // case of Serializable 428 if (flavor.isFlavorTextType() || 429 flavor.isFlavorJavaFileListType() || 430 DataFlavor.imageFlavor.equals(flavor) || 431 flavor.isRepresentationClassSerializable() || 432 flavor.isRepresentationClassInputStream() || 433 flavor.isRepresentationClassRemote()) { 434 435 AbstractMap.SimpleEntry<Long, DataFlavor> mapping = 436 new AbstractMap.SimpleEntry<>(format, flavor); 437 flavorMap.put(flavor, format); 438 mappingSet.add(mapping); 439 flavorSet.add(flavor); 440 } 441 } 442 } 443 444 // Second step: for each DataFlavor try to figure out which of the 445 // specified formats is the best to translate to this flavor. 446 // Then map each flavor to the best format. 447 // For the given flavor, FlavorTable indicates which native will 448 // best reflect data in the specified flavor to the underlying native 449 // platform. We assume that this native is the best to translate 450 // to this flavor. 451 // Note: FlavorTable allows one-way mappings, so we can occasionally 452 // map a flavor to the format for which the corresponding 453 // format-to-flavor mapping doesn't exist. For this reason we have built 454 // a mappingSet of all format-to-flavor mappings for the specified formats 455 // and check if the format-to-flavor mapping exists for the 456 // (flavor,format) pair being added. 457 for (DataFlavor flavor : flavorSet) { 458 List<String> natives = map.getNativesForFlavor(flavor); 459 for (String aNative : natives) { 460 Long lFormat = getFormatForNativeAsLong(aNative); 461 if (mappingSet.contains(new AbstractMap.SimpleEntry<>(lFormat, flavor))) { 462 flavorMap.put(flavor, lFormat); 463 break; 464 } 465 } 466 } 467 468 return flavorMap; 469 } 470 471 /** 472 * Returns a Set of all DataFlavors for which 473 * 1) a mapping from at least one of the specified formats exists in the 474 * specified map and 475 * 2) the data translation for this mapping can be performed by the data 476 * transfer subsystem. 477 * 478 * @param formats the data formats 479 * @param map the FlavorTable which contains mappings between 480 * DataFlavors and data formats 481 * @throws NullPointerException if formats or map is <code>null</code> 482 */ 483 public Set<DataFlavor> getFlavorsForFormatsAsSet(long[] formats, FlavorTable map) { 484 Set<DataFlavor> flavorSet = new HashSet<>(formats.length); 485 486 for (long format : formats) { 487 List<DataFlavor> flavors = map.getFlavorsForNative(getNativeForFormat(format)); 488 for (DataFlavor flavor : flavors) { 489 // Don't explicitly test for String, since it is just a special 490 // case of Serializable 491 if (flavor.isFlavorTextType() || 492 flavor.isFlavorJavaFileListType() || 493 DataFlavor.imageFlavor.equals(flavor) || 494 flavor.isRepresentationClassSerializable() || 495 flavor.isRepresentationClassInputStream() || 496 flavor.isRepresentationClassRemote()) { 497 flavorSet.add(flavor); 498 } 499 } 500 } 501 502 return flavorSet; 503 } 504 505 /** 506 * Returns an array of all DataFlavors for which 507 * 1) a mapping from at least one of the specified formats exists in the 508 * specified map and 509 * 2) the data translation for this mapping can be performed by the data 510 * transfer subsystem. 511 * The array will be sorted according to a 512 * <code>DataFlavorComparator</code> created with the specified 513 * map as an argument. 514 * 515 * @param formats the data formats 516 * @param map the FlavorTable which contains mappings between 517 * DataFlavors and data formats 518 * @throws NullPointerException if formats or map is <code>null</code> 519 */ 520 public DataFlavor[] getFlavorsForFormatsAsArray(long[] formats, 521 FlavorTable map) { 522 // getFlavorsForFormatsAsSet() is less expensive than 523 // getFlavorsForFormats(). 524 return setToSortedDataFlavorArray(getFlavorsForFormatsAsSet(formats, map)); 525 } 526 527 /** 528 * Looks-up or registers the String native with the native data transfer 529 * system and returns a long format corresponding to that native. 530 */ 531 protected abstract Long getFormatForNativeAsLong(String str); 532 533 /** 534 * Looks-up the String native corresponding to the specified long format in 535 * the native data transfer system. 536 */ 537 protected abstract String getNativeForFormat(long format); 538 539 /* Contains common code for finding the best charset for 540 * clipboard string encoding/decoding, basing on clipboard 541 * format and localeTransferable(on decoding, if available) 542 */ 543 protected String getBestCharsetForTextFormat(Long lFormat, 544 Transferable localeTransferable) throws IOException 545 { 546 String charset = null; 547 if (localeTransferable != null && 548 isLocaleDependentTextFormat(lFormat) && 549 localeTransferable.isDataFlavorSupported(javaTextEncodingFlavor)) { 550 try { 551 byte[] charsetNameBytes = (byte[])localeTransferable 552 .getTransferData(javaTextEncodingFlavor); 553 charset = new String(charsetNameBytes, StandardCharsets.UTF_8); 554 } catch (UnsupportedFlavorException cannotHappen) { 555 } 556 } else { 557 charset = getCharsetForTextFormat(lFormat); 558 } 559 if (charset == null) { 560 // Only happens when we have a custom text type. 561 charset = Charset.defaultCharset().name(); 562 } 563 return charset; 564 } 565 566 /** 567 * Translation function for converting string into 568 * a byte array. Search-and-replace EOLN. Encode into the 569 * target format. Append terminating NUL bytes. 570 * 571 * Java to Native string conversion 572 */ 573 private byte[] translateTransferableString(String str, 574 long format) throws IOException 575 { 576 Long lFormat = format; 577 String charset = getBestCharsetForTextFormat(lFormat, null); 578 // Search and replace EOLN. Note that if EOLN is "\n", then we 579 // never added an entry to nativeEOLNs anyway, so we'll skip this 580 // code altogether. 581 // windows: "abc\nde"->"abc\r\nde" 582 String eoln = nativeEOLNs.get(lFormat); 583 if (eoln != null) { 584 int length = str.length(); 585 StringBuilder buffer = new StringBuilder(length * 2); // 2 is a heuristic 586 for (int i = 0; i < length; i++) { 587 // Fix for 4914613 - skip native EOLN 588 if (str.startsWith(eoln, i)) { 589 buffer.append(eoln); 590 i += eoln.length() - 1; 591 continue; 592 } 593 char c = str.charAt(i); 594 if (c == '\n') { 595 buffer.append(eoln); 596 } else { 597 buffer.append(c); 598 } 599 } 600 str = buffer.toString(); 601 } 602 603 // Encode text in target format. 604 byte[] bytes = str.getBytes(charset); 605 606 // Append terminating NUL bytes. Note that if terminators is 0, 607 // the we never added an entry to nativeTerminators anyway, so 608 // we'll skip code altogether. 609 // "abcde" -> "abcde\0" 610 Integer terminators = nativeTerminators.get(lFormat); 611 if (terminators != null) { 612 int numTerminators = terminators; 613 byte[] terminatedBytes = 614 new byte[bytes.length + numTerminators]; 615 System.arraycopy(bytes, 0, terminatedBytes, 0, bytes.length); 616 for (int i = bytes.length; i < terminatedBytes.length; i++) { 617 terminatedBytes[i] = 0x0; 618 } 619 bytes = terminatedBytes; 620 } 621 return bytes; 622 } 623 624 /** 625 * Translating either a byte array or an InputStream into an String. 626 * Strip terminators and search-and-replace EOLN. 627 * 628 * Native to Java string conversion 629 */ 630 private String translateBytesToString(byte[] bytes, long format, 631 Transferable localeTransferable) 632 throws IOException 633 { 634 635 Long lFormat = format; 636 String charset = getBestCharsetForTextFormat(lFormat, localeTransferable); 637 638 // Locate terminating NUL bytes. Note that if terminators is 0, 639 // the we never added an entry to nativeTerminators anyway, so 640 // we'll skip code altogether. 641 642 // In other words: we are doing char alignment here basing on suggestion 643 // that count of zero-'terminators' is a number of bytes in one symbol 644 // for selected charset (clipboard format). It is not complitly true for 645 // multibyte coding like UTF-8, but helps understand the procedure. 646 // "abcde\0" -> "abcde" 647 648 String eoln = nativeEOLNs.get(lFormat); 649 Integer terminators = nativeTerminators.get(lFormat); 650 int count; 651 if (terminators != null) { 652 int numTerminators = terminators; 653 search: 654 for (count = 0; count < (bytes.length - numTerminators + 1); count += numTerminators) { 655 for (int i = count; i < count + numTerminators; i++) { 656 if (bytes[i] != 0x0) { 657 continue search; 658 } 659 } 660 // found terminators 661 break search; 662 } 663 } else { 664 count = bytes.length; 665 } 666 667 // Decode text to chars. Don't include any terminators. 668 String converted = new String(bytes, 0, count, charset); 669 670 // Search and replace EOLN. Note that if EOLN is "\n", then we 671 // never added an entry to nativeEOLNs anyway, so we'll skip this 672 // code altogether. 673 // Count of NUL-terminators and EOLN coding are platform-specific and 674 // loaded from flavormap.properties file 675 // windows: "abc\r\nde" -> "abc\nde" 676 677 if (eoln != null) { 678 679 /* Fix for 4463560: replace EOLNs symbol-by-symbol instead 680 * of using buf.replace() 681 */ 682 683 char[] buf = converted.toCharArray(); 684 char[] eoln_arr = eoln.toCharArray(); 685 int j = 0; 686 boolean match; 687 688 for (int i = 0; i < buf.length; ) { 689 // Catch last few bytes 690 if (i + eoln_arr.length > buf.length) { 691 buf[j++] = buf[i++]; 692 continue; 693 } 694 695 match = true; 696 for (int k = 0, l = i; k < eoln_arr.length; k++, l++) { 697 if (eoln_arr[k] != buf[l]) { 698 match = false; 699 break; 700 } 701 } 702 if (match) { 703 buf[j++] = '\n'; 704 i += eoln_arr.length; 705 } else { 706 buf[j++] = buf[i++]; 707 } 708 } 709 converted = new String(buf, 0, j); 710 } 711 712 return converted; 713 } 714 715 716 /** 717 * Primary translation function for translating a Transferable into 718 * a byte array, given a source DataFlavor and target format. 719 */ 720 @SuppressWarnings("deprecation") 721 public byte[] translateTransferable(Transferable contents, 722 DataFlavor flavor, 723 long format) throws IOException 724 { 725 // Obtain the transfer data in the source DataFlavor. 726 // 727 // Note that we special case DataFlavor.plainTextFlavor because 728 // StringSelection supports this flavor incorrectly -- instead of 729 // returning an InputStream as the DataFlavor representation class 730 // states, it returns a Reader. Instead of using this broken 731 // functionality, we request the data in stringFlavor (the other 732 // DataFlavor which StringSelection supports) and use the String 733 // translator. 734 Object obj; 735 boolean stringSelectionHack; 736 try { 737 obj = contents.getTransferData(flavor); 738 if (obj == null) { 739 return null; 740 } 741 if (flavor.equals(DataFlavor.plainTextFlavor) && 742 !(obj instanceof InputStream)) 743 { 744 obj = contents.getTransferData(DataFlavor.stringFlavor); 745 if (obj == null) { 746 return null; 747 } 748 stringSelectionHack = true; 749 } else { 750 stringSelectionHack = false; 751 } 752 } catch (UnsupportedFlavorException e) { 753 throw new IOException(e.getMessage()); 754 } 755 756 // Source data is a String. Search-and-replace EOLN. Encode into the 757 // target format. Append terminating NUL bytes. 758 if (stringSelectionHack || 759 (String.class.equals(flavor.getRepresentationClass()) && 760 DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) { 761 762 String str = removeSuspectedData(flavor, contents, (String)obj); 763 764 return translateTransferableString( 765 str, 766 format); 767 768 // Source data is a Reader. Convert to a String and recur. In the 769 // future, we may want to rewrite this so that we encode on demand. 770 } else if (flavor.isRepresentationClassReader()) { 771 if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) { 772 throw new IOException 773 ("cannot transfer non-text data as Reader"); 774 } 775 776 StringBuilder buf = new StringBuilder(); 777 try (Reader r = (Reader)obj) { 778 int c; 779 while ((c = r.read()) != -1) { 780 buf.append((char)c); 781 } 782 } 783 784 return translateTransferableString( 785 buf.toString(), 786 format); 787 788 // Source data is a CharBuffer. Convert to a String and recur. 789 } else if (flavor.isRepresentationClassCharBuffer()) { 790 if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) { 791 throw new IOException 792 ("cannot transfer non-text data as CharBuffer"); 793 } 794 795 CharBuffer buffer = (CharBuffer)obj; 796 int size = buffer.remaining(); 797 char[] chars = new char[size]; 798 buffer.get(chars, 0, size); 799 800 return translateTransferableString( 801 new String(chars), 802 format); 803 804 // Source data is a char array. Convert to a String and recur. 805 } else if (char[].class.equals(flavor.getRepresentationClass())) { 806 if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) { 807 throw new IOException 808 ("cannot transfer non-text data as char array"); 809 } 810 811 return translateTransferableString( 812 new String((char[])obj), 813 format); 814 815 // Source data is a ByteBuffer. For arbitrary flavors, simply return 816 // the array. For text flavors, decode back to a String and recur to 817 // reencode according to the requested format. 818 } else if (flavor.isRepresentationClassByteBuffer()) { 819 ByteBuffer buffer = (ByteBuffer)obj; 820 int size = buffer.remaining(); 821 byte[] bytes = new byte[size]; 822 buffer.get(bytes, 0, size); 823 824 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 825 String sourceEncoding = DataFlavorUtil.getTextCharset(flavor); 826 return translateTransferableString( 827 new String(bytes, sourceEncoding), 828 format); 829 } else { 830 return bytes; 831 } 832 833 // Source data is a byte array. For arbitrary flavors, simply return 834 // the array. For text flavors, decode back to a String and recur to 835 // reencode according to the requested format. 836 } else if (byte[].class.equals(flavor.getRepresentationClass())) { 837 byte[] bytes = (byte[])obj; 838 839 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 840 String sourceEncoding = DataFlavorUtil.getTextCharset(flavor); 841 return translateTransferableString( 842 new String(bytes, sourceEncoding), 843 format); 844 } else { 845 return bytes; 846 } 847 // Source data is Image 848 } else if (DataFlavor.imageFlavor.equals(flavor)) { 849 if (!isImageFormat(format)) { 850 throw new IOException("Data translation failed: " + 851 "not an image format"); 852 } 853 854 Image image = (Image)obj; 855 byte[] bytes = imageToPlatformBytes(image, format); 856 857 if (bytes == null) { 858 throw new IOException("Data translation failed: " + 859 "cannot convert java image to native format"); 860 } 861 return bytes; 862 } 863 864 byte[] theByteArray = null; 865 866 // Target data is a file list. Source data must be a 867 // java.util.List which contains java.io.File or String instances. 868 if (isFileFormat(format)) { 869 if (!DataFlavor.javaFileListFlavor.equals(flavor)) { 870 throw new IOException("data translation failed"); 871 } 872 873 final List<?> list = (List<?>)obj; 874 875 final ProtectionDomain userProtectionDomain = getUserProtectionDomain(contents); 876 877 final ArrayList<String> fileList = castToFiles(list, userProtectionDomain); 878 879 try (ByteArrayOutputStream bos = convertFileListToBytes(fileList)) { 880 theByteArray = bos.toByteArray(); 881 } 882 883 // Target data is a URI list. Source data must be a 884 // java.util.List which contains java.io.File or String instances. 885 } else if (isURIListFormat(format)) { 886 if (!DataFlavor.javaFileListFlavor.equals(flavor)) { 887 throw new IOException("data translation failed"); 888 } 889 String nat = getNativeForFormat(format); 890 String targetCharset = null; 891 if (nat != null) { 892 try { 893 targetCharset = new DataFlavor(nat).getParameter("charset"); 894 } catch (ClassNotFoundException cnfe) { 895 throw new IOException(cnfe); 896 } 897 } 898 if (targetCharset == null) { 899 targetCharset = "UTF-8"; 900 } 901 final List<?> list = (List<?>)obj; 902 final ProtectionDomain userProtectionDomain = getUserProtectionDomain(contents); 903 final ArrayList<String> fileList = castToFiles(list, userProtectionDomain); 904 final ArrayList<String> uriList = new ArrayList<>(fileList.size()); 905 for (String fileObject : fileList) { 906 final URI uri = new File(fileObject).toURI(); 907 // Some implementations are fussy about the number of slashes (file:///path/to/file is best) 908 try { 909 uriList.add(new URI(uri.getScheme(), "", uri.getPath(), uri.getFragment()).toString()); 910 } catch (URISyntaxException uriSyntaxException) { 911 throw new IOException(uriSyntaxException); 912 } 913 } 914 915 byte[] eoln = "\r\n".getBytes(targetCharset); 916 917 try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { 918 for (String uri : uriList) { 919 byte[] bytes = uri.getBytes(targetCharset); 920 bos.write(bytes, 0, bytes.length); 921 bos.write(eoln, 0, eoln.length); 922 } 923 theByteArray = bos.toByteArray(); 924 } 925 926 // Source data is an InputStream. For arbitrary flavors, just grab the 927 // bytes and dump them into a byte array. For text flavors, decode back 928 // to a String and recur to reencode according to the requested format. 929 } else if (flavor.isRepresentationClassInputStream()) { 930 931 // Workaround to JDK-8024061: Exception thrown when drag and drop 932 // between two components is executed quickly. 933 // and JDK-8065098: JColorChooser no longer supports drag and drop 934 // between two JVM instances 935 if (!(obj instanceof InputStream)) { 936 return new byte[0]; 937 } 938 939 try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { 940 try (InputStream is = (InputStream)obj) { 941 boolean eof = false; 942 int avail = is.available(); 943 byte[] tmp = new byte[avail > 8192 ? avail : 8192]; 944 do { 945 int aValue; 946 if (!(eof = (aValue = is.read(tmp, 0, tmp.length)) == -1)) { 947 bos.write(tmp, 0, aValue); 948 } 949 } while (!eof); 950 } 951 952 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 953 byte[] bytes = bos.toByteArray(); 954 String sourceEncoding = DataFlavorUtil.getTextCharset(flavor); 955 return translateTransferableString( 956 new String(bytes, sourceEncoding), 957 format); 958 } 959 theByteArray = bos.toByteArray(); 960 } 961 962 963 // Source data is an RMI object 964 } else if (flavor.isRepresentationClassRemote()) { 965 theByteArray = convertObjectToBytes(DataFlavorUtil.RMI.newMarshalledObject(obj)); 966 967 // Source data is Serializable 968 } else if (flavor.isRepresentationClassSerializable()) { 969 970 theByteArray = convertObjectToBytes(obj); 971 972 } else { 973 throw new IOException("data translation failed"); 974 } 975 976 977 978 return theByteArray; 979 } 980 981 private static byte[] convertObjectToBytes(Object object) throws IOException { 982 try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); 983 ObjectOutputStream oos = new ObjectOutputStream(bos)) 984 { 985 oos.writeObject(object); 986 return bos.toByteArray(); 987 } 988 } 989 990 protected abstract ByteArrayOutputStream convertFileListToBytes(ArrayList<String> fileList) throws IOException; 991 992 private String removeSuspectedData(DataFlavor flavor, final Transferable contents, final String str) 993 throws IOException 994 { 995 if (null == System.getSecurityManager() 996 || !flavor.isMimeTypeEqual("text/uri-list")) 997 { 998 return str; 999 } 1000 1001 final ProtectionDomain userProtectionDomain = getUserProtectionDomain(contents); 1002 1003 try { 1004 return AccessController.doPrivileged((PrivilegedExceptionAction<String>) () -> { 1005 1006 StringBuilder allowedFiles = new StringBuilder(str.length()); 1007 String [] uriArray = str.split("(\\s)+"); 1008 1009 for (String fileName : uriArray) 1010 { 1011 File file = new File(fileName); 1012 if (file.exists() && 1013 !(isFileInWebstartedCache(file) || 1014 isForbiddenToRead(file, userProtectionDomain))) 1015 { 1016 if (0 != allowedFiles.length()) 1017 { 1018 allowedFiles.append("\\r\\n"); 1019 } 1020 1021 allowedFiles.append(fileName); 1022 } 1023 } 1024 1025 return allowedFiles.toString(); 1026 }); 1027 } catch (PrivilegedActionException pae) { 1028 throw new IOException(pae.getMessage(), pae); 1029 } 1030 } 1031 1032 private static ProtectionDomain getUserProtectionDomain(Transferable contents) { 1033 return contents.getClass().getProtectionDomain(); 1034 } 1035 1036 private boolean isForbiddenToRead (File file, ProtectionDomain protectionDomain) 1037 { 1038 if (null == protectionDomain) { 1039 return false; 1040 } 1041 try { 1042 FilePermission filePermission = 1043 new FilePermission(file.getCanonicalPath(), "read, delete"); 1044 if (protectionDomain.implies(filePermission)) { 1045 return false; 1046 } 1047 } catch (IOException e) {} 1048 1049 return true; 1050 } 1051 1052 private ArrayList<String> castToFiles(final List<?> files, 1053 final ProtectionDomain userProtectionDomain) throws IOException { 1054 try { 1055 return AccessController.doPrivileged((PrivilegedExceptionAction<ArrayList<String>>) () -> { 1056 ArrayList<String> fileList = new ArrayList<>(); 1057 for (Object fileObject : files) 1058 { 1059 File file = castToFile(fileObject); 1060 if (file != null && 1061 (null == System.getSecurityManager() || 1062 !(isFileInWebstartedCache(file) || 1063 isForbiddenToRead(file, userProtectionDomain)))) 1064 { 1065 fileList.add(file.getCanonicalPath()); 1066 } 1067 } 1068 return fileList; 1069 }); 1070 } catch (PrivilegedActionException pae) { 1071 throw new IOException(pae.getMessage()); 1072 } 1073 } 1074 1075 // It is important do not use user's successors 1076 // of File class. 1077 private File castToFile(Object fileObject) throws IOException { 1078 String filePath = null; 1079 if (fileObject instanceof File) { 1080 filePath = ((File)fileObject).getCanonicalPath(); 1081 } else if (fileObject instanceof String) { 1082 filePath = (String) fileObject; 1083 } else { 1084 return null; 1085 } 1086 return new File(filePath); 1087 } 1088 1089 private final static String[] DEPLOYMENT_CACHE_PROPERTIES = { 1090 "deployment.system.cachedir", 1091 "deployment.user.cachedir", 1092 "deployment.javaws.cachedir", 1093 "deployment.javapi.cachedir" 1094 }; 1095 1096 private final static ArrayList <File> deploymentCacheDirectoryList = new ArrayList<>(); 1097 1098 private static boolean isFileInWebstartedCache(File f) { 1099 1100 if (deploymentCacheDirectoryList.isEmpty()) { 1101 for (String cacheDirectoryProperty : DEPLOYMENT_CACHE_PROPERTIES) { 1102 String cacheDirectoryPath = System.getProperty(cacheDirectoryProperty); 1103 if (cacheDirectoryPath != null) { 1104 try { 1105 File cacheDirectory = (new File(cacheDirectoryPath)).getCanonicalFile(); 1106 if (cacheDirectory != null) { 1107 deploymentCacheDirectoryList.add(cacheDirectory); 1108 } 1109 } catch (IOException ioe) {} 1110 } 1111 } 1112 } 1113 1114 for (File deploymentCacheDirectory : deploymentCacheDirectoryList) { 1115 for (File dir = f; dir != null; dir = dir.getParentFile()) { 1116 if (dir.equals(deploymentCacheDirectory)) { 1117 return true; 1118 } 1119 } 1120 } 1121 1122 return false; 1123 } 1124 1125 1126 public Object translateBytes(byte[] bytes, DataFlavor flavor, 1127 long format, Transferable localeTransferable) 1128 throws IOException 1129 { 1130 1131 Object theObject = null; 1132 1133 // Source data is a file list. Use the dragQueryFile native function to 1134 // do most of the decoding. Then wrap File objects around the String 1135 // filenames and return a List. 1136 if (isFileFormat(format)) { 1137 if (!DataFlavor.javaFileListFlavor.equals(flavor)) { 1138 throw new IOException("data translation failed"); 1139 } 1140 String[] filenames = dragQueryFile(bytes); 1141 if (filenames == null) { 1142 return null; 1143 } 1144 1145 // Convert the strings to File objects 1146 File[] files = new File[filenames.length]; 1147 for (int i = 0; i < filenames.length; i++) { 1148 files[i] = new File(filenames[i]); 1149 } 1150 1151 // Turn the list of Files into a List and return 1152 theObject = Arrays.asList(files); 1153 1154 // Source data is a URI list. Convert to DataFlavor.javaFileListFlavor 1155 // where possible. 1156 } else if (isURIListFormat(format) 1157 && DataFlavor.javaFileListFlavor.equals(flavor)) { 1158 1159 try (ByteArrayInputStream str = new ByteArrayInputStream(bytes)) { 1160 1161 URI uris[] = dragQueryURIs(str, format, localeTransferable); 1162 if (uris == null) { 1163 return null; 1164 } 1165 List<File> files = new ArrayList<>(); 1166 for (URI uri : uris) { 1167 try { 1168 files.add(new File(uri)); 1169 } catch (IllegalArgumentException illegalArg) { 1170 // When converting from URIs to less generic files, 1171 // common practice (Wine, SWT) seems to be to 1172 // silently drop the URIs that aren't local files. 1173 } 1174 } 1175 theObject = files; 1176 } 1177 1178 // Target data is a String. Strip terminating NUL bytes. Decode bytes 1179 // into characters. Search-and-replace EOLN. 1180 } else if (String.class.equals(flavor.getRepresentationClass()) && 1181 DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1182 1183 theObject = translateBytesToString(bytes, format, localeTransferable); 1184 1185 // Target data is a Reader. Obtain data in InputStream format, encoded 1186 // as "Unicode" (utf-16be). Then use an InputStreamReader to decode 1187 // back to chars on demand. 1188 } else if (flavor.isRepresentationClassReader()) { 1189 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) { 1190 theObject = translateStream(bais, 1191 flavor, format, localeTransferable); 1192 } 1193 // Target data is a CharBuffer. Recur to obtain String and wrap. 1194 } else if (flavor.isRepresentationClassCharBuffer()) { 1195 if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) { 1196 throw new IOException("cannot transfer non-text data as CharBuffer"); 1197 } 1198 1199 CharBuffer buffer = CharBuffer.wrap( 1200 translateBytesToString(bytes,format, localeTransferable)); 1201 1202 theObject = constructFlavoredObject(buffer, flavor, CharBuffer.class); 1203 1204 // Target data is a char array. Recur to obtain String and convert to 1205 // char array. 1206 } else if (char[].class.equals(flavor.getRepresentationClass())) { 1207 if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) { 1208 throw new IOException 1209 ("cannot transfer non-text data as char array"); 1210 } 1211 1212 theObject = translateBytesToString( 1213 bytes, format, localeTransferable).toCharArray(); 1214 1215 // Target data is a ByteBuffer. For arbitrary flavors, just return 1216 // the raw bytes. For text flavors, convert to a String to strip 1217 // terminators and search-and-replace EOLN, then reencode according to 1218 // the requested flavor. 1219 } else if (flavor.isRepresentationClassByteBuffer()) { 1220 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1221 bytes = translateBytesToString( 1222 bytes, format, localeTransferable).getBytes( 1223 DataFlavorUtil.getTextCharset(flavor) 1224 ); 1225 } 1226 1227 ByteBuffer buffer = ByteBuffer.wrap(bytes); 1228 theObject = constructFlavoredObject(buffer, flavor, ByteBuffer.class); 1229 1230 // Target data is a byte array. For arbitrary flavors, just return 1231 // the raw bytes. For text flavors, convert to a String to strip 1232 // terminators and search-and-replace EOLN, then reencode according to 1233 // the requested flavor. 1234 } else if (byte[].class.equals(flavor.getRepresentationClass())) { 1235 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1236 theObject = translateBytesToString( 1237 bytes, format, localeTransferable 1238 ).getBytes(DataFlavorUtil.getTextCharset(flavor)); 1239 } else { 1240 theObject = bytes; 1241 } 1242 1243 // Target data is an InputStream. For arbitrary flavors, just return 1244 // the raw bytes. For text flavors, decode to strip terminators and 1245 // search-and-replace EOLN, then reencode according to the requested 1246 // flavor. 1247 } else if (flavor.isRepresentationClassInputStream()) { 1248 1249 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) { 1250 theObject = translateStream(bais, flavor, format, localeTransferable); 1251 } 1252 1253 } else if (flavor.isRepresentationClassRemote()) { 1254 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); 1255 ObjectInputStream ois = new ObjectInputStream(bais)) { 1256 1257 theObject = DataFlavorUtil.RMI.getMarshalledObject(ois.readObject()); 1258 } catch (Exception e) { 1259 throw new IOException(e.getMessage()); 1260 } 1261 1262 // Target data is Serializable 1263 } else if (flavor.isRepresentationClassSerializable()) { 1264 1265 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) { 1266 theObject = translateStream(bais, flavor, format, localeTransferable); 1267 } 1268 1269 // Target data is Image 1270 } else if (DataFlavor.imageFlavor.equals(flavor)) { 1271 if (!isImageFormat(format)) { 1272 throw new IOException("data translation failed"); 1273 } 1274 1275 theObject = platformImageBytesToImage(bytes, format); 1276 } 1277 1278 if (theObject == null) { 1279 throw new IOException("data translation failed"); 1280 } 1281 1282 return theObject; 1283 1284 } 1285 1286 /** 1287 * Primary translation function for translating 1288 * an InputStream into an Object, given a source format and a target 1289 * DataFlavor. 1290 */ 1291 @SuppressWarnings("deprecation") 1292 public Object translateStream(InputStream str, DataFlavor flavor, 1293 long format, Transferable localeTransferable) 1294 throws IOException 1295 { 1296 1297 Object theObject = null; 1298 // Source data is a URI list. Convert to DataFlavor.javaFileListFlavor 1299 // where possible. 1300 if (isURIListFormat(format) 1301 && DataFlavor.javaFileListFlavor.equals(flavor)) 1302 { 1303 1304 URI uris[] = dragQueryURIs(str, format, localeTransferable); 1305 if (uris == null) { 1306 return null; 1307 } 1308 List<File> files = new ArrayList<>(); 1309 for (URI uri : uris) { 1310 try { 1311 files.add(new File(uri)); 1312 } catch (IllegalArgumentException illegalArg) { 1313 // When converting from URIs to less generic files, 1314 // common practice (Wine, SWT) seems to be to 1315 // silently drop the URIs that aren't local files. 1316 } 1317 } 1318 theObject = files; 1319 1320 // Target data is a String. Strip terminating NUL bytes. Decode bytes 1321 // into characters. Search-and-replace EOLN. 1322 } else if (String.class.equals(flavor.getRepresentationClass()) && 1323 DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1324 1325 return translateBytesToString(inputStreamToByteArray(str), 1326 format, localeTransferable); 1327 1328 // Special hack to maintain backwards-compatibility with the brokenness 1329 // of StringSelection. Return a StringReader instead of an InputStream. 1330 // Recur to obtain String and encapsulate. 1331 } else if (DataFlavor.plainTextFlavor.equals(flavor)) { 1332 theObject = new StringReader(translateBytesToString( 1333 inputStreamToByteArray(str), 1334 format, localeTransferable)); 1335 1336 // Target data is an InputStream. For arbitrary flavors, just return 1337 // the raw bytes. For text flavors, decode to strip terminators and 1338 // search-and-replace EOLN, then reencode according to the requested 1339 // flavor. 1340 } else if (flavor.isRepresentationClassInputStream()) { 1341 theObject = translateStreamToInputStream(str, flavor, format, 1342 localeTransferable); 1343 1344 // Target data is a Reader. Obtain data in InputStream format, encoded 1345 // as "Unicode" (utf-16be). Then use an InputStreamReader to decode 1346 // back to chars on demand. 1347 } else if (flavor.isRepresentationClassReader()) { 1348 if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) { 1349 throw new IOException 1350 ("cannot transfer non-text data as Reader"); 1351 } 1352 1353 InputStream is = (InputStream)translateStreamToInputStream( 1354 str, DataFlavor.plainTextFlavor, 1355 format, localeTransferable); 1356 1357 String unicode = DataFlavorUtil.getTextCharset(DataFlavor.plainTextFlavor); 1358 1359 Reader reader = new InputStreamReader(is, unicode); 1360 1361 theObject = constructFlavoredObject(reader, flavor, Reader.class); 1362 // Target data is a byte array 1363 } else if (byte[].class.equals(flavor.getRepresentationClass())) { 1364 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1365 theObject = translateBytesToString(inputStreamToByteArray(str), format, localeTransferable) 1366 .getBytes(DataFlavorUtil.getTextCharset(flavor)); 1367 } else { 1368 theObject = inputStreamToByteArray(str); 1369 } 1370 // Target data is an RMI object 1371 } else if (flavor.isRepresentationClassRemote()) { 1372 try (ObjectInputStream ois = new ObjectInputStream(str)) { 1373 theObject = DataFlavorUtil.RMI.getMarshalledObject(ois.readObject()); 1374 } catch (Exception e) { 1375 throw new IOException(e.getMessage()); 1376 } 1377 1378 // Target data is Serializable 1379 } else if (flavor.isRepresentationClassSerializable()) { 1380 try (ObjectInputStream ois = 1381 new ObjectInputStream(str)) 1382 { 1383 theObject = ois.readObject(); 1384 } catch (Exception e) { 1385 throw new IOException(e.getMessage()); 1386 } 1387 // Target data is Image 1388 } else if (DataFlavor.imageFlavor.equals(flavor)) { 1389 if (!isImageFormat(format)) { 1390 throw new IOException("data translation failed"); 1391 } 1392 theObject = platformImageBytesToImage(inputStreamToByteArray(str), format); 1393 } 1394 1395 if (theObject == null) { 1396 throw new IOException("data translation failed"); 1397 } 1398 1399 return theObject; 1400 1401 } 1402 1403 /** 1404 * For arbitrary flavors, just use the raw InputStream. For text flavors, 1405 * ReencodingInputStream will decode and reencode the InputStream on demand 1406 * so that we can strip terminators and search-and-replace EOLN. 1407 */ 1408 private Object translateStreamToInputStream 1409 (InputStream str, DataFlavor flavor, long format, 1410 Transferable localeTransferable) throws IOException 1411 { 1412 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1413 str = new ReencodingInputStream 1414 (str, format, DataFlavorUtil.getTextCharset(flavor), 1415 localeTransferable); 1416 } 1417 1418 return constructFlavoredObject(str, flavor, InputStream.class); 1419 } 1420 1421 /** 1422 * We support representations which are exactly of the specified Class, 1423 * and also arbitrary Objects which have a constructor which takes an 1424 * instance of the Class as its sole parameter. 1425 */ 1426 private Object constructFlavoredObject(Object arg, DataFlavor flavor, 1427 Class<?> clazz) 1428 throws IOException 1429 { 1430 final Class<?> dfrc = flavor.getRepresentationClass(); 1431 1432 if (clazz.equals(dfrc)) { 1433 return arg; // simple case 1434 } else { 1435 Constructor<?>[] constructors; 1436 1437 try { 1438 constructors = AccessController.doPrivileged( 1439 (PrivilegedAction<Constructor<?>[]>) dfrc::getConstructors); 1440 } catch (SecurityException se) { 1441 throw new IOException(se.getMessage()); 1442 } 1443 1444 Constructor<?> constructor = Stream.of(constructors) 1445 .filter(c -> Modifier.isPublic(c.getModifiers())) 1446 .filter(c -> { 1447 Class<?>[] ptypes = c.getParameterTypes(); 1448 return ptypes != null 1449 && ptypes.length == 1 1450 && clazz.equals(ptypes[0]); 1451 }) 1452 .findFirst() 1453 .orElseThrow(() -> 1454 new IOException("can't find <init>(L"+ clazz + ";)V for class: " + dfrc.getName())); 1455 1456 try { 1457 return constructor.newInstance(arg); 1458 } catch (Exception e) { 1459 throw new IOException(e.getMessage()); 1460 } 1461 } 1462 } 1463 1464 /** 1465 * Used for decoding and reencoding an InputStream on demand so that we 1466 * can strip NUL terminators and perform EOLN search-and-replace. 1467 */ 1468 public class ReencodingInputStream extends InputStream { 1469 BufferedReader wrapped; 1470 final char[] in = new char[2]; 1471 byte[] out; 1472 1473 CharsetEncoder encoder; 1474 CharBuffer inBuf; 1475 ByteBuffer outBuf; 1476 1477 char[] eoln; 1478 int numTerminators; 1479 1480 boolean eos; 1481 int index, limit; 1482 1483 public ReencodingInputStream(InputStream bytestream, long format, 1484 String targetEncoding, 1485 Transferable localeTransferable) 1486 throws IOException 1487 { 1488 Long lFormat = format; 1489 1490 String sourceEncoding = getBestCharsetForTextFormat(format, localeTransferable); 1491 wrapped = new BufferedReader(new InputStreamReader(bytestream, sourceEncoding)); 1492 1493 if (targetEncoding == null) { 1494 // Throw NullPointerException for compatibility with the former 1495 // call to sun.io.CharToByteConverter.getConverter(null) 1496 // (Charset.forName(null) throws unspecified IllegalArgumentException 1497 // now; see 6228568) 1498 throw new NullPointerException("null target encoding"); 1499 } 1500 1501 try { 1502 encoder = Charset.forName(targetEncoding).newEncoder(); 1503 out = new byte[(int)(encoder.maxBytesPerChar() * 2 + 0.5)]; 1504 inBuf = CharBuffer.wrap(in); 1505 outBuf = ByteBuffer.wrap(out); 1506 } catch (IllegalCharsetNameException 1507 | UnsupportedCharsetException 1508 | UnsupportedOperationException e) { 1509 throw new IOException(e.toString()); 1510 } 1511 1512 String sEoln = nativeEOLNs.get(lFormat); 1513 if (sEoln != null) { 1514 eoln = sEoln.toCharArray(); 1515 } 1516 1517 // A hope and a prayer that this works generically. This will 1518 // definitely work on Win32. 1519 Integer terminators = nativeTerminators.get(lFormat); 1520 if (terminators != null) { 1521 numTerminators = terminators; 1522 } 1523 } 1524 1525 private int readChar() throws IOException { 1526 int c = wrapped.read(); 1527 1528 if (c == -1) { // -1 is EOS 1529 eos = true; 1530 return -1; 1531 } 1532 1533 // "c == 0" is not quite correct, but good enough on Windows. 1534 if (numTerminators > 0 && c == 0) { 1535 eos = true; 1536 return -1; 1537 } else if (eoln != null && matchCharArray(eoln, c)) { 1538 c = '\n' & 0xFFFF; 1539 } 1540 1541 return c; 1542 } 1543 1544 public int read() throws IOException { 1545 if (eos) { 1546 return -1; 1547 } 1548 1549 if (index >= limit) { 1550 // deal with supplementary characters 1551 int c = readChar(); 1552 if (c == -1) { 1553 return -1; 1554 } 1555 1556 in[0] = (char) c; 1557 in[1] = 0; 1558 inBuf.limit(1); 1559 if (Character.isHighSurrogate((char) c)) { 1560 c = readChar(); 1561 if (c != -1) { 1562 in[1] = (char) c; 1563 inBuf.limit(2); 1564 } 1565 } 1566 1567 inBuf.rewind(); 1568 outBuf.limit(out.length).rewind(); 1569 encoder.encode(inBuf, outBuf, false); 1570 outBuf.flip(); 1571 limit = outBuf.limit(); 1572 1573 index = 0; 1574 1575 return read(); 1576 } else { 1577 return out[index++] & 0xFF; 1578 } 1579 } 1580 1581 public int available() throws IOException { 1582 return ((eos) ? 0 : (limit - index)); 1583 } 1584 1585 public void close() throws IOException { 1586 wrapped.close(); 1587 } 1588 1589 /** 1590 * Checks to see if the next array.length characters in wrapped 1591 * match array. The first character is provided as c. Subsequent 1592 * characters are read from wrapped itself. When this method returns, 1593 * the wrapped index may be different from what it was when this 1594 * method was called. 1595 */ 1596 private boolean matchCharArray(char[] array, int c) 1597 throws IOException 1598 { 1599 wrapped.mark(array.length); // BufferedReader supports mark 1600 1601 int count = 0; 1602 if ((char)c == array[0]) { 1603 for (count = 1; count < array.length; count++) { 1604 c = wrapped.read(); 1605 if (c == -1 || ((char)c) != array[count]) { 1606 break; 1607 } 1608 } 1609 } 1610 1611 if (count == array.length) { 1612 return true; 1613 } else { 1614 wrapped.reset(); 1615 return false; 1616 } 1617 } 1618 } 1619 1620 /** 1621 * Decodes a byte array into a set of String filenames. 1622 */ 1623 protected abstract String[] dragQueryFile(byte[] bytes); 1624 1625 /** 1626 * Decodes URIs from either a byte array or a stream. 1627 */ 1628 protected URI[] dragQueryURIs(InputStream stream, 1629 long format, 1630 Transferable localeTransferable) 1631 throws IOException 1632 { 1633 throw new IOException( 1634 new UnsupportedOperationException("not implemented on this platform")); 1635 } 1636 1637 /** 1638 * Translates either a byte array or an input stream which contain 1639 * platform-specific image data in the given format into an Image. 1640 */ 1641 1642 1643 protected abstract Image platformImageBytesToImage( 1644 byte[] bytes,long format) throws IOException; 1645 1646 /** 1647 * Translates either a byte array or an input stream which contain 1648 * an image data in the given standard format into an Image. 1649 * 1650 * @param mimeType image MIME type, such as: image/png, image/jpeg, image/gif 1651 */ 1652 protected Image standardImageBytesToImage( 1653 byte[] bytes, String mimeType) throws IOException 1654 { 1655 1656 Iterator<ImageReader> readerIterator = ImageIO.getImageReadersByMIMEType(mimeType); 1657 1658 if (!readerIterator.hasNext()) { 1659 throw new IOException("No registered service provider can decode " + 1660 " an image from " + mimeType); 1661 } 1662 1663 IOException ioe = null; 1664 1665 while (readerIterator.hasNext()) { 1666 ImageReader imageReader = readerIterator.next(); 1667 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) { 1668 try (ImageInputStream imageInputStream = ImageIO.createImageInputStream(bais)) { 1669 ImageReadParam param = imageReader.getDefaultReadParam(); 1670 imageReader.setInput(imageInputStream, true, true); 1671 BufferedImage bufferedImage = imageReader.read(imageReader.getMinIndex(), param); 1672 if (bufferedImage != null) { 1673 return bufferedImage; 1674 } 1675 } finally { 1676 imageReader.dispose(); 1677 } 1678 } catch (IOException e) { 1679 ioe = e; 1680 continue; 1681 } 1682 } 1683 1684 if (ioe == null) { 1685 ioe = new IOException("Registered service providers failed to decode" 1686 + " an image from " + mimeType); 1687 } 1688 1689 throw ioe; 1690 } 1691 1692 /** 1693 * Translates a Java Image into a byte array which contains platform- 1694 * specific image data in the given format. 1695 */ 1696 protected abstract byte[] imageToPlatformBytes(Image image, long format) 1697 throws IOException; 1698 1699 /** 1700 * Translates a Java Image into a byte array which contains 1701 * an image data in the given standard format. 1702 * 1703 * @param mimeType image MIME type, such as: image/png, image/jpeg 1704 */ 1705 protected byte[] imageToStandardBytes(Image image, String mimeType) 1706 throws IOException { 1707 IOException originalIOE = null; 1708 1709 Iterator<ImageWriter> writerIterator = ImageIO.getImageWritersByMIMEType(mimeType); 1710 1711 if (!writerIterator.hasNext()) { 1712 throw new IOException("No registered service provider can encode " + 1713 " an image to " + mimeType); 1714 } 1715 1716 if (image instanceof RenderedImage) { 1717 // Try to encode the original image. 1718 try { 1719 return imageToStandardBytesImpl((RenderedImage)image, mimeType); 1720 } catch (IOException ioe) { 1721 originalIOE = ioe; 1722 } 1723 } 1724 1725 // Retry with a BufferedImage. 1726 int width = 0; 1727 int height = 0; 1728 if (image instanceof ToolkitImage) { 1729 ImageRepresentation ir = ((ToolkitImage)image).getImageRep(); 1730 ir.reconstruct(ImageObserver.ALLBITS); 1731 width = ir.getWidth(); 1732 height = ir.getHeight(); 1733 } else { 1734 width = image.getWidth(null); 1735 height = image.getHeight(null); 1736 } 1737 1738 ColorModel model = ColorModel.getRGBdefault(); 1739 WritableRaster raster = 1740 model.createCompatibleWritableRaster(width, height); 1741 1742 BufferedImage bufferedImage = 1743 new BufferedImage(model, raster, model.isAlphaPremultiplied(), 1744 null); 1745 1746 Graphics g = bufferedImage.getGraphics(); 1747 try { 1748 g.drawImage(image, 0, 0, width, height, null); 1749 } finally { 1750 g.dispose(); 1751 } 1752 1753 try { 1754 return imageToStandardBytesImpl(bufferedImage, mimeType); 1755 } catch (IOException ioe) { 1756 if (originalIOE != null) { 1757 throw originalIOE; 1758 } else { 1759 throw ioe; 1760 } 1761 } 1762 } 1763 1764 byte[] imageToStandardBytesImpl(RenderedImage renderedImage, 1765 String mimeType) 1766 throws IOException { 1767 1768 Iterator<ImageWriter> writerIterator = ImageIO.getImageWritersByMIMEType(mimeType); 1769 1770 ImageTypeSpecifier typeSpecifier = 1771 new ImageTypeSpecifier(renderedImage); 1772 1773 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 1774 IOException ioe = null; 1775 1776 while (writerIterator.hasNext()) { 1777 ImageWriter imageWriter = writerIterator.next(); 1778 ImageWriterSpi writerSpi = imageWriter.getOriginatingProvider(); 1779 1780 if (!writerSpi.canEncodeImage(typeSpecifier)) { 1781 continue; 1782 } 1783 1784 try { 1785 try (ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(baos)) { 1786 imageWriter.setOutput(imageOutputStream); 1787 imageWriter.write(renderedImage); 1788 imageOutputStream.flush(); 1789 } 1790 } catch (IOException e) { 1791 imageWriter.dispose(); 1792 baos.reset(); 1793 ioe = e; 1794 continue; 1795 } 1796 1797 imageWriter.dispose(); 1798 baos.close(); 1799 return baos.toByteArray(); 1800 } 1801 1802 baos.close(); 1803 1804 if (ioe == null) { 1805 ioe = new IOException("Registered service providers failed to encode " 1806 + renderedImage + " to " + mimeType); 1807 } 1808 1809 throw ioe; 1810 } 1811 1812 /** 1813 * Concatenates the data represented by two objects. Objects can be either 1814 * byte arrays or instances of <code>InputStream</code>. If both arguments 1815 * are byte arrays byte array will be returned. Otherwise an 1816 * <code>InputStream</code> will be returned. 1817 * <p> 1818 * Currently is only called from native code to prepend palette data to 1819 * platform-specific image data during image transfer on Win32. 1820 * 1821 * @param obj1 the first object to be concatenated. 1822 * @param obj2 the second object to be concatenated. 1823 * @return a byte array or an <code>InputStream</code> which represents 1824 * a logical concatenation of the two arguments. 1825 * @throws NullPointerException is either of the arguments is 1826 * <code>null</code> 1827 * @throws ClassCastException is either of the arguments is 1828 * neither byte array nor an instance of <code>InputStream</code>. 1829 */ 1830 private Object concatData(Object obj1, Object obj2) { 1831 InputStream str1 = null; 1832 InputStream str2 = null; 1833 1834 if (obj1 instanceof byte[]) { 1835 byte[] arr1 = (byte[])obj1; 1836 if (obj2 instanceof byte[]) { 1837 byte[] arr2 = (byte[])obj2; 1838 byte[] ret = new byte[arr1.length + arr2.length]; 1839 System.arraycopy(arr1, 0, ret, 0, arr1.length); 1840 System.arraycopy(arr2, 0, ret, arr1.length, arr2.length); 1841 return ret; 1842 } else { 1843 str1 = new ByteArrayInputStream(arr1); 1844 str2 = (InputStream)obj2; 1845 } 1846 } else { 1847 str1 = (InputStream)obj1; 1848 if (obj2 instanceof byte[]) { 1849 str2 = new ByteArrayInputStream((byte[])obj2); 1850 } else { 1851 str2 = (InputStream)obj2; 1852 } 1853 } 1854 1855 return new SequenceInputStream(str1, str2); 1856 } 1857 1858 public byte[] convertData(final Object source, 1859 final Transferable contents, 1860 final long format, 1861 final Map<Long, DataFlavor> formatMap, 1862 final boolean isToolkitThread) 1863 throws IOException 1864 { 1865 byte[] ret = null; 1866 1867 /* 1868 * If the current thread is the Toolkit thread we should post a 1869 * Runnable to the event dispatch thread associated with source Object, 1870 * since translateTransferable() calls Transferable.getTransferData() 1871 * that may contain client code. 1872 */ 1873 if (isToolkitThread) try { 1874 final Stack<byte[]> stack = new Stack<>(); 1875 final Runnable dataConverter = new Runnable() { 1876 // Guard against multiple executions. 1877 private boolean done = false; 1878 public void run() { 1879 if (done) { 1880 return; 1881 } 1882 byte[] data = null; 1883 try { 1884 DataFlavor flavor = formatMap.get(format); 1885 if (flavor != null) { 1886 data = translateTransferable(contents, flavor, format); 1887 } 1888 } catch (Exception e) { 1889 e.printStackTrace(); 1890 data = null; 1891 } 1892 try { 1893 getToolkitThreadBlockedHandler().lock(); 1894 stack.push(data); 1895 getToolkitThreadBlockedHandler().exit(); 1896 } finally { 1897 getToolkitThreadBlockedHandler().unlock(); 1898 done = true; 1899 } 1900 } 1901 }; 1902 1903 final AppContext appContext = SunToolkit.targetToAppContext(source); 1904 1905 getToolkitThreadBlockedHandler().lock(); 1906 1907 if (appContext != null) { 1908 appContext.put(DATA_CONVERTER_KEY, dataConverter); 1909 } 1910 1911 SunToolkit.executeOnEventHandlerThread(source, dataConverter); 1912 1913 while (stack.empty()) { 1914 getToolkitThreadBlockedHandler().enter(); 1915 } 1916 1917 if (appContext != null) { 1918 appContext.remove(DATA_CONVERTER_KEY); 1919 } 1920 1921 ret = stack.pop(); 1922 } finally { 1923 getToolkitThreadBlockedHandler().unlock(); 1924 } else { 1925 DataFlavor flavor = formatMap.get(format); 1926 if (flavor != null) { 1927 ret = translateTransferable(contents, flavor, format); 1928 } 1929 } 1930 1931 return ret; 1932 } 1933 1934 public void processDataConversionRequests() { 1935 if (EventQueue.isDispatchThread()) { 1936 AppContext appContext = AppContext.getAppContext(); 1937 getToolkitThreadBlockedHandler().lock(); 1938 try { 1939 Runnable dataConverter = 1940 (Runnable)appContext.get(DATA_CONVERTER_KEY); 1941 if (dataConverter != null) { 1942 dataConverter.run(); 1943 appContext.remove(DATA_CONVERTER_KEY); 1944 } 1945 } finally { 1946 getToolkitThreadBlockedHandler().unlock(); 1947 } 1948 } 1949 } 1950 1951 public abstract ToolkitThreadBlockedHandler getToolkitThreadBlockedHandler(); 1952 1953 /** 1954 * Helper function to reduce a Map with Long keys to a long array. 1955 * <p> 1956 * The map keys are sorted according to the native formats preference 1957 * order. 1958 */ 1959 public static long[] keysToLongArray(SortedMap<Long, ?> map) { 1960 Set<Long> keySet = map.keySet(); 1961 long[] retval = new long[keySet.size()]; 1962 int i = 0; 1963 for (Iterator<Long> iter = keySet.iterator(); iter.hasNext(); i++) { 1964 retval[i] = iter.next(); 1965 } 1966 return retval; 1967 } 1968 1969 /** 1970 * Helper function to convert a Set of DataFlavors to a sorted array. 1971 * The array will be sorted according to <code>DataFlavorComparator</code>. 1972 */ 1973 public static DataFlavor[] setToSortedDataFlavorArray(Set<DataFlavor> flavorsSet) { 1974 DataFlavor[] flavors = new DataFlavor[flavorsSet.size()]; 1975 flavorsSet.toArray(flavors); 1976 final Comparator<DataFlavor> comparator = DataFlavorUtil.getDataFlavorComparator().reversed(); 1977 Arrays.sort(flavors, comparator); 1978 return flavors; 1979 } 1980 1981 /** 1982 * Helper function to convert an InputStream to a byte[] array. 1983 */ 1984 protected static byte[] inputStreamToByteArray(InputStream str) 1985 throws IOException 1986 { 1987 try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { 1988 int len = 0; 1989 byte[] buf = new byte[8192]; 1990 1991 while ((len = str.read(buf)) != -1) { 1992 baos.write(buf, 0, len); 1993 } 1994 1995 return baos.toByteArray(); 1996 } 1997 } 1998 1999 /** 2000 * Returns platform-specific mappings for the specified native. 2001 * If there are no platform-specific mappings for this native, the method 2002 * returns an empty <code>List</code>. 2003 */ 2004 public LinkedHashSet<DataFlavor> getPlatformMappingsForNative(String nat) { 2005 return new LinkedHashSet<>(); 2006 } 2007 2008 /** 2009 * Returns platform-specific mappings for the specified flavor. 2010 * If there are no platform-specific mappings for this flavor, the method 2011 * returns an empty <code>List</code>. 2012 */ 2013 public LinkedHashSet<String> getPlatformMappingsForFlavor(DataFlavor df) { 2014 return new LinkedHashSet<>(); 2015 } 2016 }