8037485: Refactor java.awt.datatransfer to eliminate dependency on AWT
Reviewed-by: alanb, mchung, plevart, serb
--- a/jdk/src/share/classes/java/awt/datatransfer/Clipboard.java Mon Jul 28 18:43:09 2014 +0400
+++ b/jdk/src/share/classes/java/awt/datatransfer/Clipboard.java Mon Jul 28 19:02:56 2014 +0400
@@ -25,7 +25,7 @@
package java.awt.datatransfer;
-import java.awt.EventQueue;
+import sun.datatransfer.DataFlavorUtil;
import java.util.Objects;
import java.util.Set;
@@ -130,7 +130,8 @@
this.contents = contents;
if (oldOwner != null && oldOwner != owner) {
- EventQueue.invokeLater(() -> oldOwner.lostOwnership(Clipboard.this, oldContents));
+ DataFlavorUtil.getDesktopService().invokeOnEventThread(() ->
+ oldOwner.lostOwnership(Clipboard.this, oldContents));
}
fireFlavorsChanged();
}
@@ -324,7 +325,7 @@
return;
}
flavorListeners.forEach(listener ->
- EventQueue.invokeLater(() ->
+ DataFlavorUtil.getDesktopService().invokeOnEventThread(() ->
listener.flavorsChanged(new FlavorEvent(Clipboard.this))));
}
--- a/jdk/src/share/classes/java/awt/datatransfer/DataFlavor.java Mon Jul 28 18:43:09 2014 +0400
+++ b/jdk/src/share/classes/java/awt/datatransfer/DataFlavor.java Mon Jul 28 19:02:56 2014 +0400
@@ -25,7 +25,7 @@
package java.awt.datatransfer;
-import sun.awt.datatransfer.DataTransferer;
+import sun.datatransfer.DataFlavorUtil;
import sun.reflect.misc.ReflectUtil;
import java.io.ByteArrayInputStream;
@@ -44,7 +44,6 @@
import java.nio.CharBuffer;
import java.util.Arrays;
import java.util.Collections;
-import java.util.Comparator;
import java.util.Objects;
import static sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION;
@@ -582,12 +581,12 @@
} else {
params += representationClass.getName();
}
- if (DataTransferer.isFlavorCharsetTextType(this) &&
+ if (DataFlavorUtil.isFlavorCharsetTextType(this) &&
(isRepresentationClassInputStream() ||
isRepresentationClassByteBuffer() ||
byte[].class.equals(representationClass)))
{
- params += ";charset=" + DataTransferer.getTextCharset(this);
+ params += ";charset=" + DataFlavorUtil.getTextCharset(this);
}
return params;
}
@@ -609,13 +608,8 @@
* @since 1.3
*/
public static final DataFlavor getTextPlainUnicodeFlavor() {
- String encoding = null;
- DataTransferer transferer = DataTransferer.getInstance();
- if (transferer != null) {
- encoding = transferer.getDefaultUnicodeEncoding();
- }
return new DataFlavor(
- "text/plain;charset="+encoding
+ "text/plain;charset=" + DataFlavorUtil.getDesktopService().getDefaultUnicodeEncoding()
+";class=java.io.InputStream", "Plain Text");
}
@@ -741,12 +735,8 @@
return null;
}
- if (textFlavorComparator == null) {
- textFlavorComparator = new TextFlavorComparator();
- }
-
DataFlavor bestFlavor = Collections.max(Arrays.asList(availableFlavors),
- textFlavorComparator);
+ DataFlavorUtil.getTextFlavorComparator());
if (!bestFlavor.isFlavorTextType()) {
return null;
@@ -755,46 +745,6 @@
return bestFlavor;
}
- private static Comparator<DataFlavor> textFlavorComparator;
-
- static class TextFlavorComparator
- extends DataTransferer.DataFlavorComparator {
-
- /**
- * Compares two <code>DataFlavor</code> objects. Returns a negative
- * integer, zero, or a positive integer as the first
- * <code>DataFlavor</code> is worse than, equal to, or better than the
- * second.
- * <p>
- * <code>DataFlavor</code>s are ordered according to the rules outlined
- * for <code>selectBestTextFlavor</code>.
- *
- * @param flavor1 the first <code>DataFlavor</code> to be compared
- * @param flavor2 the second <code>DataFlavor</code> to be compared
- * @return a negative integer, zero, or a positive integer as the first
- * argument is worse, equal to, or better than the second
- * @throws ClassCastException if either of the arguments is not an
- * instance of <code>DataFlavor</code>
- * @throws NullPointerException if either of the arguments is
- * <code>null</code>
- *
- * @see #selectBestTextFlavor
- */
- public int compare(DataFlavor flavor1, DataFlavor flavor2) {
- if (flavor1.isFlavorTextType()) {
- if (flavor2.isFlavorTextType()) {
- return super.compare(flavor1, flavor2);
- } else {
- return 1;
- }
- } else if (flavor2.isFlavorTextType()) {
- return -1;
- } else {
- return 0;
- }
- }
- }
-
/**
* Gets a Reader for a text flavor, decoded, if necessary, for the expected
* charset (encoding). The supported representation classes are
@@ -1015,13 +965,13 @@
}
if ("text".equals(getPrimaryType())) {
- if (DataTransferer.doesSubtypeSupportCharset(this)
+ if (DataFlavorUtil.doesSubtypeSupportCharset(this)
&& representationClass != null
&& !isStandardTextRepresentationClass()) {
String thisCharset =
- DataTransferer.canonicalName(this.getParameter("charset"));
+ DataFlavorUtil.canonicalName(this.getParameter("charset"));
String thatCharset =
- DataTransferer.canonicalName(that.getParameter("charset"));
+ DataFlavorUtil.canonicalName(that.getParameter("charset"));
if (!Objects.equals(thisCharset, thatCharset)) {
return false;
}
@@ -1088,10 +1038,10 @@
// subTypes is '*', regardless of the other subType.
if ("text".equals(primaryType)) {
- if (DataTransferer.doesSubtypeSupportCharset(this)
+ if (DataFlavorUtil.doesSubtypeSupportCharset(this)
&& representationClass != null
&& !isStandardTextRepresentationClass()) {
- String charset = DataTransferer.canonicalName(getParameter("charset"));
+ String charset = DataFlavorUtil.canonicalName(getParameter("charset"));
if (charset != null) {
total += charset.hashCode();
}
@@ -1280,9 +1230,8 @@
* Returns true if the representation class is <code>Remote</code>.
* @return true if the representation class is <code>Remote</code>
*/
-
public boolean isRepresentationClassRemote() {
- return DataTransferer.isRemote(representationClass);
+ return DataFlavorUtil.RMI.isRemote(representationClass);
}
/**
@@ -1356,8 +1305,8 @@
* @since 1.4
*/
public boolean isFlavorTextType() {
- return (DataTransferer.isFlavorCharsetTextType(this) ||
- DataTransferer.isFlavorNoncharsetTextType(this));
+ return (DataFlavorUtil.isFlavorCharsetTextType(this) ||
+ DataFlavorUtil.isFlavorNoncharsetTextType(this));
}
/**
--- a/jdk/src/share/classes/java/awt/datatransfer/SystemFlavorMap.java Mon Jul 28 18:43:09 2014 +0400
+++ b/jdk/src/share/classes/java/awt/datatransfer/SystemFlavorMap.java Mon Jul 28 19:02:56 2014 +0400
@@ -25,22 +25,15 @@
package java.awt.datatransfer;
-import java.awt.Toolkit;
-
-import java.io.BufferedInputStream;
-import java.io.InputStream;
-import java.lang.ref.SoftReference;
+import sun.datatransfer.DataFlavorUtil;
+import sun.datatransfer.DesktopDatatransferService;
import java.io.BufferedReader;
-import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
-import java.io.IOException;
-
-import java.net.URL;
-import java.net.MalformedURLException;
-
+import java.lang.ref.SoftReference;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -48,12 +41,8 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Properties;
import java.util.Set;
-import sun.awt.AppContext;
-import sun.awt.datatransfer.DataTransferer;
-
/**
* The SystemFlavorMap is a configurable map between "natives" (Strings), which
* correspond to platform-specific data formats, and "flavors" (DataFlavors),
@@ -191,16 +180,11 @@
/**
* Returns the default FlavorMap for this thread's ClassLoader.
+ *
* @return the default FlavorMap for this thread's ClassLoader
*/
public static FlavorMap getDefaultFlavorMap() {
- AppContext context = AppContext.getAppContext();
- FlavorMap fm = (FlavorMap) context.get(FLAVOR_MAP_KEY);
- if (fm == null) {
- fm = new SystemFlavorMap();
- context.put(FLAVOR_MAP_KEY, fm);
- }
- return fm;
+ return DataFlavorUtil.getDesktopService().getFlavorMap(SystemFlavorMap::new);
}
private SystemFlavorMap() {
@@ -239,15 +223,17 @@
MimeType mime = new MimeType(value);
if ("text".equals(mime.getPrimaryType())) {
String charset = mime.getParameter("charset");
- if (DataTransferer.doesSubtypeSupportCharset(mime.getSubType(), charset))
+ if (DataFlavorUtil.doesSubtypeSupportCharset(mime.getSubType(), charset))
{
// We need to store the charset and eoln
// parameters, if any, so that the
// DataTransferer will have this information
// for conversion into the native format.
- DataTransferer transferer = DataTransferer.getInstance();
- if (transferer != null) {
- transferer.registerTextFlavorProperties(key, charset,
+ DesktopDatatransferService desktopService =
+ DataFlavorUtil.getDesktopService();
+ if (desktopService.isDesktopPresent()) {
+ desktopService.registerTextFlavorProperties(
+ key, charset,
mime.getParameter("eoln"),
mime.getParameter("terminators"));
}
@@ -383,10 +369,10 @@
LinkedHashSet<DataFlavor> flavors = getNativeToFlavor().get(nat);
if (nat != null && !disabledMappingGenerationKeys.contains(nat)) {
- DataTransferer transferer = DataTransferer.getInstance();
- if (transferer != null) {
+ DesktopDatatransferService desktopService = DataFlavorUtil.getDesktopService();
+ if (desktopService.isDesktopPresent()) {
LinkedHashSet<DataFlavor> platformFlavors =
- transferer.getPlatformMappingsForNative(nat);
+ desktopService.getPlatformMappingsForNative(nat);
if (!platformFlavors.isEmpty()) {
if (flavors != null) {
// Prepending the platform-specific mappings ensures
@@ -446,10 +432,10 @@
LinkedHashSet<String> natives = getFlavorToNative().get(flav);
if (flav != null && !disabledMappingGenerationKeys.contains(flav)) {
- DataTransferer transferer = DataTransferer.getInstance();
- if (transferer != null) {
+ DesktopDatatransferService desktopService = DataFlavorUtil.getDesktopService();
+ if (desktopService.isDesktopPresent()) {
LinkedHashSet<String> platformNatives =
- transferer.getPlatformMappingsForFlavor(flav);
+ desktopService.getPlatformMappingsForFlavor(flav);
if (!platformNatives.isEmpty()) {
if (natives != null) {
// Prepend the platform-specific mappings to ensure
@@ -525,7 +511,7 @@
// In this case we shouldn't synthesize a native for this flavor,
// since its mappings were explicitly specified.
retval = flavorToNativeLookup(flav, false);
- } else if (DataTransferer.isFlavorCharsetTextType(flav)) {
+ } else if (DataFlavorUtil.isFlavorCharsetTextType(flav)) {
retval = new LinkedHashSet<>(0);
// For text/* flavors, flavor-to-native mappings specified in
@@ -553,7 +539,7 @@
// addUnencodedNativeForFlavor(), so they have lower priority.
retval.addAll(flavorToNativeLookup(flav, false));
}
- } else if (DataTransferer.isFlavorNoncharsetTextType(flav)) {
+ } else if (DataFlavorUtil.isFlavorNoncharsetTextType(flav)) {
retval = getTextTypeToNative().get(flav.mimeType.getBaseType());
if (retval == null || retval.isEmpty()) {
@@ -653,7 +639,7 @@
// on load from flavormap.properties.
}
- if (DataTransferer.doesSubtypeSupportCharset(subType, null)) {
+ if (DataFlavorUtil.doesSubtypeSupportCharset(subType, null)) {
if (TEXT_PLAIN_BASE_TYPE.equals(baseType))
{
returnValue.add(DataFlavor.stringFlavor);
@@ -675,7 +661,7 @@
}
}
- for (String charset : DataTransferer.standardEncodings()) {
+ for (String charset : DataFlavorUtil.standardEncodings()) {
for (String encodedTextClass : ENCODED_TEXT_CLASSES) {
final String mimeType =
--- a/jdk/src/share/classes/sun/awt/datatransfer/DataTransferer.java Mon Jul 28 18:43:09 2014 +0400
+++ b/jdk/src/share/classes/sun/awt/datatransfer/DataTransferer.java Mon Jul 28 19:02:56 2014 +0400
@@ -61,8 +61,6 @@
import java.nio.charset.UnsupportedCharsetException;
import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
@@ -73,7 +71,7 @@
import java.util.*;
-import sun.util.logging.PlatformLogger;
+import sun.datatransfer.DataFlavorUtil;
import sun.awt.AppContext;
import sun.awt.SunToolkit;
@@ -135,35 +133,6 @@
public static final DataFlavor javaTextEncodingFlavor;
/**
- * Lazy initialization of Standard Encodings.
- */
- private static class StandardEncodingsHolder {
- private static final SortedSet<String> standardEncodings = load();
-
- private static SortedSet<String> load() {
- final Comparator<String> comparator =
- new CharsetComparator(IndexedComparator.SELECT_WORST);
- final SortedSet<String> tempSet = new TreeSet<>(comparator);
- tempSet.add("US-ASCII");
- tempSet.add("ISO-8859-1");
- tempSet.add("UTF-8");
- tempSet.add("UTF-16BE");
- tempSet.add("UTF-16LE");
- tempSet.add("UTF-16");
- tempSet.add(Charset.defaultCharset().name());
- return Collections.unmodifiableSortedSet(tempSet);
- }
- }
-
- /**
- * Tracks whether a particular text/* MIME type supports the charset
- * parameter. The Map is initialized with all of the standard MIME types
- * listed in the DataFlavor.selectBestTextFlavor method comment. Additional
- * entries may be added during the life of the JRE for text/<other> types.
- */
- private static final Map<String, Boolean> textMIMESubtypeCharsetSupport;
-
- /**
* A collection of all natives listed in flavormap.properties with
* a primary MIME type of "text".
*/
@@ -193,8 +162,6 @@
*/
private static final String DATA_CONVERTER_KEY = "DATA_CONVERTER_KEY";
- private static final PlatformLogger dtLog = PlatformLogger.getLogger("sun.awt.datatransfer.DataTransfer");
-
static {
DataFlavor tJavaTextEncodingFlavor = null;
try {
@@ -202,24 +169,6 @@
} catch (ClassNotFoundException cannotHappen) {
}
javaTextEncodingFlavor = tJavaTextEncodingFlavor;
-
- Map<String, Boolean> tempMap = new HashMap<>(17);
- tempMap.put("sgml", Boolean.TRUE);
- tempMap.put("xml", Boolean.TRUE);
- tempMap.put("html", Boolean.TRUE);
- tempMap.put("enriched", Boolean.TRUE);
- tempMap.put("richtext", Boolean.TRUE);
- tempMap.put("uri-list", Boolean.TRUE);
- tempMap.put("directory", Boolean.TRUE);
- tempMap.put("css", Boolean.TRUE);
- tempMap.put("calendar", Boolean.TRUE);
- tempMap.put("plain", Boolean.TRUE);
- tempMap.put("rtf", Boolean.FALSE);
- tempMap.put("tab-separated-values", Boolean.FALSE);
- tempMap.put("t140", Boolean.FALSE);
- tempMap.put("rfc822-headers", Boolean.FALSE);
- tempMap.put("parityfec", Boolean.FALSE);
- textMIMESubtypeCharsetSupport = Collections.synchronizedMap(tempMap);
}
/**
@@ -232,171 +181,6 @@
}
/**
- * Converts an arbitrary text encoding to its canonical name.
- */
- public static String canonicalName(String encoding) {
- if (encoding == null) {
- return null;
- }
- try {
- return Charset.forName(encoding).name();
- } catch (IllegalCharsetNameException icne) {
- return encoding;
- } catch (UnsupportedCharsetException uce) {
- return encoding;
- }
- }
-
- /**
- * If the specified flavor is a text flavor which supports the "charset"
- * parameter, then this method returns that parameter, or the default
- * charset if no such parameter was specified at construction. For non-
- * text DataFlavors, and for non-charset text flavors, this method returns
- * null.
- */
- public static String getTextCharset(DataFlavor flavor) {
- if (!isFlavorCharsetTextType(flavor)) {
- return null;
- }
-
- String encoding = flavor.getParameter("charset");
-
- return (encoding != null) ? encoding : Charset.defaultCharset().name();
- }
-
- /**
- * Tests only whether the flavor's MIME type supports the charset
- * parameter. Must only be called for flavors with a primary type of
- * "text".
- */
- public static boolean doesSubtypeSupportCharset(DataFlavor flavor) {
- if (dtLog.isLoggable(PlatformLogger.Level.FINE)) {
- if (!"text".equals(flavor.getPrimaryType())) {
- dtLog.fine("Assertion (\"text\".equals(flavor.getPrimaryType())) failed");
- }
- }
-
- String subType = flavor.getSubType();
- if (subType == null) {
- return false;
- }
-
- Boolean support = textMIMESubtypeCharsetSupport.get(subType);
-
- if (support != null) {
- return support;
- }
-
- boolean ret_val = (flavor.getParameter("charset") != null);
- textMIMESubtypeCharsetSupport.put(subType, ret_val);
- return ret_val;
- }
- public static boolean doesSubtypeSupportCharset(String subType,
- String charset)
- {
- Boolean support = textMIMESubtypeCharsetSupport.get(subType);
-
- if (support != null) {
- return support;
- }
-
- boolean ret_val = (charset != null);
- textMIMESubtypeCharsetSupport.put(subType, ret_val);
- return ret_val;
- }
-
- /**
- * Returns whether this flavor is a text type which supports the
- * 'charset' parameter.
- */
- public static boolean isFlavorCharsetTextType(DataFlavor flavor) {
- // Although stringFlavor doesn't actually support the charset
- // parameter (because its primary MIME type is not "text"), it should
- // be treated as though it does. stringFlavor is semantically
- // equivalent to "text/plain" data.
- if (DataFlavor.stringFlavor.equals(flavor)) {
- return true;
- }
-
- if (!"text".equals(flavor.getPrimaryType()) ||
- !doesSubtypeSupportCharset(flavor))
- {
- return false;
- }
-
- Class<?> rep_class = flavor.getRepresentationClass();
-
- if (flavor.isRepresentationClassReader() ||
- String.class.equals(rep_class) ||
- flavor.isRepresentationClassCharBuffer() ||
- char[].class.equals(rep_class))
- {
- return true;
- }
-
- if (!(flavor.isRepresentationClassInputStream() ||
- flavor.isRepresentationClassByteBuffer() ||
- byte[].class.equals(rep_class))) {
- return false;
- }
-
- String charset = flavor.getParameter("charset");
-
- return (charset != null)
- ? DataTransferer.isEncodingSupported(charset)
- : true; // null equals default encoding which is always supported
- }
-
- /**
- * Returns whether this flavor is a text type which does not support the
- * 'charset' parameter.
- */
- public static boolean isFlavorNoncharsetTextType(DataFlavor flavor) {
- if (!"text".equals(flavor.getPrimaryType()) ||
- doesSubtypeSupportCharset(flavor))
- {
- return false;
- }
-
- return (flavor.isRepresentationClassInputStream() ||
- flavor.isRepresentationClassByteBuffer() ||
- byte[].class.equals(flavor.getRepresentationClass()));
- }
-
- /**
- * Determines whether this JRE can both encode and decode text in the
- * specified encoding.
- */
- private static boolean isEncodingSupported(String encoding) {
- if (encoding == null) {
- return false;
- }
- try {
- return Charset.isSupported(encoding);
- } catch (IllegalCharsetNameException icne) {
- return false;
- }
- }
-
- /**
- * Returns {@code true} if the given type is a java.rmi.Remote.
- */
- public static boolean isRemote(Class<?> type) {
- return RMI.isRemote(type);
- }
-
- /**
- * Returns an Iterator which traverses a SortedSet of Strings which are
- * a total order of the standard character sets supported by the JRE. The
- * ordering follows the same principles as DataFlavor.selectBestTextFlavor.
- * So as to avoid loading all available character converters, optional,
- * non-standard, character sets are not included.
- */
- public static Set <String> standardEncodings() {
- return StandardEncodingsHolder.standardEncodings;
- }
-
- /**
* Converts a FlavorMap to a FlavorTable.
*/
public static FlavorTable adaptFlavorMap(final FlavorMap map) {
@@ -600,8 +384,7 @@
indexMap.putAll(textPlainIndexMap);
// Sort the map keys according to the formats preference order.
- Comparator<Long> comparator =
- new IndexOrderComparator(indexMap, IndexedComparator.SELECT_WORST);
+ Comparator<Long> comparator = DataFlavorUtil.getIndexOrderComparator(indexMap).reversed();
SortedMap<Long, DataFlavor> sortedMap = new TreeMap<>(comparator);
sortedMap.putAll(formatMap);
@@ -972,7 +755,7 @@
// target format. Append terminating NUL bytes.
if (stringSelectionHack ||
(String.class.equals(flavor.getRepresentationClass()) &&
- isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
+ DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
String str = removeSuspectedData(flavor, contents, (String)obj);
@@ -983,7 +766,7 @@
// Source data is a Reader. Convert to a String and recur. In the
// future, we may want to rewrite this so that we encode on demand.
} else if (flavor.isRepresentationClassReader()) {
- if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
+ if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
throw new IOException
("cannot transfer non-text data as Reader");
}
@@ -1002,7 +785,7 @@
// Source data is a CharBuffer. Convert to a String and recur.
} else if (flavor.isRepresentationClassCharBuffer()) {
- if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
+ if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
throw new IOException
("cannot transfer non-text data as CharBuffer");
}
@@ -1018,7 +801,7 @@
// Source data is a char array. Convert to a String and recur.
} else if (char[].class.equals(flavor.getRepresentationClass())) {
- if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
+ if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
throw new IOException
("cannot transfer non-text data as char array");
}
@@ -1036,8 +819,8 @@
byte[] bytes = new byte[size];
buffer.get(bytes, 0, size);
- if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
- String sourceEncoding = DataTransferer.getTextCharset(flavor);
+ if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
+ String sourceEncoding = DataFlavorUtil.getTextCharset(flavor);
return translateTransferableString(
new String(bytes, sourceEncoding),
format);
@@ -1051,8 +834,8 @@
} else if (byte[].class.equals(flavor.getRepresentationClass())) {
byte[] bytes = (byte[])obj;
- if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
- String sourceEncoding = DataTransferer.getTextCharset(flavor);
+ if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
+ String sourceEncoding = DataFlavorUtil.getTextCharset(flavor);
return translateTransferableString(
new String(bytes, sourceEncoding),
format);
@@ -1155,9 +938,9 @@
} while (!eof);
}
- if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
+ if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
byte[] bytes = bos.toByteArray();
- String sourceEncoding = DataTransferer.getTextCharset(flavor);
+ String sourceEncoding = DataFlavorUtil.getTextCharset(flavor);
return translateTransferableString(
new String(bytes, sourceEncoding),
format);
@@ -1166,14 +949,11 @@
}
-
// Source data is an RMI object
} else if (flavor.isRepresentationClassRemote()) {
+ theByteArray = convertObjectToBytes(DataFlavorUtil.RMI.newMarshalledObject(obj));
- Object mo = RMI.newMarshalledObject(obj);
- theByteArray = convertObjectToBytes(mo);
-
- // Source data is Serializable
+ // Source data is Serializable
} else if (flavor.isRepresentationClassSerializable()) {
theByteArray = convertObjectToBytes(obj);
@@ -1387,7 +1167,7 @@
// Target data is a String. Strip terminating NUL bytes. Decode bytes
// into characters. Search-and-replace EOLN.
} else if (String.class.equals(flavor.getRepresentationClass()) &&
- isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
+ DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
theObject = translateBytesToString(bytes, format, localeTransferable);
@@ -1401,9 +1181,8 @@
}
// Target data is a CharBuffer. Recur to obtain String and wrap.
} else if (flavor.isRepresentationClassCharBuffer()) {
- if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
- throw new IOException
- ("cannot transfer non-text data as CharBuffer");
+ if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
+ throw new IOException("cannot transfer non-text data as CharBuffer");
}
CharBuffer buffer = CharBuffer.wrap(
@@ -1414,7 +1193,7 @@
// Target data is a char array. Recur to obtain String and convert to
// char array.
} else if (char[].class.equals(flavor.getRepresentationClass())) {
- if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
+ if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
throw new IOException
("cannot transfer non-text data as char array");
}
@@ -1427,10 +1206,10 @@
// terminators and search-and-replace EOLN, then reencode according to
// the requested flavor.
} else if (flavor.isRepresentationClassByteBuffer()) {
- if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
+ if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
bytes = translateBytesToString(
bytes, format, localeTransferable).getBytes(
- DataTransferer.getTextCharset(flavor)
+ DataFlavorUtil.getTextCharset(flavor)
);
}
@@ -1442,10 +1221,10 @@
// terminators and search-and-replace EOLN, then reencode according to
// the requested flavor.
} else if (byte[].class.equals(flavor.getRepresentationClass())) {
- if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
+ if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
theObject = translateBytesToString(
bytes, format, localeTransferable
- ).getBytes(DataTransferer.getTextCharset(flavor));
+ ).getBytes(DataFlavorUtil.getTextCharset(flavor));
} else {
theObject = bytes;
}
@@ -1462,9 +1241,9 @@
} else if (flavor.isRepresentationClassRemote()) {
try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
- ObjectInputStream ois = new ObjectInputStream(bais))
- {
- theObject = RMI.getMarshalledObject(ois.readObject());
+ ObjectInputStream ois = new ObjectInputStream(bais)) {
+
+ theObject = DataFlavorUtil.RMI.getMarshalledObject(ois.readObject());
} catch (Exception e) {
throw new IOException(e.getMessage());
}
@@ -1529,7 +1308,7 @@
// Target data is a String. Strip terminating NUL bytes. Decode bytes
// into characters. Search-and-replace EOLN.
} else if (String.class.equals(flavor.getRepresentationClass()) &&
- isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
+ DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
return translateBytesToString(inputStreamToByteArray(str),
format, localeTransferable);
@@ -1554,7 +1333,7 @@
// as "Unicode" (utf-16be). Then use an InputStreamReader to decode
// back to chars on demand.
} else if (flavor.isRepresentationClassReader()) {
- if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
+ if (!(DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
throw new IOException
("cannot transfer non-text data as Reader");
}
@@ -1563,27 +1342,24 @@
str, DataFlavor.plainTextFlavor,
format, localeTransferable);
- String unicode = DataTransferer.getTextCharset(DataFlavor.plainTextFlavor);
+ String unicode = DataFlavorUtil.getTextCharset(DataFlavor.plainTextFlavor);
Reader reader = new InputStreamReader(is, unicode);
theObject = constructFlavoredObject(reader, flavor, Reader.class);
// Target data is a byte array
} else if (byte[].class.equals(flavor.getRepresentationClass())) {
- if(isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
+ if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
theObject = translateBytesToString(inputStreamToByteArray(str), format, localeTransferable)
- .getBytes(DataTransferer.getTextCharset(flavor));
+ .getBytes(DataFlavorUtil.getTextCharset(flavor));
} else {
theObject = inputStreamToByteArray(str);
}
// Target data is an RMI object
} else if (flavor.isRepresentationClassRemote()) {
-
- try (ObjectInputStream ois =
- new ObjectInputStream(str))
- {
- theObject = RMI.getMarshalledObject(ois.readObject());
- }catch (Exception e) {
+ try (ObjectInputStream ois = new ObjectInputStream(str)) {
+ theObject = DataFlavorUtil.RMI.getMarshalledObject(ois.readObject());
+ } catch (Exception e) {
throw new IOException(e.getMessage());
}
@@ -1621,9 +1397,9 @@
(InputStream str, DataFlavor flavor, long format,
Transferable localeTransferable) throws IOException
{
- if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
+ if (DataFlavorUtil.isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
str = new ReencodingInputStream
- (str, format, DataTransferer.getTextCharset(flavor),
+ (str, format, DataFlavorUtil.getTextCharset(flavor),
localeTransferable);
}
@@ -1865,8 +1641,7 @@
byte[] bytes, String mimeType) throws IOException
{
- Iterator<ImageReader> readerIterator =
- ImageIO.getImageReadersByMIMEType(mimeType);
+ Iterator<ImageReader> readerIterator = ImageIO.getImageReadersByMIMEType(mimeType);
if (!readerIterator.hasNext()) {
throw new IOException("No registered service provider can decode " +
@@ -1919,8 +1694,7 @@
throws IOException {
IOException originalIOE = null;
- Iterator<ImageWriter> writerIterator =
- ImageIO.getImageWritersByMIMEType(mimeType);
+ Iterator<ImageWriter> writerIterator = ImageIO.getImageWritersByMIMEType(mimeType);
if (!writerIterator.hasNext()) {
throw new IOException("No registered service provider can encode " +
@@ -1979,8 +1753,7 @@
String mimeType)
throws IOException {
- Iterator<ImageWriter> writerIterator =
- ImageIO.getImageWritersByMIMEType(mimeType);
+ Iterator<ImageWriter> writerIterator = ImageIO.getImageWritersByMIMEType(mimeType);
ImageTypeSpecifier typeSpecifier =
new ImageTypeSpecifier(renderedImage);
@@ -2163,8 +1936,7 @@
}
}
- public abstract ToolkitThreadBlockedHandler
- getToolkitThreadBlockedHandler();
+ public abstract ToolkitThreadBlockedHandler getToolkitThreadBlockedHandler();
/**
* Helper function to reduce a Map with Long keys to a long array.
@@ -2189,8 +1961,7 @@
public static DataFlavor[] setToSortedDataFlavorArray(Set<DataFlavor> flavorsSet) {
DataFlavor[] flavors = new DataFlavor[flavorsSet.size()];
flavorsSet.toArray(flavors);
- final Comparator<DataFlavor> comparator =
- new DataFlavorComparator(IndexedComparator.SELECT_WORST);
+ final Comparator<DataFlavor> comparator = DataFlavorUtil.getDataFlavorComparator().reversed();
Arrays.sort(flavors, comparator);
return flavors;
}
@@ -2230,519 +2001,4 @@
public LinkedHashSet<String> getPlatformMappingsForFlavor(DataFlavor df) {
return new LinkedHashSet<>();
}
-
- /**
- * A Comparator which includes a helper function for comparing two Objects
- * which are likely to be keys in the specified Map.
- */
- public abstract static class IndexedComparator<T> implements Comparator<T> {
-
- /**
- * The best Object (e.g., DataFlavor) will be the last in sequence.
- */
- public static final boolean SELECT_BEST = true;
-
- /**
- * The best Object (e.g., DataFlavor) will be the first in sequence.
- */
- public static final boolean SELECT_WORST = false;
-
- final boolean order;
-
- public IndexedComparator(boolean order) {
- this.order = order;
- }
-
- /**
- * Helper method to compare two objects by their Integer indices in the
- * given map. If the map doesn't contain an entry for either of the
- * objects, the fallback index will be used for the object instead.
- *
- * @param indexMap the map which maps objects into Integer indexes.
- * @param obj1 the first object to be compared.
- * @param obj2 the second object to be compared.
- * @param fallbackIndex the Integer to be used as a fallback index.
- * @return a negative integer, zero, or a positive integer as the
- * first object is mapped to a less, equal to, or greater
- * index than the second.
- */
- static <T> int compareIndices(Map<T, Integer> indexMap,
- T obj1, T obj2,
- Integer fallbackIndex) {
- Integer index1 = indexMap.getOrDefault(obj1, fallbackIndex);
- Integer index2 = indexMap.getOrDefault(obj2, fallbackIndex);
- return index1.compareTo(index2);
- }
- }
-
- /**
- * An IndexedComparator which compares two String charsets. The comparison
- * follows the rules outlined in DataFlavor.selectBestTextFlavor. In order
- * to ensure that non-Unicode, non-ASCII, non-default charsets are sorted
- * in alphabetical order, charsets are not automatically converted to their
- * canonical forms.
- */
- public static class CharsetComparator extends IndexedComparator<String> {
- private static final Map<String, Integer> charsets;
-
- private static final Integer DEFAULT_CHARSET_INDEX = 2;
- private static final Integer OTHER_CHARSET_INDEX = 1;
- private static final Integer WORST_CHARSET_INDEX = 0;
- private static final Integer UNSUPPORTED_CHARSET_INDEX = Integer.MIN_VALUE;
-
- private static final String UNSUPPORTED_CHARSET = "UNSUPPORTED";
-
- static {
- Map<String, Integer> charsetsMap = new HashMap<>(8, 1.0f);
-
- // we prefer Unicode charsets
- charsetsMap.put(canonicalName("UTF-16LE"), 4);
- charsetsMap.put(canonicalName("UTF-16BE"), 5);
- charsetsMap.put(canonicalName("UTF-8"), 6);
- charsetsMap.put(canonicalName("UTF-16"), 7);
-
- // US-ASCII is the worst charset supported
- charsetsMap.put(canonicalName("US-ASCII"), WORST_CHARSET_INDEX);
-
- charsetsMap.putIfAbsent(Charset.defaultCharset().name(), DEFAULT_CHARSET_INDEX);
-
- charsetsMap.put(UNSUPPORTED_CHARSET, UNSUPPORTED_CHARSET_INDEX);
-
- charsets = Collections.unmodifiableMap(charsetsMap);
- }
-
- public CharsetComparator(boolean order) {
- super(order);
- }
-
- /**
- * Compares two String objects. Returns a negative integer, zero,
- * or a positive integer as the first charset is worse than, equal to,
- * or better than the second.
- *
- * @param obj1 the first charset to be compared
- * @param obj2 the second charset to be compared
- * @return a negative integer, zero, or a positive integer as the
- * first argument is worse, equal to, or better than the
- * second.
- * @throws ClassCastException if either of the arguments is not
- * instance of String
- * @throws NullPointerException if either of the arguments is
- * <code>null</code>.
- */
- public int compare(String obj1, String obj2) {
- if (order == SELECT_BEST) {
- return compareCharsets(obj1, obj2);
- } else {
- return compareCharsets(obj2, obj1);
- }
- }
-
- /**
- * Compares charsets. Returns a negative integer, zero, or a positive
- * integer as the first charset is worse than, equal to, or better than
- * the second.
- * <p>
- * Charsets are ordered according to the following rules:
- * <ul>
- * <li>All unsupported charsets are equal.
- * <li>Any unsupported charset is worse than any supported charset.
- * <li>Unicode charsets, such as "UTF-16", "UTF-8", "UTF-16BE" and
- * "UTF-16LE", are considered best.
- * <li>After them, platform default charset is selected.
- * <li>"US-ASCII" is the worst of supported charsets.
- * <li>For all other supported charsets, the lexicographically less
- * one is considered the better.
- * </ul>
- *
- * @param charset1 the first charset to be compared
- * @param charset2 the second charset to be compared.
- * @return a negative integer, zero, or a positive integer as the
- * first argument is worse, equal to, or better than the
- * second.
- */
- int compareCharsets(String charset1, String charset2) {
- charset1 = getEncoding(charset1);
- charset2 = getEncoding(charset2);
-
- int comp = compareIndices(charsets, charset1, charset2,
- OTHER_CHARSET_INDEX);
-
- if (comp == 0) {
- return charset2.compareTo(charset1);
- }
-
- return comp;
- }
-
- /**
- * Returns encoding for the specified charset according to the
- * following rules:
- * <ul>
- * <li>If the charset is <code>null</code>, then <code>null</code> will
- * be returned.
- * <li>Iff the charset specifies an encoding unsupported by this JRE,
- * <code>UNSUPPORTED_CHARSET</code> will be returned.
- * <li>If the charset specifies an alias name, the corresponding
- * canonical name will be returned iff the charset is a known
- * Unicode, ASCII, or default charset.
- * </ul>
- *
- * @param charset the charset.
- * @return an encoding for this charset.
- */
- static String getEncoding(String charset) {
- if (charset == null) {
- return null;
- } else if (!DataTransferer.isEncodingSupported(charset)) {
- return UNSUPPORTED_CHARSET;
- } else {
- // Only convert to canonical form if the charset is one
- // of the charsets explicitly listed in the known charsets
- // map. This will happen only for Unicode, ASCII, or default
- // charsets.
- String canonicalName = DataTransferer.canonicalName(charset);
- return (charsets.containsKey(canonicalName))
- ? canonicalName
- : charset;
- }
- }
- }
-
- /**
- * An IndexedComparator which compares two DataFlavors. For text flavors,
- * the comparison follows the rules outlined in
- * DataFlavor.selectBestTextFlavor. For non-text flavors, unknown
- * application MIME types are preferred, followed by known
- * application/x-java-* MIME types. Unknown application types are preferred
- * because if the user provides his own data flavor, it will likely be the
- * most descriptive one. For flavors which are otherwise equal, the
- * flavors' string representation are compared in the alphabetical order.
- */
- public static class DataFlavorComparator extends IndexedComparator<DataFlavor> {
-
- private final CharsetComparator charsetComparator;
-
- private static final Map<String, Integer> exactTypes;
- private static final Map<String, Integer> primaryTypes;
- private static final Map<Class<?>, Integer> nonTextRepresentations;
- private static final Map<String, Integer> textTypes;
- private static final Map<Class<?>, Integer> decodedTextRepresentations;
- private static final Map<Class<?>, Integer> encodedTextRepresentations;
-
- private static final Integer UNKNOWN_OBJECT_LOSES = Integer.MIN_VALUE;
- private static final Integer UNKNOWN_OBJECT_WINS = Integer.MAX_VALUE;
-
- static {
- {
- Map<String, Integer> exactTypesMap = new HashMap<>(4, 1.0f);
-
- // application/x-java-* MIME types
- exactTypesMap.put("application/x-java-file-list", 0);
- exactTypesMap.put("application/x-java-serialized-object", 1);
- exactTypesMap.put("application/x-java-jvm-local-objectref", 2);
- exactTypesMap.put("application/x-java-remote-object", 3);
-
- exactTypes = Collections.unmodifiableMap(exactTypesMap);
- }
-
- {
- Map<String, Integer> primaryTypesMap = new HashMap<>(1, 1.0f);
-
- primaryTypesMap.put("application", 0);
-
- primaryTypes = Collections.unmodifiableMap(primaryTypesMap);
- }
-
- {
- Map<Class<?>, Integer> nonTextRepresentationsMap = new HashMap<>(3, 1.0f);
-
- nonTextRepresentationsMap.put(java.io.InputStream.class, 0);
- nonTextRepresentationsMap.put(java.io.Serializable.class, 1);
-
- Class<?> remoteClass = RMI.remoteClass();
- if (remoteClass != null) {
- nonTextRepresentationsMap.put(remoteClass, 2);
- }
-
- nonTextRepresentations = Collections.unmodifiableMap(nonTextRepresentationsMap);
- }
-
- {
- Map<String, Integer> textTypesMap = new HashMap<>(16, 1.0f);
-
- // plain text
- textTypesMap.put("text/plain", 0);
-
- // stringFlavor
- textTypesMap.put("application/x-java-serialized-object", 1);
-
- // misc
- textTypesMap.put("text/calendar", 2);
- textTypesMap.put("text/css", 3);
- textTypesMap.put("text/directory", 4);
- textTypesMap.put("text/parityfec", 5);
- textTypesMap.put("text/rfc822-headers", 6);
- textTypesMap.put("text/t140", 7);
- textTypesMap.put("text/tab-separated-values", 8);
- textTypesMap.put("text/uri-list", 9);
-
- // enriched
- textTypesMap.put("text/richtext", 10);
- textTypesMap.put("text/enriched", 11);
- textTypesMap.put("text/rtf", 12);
-
- // markup
- textTypesMap.put("text/html", 13);
- textTypesMap.put("text/xml", 14);
- textTypesMap.put("text/sgml", 15);
-
- textTypes = Collections.unmodifiableMap(textTypesMap);
- }
-
- {
- Map<Class<?>, Integer> decodedTextRepresentationsMap = new HashMap<>(4, 1.0f);
-
- decodedTextRepresentationsMap.put(char[].class, 0);
- decodedTextRepresentationsMap.put(CharBuffer.class, 1);
- decodedTextRepresentationsMap.put(String.class, 2);
- decodedTextRepresentationsMap.put(Reader.class, 3);
-
- decodedTextRepresentations =
- Collections.unmodifiableMap(decodedTextRepresentationsMap);
- }
-
- {
- Map<Class<?>, Integer> encodedTextRepresentationsMap = new HashMap<>(3, 1.0f);
-
- encodedTextRepresentationsMap.put(byte[].class, 0);
- encodedTextRepresentationsMap.put(ByteBuffer.class, 1);
- encodedTextRepresentationsMap.put(InputStream.class, 2);
-
- encodedTextRepresentations =
- Collections.unmodifiableMap(encodedTextRepresentationsMap);
- }
- }
-
- public DataFlavorComparator() {
- this(SELECT_BEST);
- }
-
- public DataFlavorComparator(boolean order) {
- super(order);
-
- charsetComparator = new CharsetComparator(order);
- }
-
- public int compare(DataFlavor obj1, DataFlavor obj2) {
- DataFlavor flavor1 = order == SELECT_BEST ? obj1 : obj2;
- DataFlavor flavor2 = order == SELECT_BEST ? obj2 : obj1;
-
- if (flavor1.equals(flavor2)) {
- return 0;
- }
-
- int comp = 0;
-
- String primaryType1 = flavor1.getPrimaryType();
- String subType1 = flavor1.getSubType();
- String mimeType1 = primaryType1 + "/" + subType1;
- Class<?> class1 = flavor1.getRepresentationClass();
-
- String primaryType2 = flavor2.getPrimaryType();
- String subType2 = flavor2.getSubType();
- String mimeType2 = primaryType2 + "/" + subType2;
- Class<?> class2 = flavor2.getRepresentationClass();
-
- if (flavor1.isFlavorTextType() && flavor2.isFlavorTextType()) {
- // First, compare MIME types
- comp = compareIndices(textTypes, mimeType1, mimeType2,
- UNKNOWN_OBJECT_LOSES);
- if (comp != 0) {
- return comp;
- }
-
- // Only need to test one flavor because they both have the
- // same MIME type. Also don't need to worry about accidentally
- // passing stringFlavor because either
- // 1. Both flavors are stringFlavor, in which case the
- // equality test at the top of the function succeeded.
- // 2. Only one flavor is stringFlavor, in which case the MIME
- // type comparison returned a non-zero value.
- if (doesSubtypeSupportCharset(flavor1)) {
- // Next, prefer the decoded text representations of Reader,
- // String, CharBuffer, and [C, in that order.
- comp = compareIndices(decodedTextRepresentations, class1,
- class2, UNKNOWN_OBJECT_LOSES);
- if (comp != 0) {
- return comp;
- }
-
- // Next, compare charsets
- comp = charsetComparator.compareCharsets
- (DataTransferer.getTextCharset(flavor1),
- DataTransferer.getTextCharset(flavor2));
- if (comp != 0) {
- return comp;
- }
- }
-
- // Finally, prefer the encoded text representations of
- // InputStream, ByteBuffer, and [B, in that order.
- comp = compareIndices(encodedTextRepresentations, class1,
- class2, UNKNOWN_OBJECT_LOSES);
- if (comp != 0) {
- return comp;
- }
- } else {
- // First, prefer application types.
- comp = compareIndices(primaryTypes, primaryType1, primaryType2,
- UNKNOWN_OBJECT_LOSES);
- if (comp != 0) {
- return comp;
- }
-
- // Next, look for application/x-java-* types. Prefer unknown
- // MIME types because if the user provides his own data flavor,
- // it will likely be the most descriptive one.
- comp = compareIndices(exactTypes, mimeType1, mimeType2,
- UNKNOWN_OBJECT_WINS);
- if (comp != 0) {
- return comp;
- }
-
- // Finally, prefer the representation classes of Remote,
- // Serializable, and InputStream, in that order.
- comp = compareIndices(nonTextRepresentations, class1, class2,
- UNKNOWN_OBJECT_LOSES);
- if (comp != 0) {
- return comp;
- }
- }
-
- // The flavours are not equal but still not distinguishable.
- // Compare String representations in alphabetical order
- return flavor1.getMimeType().compareTo(flavor2.getMimeType());
- }
- }
-
- /*
- * Given the Map that maps objects to Integer indices and a boolean value,
- * this Comparator imposes a direct or reverse order on set of objects.
- * <p>
- * If the specified boolean value is SELECT_BEST, the Comparator imposes the
- * direct index-based order: an object A is greater than an object B if and
- * only if the index of A is greater than the index of B. An object that
- * doesn't have an associated index is less or equal than any other object.
- * <p>
- * If the specified boolean value is SELECT_WORST, the Comparator imposes the
- * reverse index-based order: an object A is greater than an object B if and
- * only if A is less than B with the direct index-based order.
- */
- public static class IndexOrderComparator extends IndexedComparator<Long> {
- private final Map<Long, Integer> indexMap;
- private static final Integer FALLBACK_INDEX = Integer.MIN_VALUE;
-
- public IndexOrderComparator(Map<Long, Integer> indexMap, boolean order) {
- super(order);
- this.indexMap = indexMap;
- }
-
- public int compare(Long obj1, Long obj2) {
- if (order == SELECT_WORST) {
- return -compareIndices(indexMap, obj1, obj2, FALLBACK_INDEX);
- } else {
- return compareIndices(indexMap, obj1, obj2, FALLBACK_INDEX);
- }
- }
- }
-
- /**
- * A class that provides access to java.rmi.Remote and java.rmi.MarshalledObject
- * without creating a static dependency.
- */
- private static class RMI {
- private static final Class<?> remoteClass = getClass("java.rmi.Remote");
- private static final Class<?> marshallObjectClass =
- getClass("java.rmi.MarshalledObject");
- private static final Constructor<?> marshallCtor =
- getConstructor(marshallObjectClass, Object.class);
- private static final Method marshallGet =
- getMethod(marshallObjectClass, "get");
-
- private static Class<?> getClass(String name) {
- try {
- return Class.forName(name, true, null);
- } catch (ClassNotFoundException e) {
- return null;
- }
- }
-
- private static Constructor<?> getConstructor(Class<?> c, Class<?>... types) {
- try {
- return (c == null) ? null : c.getDeclaredConstructor(types);
- } catch (NoSuchMethodException x) {
- throw new AssertionError(x);
- }
- }
-
- private static Method getMethod(Class<?> c, String name, Class<?>... types) {
- try {
- return (c == null) ? null : c.getMethod(name, types);
- } catch (NoSuchMethodException e) {
- throw new AssertionError(e);
- }
- }
-
- /**
- * Returns {@code true} if the given class is java.rmi.Remote.
- */
- static boolean isRemote(Class<?> c) {
- return (remoteClass == null) ? false : remoteClass.isAssignableFrom(c);
- }
-
- /**
- * Returns java.rmi.Remote.class if RMI is present; otherwise {@code null}.
- */
- static Class<?> remoteClass() {
- return remoteClass;
- }
-
- /**
- * Returns a new MarshalledObject containing the serialized representation
- * of the given object.
- */
- static Object newMarshalledObject(Object obj) throws IOException {
- try {
- return marshallCtor.newInstance(obj);
- } catch (InstantiationException | IllegalAccessException x) {
- throw new AssertionError(x);
- } catch (InvocationTargetException x) {
- Throwable cause = x.getCause();
- if (cause instanceof IOException)
- throw (IOException)cause;
- throw new AssertionError(x);
- }
- }
-
- /**
- * Returns a new copy of the contained marshalled object.
- */
- static Object getMarshalledObject(Object obj)
- throws IOException, ClassNotFoundException
- {
- try {
- return marshallGet.invoke(obj);
- } catch (IllegalAccessException x) {
- throw new AssertionError(x);
- } catch (InvocationTargetException x) {
- Throwable cause = x.getCause();
- if (cause instanceof IOException)
- throw (IOException)cause;
- if (cause instanceof ClassNotFoundException)
- throw (ClassNotFoundException)cause;
- throw new AssertionError(x);
- }
- }
- }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/awt/datatransfer/DesktopDatatransferServiceImpl.java Mon Jul 28 19:02:56 2014 +0400
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.awt.datatransfer;
+
+import sun.awt.AppContext;
+import sun.datatransfer.DesktopDatatransferService;
+
+import java.awt.EventQueue;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.FlavorMap;
+import java.util.LinkedHashSet;
+import java.util.function.Supplier;
+
+/**
+ * Provides desktop services to the datatransfer module according to
+ * {@code DesktopDatatransferService} interface.
+ *
+ * @author Petr Pchelko
+ * @since 1.9
+ */
+public class DesktopDatatransferServiceImpl implements DesktopDatatransferService {
+
+ private static final Object FLAVOR_MAP_KEY = new Object();
+
+ @Override
+ public void invokeOnEventThread(Runnable r) {
+ EventQueue.invokeLater(r);
+ }
+
+ @Override
+ public String getDefaultUnicodeEncoding() {
+ DataTransferer dataTransferer = DataTransferer.getInstance();
+ if (dataTransferer != null) {
+ return dataTransferer.getDefaultUnicodeEncoding();
+ }
+ return null;
+ }
+
+ @Override
+ public FlavorMap getFlavorMap(Supplier<FlavorMap> supplier) {
+ AppContext context = AppContext.getAppContext();
+ FlavorMap fm = (FlavorMap) context.get(FLAVOR_MAP_KEY);
+ if (fm == null) {
+ fm = supplier.get();
+ context.put(FLAVOR_MAP_KEY, fm);
+ }
+ return fm;
+ }
+
+ @Override
+ public boolean isDesktopPresent() {
+ return true;
+ }
+
+ @Override
+ public LinkedHashSet<DataFlavor> getPlatformMappingsForNative(String nat) {
+ DataTransferer instance = DataTransferer.getInstance();
+ return instance != null ? instance.getPlatformMappingsForNative(nat) : new LinkedHashSet<>();
+ }
+
+ @Override
+ public LinkedHashSet<String> getPlatformMappingsForFlavor(DataFlavor df) {
+ DataTransferer instance = DataTransferer.getInstance();
+ return instance != null ? instance.getPlatformMappingsForFlavor(df) : new LinkedHashSet<>();
+ }
+
+ @Override
+ public void registerTextFlavorProperties(String nat, String charset, String eoln, String terminators) {
+ DataTransferer instance = DataTransferer.getInstance();
+ if (instance != null) {
+ instance.registerTextFlavorProperties(nat, charset, eoln, terminators);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/awt/datatransfer/META-INF/services/sun.datatransfer.DesktopDatatransferService Mon Jul 28 19:02:56 2014 +0400
@@ -0,0 +1,1 @@
+sun.awt.datatransfer.DesktopDatatransferServiceImpl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/datatransfer/DataFlavorUtil.java Mon Jul 28 19:02:56 2014 +0400
@@ -0,0 +1,832 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.datatransfer;
+
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.FlavorMap;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.StandardCharsets;
+import java.nio.charset.UnsupportedCharsetException;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.function.Supplier;
+
+
+/**
+ * Utility class with different datatransfer helper functions
+ *
+ * @see 1.9
+ */
+public class DataFlavorUtil {
+
+ private DataFlavorUtil() {
+ // Avoid instantiation
+ }
+
+ private static Comparator<String> getCharsetComparator() {
+ return CharsetComparator.INSTANCE;
+ }
+
+ public static Comparator<DataFlavor> getDataFlavorComparator() {
+ return DataFlavorComparator.INSTANCE;
+ }
+
+ public static Comparator<Long> getIndexOrderComparator(Map<Long, Integer> indexMap) {
+ return new IndexOrderComparator(indexMap);
+ }
+
+ public static Comparator<DataFlavor> getTextFlavorComparator() {
+ return TextFlavorComparator.INSTANCE;
+ }
+
+ /**
+ * Tracks whether a particular text/* MIME type supports the charset
+ * parameter. The Map is initialized with all of the standard MIME types
+ * listed in the DataFlavor.selectBestTextFlavor method comment. Additional
+ * entries may be added during the life of the JRE for text/<other> types.
+ */
+ private static final Map<String, Boolean> textMIMESubtypeCharsetSupport;
+
+ static {
+ Map<String, Boolean> tempMap = new HashMap<>(17);
+ tempMap.put("sgml", Boolean.TRUE);
+ tempMap.put("xml", Boolean.TRUE);
+ tempMap.put("html", Boolean.TRUE);
+ tempMap.put("enriched", Boolean.TRUE);
+ tempMap.put("richtext", Boolean.TRUE);
+ tempMap.put("uri-list", Boolean.TRUE);
+ tempMap.put("directory", Boolean.TRUE);
+ tempMap.put("css", Boolean.TRUE);
+ tempMap.put("calendar", Boolean.TRUE);
+ tempMap.put("plain", Boolean.TRUE);
+ tempMap.put("rtf", Boolean.FALSE);
+ tempMap.put("tab-separated-values", Boolean.FALSE);
+ tempMap.put("t140", Boolean.FALSE);
+ tempMap.put("rfc822-headers", Boolean.FALSE);
+ tempMap.put("parityfec", Boolean.FALSE);
+ textMIMESubtypeCharsetSupport = Collections.synchronizedMap(tempMap);
+ }
+
+ /**
+ * Lazy initialization of Standard Encodings.
+ */
+ private static class StandardEncodingsHolder {
+ private static final SortedSet<String> standardEncodings = load();
+
+ private static SortedSet<String> load() {
+ final SortedSet<String> tempSet = new TreeSet<>(getCharsetComparator().reversed());
+ tempSet.add("US-ASCII");
+ tempSet.add("ISO-8859-1");
+ tempSet.add("UTF-8");
+ tempSet.add("UTF-16BE");
+ tempSet.add("UTF-16LE");
+ tempSet.add("UTF-16");
+ tempSet.add(Charset.defaultCharset().name());
+ return Collections.unmodifiableSortedSet(tempSet);
+ }
+ }
+
+ /**
+ * Returns a {@code SortedSet} of Strings which are a total order of the standard
+ * character sets supported by the JRE. The ordering follows the same principles as
+ * {@link java.awt.datatransfer.DataFlavor#selectBestTextFlavor(java.awt.datatransfer.DataFlavor[])}.
+ * So as to avoid loading all available character converters, optional, non-standard,
+ * character sets are not included.
+ */
+ public static Set<String> standardEncodings() {
+ return StandardEncodingsHolder.standardEncodings;
+ }
+
+ /**
+ * Converts an arbitrary text encoding to its canonical name.
+ */
+ public static String canonicalName(String encoding) {
+ if (encoding == null) {
+ return null;
+ }
+ try {
+ return Charset.forName(encoding).name();
+ } catch (IllegalCharsetNameException icne) {
+ return encoding;
+ } catch (UnsupportedCharsetException uce) {
+ return encoding;
+ }
+ }
+
+ /**
+ * Tests only whether the flavor's MIME type supports the charset
+ * parameter. Must only be called for flavors with a primary type of
+ * "text".
+ */
+ public static boolean doesSubtypeSupportCharset(DataFlavor flavor) {
+ String subType = flavor.getSubType();
+ if (subType == null) {
+ return false;
+ }
+
+ Boolean support = textMIMESubtypeCharsetSupport.get(subType);
+
+ if (support != null) {
+ return support;
+ }
+
+ boolean ret_val = (flavor.getParameter("charset") != null);
+ textMIMESubtypeCharsetSupport.put(subType, ret_val);
+ return ret_val;
+ }
+ public static boolean doesSubtypeSupportCharset(String subType,
+ String charset)
+ {
+ Boolean support = textMIMESubtypeCharsetSupport.get(subType);
+
+ if (support != null) {
+ return support;
+ }
+
+ boolean ret_val = (charset != null);
+ textMIMESubtypeCharsetSupport.put(subType, ret_val);
+ return ret_val;
+ }
+
+
+ /**
+ * Returns whether this flavor is a text type which supports the
+ * 'charset' parameter.
+ */
+ public static boolean isFlavorCharsetTextType(DataFlavor flavor) {
+ // Although stringFlavor doesn't actually support the charset
+ // parameter (because its primary MIME type is not "text"), it should
+ // be treated as though it does. stringFlavor is semantically
+ // equivalent to "text/plain" data.
+ if (DataFlavor.stringFlavor.equals(flavor)) {
+ return true;
+ }
+
+ if (!"text".equals(flavor.getPrimaryType()) ||
+ !doesSubtypeSupportCharset(flavor))
+ {
+ return false;
+ }
+
+ Class<?> rep_class = flavor.getRepresentationClass();
+
+ if (flavor.isRepresentationClassReader() ||
+ String.class.equals(rep_class) ||
+ flavor.isRepresentationClassCharBuffer() ||
+ char[].class.equals(rep_class))
+ {
+ return true;
+ }
+
+ if (!(flavor.isRepresentationClassInputStream() ||
+ flavor.isRepresentationClassByteBuffer() ||
+ byte[].class.equals(rep_class))) {
+ return false;
+ }
+
+ String charset = flavor.getParameter("charset");
+
+ // null equals default encoding which is always supported
+ return (charset == null) || isEncodingSupported(charset);
+ }
+
+ /**
+ * Returns whether this flavor is a text type which does not support the
+ * 'charset' parameter.
+ */
+ public static boolean isFlavorNoncharsetTextType(DataFlavor flavor) {
+ if (!"text".equals(flavor.getPrimaryType()) || doesSubtypeSupportCharset(flavor)) {
+ return false;
+ }
+
+ return (flavor.isRepresentationClassInputStream() ||
+ flavor.isRepresentationClassByteBuffer() ||
+ byte[].class.equals(flavor.getRepresentationClass()));
+ }
+
+ /**
+ * If the specified flavor is a text flavor which supports the "charset"
+ * parameter, then this method returns that parameter, or the default
+ * charset if no such parameter was specified at construction. For non-
+ * text DataFlavors, and for non-charset text flavors, this method returns
+ * null.
+ */
+ public static String getTextCharset(DataFlavor flavor) {
+ if (!isFlavorCharsetTextType(flavor)) {
+ return null;
+ }
+
+ String encoding = flavor.getParameter("charset");
+
+ return (encoding != null) ? encoding : Charset.defaultCharset().name();
+ }
+
+ /**
+ * Determines whether this JRE can both encode and decode text in the
+ * specified encoding.
+ */
+ private static boolean isEncodingSupported(String encoding) {
+ if (encoding == null) {
+ return false;
+ }
+ try {
+ return Charset.isSupported(encoding);
+ } catch (IllegalCharsetNameException icne) {
+ return false;
+ }
+ }
+
+ /**
+ * Helper method to compare two objects by their Integer indices in the
+ * given map. If the map doesn't contain an entry for either of the
+ * objects, the fallback index will be used for the object instead.
+ *
+ * @param indexMap the map which maps objects into Integer indexes.
+ * @param obj1 the first object to be compared.
+ * @param obj2 the second object to be compared.
+ * @param fallbackIndex the Integer to be used as a fallback index.
+ * @return a negative integer, zero, or a positive integer as the
+ * first object is mapped to a less, equal to, or greater
+ * index than the second.
+ */
+ static <T> int compareIndices(Map<T, Integer> indexMap,
+ T obj1, T obj2,
+ Integer fallbackIndex) {
+ Integer index1 = indexMap.getOrDefault(obj1, fallbackIndex);
+ Integer index2 = indexMap.getOrDefault(obj2, fallbackIndex);
+ return index1.compareTo(index2);
+ }
+
+ /**
+ * An IndexedComparator which compares two String charsets. The comparison
+ * follows the rules outlined in DataFlavor.selectBestTextFlavor. In order
+ * to ensure that non-Unicode, non-ASCII, non-default charsets are sorted
+ * in alphabetical order, charsets are not automatically converted to their
+ * canonical forms.
+ */
+ private static class CharsetComparator implements Comparator<String> {
+ static final CharsetComparator INSTANCE = new CharsetComparator();
+
+ private static final Map<String, Integer> charsets;
+
+ private static final Integer DEFAULT_CHARSET_INDEX = 2;
+ private static final Integer OTHER_CHARSET_INDEX = 1;
+ private static final Integer WORST_CHARSET_INDEX = 0;
+ private static final Integer UNSUPPORTED_CHARSET_INDEX = Integer.MIN_VALUE;
+
+ private static final String UNSUPPORTED_CHARSET = "UNSUPPORTED";
+
+ static {
+ Map<String, Integer> charsetsMap = new HashMap<>(8, 1.0f);
+
+ // we prefer Unicode charsets
+ charsetsMap.put(canonicalName("UTF-16LE"), 4);
+ charsetsMap.put(canonicalName("UTF-16BE"), 5);
+ charsetsMap.put(canonicalName("UTF-8"), 6);
+ charsetsMap.put(canonicalName("UTF-16"), 7);
+
+ // US-ASCII is the worst charset supported
+ charsetsMap.put(canonicalName("US-ASCII"), WORST_CHARSET_INDEX);
+
+ charsetsMap.putIfAbsent(Charset.defaultCharset().name(), DEFAULT_CHARSET_INDEX);
+
+ charsetsMap.put(UNSUPPORTED_CHARSET, UNSUPPORTED_CHARSET_INDEX);
+
+ charsets = Collections.unmodifiableMap(charsetsMap);
+ }
+
+ /**
+ * Compares charsets. Returns a negative integer, zero, or a positive
+ * integer as the first charset is worse than, equal to, or better than
+ * the second.
+ * <p>
+ * Charsets are ordered according to the following rules:
+ * <ul>
+ * <li>All unsupported charsets are equal.
+ * <li>Any unsupported charset is worse than any supported charset.
+ * <li>Unicode charsets, such as "UTF-16", "UTF-8", "UTF-16BE" and
+ * "UTF-16LE", are considered best.
+ * <li>After them, platform default charset is selected.
+ * <li>"US-ASCII" is the worst of supported charsets.
+ * <li>For all other supported charsets, the lexicographically less
+ * one is considered the better.
+ * </ul>
+ *
+ * @param charset1 the first charset to be compared
+ * @param charset2 the second charset to be compared.
+ * @return a negative integer, zero, or a positive integer as the
+ * first argument is worse, equal to, or better than the
+ * second.
+ */
+ public int compare(String charset1, String charset2) {
+ charset1 = getEncoding(charset1);
+ charset2 = getEncoding(charset2);
+
+ int comp = compareIndices(charsets, charset1, charset2, OTHER_CHARSET_INDEX);
+
+ if (comp == 0) {
+ return charset2.compareTo(charset1);
+ }
+
+ return comp;
+ }
+
+ /**
+ * Returns encoding for the specified charset according to the
+ * following rules:
+ * <ul>
+ * <li>If the charset is <code>null</code>, then <code>null</code> will
+ * be returned.
+ * <li>Iff the charset specifies an encoding unsupported by this JRE,
+ * <code>UNSUPPORTED_CHARSET</code> will be returned.
+ * <li>If the charset specifies an alias name, the corresponding
+ * canonical name will be returned iff the charset is a known
+ * Unicode, ASCII, or default charset.
+ * </ul>
+ *
+ * @param charset the charset.
+ * @return an encoding for this charset.
+ */
+ static String getEncoding(String charset) {
+ if (charset == null) {
+ return null;
+ } else if (!isEncodingSupported(charset)) {
+ return UNSUPPORTED_CHARSET;
+ } else {
+ // Only convert to canonical form if the charset is one
+ // of the charsets explicitly listed in the known charsets
+ // map. This will happen only for Unicode, ASCII, or default
+ // charsets.
+ String canonicalName = canonicalName(charset);
+ return (charsets.containsKey(canonicalName))
+ ? canonicalName
+ : charset;
+ }
+ }
+ }
+
+ /**
+ * An IndexedComparator which compares two DataFlavors. For text flavors,
+ * the comparison follows the rules outlined in
+ * DataFlavor.selectBestTextFlavor. For non-text flavors, unknown
+ * application MIME types are preferred, followed by known
+ * application/x-java-* MIME types. Unknown application types are preferred
+ * because if the user provides his own data flavor, it will likely be the
+ * most descriptive one. For flavors which are otherwise equal, the
+ * flavors' string representation are compared in the alphabetical order.
+ */
+ private static class DataFlavorComparator implements Comparator<DataFlavor> {
+
+ static final DataFlavorComparator INSTANCE = new DataFlavorComparator();
+
+ private static final Map<String, Integer> exactTypes;
+ private static final Map<String, Integer> primaryTypes;
+ private static final Map<Class<?>, Integer> nonTextRepresentations;
+ private static final Map<String, Integer> textTypes;
+ private static final Map<Class<?>, Integer> decodedTextRepresentations;
+ private static final Map<Class<?>, Integer> encodedTextRepresentations;
+
+ private static final Integer UNKNOWN_OBJECT_LOSES = Integer.MIN_VALUE;
+ private static final Integer UNKNOWN_OBJECT_WINS = Integer.MAX_VALUE;
+
+ static {
+ {
+ Map<String, Integer> exactTypesMap = new HashMap<>(4, 1.0f);
+
+ // application/x-java-* MIME types
+ exactTypesMap.put("application/x-java-file-list", 0);
+ exactTypesMap.put("application/x-java-serialized-object", 1);
+ exactTypesMap.put("application/x-java-jvm-local-objectref", 2);
+ exactTypesMap.put("application/x-java-remote-object", 3);
+
+ exactTypes = Collections.unmodifiableMap(exactTypesMap);
+ }
+
+ {
+ Map<String, Integer> primaryTypesMap = new HashMap<>(1, 1.0f);
+
+ primaryTypesMap.put("application", 0);
+
+ primaryTypes = Collections.unmodifiableMap(primaryTypesMap);
+ }
+
+ {
+ Map<Class<?>, Integer> nonTextRepresentationsMap = new HashMap<>(3, 1.0f);
+
+ nonTextRepresentationsMap.put(java.io.InputStream.class, 0);
+ nonTextRepresentationsMap.put(java.io.Serializable.class, 1);
+
+ nonTextRepresentationsMap.put(RMI.remoteClass(), 2);
+
+ nonTextRepresentations = Collections.unmodifiableMap(nonTextRepresentationsMap);
+ }
+
+ {
+ Map<String, Integer> textTypesMap = new HashMap<>(16, 1.0f);
+
+ // plain text
+ textTypesMap.put("text/plain", 0);
+
+ // stringFlavor
+ textTypesMap.put("application/x-java-serialized-object", 1);
+
+ // misc
+ textTypesMap.put("text/calendar", 2);
+ textTypesMap.put("text/css", 3);
+ textTypesMap.put("text/directory", 4);
+ textTypesMap.put("text/parityfec", 5);
+ textTypesMap.put("text/rfc822-headers", 6);
+ textTypesMap.put("text/t140", 7);
+ textTypesMap.put("text/tab-separated-values", 8);
+ textTypesMap.put("text/uri-list", 9);
+
+ // enriched
+ textTypesMap.put("text/richtext", 10);
+ textTypesMap.put("text/enriched", 11);
+ textTypesMap.put("text/rtf", 12);
+
+ // markup
+ textTypesMap.put("text/html", 13);
+ textTypesMap.put("text/xml", 14);
+ textTypesMap.put("text/sgml", 15);
+
+ textTypes = Collections.unmodifiableMap(textTypesMap);
+ }
+
+ {
+ Map<Class<?>, Integer> decodedTextRepresentationsMap = new HashMap<>(4, 1.0f);
+
+ decodedTextRepresentationsMap.put(char[].class, 0);
+ decodedTextRepresentationsMap.put(CharBuffer.class, 1);
+ decodedTextRepresentationsMap.put(String.class, 2);
+ decodedTextRepresentationsMap.put(Reader.class, 3);
+
+ decodedTextRepresentations =
+ Collections.unmodifiableMap(decodedTextRepresentationsMap);
+ }
+
+ {
+ Map<Class<?>, Integer> encodedTextRepresentationsMap = new HashMap<>(3, 1.0f);
+
+ encodedTextRepresentationsMap.put(byte[].class, 0);
+ encodedTextRepresentationsMap.put(ByteBuffer.class, 1);
+ encodedTextRepresentationsMap.put(InputStream.class, 2);
+
+ encodedTextRepresentations =
+ Collections.unmodifiableMap(encodedTextRepresentationsMap);
+ }
+ }
+
+
+ public int compare(DataFlavor flavor1, DataFlavor flavor2) {
+ if (flavor1.equals(flavor2)) {
+ return 0;
+ }
+
+ int comp;
+
+ String primaryType1 = flavor1.getPrimaryType();
+ String subType1 = flavor1.getSubType();
+ String mimeType1 = primaryType1 + "/" + subType1;
+ Class<?> class1 = flavor1.getRepresentationClass();
+
+ String primaryType2 = flavor2.getPrimaryType();
+ String subType2 = flavor2.getSubType();
+ String mimeType2 = primaryType2 + "/" + subType2;
+ Class<?> class2 = flavor2.getRepresentationClass();
+
+ if (flavor1.isFlavorTextType() && flavor2.isFlavorTextType()) {
+ // First, compare MIME types
+ comp = compareIndices(textTypes, mimeType1, mimeType2, UNKNOWN_OBJECT_LOSES);
+ if (comp != 0) {
+ return comp;
+ }
+
+ // Only need to test one flavor because they both have the
+ // same MIME type. Also don't need to worry about accidentally
+ // passing stringFlavor because either
+ // 1. Both flavors are stringFlavor, in which case the
+ // equality test at the top of the function succeeded.
+ // 2. Only one flavor is stringFlavor, in which case the MIME
+ // type comparison returned a non-zero value.
+ if (doesSubtypeSupportCharset(flavor1)) {
+ // Next, prefer the decoded text representations of Reader,
+ // String, CharBuffer, and [C, in that order.
+ comp = compareIndices(decodedTextRepresentations, class1,
+ class2, UNKNOWN_OBJECT_LOSES);
+ if (comp != 0) {
+ return comp;
+ }
+
+ // Next, compare charsets
+ comp = CharsetComparator.INSTANCE.compare(getTextCharset(flavor1),
+ getTextCharset(flavor2));
+ if (comp != 0) {
+ return comp;
+ }
+ }
+
+ // Finally, prefer the encoded text representations of
+ // InputStream, ByteBuffer, and [B, in that order.
+ comp = compareIndices(encodedTextRepresentations, class1,
+ class2, UNKNOWN_OBJECT_LOSES);
+ if (comp != 0) {
+ return comp;
+ }
+ } else {
+ // First, prefer application types.
+ comp = compareIndices(primaryTypes, primaryType1, primaryType2,
+ UNKNOWN_OBJECT_LOSES);
+ if (comp != 0) {
+ return comp;
+ }
+
+ // Next, look for application/x-java-* types. Prefer unknown
+ // MIME types because if the user provides his own data flavor,
+ // it will likely be the most descriptive one.
+ comp = compareIndices(exactTypes, mimeType1, mimeType2,
+ UNKNOWN_OBJECT_WINS);
+ if (comp != 0) {
+ return comp;
+ }
+
+ // Finally, prefer the representation classes of Remote,
+ // Serializable, and InputStream, in that order.
+ comp = compareIndices(nonTextRepresentations, class1, class2,
+ UNKNOWN_OBJECT_LOSES);
+ if (comp != 0) {
+ return comp;
+ }
+ }
+
+ // The flavours are not equal but still not distinguishable.
+ // Compare String representations in alphabetical order
+ return flavor1.getMimeType().compareTo(flavor2.getMimeType());
+ }
+ }
+
+ /*
+ * Given the Map that maps objects to Integer indices and a boolean value,
+ * this Comparator imposes a direct or reverse order on set of objects.
+ * <p>
+ * If the specified boolean value is SELECT_BEST, the Comparator imposes the
+ * direct index-based order: an object A is greater than an object B if and
+ * only if the index of A is greater than the index of B. An object that
+ * doesn't have an associated index is less or equal than any other object.
+ * <p>
+ * If the specified boolean value is SELECT_WORST, the Comparator imposes the
+ * reverse index-based order: an object A is greater than an object B if and
+ * only if A is less than B with the direct index-based order.
+ */
+ private static class IndexOrderComparator implements Comparator<Long> {
+ private final Map<Long, Integer> indexMap;
+ private static final Integer FALLBACK_INDEX = Integer.MIN_VALUE;
+
+ public IndexOrderComparator(Map<Long, Integer> indexMap) {
+ this.indexMap = indexMap;
+ }
+
+ public int compare(Long obj1, Long obj2) {
+ return compareIndices(indexMap, obj1, obj2, FALLBACK_INDEX);
+ }
+ }
+
+ private static class TextFlavorComparator extends DataFlavorComparator {
+
+ static final TextFlavorComparator INSTANCE = new TextFlavorComparator();
+ /**
+ * Compares two <code>DataFlavor</code> objects. Returns a negative
+ * integer, zero, or a positive integer as the first
+ * <code>DataFlavor</code> is worse than, equal to, or better than the
+ * second.
+ * <p>
+ * <code>DataFlavor</code>s are ordered according to the rules outlined
+ * for <code>selectBestTextFlavor</code>.
+ *
+ * @param flavor1 the first <code>DataFlavor</code> to be compared
+ * @param flavor2 the second <code>DataFlavor</code> to be compared
+ * @return a negative integer, zero, or a positive integer as the first
+ * argument is worse, equal to, or better than the second
+ * @throws ClassCastException if either of the arguments is not an
+ * instance of <code>DataFlavor</code>
+ * @throws NullPointerException if either of the arguments is
+ * <code>null</code>
+ *
+ * @see java.awt.datatransfer.DataFlavor#selectBestTextFlavor
+ */
+ public int compare(DataFlavor flavor1, DataFlavor flavor2) {
+ if (flavor1.isFlavorTextType()) {
+ if (flavor2.isFlavorTextType()) {
+ return super.compare(flavor1, flavor2);
+ } else {
+ return 1;
+ }
+ } else if (flavor2.isFlavorTextType()) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ /**
+ * A fallback implementation of {@link sun.datatransfer.DesktopDatatransferService}
+ * used if there is no desktop.
+ */
+ private static final class DefaultDesktopDatatransferService implements DesktopDatatransferService {
+ static final DesktopDatatransferService INSTANCE = getDesktopService();
+
+ private static DesktopDatatransferService getDesktopService() {
+ ServiceLoader<DesktopDatatransferService> loader =
+ ServiceLoader.load(DesktopDatatransferService.class, null);
+ Iterator<DesktopDatatransferService> iterator = loader.iterator();
+ if (iterator.hasNext()) {
+ return iterator.next();
+ } else {
+ return new DefaultDesktopDatatransferService();
+ }
+ }
+
+ /**
+ * System singleton FlavorTable.
+ * Only used if there is no desktop
+ * to provide an appropriate FlavorMap.
+ */
+ private volatile FlavorMap flavorMap;
+
+ @Override
+ public void invokeOnEventThread(Runnable r) {
+ r.run();
+ }
+
+ @Override
+ public String getDefaultUnicodeEncoding() {
+ return StandardCharsets.UTF_8.name();
+ }
+
+ @Override
+ public FlavorMap getFlavorMap(Supplier<FlavorMap> supplier) {
+ FlavorMap map = flavorMap;
+ if (map == null) {
+ synchronized (this) {
+ map = flavorMap;
+ if (map == null) {
+ flavorMap = map = supplier.get();
+ }
+ }
+ }
+ return map;
+ }
+
+ @Override
+ public boolean isDesktopPresent() {
+ return false;
+ }
+
+ @Override
+ public LinkedHashSet<DataFlavor> getPlatformMappingsForNative(String nat) {
+ return new LinkedHashSet<>();
+ }
+
+ @Override
+ public LinkedHashSet<String> getPlatformMappingsForFlavor(DataFlavor df) {
+ return new LinkedHashSet<>();
+ }
+
+ @Override
+ public void registerTextFlavorProperties(String nat, String charset,
+ String eoln, String terminators) {
+ // Not needed if desktop module is absent
+ }
+ }
+
+ public static DesktopDatatransferService getDesktopService() {
+ return DefaultDesktopDatatransferService.INSTANCE;
+ }
+
+ /**
+ * A class that provides access to java.rmi.Remote and java.rmi.MarshalledObject
+ * without creating a static dependency.
+ */
+ public static class RMI {
+ private static final Class<?> remoteClass = getClass("java.rmi.Remote");
+ private static final Class<?> marshallObjectClass = getClass("java.rmi.MarshalledObject");
+ private static final Constructor<?> marshallCtor = getConstructor(marshallObjectClass, Object.class);
+ private static final Method marshallGet = getMethod(marshallObjectClass, "get");
+
+ private static Class<?> getClass(String name) {
+ try {
+ return Class.forName(name, true, null);
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ }
+
+ private static Constructor<?> getConstructor(Class<?> c, Class<?>... types) {
+ try {
+ return (c == null) ? null : c.getDeclaredConstructor(types);
+ } catch (NoSuchMethodException x) {
+ throw new AssertionError(x);
+ }
+ }
+
+ private static Method getMethod(Class<?> c, String name, Class<?>... types) {
+ try {
+ return (c == null) ? null : c.getMethod(name, types);
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ /**
+ * Returns java.rmi.Remote.class if RMI is present; otherwise {@code null}.
+ */
+ static Class<?> remoteClass() {
+ return remoteClass;
+ }
+
+ /**
+ * Returns {@code true} if the given class is java.rmi.Remote.
+ */
+ public static boolean isRemote(Class<?> c) {
+ return (remoteClass != null) && remoteClass.isAssignableFrom(c);
+ }
+
+ /**
+ * Returns a new MarshalledObject containing the serialized representation
+ * of the given object.
+ */
+ public static Object newMarshalledObject(Object obj) throws IOException {
+ try {
+ return marshallCtor == null ? null : marshallCtor.newInstance(obj);
+ } catch (InstantiationException | IllegalAccessException x) {
+ throw new AssertionError(x);
+ } catch (InvocationTargetException x) {
+ Throwable cause = x.getCause();
+ if (cause instanceof IOException)
+ throw (IOException) cause;
+ throw new AssertionError(x);
+ }
+ }
+
+ /**
+ * Returns a new copy of the contained marshalled object.
+ */
+ public static Object getMarshalledObject(Object obj)
+ throws IOException, ClassNotFoundException {
+ try {
+ return marshallGet == null ? null : marshallGet.invoke(obj);
+ } catch (IllegalAccessException x) {
+ throw new AssertionError(x);
+ } catch (InvocationTargetException x) {
+ Throwable cause = x.getCause();
+ if (cause instanceof IOException)
+ throw (IOException) cause;
+ if (cause instanceof ClassNotFoundException)
+ throw (ClassNotFoundException) cause;
+ throw new AssertionError(x);
+ }
+ }
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/datatransfer/DesktopDatatransferService.java Mon Jul 28 19:02:56 2014 +0400
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.datatransfer;
+
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.FlavorMap;
+import java.util.LinkedHashSet;
+import java.util.function.Supplier;
+
+/**
+ * Contains services which desktop provides to the datatransfer system
+ * to enrich it's functionality
+ *
+ * @author Petr Pchelko
+ * @since 1.9
+ */
+public interface DesktopDatatransferService {
+
+ /**
+ * If desktop is present - invokes a {@code Runnable} on
+ * the event dispatch thread. Otherwise invokes a {@code run()}
+ * method directly.
+ *
+ * @param r a {@code Runnable} to invoke
+ */
+ void invokeOnEventThread(Runnable r);
+
+ /**
+ * Get a platform-dependent default unicode encoding to use in
+ * datatransfer system.
+ *
+ * @return default unicode encoding
+ */
+ String getDefaultUnicodeEncoding();
+
+ /**
+ * Takes an appropriate {@code FlavorMap} from the desktop.
+ * If no appropriate table is found - uses a provided supplier to
+ * instantiate a table. If the desktop is absent - creates and returns
+ * a system singleton.
+ *
+ * @param supplier a constructor that should be used to create a new instance of
+ * the {@code FlavorMap}
+ * @return a {@code FlavorMap}
+ */
+ FlavorMap getFlavorMap(Supplier<FlavorMap> supplier);
+
+ /**
+ * Checks if desktop is present
+ *
+ * @return {@code true} is the desktop is present
+ */
+ boolean isDesktopPresent();
+
+ /**
+ * Returns platform-specific mappings for the specified native format.
+ * If there are no platform-specific mappings for this native, the method
+ * returns an empty {@code Set}
+ *
+ * @param nat a native format to return flavors for
+ * @return set of platform-specific mappings for a native format
+ */
+ LinkedHashSet<DataFlavor> getPlatformMappingsForNative(String nat);
+
+ /**
+ * Returns platform-specific mappings for the specified flavor.
+ * If there are no platform-specific mappings for this flavor, the method
+ * returns an empty {@code Set}
+ *
+ * @param df {@code DataFlavor} to return mappings for
+ * @return set of platform-specific mappings for a {@code DataFlavor}
+ */
+ LinkedHashSet<String> getPlatformMappingsForFlavor(DataFlavor df);
+
+ /**
+ * This method is called for text flavor mappings established while parsing
+ * the default flavor mappings file. It stores the "eoln" and "terminators"
+ * parameters which are not officially part of the MIME type. They are
+ * MIME parameters specific to the flavormap.properties file format.
+ */
+ void registerTextFlavorProperties(String nat, String charset,
+ String eoln, String terminators);
+}
--- a/jdk/src/solaris/classes/sun/awt/X11/XDataTransferer.java Mon Jul 28 18:43:09 2014 +0400
+++ b/jdk/src/solaris/classes/sun/awt/X11/XDataTransferer.java Mon Jul 28 19:02:56 2014 +0400
@@ -29,7 +29,6 @@
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
-import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
@@ -46,7 +45,6 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
-import java.util.List;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
@@ -54,11 +52,11 @@
import javax.imageio.ImageWriter;
import javax.imageio.spi.ImageWriterSpi;
+import sun.datatransfer.DataFlavorUtil;
import sun.awt.datatransfer.DataTransferer;
import sun.awt.datatransfer.ToolkitThreadBlockedHandler;
import java.io.ByteArrayOutputStream;
-import java.util.stream.Stream;
/**
* Platform-specific support for the data transfer subsystem.
@@ -87,27 +85,30 @@
return transferer;
}
+ @Override
public String getDefaultUnicodeEncoding() {
return "iso-10646-ucs-2";
}
+ @Override
public boolean isLocaleDependentTextFormat(long format) {
return false;
}
+ @Override
public boolean isTextFormat(long format) {
return super.isTextFormat(format)
|| isMimeFormat(format, "text");
}
+ @Override
protected String getCharsetForTextFormat(Long lFormat) {
- long format = lFormat.longValue();
- if (isMimeFormat(format, "text")) {
- String nat = getNativeForFormat(format);
+ if (isMimeFormat(lFormat, "text")) {
+ String nat = getNativeForFormat(lFormat);
DataFlavor df = new DataFlavor(nat, null);
// Ignore the charset parameter of the MIME type if the subtype
// doesn't support charset.
- if (!DataTransferer.doesSubtypeSupportCharset(df)) {
+ if (!DataFlavorUtil.doesSubtypeSupportCharset(df)) {
return null;
}
String charset = df.getParameter("charset");
@@ -118,6 +119,7 @@
return super.getCharsetForTextFormat(lFormat);
}
+ @Override
protected boolean isURIListFormat(long format) {
String nat = getNativeForFormat(format);
if (nat == null) {
@@ -134,24 +136,27 @@
return false;
}
+ @Override
public boolean isFileFormat(long format) {
return format == FILE_NAME_ATOM.getAtom() ||
format == DT_NET_FILE_ATOM.getAtom();
}
+ @Override
public boolean isImageFormat(long format) {
return format == PNG_ATOM.getAtom() ||
format == JFIF_ATOM.getAtom() ||
isMimeFormat(format, "image");
}
+ @Override
protected Long getFormatForNativeAsLong(String str) {
// Just get the atom. If it has already been retrived
// once, we'll get a copy so this should be very fast.
- long atom = XAtom.get(str).getAtom();
- return Long.valueOf(atom);
+ return XAtom.get(str).getAtom();
}
+ @Override
protected String getNativeForFormat(long format) {
return getTargetNameForAtom(format);
}
@@ -167,6 +172,7 @@
return XAtom.get(atom).getName();
}
+ @Override
protected byte[] imageToPlatformBytes(Image image, long format)
throws IOException {
String mimeType = null;
@@ -196,6 +202,7 @@
}
}
+ @Override
protected ByteArrayOutputStream convertFileListToBytes(ArrayList<String> fileList)
throws IOException
{
@@ -213,6 +220,7 @@
* Translates either a byte array or an input stream which contain
* platform-specific image data in the given format into an Image.
*/
+ @Override
protected Image platformImageBytesToImage(
byte[] bytes, long format) throws IOException
{
@@ -317,8 +325,7 @@
return flavors;
}
- DataFlavor df = null;
-
+ DataFlavor df;
try {
df = new DataFlavor(nat);
} catch (Exception e) {
@@ -383,7 +390,7 @@
String baseType = df.getPrimaryType() + "/" + df.getSubType();
String mimeType = baseType;
- if (charset != null && DataTransferer.isFlavorCharsetTextType(df)) {
+ if (charset != null && DataFlavorUtil.isFlavorCharsetTextType(df)) {
mimeType += ";charset=" + charset;
}
@@ -413,14 +420,14 @@
}
}
}
- } else if (DataTransferer.isFlavorCharsetTextType(df)) {
+ } else if (DataFlavorUtil.isFlavorCharsetTextType(df)) {
// stringFlavor is semantically equivalent to the standard
// "text/plain" MIME type.
if (DataFlavor.stringFlavor.equals(df)) {
baseType = "text/plain";
}
- for (String encoding : DataTransferer.standardEncodings()) {
+ for (String encoding : DataFlavorUtil.standardEncodings()) {
if (!encoding.equals(charset)) {
natives.add(baseType + ";charset=" + encoding);
}
--- a/jdk/test/java/awt/datatransfer/SystemFlavorMap/AddFlavorTest.java Mon Jul 28 18:43:09 2014 +0400
+++ b/jdk/test/java/awt/datatransfer/SystemFlavorMap/AddFlavorTest.java Mon Jul 28 19:02:56 2014 +0400
@@ -23,11 +23,10 @@
* questions.
*/
-import sun.awt.datatransfer.DataTransferer;
-
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.SystemFlavorMap;
import java.util.*;
+import java.nio.charset.Charset;
/*
* @test
@@ -70,17 +69,8 @@
// construct a unique String native
key = key.concat("TEST");
- for (DataFlavor element : flavorsSet)
+ for (DataFlavor element : flavorsSet) {
flavorMap.addFlavorForUnencodedNative(key, element);
-
- // This part is valid only for X-based graphical systems
- if (!System.getProperty("os.name").startsWith("Win") && !System.getProperty("os.name").startsWith("Mac") ) {
- if (key.contains("/")) {
- Set<DataFlavor> fls = DataTransferer.getInstance().getPlatformMappingsForNative(key);
- flavorsSet.addAll(fls);
- if (!fls.isEmpty() && key.startsWith("text/"))
- flavorsSet.addAll(convertMimeTypeToDataFlavors(key));
- }
}
hashVerify.put(key, new Vector(flavorsSet));
}
@@ -103,12 +93,10 @@
}
void compareFlavors(List<DataFlavor> flavors1, List<DataFlavor> flavors2, String key){
- DataTransferer.DataFlavorComparator comparator = new DataTransferer.DataFlavorComparator();
for (DataFlavor flavor1 : flavors1) {
boolean result = false;
for (DataFlavor flavor2 : flavors2) {
- if (comparator.compare(flavor1, flavor2) == 0)
- result = true;
+ if (flavor1.equals(flavor2)) result = true;
}
if (!result)
throw new RuntimeException("\n*** Error in verifyNewMappings()" +
@@ -125,7 +113,7 @@
Set<DataFlavor> convertMimeTypeToDataFlavors(String baseType) throws Exception {
Set<DataFlavor> result = new LinkedHashSet<>();
- for (String charset : DataTransferer.standardEncodings()) {
+ for (String charset : getStandardEncodings()) {
for (String txtClass : new String[]{"java.io.InputStream", "java.nio.ByteBuffer", "\"[B\""}) {
String mimeType = baseType + ";charset=" + charset + ";class=" + txtClass;
@@ -142,5 +130,17 @@
}
return result;
}
+
+ Set<String> getStandardEncodings() {
+ Set<String> tempSet = new HashSet<>();
+ tempSet.add("US-ASCII");
+ tempSet.add("ISO-8859-1");
+ tempSet.add("UTF-8");
+ tempSet.add("UTF-16BE");
+ tempSet.add("UTF-16LE");
+ tempSet.add("UTF-16");
+ tempSet.add(Charset.defaultCharset().name());
+ return tempSet;
+ }
}