View Javadoc
1   /*
2    * Copyright (c) 2000, 2013, 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             // Source data is a URI list. Convert to DataFlavor.javaFileListFlavor
1602             // where possible.
1603         } else if (isURIListFormat(format)
1604                     && DataFlavor.javaFileListFlavor.equals(flavor)) {
1605 
1606             try (ByteArrayInputStream str = new ByteArrayInputStream(bytes))  {
1607 
1608                 URI uris[] = dragQueryURIs(str, format, localeTransferable);
1609                 if (uris == null) {
1610                     return null;
1611                 }
1612                 List<File> files = new ArrayList<>();
1613                 for (URI uri : uris) {
1614                     try {
1615                         files.add(new File(uri));
1616                     } catch (IllegalArgumentException illegalArg) {
1617                         // When converting from URIs to less generic files,
1618                         // common practice (Wine, SWT) seems to be to
1619                         // silently drop the URIs that aren't local files.
1620                     }
1621                 }
1622                 theObject = files;
1623             }
1624 
1625             // Target data is a String. Strip terminating NUL bytes. Decode bytes
1626             // into characters. Search-and-replace EOLN.
1627         } else if (String.class.equals(flavor.getRepresentationClass()) &&
1628                        isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1629 
1630             theObject = translateBytesToString(bytes, format, localeTransferable);
1631 
1632             // Target data is a Reader. Obtain data in InputStream format, encoded
1633             // as "Unicode" (utf-16be). Then use an InputStreamReader to decode
1634             // back to chars on demand.
1635         } else if (flavor.isRepresentationClassReader()) {
1636             try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
1637                 theObject = translateStream(bais,
1638                         flavor, format, localeTransferable);
1639             }
1640             // Target data is a CharBuffer. Recur to obtain String and wrap.
1641         } else if (flavor.isRepresentationClassCharBuffer()) {
1642             if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
1643                 throw new IOException
1644                           ("cannot transfer non-text data as CharBuffer");
1645             }
1646 
1647             CharBuffer buffer = CharBuffer.wrap(
1648                 translateBytesToString(bytes,format, localeTransferable));
1649 
1650             theObject = constructFlavoredObject(buffer, flavor, CharBuffer.class);
1651 
1652             // Target data is a char array. Recur to obtain String and convert to
1653             // char array.
1654         } else if (charArrayClass.equals(flavor.getRepresentationClass())) {
1655             if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
1656                 throw new IOException
1657                           ("cannot transfer non-text data as char array");
1658             }
1659 
1660             theObject = translateBytesToString(
1661                 bytes, format, localeTransferable).toCharArray();
1662 
1663             // Target data is a ByteBuffer. For arbitrary flavors, just return
1664             // the raw bytes. For text flavors, convert to a String to strip
1665             // terminators and search-and-replace EOLN, then reencode according to
1666             // the requested flavor.
1667         } else if (flavor.isRepresentationClassByteBuffer()) {
1668             if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1669                 bytes = translateBytesToString(
1670                     bytes, format, localeTransferable).getBytes(
1671                         DataTransferer.getTextCharset(flavor)
1672                     );
1673             }
1674 
1675             ByteBuffer buffer = ByteBuffer.wrap(bytes);
1676             theObject = constructFlavoredObject(buffer, flavor, ByteBuffer.class);
1677 
1678             // Target data is a byte array. For arbitrary flavors, just return
1679             // the raw bytes. For text flavors, convert to a String to strip
1680             // terminators and search-and-replace EOLN, then reencode according to
1681             // the requested flavor.
1682         } else if (byteArrayClass.equals(flavor.getRepresentationClass())) {
1683             if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1684                 theObject = translateBytesToString(
1685                     bytes, format, localeTransferable
1686                 ).getBytes(DataTransferer.getTextCharset(flavor));
1687             } else {
1688                 theObject = bytes;
1689             }
1690 
1691             // Target data is an InputStream. For arbitrary flavors, just return
1692             // the raw bytes. For text flavors, decode to strip terminators and
1693             // search-and-replace EOLN, then reencode according to the requested
1694             // flavor.
1695         } else if (flavor.isRepresentationClassInputStream()) {
1696 
1697             try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
1698                 theObject = translateStream(bais, flavor, format, localeTransferable);
1699             }
1700 
1701         } else if (flavor.isRepresentationClassRemote()) {
1702             try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
1703                  ObjectInputStream ois = new ObjectInputStream(bais))
1704             {
1705                 theObject = RMI.getMarshalledObject(ois.readObject());
1706             } catch (Exception e) {
1707                 throw new IOException(e.getMessage());
1708             }
1709 
1710             // Target data is Serializable
1711         } else if (flavor.isRepresentationClassSerializable()) {
1712 
1713             try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
1714                 theObject = translateStream(bais, flavor, format, localeTransferable);
1715             }
1716 
1717             // Target data is Image
1718         } else if (DataFlavor.imageFlavor.equals(flavor)) {
1719             if (!isImageFormat(format)) {
1720                 throw new IOException("data translation failed");
1721             }
1722 
1723             theObject = platformImageBytesToImage(bytes, format);
1724         }
1725 
1726         if (theObject == null) {
1727             throw new IOException("data translation failed");
1728         }
1729 
1730         return theObject;
1731 
1732     }
1733 
1734     /**
1735      * Primary translation function for translating
1736      * an InputStream into an Object, given a source format and a target
1737      * DataFlavor.
1738      */
1739     public Object translateStream(InputStream str, DataFlavor flavor,
1740                                   long format, Transferable localeTransferable)
1741         throws IOException
1742     {
1743 
1744         Object theObject = null;
1745         // Source data is a URI list. Convert to DataFlavor.javaFileListFlavor
1746         // where possible.
1747         if (isURIListFormat(format)
1748                 && DataFlavor.javaFileListFlavor.equals(flavor))
1749         {
1750 
1751             URI uris[] = dragQueryURIs(str, format, localeTransferable);
1752             if (uris == null) {
1753                 return null;
1754             }
1755             ArrayList files = new ArrayList();
1756             for (URI uri : uris) {
1757                 try {
1758                     files.add(new File(uri));
1759                 } catch (IllegalArgumentException illegalArg) {
1760                     // When converting from URIs to less generic files,
1761                     // common practice (Wine, SWT) seems to be to
1762                     // silently drop the URIs that aren't local files.
1763                 }
1764             }
1765             theObject = files;
1766 
1767         // Target data is a String. Strip terminating NUL bytes. Decode bytes
1768         // into characters. Search-and-replace EOLN.
1769         } else if (String.class.equals(flavor.getRepresentationClass()) &&
1770                    isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1771 
1772             return translateBytesToString(inputStreamToByteArray(str),
1773                 format, localeTransferable);
1774 
1775             // Special hack to maintain backwards-compatibility with the brokenness
1776             // of StringSelection. Return a StringReader instead of an InputStream.
1777             // Recur to obtain String and encapsulate.
1778         } else if (DataFlavor.plainTextFlavor.equals(flavor)) {
1779             theObject = new StringReader(translateBytesToString(
1780                 inputStreamToByteArray(str),
1781                 format, localeTransferable));
1782 
1783             // Target data is an InputStream. For arbitrary flavors, just return
1784             // the raw bytes. For text flavors, decode to strip terminators and
1785             // search-and-replace EOLN, then reencode according to the requested
1786             // flavor.
1787         } else if (flavor.isRepresentationClassInputStream()) {
1788             theObject = translateStreamToInputStream(str, flavor, format,
1789                                                                localeTransferable);
1790 
1791             // Target data is a Reader. Obtain data in InputStream format, encoded
1792             // as "Unicode" (utf-16be). Then use an InputStreamReader to decode
1793             // back to chars on demand.
1794         } else if (flavor.isRepresentationClassReader()) {
1795             if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
1796                 throw new IOException
1797                           ("cannot transfer non-text data as Reader");
1798             }
1799 
1800             InputStream is = (InputStream)translateStreamToInputStream(
1801                     str, DataFlavor.plainTextFlavor,
1802                     format, localeTransferable);
1803 
1804             String unicode = DataTransferer.getTextCharset(DataFlavor.plainTextFlavor);
1805 
1806             Reader reader = new InputStreamReader(is, unicode);
1807 
1808             theObject = constructFlavoredObject(reader, flavor, Reader.class);
1809             // Target data is a byte array
1810         } else if (byteArrayClass.equals(flavor.getRepresentationClass())) {
1811             if(isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1812                 theObject = translateBytesToString(inputStreamToByteArray(str), format, localeTransferable)
1813                         .getBytes(DataTransferer.getTextCharset(flavor));
1814             } else {
1815                 theObject = inputStreamToByteArray(str);
1816             }
1817             // Target data is an RMI object
1818         } else if (flavor.isRepresentationClassRemote()) {
1819 
1820             try (ObjectInputStream ois =
1821                      new ObjectInputStream(str))
1822             {
1823                 theObject = RMI.getMarshalledObject(ois.readObject());
1824             }catch (Exception e) {
1825                 throw new IOException(e.getMessage());
1826             }
1827 
1828             // Target data is Serializable
1829         } else if (flavor.isRepresentationClassSerializable()) {
1830             try (ObjectInputStream ois =
1831                      new ObjectInputStream(str))
1832             {
1833                 theObject = ois.readObject();
1834             } catch (Exception e) {
1835                 throw new IOException(e.getMessage());
1836             }
1837             // Target data is Image
1838         } else if (DataFlavor.imageFlavor.equals(flavor)) {
1839             if (!isImageFormat(format)) {
1840                 throw new IOException("data translation failed");
1841             }
1842             theObject = platformImageBytesToImage(inputStreamToByteArray(str), format);
1843         }
1844 
1845         if (theObject == null) {
1846             throw new IOException("data translation failed");
1847         }
1848 
1849         return theObject;
1850 
1851     }
1852 
1853     /**
1854      * For arbitrary flavors, just use the raw InputStream. For text flavors,
1855      * ReencodingInputStream will decode and reencode the InputStream on demand
1856      * so that we can strip terminators and search-and-replace EOLN.
1857      */
1858     private Object translateStreamToInputStream
1859         (InputStream str, DataFlavor flavor, long format,
1860          Transferable localeTransferable) throws IOException
1861     {
1862         if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1863             str = new ReencodingInputStream
1864                 (str, format, DataTransferer.getTextCharset(flavor),
1865                  localeTransferable);
1866         }
1867 
1868         return constructFlavoredObject(str, flavor, InputStream.class);
1869     }
1870 
1871     /**
1872      * We support representations which are exactly of the specified Class,
1873      * and also arbitrary Objects which have a constructor which takes an
1874      * instance of the Class as its sole parameter.
1875      */
1876     private Object constructFlavoredObject(Object arg, DataFlavor flavor,
1877                                            Class clazz)
1878         throws IOException
1879     {
1880         final Class dfrc = flavor.getRepresentationClass();
1881 
1882         if (clazz.equals(dfrc)) {
1883             return arg; // simple case
1884         } else {
1885             Constructor[] constructors = null;
1886 
1887             try {
1888                 constructors = (Constructor[])
1889                     AccessController.doPrivileged(new PrivilegedAction() {
1890                             public Object run() {
1891                                 return dfrc.getConstructors();
1892                             }
1893                         });
1894             } catch (SecurityException se) {
1895                 throw new IOException(se.getMessage());
1896             }
1897 
1898             Constructor constructor = null;
1899 
1900             for (int j = 0; j < constructors.length; j++) {
1901                 if (!Modifier.isPublic(constructors[j].getModifiers())) {
1902                     continue;
1903                 }
1904 
1905                 Class[] ptypes = constructors[j].getParameterTypes();
1906 
1907                 if (ptypes != null && ptypes.length == 1 &&
1908                     clazz.equals(ptypes[0])) {
1909                     constructor = constructors[j];
1910                     break;
1911                 }
1912             }
1913 
1914             if (constructor == null) {
1915                 throw new IOException("can't find <init>(L"+ clazz +
1916                                       ";)V for class: " + dfrc.getName());
1917             }
1918 
1919             try {
1920                 return constructor.newInstance(new Object[] { arg } );
1921             } catch (Exception e) {
1922                 throw new IOException(e.getMessage());
1923             }
1924         }
1925     }
1926 
1927     /**
1928      * Used for decoding and reencoding an InputStream on demand so that we
1929      * can strip NUL terminators and perform EOLN search-and-replace.
1930      */
1931     public class ReencodingInputStream extends InputStream {
1932         protected BufferedReader wrapped;
1933         protected final char[] in = new char[2];
1934         protected byte[] out;
1935 
1936         protected CharsetEncoder encoder;
1937         protected CharBuffer inBuf;
1938         protected ByteBuffer outBuf;
1939 
1940         protected char[] eoln;
1941         protected int numTerminators;
1942 
1943         protected boolean eos;
1944         protected int index, limit;
1945 
1946         public ReencodingInputStream(InputStream bytestream, long format,
1947                                      String targetEncoding,
1948                                      Transferable localeTransferable)
1949             throws IOException
1950         {
1951             Long lFormat = Long.valueOf(format);
1952 
1953             String sourceEncoding = null;
1954             if (isLocaleDependentTextFormat(format) &&
1955                 localeTransferable != null &&
1956                 localeTransferable.
1957                     isDataFlavorSupported(javaTextEncodingFlavor))
1958             {
1959                 try {
1960                     sourceEncoding = new String((byte[])localeTransferable.
1961                                        getTransferData(javaTextEncodingFlavor),
1962                                        "UTF-8");
1963                 } catch (UnsupportedFlavorException cannotHappen) {
1964                 }
1965             } else {
1966                 sourceEncoding = getCharsetForTextFormat(lFormat);
1967             }
1968 
1969             if (sourceEncoding == null) {
1970                 // Only happens when we have a custom text type.
1971                 sourceEncoding = getDefaultTextCharset();
1972             }
1973             wrapped = new BufferedReader
1974                 (new InputStreamReader(bytestream, sourceEncoding));
1975 
1976             if (targetEncoding == null) {
1977                 // Throw NullPointerException for compatibility with the former
1978                 // call to sun.io.CharToByteConverter.getConverter(null)
1979                 // (Charset.forName(null) throws unspecified IllegalArgumentException
1980                 // now; see 6228568)
1981                 throw new NullPointerException("null target encoding");
1982             }
1983 
1984             try {
1985                 encoder = Charset.forName(targetEncoding).newEncoder();
1986                 out = new byte[(int)(encoder.maxBytesPerChar() * 2 + 0.5)];
1987                 inBuf = CharBuffer.wrap(in);
1988                 outBuf = ByteBuffer.wrap(out);
1989             } catch (IllegalCharsetNameException e) {
1990                 throw new IOException(e.toString());
1991             } catch (UnsupportedCharsetException e) {
1992                 throw new IOException(e.toString());
1993             } catch (UnsupportedOperationException e) {
1994                 throw new IOException(e.toString());
1995             }
1996 
1997             String sEoln = (String)nativeEOLNs.get(lFormat);
1998             if (sEoln != null) {
1999                 eoln = sEoln.toCharArray();
2000             }
2001 
2002             // A hope and a prayer that this works generically. This will
2003             // definitely work on Win32.
2004             Integer terminators = (Integer)nativeTerminators.get(lFormat);
2005             if (terminators != null) {
2006                 numTerminators = terminators.intValue();
2007             }
2008         }
2009 
2010         private int readChar() throws IOException {
2011             int c = wrapped.read();
2012 
2013             if (c == -1) { // -1 is EOS
2014                 eos = true;
2015                 return -1;
2016             }
2017 
2018             // "c == 0" is not quite correct, but good enough on Windows.
2019             if (numTerminators > 0 && c == 0) {
2020                 eos = true;
2021                 return -1;
2022             } else if (eoln != null && matchCharArray(eoln, c)) {
2023                 c = '\n' & 0xFFFF;
2024             }
2025 
2026             return c;
2027         }
2028 
2029         public int read() throws IOException {
2030             if (eos) {
2031                 return -1;
2032             }
2033 
2034             if (index >= limit) {
2035                 // deal with supplementary characters
2036                 int c = readChar();
2037                 if (c == -1) {
2038                     return -1;
2039                 }
2040 
2041                 in[0] = (char) c;
2042                 in[1] = 0;
2043                 inBuf.limit(1);
2044                 if (Character.isHighSurrogate((char) c)) {
2045                     c = readChar();
2046                     if (c != -1) {
2047                         in[1] = (char) c;
2048                         inBuf.limit(2);
2049                     }
2050                 }
2051 
2052                 inBuf.rewind();
2053                 outBuf.limit(out.length).rewind();
2054                 encoder.encode(inBuf, outBuf, false);
2055                 outBuf.flip();
2056                 limit = outBuf.limit();
2057 
2058                 index = 0;
2059 
2060                 return read();
2061             } else {
2062                 return out[index++] & 0xFF;
2063             }
2064         }
2065 
2066         public int available() throws IOException {
2067             return ((eos) ? 0 : (limit - index));
2068         }
2069 
2070         public void close() throws IOException {
2071             wrapped.close();
2072         }
2073 
2074         /**
2075          * Checks to see if the next array.length characters in wrapped
2076          * match array. The first character is provided as c. Subsequent
2077          * characters are read from wrapped itself. When this method returns,
2078          * the wrapped index may be different from what it was when this
2079          * method was called.
2080          */
2081         private boolean matchCharArray(char[] array, int c)
2082             throws IOException
2083         {
2084             wrapped.mark(array.length);  // BufferedReader supports mark
2085 
2086             int count = 0;
2087             if ((char)c == array[0]) {
2088                 for (count = 1; count < array.length; count++) {
2089                     c = wrapped.read();
2090                     if (c == -1 || ((char)c) != array[count]) {
2091                         break;
2092                     }
2093                 }
2094             }
2095 
2096             if (count == array.length) {
2097                 return true;
2098             } else {
2099                 wrapped.reset();
2100                 return false;
2101             }
2102         }
2103     }
2104 
2105     /**
2106      * Decodes a byte array into a set of String filenames.
2107      */
2108     protected abstract String[] dragQueryFile(byte[] bytes);
2109 
2110     /**
2111      * Decodes URIs from either a byte array or a stream.
2112      */
2113     protected URI[] dragQueryURIs(InputStream stream,
2114                                   long format,
2115                                   Transferable localeTransferable)
2116       throws IOException
2117     {
2118         throw new IOException(
2119             new UnsupportedOperationException("not implemented on this platform"));
2120     }
2121 
2122     /**
2123      * Translates either a byte array or an input stream which contain
2124      * platform-specific image data in the given format into an Image.
2125      */
2126 
2127 
2128     protected abstract Image platformImageBytesToImage(
2129         byte[] bytes,long format) throws IOException;
2130 
2131     /**
2132      * Translates either a byte array or an input stream which contain
2133      * an image data in the given standard format into an Image.
2134      *
2135      * @param mimeType image MIME type, such as: image/png, image/jpeg, image/gif
2136      */
2137     protected Image standardImageBytesToImage(
2138         byte[] bytes, String mimeType) throws IOException
2139     {
2140 
2141         Iterator readerIterator = ImageIO.getImageReadersByMIMEType(mimeType);
2142 
2143         if (!readerIterator.hasNext()) {
2144             throw new IOException("No registered service provider can decode " +
2145                                   " an image from " + mimeType);
2146         }
2147 
2148         IOException ioe = null;
2149 
2150         while (readerIterator.hasNext()) {
2151             ImageReader imageReader = (ImageReader)readerIterator.next();
2152             try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
2153                 ImageInputStream imageInputStream =
2154                     ImageIO.createImageInputStream(bais);
2155 
2156                 try {
2157                     ImageReadParam param = imageReader.getDefaultReadParam();
2158                     imageReader.setInput(imageInputStream, true, true);
2159                     BufferedImage bufferedImage =
2160                         imageReader.read(imageReader.getMinIndex(), param);
2161                     if (bufferedImage != null) {
2162                         return bufferedImage;
2163                     }
2164                 } finally {
2165                     imageInputStream.close();
2166                     imageReader.dispose();
2167                 }
2168             } catch (IOException e) {
2169                 ioe = e;
2170                 continue;
2171             }
2172         }
2173 
2174         if (ioe == null) {
2175             ioe = new IOException("Registered service providers failed to decode"
2176                                   + " an image from " + mimeType);
2177         }
2178 
2179         throw ioe;
2180     }
2181 
2182     /**
2183      * Translates a Java Image into a byte array which contains platform-
2184      * specific image data in the given format.
2185      */
2186     protected abstract byte[] imageToPlatformBytes(Image image, long format)
2187       throws IOException;
2188 
2189     /**
2190      * Translates a Java Image into a byte array which contains
2191      * an image data in the given standard format.
2192      *
2193      * @param mimeType image MIME type, such as: image/png, image/jpeg
2194      */
2195     protected byte[] imageToStandardBytes(Image image, String mimeType)
2196       throws IOException {
2197         IOException originalIOE = null;
2198 
2199         Iterator writerIterator = ImageIO.getImageWritersByMIMEType(mimeType);
2200 
2201         if (!writerIterator.hasNext()) {
2202             throw new IOException("No registered service provider can encode " +
2203                                   " an image to " + mimeType);
2204         }
2205 
2206         if (image instanceof RenderedImage) {
2207             // Try to encode the original image.
2208             try {
2209                 return imageToStandardBytesImpl((RenderedImage)image, mimeType);
2210             } catch (IOException ioe) {
2211                 originalIOE = ioe;
2212             }
2213         }
2214 
2215         // Retry with a BufferedImage.
2216         int width = 0;
2217         int height = 0;
2218         if (image instanceof ToolkitImage) {
2219             ImageRepresentation ir = ((ToolkitImage)image).getImageRep();
2220             ir.reconstruct(ImageObserver.ALLBITS);
2221             width = ir.getWidth();
2222             height = ir.getHeight();
2223         } else {
2224             width = image.getWidth(null);
2225             height = image.getHeight(null);
2226         }
2227 
2228         ColorModel model = ColorModel.getRGBdefault();
2229         WritableRaster raster =
2230             model.createCompatibleWritableRaster(width, height);
2231 
2232         BufferedImage bufferedImage =
2233             new BufferedImage(model, raster, model.isAlphaPremultiplied(),
2234                               null);
2235 
2236         Graphics g = bufferedImage.getGraphics();
2237         try {
2238             g.drawImage(image, 0, 0, width, height, null);
2239         } finally {
2240             g.dispose();
2241         }
2242 
2243         try {
2244             return imageToStandardBytesImpl(bufferedImage, mimeType);
2245         } catch (IOException ioe) {
2246             if (originalIOE != null) {
2247                 throw originalIOE;
2248             } else {
2249                 throw ioe;
2250             }
2251         }
2252     }
2253 
2254     protected byte[] imageToStandardBytesImpl(RenderedImage renderedImage,
2255                                               String mimeType)
2256         throws IOException {
2257 
2258         Iterator writerIterator = ImageIO.getImageWritersByMIMEType(mimeType);
2259 
2260         ImageTypeSpecifier typeSpecifier =
2261             new ImageTypeSpecifier(renderedImage);
2262 
2263         ByteArrayOutputStream baos = new ByteArrayOutputStream();
2264         IOException ioe = null;
2265 
2266         while (writerIterator.hasNext()) {
2267             ImageWriter imageWriter = (ImageWriter)writerIterator.next();
2268             ImageWriterSpi writerSpi = imageWriter.getOriginatingProvider();
2269 
2270             if (!writerSpi.canEncodeImage(typeSpecifier)) {
2271                 continue;
2272             }
2273 
2274             try {
2275                 ImageOutputStream imageOutputStream =
2276                     ImageIO.createImageOutputStream(baos);
2277                 try {
2278                     imageWriter.setOutput(imageOutputStream);
2279                     imageWriter.write(renderedImage);
2280                     imageOutputStream.flush();
2281                 } finally {
2282                     imageOutputStream.close();
2283                 }
2284             } catch (IOException e) {
2285                 imageWriter.dispose();
2286                 baos.reset();
2287                 ioe = e;
2288                 continue;
2289             }
2290 
2291             imageWriter.dispose();
2292             baos.close();
2293             return baos.toByteArray();
2294         }
2295 
2296         baos.close();
2297 
2298         if (ioe == null) {
2299             ioe = new IOException("Registered service providers failed to encode "
2300                                   + renderedImage + " to " + mimeType);
2301         }
2302 
2303         throw ioe;
2304     }
2305 
2306     /**
2307      * Concatenates the data represented by two objects. Objects can be either
2308      * byte arrays or instances of <code>InputStream</code>. If both arguments
2309      * are byte arrays byte array will be returned. Otherwise an
2310      * <code>InputStream</code> will be returned.
2311      * <p>
2312      * Currently is only called from native code to prepend palette data to
2313      * platform-specific image data during image transfer on Win32.
2314      *
2315      * @param obj1 the first object to be concatenated.
2316      * @param obj2 the second object to be concatenated.
2317      * @return a byte array or an <code>InputStream</code> which represents
2318      *         a logical concatenation of the two arguments.
2319      * @throws NullPointerException is either of the arguments is
2320      *         <code>null</code>
2321      * @throws ClassCastException is either of the arguments is
2322      *         neither byte array nor an instance of <code>InputStream</code>.
2323      */
2324     private Object concatData(Object obj1, Object obj2) {
2325         InputStream str1 = null;
2326         InputStream str2 = null;
2327 
2328         if (obj1 instanceof byte[]) {
2329             byte[] arr1 = (byte[])obj1;
2330             if (obj2 instanceof byte[]) {
2331                 byte[] arr2 = (byte[])obj2;
2332                 byte[] ret = new byte[arr1.length + arr2.length];
2333                 System.arraycopy(arr1, 0, ret, 0, arr1.length);
2334                 System.arraycopy(arr2, 0, ret, arr1.length, arr2.length);
2335                 return ret;
2336             } else {
2337                 str1 = new ByteArrayInputStream(arr1);
2338                 str2 = (InputStream)obj2;
2339             }
2340         } else {
2341             str1 = (InputStream)obj1;
2342             if (obj2 instanceof byte[]) {
2343                 str2 = new ByteArrayInputStream((byte[])obj2);
2344             } else {
2345                 str2 = (InputStream)obj2;
2346             }
2347         }
2348 
2349         return new SequenceInputStream(str1, str2);
2350     }
2351 
2352     public byte[] convertData(final Object source,
2353                               final Transferable contents,
2354                               final long format,
2355                               final Map formatMap,
2356                               final boolean isToolkitThread)
2357         throws IOException
2358     {
2359         byte[] ret = null;
2360 
2361         /*
2362          * If the current thread is the Toolkit thread we should post a
2363          * Runnable to the event dispatch thread associated with source Object,
2364          * since translateTransferable() calls Transferable.getTransferData()
2365          * that may contain client code.
2366          */
2367         if (isToolkitThread) try {
2368             final Stack stack = new Stack();
2369             final Runnable dataConverter = new Runnable() {
2370                 // Guard against multiple executions.
2371                 private boolean done = false;
2372                 public void run() {
2373                     if (done) {
2374                         return;
2375                     }
2376                     byte[] data = null;
2377                     try {
2378                         DataFlavor flavor = (DataFlavor)formatMap.get(Long.valueOf(format));
2379                         if (flavor != null) {
2380                             data = translateTransferable(contents, flavor, format);
2381                         }
2382                     } catch (Exception e) {
2383                         e.printStackTrace();
2384                         data = null;
2385                     }
2386                     try {
2387                         getToolkitThreadBlockedHandler().lock();
2388                         stack.push(data);
2389                         getToolkitThreadBlockedHandler().exit();
2390                     } finally {
2391                         getToolkitThreadBlockedHandler().unlock();
2392                         done = true;
2393                     }
2394                 }
2395             };
2396 
2397             final AppContext appContext = SunToolkit.targetToAppContext(source);
2398 
2399             getToolkitThreadBlockedHandler().lock();
2400 
2401             if (appContext != null) {
2402                 appContext.put(DATA_CONVERTER_KEY, dataConverter);
2403             }
2404 
2405             SunToolkit.executeOnEventHandlerThread(source, dataConverter);
2406 
2407             while (stack.empty()) {
2408                 getToolkitThreadBlockedHandler().enter();
2409             }
2410 
2411             if (appContext != null) {
2412                 appContext.remove(DATA_CONVERTER_KEY);
2413             }
2414 
2415             ret = (byte[])stack.pop();
2416         } finally {
2417             getToolkitThreadBlockedHandler().unlock();
2418         } else {
2419             DataFlavor flavor = (DataFlavor)
2420                 formatMap.get(Long.valueOf(format));
2421             if (flavor != null) {
2422                 ret = translateTransferable(contents, flavor, format);
2423             }
2424         }
2425 
2426         return ret;
2427     }
2428 
2429     public void processDataConversionRequests() {
2430         if (EventQueue.isDispatchThread()) {
2431             AppContext appContext = AppContext.getAppContext();
2432             getToolkitThreadBlockedHandler().lock();
2433             try {
2434                 Runnable dataConverter =
2435                     (Runnable)appContext.get(DATA_CONVERTER_KEY);
2436                 if (dataConverter != null) {
2437                     dataConverter.run();
2438                     appContext.remove(DATA_CONVERTER_KEY);
2439                 }
2440             } finally {
2441                 getToolkitThreadBlockedHandler().unlock();
2442             }
2443         }
2444     }
2445 
2446     public abstract ToolkitThreadBlockedHandler
2447         getToolkitThreadBlockedHandler();
2448 
2449     /**
2450      * Helper function to reduce a Map with Long keys to a long array.
2451      * <p>
2452      * The map keys are sorted according to the native formats preference
2453      * order.
2454      */
2455     public static long[] keysToLongArray(SortedMap map) {
2456         Set keySet = map.keySet();
2457         long[] retval = new long[keySet.size()];
2458         int i = 0;
2459         for (Iterator iter = keySet.iterator(); iter.hasNext(); i++) {
2460             retval[i] = ((Long)iter.next()).longValue();
2461         }
2462         return retval;
2463     }
2464 
2465     /**
2466      * Helper function to convert a Set of DataFlavors to a sorted array.
2467      * The array will be sorted according to <code>DataFlavorComparator</code>.
2468      */
2469     public static DataFlavor[] setToSortedDataFlavorArray(Set flavorsSet) {
2470         DataFlavor[] flavors = new DataFlavor[flavorsSet.size()];
2471         flavorsSet.toArray(flavors);
2472         final Comparator comparator =
2473                 new DataFlavorComparator(IndexedComparator.SELECT_WORST);
2474         Arrays.sort(flavors, comparator);
2475         return flavors;
2476     }
2477 
2478     /**
2479      * Helper function to convert an InputStream to a byte[] array.
2480      */
2481     protected static byte[] inputStreamToByteArray(InputStream str)
2482         throws IOException
2483     {
2484         try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
2485             int len = 0;
2486             byte[] buf = new byte[8192];
2487 
2488             while ((len = str.read(buf)) != -1) {
2489                 baos.write(buf, 0, len);
2490             }
2491 
2492             return baos.toByteArray();
2493         }
2494     }
2495 
2496     /**
2497      * Returns platform-specific mappings for the specified native.
2498      * If there are no platform-specific mappings for this native, the method
2499      * returns an empty <code>List</code>.
2500      */
2501     public List getPlatformMappingsForNative(String nat) {
2502         return new ArrayList();
2503     }
2504 
2505     /**
2506      * Returns platform-specific mappings for the specified flavor.
2507      * If there are no platform-specific mappings for this flavor, the method
2508      * returns an empty <code>List</code>.
2509      */
2510     public List getPlatformMappingsForFlavor(DataFlavor df) {
2511         return new ArrayList();
2512     }
2513 
2514     /**
2515      * A Comparator which includes a helper function for comparing two Objects
2516      * which are likely to be keys in the specified Map.
2517      */
2518     public abstract static class IndexedComparator implements Comparator {
2519 
2520         /**
2521          * The best Object (e.g., DataFlavor) will be the last in sequence.
2522          */
2523         public static final boolean SELECT_BEST = true;
2524 
2525         /**
2526          * The best Object (e.g., DataFlavor) will be the first in sequence.
2527          */
2528         public static final boolean SELECT_WORST = false;
2529 
2530         protected final boolean order;
2531 
2532         public IndexedComparator() {
2533             this(SELECT_BEST);
2534         }
2535 
2536         public IndexedComparator(boolean order) {
2537             this.order = order;
2538         }
2539 
2540         /**
2541          * Helper method to compare two objects by their Integer indices in the
2542          * given map. If the map doesn't contain an entry for either of the
2543          * objects, the fallback index will be used for the object instead.
2544          *
2545          * @param indexMap the map which maps objects into Integer indexes.
2546          * @param obj1 the first object to be compared.
2547          * @param obj2 the second object to be compared.
2548          * @param fallbackIndex the Integer to be used as a fallback index.
2549          * @return a negative integer, zero, or a positive integer as the
2550          *             first object is mapped to a less, equal to, or greater
2551          *             index than the second.
2552          */
2553         protected static int compareIndices(Map indexMap,
2554                                             Object obj1, Object obj2,
2555                                             Integer fallbackIndex) {
2556             Integer index1 = (Integer)indexMap.get(obj1);
2557             Integer index2 = (Integer)indexMap.get(obj2);
2558 
2559             if (index1 == null) {
2560                 index1 = fallbackIndex;
2561             }
2562             if (index2 == null) {
2563                 index2 = fallbackIndex;
2564             }
2565 
2566             return index1.compareTo(index2);
2567         }
2568 
2569         /**
2570          * Helper method to compare two objects by their Long indices in the
2571          * given map. If the map doesn't contain an entry for either of the
2572          * objects, the fallback index will be used for the object instead.
2573          *
2574          * @param indexMap the map which maps objects into Long indexes.
2575          * @param obj1 the first object to be compared.
2576          * @param obj2 the second object to be compared.
2577          * @param fallbackIndex the Long to be used as a fallback index.
2578          * @return a negative integer, zero, or a positive integer as the
2579          *             first object is mapped to a less, equal to, or greater
2580          *             index than the second.
2581          */
2582         protected static int compareLongs(Map indexMap,
2583                                           Object obj1, Object obj2,
2584                                           Long fallbackIndex) {
2585             Long index1 = (Long)indexMap.get(obj1);
2586             Long index2 = (Long)indexMap.get(obj2);
2587 
2588             if (index1 == null) {
2589                 index1 = fallbackIndex;
2590             }
2591             if (index2 == null) {
2592                 index2 = fallbackIndex;
2593             }
2594 
2595             return index1.compareTo(index2);
2596         }
2597     }
2598 
2599     /**
2600      * An IndexedComparator which compares two String charsets. The comparison
2601      * follows the rules outlined in DataFlavor.selectBestTextFlavor. In order
2602      * to ensure that non-Unicode, non-ASCII, non-default charsets are sorted
2603      * in alphabetical order, charsets are not automatically converted to their
2604      * canonical forms.
2605      */
2606     public static class CharsetComparator extends IndexedComparator {
2607         private static final Map charsets;
2608         private static String defaultEncoding;
2609 
2610         private static final Integer DEFAULT_CHARSET_INDEX = Integer.valueOf(2);
2611         private static final Integer OTHER_CHARSET_INDEX = Integer.valueOf(1);
2612         private static final Integer WORST_CHARSET_INDEX = Integer.valueOf(0);
2613         private static final Integer UNSUPPORTED_CHARSET_INDEX =
2614             Integer.valueOf(Integer.MIN_VALUE);
2615 
2616         private static final String UNSUPPORTED_CHARSET = "UNSUPPORTED";
2617 
2618         static {
2619             HashMap charsetsMap = new HashMap(8, 1.0f);
2620 
2621             // we prefer Unicode charsets
2622             charsetsMap.put(canonicalName("UTF-16LE"), Integer.valueOf(4));
2623             charsetsMap.put(canonicalName("UTF-16BE"), Integer.valueOf(5));
2624             charsetsMap.put(canonicalName("UTF-8"), Integer.valueOf(6));
2625             charsetsMap.put(canonicalName("UTF-16"), Integer.valueOf(7));
2626 
2627             // US-ASCII is the worst charset supported
2628             charsetsMap.put(canonicalName("US-ASCII"), WORST_CHARSET_INDEX);
2629 
2630             String defEncoding = DataTransferer.canonicalName
2631                 (DataTransferer.getDefaultTextCharset());
2632 
2633             if (charsetsMap.get(defaultEncoding) == null) {
2634                 charsetsMap.put(defaultEncoding, DEFAULT_CHARSET_INDEX);
2635             }
2636             charsetsMap.put(UNSUPPORTED_CHARSET, UNSUPPORTED_CHARSET_INDEX);
2637 
2638             charsets = Collections.unmodifiableMap(charsetsMap);
2639         }
2640 
2641         public CharsetComparator() {
2642             this(SELECT_BEST);
2643         }
2644 
2645         public CharsetComparator(boolean order) {
2646             super(order);
2647         }
2648 
2649         /**
2650          * Compares two String objects. Returns a negative integer, zero,
2651          * or a positive integer as the first charset is worse than, equal to,
2652          * or better than the second.
2653          *
2654          * @param obj1 the first charset to be compared
2655          * @param obj2 the second charset to be compared
2656          * @return a negative integer, zero, or a positive integer as the
2657          *         first argument is worse, equal to, or better than the
2658          *         second.
2659          * @throws ClassCastException if either of the arguments is not
2660          *         instance of String
2661          * @throws NullPointerException if either of the arguments is
2662          *         <code>null</code>.
2663          */
2664         public int compare(Object obj1, Object obj2) {
2665             String charset1 = null;
2666             String charset2 = null;
2667             if (order == SELECT_BEST) {
2668                 charset1 = (String)obj1;
2669                 charset2 = (String)obj2;
2670             } else {
2671                 charset1 = (String)obj2;
2672                 charset2 = (String)obj1;
2673             }
2674 
2675             return compareCharsets(charset1, charset2);
2676         }
2677 
2678         /**
2679          * Compares charsets. Returns a negative integer, zero, or a positive
2680          * integer as the first charset is worse than, equal to, or better than
2681          * the second.
2682          * <p>
2683          * Charsets are ordered according to the following rules:
2684          * <ul>
2685          * <li>All unsupported charsets are equal.
2686          * <li>Any unsupported charset is worse than any supported charset.
2687          * <li>Unicode charsets, such as "UTF-16", "UTF-8", "UTF-16BE" and
2688          *     "UTF-16LE", are considered best.
2689          * <li>After them, platform default charset is selected.
2690          * <li>"US-ASCII" is the worst of supported charsets.
2691          * <li>For all other supported charsets, the lexicographically less
2692          *     one is considered the better.
2693          * </ul>
2694          *
2695          * @param charset1 the first charset to be compared
2696          * @param charset2 the second charset to be compared.
2697          * @return a negative integer, zero, or a positive integer as the
2698          *             first argument is worse, equal to, or better than the
2699          *             second.
2700          */
2701         protected int compareCharsets(String charset1, String charset2) {
2702             charset1 = getEncoding(charset1);
2703             charset2 = getEncoding(charset2);
2704 
2705             int comp = compareIndices(charsets, charset1, charset2,
2706                                       OTHER_CHARSET_INDEX);
2707 
2708             if (comp == 0) {
2709                 return charset2.compareTo(charset1);
2710             }
2711 
2712             return comp;
2713         }
2714 
2715         /**
2716          * Returns encoding for the specified charset according to the
2717          * following rules:
2718          * <ul>
2719          * <li>If the charset is <code>null</code>, then <code>null</code> will
2720          *     be returned.
2721          * <li>Iff the charset specifies an encoding unsupported by this JRE,
2722          *     <code>UNSUPPORTED_CHARSET</code> will be returned.
2723          * <li>If the charset specifies an alias name, the corresponding
2724          *     canonical name will be returned iff the charset is a known
2725          *     Unicode, ASCII, or default charset.
2726          * </ul>
2727          *
2728          * @param charset the charset.
2729          * @return an encoding for this charset.
2730          */
2731         protected static String getEncoding(String charset) {
2732             if (charset == null) {
2733                 return null;
2734             } else if (!DataTransferer.isEncodingSupported(charset)) {
2735                 return UNSUPPORTED_CHARSET;
2736             } else {
2737                 // Only convert to canonical form if the charset is one
2738                 // of the charsets explicitly listed in the known charsets
2739                 // map. This will happen only for Unicode, ASCII, or default
2740                 // charsets.
2741                 String canonicalName = DataTransferer.canonicalName(charset);
2742                 return (charsets.containsKey(canonicalName))
2743                     ? canonicalName
2744                     : charset;
2745             }
2746         }
2747     }
2748 
2749     /**
2750      * An IndexedComparator which compares two DataFlavors. For text flavors,
2751      * the comparison follows the rules outlined in
2752      * DataFlavor.selectBestTextFlavor. For non-text flavors, unknown
2753      * application MIME types are preferred, followed by known
2754      * application/x-java-* MIME types. Unknown application types are preferred
2755      * because if the user provides his own data flavor, it will likely be the
2756      * most descriptive one. For flavors which are otherwise equal, the
2757      * flavors' string representation are compared in the alphabetical order.
2758      */
2759     public static class DataFlavorComparator extends IndexedComparator {
2760 
2761         private final CharsetComparator charsetComparator;
2762 
2763         private static final Map exactTypes;
2764         private static final Map primaryTypes;
2765         private static final Map nonTextRepresentations;
2766         private static final Map textTypes;
2767         private static final Map decodedTextRepresentations;
2768         private static final Map encodedTextRepresentations;
2769 
2770         private static final Integer UNKNOWN_OBJECT_LOSES =
2771             Integer.valueOf(Integer.MIN_VALUE);
2772         private static final Integer UNKNOWN_OBJECT_WINS =
2773             Integer.valueOf(Integer.MAX_VALUE);
2774 
2775         private static final Long UNKNOWN_OBJECT_LOSES_L =
2776             Long.valueOf(Long.MIN_VALUE);
2777         private static final Long UNKNOWN_OBJECT_WINS_L =
2778             Long.valueOf(Long.MAX_VALUE);
2779 
2780         static {
2781             {
2782                 HashMap exactTypesMap = new HashMap(4, 1.0f);
2783 
2784                 // application/x-java-* MIME types
2785                 exactTypesMap.put("application/x-java-file-list",
2786                                   Integer.valueOf(0));
2787                 exactTypesMap.put("application/x-java-serialized-object",
2788                                   Integer.valueOf(1));
2789                 exactTypesMap.put("application/x-java-jvm-local-objectref",
2790                                   Integer.valueOf(2));
2791                 exactTypesMap.put("application/x-java-remote-object",
2792                                   Integer.valueOf(3));
2793 
2794                 exactTypes = Collections.unmodifiableMap(exactTypesMap);
2795             }
2796 
2797             {
2798                 HashMap primaryTypesMap = new HashMap(1, 1.0f);
2799 
2800                 primaryTypesMap.put("application", Integer.valueOf(0));
2801 
2802                 primaryTypes = Collections.unmodifiableMap(primaryTypesMap);
2803             }
2804 
2805             {
2806                 HashMap nonTextRepresentationsMap = new HashMap(3, 1.0f);
2807 
2808                 nonTextRepresentationsMap.put(java.io.InputStream.class,
2809                                               Integer.valueOf(0));
2810                 nonTextRepresentationsMap.put(java.io.Serializable.class,
2811                                               Integer.valueOf(1));
2812 
2813                 Class<?> remoteClass = RMI.remoteClass();
2814                 if (remoteClass != null) {
2815                     nonTextRepresentationsMap.put(remoteClass,
2816                                                   Integer.valueOf(2));
2817                 }
2818 
2819                 nonTextRepresentations =
2820                     Collections.unmodifiableMap(nonTextRepresentationsMap);
2821             }
2822 
2823             {
2824                 HashMap textTypesMap = new HashMap(16, 1.0f);
2825 
2826                 // plain text
2827                 textTypesMap.put("text/plain", Integer.valueOf(0));
2828 
2829                 // stringFlavor
2830                 textTypesMap.put("application/x-java-serialized-object",
2831                                 Integer.valueOf(1));
2832 
2833                 // misc
2834                 textTypesMap.put("text/calendar", Integer.valueOf(2));
2835                 textTypesMap.put("text/css", Integer.valueOf(3));
2836                 textTypesMap.put("text/directory", Integer.valueOf(4));
2837                 textTypesMap.put("text/parityfec", Integer.valueOf(5));
2838                 textTypesMap.put("text/rfc822-headers", Integer.valueOf(6));
2839                 textTypesMap.put("text/t140", Integer.valueOf(7));
2840                 textTypesMap.put("text/tab-separated-values", Integer.valueOf(8));
2841                 textTypesMap.put("text/uri-list", Integer.valueOf(9));
2842 
2843                 // enriched
2844                 textTypesMap.put("text/richtext", Integer.valueOf(10));
2845                 textTypesMap.put("text/enriched", Integer.valueOf(11));
2846                 textTypesMap.put("text/rtf", Integer.valueOf(12));
2847 
2848                 // markup
2849                 textTypesMap.put("text/html", Integer.valueOf(13));
2850                 textTypesMap.put("text/xml", Integer.valueOf(14));
2851                 textTypesMap.put("text/sgml", Integer.valueOf(15));
2852 
2853                 textTypes = Collections.unmodifiableMap(textTypesMap);
2854             }
2855 
2856             {
2857                 HashMap decodedTextRepresentationsMap = new HashMap(4, 1.0f);
2858 
2859                 decodedTextRepresentationsMap.put
2860                     (DataTransferer.charArrayClass, Integer.valueOf(0));
2861                 decodedTextRepresentationsMap.put
2862                     (java.nio.CharBuffer.class, Integer.valueOf(1));
2863                 decodedTextRepresentationsMap.put
2864                     (java.lang.String.class, Integer.valueOf(2));
2865                 decodedTextRepresentationsMap.put
2866                     (java.io.Reader.class, Integer.valueOf(3));
2867 
2868                 decodedTextRepresentations =
2869                     Collections.unmodifiableMap(decodedTextRepresentationsMap);
2870             }
2871 
2872             {
2873                 HashMap encodedTextRepresentationsMap = new HashMap(3, 1.0f);
2874 
2875                 encodedTextRepresentationsMap.put
2876                     (DataTransferer.byteArrayClass, Integer.valueOf(0));
2877                 encodedTextRepresentationsMap.put
2878                     (java.nio.ByteBuffer.class, Integer.valueOf(1));
2879                 encodedTextRepresentationsMap.put
2880                     (java.io.InputStream.class, Integer.valueOf(2));
2881 
2882                 encodedTextRepresentations =
2883                     Collections.unmodifiableMap(encodedTextRepresentationsMap);
2884             }
2885         }
2886 
2887         public DataFlavorComparator() {
2888             this(SELECT_BEST);
2889         }
2890 
2891         public DataFlavorComparator(boolean order) {
2892             super(order);
2893 
2894             charsetComparator = new CharsetComparator(order);
2895         }
2896 
2897         public int compare(Object obj1, Object obj2) {
2898             DataFlavor flavor1 = null;
2899             DataFlavor flavor2 = null;
2900             if (order == SELECT_BEST) {
2901                 flavor1 = (DataFlavor)obj1;
2902                 flavor2 = (DataFlavor)obj2;
2903             } else {
2904                 flavor1 = (DataFlavor)obj2;
2905                 flavor2 = (DataFlavor)obj1;
2906             }
2907 
2908             if (flavor1.equals(flavor2)) {
2909                 return 0;
2910             }
2911 
2912             int comp = 0;
2913 
2914             String primaryType1 = flavor1.getPrimaryType();
2915             String subType1 = flavor1.getSubType();
2916             String mimeType1 = primaryType1 + "/" + subType1;
2917             Class class1 = flavor1.getRepresentationClass();
2918 
2919             String primaryType2 = flavor2.getPrimaryType();
2920             String subType2 = flavor2.getSubType();
2921             String mimeType2 = primaryType2 + "/" + subType2;
2922             Class class2 = flavor2.getRepresentationClass();
2923 
2924             if (flavor1.isFlavorTextType() && flavor2.isFlavorTextType()) {
2925                 // First, compare MIME types
2926                 comp = compareIndices(textTypes, mimeType1, mimeType2,
2927                                       UNKNOWN_OBJECT_LOSES);
2928                 if (comp != 0) {
2929                     return comp;
2930                 }
2931 
2932                 // Only need to test one flavor because they both have the
2933                 // same MIME type. Also don't need to worry about accidentally
2934                 // passing stringFlavor because either
2935                 //   1. Both flavors are stringFlavor, in which case the
2936                 //      equality test at the top of the function succeeded.
2937                 //   2. Only one flavor is stringFlavor, in which case the MIME
2938                 //      type comparison returned a non-zero value.
2939                 if (doesSubtypeSupportCharset(flavor1)) {
2940                     // Next, prefer the decoded text representations of Reader,
2941                     // String, CharBuffer, and [C, in that order.
2942                     comp = compareIndices(decodedTextRepresentations, class1,
2943                                           class2, UNKNOWN_OBJECT_LOSES);
2944                     if (comp != 0) {
2945                         return comp;
2946                     }
2947 
2948                     // Next, compare charsets
2949                     comp = charsetComparator.compareCharsets
2950                         (DataTransferer.getTextCharset(flavor1),
2951                          DataTransferer.getTextCharset(flavor2));
2952                     if (comp != 0) {
2953                         return comp;
2954                     }
2955                 }
2956 
2957                 // Finally, prefer the encoded text representations of
2958                 // InputStream, ByteBuffer, and [B, in that order.
2959                 comp = compareIndices(encodedTextRepresentations, class1,
2960                                       class2, UNKNOWN_OBJECT_LOSES);
2961                 if (comp != 0) {
2962                     return comp;
2963                 }
2964             } else {
2965                 // First, prefer application types.
2966                 comp = compareIndices(primaryTypes, primaryType1, primaryType2,
2967                                       UNKNOWN_OBJECT_LOSES);
2968                 if (comp != 0) {
2969                     return comp;
2970                 }
2971 
2972                 // Next, look for application/x-java-* types. Prefer unknown
2973                 // MIME types because if the user provides his own data flavor,
2974                 // it will likely be the most descriptive one.
2975                 comp = compareIndices(exactTypes, mimeType1, mimeType2,
2976                                       UNKNOWN_OBJECT_WINS);
2977                 if (comp != 0) {
2978                     return comp;
2979                 }
2980 
2981                 // Finally, prefer the representation classes of Remote,
2982                 // Serializable, and InputStream, in that order.
2983                 comp = compareIndices(nonTextRepresentations, class1, class2,
2984                                       UNKNOWN_OBJECT_LOSES);
2985                 if (comp != 0) {
2986                     return comp;
2987                 }
2988             }
2989 
2990             // The flavours are not equal but still not distinguishable.
2991             // Compare String representations in alphabetical order
2992             return flavor1.getMimeType().compareTo(flavor2.getMimeType());
2993         }
2994     }
2995 
2996     /*
2997      * Given the Map that maps objects to Integer indices and a boolean value,
2998      * this Comparator imposes a direct or reverse order on set of objects.
2999      * <p>
3000      * If the specified boolean value is SELECT_BEST, the Comparator imposes the
3001      * direct index-based order: an object A is greater than an object B if and
3002      * only if the index of A is greater than the index of B. An object that
3003      * doesn't have an associated index is less or equal than any other object.
3004      * <p>
3005      * If the specified boolean value is SELECT_WORST, the Comparator imposes the
3006      * reverse index-based order: an object A is greater than an object B if and
3007      * only if A is less than B with the direct index-based order.
3008      */
3009     public static class IndexOrderComparator extends IndexedComparator {
3010         private final Map indexMap;
3011         private static final Integer FALLBACK_INDEX =
3012             Integer.valueOf(Integer.MIN_VALUE);
3013 
3014         public IndexOrderComparator(Map indexMap) {
3015             super(SELECT_BEST);
3016             this.indexMap = indexMap;
3017         }
3018 
3019         public IndexOrderComparator(Map indexMap, boolean order) {
3020             super(order);
3021             this.indexMap = indexMap;
3022         }
3023 
3024         public int compare(Object obj1, Object obj2) {
3025             if (order == SELECT_WORST) {
3026                 return -compareIndices(indexMap, obj1, obj2, FALLBACK_INDEX);
3027             } else {
3028                 return compareIndices(indexMap, obj1, obj2, FALLBACK_INDEX);
3029             }
3030         }
3031     }
3032 
3033     /**
3034      * A class that provides access to java.rmi.Remote and java.rmi.MarshalledObject
3035      * without creating a static dependency.
3036      */
3037     private static class RMI {
3038         private static final Class<?> remoteClass = getClass("java.rmi.Remote");
3039         private static final Class<?> marshallObjectClass =
3040             getClass("java.rmi.MarshalledObject");
3041         private static final Constructor<?> marshallCtor =
3042             getConstructor(marshallObjectClass, Object.class);
3043         private static final Method marshallGet =
3044             getMethod(marshallObjectClass, "get");
3045 
3046         private static Class<?> getClass(String name) {
3047             try {
3048                 return Class.forName(name, true, null);
3049             } catch (ClassNotFoundException e) {
3050                 return null;
3051             }
3052         }
3053 
3054         private static Constructor<?> getConstructor(Class<?> c, Class<?>... types) {
3055             try {
3056                 return (c == null) ? null : c.getDeclaredConstructor(types);
3057             } catch (NoSuchMethodException x) {
3058                 throw new AssertionError(x);
3059             }
3060         }
3061 
3062         private static Method getMethod(Class<?> c, String name, Class<?>... types) {
3063             try {
3064                 return (c == null) ? null : c.getMethod(name, types);
3065             } catch (NoSuchMethodException e) {
3066                 throw new AssertionError(e);
3067             }
3068         }
3069 
3070         /**
3071          * Returns {@code true} if the given class is java.rmi.Remote.
3072          */
3073         static boolean isRemote(Class<?> c) {
3074             return (remoteClass == null) ? null : remoteClass.isAssignableFrom(c);
3075         }
3076 
3077         /**
3078          * Returns java.rmi.Remote.class if RMI is present; otherwise {@code null}.
3079          */
3080         static Class<?> remoteClass() {
3081             return remoteClass;
3082         }
3083 
3084         /**
3085          * Returns a new MarshalledObject containing the serialized representation
3086          * of the given object.
3087          */
3088         static Object newMarshalledObject(Object obj) throws IOException {
3089             try {
3090                 return marshallCtor.newInstance(obj);
3091             } catch (InstantiationException x) {
3092                 throw new AssertionError(x);
3093             } catch (IllegalAccessException x) {
3094                 throw new AssertionError(x);
3095             } catch (InvocationTargetException  x) {
3096                 Throwable cause = x.getCause();
3097                 if (cause instanceof IOException)
3098                     throw (IOException)cause;
3099                 throw new AssertionError(x);
3100             }
3101         }
3102 
3103         /**
3104          * Returns a new copy of the contained marshalled object.
3105          */
3106         static Object getMarshalledObject(Object obj)
3107             throws IOException, ClassNotFoundException
3108         {
3109             try {
3110                 return marshallGet.invoke(obj);
3111             } catch (IllegalAccessException x) {
3112                 throw new AssertionError(x);
3113             } catch (InvocationTargetException x) {
3114                 Throwable cause = x.getCause();
3115                 if (cause instanceof IOException)
3116                     throw (IOException)cause;
3117                 if (cause instanceof ClassNotFoundException)
3118                     throw (ClassNotFoundException)cause;
3119                 throw new AssertionError(x);
3120             }
3121         }
3122     }
3123 }