/src/share/classes/sun/awt/datatransfer/DataTransferer.java
Java | 3123 lines | 1956 code | 414 blank | 753 comment | 410 complexity | bd5c14adea361228b2648586488d10aa MD5 | raw file
Possible License(s): LGPL-3.0, GPL-2.0, BSD-3-Clause-No-Nuclear-License-2014, BSD-3-Clause
Large files files are truncated, but you can click here to view the full file
- /*
- * Copyright (c) 2000, 2013, 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 java.awt.AWTError;
- import java.awt.EventQueue;
- import java.awt.Image;
- import java.awt.Graphics;
- import java.awt.datatransfer.DataFlavor;
- import java.awt.datatransfer.FlavorMap;
- import java.awt.datatransfer.FlavorTable;
- import java.awt.datatransfer.Transferable;
- import java.awt.datatransfer.UnsupportedFlavorException;
- import java.io.BufferedReader;
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.File;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- import java.io.Reader;
- import java.io.SequenceInputStream;
- import java.io.StringReader;
- import java.net.URI;
- import java.net.URISyntaxException;
- import java.nio.ByteBuffer;
- import java.nio.CharBuffer;
- import java.nio.charset.Charset;
- import java.nio.charset.CharsetEncoder;
- import java.nio.charset.IllegalCharsetNameException;
- 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;
- import java.security.PrivilegedAction;
- import java.security.PrivilegedActionException;
- import java.security.PrivilegedExceptionAction;
- import java.security.ProtectionDomain;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.Comparator;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.SortedMap;
- import java.util.SortedSet;
- import java.util.Set;
- import java.util.Stack;
- import java.util.TreeMap;
- import java.util.TreeSet;
- import sun.util.logging.PlatformLogger;
- import sun.awt.AppContext;
- import sun.awt.SunToolkit;
- import java.awt.image.BufferedImage;
- import java.awt.image.ImageObserver;
- import java.awt.image.RenderedImage;
- import java.awt.image.WritableRaster;
- import java.awt.image.ColorModel;
- import javax.imageio.ImageIO;
- import javax.imageio.ImageReader;
- import javax.imageio.ImageReadParam;
- import javax.imageio.ImageWriter;
- import javax.imageio.ImageTypeSpecifier;
- import javax.imageio.spi.ImageWriterSpi;
- import javax.imageio.stream.ImageInputStream;
- import javax.imageio.stream.ImageOutputStream;
- import sun.awt.image.ImageRepresentation;
- import sun.awt.image.ToolkitImage;
- import java.io.FilePermission;
- /**
- * Provides a set of functions to be shared among the DataFlavor class and
- * platform-specific data transfer implementations.
- *
- * The concept of "flavors" and "natives" is extended to include "formats",
- * which are the numeric values Win32 and X11 use to express particular data
- * types. Like FlavorMap, which provides getNativesForFlavors(DataFlavor[]) and
- * getFlavorsForNatives(String[]) functions, DataTransferer provides a set
- * of getFormatsFor(Transferable|Flavor|Flavors) and
- * getFlavorsFor(Format|Formats) functions.
- *
- * Also provided are functions for translating a Transferable into a byte
- * array, given a source DataFlavor and a target format, and for translating
- * a byte array or InputStream into an Object, given a source format and
- * a target DataFlavor.
- *
- * @author David Mendenhall
- * @author Danila Sinopalnikov
- *
- * @since 1.3.1
- */
- public abstract class DataTransferer {
- /**
- * Cached value of Class.forName("[C");
- */
- public static final Class charArrayClass;
- /**
- * Cached value of Class.forName("[B");
- */
- public static final Class byteArrayClass;
- /**
- * The <code>DataFlavor</code> representing plain text with Unicode
- * encoding, where:
- * <pre>
- * representationClass = java.lang.String
- * mimeType = "text/plain; charset=Unicode"
- * </pre>
- */
- public static final DataFlavor plainTextStringFlavor;
- /**
- * The <code>DataFlavor</code> representing a Java text encoding String
- * encoded in UTF-8, where
- * <pre>
- * representationClass = [B
- * mimeType = "application/x-java-text-encoding"
- * </pre>
- */
- 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 comparator =
- new CharsetComparator(IndexedComparator.SELECT_WORST);
- final SortedSet<String> tempSet = new TreeSet<String>(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(getDefaultTextCharset());
- 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 textMIMESubtypeCharsetSupport;
- /**
- * Cache of the platform default encoding as specified in the
- * "file.encoding" system property.
- */
- private static String defaultEncoding;
- /**
- * A collection of all natives listed in flavormap.properties with
- * a primary MIME type of "text".
- */
- private static final Set textNatives =
- Collections.synchronizedSet(new HashSet());
- /**
- * The native encodings/charsets for the Set of textNatives.
- */
- private static final Map nativeCharsets =
- Collections.synchronizedMap(new HashMap());
- /**
- * The end-of-line markers for the Set of textNatives.
- */
- private static final Map nativeEOLNs =
- Collections.synchronizedMap(new HashMap());
- /**
- * The number of terminating NUL bytes for the Set of textNatives.
- */
- private static final Map nativeTerminators =
- Collections.synchronizedMap(new HashMap());
- /**
- * The key used to store pending data conversion requests for an AppContext.
- */
- private static final String DATA_CONVERTER_KEY = "DATA_CONVERTER_KEY";
- /**
- * The singleton DataTransferer instance. It is created during MToolkit
- * or WToolkit initialization.
- */
- private static DataTransferer transferer;
- private static final PlatformLogger dtLog = PlatformLogger.getLogger("sun.awt.datatransfer.DataTransfer");
- static {
- Class tCharArrayClass = null, tByteArrayClass = null;
- try {
- tCharArrayClass = Class.forName("[C");
- tByteArrayClass = Class.forName("[B");
- } catch (ClassNotFoundException cannotHappen) {
- }
- charArrayClass = tCharArrayClass;
- byteArrayClass = tByteArrayClass;
- DataFlavor tPlainTextStringFlavor = null;
- try {
- tPlainTextStringFlavor = new DataFlavor
- ("text/plain;charset=Unicode;class=java.lang.String");
- } catch (ClassNotFoundException cannotHappen) {
- }
- plainTextStringFlavor = tPlainTextStringFlavor;
- DataFlavor tJavaTextEncodingFlavor = null;
- try {
- tJavaTextEncodingFlavor = new DataFlavor
- ("application/x-java-text-encoding;class=\"[B\"");
- } catch (ClassNotFoundException cannotHappen) {
- }
- javaTextEncodingFlavor = tJavaTextEncodingFlavor;
- Map 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);
- }
- /**
- * The accessor method for the singleton DataTransferer instance. Note
- * that in a headless environment, there may be no DataTransferer instance;
- * instead, null will be returned.
- */
- public static DataTransferer getInstance() {
- synchronized (DataTransferer.class) {
- if (transferer == null) {
- final String name = SunToolkit.getDataTransfererClassName();
- if (name != null) {
- PrivilegedAction<DataTransferer> action = new PrivilegedAction<DataTransferer>()
- {
- public DataTransferer run() {
- Class cls = null;
- Method method = null;
- DataTransferer ret = null;
- try {
- cls = Class.forName(name);
- } catch (ClassNotFoundException e) {
- ClassLoader cl = ClassLoader.
- getSystemClassLoader();
- if (cl != null) {
- try {
- cls = cl.loadClass(name);
- } catch (ClassNotFoundException ee) {
- ee.printStackTrace();
- throw new AWTError("DataTransferer not found: " + name);
- }
- }
- }
- if (cls != null) {
- try {
- method = cls.getDeclaredMethod("getInstanceImpl");
- method.setAccessible(true);
- } catch (NoSuchMethodException e) {
- e.printStackTrace();
- throw new AWTError("Cannot instantiate DataTransferer: " + name);
- } catch (SecurityException e) {
- e.printStackTrace();
- throw new AWTError("Access is denied for DataTransferer: " + name);
- }
- }
- if (method != null) {
- try {
- ret = (DataTransferer) method.invoke(null);
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- throw new AWTError("Cannot instantiate DataTransferer: " + name);
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- throw new AWTError("Cannot access DataTransferer: " + name);
- }
- }
- return ret;
- }
- };
- transferer = AccessController.doPrivileged(action);
- }
- }
- }
- return transferer;
- }
- /**
- * 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 : getDefaultTextCharset();
- }
- /**
- * Returns the platform's default character encoding.
- */
- public static String getDefaultTextCharset() {
- if (defaultEncoding != null) {
- return defaultEncoding;
- }
- return defaultEncoding = 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;
- }
- Object support = textMIMESubtypeCharsetSupport.get(subType);
- if (support != null) {
- return (support == Boolean.TRUE);
- }
- boolean ret_val = (flavor.getParameter("charset") != null);
- textMIMESubtypeCharsetSupport.put
- (subType, (ret_val) ? Boolean.TRUE : Boolean.FALSE);
- return ret_val;
- }
- public static boolean doesSubtypeSupportCharset(String subType,
- String charset)
- {
- Object support = textMIMESubtypeCharsetSupport.get(subType);
- if (support != null) {
- return (support == Boolean.TRUE);
- }
- boolean ret_val = (charset != null);
- textMIMESubtypeCharsetSupport.put
- (subType, (ret_val) ? Boolean.TRUE : Boolean.FALSE);
- 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() ||
- DataTransferer.charArrayClass.equals(rep_class))
- {
- return true;
- }
- if (!(flavor.isRepresentationClassInputStream() ||
- flavor.isRepresentationClassByteBuffer() ||
- DataTransferer.byteArrayClass.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() ||
- DataTransferer.byteArrayClass.
- equals(flavor.getRepresentationClass()));
- }
- /**
- * Determines whether this JRE can both encode and decode text in the
- * specified encoding.
- */
- public 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) {
- if (map instanceof FlavorTable) {
- return (FlavorTable)map;
- }
- return new FlavorTable() {
- public Map getNativesForFlavors(DataFlavor[] flavors) {
- return map.getNativesForFlavors(flavors);
- }
- public Map getFlavorsForNatives(String[] natives) {
- return map.getFlavorsForNatives(natives);
- }
- public List getNativesForFlavor(DataFlavor flav) {
- Map natives =
- getNativesForFlavors(new DataFlavor[] { flav } );
- String nat = (String)natives.get(flav);
- if (nat != null) {
- List list = new ArrayList(1);
- list.add(nat);
- return list;
- } else {
- return Collections.EMPTY_LIST;
- }
- }
- public List getFlavorsForNative(String nat) {
- Map flavors =
- getFlavorsForNatives(new String[] { nat } );
- DataFlavor flavor = (DataFlavor)flavors.get(nat);
- if (flavor != null) {
- List list = new ArrayList(1);
- list.add(flavor);
- return list;
- } else {
- return Collections.EMPTY_LIST;
- }
- }
- };
- }
- /**
- * Returns the default Unicode encoding for the platform. The encoding
- * need not be canonical. This method is only used by the archaic function
- * DataFlavor.getTextPlainUnicodeFlavor().
- */
- public abstract String getDefaultUnicodeEncoding();
- /**
- * This method is called for text flavor mappings established while parsing
- * the flavormap.properties 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.
- */
- public void registerTextFlavorProperties(String nat, String charset,
- String eoln, String terminators) {
- Long format = getFormatForNativeAsLong(nat);
- textNatives.add(format);
- nativeCharsets.put(format, (charset != null && charset.length() != 0)
- ? charset : getDefaultTextCharset());
- if (eoln != null && eoln.length() != 0 && !eoln.equals("\n")) {
- nativeEOLNs.put(format, eoln);
- }
- if (terminators != null && terminators.length() != 0) {
- Integer iTerminators = Integer.valueOf(terminators);
- if (iTerminators.intValue() > 0) {
- nativeTerminators.put(format, iTerminators);
- }
- }
- }
- /**
- * Determines whether the native corresponding to the specified long format
- * was listed in the flavormap.properties file.
- */
- protected boolean isTextFormat(long format) {
- return textNatives.contains(Long.valueOf(format));
- }
- protected String getCharsetForTextFormat(Long lFormat) {
- return (String)nativeCharsets.get(lFormat);
- }
- /**
- * Specifies whether text imported from the native system in the specified
- * format is locale-dependent. If so, when decoding such text,
- * 'nativeCharsets' should be ignored, and instead, the Transferable should
- * be queried for its javaTextEncodingFlavor data for the correct encoding.
- */
- public abstract boolean isLocaleDependentTextFormat(long format);
- /**
- * Determines whether the DataFlavor corresponding to the specified long
- * format is DataFlavor.javaFileListFlavor.
- */
- public abstract boolean isFileFormat(long format);
- /**
- * Determines whether the DataFlavor corresponding to the specified long
- * format is DataFlavor.imageFlavor.
- */
- public abstract boolean isImageFormat(long format);
- /**
- * Determines whether the format is a URI list we can convert to
- * a DataFlavor.javaFileListFlavor.
- */
- protected boolean isURIListFormat(long format) {
- return false;
- }
- /**
- * Returns a Map whose keys are all of the possible formats into which the
- * Transferable's transfer data flavors can be translated. The value of
- * each key is the DataFlavor in which the Transferable's data should be
- * requested when converting to the format.
- * <p>
- * The map keys are sorted according to the native formats preference
- * order.
- */
- public SortedMap<Long,DataFlavor> getFormatsForTransferable(
- Transferable contents, FlavorTable map)
- {
- DataFlavor[] flavors = contents.getTransferDataFlavors();
- if (flavors == null) {
- return new TreeMap();
- }
- return getFormatsForFlavors(flavors, map);
- }
- /**
- * Returns a Map whose keys are all of the possible formats into which data
- * in the specified DataFlavor can be translated. The value of each key
- * is the DataFlavor in which a Transferable's data should be requested
- * when converting to the format.
- * <p>
- * The map keys are sorted according to the native formats preference
- * order.
- */
- public SortedMap getFormatsForFlavor(DataFlavor flavor, FlavorTable map) {
- return getFormatsForFlavors(new DataFlavor[] { flavor },
- map);
- }
- /**
- * Returns a Map whose keys are all of the possible formats into which data
- * in the specified DataFlavors can be translated. The value of each key
- * is the DataFlavor in which the Transferable's data should be requested
- * when converting to the format.
- * <p>
- * The map keys are sorted according to the native formats preference
- * order.
- *
- * @param flavors the data flavors
- * @param map the FlavorTable which contains mappings between
- * DataFlavors and data formats
- * @throws NullPointerException if flavors or map is <code>null</code>
- */
- public SortedMap <Long, DataFlavor> getFormatsForFlavors(
- DataFlavor[] flavors, FlavorTable map)
- {
- Map <Long,DataFlavor> formatMap =
- new HashMap <> (flavors.length);
- Map <Long,DataFlavor> textPlainMap =
- new HashMap <> (flavors.length);
- // Maps formats to indices that will be used to sort the formats
- // according to the preference order.
- // Larger index value corresponds to the more preferable format.
- Map indexMap = new HashMap(flavors.length);
- Map textPlainIndexMap = new HashMap(flavors.length);
- int currentIndex = 0;
- // Iterate backwards so that preferred DataFlavors are used over
- // other DataFlavors. (See javadoc for
- // Transferable.getTransferDataFlavors.)
- for (int i = flavors.length - 1; i >= 0; i--) {
- DataFlavor flavor = flavors[i];
- if (flavor == null) continue;
- // Don't explicitly test for String, since it is just a special
- // case of Serializable
- if (flavor.isFlavorTextType() ||
- flavor.isFlavorJavaFileListType() ||
- DataFlavor.imageFlavor.equals(flavor) ||
- flavor.isRepresentationClassSerializable() ||
- flavor.isRepresentationClassInputStream() ||
- flavor.isRepresentationClassRemote())
- {
- List natives = map.getNativesForFlavor(flavor);
- currentIndex += natives.size();
- for (Iterator iter = natives.iterator(); iter.hasNext(); ) {
- Long lFormat =
- getFormatForNativeAsLong((String)iter.next());
- Integer index = Integer.valueOf(currentIndex--);
- formatMap.put(lFormat, flavor);
- indexMap.put(lFormat, index);
- // SystemFlavorMap.getNativesForFlavor will return
- // text/plain natives for all text/*. While this is good
- // for a single text/* flavor, we would prefer that
- // text/plain native data come from a text/plain flavor.
- if (("text".equals(flavor.getPrimaryType()) &&
- "plain".equals(flavor.getSubType())) ||
- flavor.equals(DataFlavor.stringFlavor))
- {
- textPlainMap.put(lFormat, flavor);
- textPlainIndexMap.put(lFormat, index);
- }
- }
- currentIndex += natives.size();
- }
- }
- formatMap.putAll(textPlainMap);
- indexMap.putAll(textPlainIndexMap);
- // Sort the map keys according to the formats preference order.
- Comparator comparator =
- new IndexOrderComparator(indexMap, IndexedComparator.SELECT_WORST);
- SortedMap sortedMap = new TreeMap(comparator);
- sortedMap.putAll(formatMap);
- return sortedMap;
- }
- /**
- * Reduces the Map output for the root function to an array of the
- * Map's keys.
- */
- public long[] getFormatsForTransferableAsArray(Transferable contents,
- FlavorTable map) {
- return keysToLongArray(getFormatsForTransferable(contents, map));
- }
- public long[] getFormatsForFlavorAsArray(DataFlavor flavor,
- FlavorTable map) {
- return keysToLongArray(getFormatsForFlavor(flavor, map));
- }
- public long[] getFormatsForFlavorsAsArray(DataFlavor[] flavors,
- FlavorTable map) {
- return keysToLongArray(getFormatsForFlavors(flavors, map));
- }
- /**
- * Returns a Map whose keys are all of the possible DataFlavors into which
- * data in the specified format can be translated. The value of each key
- * is the format in which the Clipboard or dropped data should be requested
- * when converting to the DataFlavor.
- */
- public Map getFlavorsForFormat(long format, FlavorTable map) {
- return getFlavorsForFormats(new long[] { format }, map);
- }
- /**
- * Returns a Map whose keys are all of the possible DataFlavors into which
- * data in the specified formats can be translated. The value of each key
- * is the format in which the Clipboard or dropped data should be requested
- * when converting to the DataFlavor.
- */
- public Map getFlavorsForFormats(long[] formats, FlavorTable map) {
- Map flavorMap = new HashMap(formats.length);
- Set mappingSet = new HashSet(formats.length);
- Set flavorSet = new HashSet(formats.length);
- // First step: build flavorSet, mappingSet and initial flavorMap
- // flavorSet - the set of all the DataFlavors into which
- // data in the specified formats can be translated;
- // mappingSet - the set of all the mappings from the specified formats
- // into any DataFlavor;
- // flavorMap - after this step, this map maps each of the DataFlavors
- // from flavorSet to any of the specified formats.
- for (int i = 0; i < formats.length; i++) {
- long format = formats[i];
- String nat = getNativeForFormat(format);
- List flavors = map.getFlavorsForNative(nat);
- for (Iterator iter = flavors.iterator(); iter.hasNext(); ) {
- DataFlavor flavor = (DataFlavor)iter.next();
- // Don't explicitly test for String, since it is just a special
- // case of Serializable
- if (flavor.isFlavorTextType() ||
- flavor.isFlavorJavaFileListType() ||
- DataFlavor.imageFlavor.equals(flavor) ||
- flavor.isRepresentationClassSerializable() ||
- flavor.isRepresentationClassInputStream() ||
- flavor.isRepresentationClassRemote())
- {
- Long lFormat = Long.valueOf(format);
- Object mapping =
- DataTransferer.createMapping(lFormat, flavor);
- flavorMap.put(flavor, lFormat);
- mappingSet.add(mapping);
- flavorSet.add(flavor);
- }
- }
- }
- // Second step: for each DataFlavor try to figure out which of the
- // specified formats is the best to translate to this flavor.
- // Then map each flavor to the best format.
- // For the given flavor, FlavorTable indicates which native will
- // best reflect data in the specified flavor to the underlying native
- // platform. We assume that this native is the best to translate
- // to this flavor.
- // Note: FlavorTable allows one-way mappings, so we can occasionally
- // map a flavor to the format for which the corresponding
- // format-to-flavor mapping doesn't exist. For this reason we have built
- // a mappingSet of all format-to-flavor mappings for the specified formats
- // and check if the format-to-flavor mapping exists for the
- // (flavor,format) pair being added.
- for (Iterator flavorIter = flavorSet.iterator();
- flavorIter.hasNext(); ) {
- DataFlavor flavor = (DataFlavor)flavorIter.next();
- List natives = map.getNativesForFlavor(flavor);
- for (Iterator nativeIter = natives.iterator();
- nativeIter.hasNext(); ) {
- Long lFormat =
- getFormatForNativeAsLong((String)nativeIter.next());
- Object mapping = DataTransferer.createMapping(lFormat, flavor);
- if (mappingSet.contains(mapping)) {
- flavorMap.put(flavor, lFormat);
- break;
- }
- }
- }
- return flavorMap;
- }
- /**
- * Returns a Set of all DataFlavors for which
- * 1) a mapping from at least one of the specified formats exists in the
- * specified map and
- * 2) the data translation for this mapping can be performed by the data
- * transfer subsystem.
- *
- * @param formats the data formats
- * @param map the FlavorTable which contains mappings between
- * DataFlavors and data formats
- * @throws NullPointerException if formats or map is <code>null</code>
- */
- public Set getFlavorsForFormatsAsSet(long[] formats, FlavorTable map) {
- Set flavorSet = new HashSet(formats.length);
- for (int i = 0; i < formats.length; i++) {
- String nat = getNativeForFormat(formats[i]);
- List flavors = map.getFlavorsForNative(nat);
- for (Iterator iter = flavors.iterator(); iter.hasNext(); ) {
- DataFlavor flavor = (DataFlavor)iter.next();
- // Don't explicitly test for String, since it is just a special
- // case of Serializable
- if (flavor.isFlavorTextType() ||
- flavor.isFlavorJavaFileListType() ||
- DataFlavor.imageFlavor.equals(flavor) ||
- flavor.isRepresentationClassSerializable() ||
- flavor.isRepresentationClassInputStream() ||
- flavor.isRepresentationClassRemote())
- {
- flavorSet.add(flavor);
- }
- }
- }
- return flavorSet;
- }
- /**
- * Returns an array of all DataFlavors for which
- * 1) a mapping from the specified format exists in the specified map and
- * 2) the data translation for this mapping can be performed by the data
- * transfer subsystem.
- * The array will be sorted according to a
- * <code>DataFlavorComparator</code> created with the specified
- * map as an argument.
- *
- * @param format the data format
- * @param map the FlavorTable which contains mappings between
- * DataFlavors and data formats
- * @throws NullPointerException if map is <code>null</code>
- */
- public DataFlavor[] getFlavorsForFormatAsArray(long format,
- FlavorTable map) {
- return getFlavorsForFormatsAsArray(new long[] { format }, map);
- }
- /**
- * Returns an array of all DataFlavors for which
- * 1) a mapping from at least one of the specified formats exists in the
- * specified map and
- * 2) the data translation for this mapping can be performed by the data
- * transfer subsystem.
- * The array will be sorted according to a
- * <code>DataFlavorComparator</code> created with the specified
- * map as an argument.
- *
- * @param formats the data formats
- * @param map the FlavorTable which contains mappings between
- * DataFlavors and data formats
- * @throws NullPointerException if formats or map is <code>null</code>
- */
- public DataFlavor[] getFlavorsForFormatsAsArray(long[] formats,
- FlavorTable map) {
- // getFlavorsForFormatsAsSet() is less expensive than
- // getFlavorsForFormats().
- return setToSortedDataFlavorArray(getFlavorsForFormatsAsSet(formats, map));
- }
- /**
- * Returns an object that represents a mapping between the specified
- * key and value. <tt>null</tt> values and the <tt>null</tt> keys are
- * permitted. The internal representation of the mapping object is
- * irrelevant. The only requrement is that the two mapping objects are equal
- * if and only if their keys are equal and their values are equal.
- * More formally, the two mapping objects are equal if and only if
- * <tt>(value1 == null ? value2 == null : value1.equals(value2))
- * && (key1 == null ? key2 == null : key1.equals(key2))</tt>.
- */
- private static Object createMapping(Object key, Object value) {
- // NOTE: Should be updated to use AbstractMap.SimpleEntry as
- // soon as it is made public.
- return Arrays.asList(new Object[] { key, value });
- }
- /**
- * Looks-up or registers the String native with the native data transfer
- * system and returns a long format corresponding to that native.
- */
- protected abstract Long getFormatForNativeAsLong(String str);
- /**
- * Looks-up the String native corresponding to the specified long format in
- * the native data transfer system.
- */
- protected abstract String getNativeForFormat(long format);
- /* Contains common code for finding the best charset for
- * clipboard string encoding/decoding, basing on clipboard
- * format and localeTransferable(on decoding, if available)
- */
- private String getBestCharsetForTextFormat(Long lFormat,
- Transferable localeTransferable) throws IOException
- {
- String charset = null;
- if (localeTransferable != null &&
- isLocaleDependentTextFormat(lFormat) &&
- localeTransferable.isDataFlavorSupported(javaTextEncodingFlavor))
- {
- try {
- charset = new String(
- (byte[])localeTransferable.getTransferData(javaTextEncodingFlavor),
- "UTF-8"
- );
- } catch (UnsupportedFlavorException cannotHappen) {
- }
- } else {
- charset = getCharsetForTextFormat(lFormat);
- }
- if (charset == null) {
- // Only happens when we have a custom text type.
- charset = getDefaultTextCharset();
- }
- return charset;
- }
- /**
- * Translation function for converting string into
- * a byte array. Search-and-replace EOLN. Encode into the
- * target format. Append terminating NUL bytes.
- *
- * Java to Native string conversion
- */
- private byte[] translateTransferableString(String str,
- long format) throws IOException
- {
- Long lFormat = Long.valueOf(format);
- String charset = getBestCharsetForTextFormat(lFormat, null);
- // Search and replace EOLN. Note that if EOLN is "\n", then we
- // never added an entry to nativeEOLNs anyway, so we'll skip this
- // code altogether.
- // windows: "abc\nde"->"abc\r\nde"
- String eoln = (String)nativeEOLNs.get(lFormat);
- if (eoln != null) {
- int length = str.length();
- StringBuffer buffer =
- new StringBuffer(length * 2); // 2 is a heuristic
- for (int i = 0; i < length; i++) {
- // Fix for 4914613 - skip native EOLN
- if (str.startsWith(eoln, i)) {
- buffer.append(eoln);
- i += eoln.length() - 1;
- continue;
- }
- char c = str.charAt(i);
- if (c == '\n') {
- buffer.append(eoln);
- } else {
- buffer.append(c);
- }
- }
- str = buffer.toString();
- }
- // Encode text in target format.
- byte[] bytes = str.getBytes(charset);
- // Append terminating NUL bytes. Note that if terminators is 0,
- // the we never added an entry to nativeTerminators anyway, so
- // we'll skip code altogether.
- // "abcde" -> "abcde\0"
- Integer terminators = (Integer)nativeTerminators.get(lFormat);
- if (terminators != null) {
- int numTerminators = terminators.intValue();
- byte[] terminatedBytes =
- new byte[bytes.length + numTerminators];
- System.arraycopy(bytes, 0, terminatedBytes, 0, bytes.length);
- for (int i = bytes.length; i < terminatedBytes.length; i++) {
- terminatedBytes[i] = 0x0;
- }
- bytes = terminatedBytes;
- }
- return bytes;
- }
- /**
- * Translating either a byte array or an InputStream into an String.
- * Strip terminators and search-and-replace EOLN.
- *
- * Native to Java string conversion
- */
- private String translateBytesToString(byte[] bytes, long format,
- Transferable localeTransferable)
- throws IOException
- {
- Long lFormat = Long.valueOf(format);
- String charset = getBestCharsetForTextFormat(lFormat, localeTransferable);
- // Locate terminating NUL bytes. Note that if terminators is 0,
- // the we never added an entry to nativeTerminators anyway, so
- // we'll skip code altogether.
- // In other words: we are doing char alignment here basing on suggestion
- // that count of zero-'terminators' is a number of bytes in one symbol
- // for selected charset (clipboard format). It is not complitly true for
- // multibyte coding like UTF-8, but helps understand the procedure.
- // "abcde\0" -> "abcde"
- String eoln = (String)nativeEOLNs.get(lFormat);
- Integer terminators = (Integer)nativeTerminators.get(lFormat);
- int count;
- if (terminators != null) {
- int numTerminators = terminators.intValue();
- search:
- for (count = 0; count < (bytes.length - numTerminators + 1); count += numTerminators) {
- for (int i = count; i < count + numTerminators; i++) {
- if (bytes[i] != 0x0) {
- continue search;
- }
- }
- // found terminators
- break search;
- }
- } else {
- count = bytes.length;
- }
- // Decode text to chars. Don't include any terminators.
- String converted = new String(bytes, 0, count, charset);
- // Search and replace EOLN. Note that if EOLN is "\n", then we
- // never added an entry to nativeEOLNs anyway, so we'll skip this
- // code altogether.
- // Count of NUL-terminators and EOLN coding are platform-specific and
- // loaded from flavormap.properties file
- // windows: "abc\r\nde" -> "abc\nde"
- if (eoln != null) {
- /* Fix for 4463560: replace EOLNs symbol-by-symbol instead
- * of using buf.replace()
- */
- char[] buf = converted.toCharArray();
- char[] eoln_arr = eoln.toCharArray();
- converted = null;
- int j = 0;
- boolean match;
- for (int i = 0; i < buf.length; ) {
- // Catch last few bytes
- if (i + eoln_arr.length > buf.length) {
- buf[j++] = buf[i++];
- continue;
- }
- match = true;
- for (int k = 0, l = i; k < eoln_arr.length; k++, l++) {
- if (eoln_arr[k] != buf[l]) {
- match = false;
- break;
- }
- }
- if (match) {
- buf[j++] = '\n';
- i += eoln_arr.length;
- } else {
- buf[j++] = buf[i++];
- }
- }
- converted = new String(buf, 0, j);
- }
- return converted;
- }
- /**
- * Primary translation function for translating a Transferable into
- * a byte array, given a source DataFlavor and target format.
- */
- public byte[] translateTransferable(Transferable contents,
- DataFlavor flavor,
- long format) throws IOException
- {
- // Obtain the transfer data in the source DataFlavor.
- //
- // Note that we special case DataFlavor.plainTextFlavor because
- // StringSelection supports this flavor incorrectly -- instead of
- // returning an InputStream as the DataFlavor representation class
- // states, it returns a Reader. Instead of using this broken
- // functionality, we request the data in stringFlavor (the other
- // DataFlavor which StringSelection supports) and use the String
- // translator.
- Object obj;
- boolean stringSelectionHack;
- try {
- obj = contents.getTransferData(flavor);
- if (obj == null) {
- return null;
- }
- if (flavor.equals(DataFlavor.plainTextFlavor) &&
- !(obj instanceof InputStream))
- {
- obj = contents.getTransferData(DataFlavor.stringFlavor);
- if (obj == null) {
- return null;
- }
- stringSelectionHack = true;
- } else {
- stringSelectionHack = false;
- }
- } catch (UnsupportedFlavorException e) {
- throw new IOException(e.getMessage());
- }
- // Source data is a String. Search-and-replace EOLN. Encode into the
- // target format. Append terminating NUL bytes.
- if (stringSelectionHack ||
- (String.class.equals(flavor.getRepresentationClass()) &&
- isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
- String str = removeSuspectedData(flavor, contents, (String)obj);
- return translateTransferableString(
- str,
- format);
- // 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))) {
- throw new IOException
- ("cannot transfer non-text data as Reader");
- }
- StringBuffer buf = new StringBuffer();
- try (Reader r = (Reader)obj) {
- int c;
- while ((c = r.read()) != -1) {
- buf.append((char)c);
- }
- }
- return translateTransferableString(
- buf.toString(),
- format);
- // Source data is a CharBuffer. Convert to a String and recur.
- } else if (flavor.isRepresentationClassCharBuffer()) {
- if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
- throw new IOException
- ("cannot transfer non-text data as CharBuffer");
- }
- CharBuffer buffer = (CharBuffer)obj;
- int size = buffer.remaining();
- char[] chars = new char[size];
- buffer.get(chars, 0, size);
- return translateTransferableString(
- new String(chars),
- format);
- // Source data is a char array. Convert to a String and recur.
- } else if (charArrayClass.equals(flavor.getRepresentationClass())) {
- if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
- throw new IOException
- ("cannot transfer non-text data as char array");
- }
- return translateTransferableString(
- new String((char[])obj),
- format);
- // Source data is a ByteBuffer. For arbitrary flavors, simply return
- // the array. For text flavors, decode back to a String and recur to
- // reencode according to the requested format.
- } else if (flavor.isRepresentationClassByteBuffer()) {
- ByteBuffer buffer = (ByteBuffer)obj;
- int size = buffer.remaining();
- byte[] bytes = new byte[size];
- buffer.get(bytes, 0, size);
- if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
- String sourceEncoding = DataTransferer.getTextCharset(flavor);
- return translateTransferableString(
- new String(bytes, sourceEncoding),
- format);
- } else {
- return bytes;
- }
- // Source data is a byte array. For arbitrary flavors, simply return
- // the array. For text flavors, decode back to a String and recur to
- // reencode according to the requested format.
- } else if (byteArrayClass.equals(flavor.getRepresentationClass())) {
- byte[] bytes = (byte[])obj;
- if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
- String sourceEncoding = DataTransferer.getTextCharset(flavor);
- return translateTransferableString(
- new String(bytes, sourceEncoding),
- format);
- } else {
- return bytes;
- }
- // Source data is Image
- } else if (DataFlavor.imageFlavor.equals(flavor)) {
- if (!isImageFormat(format)) {
- …
Large files files are truncated, but you can click here to view the full file