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