337
338 /**
339 * Returns whether this flavor is a text type which supports the
340 * 'charset' parameter.
341 */
342 public static boolean isFlavorCharsetTextType(DataFlavor flavor) {
343 // Although stringFlavor doesn't actually support the charset
344 // parameter (because its primary MIME type is not "text"), it should
345 // be treated as though it does. stringFlavor is semantically
346 // equivalent to "text/plain" data.
347 if (DataFlavor.stringFlavor.equals(flavor)) {
348 return true;
349 }
350
351 if (!"text".equals(flavor.getPrimaryType()) ||
352 !doesSubtypeSupportCharset(flavor))
353 {
354 return false;
355 }
356
357 Class rep_class = flavor.getRepresentationClass();
358
359 if (flavor.isRepresentationClassReader() ||
360 String.class.equals(rep_class) ||
361 flavor.isRepresentationClassCharBuffer() ||
362 char[].class.equals(rep_class))
363 {
364 return true;
365 }
366
367 if (!(flavor.isRepresentationClassInputStream() ||
368 flavor.isRepresentationClassByteBuffer() ||
369 byte[].class.equals(rep_class))) {
370 return false;
371 }
372
373 String charset = flavor.getParameter("charset");
374
375 return (charset != null)
376 ? DataTransferer.isEncodingSupported(charset)
377 : true; // null equals default encoding which is always supported
709 break;
710 }
711 }
712 }
713
714 return flavorMap;
715 }
716
717 /**
718 * Returns a Set of all DataFlavors for which
719 * 1) a mapping from at least one of the specified formats exists in the
720 * specified map and
721 * 2) the data translation for this mapping can be performed by the data
722 * transfer subsystem.
723 *
724 * @param formats the data formats
725 * @param map the FlavorTable which contains mappings between
726 * DataFlavors and data formats
727 * @throws NullPointerException if formats or map is <code>null</code>
728 */
729 public Set getFlavorsForFormatsAsSet(long[] formats, FlavorTable map) {
730 Set<DataFlavor> flavorSet = new HashSet<>(formats.length);
731
732 for (long format : formats) {
733 List<DataFlavor> flavors = map.getFlavorsForNative(getNativeForFormat(format));
734 for (DataFlavor flavor : flavors) {
735 // Don't explicitly test for String, since it is just a special
736 // case of Serializable
737 if (flavor.isFlavorTextType() ||
738 flavor.isFlavorJavaFileListType() ||
739 DataFlavor.imageFlavor.equals(flavor) ||
740 flavor.isRepresentationClassSerializable() ||
741 flavor.isRepresentationClassInputStream() ||
742 flavor.isRepresentationClassRemote()) {
743 flavorSet.add(flavor);
744 }
745 }
746 }
747
748 return flavorSet;
749 }
1100
1101 Image image = (Image)obj;
1102 byte[] bytes = imageToPlatformBytes(image, format);
1103
1104 if (bytes == null) {
1105 throw new IOException("Data translation failed: " +
1106 "cannot convert java image to native format");
1107 }
1108 return bytes;
1109 }
1110
1111 byte[] theByteArray = null;
1112
1113 // Target data is a file list. Source data must be a
1114 // java.util.List which contains java.io.File or String instances.
1115 if (isFileFormat(format)) {
1116 if (!DataFlavor.javaFileListFlavor.equals(flavor)) {
1117 throw new IOException("data translation failed");
1118 }
1119
1120 final List list = (List)obj;
1121
1122 final ProtectionDomain userProtectionDomain = getUserProtectionDomain(contents);
1123
1124 final ArrayList<String> fileList = castToFiles(list, userProtectionDomain);
1125
1126 try (ByteArrayOutputStream bos = convertFileListToBytes(fileList)) {
1127 theByteArray = bos.toByteArray();
1128 }
1129
1130 // Target data is a URI list. Source data must be a
1131 // java.util.List which contains java.io.File or String instances.
1132 } else if (isURIListFormat(format)) {
1133 if (!DataFlavor.javaFileListFlavor.equals(flavor)) {
1134 throw new IOException("data translation failed");
1135 }
1136 String nat = getNativeForFormat(format);
1137 String targetCharset = null;
1138 if (nat != null) {
1139 try {
1140 targetCharset = new DataFlavor(nat).getParameter("charset");
1141 } catch (ClassNotFoundException cnfe) {
1142 throw new IOException(cnfe);
1143 }
1144 }
1145 if (targetCharset == null) {
1146 targetCharset = "UTF-8";
1147 }
1148 final List list = (List)obj;
1149 final ProtectionDomain userProtectionDomain = getUserProtectionDomain(contents);
1150 final ArrayList<String> fileList = castToFiles(list, userProtectionDomain);
1151 final ArrayList<String> uriList = new ArrayList<>(fileList.size());
1152 for (String fileObject : fileList) {
1153 final URI uri = new File(fileObject).toURI();
1154 // Some implementations are fussy about the number of slashes (file:///path/to/file is best)
1155 try {
1156 uriList.add(new URI(uri.getScheme(), "", uri.getPath(), uri.getFragment()).toString());
1157 } catch (URISyntaxException uriSyntaxException) {
1158 throw new IOException(uriSyntaxException);
1159 }
1160 }
1161
1162 byte[] eoln = "\r\n".getBytes(targetCharset);
1163
1164 try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
1165 for (String uri : uriList) {
1166 byte[] bytes = uri.getBytes(targetCharset);
1167 bos.write(bytes, 0, bytes.length);
1168 bos.write(eoln, 0, eoln.length);
1273 private static ProtectionDomain getUserProtectionDomain(Transferable contents) {
1274 return contents.getClass().getProtectionDomain();
1275 }
1276
1277 private boolean isForbiddenToRead (File file, ProtectionDomain protectionDomain)
1278 {
1279 if (null == protectionDomain) {
1280 return false;
1281 }
1282 try {
1283 FilePermission filePermission =
1284 new FilePermission(file.getCanonicalPath(), "read, delete");
1285 if (protectionDomain.implies(filePermission)) {
1286 return false;
1287 }
1288 } catch (IOException e) {}
1289
1290 return true;
1291 }
1292
1293 private ArrayList<String> castToFiles(final List files,
1294 final ProtectionDomain userProtectionDomain) throws IOException {
1295 try {
1296 return AccessController.doPrivileged((PrivilegedExceptionAction<ArrayList<String>>) () -> {
1297 ArrayList<String> fileList = new ArrayList<>();
1298 for (Object fileObject : files)
1299 {
1300 File file = castToFile(fileObject);
1301 if (file != null &&
1302 (null == System.getSecurityManager() ||
1303 !(isFileInWebstartedCache(file) ||
1304 isForbiddenToRead(file, userProtectionDomain))))
1305 {
1306 fileList.add(file.getCanonicalPath());
1307 }
1308 }
1309 return fileList;
1310 });
1311 } catch (PrivilegedActionException pae) {
1312 throw new IOException(pae.getMessage());
1313 }
1651 */
1652 private Object translateStreamToInputStream
1653 (InputStream str, DataFlavor flavor, long format,
1654 Transferable localeTransferable) throws IOException
1655 {
1656 if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1657 str = new ReencodingInputStream
1658 (str, format, DataTransferer.getTextCharset(flavor),
1659 localeTransferable);
1660 }
1661
1662 return constructFlavoredObject(str, flavor, InputStream.class);
1663 }
1664
1665 /**
1666 * We support representations which are exactly of the specified Class,
1667 * and also arbitrary Objects which have a constructor which takes an
1668 * instance of the Class as its sole parameter.
1669 */
1670 private Object constructFlavoredObject(Object arg, DataFlavor flavor,
1671 Class clazz)
1672 throws IOException
1673 {
1674 final Class<?> dfrc = flavor.getRepresentationClass();
1675
1676 if (clazz.equals(dfrc)) {
1677 return arg; // simple case
1678 } else {
1679 Constructor[] constructors;
1680
1681 try {
1682 constructors = AccessController.doPrivileged(
1683 (PrivilegedAction<Constructor[]>) dfrc::getConstructors);
1684 } catch (SecurityException se) {
1685 throw new IOException(se.getMessage());
1686 }
1687
1688 Constructor constructor = Stream.of(constructors)
1689 .filter(c -> Modifier.isPublic(c.getModifiers()))
1690 .filter(c -> {
1691 Class[] ptypes = c.getParameterTypes();
1692 return ptypes != null
1693 && ptypes.length == 1
1694 && clazz.equals(ptypes[0]);
1695 })
1696 .findFirst()
1697 .orElseThrow(() ->
1698 new IOException("can't find <init>(L"+ clazz + ";)V for class: " + dfrc.getName()));
1699
1700 try {
1701 return constructor.newInstance(arg);
1702 } catch (Exception e) {
1703 throw new IOException(e.getMessage());
1704 }
1705 }
1706 }
1707
1708 /**
1709 * Used for decoding and reencoding an InputStream on demand so that we
1710 * can strip NUL terminators and perform EOLN search-and-replace.
1711 */
1900
1901 /**
1902 * Translates either a byte array or an input stream which contain
1903 * platform-specific image data in the given format into an Image.
1904 */
1905
1906
1907 protected abstract Image platformImageBytesToImage(
1908 byte[] bytes,long format) throws IOException;
1909
1910 /**
1911 * Translates either a byte array or an input stream which contain
1912 * an image data in the given standard format into an Image.
1913 *
1914 * @param mimeType image MIME type, such as: image/png, image/jpeg, image/gif
1915 */
1916 protected Image standardImageBytesToImage(
1917 byte[] bytes, String mimeType) throws IOException
1918 {
1919
1920 Iterator readerIterator = ImageIO.getImageReadersByMIMEType(mimeType);
1921
1922 if (!readerIterator.hasNext()) {
1923 throw new IOException("No registered service provider can decode " +
1924 " an image from " + mimeType);
1925 }
1926
1927 IOException ioe = null;
1928
1929 while (readerIterator.hasNext()) {
1930 ImageReader imageReader = (ImageReader)readerIterator.next();
1931 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
1932 try (ImageInputStream imageInputStream = ImageIO.createImageInputStream(bais)) {
1933 ImageReadParam param = imageReader.getDefaultReadParam();
1934 imageReader.setInput(imageInputStream, true, true);
1935 BufferedImage bufferedImage = imageReader.read(imageReader.getMinIndex(), param);
1936 if (bufferedImage != null) {
1937 return bufferedImage;
1938 }
1939 } finally {
1940 imageReader.dispose();
1941 }
1942 } catch (IOException e) {
1943 ioe = e;
1944 continue;
1945 }
1946 }
1947
1948 if (ioe == null) {
1949 ioe = new IOException("Registered service providers failed to decode"
1950 + " an image from " + mimeType);
1953 throw ioe;
1954 }
1955
1956 /**
1957 * Translates a Java Image into a byte array which contains platform-
1958 * specific image data in the given format.
1959 */
1960 protected abstract byte[] imageToPlatformBytes(Image image, long format)
1961 throws IOException;
1962
1963 /**
1964 * Translates a Java Image into a byte array which contains
1965 * an image data in the given standard format.
1966 *
1967 * @param mimeType image MIME type, such as: image/png, image/jpeg
1968 */
1969 protected byte[] imageToStandardBytes(Image image, String mimeType)
1970 throws IOException {
1971 IOException originalIOE = null;
1972
1973 Iterator writerIterator = ImageIO.getImageWritersByMIMEType(mimeType);
1974
1975 if (!writerIterator.hasNext()) {
1976 throw new IOException("No registered service provider can encode " +
1977 " an image to " + mimeType);
1978 }
1979
1980 if (image instanceof RenderedImage) {
1981 // Try to encode the original image.
1982 try {
1983 return imageToStandardBytesImpl((RenderedImage)image, mimeType);
1984 } catch (IOException ioe) {
1985 originalIOE = ioe;
1986 }
1987 }
1988
1989 // Retry with a BufferedImage.
1990 int width = 0;
1991 int height = 0;
1992 if (image instanceof ToolkitImage) {
1993 ImageRepresentation ir = ((ToolkitImage)image).getImageRep();
2012 g.drawImage(image, 0, 0, width, height, null);
2013 } finally {
2014 g.dispose();
2015 }
2016
2017 try {
2018 return imageToStandardBytesImpl(bufferedImage, mimeType);
2019 } catch (IOException ioe) {
2020 if (originalIOE != null) {
2021 throw originalIOE;
2022 } else {
2023 throw ioe;
2024 }
2025 }
2026 }
2027
2028 byte[] imageToStandardBytesImpl(RenderedImage renderedImage,
2029 String mimeType)
2030 throws IOException {
2031
2032 Iterator writerIterator = ImageIO.getImageWritersByMIMEType(mimeType);
2033
2034 ImageTypeSpecifier typeSpecifier =
2035 new ImageTypeSpecifier(renderedImage);
2036
2037 ByteArrayOutputStream baos = new ByteArrayOutputStream();
2038 IOException ioe = null;
2039
2040 while (writerIterator.hasNext()) {
2041 ImageWriter imageWriter = (ImageWriter)writerIterator.next();
2042 ImageWriterSpi writerSpi = imageWriter.getOriginatingProvider();
2043
2044 if (!writerSpi.canEncodeImage(typeSpecifier)) {
2045 continue;
2046 }
2047
2048 try {
2049 try (ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(baos)) {
2050 imageWriter.setOutput(imageOutputStream);
2051 imageWriter.write(renderedImage);
2052 imageOutputStream.flush();
2053 }
2054 } catch (IOException e) {
2055 imageWriter.dispose();
2056 baos.reset();
2057 ioe = e;
2058 continue;
2059 }
2060
2061 imageWriter.dispose();
2105 return ret;
2106 } else {
2107 str1 = new ByteArrayInputStream(arr1);
2108 str2 = (InputStream)obj2;
2109 }
2110 } else {
2111 str1 = (InputStream)obj1;
2112 if (obj2 instanceof byte[]) {
2113 str2 = new ByteArrayInputStream((byte[])obj2);
2114 } else {
2115 str2 = (InputStream)obj2;
2116 }
2117 }
2118
2119 return new SequenceInputStream(str1, str2);
2120 }
2121
2122 public byte[] convertData(final Object source,
2123 final Transferable contents,
2124 final long format,
2125 final Map formatMap,
2126 final boolean isToolkitThread)
2127 throws IOException
2128 {
2129 byte[] ret = null;
2130
2131 /*
2132 * If the current thread is the Toolkit thread we should post a
2133 * Runnable to the event dispatch thread associated with source Object,
2134 * since translateTransferable() calls Transferable.getTransferData()
2135 * that may contain client code.
2136 */
2137 if (isToolkitThread) try {
2138 final Stack<byte[]> stack = new Stack<>();
2139 final Runnable dataConverter = new Runnable() {
2140 // Guard against multiple executions.
2141 private boolean done = false;
2142 public void run() {
2143 if (done) {
2144 return;
2145 }
2146 byte[] data = null;
2147 try {
2148 DataFlavor flavor = (DataFlavor)formatMap.get(format);
2149 if (flavor != null) {
2150 data = translateTransferable(contents, flavor, format);
2151 }
2152 } catch (Exception e) {
2153 e.printStackTrace();
2154 data = null;
2155 }
2156 try {
2157 getToolkitThreadBlockedHandler().lock();
2158 stack.push(data);
2159 getToolkitThreadBlockedHandler().exit();
2160 } finally {
2161 getToolkitThreadBlockedHandler().unlock();
2162 done = true;
2163 }
2164 }
2165 };
2166
2167 final AppContext appContext = SunToolkit.targetToAppContext(source);
2168
2169 getToolkitThreadBlockedHandler().lock();
2170
2171 if (appContext != null) {
2172 appContext.put(DATA_CONVERTER_KEY, dataConverter);
2173 }
2174
2175 SunToolkit.executeOnEventHandlerThread(source, dataConverter);
2176
2177 while (stack.empty()) {
2178 getToolkitThreadBlockedHandler().enter();
2179 }
2180
2181 if (appContext != null) {
2182 appContext.remove(DATA_CONVERTER_KEY);
2183 }
2184
2185 ret = stack.pop();
2186 } finally {
2187 getToolkitThreadBlockedHandler().unlock();
2188 } else {
2189 DataFlavor flavor = (DataFlavor)formatMap.get(format);
2190 if (flavor != null) {
2191 ret = translateTransferable(contents, flavor, format);
2192 }
2193 }
2194
2195 return ret;
2196 }
2197
2198 public void processDataConversionRequests() {
2199 if (EventQueue.isDispatchThread()) {
2200 AppContext appContext = AppContext.getAppContext();
2201 getToolkitThreadBlockedHandler().lock();
2202 try {
2203 Runnable dataConverter =
2204 (Runnable)appContext.get(DATA_CONVERTER_KEY);
2205 if (dataConverter != null) {
2206 dataConverter.run();
2207 appContext.remove(DATA_CONVERTER_KEY);
2208 }
2209 } finally {
2218 /**
2219 * Helper function to reduce a Map with Long keys to a long array.
2220 * <p>
2221 * The map keys are sorted according to the native formats preference
2222 * order.
2223 */
2224 public static long[] keysToLongArray(SortedMap<Long, ?> map) {
2225 Set<Long> keySet = map.keySet();
2226 long[] retval = new long[keySet.size()];
2227 int i = 0;
2228 for (Iterator<Long> iter = keySet.iterator(); iter.hasNext(); i++) {
2229 retval[i] = iter.next();
2230 }
2231 return retval;
2232 }
2233
2234 /**
2235 * Helper function to convert a Set of DataFlavors to a sorted array.
2236 * The array will be sorted according to <code>DataFlavorComparator</code>.
2237 */
2238 public static DataFlavor[] setToSortedDataFlavorArray(Set flavorsSet) {
2239 DataFlavor[] flavors = new DataFlavor[flavorsSet.size()];
2240 flavorsSet.toArray(flavors);
2241 final Comparator<DataFlavor> comparator =
2242 new DataFlavorComparator(IndexedComparator.SELECT_WORST);
2243 Arrays.sort(flavors, comparator);
2244 return flavors;
2245 }
2246
2247 /**
2248 * Helper function to convert an InputStream to a byte[] array.
2249 */
2250 protected static byte[] inputStreamToByteArray(InputStream str)
2251 throws IOException
2252 {
2253 try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
2254 int len = 0;
2255 byte[] buf = new byte[8192];
2256
2257 while ((len = str.read(buf)) != -1) {
2258 baos.write(buf, 0, len);
2581
2582 public DataFlavorComparator(boolean order) {
2583 super(order);
2584
2585 charsetComparator = new CharsetComparator(order);
2586 }
2587
2588 public int compare(DataFlavor obj1, DataFlavor obj2) {
2589 DataFlavor flavor1 = order == SELECT_BEST ? obj1 : obj2;
2590 DataFlavor flavor2 = order == SELECT_BEST ? obj2 : obj1;
2591
2592 if (flavor1.equals(flavor2)) {
2593 return 0;
2594 }
2595
2596 int comp = 0;
2597
2598 String primaryType1 = flavor1.getPrimaryType();
2599 String subType1 = flavor1.getSubType();
2600 String mimeType1 = primaryType1 + "/" + subType1;
2601 Class class1 = flavor1.getRepresentationClass();
2602
2603 String primaryType2 = flavor2.getPrimaryType();
2604 String subType2 = flavor2.getSubType();
2605 String mimeType2 = primaryType2 + "/" + subType2;
2606 Class class2 = flavor2.getRepresentationClass();
2607
2608 if (flavor1.isFlavorTextType() && flavor2.isFlavorTextType()) {
2609 // First, compare MIME types
2610 comp = compareIndices(textTypes, mimeType1, mimeType2,
2611 UNKNOWN_OBJECT_LOSES);
2612 if (comp != 0) {
2613 return comp;
2614 }
2615
2616 // Only need to test one flavor because they both have the
2617 // same MIME type. Also don't need to worry about accidentally
2618 // passing stringFlavor because either
2619 // 1. Both flavors are stringFlavor, in which case the
2620 // equality test at the top of the function succeeded.
2621 // 2. Only one flavor is stringFlavor, in which case the MIME
2622 // type comparison returned a non-zero value.
2623 if (doesSubtypeSupportCharset(flavor1)) {
2624 // Next, prefer the decoded text representations of Reader,
2625 // String, CharBuffer, and [C, in that order.
2626 comp = compareIndices(decodedTextRepresentations, class1,
|
337
338 /**
339 * Returns whether this flavor is a text type which supports the
340 * 'charset' parameter.
341 */
342 public static boolean isFlavorCharsetTextType(DataFlavor flavor) {
343 // Although stringFlavor doesn't actually support the charset
344 // parameter (because its primary MIME type is not "text"), it should
345 // be treated as though it does. stringFlavor is semantically
346 // equivalent to "text/plain" data.
347 if (DataFlavor.stringFlavor.equals(flavor)) {
348 return true;
349 }
350
351 if (!"text".equals(flavor.getPrimaryType()) ||
352 !doesSubtypeSupportCharset(flavor))
353 {
354 return false;
355 }
356
357 Class<?> rep_class = flavor.getRepresentationClass();
358
359 if (flavor.isRepresentationClassReader() ||
360 String.class.equals(rep_class) ||
361 flavor.isRepresentationClassCharBuffer() ||
362 char[].class.equals(rep_class))
363 {
364 return true;
365 }
366
367 if (!(flavor.isRepresentationClassInputStream() ||
368 flavor.isRepresentationClassByteBuffer() ||
369 byte[].class.equals(rep_class))) {
370 return false;
371 }
372
373 String charset = flavor.getParameter("charset");
374
375 return (charset != null)
376 ? DataTransferer.isEncodingSupported(charset)
377 : true; // null equals default encoding which is always supported
709 break;
710 }
711 }
712 }
713
714 return flavorMap;
715 }
716
717 /**
718 * Returns a Set of all DataFlavors for which
719 * 1) a mapping from at least one of the specified formats exists in the
720 * specified map and
721 * 2) the data translation for this mapping can be performed by the data
722 * transfer subsystem.
723 *
724 * @param formats the data formats
725 * @param map the FlavorTable which contains mappings between
726 * DataFlavors and data formats
727 * @throws NullPointerException if formats or map is <code>null</code>
728 */
729 public Set<DataFlavor> getFlavorsForFormatsAsSet(long[] formats, FlavorTable map) {
730 Set<DataFlavor> flavorSet = new HashSet<>(formats.length);
731
732 for (long format : formats) {
733 List<DataFlavor> flavors = map.getFlavorsForNative(getNativeForFormat(format));
734 for (DataFlavor flavor : flavors) {
735 // Don't explicitly test for String, since it is just a special
736 // case of Serializable
737 if (flavor.isFlavorTextType() ||
738 flavor.isFlavorJavaFileListType() ||
739 DataFlavor.imageFlavor.equals(flavor) ||
740 flavor.isRepresentationClassSerializable() ||
741 flavor.isRepresentationClassInputStream() ||
742 flavor.isRepresentationClassRemote()) {
743 flavorSet.add(flavor);
744 }
745 }
746 }
747
748 return flavorSet;
749 }
1100
1101 Image image = (Image)obj;
1102 byte[] bytes = imageToPlatformBytes(image, format);
1103
1104 if (bytes == null) {
1105 throw new IOException("Data translation failed: " +
1106 "cannot convert java image to native format");
1107 }
1108 return bytes;
1109 }
1110
1111 byte[] theByteArray = null;
1112
1113 // Target data is a file list. Source data must be a
1114 // java.util.List which contains java.io.File or String instances.
1115 if (isFileFormat(format)) {
1116 if (!DataFlavor.javaFileListFlavor.equals(flavor)) {
1117 throw new IOException("data translation failed");
1118 }
1119
1120 final List<?> list = (List<?>)obj;
1121
1122 final ProtectionDomain userProtectionDomain = getUserProtectionDomain(contents);
1123
1124 final ArrayList<String> fileList = castToFiles(list, userProtectionDomain);
1125
1126 try (ByteArrayOutputStream bos = convertFileListToBytes(fileList)) {
1127 theByteArray = bos.toByteArray();
1128 }
1129
1130 // Target data is a URI list. Source data must be a
1131 // java.util.List which contains java.io.File or String instances.
1132 } else if (isURIListFormat(format)) {
1133 if (!DataFlavor.javaFileListFlavor.equals(flavor)) {
1134 throw new IOException("data translation failed");
1135 }
1136 String nat = getNativeForFormat(format);
1137 String targetCharset = null;
1138 if (nat != null) {
1139 try {
1140 targetCharset = new DataFlavor(nat).getParameter("charset");
1141 } catch (ClassNotFoundException cnfe) {
1142 throw new IOException(cnfe);
1143 }
1144 }
1145 if (targetCharset == null) {
1146 targetCharset = "UTF-8";
1147 }
1148 final List<?> list = (List<?>)obj;
1149 final ProtectionDomain userProtectionDomain = getUserProtectionDomain(contents);
1150 final ArrayList<String> fileList = castToFiles(list, userProtectionDomain);
1151 final ArrayList<String> uriList = new ArrayList<>(fileList.size());
1152 for (String fileObject : fileList) {
1153 final URI uri = new File(fileObject).toURI();
1154 // Some implementations are fussy about the number of slashes (file:///path/to/file is best)
1155 try {
1156 uriList.add(new URI(uri.getScheme(), "", uri.getPath(), uri.getFragment()).toString());
1157 } catch (URISyntaxException uriSyntaxException) {
1158 throw new IOException(uriSyntaxException);
1159 }
1160 }
1161
1162 byte[] eoln = "\r\n".getBytes(targetCharset);
1163
1164 try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
1165 for (String uri : uriList) {
1166 byte[] bytes = uri.getBytes(targetCharset);
1167 bos.write(bytes, 0, bytes.length);
1168 bos.write(eoln, 0, eoln.length);
1273 private static ProtectionDomain getUserProtectionDomain(Transferable contents) {
1274 return contents.getClass().getProtectionDomain();
1275 }
1276
1277 private boolean isForbiddenToRead (File file, ProtectionDomain protectionDomain)
1278 {
1279 if (null == protectionDomain) {
1280 return false;
1281 }
1282 try {
1283 FilePermission filePermission =
1284 new FilePermission(file.getCanonicalPath(), "read, delete");
1285 if (protectionDomain.implies(filePermission)) {
1286 return false;
1287 }
1288 } catch (IOException e) {}
1289
1290 return true;
1291 }
1292
1293 private ArrayList<String> castToFiles(final List<?> files,
1294 final ProtectionDomain userProtectionDomain) throws IOException {
1295 try {
1296 return AccessController.doPrivileged((PrivilegedExceptionAction<ArrayList<String>>) () -> {
1297 ArrayList<String> fileList = new ArrayList<>();
1298 for (Object fileObject : files)
1299 {
1300 File file = castToFile(fileObject);
1301 if (file != null &&
1302 (null == System.getSecurityManager() ||
1303 !(isFileInWebstartedCache(file) ||
1304 isForbiddenToRead(file, userProtectionDomain))))
1305 {
1306 fileList.add(file.getCanonicalPath());
1307 }
1308 }
1309 return fileList;
1310 });
1311 } catch (PrivilegedActionException pae) {
1312 throw new IOException(pae.getMessage());
1313 }
1651 */
1652 private Object translateStreamToInputStream
1653 (InputStream str, DataFlavor flavor, long format,
1654 Transferable localeTransferable) throws IOException
1655 {
1656 if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
1657 str = new ReencodingInputStream
1658 (str, format, DataTransferer.getTextCharset(flavor),
1659 localeTransferable);
1660 }
1661
1662 return constructFlavoredObject(str, flavor, InputStream.class);
1663 }
1664
1665 /**
1666 * We support representations which are exactly of the specified Class,
1667 * and also arbitrary Objects which have a constructor which takes an
1668 * instance of the Class as its sole parameter.
1669 */
1670 private Object constructFlavoredObject(Object arg, DataFlavor flavor,
1671 Class<?> clazz)
1672 throws IOException
1673 {
1674 final Class<?> dfrc = flavor.getRepresentationClass();
1675
1676 if (clazz.equals(dfrc)) {
1677 return arg; // simple case
1678 } else {
1679 Constructor<?>[] constructors;
1680
1681 try {
1682 constructors = AccessController.doPrivileged(
1683 (PrivilegedAction<Constructor<?>[]>) dfrc::getConstructors);
1684 } catch (SecurityException se) {
1685 throw new IOException(se.getMessage());
1686 }
1687
1688 Constructor<?> constructor = Stream.of(constructors)
1689 .filter(c -> Modifier.isPublic(c.getModifiers()))
1690 .filter(c -> {
1691 Class<?>[] ptypes = c.getParameterTypes();
1692 return ptypes != null
1693 && ptypes.length == 1
1694 && clazz.equals(ptypes[0]);
1695 })
1696 .findFirst()
1697 .orElseThrow(() ->
1698 new IOException("can't find <init>(L"+ clazz + ";)V for class: " + dfrc.getName()));
1699
1700 try {
1701 return constructor.newInstance(arg);
1702 } catch (Exception e) {
1703 throw new IOException(e.getMessage());
1704 }
1705 }
1706 }
1707
1708 /**
1709 * Used for decoding and reencoding an InputStream on demand so that we
1710 * can strip NUL terminators and perform EOLN search-and-replace.
1711 */
1900
1901 /**
1902 * Translates either a byte array or an input stream which contain
1903 * platform-specific image data in the given format into an Image.
1904 */
1905
1906
1907 protected abstract Image platformImageBytesToImage(
1908 byte[] bytes,long format) throws IOException;
1909
1910 /**
1911 * Translates either a byte array or an input stream which contain
1912 * an image data in the given standard format into an Image.
1913 *
1914 * @param mimeType image MIME type, such as: image/png, image/jpeg, image/gif
1915 */
1916 protected Image standardImageBytesToImage(
1917 byte[] bytes, String mimeType) throws IOException
1918 {
1919
1920 Iterator<ImageReader> readerIterator =
1921 ImageIO.getImageReadersByMIMEType(mimeType);
1922
1923 if (!readerIterator.hasNext()) {
1924 throw new IOException("No registered service provider can decode " +
1925 " an image from " + mimeType);
1926 }
1927
1928 IOException ioe = null;
1929
1930 while (readerIterator.hasNext()) {
1931 ImageReader imageReader = readerIterator.next();
1932 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
1933 try (ImageInputStream imageInputStream = ImageIO.createImageInputStream(bais)) {
1934 ImageReadParam param = imageReader.getDefaultReadParam();
1935 imageReader.setInput(imageInputStream, true, true);
1936 BufferedImage bufferedImage = imageReader.read(imageReader.getMinIndex(), param);
1937 if (bufferedImage != null) {
1938 return bufferedImage;
1939 }
1940 } finally {
1941 imageReader.dispose();
1942 }
1943 } catch (IOException e) {
1944 ioe = e;
1945 continue;
1946 }
1947 }
1948
1949 if (ioe == null) {
1950 ioe = new IOException("Registered service providers failed to decode"
1951 + " an image from " + mimeType);
1954 throw ioe;
1955 }
1956
1957 /**
1958 * Translates a Java Image into a byte array which contains platform-
1959 * specific image data in the given format.
1960 */
1961 protected abstract byte[] imageToPlatformBytes(Image image, long format)
1962 throws IOException;
1963
1964 /**
1965 * Translates a Java Image into a byte array which contains
1966 * an image data in the given standard format.
1967 *
1968 * @param mimeType image MIME type, such as: image/png, image/jpeg
1969 */
1970 protected byte[] imageToStandardBytes(Image image, String mimeType)
1971 throws IOException {
1972 IOException originalIOE = null;
1973
1974 Iterator<ImageWriter> writerIterator =
1975 ImageIO.getImageWritersByMIMEType(mimeType);
1976
1977 if (!writerIterator.hasNext()) {
1978 throw new IOException("No registered service provider can encode " +
1979 " an image to " + mimeType);
1980 }
1981
1982 if (image instanceof RenderedImage) {
1983 // Try to encode the original image.
1984 try {
1985 return imageToStandardBytesImpl((RenderedImage)image, mimeType);
1986 } catch (IOException ioe) {
1987 originalIOE = ioe;
1988 }
1989 }
1990
1991 // Retry with a BufferedImage.
1992 int width = 0;
1993 int height = 0;
1994 if (image instanceof ToolkitImage) {
1995 ImageRepresentation ir = ((ToolkitImage)image).getImageRep();
2014 g.drawImage(image, 0, 0, width, height, null);
2015 } finally {
2016 g.dispose();
2017 }
2018
2019 try {
2020 return imageToStandardBytesImpl(bufferedImage, mimeType);
2021 } catch (IOException ioe) {
2022 if (originalIOE != null) {
2023 throw originalIOE;
2024 } else {
2025 throw ioe;
2026 }
2027 }
2028 }
2029
2030 byte[] imageToStandardBytesImpl(RenderedImage renderedImage,
2031 String mimeType)
2032 throws IOException {
2033
2034 Iterator<ImageWriter> writerIterator =
2035 ImageIO.getImageWritersByMIMEType(mimeType);
2036
2037 ImageTypeSpecifier typeSpecifier =
2038 new ImageTypeSpecifier(renderedImage);
2039
2040 ByteArrayOutputStream baos = new ByteArrayOutputStream();
2041 IOException ioe = null;
2042
2043 while (writerIterator.hasNext()) {
2044 ImageWriter imageWriter = writerIterator.next();
2045 ImageWriterSpi writerSpi = imageWriter.getOriginatingProvider();
2046
2047 if (!writerSpi.canEncodeImage(typeSpecifier)) {
2048 continue;
2049 }
2050
2051 try {
2052 try (ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(baos)) {
2053 imageWriter.setOutput(imageOutputStream);
2054 imageWriter.write(renderedImage);
2055 imageOutputStream.flush();
2056 }
2057 } catch (IOException e) {
2058 imageWriter.dispose();
2059 baos.reset();
2060 ioe = e;
2061 continue;
2062 }
2063
2064 imageWriter.dispose();
2108 return ret;
2109 } else {
2110 str1 = new ByteArrayInputStream(arr1);
2111 str2 = (InputStream)obj2;
2112 }
2113 } else {
2114 str1 = (InputStream)obj1;
2115 if (obj2 instanceof byte[]) {
2116 str2 = new ByteArrayInputStream((byte[])obj2);
2117 } else {
2118 str2 = (InputStream)obj2;
2119 }
2120 }
2121
2122 return new SequenceInputStream(str1, str2);
2123 }
2124
2125 public byte[] convertData(final Object source,
2126 final Transferable contents,
2127 final long format,
2128 final Map<Long, DataFlavor> formatMap,
2129 final boolean isToolkitThread)
2130 throws IOException
2131 {
2132 byte[] ret = null;
2133
2134 /*
2135 * If the current thread is the Toolkit thread we should post a
2136 * Runnable to the event dispatch thread associated with source Object,
2137 * since translateTransferable() calls Transferable.getTransferData()
2138 * that may contain client code.
2139 */
2140 if (isToolkitThread) try {
2141 final Stack<byte[]> stack = new Stack<>();
2142 final Runnable dataConverter = new Runnable() {
2143 // Guard against multiple executions.
2144 private boolean done = false;
2145 public void run() {
2146 if (done) {
2147 return;
2148 }
2149 byte[] data = null;
2150 try {
2151 DataFlavor flavor = formatMap.get(format);
2152 if (flavor != null) {
2153 data = translateTransferable(contents, flavor, format);
2154 }
2155 } catch (Exception e) {
2156 e.printStackTrace();
2157 data = null;
2158 }
2159 try {
2160 getToolkitThreadBlockedHandler().lock();
2161 stack.push(data);
2162 getToolkitThreadBlockedHandler().exit();
2163 } finally {
2164 getToolkitThreadBlockedHandler().unlock();
2165 done = true;
2166 }
2167 }
2168 };
2169
2170 final AppContext appContext = SunToolkit.targetToAppContext(source);
2171
2172 getToolkitThreadBlockedHandler().lock();
2173
2174 if (appContext != null) {
2175 appContext.put(DATA_CONVERTER_KEY, dataConverter);
2176 }
2177
2178 SunToolkit.executeOnEventHandlerThread(source, dataConverter);
2179
2180 while (stack.empty()) {
2181 getToolkitThreadBlockedHandler().enter();
2182 }
2183
2184 if (appContext != null) {
2185 appContext.remove(DATA_CONVERTER_KEY);
2186 }
2187
2188 ret = stack.pop();
2189 } finally {
2190 getToolkitThreadBlockedHandler().unlock();
2191 } else {
2192 DataFlavor flavor = formatMap.get(format);
2193 if (flavor != null) {
2194 ret = translateTransferable(contents, flavor, format);
2195 }
2196 }
2197
2198 return ret;
2199 }
2200
2201 public void processDataConversionRequests() {
2202 if (EventQueue.isDispatchThread()) {
2203 AppContext appContext = AppContext.getAppContext();
2204 getToolkitThreadBlockedHandler().lock();
2205 try {
2206 Runnable dataConverter =
2207 (Runnable)appContext.get(DATA_CONVERTER_KEY);
2208 if (dataConverter != null) {
2209 dataConverter.run();
2210 appContext.remove(DATA_CONVERTER_KEY);
2211 }
2212 } finally {
2221 /**
2222 * Helper function to reduce a Map with Long keys to a long array.
2223 * <p>
2224 * The map keys are sorted according to the native formats preference
2225 * order.
2226 */
2227 public static long[] keysToLongArray(SortedMap<Long, ?> map) {
2228 Set<Long> keySet = map.keySet();
2229 long[] retval = new long[keySet.size()];
2230 int i = 0;
2231 for (Iterator<Long> iter = keySet.iterator(); iter.hasNext(); i++) {
2232 retval[i] = iter.next();
2233 }
2234 return retval;
2235 }
2236
2237 /**
2238 * Helper function to convert a Set of DataFlavors to a sorted array.
2239 * The array will be sorted according to <code>DataFlavorComparator</code>.
2240 */
2241 public static DataFlavor[] setToSortedDataFlavorArray(Set<DataFlavor> flavorsSet) {
2242 DataFlavor[] flavors = new DataFlavor[flavorsSet.size()];
2243 flavorsSet.toArray(flavors);
2244 final Comparator<DataFlavor> comparator =
2245 new DataFlavorComparator(IndexedComparator.SELECT_WORST);
2246 Arrays.sort(flavors, comparator);
2247 return flavors;
2248 }
2249
2250 /**
2251 * Helper function to convert an InputStream to a byte[] array.
2252 */
2253 protected static byte[] inputStreamToByteArray(InputStream str)
2254 throws IOException
2255 {
2256 try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
2257 int len = 0;
2258 byte[] buf = new byte[8192];
2259
2260 while ((len = str.read(buf)) != -1) {
2261 baos.write(buf, 0, len);
2584
2585 public DataFlavorComparator(boolean order) {
2586 super(order);
2587
2588 charsetComparator = new CharsetComparator(order);
2589 }
2590
2591 public int compare(DataFlavor obj1, DataFlavor obj2) {
2592 DataFlavor flavor1 = order == SELECT_BEST ? obj1 : obj2;
2593 DataFlavor flavor2 = order == SELECT_BEST ? obj2 : obj1;
2594
2595 if (flavor1.equals(flavor2)) {
2596 return 0;
2597 }
2598
2599 int comp = 0;
2600
2601 String primaryType1 = flavor1.getPrimaryType();
2602 String subType1 = flavor1.getSubType();
2603 String mimeType1 = primaryType1 + "/" + subType1;
2604 Class<?> class1 = flavor1.getRepresentationClass();
2605
2606 String primaryType2 = flavor2.getPrimaryType();
2607 String subType2 = flavor2.getSubType();
2608 String mimeType2 = primaryType2 + "/" + subType2;
2609 Class<?> class2 = flavor2.getRepresentationClass();
2610
2611 if (flavor1.isFlavorTextType() && flavor2.isFlavorTextType()) {
2612 // First, compare MIME types
2613 comp = compareIndices(textTypes, mimeType1, mimeType2,
2614 UNKNOWN_OBJECT_LOSES);
2615 if (comp != 0) {
2616 return comp;
2617 }
2618
2619 // Only need to test one flavor because they both have the
2620 // same MIME type. Also don't need to worry about accidentally
2621 // passing stringFlavor because either
2622 // 1. Both flavors are stringFlavor, in which case the
2623 // equality test at the top of the function succeeded.
2624 // 2. Only one flavor is stringFlavor, in which case the MIME
2625 // type comparison returned a non-zero value.
2626 if (doesSubtypeSupportCharset(flavor1)) {
2627 // Next, prefer the decoded text representations of Reader,
2628 // String, CharBuffer, and [C, in that order.
2629 comp = compareIndices(decodedTextRepresentations, class1,
|