Path: blob/master/src/java.datatransfer/share/classes/java/awt/datatransfer/DataFlavor.java
41159 views
/*1* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.awt.datatransfer;2627import java.io.ByteArrayInputStream;28import java.io.CharArrayReader;29import java.io.Externalizable;30import java.io.IOException;31import java.io.InputStream;32import java.io.InputStreamReader;33import java.io.ObjectInput;34import java.io.ObjectOutput;35import java.io.OptionalDataException;36import java.io.Reader;37import java.io.Serial;38import java.io.StringReader;39import java.io.UnsupportedEncodingException;40import java.nio.ByteBuffer;41import java.nio.CharBuffer;42import java.util.Arrays;43import java.util.Collections;44import java.util.Objects;4546import sun.datatransfer.DataFlavorUtil;47import sun.reflect.misc.ReflectUtil;4849/**50* A {@code DataFlavor} provides meta information about data. {@code DataFlavor}51* is typically used to access data on the clipboard, or during a drag and drop52* operation.53* <p>54* An instance of {@code DataFlavor} encapsulates a content type as defined in55* <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a> and56* <a href="http://www.ietf.org/rfc/rfc2046.txt">RFC 2046</a>. A content type is57* typically referred to as a MIME type.58* <p>59* A content type consists of a media type (referred to as the primary type), a60* subtype, and optional parameters. See61* <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a> for details on the62* syntax of a MIME type.63* <p>64* The JRE data transfer implementation interprets the parameter65* "class" of a MIME type as <B>a representation class</b>. The66* representation class reflects the class of the object being transferred. In67* other words, the representation class is the type of object returned by68* {@link Transferable#getTransferData}. For example, the MIME type of69* {@link #imageFlavor} is {@code "image/x-java-image;class=java.awt.Image"},70* the primary type is {@code image}, the subtype is {@code x-java-image}, and71* the representation class is {@code java.awt.Image}. When72* {@code getTransferData} is invoked with a {@code DataFlavor} of73* {@code imageFlavor}, an instance of {@code java.awt.Image} is returned. It's74* important to note that {@code DataFlavor} does no error checking against the75* representation class. It is up to consumers of {@code DataFlavor}, such as76* {@code Transferable}, to honor the representation class.77* <br>78* Note, if you do not specify a representation class when creating a79* {@code DataFlavor}, the default representation class is used. See appropriate80* documentation for {@code DataFlavor}'s constructors.81* <p>82* Also, {@code DataFlavor} instances with the "text" primary MIME83* type may have a "charset" parameter. Refer to84* <a href="http://www.ietf.org/rfc/rfc2046.txt">RFC 2046</a> and85* {@link #selectBestTextFlavor} for details on "text" MIME types and86* the "charset" parameter.87* <p>88* Equality of {@code DataFlavors} is determined by the primary type, subtype,89* and representation class. Refer to {@link #equals(DataFlavor)} for details.90* When determining equality, any optional parameters are ignored. For example,91* the following produces two {@code DataFlavors} that are considered identical:92* <pre>93* DataFlavor flavor1 = new DataFlavor(Object.class, "X-test/test; class=<java.lang.Object>; foo=bar");94* DataFlavor flavor2 = new DataFlavor(Object.class, "X-test/test; class=<java.lang.Object>; x=y");95* // The following returns true.96* flavor1.equals(flavor2);97* </pre>98* As mentioned, {@code flavor1} and {@code flavor2} are considered identical.99* As such, asking a {@code Transferable} for either {@code DataFlavor} returns100* the same results.101* <p>102* For more information on using data transfer with Swing see the103* <a href="http://docs.oracle.com/javase/tutorial/uiswing/dnd/index.html">How104* to Use Drag and Drop and Data Transfer</a>, section in105* <em>The Java Tutorial</em>.106*107* @author Blake Sullivan108* @author Laurence P. G. Cable109* @author Jeff Dunn110* @since 1.1111*/112public class DataFlavor implements Externalizable, Cloneable {113114/**115* Use serialVersionUID from JDK 1.2 for interoperability.116*/117@Serial118private static final long serialVersionUID = 8367026044764648243L;119120/**121* Tries to load a class from: the bootstrap loader, the system loader, the122* context loader (if one is present) and finally the loader specified.123*124* @param className the name of the class to be loaded125* @param fallback the fallback loader126* @return the class loaded127* @throws ClassNotFoundException if class is not found128*/129protected static final Class<?> tryToLoadClass(String className,130ClassLoader fallback)131throws ClassNotFoundException132{133ReflectUtil.checkPackageAccess(className);134try {135@SuppressWarnings("removal")136SecurityManager sm = System.getSecurityManager();137if (sm != null) {138sm.checkPermission(new RuntimePermission("getClassLoader"));139}140ClassLoader loader = ClassLoader.getSystemClassLoader();141try {142// bootstrap class loader and system class loader if present143return Class.forName(className, true, loader);144}145catch (ClassNotFoundException exception) {146// thread context class loader if and only if present147loader = Thread.currentThread().getContextClassLoader();148if (loader != null) {149try {150return Class.forName(className, true, loader);151}152catch (ClassNotFoundException e) {153// fallback to user's class loader154}155}156}157} catch (SecurityException exception) {158// ignore secured class loaders159}160return Class.forName(className, true, fallback);161}162163/*164* private initializer165*/166private static DataFlavor createConstant(Class<?> rc, String prn) {167try {168return new DataFlavor(rc, prn);169} catch (Exception e) {170return null;171}172}173174/*175* private initializer176*/177private static DataFlavor createConstant(String mt, String prn) {178try {179return new DataFlavor(mt, prn);180} catch (Exception e) {181return null;182}183}184185/*186* private initializer187*/188private static DataFlavor initHtml(String htmlFlavorType) {189try {190return new DataFlavor("text/html; class=java.lang.String;document="191+ htmlFlavorType + ";charset=Unicode");192} catch (Exception e) {193return null;194}195}196197/**198* The {@code DataFlavor} representing a Java Unicode String class, where:199* <pre>200* representationClass = java.lang.String201* mimeType = "application/x-java-serialized-object"202* </pre>203*/204public static final DataFlavor stringFlavor = createConstant(java.lang.String.class, "Unicode String");205206/**207* The {@code DataFlavor} representing a Java Image class, where:208* <pre>209* representationClass = java.awt.Image210* mimeType = "image/x-java-image"211* </pre>212* Will be {@code null} if {@code java.awt.Image} is not visible, the213* {@code java.desktop} module is not loaded, or the {@code java.desktop}214* module is not in the run-time image.215*/216public static final DataFlavor imageFlavor = createConstant("image/x-java-image; class=java.awt.Image", "Image");217218/**219* The {@code DataFlavor} representing plain text with Unicode encoding,220* where:221* <pre>222* representationClass = InputStream223* mimeType = "text/plain; charset=unicode"224* </pre>225* This {@code DataFlavor} has been <b>deprecated</b> because:226* <ul>227* <li>Its representation is an InputStream, an 8-bit based representation,228* while Unicode is a 16-bit character set</li>229* <li>The charset "unicode" is not well-defined. "unicode" implies a230* particular platform's implementation of Unicode, not a cross-platform231* implementation</li>232* </ul>233*234* @deprecated as of 1.3. Use {@link #getReaderForText} instead of235* {@code Transferable.getTransferData(DataFlavor.plainTextFlavor)}.236*/237@Deprecated238public static final DataFlavor plainTextFlavor = createConstant("text/plain; charset=unicode; class=java.io.InputStream", "Plain Text");239240/**241* A MIME Content-Type of application/x-java-serialized-object represents a242* graph of Java object(s) that have been made persistent.243* <p>244* The representation class associated with this {@code DataFlavor}245* identifies the Java type of an object returned as a reference from an246* invocation {@code java.awt.datatransfer.getTransferData}.247*/248public static final String javaSerializedObjectMimeType = "application/x-java-serialized-object";249250/**251* To transfer a list of files to/from Java (and the underlying platform) a252* {@code DataFlavor} of this type/subtype and representation class of253* {@code java.util.List} is used. Each element of the list is254* required/guaranteed to be of type {@code java.io.File}.255*/256public static final DataFlavor javaFileListFlavor = createConstant("application/x-java-file-list;class=java.util.List", null);257258/**259* To transfer a reference to an arbitrary Java object reference that has no260* associated MIME Content-type, across a {@code Transferable} interface261* WITHIN THE SAME JVM, a {@code DataFlavor} with this type/subtype is used,262* with a {@code representationClass} equal to the type of the263* class/interface being passed across the {@code Transferable}.264* <p>265* The object reference returned from {@code Transferable.getTransferData}266* for a {@code DataFlavor} with this MIME Content-Type is required to be an267* instance of the representation Class of the {@code DataFlavor}.268*/269public static final String javaJVMLocalObjectMimeType = "application/x-java-jvm-local-objectref";270271/**272* In order to pass a live link to a Remote object via a Drag and Drop273* {@code ACTION_LINK} operation a Mime Content Type of274* application/x-java-remote-object should be used, where the representation275* class of the {@code DataFlavor} represents the type of the {@code Remote}276* interface to be transferred.277*/278public static final String javaRemoteObjectMimeType = "application/x-java-remote-object";279280/**281* Represents a piece of an HTML markup. The markup consists of the part282* selected on the source side. Therefore some tags in the markup may be283* unpaired. If the flavor is used to represent the data in a284* {@link Transferable} instance, no additional changes will be made. This285* DataFlavor instance represents the same HTML markup as DataFlavor286* instances which content MIME type does not contain document parameter287* and representation class is the String class.288* <pre>289* representationClass = String290* mimeType = "text/html"291* </pre>292*293* @since 1.8294*/295public static final DataFlavor selectionHtmlFlavor = initHtml("selection");296297/**298* Represents a piece of an HTML markup. If possible, the markup received299* from a native system is supplemented with pair tags to be a well-formed300* HTML markup. If the flavor is used to represent the data in a301* {@link Transferable} instance, no additional changes will be made.302* <pre>303* representationClass = String304* mimeType = "text/html"305* </pre>306*307* @since 1.8308*/309public static final DataFlavor fragmentHtmlFlavor = initHtml("fragment");310311/**312* Represents a piece of an HTML markup. If possible, the markup received313* from a native system is supplemented with additional tags to make up a314* well-formed HTML document. If the flavor is used to represent the data in315* a {@link Transferable} instance, no additional changes will be made.316* <pre>317* representationClass = String318* mimeType = "text/html"319* </pre>320*321* @since 1.8322*/323public static final DataFlavor allHtmlFlavor = initHtml("all");324325/**326* Constructs a new {@code DataFlavor}. This constructor is provided only327* for the purpose of supporting the {@code Externalizable} interface. It is328* not intended for public (client) use.329*330* @since 1.2331*/332public DataFlavor() {333super();334}335336/**337* Constructs a fully specified {@code DataFlavor}.338*339* @throws NullPointerException if either {@code primaryType},340* {@code subType} or {@code representationClass} is {@code null}341*/342private DataFlavor(String primaryType, String subType, MimeTypeParameterList params, Class<?> representationClass, String humanPresentableName) {343super();344if (primaryType == null) {345throw new NullPointerException("primaryType");346}347if (subType == null) {348throw new NullPointerException("subType");349}350if (representationClass == null) {351throw new NullPointerException("representationClass");352}353354if (params == null) params = new MimeTypeParameterList();355356params.set("class", representationClass.getName());357358if (humanPresentableName == null) {359humanPresentableName = params.get("humanPresentableName");360361if (humanPresentableName == null)362humanPresentableName = primaryType + "/" + subType;363}364365try {366mimeType = new MimeType(primaryType, subType, params);367} catch (MimeTypeParseException mtpe) {368throw new IllegalArgumentException("MimeType Parse Exception: " + mtpe.getMessage());369}370371this.representationClass = representationClass;372this.humanPresentableName = humanPresentableName;373374mimeType.removeParameter("humanPresentableName");375}376377/**378* Constructs a {@code DataFlavor} that represents a Java class.379* <p>380* The returned {@code DataFlavor} will have the following characteristics:381* <pre>382* representationClass = representationClass383* mimeType = application/x-java-serialized-object384* </pre>385*386* @param representationClass the class used to transfer data in this387* flavor388* @param humanPresentableName the human-readable string used to identify389* this flavor; if this parameter is {@code null} then the value of390* the MIME Content Type is used391* @throws NullPointerException if {@code representationClass} is392* {@code null}393*/394public DataFlavor(Class<?> representationClass, String humanPresentableName) {395this("application", "x-java-serialized-object", null, representationClass, humanPresentableName);396if (representationClass == null) {397throw new NullPointerException("representationClass");398}399}400401/**402* Constructs a {@code DataFlavor} that represents a {@code MimeType}.403* <p>404* The returned {@code DataFlavor} will have the following characteristics:405* <p>406* If the {@code mimeType} is407* "application/x-java-serialized-object; class=<representation class>",408* the result is the same as calling409* {@code new DataFlavor(Class.forName(<representation class>)}.410* <p>411* Otherwise:412* <pre>413* representationClass = InputStream414* mimeType = mimeType415* </pre>416*417* @param mimeType the string used to identify the MIME type for this418* flavor; if the {@code mimeType} does not specify a "class="419* parameter, or if the class is not successfully loaded, then an420* {@code IllegalArgumentException} is thrown421* @param humanPresentableName the human-readable string used to identify422* this flavor; if this parameter is {@code null} then the value of423* the MIME Content Type is used424* @throws IllegalArgumentException if {@code mimeType} is invalid or if the425* class is not successfully loaded426* @throws NullPointerException if {@code mimeType} is {@code null}427*/428public DataFlavor(String mimeType, String humanPresentableName) {429super();430if (mimeType == null) {431throw new NullPointerException("mimeType");432}433try {434initialize(mimeType, humanPresentableName, this.getClass().getClassLoader());435} catch (MimeTypeParseException mtpe) {436throw new IllegalArgumentException("failed to parse:" + mimeType);437} catch (ClassNotFoundException cnfe) {438throw new IllegalArgumentException("can't find specified class: " + cnfe.getMessage());439}440}441442/**443* Constructs a {@code DataFlavor} that represents a {@code MimeType}.444* <p>445* The returned {@code DataFlavor} will have the following characteristics:446* <p>447* If the mimeType is448* "application/x-java-serialized-object; class=<representation class>",449* the result is the same as calling450* {@code new DataFlavor(Class.forName(<representation class>)}.451* <p>452* Otherwise:453* <pre>454* representationClass = InputStream455* mimeType = mimeType456* </pre>457*458* @param mimeType the string used to identify the MIME type for this459* flavor460* @param humanPresentableName the human-readable string used to identify461* this flavor462* @param classLoader the class loader to use463* @throws ClassNotFoundException if the class is not loaded464* @throws IllegalArgumentException if {@code mimeType} is invalid465* @throws NullPointerException if {@code mimeType} is {@code null}466*/467public DataFlavor(String mimeType, String humanPresentableName, ClassLoader classLoader) throws ClassNotFoundException {468super();469if (mimeType == null) {470throw new NullPointerException("mimeType");471}472try {473initialize(mimeType, humanPresentableName, classLoader);474} catch (MimeTypeParseException mtpe) {475throw new IllegalArgumentException("failed to parse:" + mimeType);476}477}478479/**480* Constructs a {@code DataFlavor} from a {@code mimeType} string. The481* string can specify a "class=<fully specified Java class name>"482* parameter to create a {@code DataFlavor} with the desired representation483* class. If the string does not contain "class=" parameter,484* {@code java.io.InputStream} is used as default.485*486* @param mimeType the string used to identify the MIME type for this487* flavor; if the class specified by "class=" parameter is not488* successfully loaded, then a {@code ClassNotFoundException} is489* thrown490* @throws ClassNotFoundException if the class is not loaded491* @throws IllegalArgumentException if {@code mimeType} is invalid492* @throws NullPointerException if {@code mimeType} is {@code null}493*/494public DataFlavor(String mimeType) throws ClassNotFoundException {495super();496if (mimeType == null) {497throw new NullPointerException("mimeType");498}499try {500initialize(mimeType, null, this.getClass().getClassLoader());501} catch (MimeTypeParseException mtpe) {502throw new IllegalArgumentException("failed to parse:" + mimeType);503}504}505506/**507* Common initialization code called from various constructors.508*509* @param mimeType the MIME Content Type (must have a class= param)510* @param humanPresentableName the human Presentable Name or {@code null}511* @param classLoader the fallback class loader to resolve against512* @throws MimeTypeParseException513* @throws ClassNotFoundException514* @throws NullPointerException if {@code mimeType} is {@code null}515* @see #tryToLoadClass516*/517private void initialize(String mimeType, String humanPresentableName, ClassLoader classLoader) throws MimeTypeParseException, ClassNotFoundException {518if (mimeType == null) {519throw new NullPointerException("mimeType");520}521522this.mimeType = new MimeType(mimeType); // throws523524String rcn = getParameter("class");525526if (rcn == null) {527if ("application/x-java-serialized-object".equals(this.mimeType.getBaseType()))528529throw new IllegalArgumentException("no representation class specified for:" + mimeType);530else531representationClass = java.io.InputStream.class; // default532} else { // got a class name533representationClass = DataFlavor.tryToLoadClass(rcn, classLoader);534}535536this.mimeType.setParameter("class", representationClass.getName());537538if (humanPresentableName == null) {539humanPresentableName = this.mimeType.getParameter("humanPresentableName");540if (humanPresentableName == null)541humanPresentableName = this.mimeType.getPrimaryType() + "/" + this.mimeType.getSubType();542}543544this.humanPresentableName = humanPresentableName; // set it.545546this.mimeType.removeParameter("humanPresentableName"); // just in case547}548549/**550* String representation of this {@code DataFlavor} and its parameters. The551* resulting {@code String} contains the name of the {@code DataFlavor}552* class, this flavor's MIME type, and its representation class. If this553* flavor has a primary MIME type of "text", supports the charset parameter,554* and has an encoded representation, the flavor's charset is also included.555* See {@code selectBestTextFlavor} for a list of text flavors which support556* the charset parameter.557*558* @return string representation of this {@code DataFlavor}559* @see #selectBestTextFlavor560*/561public String toString() {562String string = getClass().getName();563string += "["+paramString()+"]";564return string;565}566567private String paramString() {568String params = "";569params += "mimetype=";570if (mimeType == null) {571params += "null";572} else {573params += mimeType.getBaseType();574}575params += ";representationclass=";576if (representationClass == null) {577params += "null";578} else {579params += representationClass.getName();580}581if (DataFlavorUtil.isFlavorCharsetTextType(this) &&582(isRepresentationClassInputStream() ||583isRepresentationClassByteBuffer() ||584byte[].class.equals(representationClass)))585{586params += ";charset=" + DataFlavorUtil.getTextCharset(this);587}588return params;589}590591/**592* Returns a {@code DataFlavor} representing plain text with Unicode593* encoding, where:594* <pre>595* representationClass = java.io.InputStream596* mimeType = "text/plain;597* charset=<platform default Unicode encoding>"598* </pre>599* @implNote Oracle's implementation for Microsoft Windows and macOS uses600* the encoding {@code utf-16le}. Oracle's implementation for Solaris and601* Linux uses the encoding {@code iso-10646-ucs-2}.602*603* @return a {@code DataFlavor} representing plain text with Unicode604* encoding605* @since 1.3606*/607public static final DataFlavor getTextPlainUnicodeFlavor() {608return new DataFlavor(609"text/plain;charset=" + DataFlavorUtil.getDesktopService().getDefaultUnicodeEncoding()610+";class=java.io.InputStream", "Plain Text");611}612613/**614* Selects the best text {@code DataFlavor} from an array of615* {@code DataFlavor}s. Only {@code DataFlavor.stringFlavor}, and equivalent616* flavors, and flavors that have a primary MIME type of "text", are617* considered for selection.618* <p>619* Flavors are first sorted by their MIME types in the following order:620* <ul>621* <li>"text/sgml"622* <li>"text/xml"623* <li>"text/html"624* <li>"text/rtf"625* <li>"text/enriched"626* <li>"text/richtext"627* <li>"text/uri-list"628* <li>"text/tab-separated-values"629* <li>"text/t140"630* <li>"text/rfc822-headers"631* <li>"text/parityfec"632* <li>"text/directory"633* <li>"text/css"634* <li>"text/calendar"635* <li>"application/x-java-serialized-object"636* <li>"text/plain"637* <li>"text/<other>"638* </ul>639* <p>640* For example, "text/sgml" will be selected over "text/html", and641* {@code DataFlavor.stringFlavor} will be chosen over642* {@code DataFlavor.plainTextFlavor}.643* <p>644* If two or more flavors share the best MIME type in the array, then that645* MIME type will be checked to see if it supports the charset parameter.646* <p>647* The following MIME types support, or are treated as though they support,648* the charset parameter:649* <ul>650* <li>"text/sgml"651* <li>"text/xml"652* <li>"text/html"653* <li>"text/enriched"654* <li>"text/richtext"655* <li>"text/uri-list"656* <li>"text/directory"657* <li>"text/css"658* <li>"text/calendar"659* <li>"application/x-java-serialized-object"660* <li>"text/plain"661* </ul>662* The following MIME types do not support, or are treated as though they do663* not support, the charset parameter:664* <ul>665* <li>"text/rtf"666* <li>"text/tab-separated-values"667* <li>"text/t140"668* <li>"text/rfc822-headers"669* <li>"text/parityfec"670* </ul>671* For "text/<other>" MIME types, the first time the JRE needs to672* determine whether the MIME type supports the charset parameter, it will673* check whether the parameter is explicitly listed in an arbitrarily chosen674* {@code DataFlavor} which uses that MIME type. If so, the JRE will assume675* from that point on that the MIME type supports the charset parameter and676* will not check again. If the parameter is not explicitly listed, the JRE677* will assume from that point on that the MIME type does not support the678* charset parameter and will not check again. Because this check is679* performed on an arbitrarily chosen {@code DataFlavor}, developers must680* ensure that all {@code DataFlavor}s with a "text/<other>" MIME type681* specify the charset parameter if it is supported by that MIME type.682* Developers should never rely on the JRE to substitute the platform's683* default charset for a "text/<other>" DataFlavor. Failure to adhere684* to this restriction will lead to undefined behavior.685* <p>686* If the best MIME type in the array does not support the charset687* parameter, the flavors which share that MIME type will then be sorted by688* their representation classes in the following order:689* {@code java.io.InputStream}, {@code java.nio.ByteBuffer}, {@code [B},690* <all others>.691* <p>692* If two or more flavors share the best representation class, or if no693* flavor has one of the three specified representations, then one of those694* flavors will be chosen non-deterministically.695* <p>696* If the best MIME type in the array does support the charset parameter,697* the flavors which share that MIME type will then be sorted by their698* representation classes in the following order: {@code java.io.Reader},699* {@code java.lang.String}, {@code java.nio.CharBuffer}, {@code [C},700* <all others>.701* <p>702* If two or more flavors share the best representation class, and that703* representation is one of the four explicitly listed, then one of those704* flavors will be chosen non-deterministically. If, however, no flavor has705* one of the four specified representations, the flavors will then be706* sorted by their charsets. Unicode charsets, such as "UTF-16", "UTF-8",707* "UTF-16BE", "UTF-16LE", and their aliases, are considered best. After708* them, the platform default charset and its aliases are selected.709* "US-ASCII" and its aliases are worst. All other charsets are chosen in710* alphabetical order, but only charsets supported by this implementation of711* the Java platform will be considered.712* <p>713* If two or more flavors share the best charset, the flavors will then714* again be sorted by their representation classes in the following order:715* {@code java.io.InputStream}, {@code java.nio.ByteBuffer}, {@code [B},716* <all others>.717* <p>718* If two or more flavors share the best representation class, or if no719* flavor has one of the three specified representations, then one of those720* flavors will be chosen non-deterministically.721*722* @param availableFlavors an array of available {@code DataFlavor}s723* @return the best (highest fidelity) flavor according to the rules724* specified above, or {@code null}, if {@code availableFlavors} is725* {@code null}, has zero length, or contains no text flavors726* @since 1.3727*/728public static final DataFlavor selectBestTextFlavor(729DataFlavor[] availableFlavors) {730if (availableFlavors == null || availableFlavors.length == 0) {731return null;732}733734DataFlavor bestFlavor = Collections.max(Arrays.asList(availableFlavors),735DataFlavorUtil.getTextFlavorComparator());736737if (!bestFlavor.isFlavorTextType()) {738return null;739}740741return bestFlavor;742}743744/**745* Gets a Reader for a text flavor, decoded, if necessary, for the expected746* charset (encoding). The supported representation classes are747* {@code java.io.Reader}, {@code java.lang.String},748* {@code java.nio.CharBuffer}, {@code [C}, {@code java.io.InputStream},749* {@code java.nio.ByteBuffer}, and {@code [B}.750* <p>751* Because text flavors which do not support the charset parameter are752* encoded in a non-standard format, this method should not be called for753* such flavors. However, in order to maintain backward-compatibility, if754* this method is called for such a flavor, this method will treat the755* flavor as though it supports the charset parameter and attempt to decode756* it accordingly. See {@code selectBestTextFlavor} for a list of text757* flavors which do not support the charset parameter.758*759* @param transferable the {@code Transferable} whose data will be760* requested in this flavor761* @return a {@code Reader} to read the {@code Transferable}'s data762* @throws IllegalArgumentException if the representation class is not one763* of the seven listed above764* @throws IllegalArgumentException if the {@code Transferable} has765* {@code null} data766* @throws NullPointerException if the {@code Transferable} is {@code null}767* @throws UnsupportedEncodingException if this flavor's representation is768* {@code java.io.InputStream}, {@code java.nio.ByteBuffer}, or769* {@code [B} and this flavor's encoding is not supported by this770* implementation of the Java platform771* @throws UnsupportedFlavorException if the {@code Transferable} does not772* support this flavor773* @throws IOException if the data cannot be read because of an I/O error774* @see #selectBestTextFlavor775* @since 1.3776*/777public Reader getReaderForText(Transferable transferable)778throws UnsupportedFlavorException, IOException779{780Object transferObject = transferable.getTransferData(this);781if (transferObject == null) {782throw new IllegalArgumentException783("getTransferData() returned null");784}785786if (transferObject instanceof Reader) {787return (Reader)transferObject;788} else if (transferObject instanceof String) {789return new StringReader((String)transferObject);790} else if (transferObject instanceof CharBuffer) {791CharBuffer buffer = (CharBuffer)transferObject;792int size = buffer.remaining();793char[] chars = new char[size];794buffer.get(chars, 0, size);795return new CharArrayReader(chars);796} else if (transferObject instanceof char[]) {797return new CharArrayReader((char[])transferObject);798}799800InputStream stream = null;801802if (transferObject instanceof InputStream) {803stream = (InputStream)transferObject;804} else if (transferObject instanceof ByteBuffer) {805ByteBuffer buffer = (ByteBuffer)transferObject;806int size = buffer.remaining();807byte[] bytes = new byte[size];808buffer.get(bytes, 0, size);809stream = new ByteArrayInputStream(bytes);810} else if (transferObject instanceof byte[]) {811stream = new ByteArrayInputStream((byte[])transferObject);812}813814if (stream == null) {815throw new IllegalArgumentException("transfer data is not Reader, String, CharBuffer, char array, InputStream, ByteBuffer, or byte array");816}817818String encoding = getParameter("charset");819return (encoding == null)820? new InputStreamReader(stream)821: new InputStreamReader(stream, encoding);822}823824/**825* Returns the MIME type string for this {@code DataFlavor}.826*827* @return the MIME type string for this flavor828*/829public String getMimeType() {830return (mimeType != null) ? mimeType.toString() : null;831}832833/**834* Returns the {@code Class} which objects supporting this835* {@code DataFlavor} will return when this {@code DataFlavor} is requested.836*837* @return the {@code Class} which objects supporting this838* {@code DataFlavor} will return when this {@code DataFlavor} is839* requested840*/841public Class<?> getRepresentationClass() {842return representationClass;843}844845/**846* Returns the human presentable name for the data format that this847* {@code DataFlavor} represents. This name would be localized for different848* countries.849*850* @return the human presentable name for the data format that this851* {@code DataFlavor} represents852*/853public String getHumanPresentableName() {854return humanPresentableName;855}856857/**858* Returns the primary MIME type for this {@code DataFlavor}.859*860* @return the primary MIME type of this {@code DataFlavor}861*/862public String getPrimaryType() {863return (mimeType != null) ? mimeType.getPrimaryType() : null;864}865866/**867* Returns the sub MIME type of this {@code DataFlavor}.868*869* @return the Sub MIME type of this {@code DataFlavor}870*/871public String getSubType() {872return (mimeType != null) ? mimeType.getSubType() : null;873}874875/**876* Returns the human presentable name for this {@code DataFlavor} if877* {@code paramName} equals "humanPresentableName". Otherwise returns the878* MIME type value associated with {@code paramName}.879*880* @param paramName the parameter name requested881* @return the value of the name parameter, or {@code null} if there is no882* associated value883*/884public String getParameter(String paramName) {885if (paramName.equals("humanPresentableName")) {886return humanPresentableName;887} else {888return (mimeType != null)889? mimeType.getParameter(paramName) : null;890}891}892893/**894* Sets the human presentable name for the data format that this895* {@code DataFlavor} represents. This name would be localized for different896* countries.897*898* @param humanPresentableName the new human presentable name899*/900public void setHumanPresentableName(String humanPresentableName) {901this.humanPresentableName = humanPresentableName;902}903904/**905* {@inheritDoc}906* <p>907* The equals comparison for the {@code DataFlavor} class is implemented as908* follows: Two {@code DataFlavor}s are considered equal if and only if909* their MIME primary type and subtype and representation class are equal.910* Additionally, if the primary type is "text", the subtype denotes a text911* flavor which supports the charset parameter, and the representation class912* is not {@code java.io.Reader}, {@code java.lang.String},913* {@code java.nio.CharBuffer}, or {@code [C}, the {@code charset} parameter914* must also be equal. If a charset is not explicitly specified for one or915* both {@code DataFlavor}s, the platform default encoding is assumed. See916* {@code selectBestTextFlavor} for a list of text flavors which support the917* charset parameter.918*919* @param o the {@code Object} to compare with {@code this}920* @return {@code true} if {@code that} is equivalent to this921* {@code DataFlavor}; {@code false} otherwise922* @see #selectBestTextFlavor923*/924public boolean equals(Object o) {925return ((o instanceof DataFlavor) && equals((DataFlavor)o));926}927928/**929* This method has the same behavior as {@link #equals(Object)}. The only930* difference being that it takes a {@code DataFlavor} instance as a931* parameter.932*933* @param that the {@code DataFlavor} to compare with {@code this}934* @return {@code true} if {@code that} is equivalent to this935* {@code DataFlavor}; {@code false} otherwise936* @see #selectBestTextFlavor937*/938public boolean equals(DataFlavor that) {939if (that == null) {940return false;941}942if (this == that) {943return true;944}945946if (!Objects.equals(this.getRepresentationClass(), that.getRepresentationClass())) {947return false;948}949950if (mimeType == null) {951if (that.mimeType != null) {952return false;953}954} else {955if (!mimeType.match(that.mimeType)) {956return false;957}958959if ("text".equals(getPrimaryType())) {960if (DataFlavorUtil.doesSubtypeSupportCharset(this)961&& representationClass != null962&& !isStandardTextRepresentationClass()) {963String thisCharset =964DataFlavorUtil.canonicalName(this.getParameter("charset"));965String thatCharset =966DataFlavorUtil.canonicalName(that.getParameter("charset"));967if (!Objects.equals(thisCharset, thatCharset)) {968return false;969}970}971972if ("html".equals(getSubType())) {973String thisDocument = this.getParameter("document");974String thatDocument = that.getParameter("document");975if (!Objects.equals(thisDocument, thatDocument)) {976return false;977}978}979}980}981982return true;983}984985/**986* Compares only the {@code mimeType} against the passed in {@code String}987* and {@code representationClass} is not considered in the comparison. If988* {@code representationClass} needs to be compared, then989* {@code equals(new DataFlavor(s))} may be used.990*991* @param s the {@code mimeType} to compare992* @return {@code true} if the String (MimeType) is equal; {@code false}993* otherwise or if {@code s} is {@code null}994* @deprecated As inconsistent with {@code hashCode()} contract, use995* {@link #isMimeTypeEqual(String)} instead.996*/997@Deprecated998public boolean equals(String s) {999if (s == null || mimeType == null)1000return false;1001return isMimeTypeEqual(s);1002}10031004/**1005* Returns hash code for this {@code DataFlavor}. For two equal1006* {@code DataFlavor}s, hash codes are equal. For the {@code String} that1007* matches {@code DataFlavor.equals(String)}, it is not guaranteed that1008* {@code DataFlavor}'s hash code is equal to the hash code of the1009* {@code String}.1010*1011* @return a hash code for this {@code DataFlavor}1012*/1013public int hashCode() {1014int total = 0;10151016if (representationClass != null) {1017total += representationClass.hashCode();1018}10191020if (mimeType != null) {1021String primaryType = mimeType.getPrimaryType();1022if (primaryType != null) {1023total += primaryType.hashCode();1024}10251026// Do not add subType.hashCode() to the total. equals uses1027// MimeType.match which reports a match if one or both of the1028// subTypes is '*', regardless of the other subType.10291030if ("text".equals(primaryType)) {1031if (DataFlavorUtil.doesSubtypeSupportCharset(this)1032&& representationClass != null1033&& !isStandardTextRepresentationClass()) {1034String charset = DataFlavorUtil.canonicalName(getParameter("charset"));1035if (charset != null) {1036total += charset.hashCode();1037}1038}10391040if ("html".equals(getSubType())) {1041String document = this.getParameter("document");1042if (document != null) {1043total += document.hashCode();1044}1045}1046}1047}10481049return total;1050}10511052/**1053* Identical to {@link #equals(DataFlavor)}.1054*1055* @param that the {@code DataFlavor} to compare with {@code this}1056* @return {@code true} if {@code that} is equivalent to this1057* {@code DataFlavor}; {@code false} otherwise1058* @see #selectBestTextFlavor1059* @since 1.31060*/1061public boolean match(DataFlavor that) {1062return equals(that);1063}10641065/**1066* Returns whether the string representation of the MIME type passed in is1067* equivalent to the MIME type of this {@code DataFlavor}. Parameters are1068* not included in the comparison.1069*1070* @param mimeType the string representation of the MIME type1071* @return {@code true} if the string representation of the MIME type passed1072* in is equivalent to the MIME type of this {@code DataFlavor};1073* {@code false} otherwise1074* @throws NullPointerException if mimeType is {@code null}1075*/1076public boolean isMimeTypeEqual(String mimeType) {1077// JCK Test DataFlavor0117: if 'mimeType' is null, throw NPE1078if (mimeType == null) {1079throw new NullPointerException("mimeType");1080}1081if (this.mimeType == null) {1082return false;1083}1084try {1085return this.mimeType.match(new MimeType(mimeType));1086} catch (MimeTypeParseException mtpe) {1087return false;1088}1089}10901091/**1092* Compares the {@code mimeType} of two {@code DataFlavor} objects. No1093* parameters are considered.1094*1095* @param dataFlavor the {@code DataFlavor} to be compared1096* @return {@code true} if the {@code MimeType}s are equal, otherwise1097* {@code false}1098*/1099public final boolean isMimeTypeEqual(DataFlavor dataFlavor) {1100return isMimeTypeEqual(dataFlavor.mimeType);1101}11021103/**1104* Compares the {@code mimeType} of two {@code DataFlavor} objects. No1105* parameters are considered.1106*1107* @return {@code true} if the {@code MimeType}s are equal, otherwise1108* {@code false}1109*/1110private boolean isMimeTypeEqual(MimeType mtype) {1111if (this.mimeType == null) {1112return (mtype == null);1113}1114return mimeType.match(mtype);1115}11161117/**1118* Checks if the representation class is one of the standard text1119* representation classes.1120*1121* @return {@code true} if the representation class is one of the standard1122* text representation classes, otherwise {@code false}1123*/1124private boolean isStandardTextRepresentationClass() {1125return isRepresentationClassReader()1126|| String.class.equals(representationClass)1127|| isRepresentationClassCharBuffer()1128|| char[].class.equals(representationClass);1129}11301131/**1132* Does the {@code DataFlavor} represent a serialized object?1133*1134* @return whether or not a serialized object is represented1135*/1136public boolean isMimeTypeSerializedObject() {1137return isMimeTypeEqual(javaSerializedObjectMimeType);1138}11391140/**1141* Returns the default representation class.1142*1143* @return the default representation class1144*/1145public final Class<?> getDefaultRepresentationClass() {1146return java.io.InputStream.class;1147}11481149/**1150* Returns the name of the default representation class.1151*1152* @return the name of the default representation class1153*/1154public final String getDefaultRepresentationClassAsString() {1155return getDefaultRepresentationClass().getName();1156}11571158/**1159* Does the {@code DataFlavor} represent a {@code java.io.InputStream}?1160*1161* @return whether or not this {@code DataFlavor} represent a1162* {@code java.io.InputStream}1163*/1164public boolean isRepresentationClassInputStream() {1165return java.io.InputStream.class.isAssignableFrom(representationClass);1166}11671168/**1169* Returns whether the representation class for this {@code DataFlavor} is1170* {@code java.io.Reader} or a subclass thereof.1171*1172* @return whether or not the representation class for this1173* {@code DataFlavor} is {@code java.io.Reader} or a subclass1174* thereof1175* @since 1.41176*/1177public boolean isRepresentationClassReader() {1178return java.io.Reader.class.isAssignableFrom(representationClass);1179}11801181/**1182* Returns whether the representation class for this {@code DataFlavor} is1183* {@code java.nio.CharBuffer} or a subclass thereof.1184*1185* @return whether or not the representation class for this1186* {@code DataFlavor} is {@code java.nio.CharBuffer} or a subclass1187* thereof1188* @since 1.41189*/1190public boolean isRepresentationClassCharBuffer() {1191return java.nio.CharBuffer.class.isAssignableFrom(representationClass);1192}11931194/**1195* Returns whether the representation class for this {@code DataFlavor} is1196* {@code java.nio.ByteBuffer} or a subclass thereof.1197*1198* @return whether or not the representation class for this1199* {@code DataFlavor} is {@code java.nio.ByteBuffer} or a subclass1200* thereof1201* @since 1.41202*/1203public boolean isRepresentationClassByteBuffer() {1204return java.nio.ByteBuffer.class.isAssignableFrom(representationClass);1205}12061207/**1208* Returns {@code true} if the representation class can be serialized.1209*1210* @return {@code true} if the representation class can be serialized1211*/1212public boolean isRepresentationClassSerializable() {1213return java.io.Serializable.class.isAssignableFrom(representationClass);1214}12151216/**1217* Returns {@code true} if the representation class is {@code Remote}.1218*1219* @return {@code true} if the representation class is {@code Remote}1220*/1221public boolean isRepresentationClassRemote() {1222return DataFlavorUtil.RMI.isRemote(representationClass);1223}12241225/**1226* Returns {@code true} if the {@code DataFlavor} specified represents a1227* serialized object.1228*1229* @return {@code true} if the {@code DataFlavor} specified represents a1230* Serialized Object1231*/1232public boolean isFlavorSerializedObjectType() {1233return isRepresentationClassSerializable() && isMimeTypeEqual(javaSerializedObjectMimeType);1234}12351236/**1237* Returns {@code true} if the {@code DataFlavor} specified represents a1238* remote object.1239*1240* @return {@code true} if the {@code DataFlavor} specified represents a1241* Remote Object1242*/1243public boolean isFlavorRemoteObjectType() {1244return isRepresentationClassRemote()1245&& isRepresentationClassSerializable()1246&& isMimeTypeEqual(javaRemoteObjectMimeType);1247}12481249/**1250* Returns {@code true} if the {@code DataFlavor} specified represents a1251* list of file objects.1252*1253* @return {@code true} if the {@code DataFlavor} specified represents a1254* {@code java.util.List} of {@code java.io.File} objects1255*/1256public boolean isFlavorJavaFileListType() {1257if (mimeType == null || representationClass == null)1258return false;1259return java.util.List.class.isAssignableFrom(representationClass) &&1260mimeType.match(javaFileListFlavor.mimeType);12611262}12631264/**1265* Returns whether this {@code DataFlavor} is a valid text flavor for this1266* implementation of the Java platform. Only flavors equivalent to1267* {@code DataFlavor.stringFlavor} and {@code DataFlavor}s with a primary1268* MIME type of "text" can be valid text flavors.1269* <p>1270* If this flavor supports the charset parameter, it must be equivalent to1271* {@code DataFlavor.stringFlavor}, or its representation must be1272* {@code java.io.Reader}, {@code java.lang.String},1273* {@code java.nio.CharBuffer}, {@code [C}, {@code java.io.InputStream},1274* {@code java.nio.ByteBuffer}, or {@code [B}. If the representation is1275* {@code java.io.InputStream}, {@code java.nio.ByteBuffer}, or {@code [B},1276* then this flavor's {@code charset} parameter must be supported by this1277* implementation of the Java platform. If a charset is not specified, then1278* the platform default charset, which is always supported, is assumed.1279* <p>1280* If this flavor does not support the charset parameter, its representation1281* must be {@code java.io.InputStream}, {@code java.nio.ByteBuffer}, or1282* {@code [B}.1283* <p>1284* See {@code selectBestTextFlavor} for a list of text flavors which support1285* the charset parameter.1286*1287* @return {@code true} if this {@code DataFlavor} is a valid text flavor as1288* described above; {@code false} otherwise1289* @see #selectBestTextFlavor1290* @since 1.41291*/1292public boolean isFlavorTextType() {1293return (DataFlavorUtil.isFlavorCharsetTextType(this) ||1294DataFlavorUtil.isFlavorNoncharsetTextType(this));1295}12961297/**1298* Serializes this {@code DataFlavor}.1299*/1300public synchronized void writeExternal(ObjectOutput os) throws IOException {1301if (mimeType != null) {1302mimeType.setParameter("humanPresentableName", humanPresentableName);1303os.writeObject(mimeType);1304mimeType.removeParameter("humanPresentableName");1305} else {1306os.writeObject(null);1307}13081309os.writeObject(representationClass);1310}13111312/**1313* Restores this {@code DataFlavor} from a Serialized state.1314*/1315public synchronized void readExternal(ObjectInput is) throws IOException , ClassNotFoundException {1316String rcn = null;1317mimeType = (MimeType)is.readObject();13181319if (mimeType != null) {1320humanPresentableName =1321mimeType.getParameter("humanPresentableName");1322mimeType.removeParameter("humanPresentableName");1323rcn = mimeType.getParameter("class");1324if (rcn == null) {1325throw new IOException("no class parameter specified in: " +1326mimeType);1327}1328}13291330try {1331representationClass = (Class)is.readObject();1332} catch (OptionalDataException ode) {1333if (!ode.eof || ode.length != 0) {1334throw ode;1335}1336// Ensure backward compatibility.1337// Old versions didn't write the representation class to the stream.1338if (rcn != null) {1339representationClass =1340DataFlavor.tryToLoadClass(rcn, getClass().getClassLoader());1341}1342}1343}13441345/**1346* Returns a clone of this {@code DataFlavor}.1347*1348* @return a clone of this {@code DataFlavor}1349*/1350public Object clone() throws CloneNotSupportedException {1351Object newObj = super.clone();1352if (mimeType != null) {1353((DataFlavor)newObj).mimeType = (MimeType)mimeType.clone();1354}1355return newObj;1356} // clone()13571358/**1359* Called on {@code DataFlavor} for every MIME Type parameter to allow1360* {@code DataFlavor} subclasses to handle special parameters like the1361* text/plain {@code charset} parameters, whose values are case insensitive.1362* (MIME type parameter values are supposed to be case sensitive.1363* <p>1364* This method is called for each parameter name/value pair and should1365* return the normalized representation of the {@code parameterValue}.1366*1367* @param parameterName the parameter name1368* @param parameterValue the parameter value1369* @return the parameter value1370* @deprecated This method is never invoked by this implementation from 1.11371* onwards1372*/1373@Deprecated1374protected String normalizeMimeTypeParameter(String parameterName, String parameterValue) {1375return parameterValue;1376}13771378/**1379* Called for each MIME type string to give {@code DataFlavor} subtypes the1380* opportunity to change how the normalization of MIME types is1381* accomplished. One possible use would be to add default parameter/value1382* pairs in cases where none are present in the MIME type string passed in.1383*1384* @param mimeType the mime type1385* @return the mime type1386* @deprecated This method is never invoked by this implementation from 1.11387* onwards1388*/1389@Deprecated1390protected String normalizeMimeType(String mimeType) {1391return mimeType;1392}13931394/*1395* fields1396*/13971398/* placeholder for caching any platform-specific data for flavor */13991400transient int atom;14011402/* Mime Type of DataFlavor */14031404MimeType mimeType;14051406private String humanPresentableName;14071408/**1409* Java class of objects this DataFlavor represents.1410**/1411private Class<?> representationClass;14121413} // class DataFlavor141414151416