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 try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { 931 try (InputStream is = (InputStream)obj) { 932 boolean eof = false; 933 int avail = is.available(); 934 byte[] tmp = new byte[avail > 8192 ? avail : 8192]; 935 do { 936 int aValue; 937 if (!(eof = (aValue = is.read(tmp, 0, tmp.length)) == -1)) { 938 bos.write(tmp, 0, aValue); 939 } 940 } while (!eof); 941 } 942 943 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 944 byte[] bytes = bos.toByteArray(); 945 String sourceEncoding = DataFlavorUtil.getTextCharset(flavor); 946 return translateTransferableString( 947 new String(bytes, sourceEncoding), 948 format); 949 } 950 theByteArray = bos.toByteArray(); 951 } 952 953 954 // Source data is an RMI object 955 } else if (flavor.isRepresentationClassRemote()) { 956 theByteArray = convertObjectToBytes(DataFlavorUtil.RMI.newMarshalledObject(obj)); 957 958 // Source data is Serializable 959 } else if (flavor.isRepresentationClassSerializable()) { 960 961 theByteArray = convertObjectToBytes(obj); 962 963 } else { 964 throw new IOException("data translation failed"); 965 } 966 967 968 969 return theByteArray; 970 } 971 972 private static byte[] convertObjectToBytes(Object object) throws IOException { 973 try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); 974 ObjectOutputStream oos = new ObjectOutputStream(bos)) 975 { 976 oos.writeObject(object); 977 return bos.toByteArray(); 978 } 979 } 980 981 protected abstract ByteArrayOutputStream convertFileListToBytes(ArrayList<String> fileList) throws IOException; 982 983 private String removeSuspectedData(DataFlavor flavor, final Transferable contents, final String str) 984 throws IOException 985 { 986 if (null == System.getSecurityManager() 987 || !flavor.isMimeTypeEqual("text/uri-list")) 988 { 989 return str; 990 } 991 992 final ProtectionDomain userProtectionDomain = getUserProtectionDomain(contents); 993 994 try { 995 return AccessController.doPrivileged((PrivilegedExceptionAction<String>) () -> { 996 997 StringBuilder allowedFiles = new StringBuilder(str.length()); 998 String [] uriArray = str.split("(\\s)+"); 999 1000 for (String fileName : uriArray) 1001 { 1002 File file = new File(fileName); 1003 if (file.exists() && 1004 !(isFileInWebstartedCache(file) || 1005 isForbiddenToRead(file, userProtectionDomain))) 1006 { 1007 if (0 != allowedFiles.length()) 1008 { 1009 allowedFiles.append("\\r\\n"); 1010 } 1011 1012 allowedFiles.append(fileName); 1013 } 1014 } 1015 1016 return allowedFiles.toString(); 1017 }); 1018 } catch (PrivilegedActionException pae) { 1019 throw new IOException(pae.getMessage(), pae); 1020 } 1021 } 1022 1023 private static ProtectionDomain getUserProtectionDomain(Transferable contents) { 1024 return contents.getClass().getProtectionDomain(); 1025 } 1026 1027 private boolean isForbiddenToRead (File file, ProtectionDomain protectionDomain) 1028 { 1029 if (null == protectionDomain) { 1030 return false; 1031 } 1032 try { 1033 FilePermission filePermission = 1034 new FilePermission(file.getCanonicalPath(), "read, delete"); 1035 if (protectionDomain.implies(filePermission)) { 1036 return false; 1037 } 1038 } catch (IOException e) {} 1039 1040 return true; 1041 } 1042 1043 private ArrayList<String> castToFiles(final List<?> files, 1044 final ProtectionDomain userProtectionDomain) throws IOException { 1045 try { 1046 return AccessController.doPrivileged((PrivilegedExceptionAction<ArrayList<String>>) () -> { 1047 ArrayList<String> fileList = new ArrayList<>(); 1048 for (Object fileObject : files) 1049 { 1050 File file = castToFile(fileObject); 1051 if (file != null && 1052 (null == System.getSecurityManager() || 1053 !(isFileInWebstartedCache(file) || 1054 isForbiddenToRead(file, userProtectionDomain)))) 1055 { 1056 fileList.add(file.getCanonicalPath()); 1057 } 1058 } 1059 return fileList; 1060 }); 1061 } catch (PrivilegedActionException pae) { 1062 throw new IOException(pae.getMessage()); 1063 } 1064 } 1065 1066 // It is important do not use user's successors 1067 // of File class. 1068 private File castToFile(Object fileObject) throws IOException { 1069 String filePath = null; 1070 if (fileObject instanceof File) { 1071 filePath = ((File)fileObject).getCanonicalPath(); 1072 } else if (fileObject instanceof String) { 1073 filePath = (String) fileObject; 1074 } else { 1075 return null; 1076 } 1077 return new File(filePath); 1078 } 1079 1080 private final static String[] DEPLOYMENT_CACHE_PROPERTIES = { 1081 "deployment.system.cachedir", 1082 "deployment.user.cachedir", 1083 "deployment.javaws.cachedir", 1084 "deployment.javapi.cachedir" 1085 }; 1086 1087 private final static ArrayList <File> deploymentCacheDirectoryList = new ArrayList<>(); 1088 1089 private static boolean isFileInWebstartedCache(File f) { 1090 1091 if (deploymentCacheDirectoryList.isEmpty()) { 1092 for (String cacheDirectoryProperty : DEPLOYMENT_CACHE_PROPERTIES) { 1093 String cacheDirectoryPath = System.getProperty(cacheDirectoryProperty); 1094 if (cacheDirectoryPath != null) { 1095 try { 1096 File cacheDirectory = (new File(cacheDirectoryPath)).getCanonicalFile(); 1097 if (cacheDirectory != null) { 1098 deploymentCacheDirectoryList.add(cacheDirectory); 1099 } 1100 } catch (IOException ioe) {} 1101 } 1102 } 1103 } 1104 1105 for (File deploymentCacheDirectory : deploymentCacheDirectoryList) { 1106 for (File dir = f; dir != null; dir = dir.getParentFile()) { 1107 if (dir.equals(deploymentCacheDirectory)) { 1108 return true; 1109 } 1110 } 1111 } 1112 1113 return false; 1114 } 1115 1116 1117 public Object translateBytes(byte[] bytes, DataFlavor flavor, 1118 long format, Transferable localeTransferable) 1119 throws IOException 1120 { 1121 1122 Object theObject = null; 1123 1124 // Source data is a file list. Use the dragQueryFile native function to 1125 // do most of the decoding. Then wrap File objects around the String 1126 // filenames and return a List. 1127 if (isFileFormat(format)) { 1128 if (!DataFlavor.javaFileListFlavor.equals(flavor)) { 1129 throw new IOException("data translation failed"); 1130 } 1131 String[] filenames = dragQueryFile(bytes); 1132 if (filenames == null) { 1133 return null; 1134 } 1135 1136 // Convert the strings to File objects 1137 File[] files = new File[filenames.length]; 1138 for (int i = 0; i < filenames.length; i++) { 1139 files[i] = new File(filenames[i]); 1140 } 1141 1142 // Turn the list of Files into a List and return 1143 theObject = Arrays.asList(files); 1144 1145 // Source data is a URI list. Convert to DataFlavor.javaFileListFlavor 1146 // where possible. 1147 } else if (isURIListFormat(format) 1148 && DataFlavor.javaFileListFlavor.equals(flavor)) { 1149 1150 try (ByteArrayInputStream str = new ByteArrayInputStream(bytes)) { 1151 1152 URI uris[] = dragQueryURIs(str, format, localeTransferable); 1153 if (uris == null) { 1154 return null; 1155 } 1156 List<File> files = new ArrayList<>(); 1157 for (URI uri : uris) { 1158 try { 1159 files.add(new File(uri)); 1160 } catch (IllegalArgumentException illegalArg) { 1161 // When converting from URIs to less generic files, 1162 // common practice (Wine, SWT) seems to be to 1163 // silently drop the URIs that aren't local files. 1164 } 1165 } 1166 theObject = files; 1167 } 1168 1169 // Target data is a String. Strip terminating NUL bytes. Decode bytes 1170 // into characters. Search-and-replace EOLN. 1171 } else if (String.class.equals(flavor.getRepresentationClass()) && 1172 DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1173 1174 theObject = translateBytesToString(bytes, format, localeTransferable); 1175 1176 // Target data is a Reader. Obtain data in InputStream format, encoded 1177 // as "Unicode" (utf-16be). Then use an InputStreamReader to decode 1178 // back to chars on demand. 1179 } else if (flavor.isRepresentationClassReader()) { 1180 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) { 1181 theObject = translateStream(bais, 1182 flavor, format, localeTransferable); 1183 } 1184 // Target data is a CharBuffer. Recur to obtain String and wrap. 1185 } else if (flavor.isRepresentationClassCharBuffer()) { 1186 if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) { 1187 throw new IOException("cannot transfer non-text data as CharBuffer"); 1188 } 1189 1190 CharBuffer buffer = CharBuffer.wrap( 1191 translateBytesToString(bytes,format, localeTransferable)); 1192 1193 theObject = constructFlavoredObject(buffer, flavor, CharBuffer.class); 1194 1195 // Target data is a char array. Recur to obtain String and convert to 1196 // char array. 1197 } else if (char[].class.equals(flavor.getRepresentationClass())) { 1198 if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) { 1199 throw new IOException 1200 ("cannot transfer non-text data as char array"); 1201 } 1202 1203 theObject = translateBytesToString( 1204 bytes, format, localeTransferable).toCharArray(); 1205 1206 // Target data is a ByteBuffer. For arbitrary flavors, just return 1207 // the raw bytes. For text flavors, convert to a String to strip 1208 // terminators and search-and-replace EOLN, then reencode according to 1209 // the requested flavor. 1210 } else if (flavor.isRepresentationClassByteBuffer()) { 1211 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1212 bytes = translateBytesToString( 1213 bytes, format, localeTransferable).getBytes( 1214 DataFlavorUtil.getTextCharset(flavor) 1215 ); 1216 } 1217 1218 ByteBuffer buffer = ByteBuffer.wrap(bytes); 1219 theObject = constructFlavoredObject(buffer, flavor, ByteBuffer.class); 1220 1221 // Target data is a byte array. For arbitrary flavors, just return 1222 // the raw bytes. For text flavors, convert to a String to strip 1223 // terminators and search-and-replace EOLN, then reencode according to 1224 // the requested flavor. 1225 } else if (byte[].class.equals(flavor.getRepresentationClass())) { 1226 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1227 theObject = translateBytesToString( 1228 bytes, format, localeTransferable 1229 ).getBytes(DataFlavorUtil.getTextCharset(flavor)); 1230 } else { 1231 theObject = bytes; 1232 } 1233 1234 // Target data is an InputStream. For arbitrary flavors, just return 1235 // the raw bytes. For text flavors, decode to strip terminators and 1236 // search-and-replace EOLN, then reencode according to the requested 1237 // flavor. 1238 } else if (flavor.isRepresentationClassInputStream()) { 1239 1240 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) { 1241 theObject = translateStream(bais, flavor, format, localeTransferable); 1242 } 1243 1244 } else if (flavor.isRepresentationClassRemote()) { 1245 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); 1246 ObjectInputStream ois = new ObjectInputStream(bais)) { 1247 1248 theObject = DataFlavorUtil.RMI.getMarshalledObject(ois.readObject()); 1249 } catch (Exception e) { 1250 throw new IOException(e.getMessage()); 1251 } 1252 1253 // Target data is Serializable 1254 } else if (flavor.isRepresentationClassSerializable()) { 1255 1256 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) { 1257 theObject = translateStream(bais, flavor, format, localeTransferable); 1258 } 1259 1260 // Target data is Image 1261 } else if (DataFlavor.imageFlavor.equals(flavor)) { 1262 if (!isImageFormat(format)) { 1263 throw new IOException("data translation failed"); 1264 } 1265 1266 theObject = platformImageBytesToImage(bytes, format); 1267 } 1268 1269 if (theObject == null) { 1270 throw new IOException("data translation failed"); 1271 } 1272 1273 return theObject; 1274 1275 } 1276 1277 /** 1278 * Primary translation function for translating 1279 * an InputStream into an Object, given a source format and a target 1280 * DataFlavor. 1281 */ 1282 @SuppressWarnings("deprecation") 1283 public Object translateStream(InputStream str, DataFlavor flavor, 1284 long format, Transferable localeTransferable) 1285 throws IOException 1286 { 1287 1288 Object theObject = null; 1289 // Source data is a URI list. Convert to DataFlavor.javaFileListFlavor 1290 // where possible. 1291 if (isURIListFormat(format) 1292 && DataFlavor.javaFileListFlavor.equals(flavor)) 1293 { 1294 1295 URI uris[] = dragQueryURIs(str, format, localeTransferable); 1296 if (uris == null) { 1297 return null; 1298 } 1299 List<File> files = new ArrayList<>(); 1300 for (URI uri : uris) { 1301 try { 1302 files.add(new File(uri)); 1303 } catch (IllegalArgumentException illegalArg) { 1304 // When converting from URIs to less generic files, 1305 // common practice (Wine, SWT) seems to be to 1306 // silently drop the URIs that aren't local files. 1307 } 1308 } 1309 theObject = files; 1310 1311 // Target data is a String. Strip terminating NUL bytes. Decode bytes 1312 // into characters. Search-and-replace EOLN. 1313 } else if (String.class.equals(flavor.getRepresentationClass()) && 1314 DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1315 1316 return translateBytesToString(inputStreamToByteArray(str), 1317 format, localeTransferable); 1318 1319 // Special hack to maintain backwards-compatibility with the brokenness 1320 // of StringSelection. Return a StringReader instead of an InputStream. 1321 // Recur to obtain String and encapsulate. 1322 } else if (DataFlavor.plainTextFlavor.equals(flavor)) { 1323 theObject = new StringReader(translateBytesToString( 1324 inputStreamToByteArray(str), 1325 format, localeTransferable)); 1326 1327 // Target data is an InputStream. For arbitrary flavors, just return 1328 // the raw bytes. For text flavors, decode to strip terminators and 1329 // search-and-replace EOLN, then reencode according to the requested 1330 // flavor. 1331 } else if (flavor.isRepresentationClassInputStream()) { 1332 theObject = translateStreamToInputStream(str, flavor, format, 1333 localeTransferable); 1334 1335 // Target data is a Reader. Obtain data in InputStream format, encoded 1336 // as "Unicode" (utf-16be). Then use an InputStreamReader to decode 1337 // back to chars on demand. 1338 } else if (flavor.isRepresentationClassReader()) { 1339 if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) { 1340 throw new IOException 1341 ("cannot transfer non-text data as Reader"); 1342 } 1343 1344 InputStream is = (InputStream)translateStreamToInputStream( 1345 str, DataFlavor.plainTextFlavor, 1346 format, localeTransferable); 1347 1348 String unicode = DataFlavorUtil.getTextCharset(DataFlavor.plainTextFlavor); 1349 1350 Reader reader = new InputStreamReader(is, unicode); 1351 1352 theObject = constructFlavoredObject(reader, flavor, Reader.class); 1353 // Target data is a byte array 1354 } else if (byte[].class.equals(flavor.getRepresentationClass())) { 1355 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1356 theObject = translateBytesToString(inputStreamToByteArray(str), format, localeTransferable) 1357 .getBytes(DataFlavorUtil.getTextCharset(flavor)); 1358 } else { 1359 theObject = inputStreamToByteArray(str); 1360 } 1361 // Target data is an RMI object 1362 } else if (flavor.isRepresentationClassRemote()) { 1363 try (ObjectInputStream ois = new ObjectInputStream(str)) { 1364 theObject = DataFlavorUtil.RMI.getMarshalledObject(ois.readObject()); 1365 } catch (Exception e) { 1366 throw new IOException(e.getMessage()); 1367 } 1368 1369 // Target data is Serializable 1370 } else if (flavor.isRepresentationClassSerializable()) { 1371 try (ObjectInputStream ois = 1372 new ObjectInputStream(str)) 1373 { 1374 theObject = ois.readObject(); 1375 } catch (Exception e) { 1376 throw new IOException(e.getMessage()); 1377 } 1378 // Target data is Image 1379 } else if (DataFlavor.imageFlavor.equals(flavor)) { 1380 if (!isImageFormat(format)) { 1381 throw new IOException("data translation failed"); 1382 } 1383 theObject = platformImageBytesToImage(inputStreamToByteArray(str), format); 1384 } 1385 1386 if (theObject == null) { 1387 throw new IOException("data translation failed"); 1388 } 1389 1390 return theObject; 1391 1392 } 1393 1394 /** 1395 * For arbitrary flavors, just use the raw InputStream. For text flavors, 1396 * ReencodingInputStream will decode and reencode the InputStream on demand 1397 * so that we can strip terminators and search-and-replace EOLN. 1398 */ 1399 private Object translateStreamToInputStream 1400 (InputStream str, DataFlavor flavor, long format, 1401 Transferable localeTransferable) throws IOException 1402 { 1403 if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1404 str = new ReencodingInputStream 1405 (str, format, DataFlavorUtil.getTextCharset(flavor), 1406 localeTransferable); 1407 } 1408 1409 return constructFlavoredObject(str, flavor, InputStream.class); 1410 } 1411 1412 /** 1413 * We support representations which are exactly of the specified Class, 1414 * and also arbitrary Objects which have a constructor which takes an 1415 * instance of the Class as its sole parameter. 1416 */ 1417 private Object constructFlavoredObject(Object arg, DataFlavor flavor, 1418 Class<?> clazz) 1419 throws IOException 1420 { 1421 final Class<?> dfrc = flavor.getRepresentationClass(); 1422 1423 if (clazz.equals(dfrc)) { 1424 return arg; // simple case 1425 } else { 1426 Constructor<?>[] constructors; 1427 1428 try { 1429 constructors = AccessController.doPrivileged( 1430 (PrivilegedAction<Constructor<?>[]>) dfrc::getConstructors); 1431 } catch (SecurityException se) { 1432 throw new IOException(se.getMessage()); 1433 } 1434 1435 Constructor<?> constructor = Stream.of(constructors) 1436 .filter(c -> Modifier.isPublic(c.getModifiers())) 1437 .filter(c -> { 1438 Class<?>[] ptypes = c.getParameterTypes(); 1439 return ptypes != null 1440 && ptypes.length == 1 1441 && clazz.equals(ptypes[0]); 1442 }) 1443 .findFirst() 1444 .orElseThrow(() -> 1445 new IOException("can't find <init>(L"+ clazz + ";)V for class: " + dfrc.getName())); 1446 1447 try { 1448 return constructor.newInstance(arg); 1449 } catch (Exception e) { 1450 throw new IOException(e.getMessage()); 1451 } 1452 } 1453 } 1454 1455 /** 1456 * Used for decoding and reencoding an InputStream on demand so that we 1457 * can strip NUL terminators and perform EOLN search-and-replace. 1458 */ 1459 public class ReencodingInputStream extends InputStream { 1460 BufferedReader wrapped; 1461 final char[] in = new char[2]; 1462 byte[] out; 1463 1464 CharsetEncoder encoder; 1465 CharBuffer inBuf; 1466 ByteBuffer outBuf; 1467 1468 char[] eoln; 1469 int numTerminators; 1470 1471 boolean eos; 1472 int index, limit; 1473 1474 public ReencodingInputStream(InputStream bytestream, long format, 1475 String targetEncoding, 1476 Transferable localeTransferable) 1477 throws IOException 1478 { 1479 Long lFormat = format; 1480 1481 String sourceEncoding = getBestCharsetForTextFormat(format, localeTransferable); 1482 wrapped = new BufferedReader(new InputStreamReader(bytestream, sourceEncoding)); 1483 1484 if (targetEncoding == null) { 1485 // Throw NullPointerException for compatibility with the former 1486 // call to sun.io.CharToByteConverter.getConverter(null) 1487 // (Charset.forName(null) throws unspecified IllegalArgumentException 1488 // now; see 6228568) 1489 throw new NullPointerException("null target encoding"); 1490 } 1491 1492 try { 1493 encoder = Charset.forName(targetEncoding).newEncoder(); 1494 out = new byte[(int)(encoder.maxBytesPerChar() * 2 + 0.5)]; 1495 inBuf = CharBuffer.wrap(in); 1496 outBuf = ByteBuffer.wrap(out); 1497 } catch (IllegalCharsetNameException 1498 | UnsupportedCharsetException 1499 | UnsupportedOperationException e) { 1500 throw new IOException(e.toString()); 1501 } 1502 1503 String sEoln = nativeEOLNs.get(lFormat); 1504 if (sEoln != null) { 1505 eoln = sEoln.toCharArray(); 1506 } 1507 1508 // A hope and a prayer that this works generically. This will 1509 // definitely work on Win32. 1510 Integer terminators = nativeTerminators.get(lFormat); 1511 if (terminators != null) { 1512 numTerminators = terminators; 1513 } 1514 } 1515 1516 private int readChar() throws IOException { 1517 int c = wrapped.read(); 1518 1519 if (c == -1) { // -1 is EOS 1520 eos = true; 1521 return -1; 1522 } 1523 1524 // "c == 0" is not quite correct, but good enough on Windows. 1525 if (numTerminators > 0 && c == 0) { 1526 eos = true; 1527 return -1; 1528 } else if (eoln != null && matchCharArray(eoln, c)) { 1529 c = '\n' & 0xFFFF; 1530 } 1531 1532 return c; 1533 } 1534 1535 public int read() throws IOException { 1536 if (eos) { 1537 return -1; 1538 } 1539 1540 if (index >= limit) { 1541 // deal with supplementary characters 1542 int c = readChar(); 1543 if (c == -1) { 1544 return -1; 1545 } 1546 1547 in[0] = (char) c; 1548 in[1] = 0; 1549 inBuf.limit(1); 1550 if (Character.isHighSurrogate((char) c)) { 1551 c = readChar(); 1552 if (c != -1) { 1553 in[1] = (char) c; 1554 inBuf.limit(2); 1555 } 1556 } 1557 1558 inBuf.rewind(); 1559 outBuf.limit(out.length).rewind(); 1560 encoder.encode(inBuf, outBuf, false); 1561 outBuf.flip(); 1562 limit = outBuf.limit(); 1563 1564 index = 0; 1565 1566 return read(); 1567 } else { 1568 return out[index++] & 0xFF; 1569 } 1570 } 1571 1572 public int available() throws IOException { 1573 return ((eos) ? 0 : (limit - index)); 1574 } 1575 1576 public void close() throws IOException { 1577 wrapped.close(); 1578 } 1579 1580 /** 1581 * Checks to see if the next array.length characters in wrapped 1582 * match array. The first character is provided as c. Subsequent 1583 * characters are read from wrapped itself. When this method returns, 1584 * the wrapped index may be different from what it was when this 1585 * method was called. 1586 */ 1587 private boolean matchCharArray(char[] array, int c) 1588 throws IOException 1589 { 1590 wrapped.mark(array.length); // BufferedReader supports mark 1591 1592 int count = 0; 1593 if ((char)c == array[0]) { 1594 for (count = 1; count < array.length; count++) { 1595 c = wrapped.read(); 1596 if (c == -1 || ((char)c) != array[count]) { 1597 break; 1598 } 1599 } 1600 } 1601 1602 if (count == array.length) { 1603 return true; 1604 } else { 1605 wrapped.reset(); 1606 return false; 1607 } 1608 } 1609 } 1610 1611 /** 1612 * Decodes a byte array into a set of String filenames. 1613 */ 1614 protected abstract String[] dragQueryFile(byte[] bytes); 1615 1616 /** 1617 * Decodes URIs from either a byte array or a stream. 1618 */ 1619 protected URI[] dragQueryURIs(InputStream stream, 1620 long format, 1621 Transferable localeTransferable) 1622 throws IOException 1623 { 1624 throw new IOException( 1625 new UnsupportedOperationException("not implemented on this platform")); 1626 } 1627 1628 /** 1629 * Translates either a byte array or an input stream which contain 1630 * platform-specific image data in the given format into an Image. 1631 */ 1632 1633 1634 protected abstract Image platformImageBytesToImage( 1635 byte[] bytes,long format) throws IOException; 1636 1637 /** 1638 * Translates either a byte array or an input stream which contain 1639 * an image data in the given standard format into an Image. 1640 * 1641 * @param mimeType image MIME type, such as: image/png, image/jpeg, image/gif 1642 */ 1643 protected Image standardImageBytesToImage( 1644 byte[] bytes, String mimeType) throws IOException 1645 { 1646 1647 Iterator<ImageReader> readerIterator = ImageIO.getImageReadersByMIMEType(mimeType); 1648 1649 if (!readerIterator.hasNext()) { 1650 throw new IOException("No registered service provider can decode " + 1651 " an image from " + mimeType); 1652 } 1653 1654 IOException ioe = null; 1655 1656 while (readerIterator.hasNext()) { 1657 ImageReader imageReader = readerIterator.next(); 1658 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) { 1659 try (ImageInputStream imageInputStream = ImageIO.createImageInputStream(bais)) { 1660 ImageReadParam param = imageReader.getDefaultReadParam(); 1661 imageReader.setInput(imageInputStream, true, true); 1662 BufferedImage bufferedImage = imageReader.read(imageReader.getMinIndex(), param); 1663 if (bufferedImage != null) { 1664 return bufferedImage; 1665 } 1666 } finally { 1667 imageReader.dispose(); 1668 } 1669 } catch (IOException e) { 1670 ioe = e; 1671 continue; 1672 } 1673 } 1674 1675 if (ioe == null) { 1676 ioe = new IOException("Registered service providers failed to decode" 1677 + " an image from " + mimeType); 1678 } 1679 1680 throw ioe; 1681 } 1682 1683 /** 1684 * Translates a Java Image into a byte array which contains platform- 1685 * specific image data in the given format. 1686 */ 1687 protected abstract byte[] imageToPlatformBytes(Image image, long format) 1688 throws IOException; 1689 1690 /** 1691 * Translates a Java Image into a byte array which contains 1692 * an image data in the given standard format. 1693 * 1694 * @param mimeType image MIME type, such as: image/png, image/jpeg 1695 */ 1696 protected byte[] imageToStandardBytes(Image image, String mimeType) 1697 throws IOException { 1698 IOException originalIOE = null; 1699 1700 Iterator<ImageWriter> writerIterator = ImageIO.getImageWritersByMIMEType(mimeType); 1701 1702 if (!writerIterator.hasNext()) { 1703 throw new IOException("No registered service provider can encode " + 1704 " an image to " + mimeType); 1705 } 1706 1707 if (image instanceof RenderedImage) { 1708 // Try to encode the original image. 1709 try { 1710 return imageToStandardBytesImpl((RenderedImage)image, mimeType); 1711 } catch (IOException ioe) { 1712 originalIOE = ioe; 1713 } 1714 } 1715 1716 // Retry with a BufferedImage. 1717 int width = 0; 1718 int height = 0; 1719 if (image instanceof ToolkitImage) { 1720 ImageRepresentation ir = ((ToolkitImage)image).getImageRep(); 1721 ir.reconstruct(ImageObserver.ALLBITS); 1722 width = ir.getWidth(); 1723 height = ir.getHeight(); 1724 } else { 1725 width = image.getWidth(null); 1726 height = image.getHeight(null); 1727 } 1728 1729 ColorModel model = ColorModel.getRGBdefault(); 1730 WritableRaster raster = 1731 model.createCompatibleWritableRaster(width, height); 1732 1733 BufferedImage bufferedImage = 1734 new BufferedImage(model, raster, model.isAlphaPremultiplied(), 1735 null); 1736 1737 Graphics g = bufferedImage.getGraphics(); 1738 try { 1739 g.drawImage(image, 0, 0, width, height, null); 1740 } finally { 1741 g.dispose(); 1742 } 1743 1744 try { 1745 return imageToStandardBytesImpl(bufferedImage, mimeType); 1746 } catch (IOException ioe) { 1747 if (originalIOE != null) { 1748 throw originalIOE; 1749 } else { 1750 throw ioe; 1751 } 1752 } 1753 } 1754 1755 byte[] imageToStandardBytesImpl(RenderedImage renderedImage, 1756 String mimeType) 1757 throws IOException { 1758 1759 Iterator<ImageWriter> writerIterator = ImageIO.getImageWritersByMIMEType(mimeType); 1760 1761 ImageTypeSpecifier typeSpecifier = 1762 new ImageTypeSpecifier(renderedImage); 1763 1764 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 1765 IOException ioe = null; 1766 1767 while (writerIterator.hasNext()) { 1768 ImageWriter imageWriter = writerIterator.next(); 1769 ImageWriterSpi writerSpi = imageWriter.getOriginatingProvider(); 1770 1771 if (!writerSpi.canEncodeImage(typeSpecifier)) { 1772 continue; 1773 } 1774 1775 try { 1776 try (ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(baos)) { 1777 imageWriter.setOutput(imageOutputStream); 1778 imageWriter.write(renderedImage); 1779 imageOutputStream.flush(); 1780 } 1781 } catch (IOException e) { 1782 imageWriter.dispose(); 1783 baos.reset(); 1784 ioe = e; 1785 continue; 1786 } 1787 1788 imageWriter.dispose(); 1789 baos.close(); 1790 return baos.toByteArray(); 1791 } 1792 1793 baos.close(); 1794 1795 if (ioe == null) { 1796 ioe = new IOException("Registered service providers failed to encode " 1797 + renderedImage + " to " + mimeType); 1798 } 1799 1800 throw ioe; 1801 } 1802 1803 /** 1804 * Concatenates the data represented by two objects. Objects can be either 1805 * byte arrays or instances of <code>InputStream</code>. If both arguments 1806 * are byte arrays byte array will be returned. Otherwise an 1807 * <code>InputStream</code> will be returned. 1808 * <p> 1809 * Currently is only called from native code to prepend palette data to 1810 * platform-specific image data during image transfer on Win32. 1811 * 1812 * @param obj1 the first object to be concatenated. 1813 * @param obj2 the second object to be concatenated. 1814 * @return a byte array or an <code>InputStream</code> which represents 1815 * a logical concatenation of the two arguments. 1816 * @throws NullPointerException is either of the arguments is 1817 * <code>null</code> 1818 * @throws ClassCastException is either of the arguments is 1819 * neither byte array nor an instance of <code>InputStream</code>. 1820 */ 1821 private Object concatData(Object obj1, Object obj2) { 1822 InputStream str1 = null; 1823 InputStream str2 = null; 1824 1825 if (obj1 instanceof byte[]) { 1826 byte[] arr1 = (byte[])obj1; 1827 if (obj2 instanceof byte[]) { 1828 byte[] arr2 = (byte[])obj2; 1829 byte[] ret = new byte[arr1.length + arr2.length]; 1830 System.arraycopy(arr1, 0, ret, 0, arr1.length); 1831 System.arraycopy(arr2, 0, ret, arr1.length, arr2.length); 1832 return ret; 1833 } else { 1834 str1 = new ByteArrayInputStream(arr1); 1835 str2 = (InputStream)obj2; 1836 } 1837 } else { 1838 str1 = (InputStream)obj1; 1839 if (obj2 instanceof byte[]) { 1840 str2 = new ByteArrayInputStream((byte[])obj2); 1841 } else { 1842 str2 = (InputStream)obj2; 1843 } 1844 } 1845 1846 return new SequenceInputStream(str1, str2); 1847 } 1848 1849 public byte[] convertData(final Object source, 1850 final Transferable contents, 1851 final long format, 1852 final Map<Long, DataFlavor> formatMap, 1853 final boolean isToolkitThread) 1854 throws IOException 1855 { 1856 byte[] ret = null; 1857 1858 /* 1859 * If the current thread is the Toolkit thread we should post a 1860 * Runnable to the event dispatch thread associated with source Object, 1861 * since translateTransferable() calls Transferable.getTransferData() 1862 * that may contain client code. 1863 */ 1864 if (isToolkitThread) try { 1865 final Stack<byte[]> stack = new Stack<>(); 1866 final Runnable dataConverter = new Runnable() { 1867 // Guard against multiple executions. 1868 private boolean done = false; 1869 public void run() { 1870 if (done) { 1871 return; 1872 } 1873 byte[] data = null; 1874 try { 1875 DataFlavor flavor = formatMap.get(format); 1876 if (flavor != null) { 1877 data = translateTransferable(contents, flavor, format); 1878 } 1879 } catch (Exception e) { 1880 e.printStackTrace(); 1881 data = null; 1882 } 1883 try { 1884 getToolkitThreadBlockedHandler().lock(); 1885 stack.push(data); 1886 getToolkitThreadBlockedHandler().exit(); 1887 } finally { 1888 getToolkitThreadBlockedHandler().unlock(); 1889 done = true; 1890 } 1891 } 1892 }; 1893 1894 final AppContext appContext = SunToolkit.targetToAppContext(source); 1895 1896 getToolkitThreadBlockedHandler().lock(); 1897 1898 if (appContext != null) { 1899 appContext.put(DATA_CONVERTER_KEY, dataConverter); 1900 } 1901 1902 SunToolkit.executeOnEventHandlerThread(source, dataConverter); 1903 1904 while (stack.empty()) { 1905 getToolkitThreadBlockedHandler().enter(); 1906 } 1907 1908 if (appContext != null) { 1909 appContext.remove(DATA_CONVERTER_KEY); 1910 } 1911 1912 ret = stack.pop(); 1913 } finally { 1914 getToolkitThreadBlockedHandler().unlock(); 1915 } else { 1916 DataFlavor flavor = formatMap.get(format); 1917 if (flavor != null) { 1918 ret = translateTransferable(contents, flavor, format); 1919 } 1920 } 1921 1922 return ret; 1923 } 1924 1925 public void processDataConversionRequests() { 1926 if (EventQueue.isDispatchThread()) { 1927 AppContext appContext = AppContext.getAppContext(); 1928 getToolkitThreadBlockedHandler().lock(); 1929 try { 1930 Runnable dataConverter = 1931 (Runnable)appContext.get(DATA_CONVERTER_KEY); 1932 if (dataConverter != null) { 1933 dataConverter.run(); 1934 appContext.remove(DATA_CONVERTER_KEY); 1935 } 1936 } finally { 1937 getToolkitThreadBlockedHandler().unlock(); 1938 } 1939 } 1940 } 1941 1942 public abstract ToolkitThreadBlockedHandler getToolkitThreadBlockedHandler(); 1943 1944 /** 1945 * Helper function to reduce a Map with Long keys to a long array. 1946 * <p> 1947 * The map keys are sorted according to the native formats preference 1948 * order. 1949 */ 1950 public static long[] keysToLongArray(SortedMap<Long, ?> map) { 1951 Set<Long> keySet = map.keySet(); 1952 long[] retval = new long[keySet.size()]; 1953 int i = 0; 1954 for (Iterator<Long> iter = keySet.iterator(); iter.hasNext(); i++) { 1955 retval[i] = iter.next(); 1956 } 1957 return retval; 1958 } 1959 1960 /** 1961 * Helper function to convert a Set of DataFlavors to a sorted array. 1962 * The array will be sorted according to <code>DataFlavorComparator</code>. 1963 */ 1964 public static DataFlavor[] setToSortedDataFlavorArray(Set<DataFlavor> flavorsSet) { 1965 DataFlavor[] flavors = new DataFlavor[flavorsSet.size()]; 1966 flavorsSet.toArray(flavors); 1967 final Comparator<DataFlavor> comparator = DataFlavorUtil.getDataFlavorComparator().reversed(); 1968 Arrays.sort(flavors, comparator); 1969 return flavors; 1970 } 1971 1972 /** 1973 * Helper function to convert an InputStream to a byte[] array. 1974 */ 1975 protected static byte[] inputStreamToByteArray(InputStream str) 1976 throws IOException 1977 { 1978 try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { 1979 int len = 0; 1980 byte[] buf = new byte[8192]; 1981 1982 while ((len = str.read(buf)) != -1) { 1983 baos.write(buf, 0, len); 1984 } 1985 1986 return baos.toByteArray(); 1987 } 1988 } 1989 1990 /** 1991 * Returns platform-specific mappings for the specified native. 1992 * If there are no platform-specific mappings for this native, the method 1993 * returns an empty <code>List</code>. 1994 */ 1995 public LinkedHashSet<DataFlavor> getPlatformMappingsForNative(String nat) { 1996 return new LinkedHashSet<>(); 1997 } 1998 1999 /** 2000 * Returns platform-specific mappings for the specified flavor. 2001 * If there are no platform-specific mappings for this flavor, the method 2002 * returns an empty <code>List</code>. 2003 */ 2004 public LinkedHashSet<String> getPlatformMappingsForFlavor(DataFlavor df) { 2005 return new LinkedHashSet<>(); 2006 } 2007 }