107 *
108 * The concept of "flavors" and "natives" is extended to include "formats",
109 * which are the numeric values Win32 and X11 use to express particular data
110 * types. Like FlavorMap, which provides getNativesForFlavors(DataFlavor[]) and
111 * getFlavorsForNatives(String[]) functions, DataTransferer provides a set
112 * of getFormatsFor(Transferable|Flavor|Flavors) and
113 * getFlavorsFor(Format|Formats) functions.
114 *
115 * Also provided are functions for translating a Transferable into a byte
116 * array, given a source DataFlavor and a target format, and for translating
117 * a byte array or InputStream into an Object, given a source format and
118 * a target DataFlavor.
119 *
120 * @author David Mendenhall
121 * @author Danila Sinopalnikov
122 *
123 * @since 1.3.1
124 */
125 public abstract class DataTransferer {
126 /**
127 * The <code>DataFlavor</code> representing a Java text encoding String
128 * encoded in UTF-8, where
129 * <pre>
130 * representationClass = [B
131 * mimeType = "application/x-java-text-encoding"
132 * </pre>
133 */
134 public static final DataFlavor javaTextEncodingFlavor;
135
136 /**
137 * A collection of all natives listed in flavormap.properties with
138 * a primary MIME type of "text".
139 */
140 private static final Set<Long> textNatives =
141 Collections.synchronizedSet(new HashSet<>());
142
143 /**
144 * The native encodings/charsets for the Set of textNatives.
145 */
146 private static final Map<Long, String> nativeCharsets =
147 Collections.synchronizedMap(new HashMap<>());
306 {
307 DataFlavor[] flavors = contents.getTransferDataFlavors();
308 if (flavors == null) {
309 return Collections.emptySortedMap();
310 }
311 return getFormatsForFlavors(flavors, map);
312 }
313
314 /**
315 * Returns a Map whose keys are all of the possible formats into which data
316 * in the specified DataFlavors can be translated. The value of each key
317 * is the DataFlavor in which the Transferable's data should be requested
318 * when converting to the format.
319 * <p>
320 * The map keys are sorted according to the native formats preference
321 * order.
322 *
323 * @param flavors the data flavors
324 * @param map the FlavorTable which contains mappings between
325 * DataFlavors and data formats
326 * @throws NullPointerException if flavors or map is <code>null</code>
327 */
328 public SortedMap<Long, DataFlavor> getFormatsForFlavors(DataFlavor[] flavors,
329 FlavorTable map)
330 {
331 Map<Long,DataFlavor> formatMap = new HashMap<>(flavors.length);
332 Map<Long,DataFlavor> textPlainMap = new HashMap<>(flavors.length);
333 // Maps formats to indices that will be used to sort the formats
334 // according to the preference order.
335 // Larger index value corresponds to the more preferable format.
336 Map<Long, Integer> indexMap = new HashMap<>(flavors.length);
337 Map<Long, Integer> textPlainIndexMap = new HashMap<>(flavors.length);
338
339 int currentIndex = 0;
340
341 // Iterate backwards so that preferred DataFlavors are used over
342 // other DataFlavors. (See javadoc for
343 // Transferable.getTransferDataFlavors.)
344 for (int i = flavors.length - 1; i >= 0; i--) {
345 DataFlavor flavor = flavors[i];
346 if (flavor == null) continue;
461 if (mappingSet.contains(new AbstractMap.SimpleEntry<>(lFormat, flavor))) {
462 flavorMap.put(flavor, lFormat);
463 break;
464 }
465 }
466 }
467
468 return flavorMap;
469 }
470
471 /**
472 * Returns a Set of all DataFlavors for which
473 * 1) a mapping from at least one of the specified formats exists in the
474 * specified map and
475 * 2) the data translation for this mapping can be performed by the data
476 * transfer subsystem.
477 *
478 * @param formats the data formats
479 * @param map the FlavorTable which contains mappings between
480 * DataFlavors and data formats
481 * @throws NullPointerException if formats or map is <code>null</code>
482 */
483 public Set<DataFlavor> getFlavorsForFormatsAsSet(long[] formats, FlavorTable map) {
484 Set<DataFlavor> flavorSet = new HashSet<>(formats.length);
485
486 for (long format : formats) {
487 List<DataFlavor> flavors = map.getFlavorsForNative(getNativeForFormat(format));
488 for (DataFlavor flavor : flavors) {
489 // Don't explicitly test for String, since it is just a special
490 // case of Serializable
491 if (flavor.isFlavorTextType() ||
492 flavor.isFlavorJavaFileListType() ||
493 DataFlavor.imageFlavor.equals(flavor) ||
494 flavor.isRepresentationClassSerializable() ||
495 flavor.isRepresentationClassInputStream() ||
496 flavor.isRepresentationClassRemote()) {
497 flavorSet.add(flavor);
498 }
499 }
500 }
501
502 return flavorSet;
503 }
504
505 /**
506 * Returns an array of all DataFlavors for which
507 * 1) a mapping from at least one of the specified formats exists in the
508 * specified map and
509 * 2) the data translation for this mapping can be performed by the data
510 * transfer subsystem.
511 * The array will be sorted according to a
512 * <code>DataFlavorComparator</code> created with the specified
513 * map as an argument.
514 *
515 * @param formats the data formats
516 * @param map the FlavorTable which contains mappings between
517 * DataFlavors and data formats
518 * @throws NullPointerException if formats or map is <code>null</code>
519 */
520 public DataFlavor[] getFlavorsForFormatsAsArray(long[] formats,
521 FlavorTable map) {
522 // getFlavorsForFormatsAsSet() is less expensive than
523 // getFlavorsForFormats().
524 return setToSortedDataFlavorArray(getFlavorsForFormatsAsSet(formats, map));
525 }
526
527 /**
528 * Looks-up or registers the String native with the native data transfer
529 * system and returns a long format corresponding to that native.
530 */
531 protected abstract Long getFormatForNativeAsLong(String str);
532
533 /**
534 * Looks-up the String native corresponding to the specified long format in
535 * the native data transfer system.
536 */
537 protected abstract String getNativeForFormat(long format);
538
1788 continue;
1789 }
1790
1791 imageWriter.dispose();
1792 baos.close();
1793 return baos.toByteArray();
1794 }
1795
1796 baos.close();
1797
1798 if (ioe == null) {
1799 ioe = new IOException("Registered service providers failed to encode "
1800 + renderedImage + " to " + mimeType);
1801 }
1802
1803 throw ioe;
1804 }
1805
1806 /**
1807 * Concatenates the data represented by two objects. Objects can be either
1808 * byte arrays or instances of <code>InputStream</code>. If both arguments
1809 * are byte arrays byte array will be returned. Otherwise an
1810 * <code>InputStream</code> will be returned.
1811 * <p>
1812 * Currently is only called from native code to prepend palette data to
1813 * platform-specific image data during image transfer on Win32.
1814 *
1815 * @param obj1 the first object to be concatenated.
1816 * @param obj2 the second object to be concatenated.
1817 * @return a byte array or an <code>InputStream</code> which represents
1818 * a logical concatenation of the two arguments.
1819 * @throws NullPointerException is either of the arguments is
1820 * <code>null</code>
1821 * @throws ClassCastException is either of the arguments is
1822 * neither byte array nor an instance of <code>InputStream</code>.
1823 */
1824 private Object concatData(Object obj1, Object obj2) {
1825 InputStream str1 = null;
1826 InputStream str2 = null;
1827
1828 if (obj1 instanceof byte[]) {
1829 byte[] arr1 = (byte[])obj1;
1830 if (obj2 instanceof byte[]) {
1831 byte[] arr2 = (byte[])obj2;
1832 byte[] ret = new byte[arr1.length + arr2.length];
1833 System.arraycopy(arr1, 0, ret, 0, arr1.length);
1834 System.arraycopy(arr2, 0, ret, arr1.length, arr2.length);
1835 return ret;
1836 } else {
1837 str1 = new ByteArrayInputStream(arr1);
1838 str2 = (InputStream)obj2;
1839 }
1840 } else {
1841 str1 = (InputStream)obj1;
1842 if (obj2 instanceof byte[]) {
1945 public abstract ToolkitThreadBlockedHandler getToolkitThreadBlockedHandler();
1946
1947 /**
1948 * Helper function to reduce a Map with Long keys to a long array.
1949 * <p>
1950 * The map keys are sorted according to the native formats preference
1951 * order.
1952 */
1953 public static long[] keysToLongArray(SortedMap<Long, ?> map) {
1954 Set<Long> keySet = map.keySet();
1955 long[] retval = new long[keySet.size()];
1956 int i = 0;
1957 for (Iterator<Long> iter = keySet.iterator(); iter.hasNext(); i++) {
1958 retval[i] = iter.next();
1959 }
1960 return retval;
1961 }
1962
1963 /**
1964 * Helper function to convert a Set of DataFlavors to a sorted array.
1965 * The array will be sorted according to <code>DataFlavorComparator</code>.
1966 */
1967 public static DataFlavor[] setToSortedDataFlavorArray(Set<DataFlavor> flavorsSet) {
1968 DataFlavor[] flavors = new DataFlavor[flavorsSet.size()];
1969 flavorsSet.toArray(flavors);
1970 final Comparator<DataFlavor> comparator = DataFlavorUtil.getDataFlavorComparator().reversed();
1971 Arrays.sort(flavors, comparator);
1972 return flavors;
1973 }
1974
1975 /**
1976 * Helper function to convert an InputStream to a byte[] array.
1977 */
1978 protected static byte[] inputStreamToByteArray(InputStream str)
1979 throws IOException
1980 {
1981 try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
1982 int len = 0;
1983 byte[] buf = new byte[8192];
1984
1985 while ((len = str.read(buf)) != -1) {
1986 baos.write(buf, 0, len);
1987 }
1988
1989 return baos.toByteArray();
1990 }
1991 }
1992
1993 /**
1994 * Returns platform-specific mappings for the specified native.
1995 * If there are no platform-specific mappings for this native, the method
1996 * returns an empty <code>List</code>.
1997 */
1998 public LinkedHashSet<DataFlavor> getPlatformMappingsForNative(String nat) {
1999 return new LinkedHashSet<>();
2000 }
2001
2002 /**
2003 * Returns platform-specific mappings for the specified flavor.
2004 * If there are no platform-specific mappings for this flavor, the method
2005 * returns an empty <code>List</code>.
2006 */
2007 public LinkedHashSet<String> getPlatformMappingsForFlavor(DataFlavor df) {
2008 return new LinkedHashSet<>();
2009 }
2010 }
|
107 *
108 * The concept of "flavors" and "natives" is extended to include "formats",
109 * which are the numeric values Win32 and X11 use to express particular data
110 * types. Like FlavorMap, which provides getNativesForFlavors(DataFlavor[]) and
111 * getFlavorsForNatives(String[]) functions, DataTransferer provides a set
112 * of getFormatsFor(Transferable|Flavor|Flavors) and
113 * getFlavorsFor(Format|Formats) functions.
114 *
115 * Also provided are functions for translating a Transferable into a byte
116 * array, given a source DataFlavor and a target format, and for translating
117 * a byte array or InputStream into an Object, given a source format and
118 * a target DataFlavor.
119 *
120 * @author David Mendenhall
121 * @author Danila Sinopalnikov
122 *
123 * @since 1.3.1
124 */
125 public abstract class DataTransferer {
126 /**
127 * The {@code DataFlavor} representing a Java text encoding String
128 * encoded in UTF-8, where
129 * <pre>
130 * representationClass = [B
131 * mimeType = "application/x-java-text-encoding"
132 * </pre>
133 */
134 public static final DataFlavor javaTextEncodingFlavor;
135
136 /**
137 * A collection of all natives listed in flavormap.properties with
138 * a primary MIME type of "text".
139 */
140 private static final Set<Long> textNatives =
141 Collections.synchronizedSet(new HashSet<>());
142
143 /**
144 * The native encodings/charsets for the Set of textNatives.
145 */
146 private static final Map<Long, String> nativeCharsets =
147 Collections.synchronizedMap(new HashMap<>());
306 {
307 DataFlavor[] flavors = contents.getTransferDataFlavors();
308 if (flavors == null) {
309 return Collections.emptySortedMap();
310 }
311 return getFormatsForFlavors(flavors, map);
312 }
313
314 /**
315 * Returns a Map whose keys are all of the possible formats into which data
316 * in the specified DataFlavors can be translated. The value of each key
317 * is the DataFlavor in which the Transferable's data should be requested
318 * when converting to the format.
319 * <p>
320 * The map keys are sorted according to the native formats preference
321 * order.
322 *
323 * @param flavors the data flavors
324 * @param map the FlavorTable which contains mappings between
325 * DataFlavors and data formats
326 * @throws NullPointerException if flavors or map is {@code null}
327 */
328 public SortedMap<Long, DataFlavor> getFormatsForFlavors(DataFlavor[] flavors,
329 FlavorTable map)
330 {
331 Map<Long,DataFlavor> formatMap = new HashMap<>(flavors.length);
332 Map<Long,DataFlavor> textPlainMap = new HashMap<>(flavors.length);
333 // Maps formats to indices that will be used to sort the formats
334 // according to the preference order.
335 // Larger index value corresponds to the more preferable format.
336 Map<Long, Integer> indexMap = new HashMap<>(flavors.length);
337 Map<Long, Integer> textPlainIndexMap = new HashMap<>(flavors.length);
338
339 int currentIndex = 0;
340
341 // Iterate backwards so that preferred DataFlavors are used over
342 // other DataFlavors. (See javadoc for
343 // Transferable.getTransferDataFlavors.)
344 for (int i = flavors.length - 1; i >= 0; i--) {
345 DataFlavor flavor = flavors[i];
346 if (flavor == null) continue;
461 if (mappingSet.contains(new AbstractMap.SimpleEntry<>(lFormat, flavor))) {
462 flavorMap.put(flavor, lFormat);
463 break;
464 }
465 }
466 }
467
468 return flavorMap;
469 }
470
471 /**
472 * Returns a Set of all DataFlavors for which
473 * 1) a mapping from at least one of the specified formats exists in the
474 * specified map and
475 * 2) the data translation for this mapping can be performed by the data
476 * transfer subsystem.
477 *
478 * @param formats the data formats
479 * @param map the FlavorTable which contains mappings between
480 * DataFlavors and data formats
481 * @throws NullPointerException if formats or map is {@code null}
482 */
483 public Set<DataFlavor> getFlavorsForFormatsAsSet(long[] formats, FlavorTable map) {
484 Set<DataFlavor> flavorSet = new HashSet<>(formats.length);
485
486 for (long format : formats) {
487 List<DataFlavor> flavors = map.getFlavorsForNative(getNativeForFormat(format));
488 for (DataFlavor flavor : flavors) {
489 // Don't explicitly test for String, since it is just a special
490 // case of Serializable
491 if (flavor.isFlavorTextType() ||
492 flavor.isFlavorJavaFileListType() ||
493 DataFlavor.imageFlavor.equals(flavor) ||
494 flavor.isRepresentationClassSerializable() ||
495 flavor.isRepresentationClassInputStream() ||
496 flavor.isRepresentationClassRemote()) {
497 flavorSet.add(flavor);
498 }
499 }
500 }
501
502 return flavorSet;
503 }
504
505 /**
506 * Returns an array of all DataFlavors for which
507 * 1) a mapping from at least one of the specified formats exists in the
508 * specified map and
509 * 2) the data translation for this mapping can be performed by the data
510 * transfer subsystem.
511 * The array will be sorted according to a
512 * {@code DataFlavorComparator} created with the specified
513 * map as an argument.
514 *
515 * @param formats the data formats
516 * @param map the FlavorTable which contains mappings between
517 * DataFlavors and data formats
518 * @throws NullPointerException if formats or map is {@code null}
519 */
520 public DataFlavor[] getFlavorsForFormatsAsArray(long[] formats,
521 FlavorTable map) {
522 // getFlavorsForFormatsAsSet() is less expensive than
523 // getFlavorsForFormats().
524 return setToSortedDataFlavorArray(getFlavorsForFormatsAsSet(formats, map));
525 }
526
527 /**
528 * Looks-up or registers the String native with the native data transfer
529 * system and returns a long format corresponding to that native.
530 */
531 protected abstract Long getFormatForNativeAsLong(String str);
532
533 /**
534 * Looks-up the String native corresponding to the specified long format in
535 * the native data transfer system.
536 */
537 protected abstract String getNativeForFormat(long format);
538
1788 continue;
1789 }
1790
1791 imageWriter.dispose();
1792 baos.close();
1793 return baos.toByteArray();
1794 }
1795
1796 baos.close();
1797
1798 if (ioe == null) {
1799 ioe = new IOException("Registered service providers failed to encode "
1800 + renderedImage + " to " + mimeType);
1801 }
1802
1803 throw ioe;
1804 }
1805
1806 /**
1807 * Concatenates the data represented by two objects. Objects can be either
1808 * byte arrays or instances of {@code InputStream}. If both arguments
1809 * are byte arrays byte array will be returned. Otherwise an
1810 * {@code InputStream} will be returned.
1811 * <p>
1812 * Currently is only called from native code to prepend palette data to
1813 * platform-specific image data during image transfer on Win32.
1814 *
1815 * @param obj1 the first object to be concatenated.
1816 * @param obj2 the second object to be concatenated.
1817 * @return a byte array or an {@code InputStream} which represents
1818 * a logical concatenation of the two arguments.
1819 * @throws NullPointerException is either of the arguments is
1820 * {@code null}
1821 * @throws ClassCastException is either of the arguments is
1822 * neither byte array nor an instance of {@code InputStream}.
1823 */
1824 private Object concatData(Object obj1, Object obj2) {
1825 InputStream str1 = null;
1826 InputStream str2 = null;
1827
1828 if (obj1 instanceof byte[]) {
1829 byte[] arr1 = (byte[])obj1;
1830 if (obj2 instanceof byte[]) {
1831 byte[] arr2 = (byte[])obj2;
1832 byte[] ret = new byte[arr1.length + arr2.length];
1833 System.arraycopy(arr1, 0, ret, 0, arr1.length);
1834 System.arraycopy(arr2, 0, ret, arr1.length, arr2.length);
1835 return ret;
1836 } else {
1837 str1 = new ByteArrayInputStream(arr1);
1838 str2 = (InputStream)obj2;
1839 }
1840 } else {
1841 str1 = (InputStream)obj1;
1842 if (obj2 instanceof byte[]) {
1945 public abstract ToolkitThreadBlockedHandler getToolkitThreadBlockedHandler();
1946
1947 /**
1948 * Helper function to reduce a Map with Long keys to a long array.
1949 * <p>
1950 * The map keys are sorted according to the native formats preference
1951 * order.
1952 */
1953 public static long[] keysToLongArray(SortedMap<Long, ?> map) {
1954 Set<Long> keySet = map.keySet();
1955 long[] retval = new long[keySet.size()];
1956 int i = 0;
1957 for (Iterator<Long> iter = keySet.iterator(); iter.hasNext(); i++) {
1958 retval[i] = iter.next();
1959 }
1960 return retval;
1961 }
1962
1963 /**
1964 * Helper function to convert a Set of DataFlavors to a sorted array.
1965 * The array will be sorted according to {@code DataFlavorComparator}.
1966 */
1967 public static DataFlavor[] setToSortedDataFlavorArray(Set<DataFlavor> flavorsSet) {
1968 DataFlavor[] flavors = new DataFlavor[flavorsSet.size()];
1969 flavorsSet.toArray(flavors);
1970 final Comparator<DataFlavor> comparator = DataFlavorUtil.getDataFlavorComparator().reversed();
1971 Arrays.sort(flavors, comparator);
1972 return flavors;
1973 }
1974
1975 /**
1976 * Helper function to convert an InputStream to a byte[] array.
1977 */
1978 protected static byte[] inputStreamToByteArray(InputStream str)
1979 throws IOException
1980 {
1981 try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
1982 int len = 0;
1983 byte[] buf = new byte[8192];
1984
1985 while ((len = str.read(buf)) != -1) {
1986 baos.write(buf, 0, len);
1987 }
1988
1989 return baos.toByteArray();
1990 }
1991 }
1992
1993 /**
1994 * Returns platform-specific mappings for the specified native.
1995 * If there are no platform-specific mappings for this native, the method
1996 * returns an empty {@code List}.
1997 */
1998 public LinkedHashSet<DataFlavor> getPlatformMappingsForNative(String nat) {
1999 return new LinkedHashSet<>();
2000 }
2001
2002 /**
2003 * Returns platform-specific mappings for the specified flavor.
2004 * If there are no platform-specific mappings for this flavor, the method
2005 * returns an empty {@code List}.
2006 */
2007 public LinkedHashSet<String> getPlatformMappingsForFlavor(DataFlavor df) {
2008 return new LinkedHashSet<>();
2009 }
2010 }
|