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.InvocationTargetException; 65 import java.lang.reflect.Method; 66 import java.lang.reflect.Modifier; 67 68 import java.security.AccessController; 69 import java.security.PrivilegedAction; 70 import java.security.PrivilegedActionException; 71 import java.security.PrivilegedExceptionAction; 72 import java.security.ProtectionDomain; 73 74 import java.util.AbstractMap; 75 import java.util.ArrayList; 76 import java.util.Arrays; 77 import java.util.Collections; 78 import java.util.Comparator; 79 import java.util.HashMap; 80 import java.util.HashSet; 81 import java.util.Iterator; 82 import java.util.List; 83 import java.util.Map; 84 import java.util.SortedMap; 85 import java.util.SortedSet; 86 import java.util.Set; 87 import java.util.Stack; 88 import java.util.TreeMap; 89 import java.util.TreeSet; 90 91 import sun.util.logging.PlatformLogger; 92 93 import sun.awt.AppContext; 94 import sun.awt.SunToolkit; 95 96 import java.awt.image.BufferedImage; 97 import java.awt.image.ImageObserver; 98 import java.awt.image.RenderedImage; 99 import java.awt.image.WritableRaster; 100 import java.awt.image.ColorModel; 101 102 import javax.imageio.ImageIO; 103 import javax.imageio.ImageReader; 104 import javax.imageio.ImageReadParam; 105 import javax.imageio.ImageWriter; 106 import javax.imageio.ImageTypeSpecifier; 107 108 import javax.imageio.spi.ImageWriterSpi; 109 110 import javax.imageio.stream.ImageInputStream; 111 import javax.imageio.stream.ImageOutputStream; 112 113 import sun.awt.image.ImageRepresentation; 114 import sun.awt.image.ToolkitImage; 115 116 import java.io.FilePermission; 117 import java.util.stream.Stream; 118 119 120 /** 121 * Provides a set of functions to be shared among the DataFlavor class and 122 * platform-specific data transfer implementations. 123 * 124 * The concept of "flavors" and "natives" is extended to include "formats", 125 * which are the numeric values Win32 and X11 use to express particular data 126 * types. Like FlavorMap, which provides getNativesForFlavors(DataFlavor[]) and 127 * getFlavorsForNatives(String[]) functions, DataTransferer provides a set 128 * of getFormatsFor(Transferable|Flavor|Flavors) and 129 * getFlavorsFor(Format|Formats) functions. 130 * 131 * Also provided are functions for translating a Transferable into a byte 132 * array, given a source DataFlavor and a target format, and for translating 133 * a byte array or InputStream into an Object, given a source format and 134 * a target DataFlavor. 135 * 136 * @author David Mendenhall 137 * @author Danila Sinopalnikov 138 * 139 * @since 1.3.1 140 */ 141 public abstract class DataTransferer { 142 /** 143 * The <code>DataFlavor</code> representing a Java text encoding String 144 * encoded in UTF-8, where 145 * <pre> 146 * representationClass = [B 147 * mimeType = "application/x-java-text-encoding" 148 * </pre> 149 */ 150 public static final DataFlavor javaTextEncodingFlavor; 151 152 /** 153 * Lazy initialization of Standard Encodings. 154 */ 155 private static class StandardEncodingsHolder { 156 private static final SortedSet<String> standardEncodings = load(); 157 158 private static SortedSet<String> load() { 159 final Comparator<String> comparator = 160 new CharsetComparator(IndexedComparator.SELECT_WORST); 161 final SortedSet<String> tempSet = new TreeSet<>(comparator); 162 tempSet.add("US-ASCII"); 163 tempSet.add("ISO-8859-1"); 164 tempSet.add("UTF-8"); 165 tempSet.add("UTF-16BE"); 166 tempSet.add("UTF-16LE"); 167 tempSet.add("UTF-16"); 168 tempSet.add(Charset.defaultCharset().name()); 169 return Collections.unmodifiableSortedSet(tempSet); 170 } 171 } 172 173 /** 174 * Tracks whether a particular text/* MIME type supports the charset 175 * parameter. The Map is initialized with all of the standard MIME types 176 * listed in the DataFlavor.selectBestTextFlavor method comment. Additional 177 * entries may be added during the life of the JRE for text/<other> types. 178 */ 179 private static final Map<String, Boolean> textMIMESubtypeCharsetSupport; 180 181 /** 182 * A collection of all natives listed in flavormap.properties with 183 * a primary MIME type of "text". 184 */ 185 private static final Set<Long> textNatives = 186 Collections.synchronizedSet(new HashSet<>()); 187 188 /** 189 * The native encodings/charsets for the Set of textNatives. 190 */ 191 private static final Map<Long, String> nativeCharsets = 192 Collections.synchronizedMap(new HashMap<>()); 193 194 /** 195 * The end-of-line markers for the Set of textNatives. 196 */ 197 private static final Map<Long, String> nativeEOLNs = 198 Collections.synchronizedMap(new HashMap<>()); 199 200 /** 201 * The number of terminating NUL bytes for the Set of textNatives. 202 */ 203 private static final Map<Long, Integer> nativeTerminators = 204 Collections.synchronizedMap(new HashMap<>()); 205 206 /** 207 * The key used to store pending data conversion requests for an AppContext. 208 */ 209 private static final String DATA_CONVERTER_KEY = "DATA_CONVERTER_KEY"; 210 211 private static final PlatformLogger dtLog = PlatformLogger.getLogger("sun.awt.datatransfer.DataTransfer"); 212 213 static { 214 DataFlavor tJavaTextEncodingFlavor = null; 215 try { 216 tJavaTextEncodingFlavor = new DataFlavor("application/x-java-text-encoding;class=\"[B\""); 217 } catch (ClassNotFoundException cannotHappen) { 218 } 219 javaTextEncodingFlavor = tJavaTextEncodingFlavor; 220 221 Map<String, Boolean> tempMap = new HashMap<>(17); 222 tempMap.put("sgml", Boolean.TRUE); 223 tempMap.put("xml", Boolean.TRUE); 224 tempMap.put("html", Boolean.TRUE); 225 tempMap.put("enriched", Boolean.TRUE); 226 tempMap.put("richtext", Boolean.TRUE); 227 tempMap.put("uri-list", Boolean.TRUE); 228 tempMap.put("directory", Boolean.TRUE); 229 tempMap.put("css", Boolean.TRUE); 230 tempMap.put("calendar", Boolean.TRUE); 231 tempMap.put("plain", Boolean.TRUE); 232 tempMap.put("rtf", Boolean.FALSE); 233 tempMap.put("tab-separated-values", Boolean.FALSE); 234 tempMap.put("t140", Boolean.FALSE); 235 tempMap.put("rfc822-headers", Boolean.FALSE); 236 tempMap.put("parityfec", Boolean.FALSE); 237 textMIMESubtypeCharsetSupport = Collections.synchronizedMap(tempMap); 238 } 239 240 /** 241 * The accessor method for the singleton DataTransferer instance. Note 242 * that in a headless environment, there may be no DataTransferer instance; 243 * instead, null will be returned. 244 */ 245 public static synchronized DataTransferer getInstance() { 246 return ((SunToolkit) Toolkit.getDefaultToolkit()).getDataTransferer(); 247 } 248 249 /** 250 * Converts an arbitrary text encoding to its canonical name. 251 */ 252 public static String canonicalName(String encoding) { 253 if (encoding == null) { 254 return null; 255 } 256 try { 257 return Charset.forName(encoding).name(); 258 } catch (IllegalCharsetNameException icne) { 259 return encoding; 260 } catch (UnsupportedCharsetException uce) { 261 return encoding; 262 } 263 } 264 265 /** 266 * If the specified flavor is a text flavor which supports the "charset" 267 * parameter, then this method returns that parameter, or the default 268 * charset if no such parameter was specified at construction. For non- 269 * text DataFlavors, and for non-charset text flavors, this method returns 270 * null. 271 */ 272 public static String getTextCharset(DataFlavor flavor) { 273 if (!isFlavorCharsetTextType(flavor)) { 274 return null; 275 } 276 277 String encoding = flavor.getParameter("charset"); 278 279 return (encoding != null) ? encoding : Charset.defaultCharset().name(); 280 } 281 282 /** 283 * Tests only whether the flavor's MIME type supports the charset 284 * parameter. Must only be called for flavors with a primary type of 285 * "text". 286 */ 287 public static boolean doesSubtypeSupportCharset(DataFlavor flavor) { 288 if (dtLog.isLoggable(PlatformLogger.Level.FINE)) { 289 if (!"text".equals(flavor.getPrimaryType())) { 290 dtLog.fine("Assertion (\"text\".equals(flavor.getPrimaryType())) failed"); 291 } 292 } 293 294 String subType = flavor.getSubType(); 295 if (subType == null) { 296 return false; 297 } 298 299 Boolean support = textMIMESubtypeCharsetSupport.get(subType); 300 301 if (support != null) { 302 return support; 303 } 304 305 boolean ret_val = (flavor.getParameter("charset") != null); 306 textMIMESubtypeCharsetSupport.put(subType, ret_val); 307 return ret_val; 308 } 309 public static boolean doesSubtypeSupportCharset(String subType, 310 String charset) 311 { 312 Boolean support = textMIMESubtypeCharsetSupport.get(subType); 313 314 if (support != null) { 315 return support; 316 } 317 318 boolean ret_val = (charset != null); 319 textMIMESubtypeCharsetSupport.put(subType, ret_val); 320 return ret_val; 321 } 322 323 /** 324 * Returns whether this flavor is a text type which supports the 325 * 'charset' parameter. 326 */ 327 public static boolean isFlavorCharsetTextType(DataFlavor flavor) { 328 // Although stringFlavor doesn't actually support the charset 329 // parameter (because its primary MIME type is not "text"), it should 330 // be treated as though it does. stringFlavor is semantically 331 // equivalent to "text/plain" data. 332 if (DataFlavor.stringFlavor.equals(flavor)) { 333 return true; 334 } 335 336 if (!"text".equals(flavor.getPrimaryType()) || 337 !doesSubtypeSupportCharset(flavor)) 338 { 339 return false; 340 } 341 342 Class rep_class = flavor.getRepresentationClass(); 343 344 if (flavor.isRepresentationClassReader() || 345 String.class.equals(rep_class) || 346 flavor.isRepresentationClassCharBuffer() || 347 char[].class.equals(rep_class)) 348 { 349 return true; 350 } 351 352 if (!(flavor.isRepresentationClassInputStream() || 353 flavor.isRepresentationClassByteBuffer() || 354 byte[].class.equals(rep_class))) { 355 return false; 356 } 357 358 String charset = flavor.getParameter("charset"); 359 360 return (charset != null) 361 ? DataTransferer.isEncodingSupported(charset) 362 : true; // null equals default encoding which is always supported 363 } 364 365 /** 366 * Returns whether this flavor is a text type which does not support the 367 * 'charset' parameter. 368 */ 369 public static boolean isFlavorNoncharsetTextType(DataFlavor flavor) { 370 if (!"text".equals(flavor.getPrimaryType()) || 371 doesSubtypeSupportCharset(flavor)) 372 { 373 return false; 374 } 375 376 return (flavor.isRepresentationClassInputStream() || 377 flavor.isRepresentationClassByteBuffer() || 378 byte[].class.equals(flavor.getRepresentationClass())); 379 } 380 381 /** 382 * Determines whether this JRE can both encode and decode text in the 383 * specified encoding. 384 */ 385 private static boolean isEncodingSupported(String encoding) { 386 if (encoding == null) { 387 return false; 388 } 389 try { 390 return Charset.isSupported(encoding); 391 } catch (IllegalCharsetNameException icne) { 392 return false; 393 } 394 } 395 396 /** 397 * Returns {@code true} if the given type is a java.rmi.Remote. 398 */ 399 public static boolean isRemote(Class<?> type) { 400 return RMI.isRemote(type); 401 } 402 403 /** 404 * Returns an Iterator which traverses a SortedSet of Strings which are 405 * a total order of the standard character sets supported by the JRE. The 406 * ordering follows the same principles as DataFlavor.selectBestTextFlavor. 407 * So as to avoid loading all available character converters, optional, 408 * non-standard, character sets are not included. 409 */ 410 public static Set <String> standardEncodings() { 411 return StandardEncodingsHolder.standardEncodings; 412 } 413 414 /** 415 * Converts a FlavorMap to a FlavorTable. 416 */ 417 public static FlavorTable adaptFlavorMap(final FlavorMap map) { 418 if (map instanceof FlavorTable) { 419 return (FlavorTable)map; 420 } 421 422 return new FlavorTable() { 423 @Override 424 public Map<DataFlavor, String> getNativesForFlavors(DataFlavor[] flavors) { 425 return map.getNativesForFlavors(flavors); 426 } 427 @Override 428 public Map<String, DataFlavor> getFlavorsForNatives(String[] natives) { 429 return map.getFlavorsForNatives(natives); 430 } 431 @Override 432 public List<String> getNativesForFlavor(DataFlavor flav) { 433 Map<DataFlavor, String> natives = getNativesForFlavors(new DataFlavor[]{flav}); 434 String nat = natives.get(flav); 435 if (nat != null) { 436 return Collections.singletonList(nat); 437 } else { 438 return Collections.emptyList(); 439 } 440 } 441 @Override 442 public List<DataFlavor> getFlavorsForNative(String nat) { 443 Map<String, DataFlavor> flavors = getFlavorsForNatives(new String[]{nat}); 444 DataFlavor flavor = flavors.get(nat); 445 if (flavor != null) { 446 return Collections.singletonList(flavor); 447 } else { 448 return Collections.emptyList(); 449 } 450 } 451 }; 452 } 453 454 /** 455 * Returns the default Unicode encoding for the platform. The encoding 456 * need not be canonical. This method is only used by the archaic function 457 * DataFlavor.getTextPlainUnicodeFlavor(). 458 */ 459 public abstract String getDefaultUnicodeEncoding(); 460 461 /** 462 * This method is called for text flavor mappings established while parsing 463 * the flavormap.properties file. It stores the "eoln" and "terminators" 464 * parameters which are not officially part of the MIME type. They are 465 * MIME parameters specific to the flavormap.properties file format. 466 */ 467 public void registerTextFlavorProperties(String nat, String charset, 468 String eoln, String terminators) { 469 Long format = getFormatForNativeAsLong(nat); 470 471 textNatives.add(format); 472 nativeCharsets.put(format, (charset != null && charset.length() != 0) 473 ? charset : Charset.defaultCharset().name()); 474 if (eoln != null && eoln.length() != 0 && !eoln.equals("\n")) { 475 nativeEOLNs.put(format, eoln); 476 } 477 if (terminators != null && terminators.length() != 0) { 478 Integer iTerminators = Integer.valueOf(terminators); 479 if (iTerminators > 0) { 480 nativeTerminators.put(format, iTerminators); 481 } 482 } 483 } 484 485 /** 486 * Determines whether the native corresponding to the specified long format 487 * was listed in the flavormap.properties file. 488 */ 489 protected boolean isTextFormat(long format) { 490 return textNatives.contains(Long.valueOf(format)); 491 } 492 493 protected String getCharsetForTextFormat(Long lFormat) { 494 return nativeCharsets.get(lFormat); 495 } 496 497 /** 498 * Specifies whether text imported from the native system in the specified 499 * format is locale-dependent. If so, when decoding such text, 500 * 'nativeCharsets' should be ignored, and instead, the Transferable should 501 * be queried for its javaTextEncodingFlavor data for the correct encoding. 502 */ 503 public abstract boolean isLocaleDependentTextFormat(long format); 504 505 /** 506 * Determines whether the DataFlavor corresponding to the specified long 507 * format is DataFlavor.javaFileListFlavor. 508 */ 509 public abstract boolean isFileFormat(long format); 510 511 /** 512 * Determines whether the DataFlavor corresponding to the specified long 513 * format is DataFlavor.imageFlavor. 514 */ 515 public abstract boolean isImageFormat(long format); 516 517 /** 518 * Determines whether the format is a URI list we can convert to 519 * a DataFlavor.javaFileListFlavor. 520 */ 521 protected boolean isURIListFormat(long format) { 522 return false; 523 } 524 525 /** 526 * Returns a Map whose keys are all of the possible formats into which the 527 * Transferable's transfer data flavors can be translated. The value of 528 * each key is the DataFlavor in which the Transferable's data should be 529 * requested when converting to the format. 530 * <p> 531 * The map keys are sorted according to the native formats preference 532 * order. 533 */ 534 public SortedMap<Long,DataFlavor> getFormatsForTransferable(Transferable contents, 535 FlavorTable map) 536 { 537 DataFlavor[] flavors = contents.getTransferDataFlavors(); 538 if (flavors == null) { 539 return Collections.emptySortedMap(); 540 } 541 return getFormatsForFlavors(flavors, map); 542 } 543 544 /** 545 * Returns a Map whose keys are all of the possible formats into which data 546 * in the specified DataFlavors can be translated. The value of each key 547 * is the DataFlavor in which the Transferable's data should be requested 548 * when converting to the format. 549 * <p> 550 * The map keys are sorted according to the native formats preference 551 * order. 552 * 553 * @param flavors the data flavors 554 * @param map the FlavorTable which contains mappings between 555 * DataFlavors and data formats 556 * @throws NullPointerException if flavors or map is <code>null</code> 557 */ 558 public SortedMap<Long, DataFlavor> getFormatsForFlavors(DataFlavor[] flavors, 559 FlavorTable map) 560 { 561 Map<Long,DataFlavor> formatMap = new HashMap<>(flavors.length); 562 Map<Long,DataFlavor> textPlainMap = new HashMap<>(flavors.length); 563 // Maps formats to indices that will be used to sort the formats 564 // according to the preference order. 565 // Larger index value corresponds to the more preferable format. 566 Map<Long, Integer> indexMap = new HashMap<>(flavors.length); 567 Map<Long, Integer> textPlainIndexMap = new HashMap<>(flavors.length); 568 569 int currentIndex = 0; 570 571 // Iterate backwards so that preferred DataFlavors are used over 572 // other DataFlavors. (See javadoc for 573 // Transferable.getTransferDataFlavors.) 574 for (int i = flavors.length - 1; i >= 0; i--) { 575 DataFlavor flavor = flavors[i]; 576 if (flavor == null) continue; 577 578 // Don't explicitly test for String, since it is just a special 579 // case of Serializable 580 if (flavor.isFlavorTextType() || 581 flavor.isFlavorJavaFileListType() || 582 DataFlavor.imageFlavor.equals(flavor) || 583 flavor.isRepresentationClassSerializable() || 584 flavor.isRepresentationClassInputStream() || 585 flavor.isRepresentationClassRemote()) 586 { 587 List<String> natives = map.getNativesForFlavor(flavor); 588 589 currentIndex += natives.size(); 590 591 for (String aNative : natives) { 592 Long lFormat = getFormatForNativeAsLong(aNative); 593 Integer index = currentIndex--; 594 595 formatMap.put(lFormat, flavor); 596 indexMap.put(lFormat, index); 597 598 // SystemFlavorMap.getNativesForFlavor will return 599 // text/plain natives for all text/*. While this is good 600 // for a single text/* flavor, we would prefer that 601 // text/plain native data come from a text/plain flavor. 602 if (("text".equals(flavor.getPrimaryType()) && 603 "plain".equals(flavor.getSubType())) || 604 flavor.equals(DataFlavor.stringFlavor)) { 605 textPlainMap.put(lFormat, flavor); 606 textPlainIndexMap.put(lFormat, index); 607 } 608 } 609 610 currentIndex += natives.size(); 611 } 612 } 613 614 formatMap.putAll(textPlainMap); 615 indexMap.putAll(textPlainIndexMap); 616 617 // Sort the map keys according to the formats preference order. 618 Comparator<Long> comparator = 619 new IndexOrderComparator(indexMap, IndexedComparator.SELECT_WORST); 620 SortedMap<Long, DataFlavor> sortedMap = new TreeMap<>(comparator); 621 sortedMap.putAll(formatMap); 622 623 return sortedMap; 624 } 625 626 /** 627 * Reduces the Map output for the root function to an array of the 628 * Map's keys. 629 */ 630 public long[] getFormatsForTransferableAsArray(Transferable contents, 631 FlavorTable map) { 632 return keysToLongArray(getFormatsForTransferable(contents, map)); 633 } 634 635 /** 636 * Returns a Map whose keys are all of the possible DataFlavors into which 637 * data in the specified formats can be translated. The value of each key 638 * is the format in which the Clipboard or dropped data should be requested 639 * when converting to the DataFlavor. 640 */ 641 public Map<DataFlavor, Long> getFlavorsForFormats(long[] formats, FlavorTable map) { 642 Map<DataFlavor, Long> flavorMap = new HashMap<>(formats.length); 643 Set<AbstractMap.SimpleEntry<Long, DataFlavor>> mappingSet = new HashSet<>(formats.length); 644 Set<DataFlavor> flavorSet = new HashSet<>(formats.length); 645 646 // First step: build flavorSet, mappingSet and initial flavorMap 647 // flavorSet - the set of all the DataFlavors into which 648 // data in the specified formats can be translated; 649 // mappingSet - the set of all the mappings from the specified formats 650 // into any DataFlavor; 651 // flavorMap - after this step, this map maps each of the DataFlavors 652 // from flavorSet to any of the specified formats. 653 for (long format : formats) { 654 String nat = getNativeForFormat(format); 655 List<DataFlavor> flavors = map.getFlavorsForNative(nat); 656 for (DataFlavor flavor : flavors) { 657 // Don't explicitly test for String, since it is just a special 658 // case of Serializable 659 if (flavor.isFlavorTextType() || 660 flavor.isFlavorJavaFileListType() || 661 DataFlavor.imageFlavor.equals(flavor) || 662 flavor.isRepresentationClassSerializable() || 663 flavor.isRepresentationClassInputStream() || 664 flavor.isRepresentationClassRemote()) { 665 666 AbstractMap.SimpleEntry<Long, DataFlavor> mapping = 667 new AbstractMap.SimpleEntry<>(format, flavor); 668 flavorMap.put(flavor, format); 669 mappingSet.add(mapping); 670 flavorSet.add(flavor); 671 } 672 } 673 } 674 675 // Second step: for each DataFlavor try to figure out which of the 676 // specified formats is the best to translate to this flavor. 677 // Then map each flavor to the best format. 678 // For the given flavor, FlavorTable indicates which native will 679 // best reflect data in the specified flavor to the underlying native 680 // platform. We assume that this native is the best to translate 681 // to this flavor. 682 // Note: FlavorTable allows one-way mappings, so we can occasionally 683 // map a flavor to the format for which the corresponding 684 // format-to-flavor mapping doesn't exist. For this reason we have built 685 // a mappingSet of all format-to-flavor mappings for the specified formats 686 // and check if the format-to-flavor mapping exists for the 687 // (flavor,format) pair being added. 688 for (DataFlavor flavor : flavorSet) { 689 List<String> natives = map.getNativesForFlavor(flavor); 690 for (String aNative : natives) { 691 Long lFormat = getFormatForNativeAsLong(aNative); 692 if (mappingSet.contains(new AbstractMap.SimpleEntry<>(lFormat, flavor))) { 693 flavorMap.put(flavor, lFormat); 694 break; 695 } 696 } 697 } 698 699 return flavorMap; 700 } 701 702 /** 703 * Returns a Set of all DataFlavors for which 704 * 1) a mapping from at least one of the specified formats exists in the 705 * specified map and 706 * 2) the data translation for this mapping can be performed by the data 707 * transfer subsystem. 708 * 709 * @param formats the data formats 710 * @param map the FlavorTable which contains mappings between 711 * DataFlavors and data formats 712 * @throws NullPointerException if formats or map is <code>null</code> 713 */ 714 public Set getFlavorsForFormatsAsSet(long[] formats, FlavorTable map) { 715 Set<DataFlavor> flavorSet = new HashSet<>(formats.length); 716 717 for (long format : formats) { 718 List<DataFlavor> flavors = map.getFlavorsForNative(getNativeForFormat(format)); 719 for (DataFlavor flavor : flavors) { 720 // Don't explicitly test for String, since it is just a special 721 // case of Serializable 722 if (flavor.isFlavorTextType() || 723 flavor.isFlavorJavaFileListType() || 724 DataFlavor.imageFlavor.equals(flavor) || 725 flavor.isRepresentationClassSerializable() || 726 flavor.isRepresentationClassInputStream() || 727 flavor.isRepresentationClassRemote()) { 728 flavorSet.add(flavor); 729 } 730 } 731 } 732 733 return flavorSet; 734 } 735 736 /** 737 * Returns an array of all DataFlavors for which 738 * 1) a mapping from at least one of the specified formats exists in the 739 * specified map and 740 * 2) the data translation for this mapping can be performed by the data 741 * transfer subsystem. 742 * The array will be sorted according to a 743 * <code>DataFlavorComparator</code> created with the specified 744 * map as an argument. 745 * 746 * @param formats the data formats 747 * @param map the FlavorTable which contains mappings between 748 * DataFlavors and data formats 749 * @throws NullPointerException if formats or map is <code>null</code> 750 */ 751 public DataFlavor[] getFlavorsForFormatsAsArray(long[] formats, 752 FlavorTable map) { 753 // getFlavorsForFormatsAsSet() is less expensive than 754 // getFlavorsForFormats(). 755 return setToSortedDataFlavorArray(getFlavorsForFormatsAsSet(formats, map)); 756 } 757 758 /** 759 * Looks-up or registers the String native with the native data transfer 760 * system and returns a long format corresponding to that native. 761 */ 762 protected abstract Long getFormatForNativeAsLong(String str); 763 764 /** 765 * Looks-up the String native corresponding to the specified long format in 766 * the native data transfer system. 767 */ 768 protected abstract String getNativeForFormat(long format); 769 770 /* Contains common code for finding the best charset for 771 * clipboard string encoding/decoding, basing on clipboard 772 * format and localeTransferable(on decoding, if available) 773 */ 774 protected String getBestCharsetForTextFormat(Long lFormat, 775 Transferable localeTransferable) throws IOException 776 { 777 String charset = null; 778 if (localeTransferable != null && 779 isLocaleDependentTextFormat(lFormat) && 780 localeTransferable.isDataFlavorSupported(javaTextEncodingFlavor)) { 781 try { 782 byte[] charsetNameBytes = (byte[])localeTransferable 783 .getTransferData(javaTextEncodingFlavor); 784 charset = new String(charsetNameBytes, StandardCharsets.UTF_8); 785 } catch (UnsupportedFlavorException cannotHappen) { 786 } 787 } else { 788 charset = getCharsetForTextFormat(lFormat); 789 } 790 if (charset == null) { 791 // Only happens when we have a custom text type. 792 charset = Charset.defaultCharset().name(); 793 } 794 return charset; 795 } 796 797 /** 798 * Translation function for converting string into 799 * a byte array. Search-and-replace EOLN. Encode into the 800 * target format. Append terminating NUL bytes. 801 * 802 * Java to Native string conversion 803 */ 804 private byte[] translateTransferableString(String str, 805 long format) throws IOException 806 { 807 Long lFormat = format; 808 String charset = getBestCharsetForTextFormat(lFormat, null); 809 // Search and replace EOLN. Note that if EOLN is "\n", then we 810 // never added an entry to nativeEOLNs anyway, so we'll skip this 811 // code altogether. 812 // windows: "abc\nde"->"abc\r\nde" 813 String eoln = nativeEOLNs.get(lFormat); 814 if (eoln != null) { 815 int length = str.length(); 816 StringBuilder buffer = new StringBuilder(length * 2); // 2 is a heuristic 817 for (int i = 0; i < length; i++) { 818 // Fix for 4914613 - skip native EOLN 819 if (str.startsWith(eoln, i)) { 820 buffer.append(eoln); 821 i += eoln.length() - 1; 822 continue; 823 } 824 char c = str.charAt(i); 825 if (c == '\n') { 826 buffer.append(eoln); 827 } else { 828 buffer.append(c); 829 } 830 } 831 str = buffer.toString(); 832 } 833 834 // Encode text in target format. 835 byte[] bytes = str.getBytes(charset); 836 837 // Append terminating NUL bytes. Note that if terminators is 0, 838 // the we never added an entry to nativeTerminators anyway, so 839 // we'll skip code altogether. 840 // "abcde" -> "abcde\0" 841 Integer terminators = nativeTerminators.get(lFormat); 842 if (terminators != null) { 843 int numTerminators = terminators; 844 byte[] terminatedBytes = 845 new byte[bytes.length + numTerminators]; 846 System.arraycopy(bytes, 0, terminatedBytes, 0, bytes.length); 847 for (int i = bytes.length; i < terminatedBytes.length; i++) { 848 terminatedBytes[i] = 0x0; 849 } 850 bytes = terminatedBytes; 851 } 852 return bytes; 853 } 854 855 /** 856 * Translating either a byte array or an InputStream into an String. 857 * Strip terminators and search-and-replace EOLN. 858 * 859 * Native to Java string conversion 860 */ 861 private String translateBytesToString(byte[] bytes, long format, 862 Transferable localeTransferable) 863 throws IOException 864 { 865 866 Long lFormat = format; 867 String charset = getBestCharsetForTextFormat(lFormat, localeTransferable); 868 869 // Locate terminating NUL bytes. Note that if terminators is 0, 870 // the we never added an entry to nativeTerminators anyway, so 871 // we'll skip code altogether. 872 873 // In other words: we are doing char alignment here basing on suggestion 874 // that count of zero-'terminators' is a number of bytes in one symbol 875 // for selected charset (clipboard format). It is not complitly true for 876 // multibyte coding like UTF-8, but helps understand the procedure. 877 // "abcde\0" -> "abcde" 878 879 String eoln = nativeEOLNs.get(lFormat); 880 Integer terminators = nativeTerminators.get(lFormat); 881 int count; 882 if (terminators != null) { 883 int numTerminators = terminators; 884 search: 885 for (count = 0; count < (bytes.length - numTerminators + 1); count += numTerminators) { 886 for (int i = count; i < count + numTerminators; i++) { 887 if (bytes[i] != 0x0) { 888 continue search; 889 } 890 } 891 // found terminators 892 break search; 893 } 894 } else { 895 count = bytes.length; 896 } 897 898 // Decode text to chars. Don't include any terminators. 899 String converted = new String(bytes, 0, count, charset); 900 901 // Search and replace EOLN. Note that if EOLN is "\n", then we 902 // never added an entry to nativeEOLNs anyway, so we'll skip this 903 // code altogether. 904 // Count of NUL-terminators and EOLN coding are platform-specific and 905 // loaded from flavormap.properties file 906 // windows: "abc\r\nde" -> "abc\nde" 907 908 if (eoln != null) { 909 910 /* Fix for 4463560: replace EOLNs symbol-by-symbol instead 911 * of using buf.replace() 912 */ 913 914 char[] buf = converted.toCharArray(); 915 char[] eoln_arr = eoln.toCharArray(); 916 int j = 0; 917 boolean match; 918 919 for (int i = 0; i < buf.length; ) { 920 // Catch last few bytes 921 if (i + eoln_arr.length > buf.length) { 922 buf[j++] = buf[i++]; 923 continue; 924 } 925 926 match = true; 927 for (int k = 0, l = i; k < eoln_arr.length; k++, l++) { 928 if (eoln_arr[k] != buf[l]) { 929 match = false; 930 break; 931 } 932 } 933 if (match) { 934 buf[j++] = '\n'; 935 i += eoln_arr.length; 936 } else { 937 buf[j++] = buf[i++]; 938 } 939 } 940 converted = new String(buf, 0, j); 941 } 942 943 return converted; 944 } 945 946 947 /** 948 * Primary translation function for translating a Transferable into 949 * a byte array, given a source DataFlavor and target format. 950 */ 951 public byte[] translateTransferable(Transferable contents, 952 DataFlavor flavor, 953 long format) throws IOException 954 { 955 // Obtain the transfer data in the source DataFlavor. 956 // 957 // Note that we special case DataFlavor.plainTextFlavor because 958 // StringSelection supports this flavor incorrectly -- instead of 959 // returning an InputStream as the DataFlavor representation class 960 // states, it returns a Reader. Instead of using this broken 961 // functionality, we request the data in stringFlavor (the other 962 // DataFlavor which StringSelection supports) and use the String 963 // translator. 964 Object obj; 965 boolean stringSelectionHack; 966 try { 967 obj = contents.getTransferData(flavor); 968 if (obj == null) { 969 return null; 970 } 971 if (flavor.equals(DataFlavor.plainTextFlavor) && 972 !(obj instanceof InputStream)) 973 { 974 obj = contents.getTransferData(DataFlavor.stringFlavor); 975 if (obj == null) { 976 return null; 977 } 978 stringSelectionHack = true; 979 } else { 980 stringSelectionHack = false; 981 } 982 } catch (UnsupportedFlavorException e) { 983 throw new IOException(e.getMessage()); 984 } 985 986 // Source data is a String. Search-and-replace EOLN. Encode into the 987 // target format. Append terminating NUL bytes. 988 if (stringSelectionHack || 989 (String.class.equals(flavor.getRepresentationClass()) && 990 isFlavorCharsetTextType(flavor) && isTextFormat(format))) { 991 992 String str = removeSuspectedData(flavor, contents, (String)obj); 993 994 return translateTransferableString( 995 str, 996 format); 997 998 // Source data is a Reader. Convert to a String and recur. In the 999 // future, we may want to rewrite this so that we encode on demand. 1000 } else if (flavor.isRepresentationClassReader()) { 1001 if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) { 1002 throw new IOException 1003 ("cannot transfer non-text data as Reader"); 1004 } 1005 1006 StringBuilder buf = new StringBuilder(); 1007 try (Reader r = (Reader)obj) { 1008 int c; 1009 while ((c = r.read()) != -1) { 1010 buf.append((char)c); 1011 } 1012 } 1013 1014 return translateTransferableString( 1015 buf.toString(), 1016 format); 1017 1018 // Source data is a CharBuffer. Convert to a String and recur. 1019 } else if (flavor.isRepresentationClassCharBuffer()) { 1020 if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) { 1021 throw new IOException 1022 ("cannot transfer non-text data as CharBuffer"); 1023 } 1024 1025 CharBuffer buffer = (CharBuffer)obj; 1026 int size = buffer.remaining(); 1027 char[] chars = new char[size]; 1028 buffer.get(chars, 0, size); 1029 1030 return translateTransferableString( 1031 new String(chars), 1032 format); 1033 1034 // Source data is a char array. Convert to a String and recur. 1035 } else if (char[].class.equals(flavor.getRepresentationClass())) { 1036 if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) { 1037 throw new IOException 1038 ("cannot transfer non-text data as char array"); 1039 } 1040 1041 return translateTransferableString( 1042 new String((char[])obj), 1043 format); 1044 1045 // Source data is a ByteBuffer. For arbitrary flavors, simply return 1046 // the array. For text flavors, decode back to a String and recur to 1047 // reencode according to the requested format. 1048 } else if (flavor.isRepresentationClassByteBuffer()) { 1049 ByteBuffer buffer = (ByteBuffer)obj; 1050 int size = buffer.remaining(); 1051 byte[] bytes = new byte[size]; 1052 buffer.get(bytes, 0, size); 1053 1054 if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1055 String sourceEncoding = DataTransferer.getTextCharset(flavor); 1056 return translateTransferableString( 1057 new String(bytes, sourceEncoding), 1058 format); 1059 } else { 1060 return bytes; 1061 } 1062 1063 // Source data is a byte array. For arbitrary flavors, simply return 1064 // the array. For text flavors, decode back to a String and recur to 1065 // reencode according to the requested format. 1066 } else if (byte[].class.equals(flavor.getRepresentationClass())) { 1067 byte[] bytes = (byte[])obj; 1068 1069 if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1070 String sourceEncoding = DataTransferer.getTextCharset(flavor); 1071 return translateTransferableString( 1072 new String(bytes, sourceEncoding), 1073 format); 1074 } else { 1075 return bytes; 1076 } 1077 // Source data is Image 1078 } else if (DataFlavor.imageFlavor.equals(flavor)) { 1079 if (!isImageFormat(format)) { 1080 throw new IOException("Data translation failed: " + 1081 "not an image format"); 1082 } 1083 1084 Image image = (Image)obj; 1085 byte[] bytes = imageToPlatformBytes(image, format); 1086 1087 if (bytes == null) { 1088 throw new IOException("Data translation failed: " + 1089 "cannot convert java image to native format"); 1090 } 1091 return bytes; 1092 } 1093 1094 byte[] theByteArray = null; 1095 1096 // Target data is a file list. Source data must be a 1097 // java.util.List which contains java.io.File or String instances. 1098 if (isFileFormat(format)) { 1099 if (!DataFlavor.javaFileListFlavor.equals(flavor)) { 1100 throw new IOException("data translation failed"); 1101 } 1102 1103 final List list = (List)obj; 1104 1105 final ProtectionDomain userProtectionDomain = getUserProtectionDomain(contents); 1106 1107 final ArrayList<String> fileList = castToFiles(list, userProtectionDomain); 1108 1109 try (ByteArrayOutputStream bos = convertFileListToBytes(fileList)) { 1110 theByteArray = bos.toByteArray(); 1111 } 1112 1113 // Target data is a URI list. Source data must be a 1114 // java.util.List which contains java.io.File or String instances. 1115 } else if (isURIListFormat(format)) { 1116 if (!DataFlavor.javaFileListFlavor.equals(flavor)) { 1117 throw new IOException("data translation failed"); 1118 } 1119 String nat = getNativeForFormat(format); 1120 String targetCharset = null; 1121 if (nat != null) { 1122 try { 1123 targetCharset = new DataFlavor(nat).getParameter("charset"); 1124 } catch (ClassNotFoundException cnfe) { 1125 throw new IOException(cnfe); 1126 } 1127 } 1128 if (targetCharset == null) { 1129 targetCharset = "UTF-8"; 1130 } 1131 final List list = (List)obj; 1132 final ProtectionDomain userProtectionDomain = getUserProtectionDomain(contents); 1133 final ArrayList<String> fileList = castToFiles(list, userProtectionDomain); 1134 final ArrayList<String> uriList = new ArrayList<>(fileList.size()); 1135 for (String fileObject : fileList) { 1136 final URI uri = new File(fileObject).toURI(); 1137 // Some implementations are fussy about the number of slashes (file:///path/to/file is best) 1138 try { 1139 uriList.add(new URI(uri.getScheme(), "", uri.getPath(), uri.getFragment()).toString()); 1140 } catch (URISyntaxException uriSyntaxException) { 1141 throw new IOException(uriSyntaxException); 1142 } 1143 } 1144 1145 byte[] eoln = "\r\n".getBytes(targetCharset); 1146 1147 try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { 1148 for (String uri : uriList) { 1149 byte[] bytes = uri.getBytes(targetCharset); 1150 bos.write(bytes, 0, bytes.length); 1151 bos.write(eoln, 0, eoln.length); 1152 } 1153 theByteArray = bos.toByteArray(); 1154 } 1155 1156 // Source data is an InputStream. For arbitrary flavors, just grab the 1157 // bytes and dump them into a byte array. For text flavors, decode back 1158 // to a String and recur to reencode according to the requested format. 1159 } else if (flavor.isRepresentationClassInputStream()) { 1160 try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { 1161 try (InputStream is = (InputStream)obj) { 1162 boolean eof = false; 1163 int avail = is.available(); 1164 byte[] tmp = new byte[avail > 8192 ? avail : 8192]; 1165 do { 1166 int aValue; 1167 if (!(eof = (aValue = is.read(tmp, 0, tmp.length)) == -1)) { 1168 bos.write(tmp, 0, aValue); 1169 } 1170 } while (!eof); 1171 } 1172 1173 if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1174 byte[] bytes = bos.toByteArray(); 1175 String sourceEncoding = DataTransferer.getTextCharset(flavor); 1176 return translateTransferableString( 1177 new String(bytes, sourceEncoding), 1178 format); 1179 } 1180 theByteArray = bos.toByteArray(); 1181 } 1182 1183 1184 1185 // Source data is an RMI object 1186 } else if (flavor.isRepresentationClassRemote()) { 1187 1188 Object mo = RMI.newMarshalledObject(obj); 1189 theByteArray = convertObjectToBytes(mo); 1190 1191 // Source data is Serializable 1192 } else if (flavor.isRepresentationClassSerializable()) { 1193 1194 theByteArray = convertObjectToBytes(obj); 1195 1196 } else { 1197 throw new IOException("data translation failed"); 1198 } 1199 1200 1201 1202 return theByteArray; 1203 } 1204 1205 private static byte[] convertObjectToBytes(Object object) throws IOException { 1206 try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); 1207 ObjectOutputStream oos = new ObjectOutputStream(bos)) 1208 { 1209 oos.writeObject(object); 1210 return bos.toByteArray(); 1211 } 1212 } 1213 1214 protected abstract ByteArrayOutputStream convertFileListToBytes(ArrayList<String> fileList) throws IOException; 1215 1216 private String removeSuspectedData(DataFlavor flavor, final Transferable contents, final String str) 1217 throws IOException 1218 { 1219 if (null == System.getSecurityManager() 1220 || !flavor.isMimeTypeEqual("text/uri-list")) 1221 { 1222 return str; 1223 } 1224 1225 final ProtectionDomain userProtectionDomain = getUserProtectionDomain(contents); 1226 1227 try { 1228 return AccessController.doPrivileged((PrivilegedExceptionAction<String>) () -> { 1229 1230 StringBuilder allowedFiles = new StringBuilder(str.length()); 1231 String [] uriArray = str.split("(\\s)+"); 1232 1233 for (String fileName : uriArray) 1234 { 1235 File file = new File(fileName); 1236 if (file.exists() && 1237 !(isFileInWebstartedCache(file) || 1238 isForbiddenToRead(file, userProtectionDomain))) 1239 { 1240 if (0 != allowedFiles.length()) 1241 { 1242 allowedFiles.append("\\r\\n"); 1243 } 1244 1245 allowedFiles.append(fileName); 1246 } 1247 } 1248 1249 return allowedFiles.toString(); 1250 }); 1251 } catch (PrivilegedActionException pae) { 1252 throw new IOException(pae.getMessage(), pae); 1253 } 1254 } 1255 1256 private static ProtectionDomain getUserProtectionDomain(Transferable contents) { 1257 return contents.getClass().getProtectionDomain(); 1258 } 1259 1260 private boolean isForbiddenToRead (File file, ProtectionDomain protectionDomain) 1261 { 1262 if (null == protectionDomain) { 1263 return false; 1264 } 1265 try { 1266 FilePermission filePermission = 1267 new FilePermission(file.getCanonicalPath(), "read, delete"); 1268 if (protectionDomain.implies(filePermission)) { 1269 return false; 1270 } 1271 } catch (IOException e) {} 1272 1273 return true; 1274 } 1275 1276 private ArrayList<String> castToFiles(final List files, 1277 final ProtectionDomain userProtectionDomain) throws IOException { 1278 try { 1279 return AccessController.doPrivileged((PrivilegedExceptionAction<ArrayList<String>>) () -> { 1280 ArrayList<String> fileList = new ArrayList<>(); 1281 for (Object fileObject : files) 1282 { 1283 File file = castToFile(fileObject); 1284 if (file != null && 1285 (null == System.getSecurityManager() || 1286 !(isFileInWebstartedCache(file) || 1287 isForbiddenToRead(file, userProtectionDomain)))) 1288 { 1289 fileList.add(file.getCanonicalPath()); 1290 } 1291 } 1292 return fileList; 1293 }); 1294 } catch (PrivilegedActionException pae) { 1295 throw new IOException(pae.getMessage()); 1296 } 1297 } 1298 1299 // It is important do not use user's successors 1300 // of File class. 1301 private File castToFile(Object fileObject) throws IOException { 1302 String filePath = null; 1303 if (fileObject instanceof File) { 1304 filePath = ((File)fileObject).getCanonicalPath(); 1305 } else if (fileObject instanceof String) { 1306 filePath = (String) fileObject; 1307 } else { 1308 return null; 1309 } 1310 return new File(filePath); 1311 } 1312 1313 private final static String[] DEPLOYMENT_CACHE_PROPERTIES = { 1314 "deployment.system.cachedir", 1315 "deployment.user.cachedir", 1316 "deployment.javaws.cachedir", 1317 "deployment.javapi.cachedir" 1318 }; 1319 1320 private final static ArrayList <File> deploymentCacheDirectoryList = new ArrayList<>(); 1321 1322 private static boolean isFileInWebstartedCache(File f) { 1323 1324 if (deploymentCacheDirectoryList.isEmpty()) { 1325 for (String cacheDirectoryProperty : DEPLOYMENT_CACHE_PROPERTIES) { 1326 String cacheDirectoryPath = System.getProperty(cacheDirectoryProperty); 1327 if (cacheDirectoryPath != null) { 1328 try { 1329 File cacheDirectory = (new File(cacheDirectoryPath)).getCanonicalFile(); 1330 if (cacheDirectory != null) { 1331 deploymentCacheDirectoryList.add(cacheDirectory); 1332 } 1333 } catch (IOException ioe) {} 1334 } 1335 } 1336 } 1337 1338 for (File deploymentCacheDirectory : deploymentCacheDirectoryList) { 1339 for (File dir = f; dir != null; dir = dir.getParentFile()) { 1340 if (dir.equals(deploymentCacheDirectory)) { 1341 return true; 1342 } 1343 } 1344 } 1345 1346 return false; 1347 } 1348 1349 1350 public Object translateBytes(byte[] bytes, DataFlavor flavor, 1351 long format, Transferable localeTransferable) 1352 throws IOException 1353 { 1354 1355 Object theObject = null; 1356 1357 // Source data is a file list. Use the dragQueryFile native function to 1358 // do most of the decoding. Then wrap File objects around the String 1359 // filenames and return a List. 1360 if (isFileFormat(format)) { 1361 if (!DataFlavor.javaFileListFlavor.equals(flavor)) { 1362 throw new IOException("data translation failed"); 1363 } 1364 String[] filenames = dragQueryFile(bytes); 1365 if (filenames == null) { 1366 return null; 1367 } 1368 1369 // Convert the strings to File objects 1370 File[] files = new File[filenames.length]; 1371 for (int i = 0; i < filenames.length; i++) { 1372 files[i] = new File(filenames[i]); 1373 } 1374 1375 // Turn the list of Files into a List and return 1376 theObject = Arrays.asList(files); 1377 1378 // Source data is a URI list. Convert to DataFlavor.javaFileListFlavor 1379 // where possible. 1380 } else if (isURIListFormat(format) 1381 && DataFlavor.javaFileListFlavor.equals(flavor)) { 1382 1383 try (ByteArrayInputStream str = new ByteArrayInputStream(bytes)) { 1384 1385 URI uris[] = dragQueryURIs(str, format, localeTransferable); 1386 if (uris == null) { 1387 return null; 1388 } 1389 List<File> files = new ArrayList<>(); 1390 for (URI uri : uris) { 1391 try { 1392 files.add(new File(uri)); 1393 } catch (IllegalArgumentException illegalArg) { 1394 // When converting from URIs to less generic files, 1395 // common practice (Wine, SWT) seems to be to 1396 // silently drop the URIs that aren't local files. 1397 } 1398 } 1399 theObject = files; 1400 } 1401 1402 // Target data is a String. Strip terminating NUL bytes. Decode bytes 1403 // into characters. Search-and-replace EOLN. 1404 } else if (String.class.equals(flavor.getRepresentationClass()) && 1405 isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1406 1407 theObject = translateBytesToString(bytes, format, localeTransferable); 1408 1409 // Target data is a Reader. Obtain data in InputStream format, encoded 1410 // as "Unicode" (utf-16be). Then use an InputStreamReader to decode 1411 // back to chars on demand. 1412 } else if (flavor.isRepresentationClassReader()) { 1413 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) { 1414 theObject = translateStream(bais, 1415 flavor, format, localeTransferable); 1416 } 1417 // Target data is a CharBuffer. Recur to obtain String and wrap. 1418 } else if (flavor.isRepresentationClassCharBuffer()) { 1419 if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) { 1420 throw new IOException 1421 ("cannot transfer non-text data as CharBuffer"); 1422 } 1423 1424 CharBuffer buffer = CharBuffer.wrap( 1425 translateBytesToString(bytes,format, localeTransferable)); 1426 1427 theObject = constructFlavoredObject(buffer, flavor, CharBuffer.class); 1428 1429 // Target data is a char array. Recur to obtain String and convert to 1430 // char array. 1431 } else if (char[].class.equals(flavor.getRepresentationClass())) { 1432 if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) { 1433 throw new IOException 1434 ("cannot transfer non-text data as char array"); 1435 } 1436 1437 theObject = translateBytesToString( 1438 bytes, format, localeTransferable).toCharArray(); 1439 1440 // Target data is a ByteBuffer. For arbitrary flavors, just return 1441 // the raw bytes. For text flavors, convert to a String to strip 1442 // terminators and search-and-replace EOLN, then reencode according to 1443 // the requested flavor. 1444 } else if (flavor.isRepresentationClassByteBuffer()) { 1445 if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1446 bytes = translateBytesToString( 1447 bytes, format, localeTransferable).getBytes( 1448 DataTransferer.getTextCharset(flavor) 1449 ); 1450 } 1451 1452 ByteBuffer buffer = ByteBuffer.wrap(bytes); 1453 theObject = constructFlavoredObject(buffer, flavor, ByteBuffer.class); 1454 1455 // Target data is a byte array. For arbitrary flavors, just return 1456 // the raw bytes. For text flavors, convert to a String to strip 1457 // terminators and search-and-replace EOLN, then reencode according to 1458 // the requested flavor. 1459 } else if (byte[].class.equals(flavor.getRepresentationClass())) { 1460 if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1461 theObject = translateBytesToString( 1462 bytes, format, localeTransferable 1463 ).getBytes(DataTransferer.getTextCharset(flavor)); 1464 } else { 1465 theObject = bytes; 1466 } 1467 1468 // Target data is an InputStream. For arbitrary flavors, just return 1469 // the raw bytes. For text flavors, decode to strip terminators and 1470 // search-and-replace EOLN, then reencode according to the requested 1471 // flavor. 1472 } else if (flavor.isRepresentationClassInputStream()) { 1473 1474 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) { 1475 theObject = translateStream(bais, flavor, format, localeTransferable); 1476 } 1477 1478 } else if (flavor.isRepresentationClassRemote()) { 1479 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); 1480 ObjectInputStream ois = new ObjectInputStream(bais)) 1481 { 1482 theObject = RMI.getMarshalledObject(ois.readObject()); 1483 } catch (Exception e) { 1484 throw new IOException(e.getMessage()); 1485 } 1486 1487 // Target data is Serializable 1488 } else if (flavor.isRepresentationClassSerializable()) { 1489 1490 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) { 1491 theObject = translateStream(bais, flavor, format, localeTransferable); 1492 } 1493 1494 // Target data is Image 1495 } else if (DataFlavor.imageFlavor.equals(flavor)) { 1496 if (!isImageFormat(format)) { 1497 throw new IOException("data translation failed"); 1498 } 1499 1500 theObject = platformImageBytesToImage(bytes, format); 1501 } 1502 1503 if (theObject == null) { 1504 throw new IOException("data translation failed"); 1505 } 1506 1507 return theObject; 1508 1509 } 1510 1511 /** 1512 * Primary translation function for translating 1513 * an InputStream into an Object, given a source format and a target 1514 * DataFlavor. 1515 */ 1516 public Object translateStream(InputStream str, DataFlavor flavor, 1517 long format, Transferable localeTransferable) 1518 throws IOException 1519 { 1520 1521 Object theObject = null; 1522 // Source data is a URI list. Convert to DataFlavor.javaFileListFlavor 1523 // where possible. 1524 if (isURIListFormat(format) 1525 && DataFlavor.javaFileListFlavor.equals(flavor)) 1526 { 1527 1528 URI uris[] = dragQueryURIs(str, format, localeTransferable); 1529 if (uris == null) { 1530 return null; 1531 } 1532 List<File> files = new ArrayList<>(); 1533 for (URI uri : uris) { 1534 try { 1535 files.add(new File(uri)); 1536 } catch (IllegalArgumentException illegalArg) { 1537 // When converting from URIs to less generic files, 1538 // common practice (Wine, SWT) seems to be to 1539 // silently drop the URIs that aren't local files. 1540 } 1541 } 1542 theObject = files; 1543 1544 // Target data is a String. Strip terminating NUL bytes. Decode bytes 1545 // into characters. Search-and-replace EOLN. 1546 } else if (String.class.equals(flavor.getRepresentationClass()) && 1547 isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1548 1549 return translateBytesToString(inputStreamToByteArray(str), 1550 format, localeTransferable); 1551 1552 // Special hack to maintain backwards-compatibility with the brokenness 1553 // of StringSelection. Return a StringReader instead of an InputStream. 1554 // Recur to obtain String and encapsulate. 1555 } else if (DataFlavor.plainTextFlavor.equals(flavor)) { 1556 theObject = new StringReader(translateBytesToString( 1557 inputStreamToByteArray(str), 1558 format, localeTransferable)); 1559 1560 // Target data is an InputStream. For arbitrary flavors, just return 1561 // the raw bytes. For text flavors, decode to strip terminators and 1562 // search-and-replace EOLN, then reencode according to the requested 1563 // flavor. 1564 } else if (flavor.isRepresentationClassInputStream()) { 1565 theObject = translateStreamToInputStream(str, flavor, format, 1566 localeTransferable); 1567 1568 // Target data is a Reader. Obtain data in InputStream format, encoded 1569 // as "Unicode" (utf-16be). Then use an InputStreamReader to decode 1570 // back to chars on demand. 1571 } else if (flavor.isRepresentationClassReader()) { 1572 if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) { 1573 throw new IOException 1574 ("cannot transfer non-text data as Reader"); 1575 } 1576 1577 InputStream is = (InputStream)translateStreamToInputStream( 1578 str, DataFlavor.plainTextFlavor, 1579 format, localeTransferable); 1580 1581 String unicode = DataTransferer.getTextCharset(DataFlavor.plainTextFlavor); 1582 1583 Reader reader = new InputStreamReader(is, unicode); 1584 1585 theObject = constructFlavoredObject(reader, flavor, Reader.class); 1586 // Target data is a byte array 1587 } else if (byte[].class.equals(flavor.getRepresentationClass())) { 1588 if(isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1589 theObject = translateBytesToString(inputStreamToByteArray(str), format, localeTransferable) 1590 .getBytes(DataTransferer.getTextCharset(flavor)); 1591 } else { 1592 theObject = inputStreamToByteArray(str); 1593 } 1594 // Target data is an RMI object 1595 } else if (flavor.isRepresentationClassRemote()) { 1596 1597 try (ObjectInputStream ois = 1598 new ObjectInputStream(str)) 1599 { 1600 theObject = RMI.getMarshalledObject(ois.readObject()); 1601 }catch (Exception e) { 1602 throw new IOException(e.getMessage()); 1603 } 1604 1605 // Target data is Serializable 1606 } else if (flavor.isRepresentationClassSerializable()) { 1607 try (ObjectInputStream ois = 1608 new ObjectInputStream(str)) 1609 { 1610 theObject = ois.readObject(); 1611 } catch (Exception e) { 1612 throw new IOException(e.getMessage()); 1613 } 1614 // Target data is Image 1615 } else if (DataFlavor.imageFlavor.equals(flavor)) { 1616 if (!isImageFormat(format)) { 1617 throw new IOException("data translation failed"); 1618 } 1619 theObject = platformImageBytesToImage(inputStreamToByteArray(str), format); 1620 } 1621 1622 if (theObject == null) { 1623 throw new IOException("data translation failed"); 1624 } 1625 1626 return theObject; 1627 1628 } 1629 1630 /** 1631 * For arbitrary flavors, just use the raw InputStream. For text flavors, 1632 * ReencodingInputStream will decode and reencode the InputStream on demand 1633 * so that we can strip terminators and search-and-replace EOLN. 1634 */ 1635 private Object translateStreamToInputStream 1636 (InputStream str, DataFlavor flavor, long format, 1637 Transferable localeTransferable) throws IOException 1638 { 1639 if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) { 1640 str = new ReencodingInputStream 1641 (str, format, DataTransferer.getTextCharset(flavor), 1642 localeTransferable); 1643 } 1644 1645 return constructFlavoredObject(str, flavor, InputStream.class); 1646 } 1647 1648 /** 1649 * We support representations which are exactly of the specified Class, 1650 * and also arbitrary Objects which have a constructor which takes an 1651 * instance of the Class as its sole parameter. 1652 */ 1653 private Object constructFlavoredObject(Object arg, DataFlavor flavor, 1654 Class clazz) 1655 throws IOException 1656 { 1657 final Class<?> dfrc = flavor.getRepresentationClass(); 1658 1659 if (clazz.equals(dfrc)) { 1660 return arg; // simple case 1661 } else { 1662 Constructor[] constructors; 1663 1664 try { 1665 constructors = AccessController.doPrivileged( 1666 (PrivilegedAction<Constructor[]>) dfrc::getConstructors); 1667 } catch (SecurityException se) { 1668 throw new IOException(se.getMessage()); 1669 } 1670 1671 Constructor constructor = Stream.of(constructors) 1672 .filter(c -> Modifier.isPublic(c.getModifiers())) 1673 .filter(c -> { 1674 Class[] ptypes = c.getParameterTypes(); 1675 return ptypes != null 1676 && ptypes.length == 1 1677 && clazz.equals(ptypes[0]); 1678 }) 1679 .findFirst() 1680 .orElseThrow(() -> 1681 new IOException("can't find <init>(L"+ clazz + ";)V for class: " + dfrc.getName())); 1682 1683 try { 1684 return constructor.newInstance(arg); 1685 } catch (Exception e) { 1686 throw new IOException(e.getMessage()); 1687 } 1688 } 1689 } 1690 1691 /** 1692 * Used for decoding and reencoding an InputStream on demand so that we 1693 * can strip NUL terminators and perform EOLN search-and-replace. 1694 */ 1695 public class ReencodingInputStream extends InputStream { 1696 BufferedReader wrapped; 1697 final char[] in = new char[2]; 1698 byte[] out; 1699 1700 CharsetEncoder encoder; 1701 CharBuffer inBuf; 1702 ByteBuffer outBuf; 1703 1704 char[] eoln; 1705 int numTerminators; 1706 1707 boolean eos; 1708 int index, limit; 1709 1710 public ReencodingInputStream(InputStream bytestream, long format, 1711 String targetEncoding, 1712 Transferable localeTransferable) 1713 throws IOException 1714 { 1715 Long lFormat = format; 1716 1717 String sourceEncoding = getBestCharsetForTextFormat(format, localeTransferable); 1718 wrapped = new BufferedReader(new InputStreamReader(bytestream, sourceEncoding)); 1719 1720 if (targetEncoding == null) { 1721 // Throw NullPointerException for compatibility with the former 1722 // call to sun.io.CharToByteConverter.getConverter(null) 1723 // (Charset.forName(null) throws unspecified IllegalArgumentException 1724 // now; see 6228568) 1725 throw new NullPointerException("null target encoding"); 1726 } 1727 1728 try { 1729 encoder = Charset.forName(targetEncoding).newEncoder(); 1730 out = new byte[(int)(encoder.maxBytesPerChar() * 2 + 0.5)]; 1731 inBuf = CharBuffer.wrap(in); 1732 outBuf = ByteBuffer.wrap(out); 1733 } catch (IllegalCharsetNameException 1734 | UnsupportedCharsetException 1735 | UnsupportedOperationException e) { 1736 throw new IOException(e.toString()); 1737 } 1738 1739 String sEoln = nativeEOLNs.get(lFormat); 1740 if (sEoln != null) { 1741 eoln = sEoln.toCharArray(); 1742 } 1743 1744 // A hope and a prayer that this works generically. This will 1745 // definitely work on Win32. 1746 Integer terminators = nativeTerminators.get(lFormat); 1747 if (terminators != null) { 1748 numTerminators = terminators; 1749 } 1750 } 1751 1752 private int readChar() throws IOException { 1753 int c = wrapped.read(); 1754 1755 if (c == -1) { // -1 is EOS 1756 eos = true; 1757 return -1; 1758 } 1759 1760 // "c == 0" is not quite correct, but good enough on Windows. 1761 if (numTerminators > 0 && c == 0) { 1762 eos = true; 1763 return -1; 1764 } else if (eoln != null && matchCharArray(eoln, c)) { 1765 c = '\n' & 0xFFFF; 1766 } 1767 1768 return c; 1769 } 1770 1771 public int read() throws IOException { 1772 if (eos) { 1773 return -1; 1774 } 1775 1776 if (index >= limit) { 1777 // deal with supplementary characters 1778 int c = readChar(); 1779 if (c == -1) { 1780 return -1; 1781 } 1782 1783 in[0] = (char) c; 1784 in[1] = 0; 1785 inBuf.limit(1); 1786 if (Character.isHighSurrogate((char) c)) { 1787 c = readChar(); 1788 if (c != -1) { 1789 in[1] = (char) c; 1790 inBuf.limit(2); 1791 } 1792 } 1793 1794 inBuf.rewind(); 1795 outBuf.limit(out.length).rewind(); 1796 encoder.encode(inBuf, outBuf, false); 1797 outBuf.flip(); 1798 limit = outBuf.limit(); 1799 1800 index = 0; 1801 1802 return read(); 1803 } else { 1804 return out[index++] & 0xFF; 1805 } 1806 } 1807 1808 public int available() throws IOException { 1809 return ((eos) ? 0 : (limit - index)); 1810 } 1811 1812 public void close() throws IOException { 1813 wrapped.close(); 1814 } 1815 1816 /** 1817 * Checks to see if the next array.length characters in wrapped 1818 * match array. The first character is provided as c. Subsequent 1819 * characters are read from wrapped itself. When this method returns, 1820 * the wrapped index may be different from what it was when this 1821 * method was called. 1822 */ 1823 private boolean matchCharArray(char[] array, int c) 1824 throws IOException 1825 { 1826 wrapped.mark(array.length); // BufferedReader supports mark 1827 1828 int count = 0; 1829 if ((char)c == array[0]) { 1830 for (count = 1; count < array.length; count++) { 1831 c = wrapped.read(); 1832 if (c == -1 || ((char)c) != array[count]) { 1833 break; 1834 } 1835 } 1836 } 1837 1838 if (count == array.length) { 1839 return true; 1840 } else { 1841 wrapped.reset(); 1842 return false; 1843 } 1844 } 1845 } 1846 1847 /** 1848 * Decodes a byte array into a set of String filenames. 1849 */ 1850 protected abstract String[] dragQueryFile(byte[] bytes); 1851 1852 /** 1853 * Decodes URIs from either a byte array or a stream. 1854 */ 1855 protected URI[] dragQueryURIs(InputStream stream, 1856 long format, 1857 Transferable localeTransferable) 1858 throws IOException 1859 { 1860 throw new IOException( 1861 new UnsupportedOperationException("not implemented on this platform")); 1862 } 1863 1864 /** 1865 * Translates either a byte array or an input stream which contain 1866 * platform-specific image data in the given format into an Image. 1867 */ 1868 1869 1870 protected abstract Image platformImageBytesToImage( 1871 byte[] bytes,long format) throws IOException; 1872 1873 /** 1874 * Translates either a byte array or an input stream which contain 1875 * an image data in the given standard format into an Image. 1876 * 1877 * @param mimeType image MIME type, such as: image/png, image/jpeg, image/gif 1878 */ 1879 protected Image standardImageBytesToImage( 1880 byte[] bytes, String mimeType) throws IOException 1881 { 1882 1883 Iterator readerIterator = ImageIO.getImageReadersByMIMEType(mimeType); 1884 1885 if (!readerIterator.hasNext()) { 1886 throw new IOException("No registered service provider can decode " + 1887 " an image from " + mimeType); 1888 } 1889 1890 IOException ioe = null; 1891 1892 while (readerIterator.hasNext()) { 1893 ImageReader imageReader = (ImageReader)readerIterator.next(); 1894 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) { 1895 try (ImageInputStream imageInputStream = ImageIO.createImageInputStream(bais)) { 1896 ImageReadParam param = imageReader.getDefaultReadParam(); 1897 imageReader.setInput(imageInputStream, true, true); 1898 BufferedImage bufferedImage = imageReader.read(imageReader.getMinIndex(), param); 1899 if (bufferedImage != null) { 1900 return bufferedImage; 1901 } 1902 } finally { 1903 imageReader.dispose(); 1904 } 1905 } catch (IOException e) { 1906 ioe = e; 1907 continue; 1908 } 1909 } 1910 1911 if (ioe == null) { 1912 ioe = new IOException("Registered service providers failed to decode" 1913 + " an image from " + mimeType); 1914 } 1915 1916 throw ioe; 1917 } 1918 1919 /** 1920 * Translates a Java Image into a byte array which contains platform- 1921 * specific image data in the given format. 1922 */ 1923 protected abstract byte[] imageToPlatformBytes(Image image, long format) 1924 throws IOException; 1925 1926 /** 1927 * Translates a Java Image into a byte array which contains 1928 * an image data in the given standard format. 1929 * 1930 * @param mimeType image MIME type, such as: image/png, image/jpeg 1931 */ 1932 protected byte[] imageToStandardBytes(Image image, String mimeType) 1933 throws IOException { 1934 IOException originalIOE = null; 1935 1936 Iterator writerIterator = ImageIO.getImageWritersByMIMEType(mimeType); 1937 1938 if (!writerIterator.hasNext()) { 1939 throw new IOException("No registered service provider can encode " + 1940 " an image to " + mimeType); 1941 } 1942 1943 if (image instanceof RenderedImage) { 1944 // Try to encode the original image. 1945 try { 1946 return imageToStandardBytesImpl((RenderedImage)image, mimeType); 1947 } catch (IOException ioe) { 1948 originalIOE = ioe; 1949 } 1950 } 1951 1952 // Retry with a BufferedImage. 1953 int width = 0; 1954 int height = 0; 1955 if (image instanceof ToolkitImage) { 1956 ImageRepresentation ir = ((ToolkitImage)image).getImageRep(); 1957 ir.reconstruct(ImageObserver.ALLBITS); 1958 width = ir.getWidth(); 1959 height = ir.getHeight(); 1960 } else { 1961 width = image.getWidth(null); 1962 height = image.getHeight(null); 1963 } 1964 1965 ColorModel model = ColorModel.getRGBdefault(); 1966 WritableRaster raster = 1967 model.createCompatibleWritableRaster(width, height); 1968 1969 BufferedImage bufferedImage = 1970 new BufferedImage(model, raster, model.isAlphaPremultiplied(), 1971 null); 1972 1973 Graphics g = bufferedImage.getGraphics(); 1974 try { 1975 g.drawImage(image, 0, 0, width, height, null); 1976 } finally { 1977 g.dispose(); 1978 } 1979 1980 try { 1981 return imageToStandardBytesImpl(bufferedImage, mimeType); 1982 } catch (IOException ioe) { 1983 if (originalIOE != null) { 1984 throw originalIOE; 1985 } else { 1986 throw ioe; 1987 } 1988 } 1989 } 1990 1991 byte[] imageToStandardBytesImpl(RenderedImage renderedImage, 1992 String mimeType) 1993 throws IOException { 1994 1995 Iterator writerIterator = ImageIO.getImageWritersByMIMEType(mimeType); 1996 1997 ImageTypeSpecifier typeSpecifier = 1998 new ImageTypeSpecifier(renderedImage); 1999 2000 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 2001 IOException ioe = null; 2002 2003 while (writerIterator.hasNext()) { 2004 ImageWriter imageWriter = (ImageWriter)writerIterator.next(); 2005 ImageWriterSpi writerSpi = imageWriter.getOriginatingProvider(); 2006 2007 if (!writerSpi.canEncodeImage(typeSpecifier)) { 2008 continue; 2009 } 2010 2011 try { 2012 try (ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(baos)) { 2013 imageWriter.setOutput(imageOutputStream); 2014 imageWriter.write(renderedImage); 2015 imageOutputStream.flush(); 2016 } 2017 } catch (IOException e) { 2018 imageWriter.dispose(); 2019 baos.reset(); 2020 ioe = e; 2021 continue; 2022 } 2023 2024 imageWriter.dispose(); 2025 baos.close(); 2026 return baos.toByteArray(); 2027 } 2028 2029 baos.close(); 2030 2031 if (ioe == null) { 2032 ioe = new IOException("Registered service providers failed to encode " 2033 + renderedImage + " to " + mimeType); 2034 } 2035 2036 throw ioe; 2037 } 2038 2039 /** 2040 * Concatenates the data represented by two objects. Objects can be either 2041 * byte arrays or instances of <code>InputStream</code>. If both arguments 2042 * are byte arrays byte array will be returned. Otherwise an 2043 * <code>InputStream</code> will be returned. 2044 * <p> 2045 * Currently is only called from native code to prepend palette data to 2046 * platform-specific image data during image transfer on Win32. 2047 * 2048 * @param obj1 the first object to be concatenated. 2049 * @param obj2 the second object to be concatenated. 2050 * @return a byte array or an <code>InputStream</code> which represents 2051 * a logical concatenation of the two arguments. 2052 * @throws NullPointerException is either of the arguments is 2053 * <code>null</code> 2054 * @throws ClassCastException is either of the arguments is 2055 * neither byte array nor an instance of <code>InputStream</code>. 2056 */ 2057 private Object concatData(Object obj1, Object obj2) { 2058 InputStream str1 = null; 2059 InputStream str2 = null; 2060 2061 if (obj1 instanceof byte[]) { 2062 byte[] arr1 = (byte[])obj1; 2063 if (obj2 instanceof byte[]) { 2064 byte[] arr2 = (byte[])obj2; 2065 byte[] ret = new byte[arr1.length + arr2.length]; 2066 System.arraycopy(arr1, 0, ret, 0, arr1.length); 2067 System.arraycopy(arr2, 0, ret, arr1.length, arr2.length); 2068 return ret; 2069 } else { 2070 str1 = new ByteArrayInputStream(arr1); 2071 str2 = (InputStream)obj2; 2072 } 2073 } else { 2074 str1 = (InputStream)obj1; 2075 if (obj2 instanceof byte[]) { 2076 str2 = new ByteArrayInputStream((byte[])obj2); 2077 } else { 2078 str2 = (InputStream)obj2; 2079 } 2080 } 2081 2082 return new SequenceInputStream(str1, str2); 2083 } 2084 2085 public byte[] convertData(final Object source, 2086 final Transferable contents, 2087 final long format, 2088 final Map formatMap, 2089 final boolean isToolkitThread) 2090 throws IOException 2091 { 2092 byte[] ret = null; 2093 2094 /* 2095 * If the current thread is the Toolkit thread we should post a 2096 * Runnable to the event dispatch thread associated with source Object, 2097 * since translateTransferable() calls Transferable.getTransferData() 2098 * that may contain client code. 2099 */ 2100 if (isToolkitThread) try { 2101 final Stack<byte[]> stack = new Stack<>(); 2102 final Runnable dataConverter = new Runnable() { 2103 // Guard against multiple executions. 2104 private boolean done = false; 2105 public void run() { 2106 if (done) { 2107 return; 2108 } 2109 byte[] data = null; 2110 try { 2111 DataFlavor flavor = (DataFlavor)formatMap.get(format); 2112 if (flavor != null) { 2113 data = translateTransferable(contents, flavor, format); 2114 } 2115 } catch (Exception e) { 2116 e.printStackTrace(); 2117 data = null; 2118 } 2119 try { 2120 getToolkitThreadBlockedHandler().lock(); 2121 stack.push(data); 2122 getToolkitThreadBlockedHandler().exit(); 2123 } finally { 2124 getToolkitThreadBlockedHandler().unlock(); 2125 done = true; 2126 } 2127 } 2128 }; 2129 2130 final AppContext appContext = SunToolkit.targetToAppContext(source); 2131 2132 getToolkitThreadBlockedHandler().lock(); 2133 2134 if (appContext != null) { 2135 appContext.put(DATA_CONVERTER_KEY, dataConverter); 2136 } 2137 2138 SunToolkit.executeOnEventHandlerThread(source, dataConverter); 2139 2140 while (stack.empty()) { 2141 getToolkitThreadBlockedHandler().enter(); 2142 } 2143 2144 if (appContext != null) { 2145 appContext.remove(DATA_CONVERTER_KEY); 2146 } 2147 2148 ret = stack.pop(); 2149 } finally { 2150 getToolkitThreadBlockedHandler().unlock(); 2151 } else { 2152 DataFlavor flavor = (DataFlavor)formatMap.get(format); 2153 if (flavor != null) { 2154 ret = translateTransferable(contents, flavor, format); 2155 } 2156 } 2157 2158 return ret; 2159 } 2160 2161 public void processDataConversionRequests() { 2162 if (EventQueue.isDispatchThread()) { 2163 AppContext appContext = AppContext.getAppContext(); 2164 getToolkitThreadBlockedHandler().lock(); 2165 try { 2166 Runnable dataConverter = 2167 (Runnable)appContext.get(DATA_CONVERTER_KEY); 2168 if (dataConverter != null) { 2169 dataConverter.run(); 2170 appContext.remove(DATA_CONVERTER_KEY); 2171 } 2172 } finally { 2173 getToolkitThreadBlockedHandler().unlock(); 2174 } 2175 } 2176 } 2177 2178 public abstract ToolkitThreadBlockedHandler 2179 getToolkitThreadBlockedHandler(); 2180 2181 /** 2182 * Helper function to reduce a Map with Long keys to a long array. 2183 * <p> 2184 * The map keys are sorted according to the native formats preference 2185 * order. 2186 */ 2187 public static long[] keysToLongArray(SortedMap<Long, ?> map) { 2188 Set<Long> keySet = map.keySet(); 2189 long[] retval = new long[keySet.size()]; 2190 int i = 0; 2191 for (Iterator<Long> iter = keySet.iterator(); iter.hasNext(); i++) { 2192 retval[i] = iter.next(); 2193 } 2194 return retval; 2195 } 2196 2197 /** 2198 * Helper function to convert a Set of DataFlavors to a sorted array. 2199 * The array will be sorted according to <code>DataFlavorComparator</code>. 2200 */ 2201 public static DataFlavor[] setToSortedDataFlavorArray(Set flavorsSet) { 2202 DataFlavor[] flavors = new DataFlavor[flavorsSet.size()]; 2203 flavorsSet.toArray(flavors); 2204 final Comparator<DataFlavor> comparator = 2205 new DataFlavorComparator(IndexedComparator.SELECT_WORST); 2206 Arrays.sort(flavors, comparator); 2207 return flavors; 2208 } 2209 2210 /** 2211 * Helper function to convert an InputStream to a byte[] array. 2212 */ 2213 protected static byte[] inputStreamToByteArray(InputStream str) 2214 throws IOException 2215 { 2216 try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { 2217 int len = 0; 2218 byte[] buf = new byte[8192]; 2219 2220 while ((len = str.read(buf)) != -1) { 2221 baos.write(buf, 0, len); 2222 } 2223 2224 return baos.toByteArray(); 2225 } 2226 } 2227 2228 /** 2229 * Returns platform-specific mappings for the specified native. 2230 * If there are no platform-specific mappings for this native, the method 2231 * returns an empty <code>List</code>. 2232 */ 2233 public List<DataFlavor> getPlatformMappingsForNative(String nat) { 2234 return new ArrayList<>(); 2235 } 2236 2237 /** 2238 * Returns platform-specific mappings for the specified flavor. 2239 * If there are no platform-specific mappings for this flavor, the method 2240 * returns an empty <code>List</code>. 2241 */ 2242 public List<String> getPlatformMappingsForFlavor(DataFlavor df) { 2243 return new ArrayList<>(); 2244 } 2245 2246 /** 2247 * A Comparator which includes a helper function for comparing two Objects 2248 * which are likely to be keys in the specified Map. 2249 */ 2250 public abstract static class IndexedComparator<T> implements Comparator<T> { 2251 2252 /** 2253 * The best Object (e.g., DataFlavor) will be the last in sequence. 2254 */ 2255 public static final boolean SELECT_BEST = true; 2256 2257 /** 2258 * The best Object (e.g., DataFlavor) will be the first in sequence. 2259 */ 2260 public static final boolean SELECT_WORST = false; 2261 2262 final boolean order; 2263 2264 public IndexedComparator(boolean order) { 2265 this.order = order; 2266 } 2267 2268 /** 2269 * Helper method to compare two objects by their Integer indices in the 2270 * given map. If the map doesn't contain an entry for either of the 2271 * objects, the fallback index will be used for the object instead. 2272 * 2273 * @param indexMap the map which maps objects into Integer indexes. 2274 * @param obj1 the first object to be compared. 2275 * @param obj2 the second object to be compared. 2276 * @param fallbackIndex the Integer to be used as a fallback index. 2277 * @return a negative integer, zero, or a positive integer as the 2278 * first object is mapped to a less, equal to, or greater 2279 * index than the second. 2280 */ 2281 static <T> int compareIndices(Map<T, Integer> indexMap, 2282 T obj1, T obj2, 2283 Integer fallbackIndex) { 2284 Integer index1 = indexMap.getOrDefault(obj1, fallbackIndex); 2285 Integer index2 = indexMap.getOrDefault(obj2, fallbackIndex); 2286 return index1.compareTo(index2); 2287 } 2288 } 2289 2290 /** 2291 * An IndexedComparator which compares two String charsets. The comparison 2292 * follows the rules outlined in DataFlavor.selectBestTextFlavor. In order 2293 * to ensure that non-Unicode, non-ASCII, non-default charsets are sorted 2294 * in alphabetical order, charsets are not automatically converted to their 2295 * canonical forms. 2296 */ 2297 public static class CharsetComparator extends IndexedComparator<String> { 2298 private static final Map<String, Integer> charsets; 2299 2300 private static final Integer DEFAULT_CHARSET_INDEX = 2; 2301 private static final Integer OTHER_CHARSET_INDEX = 1; 2302 private static final Integer WORST_CHARSET_INDEX = 0; 2303 private static final Integer UNSUPPORTED_CHARSET_INDEX = Integer.MIN_VALUE; 2304 2305 private static final String UNSUPPORTED_CHARSET = "UNSUPPORTED"; 2306 2307 static { 2308 Map<String, Integer> charsetsMap = new HashMap<>(8, 1.0f); 2309 2310 // we prefer Unicode charsets 2311 charsetsMap.put(canonicalName("UTF-16LE"), 4); 2312 charsetsMap.put(canonicalName("UTF-16BE"), 5); 2313 charsetsMap.put(canonicalName("UTF-8"), 6); 2314 charsetsMap.put(canonicalName("UTF-16"), 7); 2315 2316 // US-ASCII is the worst charset supported 2317 charsetsMap.put(canonicalName("US-ASCII"), WORST_CHARSET_INDEX); 2318 2319 charsetsMap.putIfAbsent(Charset.defaultCharset().name(), DEFAULT_CHARSET_INDEX); 2320 2321 charsetsMap.put(UNSUPPORTED_CHARSET, UNSUPPORTED_CHARSET_INDEX); 2322 2323 charsets = Collections.unmodifiableMap(charsetsMap); 2324 } 2325 2326 public CharsetComparator(boolean order) { 2327 super(order); 2328 } 2329 2330 /** 2331 * Compares two String objects. Returns a negative integer, zero, 2332 * or a positive integer as the first charset is worse than, equal to, 2333 * or better than the second. 2334 * 2335 * @param obj1 the first charset to be compared 2336 * @param obj2 the second charset to be compared 2337 * @return a negative integer, zero, or a positive integer as the 2338 * first argument is worse, equal to, or better than the 2339 * second. 2340 * @throws ClassCastException if either of the arguments is not 2341 * instance of String 2342 * @throws NullPointerException if either of the arguments is 2343 * <code>null</code>. 2344 */ 2345 public int compare(String obj1, String obj2) { 2346 if (order == SELECT_BEST) { 2347 return compareCharsets(obj1, obj2); 2348 } else { 2349 return compareCharsets(obj2, obj1); 2350 } 2351 } 2352 2353 /** 2354 * Compares charsets. Returns a negative integer, zero, or a positive 2355 * integer as the first charset is worse than, equal to, or better than 2356 * the second. 2357 * <p> 2358 * Charsets are ordered according to the following rules: 2359 * <ul> 2360 * <li>All unsupported charsets are equal. 2361 * <li>Any unsupported charset is worse than any supported charset. 2362 * <li>Unicode charsets, such as "UTF-16", "UTF-8", "UTF-16BE" and 2363 * "UTF-16LE", are considered best. 2364 * <li>After them, platform default charset is selected. 2365 * <li>"US-ASCII" is the worst of supported charsets. 2366 * <li>For all other supported charsets, the lexicographically less 2367 * one is considered the better. 2368 * </ul> 2369 * 2370 * @param charset1 the first charset to be compared 2371 * @param charset2 the second charset to be compared. 2372 * @return a negative integer, zero, or a positive integer as the 2373 * first argument is worse, equal to, or better than the 2374 * second. 2375 */ 2376 int compareCharsets(String charset1, String charset2) { 2377 charset1 = getEncoding(charset1); 2378 charset2 = getEncoding(charset2); 2379 2380 int comp = compareIndices(charsets, charset1, charset2, 2381 OTHER_CHARSET_INDEX); 2382 2383 if (comp == 0) { 2384 return charset2.compareTo(charset1); 2385 } 2386 2387 return comp; 2388 } 2389 2390 /** 2391 * Returns encoding for the specified charset according to the 2392 * following rules: 2393 * <ul> 2394 * <li>If the charset is <code>null</code>, then <code>null</code> will 2395 * be returned. 2396 * <li>Iff the charset specifies an encoding unsupported by this JRE, 2397 * <code>UNSUPPORTED_CHARSET</code> will be returned. 2398 * <li>If the charset specifies an alias name, the corresponding 2399 * canonical name will be returned iff the charset is a known 2400 * Unicode, ASCII, or default charset. 2401 * </ul> 2402 * 2403 * @param charset the charset. 2404 * @return an encoding for this charset. 2405 */ 2406 static String getEncoding(String charset) { 2407 if (charset == null) { 2408 return null; 2409 } else if (!DataTransferer.isEncodingSupported(charset)) { 2410 return UNSUPPORTED_CHARSET; 2411 } else { 2412 // Only convert to canonical form if the charset is one 2413 // of the charsets explicitly listed in the known charsets 2414 // map. This will happen only for Unicode, ASCII, or default 2415 // charsets. 2416 String canonicalName = DataTransferer.canonicalName(charset); 2417 return (charsets.containsKey(canonicalName)) 2418 ? canonicalName 2419 : charset; 2420 } 2421 } 2422 } 2423 2424 /** 2425 * An IndexedComparator which compares two DataFlavors. For text flavors, 2426 * the comparison follows the rules outlined in 2427 * DataFlavor.selectBestTextFlavor. For non-text flavors, unknown 2428 * application MIME types are preferred, followed by known 2429 * application/x-java-* MIME types. Unknown application types are preferred 2430 * because if the user provides his own data flavor, it will likely be the 2431 * most descriptive one. For flavors which are otherwise equal, the 2432 * flavors' string representation are compared in the alphabetical order. 2433 */ 2434 public static class DataFlavorComparator extends IndexedComparator<DataFlavor> { 2435 2436 private final CharsetComparator charsetComparator; 2437 2438 private static final Map<String, Integer> exactTypes; 2439 private static final Map<String, Integer> primaryTypes; 2440 private static final Map<Class<?>, Integer> nonTextRepresentations; 2441 private static final Map<String, Integer> textTypes; 2442 private static final Map<Class<?>, Integer> decodedTextRepresentations; 2443 private static final Map<Class<?>, Integer> encodedTextRepresentations; 2444 2445 private static final Integer UNKNOWN_OBJECT_LOSES = Integer.MIN_VALUE; 2446 private static final Integer UNKNOWN_OBJECT_WINS = Integer.MAX_VALUE; 2447 2448 static { 2449 { 2450 Map<String, Integer> exactTypesMap = new HashMap<>(4, 1.0f); 2451 2452 // application/x-java-* MIME types 2453 exactTypesMap.put("application/x-java-file-list", 0); 2454 exactTypesMap.put("application/x-java-serialized-object", 1); 2455 exactTypesMap.put("application/x-java-jvm-local-objectref", 2); 2456 exactTypesMap.put("application/x-java-remote-object", 3); 2457 2458 exactTypes = Collections.unmodifiableMap(exactTypesMap); 2459 } 2460 2461 { 2462 Map<String, Integer> primaryTypesMap = new HashMap<>(1, 1.0f); 2463 2464 primaryTypesMap.put("application", 0); 2465 2466 primaryTypes = Collections.unmodifiableMap(primaryTypesMap); 2467 } 2468 2469 { 2470 Map<Class<?>, Integer> nonTextRepresentationsMap = new HashMap<>(3, 1.0f); 2471 2472 nonTextRepresentationsMap.put(java.io.InputStream.class, 0); 2473 nonTextRepresentationsMap.put(java.io.Serializable.class, 1); 2474 2475 Class<?> remoteClass = RMI.remoteClass(); 2476 if (remoteClass != null) { 2477 nonTextRepresentationsMap.put(remoteClass, 2); 2478 } 2479 2480 nonTextRepresentations = Collections.unmodifiableMap(nonTextRepresentationsMap); 2481 } 2482 2483 { 2484 Map<String, Integer> textTypesMap = new HashMap<>(16, 1.0f); 2485 2486 // plain text 2487 textTypesMap.put("text/plain", 0); 2488 2489 // stringFlavor 2490 textTypesMap.put("application/x-java-serialized-object", 1); 2491 2492 // misc 2493 textTypesMap.put("text/calendar", 2); 2494 textTypesMap.put("text/css", 3); 2495 textTypesMap.put("text/directory", 4); 2496 textTypesMap.put("text/parityfec", 5); 2497 textTypesMap.put("text/rfc822-headers", 6); 2498 textTypesMap.put("text/t140", 7); 2499 textTypesMap.put("text/tab-separated-values", 8); 2500 textTypesMap.put("text/uri-list", 9); 2501 2502 // enriched 2503 textTypesMap.put("text/richtext", 10); 2504 textTypesMap.put("text/enriched", 11); 2505 textTypesMap.put("text/rtf", 12); 2506 2507 // markup 2508 textTypesMap.put("text/html", 13); 2509 textTypesMap.put("text/xml", 14); 2510 textTypesMap.put("text/sgml", 15); 2511 2512 textTypes = Collections.unmodifiableMap(textTypesMap); 2513 } 2514 2515 { 2516 Map<Class<?>, Integer> decodedTextRepresentationsMap = new HashMap<>(4, 1.0f); 2517 2518 decodedTextRepresentationsMap.put(char[].class, 0); 2519 decodedTextRepresentationsMap.put(CharBuffer.class, 1); 2520 decodedTextRepresentationsMap.put(String.class, 2); 2521 decodedTextRepresentationsMap.put(Reader.class, 3); 2522 2523 decodedTextRepresentations = 2524 Collections.unmodifiableMap(decodedTextRepresentationsMap); 2525 } 2526 2527 { 2528 Map<Class<?>, Integer> encodedTextRepresentationsMap = new HashMap<>(3, 1.0f); 2529 2530 encodedTextRepresentationsMap.put(byte[].class, 0); 2531 encodedTextRepresentationsMap.put(ByteBuffer.class, 1); 2532 encodedTextRepresentationsMap.put(InputStream.class, 2); 2533 2534 encodedTextRepresentations = 2535 Collections.unmodifiableMap(encodedTextRepresentationsMap); 2536 } 2537 } 2538 2539 public DataFlavorComparator() { 2540 this(SELECT_BEST); 2541 } 2542 2543 public DataFlavorComparator(boolean order) { 2544 super(order); 2545 2546 charsetComparator = new CharsetComparator(order); 2547 } 2548 2549 public int compare(DataFlavor obj1, DataFlavor obj2) { 2550 DataFlavor flavor1 = order == SELECT_BEST ? obj1 : obj2; 2551 DataFlavor flavor2 = order == SELECT_BEST ? obj2 : obj1; 2552 2553 if (flavor1.equals(flavor2)) { 2554 return 0; 2555 } 2556 2557 int comp = 0; 2558 2559 String primaryType1 = flavor1.getPrimaryType(); 2560 String subType1 = flavor1.getSubType(); 2561 String mimeType1 = primaryType1 + "/" + subType1; 2562 Class class1 = flavor1.getRepresentationClass(); 2563 2564 String primaryType2 = flavor2.getPrimaryType(); 2565 String subType2 = flavor2.getSubType(); 2566 String mimeType2 = primaryType2 + "/" + subType2; 2567 Class class2 = flavor2.getRepresentationClass(); 2568 2569 if (flavor1.isFlavorTextType() && flavor2.isFlavorTextType()) { 2570 // First, compare MIME types 2571 comp = compareIndices(textTypes, mimeType1, mimeType2, 2572 UNKNOWN_OBJECT_LOSES); 2573 if (comp != 0) { 2574 return comp; 2575 } 2576 2577 // Only need to test one flavor because they both have the 2578 // same MIME type. Also don't need to worry about accidentally 2579 // passing stringFlavor because either 2580 // 1. Both flavors are stringFlavor, in which case the 2581 // equality test at the top of the function succeeded. 2582 // 2. Only one flavor is stringFlavor, in which case the MIME 2583 // type comparison returned a non-zero value. 2584 if (doesSubtypeSupportCharset(flavor1)) { 2585 // Next, prefer the decoded text representations of Reader, 2586 // String, CharBuffer, and [C, in that order. 2587 comp = compareIndices(decodedTextRepresentations, class1, 2588 class2, UNKNOWN_OBJECT_LOSES); 2589 if (comp != 0) { 2590 return comp; 2591 } 2592 2593 // Next, compare charsets 2594 comp = charsetComparator.compareCharsets 2595 (DataTransferer.getTextCharset(flavor1), 2596 DataTransferer.getTextCharset(flavor2)); 2597 if (comp != 0) { 2598 return comp; 2599 } 2600 } 2601 2602 // Finally, prefer the encoded text representations of 2603 // InputStream, ByteBuffer, and [B, in that order. 2604 comp = compareIndices(encodedTextRepresentations, class1, 2605 class2, UNKNOWN_OBJECT_LOSES); 2606 if (comp != 0) { 2607 return comp; 2608 } 2609 } else { 2610 // First, prefer application types. 2611 comp = compareIndices(primaryTypes, primaryType1, primaryType2, 2612 UNKNOWN_OBJECT_LOSES); 2613 if (comp != 0) { 2614 return comp; 2615 } 2616 2617 // Next, look for application/x-java-* types. Prefer unknown 2618 // MIME types because if the user provides his own data flavor, 2619 // it will likely be the most descriptive one. 2620 comp = compareIndices(exactTypes, mimeType1, mimeType2, 2621 UNKNOWN_OBJECT_WINS); 2622 if (comp != 0) { 2623 return comp; 2624 } 2625 2626 // Finally, prefer the representation classes of Remote, 2627 // Serializable, and InputStream, in that order. 2628 comp = compareIndices(nonTextRepresentations, class1, class2, 2629 UNKNOWN_OBJECT_LOSES); 2630 if (comp != 0) { 2631 return comp; 2632 } 2633 } 2634 2635 // The flavours are not equal but still not distinguishable. 2636 // Compare String representations in alphabetical order 2637 return flavor1.getMimeType().compareTo(flavor2.getMimeType()); 2638 } 2639 } 2640 2641 /* 2642 * Given the Map that maps objects to Integer indices and a boolean value, 2643 * this Comparator imposes a direct or reverse order on set of objects. 2644 * <p> 2645 * If the specified boolean value is SELECT_BEST, the Comparator imposes the 2646 * direct index-based order: an object A is greater than an object B if and 2647 * only if the index of A is greater than the index of B. An object that 2648 * doesn't have an associated index is less or equal than any other object. 2649 * <p> 2650 * If the specified boolean value is SELECT_WORST, the Comparator imposes the 2651 * reverse index-based order: an object A is greater than an object B if and 2652 * only if A is less than B with the direct index-based order. 2653 */ 2654 public static class IndexOrderComparator extends IndexedComparator<Long> { 2655 private final Map<Long, Integer> indexMap; 2656 private static final Integer FALLBACK_INDEX = Integer.MIN_VALUE; 2657 2658 public IndexOrderComparator(Map<Long, Integer> indexMap, boolean order) { 2659 super(order); 2660 this.indexMap = indexMap; 2661 } 2662 2663 public int compare(Long obj1, Long obj2) { 2664 if (order == SELECT_WORST) { 2665 return -compareIndices(indexMap, obj1, obj2, FALLBACK_INDEX); 2666 } else { 2667 return compareIndices(indexMap, obj1, obj2, FALLBACK_INDEX); 2668 } 2669 } 2670 } 2671 2672 /** 2673 * A class that provides access to java.rmi.Remote and java.rmi.MarshalledObject 2674 * without creating a static dependency. 2675 */ 2676 private static class RMI { 2677 private static final Class<?> remoteClass = getClass("java.rmi.Remote"); 2678 private static final Class<?> marshallObjectClass = 2679 getClass("java.rmi.MarshalledObject"); 2680 private static final Constructor<?> marshallCtor = 2681 getConstructor(marshallObjectClass, Object.class); 2682 private static final Method marshallGet = 2683 getMethod(marshallObjectClass, "get"); 2684 2685 private static Class<?> getClass(String name) { 2686 try { 2687 return Class.forName(name, true, null); 2688 } catch (ClassNotFoundException e) { 2689 return null; 2690 } 2691 } 2692 2693 private static Constructor<?> getConstructor(Class<?> c, Class<?>... types) { 2694 try { 2695 return (c == null) ? null : c.getDeclaredConstructor(types); 2696 } catch (NoSuchMethodException x) { 2697 throw new AssertionError(x); 2698 } 2699 } 2700 2701 private static Method getMethod(Class<?> c, String name, Class<?>... types) { 2702 try { 2703 return (c == null) ? null : c.getMethod(name, types); 2704 } catch (NoSuchMethodException e) { 2705 throw new AssertionError(e); 2706 } 2707 } 2708 2709 /** 2710 * Returns {@code true} if the given class is java.rmi.Remote. 2711 */ 2712 static boolean isRemote(Class<?> c) { 2713 return (remoteClass == null) ? false : remoteClass.isAssignableFrom(c); 2714 } 2715 2716 /** 2717 * Returns java.rmi.Remote.class if RMI is present; otherwise {@code null}. 2718 */ 2719 static Class<?> remoteClass() { 2720 return remoteClass; 2721 } 2722 2723 /** 2724 * Returns a new MarshalledObject containing the serialized representation 2725 * of the given object. 2726 */ 2727 static Object newMarshalledObject(Object obj) throws IOException { 2728 try { 2729 return marshallCtor.newInstance(obj); 2730 } catch (InstantiationException | IllegalAccessException x) { 2731 throw new AssertionError(x); 2732 } catch (InvocationTargetException x) { 2733 Throwable cause = x.getCause(); 2734 if (cause instanceof IOException) 2735 throw (IOException)cause; 2736 throw new AssertionError(x); 2737 } 2738 } 2739 2740 /** 2741 * Returns a new copy of the contained marshalled object. 2742 */ 2743 static Object getMarshalledObject(Object obj) 2744 throws IOException, ClassNotFoundException 2745 { 2746 try { 2747 return marshallGet.invoke(obj); 2748 } catch (IllegalAccessException x) { 2749 throw new AssertionError(x); 2750 } catch (InvocationTargetException x) { 2751 Throwable cause = x.getCause(); 2752 if (cause instanceof IOException) 2753 throw (IOException)cause; 2754 if (cause instanceof ClassNotFoundException) 2755 throw (ClassNotFoundException)cause; 2756 throw new AssertionError(x); 2757 } 2758 } 2759 } 2760 }