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