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