/* * Copyright (c) 1997, 2012, 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 java.awt.datatransfer; import java.awt.Toolkit; import java.lang.ref.SoftReference; import java.io.BufferedReader; import java.io.File; import java.io.InputStreamReader; import java.io.IOException; import java.net.URL; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; 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), * which correspond to platform-independent MIME types. This mapping is used * by the data transfer subsystem to transfer data between Java and native * applications, and between Java applications in separate VMs. *
*
* @since 1.2
*/
public final class SystemFlavorMap implements FlavorMap, FlavorTable {
/**
* Constant prefix used to tag Java types converted to native platform
* type.
*/
private static String JavaMIME = "JAVA_DATAFLAVOR:";
private static final Object FLAVOR_MAP_KEY = new Object();
/**
* Copied from java.util.Properties.
*/
private static final String keyValueSeparators = "=: \t\r\n\f";
private static final String strictKeyValueSeparators = "=:";
private static final String whiteSpaceChars = " \t\r\n\f";
/**
* The list of valid, decoded text flavor representation classes, in order
* from best to worst.
*/
private static final String[] UNICODE_TEXT_CLASSES = {
"java.io.Reader", "java.lang.String", "java.nio.CharBuffer", "\"[C\""
};
/**
* The list of valid, encoded text flavor representation classes, in order
* from best to worst.
*/
private static final String[] ENCODED_TEXT_CLASSES = {
"java.io.InputStream", "java.nio.ByteBuffer", "\"[B\""
};
/**
* A String representing text/plain MIME type.
*/
private static final String TEXT_PLAIN_BASE_TYPE = "text/plain";
/**
* A String representing text/html MIME type.
*/
private static final String HTML_TEXT_BASE_TYPE = "text/html";
/**
* This constant is passed to flavorToNativeLookup() to indicate that a
* a native should be synthesized, stored, and returned by encoding the
* DataFlavor's MIME type in case if the DataFlavor is not found in
* 'flavorToNative' map.
*/
private static final boolean SYNTHESIZE_IF_NOT_FOUND = true;
/**
* Maps native Strings to Lists of DataFlavors (or base type Strings for
* text DataFlavors).
* Do not use the field directly, use getNativeToFlavor() instead.
*/
private final Map
* If the specified
* If the specified native is previously unknown to the data transfer
* subsystem, and that native has been properly encoded, then invoking this
* method will establish a mapping in both directions between the specified
* native and a
* If the specified native is not a properly encoded native and the
* mappings for this native have not been altered with
*
* If a specified
* If a specified native is previously unknown to the data transfer
* subsystem, and that native has been properly encoded, then invoking this
* method will establish a mapping in both directions between the specified
* native and a
* If the array contains several elements that reference equal
*
* It is recommended that client code not reset mappings established by the
* data transfer subsystem. This method should only be used for
* application-level mappings.
*
* @param flav the
* If the array contains several elements that reference equal
*
* It is recommended that client code not reset mappings established by the
* data transfer subsystem. This method should only be used for
* application-level mappings.
*
* @param nat the
* The reference implementation of this method returns the specified MIME
* type
* The reference implementation of this method returns the MIME type
* List of String natives to which the
* specified DataFlavor can be translated by the data transfer
* subsystem. The List will be sorted from best native to
* worst. That is, the first native will best reflect data in the specified
* flavor to the underlying native platform.
* DataFlavor is previously unknown to the
* data transfer subsystem and the data transfer subsystem is unable to
* translate this DataFlavor to any existing native, then
* invoking this method will establish a
* mapping in both directions between the specified DataFlavor
* and an encoded version of its MIME type as its native.
*
* @param flav the DataFlavor whose corresponding natives
* should be returned. If null is specified, all
* natives currently known to the data transfer subsystem are
* returned in a non-deterministic order.
* @return a java.util.List of java.lang.String
* objects which are platform-specific representations of platform-
* specific data formats
*
* @see #encodeDataFlavor
* @since 1.4
*/
public synchronized List> ref = getNativesForFlavorCache.get(flav);
if (ref != null) {
retval = ref.get();
if (retval != null) {
// Create a copy, because client code can modify the returned
// list.
return new ArrayList<>(retval);
}
}
if (flav == null) {
retval = new ArrayList<>(getNativeToFlavor().keySet());
} else if (disabledMappingGenerationKeys.contains(flav)) {
// In this case we shouldn't synthesize a native for this flavor,
// since its mappings were explicitly specified.
retval = flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND);
} else if (DataTransferer.isFlavorCharsetTextType(flav)) {
// For text/* flavors, flavor-to-native mappings specified in
// flavormap.properties are stored per flavor's base type.
if ("text".equals(flav.getPrimaryType())) {
retval = getAllNativesForType(flav.mimeType.getBaseType());
if (retval != null) {
// To prevent the List stored in the map from modification.
retval = new ArrayList(retval);
}
}
// Also include text/plain natives, but don't duplicate Strings
List
List of DataFlavors to which the
* specified String native can be translated by the data
* transfer subsystem. The List will be sorted from best
* DataFlavor to worst. That is, the first
* DataFlavor will best reflect data in the specified
* native to a Java application.
* DataFlavor whose MIME type is a decoded
* version of the native.
* setFlavorsForNative, then the contents of the
* List is platform dependent, but null
* cannot be returned.
*
* @param nat the native whose corresponding DataFlavors
* should be returned. If null is specified, all
* DataFlavors currently known to the data transfer
* subsystem are returned in a non-deterministic order.
* @return a java.util.List of DataFlavor
* objects into which platform-specific data in the specified,
* platform-specific native can be translated
*
* @see #encodeJavaMIMEType
* @since 1.4
*/
public synchronized List> ref = getFlavorsForNativeCache.get(nat);
if (ref != null) {
List
Map of the specified DataFlavors to
* their most preferred String native. Each native value will
* be the same as the first native in the List returned by
* getNativesForFlavor for the specified flavor.
* DataFlavor is previously unknown to the
* data transfer subsystem, then invoking this method will establish a
* mapping in both directions between the specified DataFlavor
* and an encoded version of its MIME type as its native.
*
* @param flavors an array of DataFlavors which will be the
* key set of the returned Map. If null is
* specified, a mapping of all DataFlavors known to the
* data transfer subsystem to their most preferred
* String natives will be returned.
* @return a java.util.Map of DataFlavors to
* String natives
*
* @see #getNativesForFlavor
* @see #encodeDataFlavor
*/
public synchronized MapMap of the specified String natives
* to their most preferred DataFlavor. Each
* DataFlavor value will be the same as the first
* DataFlavor in the List returned by
* getFlavorsForNative for the specified native.
* DataFlavor whose MIME type is a decoded
* version of the native.
*
* @param natives an array of Strings which will be the
* key set of the returned Map. If null is
* specified, a mapping of all supported String natives
* to their most preferred DataFlavors will be
* returned.
* @return a java.util.Map of String natives to
* DataFlavors
*
* @see #getFlavorsForNative
* @see #encodeJavaMIMEType
*/
public synchronized MapDataFlavor (and all
* DataFlavors equal to the specified DataFlavor)
* to the specified String native.
* Unlike getNativesForFlavor, the mapping will only be
* established in one direction, and the native will not be encoded. To
* establish a two-way mapping, call
* addFlavorForUnencodedNative as well. The new mapping will
* be of lower priority than any existing mapping.
* This method has no effect if a mapping from the specified or equal
* DataFlavor to the specified String native
* already exists.
*
* @param flav the DataFlavor key for the mapping
* @param nat the String native value for the mapping
* @throws NullPointerException if flav or nat is null
*
* @see #addFlavorForUnencodedNative
* @since 1.4
*/
public synchronized void addUnencodedNativeForFlavor(DataFlavor flav,
String nat) {
if (flav == null || nat == null) {
throw new NullPointerException("null arguments not permitted");
}
ListDataFlavor
* and all DataFlavors equal to the specified
* DataFlavor, and creates new mappings to the
* specified String natives.
* Unlike getNativesForFlavor, the mappings will only be
* established in one direction, and the natives will not be encoded. To
* establish two-way mappings, call setFlavorsForNative
* as well. The first native in the array will represent the highest
* priority mapping. Subsequent natives will represent mappings of
* decreasing priority.
* String natives, this method will establish new mappings
* for the first of those elements and ignore the rest of them.
* DataFlavor key for the mappings
* @param natives the String native values for the mappings
* @throws NullPointerException if flav or natives is null
* or if natives contains null elements
*
* @see #setFlavorsForNative
* @since 1.4
*/
public synchronized void setNativesForFlavor(DataFlavor flav,
String[] natives) {
if (flav == null || natives == null) {
throw new NullPointerException("null arguments not permitted");
}
getFlavorToNative().remove(flav);
for (String aNative : natives) {
addUnencodedNativeForFlavor(flav, aNative);
}
disabledMappingGenerationKeys.add(flav);
// Clear the cache to handle the case of empty natives.
getNativesForFlavorCache.remove(flav);
getNativesForFlavorCache.remove(null);
}
/**
* Adds a mapping from a single String native to a single
* DataFlavor. Unlike getFlavorsForNative, the
* mapping will only be established in one direction, and the native will
* not be encoded. To establish a two-way mapping, call
* addUnencodedNativeForFlavor as well. The new mapping will
* be of lower priority than any existing mapping.
* This method has no effect if a mapping from the specified
* String native to the specified or equal
* DataFlavor already exists.
*
* @param nat the String native key for the mapping
* @param flav the DataFlavor value for the mapping
* @throws NullPointerException if nat or flav is null
*
* @see #addUnencodedNativeForFlavor
* @since 1.4
*/
public synchronized void addFlavorForUnencodedNative(String nat,
DataFlavor flav) {
if (nat == null || flav == null) {
throw new NullPointerException("null arguments not permitted");
}
ListString
* native, and creates new mappings to the specified
* DataFlavors. Unlike getFlavorsForNative, the
* mappings will only be established in one direction, and the natives need
* not be encoded. To establish two-way mappings, call
* setNativesForFlavor as well. The first
* DataFlavor in the array will represent the highest priority
* mapping. Subsequent DataFlavors will represent mappings of
* decreasing priority.
* DataFlavors, this method will establish new mappings
* for the first of those elements and ignore the rest of them.
* String native key for the mappings
* @param flavors the DataFlavor values for the mappings
* @throws NullPointerException if nat or flavors is null
* or if flavors contains null elements
*
* @see #setNativesForFlavor
* @since 1.4
*/
public synchronized void setFlavorsForNative(String nat,
DataFlavor[] flavors) {
if (nat == null || flavors == null) {
throw new NullPointerException("null arguments not permitted");
}
getNativeToFlavor().remove(nat);
for (DataFlavor flavor : flavors) {
addFlavorForUnencodedNative(nat, flavor);
}
disabledMappingGenerationKeys.add(nat);
// Clear the cache to handle the case of empty flavors.
getFlavorsForNativeCache.remove(nat);
getFlavorsForNativeCache.remove(null);
}
/**
* Encodes a MIME type for use as a String native. The format
* of an encoded representation of a MIME type is implementation-dependent.
* The only restrictions are:
*
*
* null if and only if the
* MIME type String is null.null MIME type
* Strings are equal if and only if these Strings
* are equal according to String.equals(Object).String prefixed with JAVA_DATAFLAVOR:.
*
* @param mimeType the MIME type to encode
* @return the encoded String, or null if
* mimeType is null
*/
public static String encodeJavaMIMEType(String mimeType) {
return (mimeType != null)
? JavaMIME + mimeType
: null;
}
/**
* Encodes a DataFlavor for use as a String
* native. The format of an encoded DataFlavor is
* implementation-dependent. The only restrictions are:
*
*
* null if and only if the
* specified DataFlavor is null or its MIME type
* String is null.null
* DataFlavors with non-null MIME type
* Strings are equal if and only if the MIME type
* Strings of these DataFlavors are equal
* according to String.equals(Object).String of the specified DataFlavor prefixed
* with JAVA_DATAFLAVOR:.
*
* @param flav the DataFlavor to encode
* @return the encoded String, or null if
* flav is null or has a null MIME type
*/
public static String encodeDataFlavor(DataFlavor flav) {
return (flav != null)
? SystemFlavorMap.encodeJavaMIMEType(flav.getMimeType())
: null;
}
/**
* Returns whether the specified String is an encoded Java
* MIME type.
*
* @param str the String to test
* @return true if the String is encoded;
* false otherwise
*/
public static boolean isJavaMIMEType(String str) {
return (str != null && str.startsWith(JavaMIME, 0));
}
/**
* Decodes a String native for use as a Java MIME type.
*
* @param nat the String to decode
* @return the decoded Java MIME type, or null if nat is not
* an encoded String native
*/
public static String decodeJavaMIMEType(String nat) {
return (isJavaMIMEType(nat))
? nat.substring(JavaMIME.length(), nat.length()).trim()
: null;
}
/**
* Decodes a String native for use as a
* DataFlavor.
*
* @param nat the String to decode
* @return the decoded DataFlavor, or null if
* nat is not an encoded String native
*/
public static DataFlavor decodeDataFlavor(String nat)
throws ClassNotFoundException
{
String retval_str = SystemFlavorMap.decodeJavaMIMEType(nat);
return (retval_str != null)
? new DataFlavor(retval_str)
: null;
}
private List