View Javadoc
1   /*
2    * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package java.awt.datatransfer;
27  
28  import java.io.*;
29  import java.nio.*;
30  import java.util.*;
31  
32  import sun.awt.datatransfer.DataTransferer;
33  import sun.reflect.misc.ReflectUtil;
34  
35  import static sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION;
36  
37  /**
38   * A {@code DataFlavor} provides meta information about data. {@code DataFlavor}
39   * is typically used to access data on the clipboard, or during
40   * a drag and drop operation.
41   * <p>
42   * An instance of {@code DataFlavor} encapsulates a content type as
43   * defined in <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
44   * and <a href="http://www.ietf.org/rfc/rfc2046.txt">RFC 2046</a>.
45   * A content type is typically referred to as a MIME type.
46   * <p>
47   * A content type consists of a media type (referred
48   * to as the primary type), a subtype, and optional parameters. See
49   * <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
50   * for details on the syntax of a MIME type.
51   * <p>
52   * The JRE data transfer implementation interprets the parameter &quot;class&quot;
53   * of a MIME type as <B>a representation class</b>.
54   * The representation class reflects the class of the object being
55   * transferred. In other words, the representation class is the type of
56   * object returned by {@link Transferable#getTransferData}.
57   * For example, the MIME type of {@link #imageFlavor} is
58   * {@code "image/x-java-image;class=java.awt.Image"},
59   * the primary type is {@code image}, the subtype is
60   * {@code x-java-image}, and the representation class is
61   * {@code java.awt.Image}. When {@code getTransferData} is invoked
62   * with a {@code DataFlavor} of {@code imageFlavor}, an instance of
63   * {@code java.awt.Image} is returned.
64   * It's important to note that {@code DataFlavor} does no error checking
65   * against the representation class. It is up to consumers of
66   * {@code DataFlavor}, such as {@code Transferable}, to honor the representation
67   * class.
68   * <br>
69   * Note, if you do not specify a representation class when
70   * creating a {@code DataFlavor}, the default
71   * representation class is used. See appropriate documentation for
72   * {@code DataFlavor}'s constructors.
73   * <p>
74   * Also, {@code DataFlavor} instances with the &quot;text&quot; primary
75   * MIME type may have a &quot;charset&quot; parameter. Refer to
76   * <a href="http://www.ietf.org/rfc/rfc2046.txt">RFC 2046</a> and
77   * {@link #selectBestTextFlavor} for details on &quot;text&quot; MIME types
78   * and the &quot;charset&quot; parameter.
79   * <p>
80   * Equality of {@code DataFlavors} is determined by the primary type,
81   * subtype, and representation class. Refer to {@link #equals(DataFlavor)} for
82   * details. When determining equality, any optional parameters are ignored.
83   * For example, the following produces two {@code DataFlavors} that
84   * are considered identical:
85   * <pre>
86   *   DataFlavor flavor1 = new DataFlavor(Object.class, &quot;X-test/test; class=&lt;java.lang.Object&gt;; foo=bar&quot;);
87   *   DataFlavor flavor2 = new DataFlavor(Object.class, &quot;X-test/test; class=&lt;java.lang.Object&gt;; x=y&quot;);
88   *   // The following returns true.
89   *   flavor1.equals(flavor2);
90   * </pre>
91   * As mentioned, {@code flavor1} and {@code flavor2} are considered identical.
92   * As such, asking a {@code Transferable} for either {@code DataFlavor} returns
93   * the same results.
94   * <p>
95   * For more information on the using data transfer with Swing see
96   * the <a href="http://docs.oracle.com/javase/tutorial/uiswing/dnd/index.html">
97   * How to Use Drag and Drop and Data Transfer</a>,
98   * section in <em>Java Tutorial</em>.
99   *
100  * @author      Blake Sullivan
101  * @author      Laurence P. G. Cable
102  * @author      Jeff Dunn
103  */
104 public class DataFlavor implements Externalizable, Cloneable {
105 
106     private static final long serialVersionUID = 8367026044764648243L;
107     private static final Class<InputStream> ioInputStreamClass = InputStream.class;
108 
109     /**
110      * Tries to load a class from: the bootstrap loader, the system loader,
111      * the context loader (if one is present) and finally the loader specified.
112      *
113      * @param className the name of the class to be loaded
114      * @param fallback the fallback loader
115      * @return the class loaded
116      * @exception ClassNotFoundException if class is not found
117      */
118     protected final static Class<?> tryToLoadClass(String className,
119                                                    ClassLoader fallback)
120         throws ClassNotFoundException
121     {
122         ReflectUtil.checkPackageAccess(className);
123         try {
124             SecurityManager sm = System.getSecurityManager();
125             if (sm != null) {
126                 sm.checkPermission(GET_CLASSLOADER_PERMISSION);
127             }
128             ClassLoader loader = ClassLoader.getSystemClassLoader();
129             try {
130                 // bootstrap class loader and system class loader if present
131                 return Class.forName(className, true, loader);
132             }
133             catch (ClassNotFoundException exception) {
134                 // thread context class loader if and only if present
135                 loader = Thread.currentThread().getContextClassLoader();
136                 if (loader != null) {
137                     try {
138                         return Class.forName(className, true, loader);
139                     }
140                     catch (ClassNotFoundException e) {
141                         // fallback to user's class loader
142                     }
143                 }
144             }
145         } catch (SecurityException exception) {
146             // ignore secured class loaders
147         }
148         return Class.forName(className, true, fallback);
149     }
150 
151     /*
152      * private initializer
153      */
154     static private DataFlavor createConstant(Class<?> rc, String prn) {
155         try {
156             return new DataFlavor(rc, prn);
157         } catch (Exception e) {
158             return null;
159         }
160     }
161 
162     /*
163      * private initializer
164      */
165     static private DataFlavor createConstant(String mt, String prn) {
166         try {
167             return new DataFlavor(mt, prn);
168         } catch (Exception e) {
169             return null;
170         }
171     }
172 
173     /*
174      * private initializer
175      */
176     static private DataFlavor initHtmlDataFlavor(String htmlFlavorType) {
177         try {
178             return new DataFlavor ("text/html; class=java.lang.String;document=" +
179                                        htmlFlavorType + ";charset=Unicode");
180         } catch (Exception e) {
181             return null;
182         }
183     }
184 
185     /**
186      * The <code>DataFlavor</code> representing a Java Unicode String class,
187      * where:
188      * <pre>
189      *     representationClass = java.lang.String
190      *     mimeType           = "application/x-java-serialized-object"
191      * </pre>
192      */
193     public static final DataFlavor stringFlavor = createConstant(java.lang.String.class, "Unicode String");
194 
195     /**
196      * The <code>DataFlavor</code> representing a Java Image class,
197      * where:
198      * <pre>
199      *     representationClass = java.awt.Image
200      *     mimeType            = "image/x-java-image"
201      * </pre>
202      */
203     public static final DataFlavor imageFlavor = createConstant("image/x-java-image; class=java.awt.Image", "Image");
204 
205     /**
206      * The <code>DataFlavor</code> representing plain text with Unicode
207      * encoding, where:
208      * <pre>
209      *     representationClass = InputStream
210      *     mimeType            = "text/plain; charset=unicode"
211      * </pre>
212      * This <code>DataFlavor</code> has been <b>deprecated</b> because
213      * (1) Its representation is an InputStream, an 8-bit based representation,
214      * while Unicode is a 16-bit character set; and (2) The charset "unicode"
215      * is not well-defined. "unicode" implies a particular platform's
216      * implementation of Unicode, not a cross-platform implementation.
217      *
218      * @deprecated as of 1.3. Use <code>DataFlavor.getReaderForText(Transferable)</code>
219      *             instead of <code>Transferable.getTransferData(DataFlavor.plainTextFlavor)</code>.
220      */
221     @Deprecated
222     public static final DataFlavor plainTextFlavor = createConstant("text/plain; charset=unicode; class=java.io.InputStream", "Plain Text");
223 
224     /**
225      * A MIME Content-Type of application/x-java-serialized-object represents
226      * a graph of Java object(s) that have been made persistent.
227      *
228      * The representation class associated with this <code>DataFlavor</code>
229      * identifies the Java type of an object returned as a reference
230      * from an invocation <code>java.awt.datatransfer.getTransferData</code>.
231      */
232     public static final String javaSerializedObjectMimeType = "application/x-java-serialized-object";
233 
234     /**
235      * To transfer a list of files to/from Java (and the underlying
236      * platform) a <code>DataFlavor</code> of this type/subtype and
237      * representation class of <code>java.util.List</code> is used.
238      * Each element of the list is required/guaranteed to be of type
239      * <code>java.io.File</code>.
240      */
241     public static final DataFlavor javaFileListFlavor = createConstant("application/x-java-file-list;class=java.util.List", null);
242 
243     /**
244      * To transfer a reference to an arbitrary Java object reference that
245      * has no associated MIME Content-type, across a <code>Transferable</code>
246      * interface WITHIN THE SAME JVM, a <code>DataFlavor</code>
247      * with this type/subtype is used, with a <code>representationClass</code>
248      * equal to the type of the class/interface being passed across the
249      * <code>Transferable</code>.
250      * <p>
251      * The object reference returned from
252      * <code>Transferable.getTransferData</code> for a <code>DataFlavor</code>
253      * with this MIME Content-Type is required to be
254      * an instance of the representation Class of the <code>DataFlavor</code>.
255      */
256     public static final String javaJVMLocalObjectMimeType = "application/x-java-jvm-local-objectref";
257 
258     /**
259      * In order to pass a live link to a Remote object via a Drag and Drop
260      * <code>ACTION_LINK</code> operation a Mime Content Type of
261      * application/x-java-remote-object should be used,
262      * where the representation class of the <code>DataFlavor</code>
263      * represents the type of the <code>Remote</code> interface to be
264      * transferred.
265      */
266     public static final String javaRemoteObjectMimeType = "application/x-java-remote-object";
267 
268     /**
269      * Represents a piece of an HTML markup. The markup consists of the part
270      * selected on the source side. Therefore some tags in the markup may be
271      * unpaired. If the flavor is used to represent the data in
272      * a {@link Transferable} instance, no additional changes will be made.
273      * This DataFlavor instance represents the same HTML markup as DataFlavor
274      * instances which content MIME type does not contain document parameter
275      * and representation class is the String class.
276      * <pre>
277      *     representationClass = String
278      *     mimeType           = "text/html"
279      * </pre>
280      */
281     public static DataFlavor selectionHtmlFlavor = initHtmlDataFlavor("selection");
282 
283     /**
284      * Represents a piece of an HTML markup. If possible, the markup received
285      * from a native system is supplemented with pair tags to be
286      * a well-formed HTML markup. If the flavor is used to represent the data in
287      * a {@link Transferable} instance, no additional changes will be made.
288      * <pre>
289      *     representationClass = String
290      *     mimeType           = "text/html"
291      * </pre>
292      */
293     public static DataFlavor fragmentHtmlFlavor = initHtmlDataFlavor("fragment");
294 
295     /**
296      * Represents a piece of an HTML markup. If possible, the markup
297      * received from a native system is supplemented with additional
298      * tags to make up a well-formed HTML document. If the flavor is used to
299      * represent the data in a {@link Transferable} instance,
300      * no additional changes will be made.
301      * <pre>
302      *     representationClass = String
303      *     mimeType           = "text/html"
304      * </pre>
305      */
306     public static  DataFlavor allHtmlFlavor = initHtmlDataFlavor("all");
307 
308     /**
309      * Constructs a new <code>DataFlavor</code>.  This constructor is
310      * provided only for the purpose of supporting the
311      * <code>Externalizable</code> interface.  It is not
312      * intended for public (client) use.
313      *
314      * @since 1.2
315      */
316     public DataFlavor() {
317         super();
318     }
319 
320     /**
321      * Constructs a fully specified <code>DataFlavor</code>.
322      *
323      * @exception NullPointerException if either <code>primaryType</code>,
324      *            <code>subType</code> or <code>representationClass</code> is null
325      */
326     private DataFlavor(String primaryType, String subType, MimeTypeParameterList params, Class<?> representationClass, String humanPresentableName) {
327         super();
328         if (primaryType == null) {
329             throw new NullPointerException("primaryType");
330         }
331         if (subType == null) {
332             throw new NullPointerException("subType");
333         }
334         if (representationClass == null) {
335             throw new NullPointerException("representationClass");
336         }
337 
338         if (params == null) params = new MimeTypeParameterList();
339 
340         params.set("class", representationClass.getName());
341 
342         if (humanPresentableName == null) {
343             humanPresentableName = params.get("humanPresentableName");
344 
345             if (humanPresentableName == null)
346                 humanPresentableName = primaryType + "/" + subType;
347         }
348 
349         try {
350             mimeType = new MimeType(primaryType, subType, params);
351         } catch (MimeTypeParseException mtpe) {
352             throw new IllegalArgumentException("MimeType Parse Exception: " + mtpe.getMessage());
353         }
354 
355         this.representationClass  = representationClass;
356         this.humanPresentableName = humanPresentableName;
357 
358         mimeType.removeParameter("humanPresentableName");
359     }
360 
361     /**
362      * Constructs a <code>DataFlavor</code> that represents a Java class.
363      * <p>
364      * The returned <code>DataFlavor</code> will have the following
365      * characteristics:
366      * <pre>
367      *    representationClass = representationClass
368      *    mimeType            = application/x-java-serialized-object
369      * </pre>
370      * @param representationClass the class used to transfer data in this flavor
371      * @param humanPresentableName the human-readable string used to identify
372      *                 this flavor; if this parameter is <code>null</code>
373      *                 then the value of the the MIME Content Type is used
374      * @exception NullPointerException if <code>representationClass</code> is null
375      */
376     public DataFlavor(Class<?> representationClass, String humanPresentableName) {
377         this("application", "x-java-serialized-object", null, representationClass, humanPresentableName);
378         if (representationClass == null) {
379             throw new NullPointerException("representationClass");
380         }
381     }
382 
383     /**
384      * Constructs a <code>DataFlavor</code> that represents a
385      * <code>MimeType</code>.
386      * <p>
387      * The returned <code>DataFlavor</code> will have the following
388      * characteristics:
389      * <p>
390      * If the <code>mimeType</code> is
391      * "application/x-java-serialized-object; class=&lt;representation class&gt;",
392      * the result is the same as calling
393      * <code>new DataFlavor(Class:forName(&lt;representation class&gt;)</code>.
394      * <p>
395      * Otherwise:
396      * <pre>
397      *     representationClass = InputStream
398      *     mimeType            = mimeType
399      * </pre>
400      * @param mimeType the string used to identify the MIME type for this flavor;
401      *                 if the the <code>mimeType</code> does not specify a
402      *                 "class=" parameter, or if the class is not successfully
403      *                 loaded, then an <code>IllegalArgumentException</code>
404      *                 is thrown
405      * @param humanPresentableName the human-readable string used to identify
406      *                 this flavor; if this parameter is <code>null</code>
407      *                 then the value of the the MIME Content Type is used
408      * @exception IllegalArgumentException if <code>mimeType</code> is
409      *                 invalid or if the class is not successfully loaded
410      * @exception NullPointerException if <code>mimeType</code> is null
411      */
412     public DataFlavor(String mimeType, String humanPresentableName) {
413         super();
414         if (mimeType == null) {
415             throw new NullPointerException("mimeType");
416         }
417         try {
418             initialize(mimeType, humanPresentableName, this.getClass().getClassLoader());
419         } catch (MimeTypeParseException mtpe) {
420             throw new IllegalArgumentException("failed to parse:" + mimeType);
421         } catch (ClassNotFoundException cnfe) {
422             throw new IllegalArgumentException("can't find specified class: " + cnfe.getMessage());
423         }
424     }
425 
426     /**
427      * Constructs a <code>DataFlavor</code> that represents a
428      * <code>MimeType</code>.
429      * <p>
430      * The returned <code>DataFlavor</code> will have the following
431      * characteristics:
432      * <p>
433      * If the mimeType is
434      * "application/x-java-serialized-object; class=&lt;representation class&gt;",
435      * the result is the same as calling
436      * <code>new DataFlavor(Class:forName(&lt;representation class&gt;)</code>.
437      * <p>
438      * Otherwise:
439      * <pre>
440      *     representationClass = InputStream
441      *     mimeType            = mimeType
442      * </pre>
443      * @param mimeType the string used to identify the MIME type for this flavor
444      * @param humanPresentableName the human-readable string used to
445      *          identify this flavor
446      * @param classLoader the class loader to use
447      * @exception ClassNotFoundException if the class is not loaded
448      * @exception IllegalArgumentException if <code>mimeType</code> is
449      *                 invalid
450      * @exception NullPointerException if <code>mimeType</code> is null
451      */
452     public DataFlavor(String mimeType, String humanPresentableName, ClassLoader classLoader) throws ClassNotFoundException {
453         super();
454         if (mimeType == null) {
455             throw new NullPointerException("mimeType");
456         }
457         try {
458             initialize(mimeType, humanPresentableName, classLoader);
459         } catch (MimeTypeParseException mtpe) {
460             throw new IllegalArgumentException("failed to parse:" + mimeType);
461         }
462     }
463 
464     /**
465      * Constructs a <code>DataFlavor</code> from a <code>mimeType</code> string.
466      * The string can specify a "class=&lt;fully specified Java class name&gt;"
467      * parameter to create a <code>DataFlavor</code> with the desired
468      * representation class. If the string does not contain "class=" parameter,
469      * <code>java.io.InputStream</code> is used as default.
470      *
471      * @param mimeType the string used to identify the MIME type for this flavor;
472      *                 if the class specified by "class=" parameter is not
473      *                 successfully loaded, then an
474      *                 <code>ClassNotFoundException</code> is thrown
475      * @exception ClassNotFoundException if the class is not loaded
476      * @exception IllegalArgumentException if <code>mimeType</code> is
477      *                 invalid
478      * @exception NullPointerException if <code>mimeType</code> is null
479      */
480     public DataFlavor(String mimeType) throws ClassNotFoundException {
481         super();
482         if (mimeType == null) {
483             throw new NullPointerException("mimeType");
484         }
485         try {
486             initialize(mimeType, null, this.getClass().getClassLoader());
487         } catch (MimeTypeParseException mtpe) {
488             throw new IllegalArgumentException("failed to parse:" + mimeType);
489         }
490     }
491 
492    /**
493     * Common initialization code called from various constructors.
494     *
495     * @param mimeType the MIME Content Type (must have a class= param)
496     * @param humanPresentableName the human Presentable Name or
497     *                 <code>null</code>
498     * @param classLoader the fallback class loader to resolve against
499     *
500     * @throws MimeTypeParseException
501     * @throws ClassNotFoundException
502     * @throws  NullPointerException if <code>mimeType</code> is null
503     *
504     * @see tryToLoadClass
505     */
506     private void initialize(String mimeType, String humanPresentableName, ClassLoader classLoader) throws MimeTypeParseException, ClassNotFoundException {
507         if (mimeType == null) {
508             throw new NullPointerException("mimeType");
509         }
510 
511         this.mimeType = new MimeType(mimeType); // throws
512 
513         String rcn = getParameter("class");
514 
515         if (rcn == null) {
516             if ("application/x-java-serialized-object".equals(this.mimeType.getBaseType()))
517 
518                 throw new IllegalArgumentException("no representation class specified for:" + mimeType);
519             else
520                 representationClass = java.io.InputStream.class; // default
521         } else { // got a class name
522             representationClass = DataFlavor.tryToLoadClass(rcn, classLoader);
523         }
524 
525         this.mimeType.setParameter("class", representationClass.getName());
526 
527         if (humanPresentableName == null) {
528             humanPresentableName = this.mimeType.getParameter("humanPresentableName");
529             if (humanPresentableName == null)
530                 humanPresentableName = this.mimeType.getPrimaryType() + "/" + this.mimeType.getSubType();
531         }
532 
533         this.humanPresentableName = humanPresentableName; // set it.
534 
535         this.mimeType.removeParameter("humanPresentableName"); // just in case
536     }
537 
538     /**
539      * String representation of this <code>DataFlavor</code> and its
540      * parameters. The resulting <code>String</code> contains the name of
541      * the <code>DataFlavor</code> class, this flavor's MIME type, and its
542      * representation class. If this flavor has a primary MIME type of "text",
543      * supports the charset parameter, and has an encoded representation, the
544      * flavor's charset is also included. See <code>selectBestTextFlavor</code>
545      * for a list of text flavors which support the charset parameter.
546      *
547      * @return  string representation of this <code>DataFlavor</code>
548      * @see #selectBestTextFlavor
549      */
550     public String toString() {
551         String string = getClass().getName();
552         string += "["+paramString()+"]";
553         return string;
554     }
555 
556     private String paramString() {
557         String params = "";
558         params += "mimetype=";
559         if (mimeType == null) {
560             params += "null";
561         } else {
562             params += mimeType.getBaseType();
563         }
564         params += ";representationclass=";
565         if (representationClass == null) {
566            params += "null";
567         } else {
568            params += representationClass.getName();
569         }
570         if (DataTransferer.isFlavorCharsetTextType(this) &&
571             (isRepresentationClassInputStream() ||
572              isRepresentationClassByteBuffer() ||
573              DataTransferer.byteArrayClass.equals(representationClass)))
574         {
575             params += ";charset=" + DataTransferer.getTextCharset(this);
576         }
577         return params;
578     }
579 
580     /**
581      * Returns a <code>DataFlavor</code> representing plain text with Unicode
582      * encoding, where:
583      * <pre>
584      *     representationClass = java.io.InputStream
585      *     mimeType            = "text/plain;
586      *                            charset=&lt;platform default Unicode encoding&gt;"
587      * </pre>
588      * Sun's implementation for Microsoft Windows uses the encoding <code>utf-16le</code>.
589      * Sun's implementation for Solaris and Linux uses the encoding
590      * <code>iso-10646-ucs-2</code>.
591      *
592      * @return a <code>DataFlavor</code> representing plain text
593      *    with Unicode encoding
594      * @since 1.3
595      */
596     public static final DataFlavor getTextPlainUnicodeFlavor() {
597         String encoding = null;
598         DataTransferer transferer = DataTransferer.getInstance();
599         if (transferer != null) {
600             encoding = transferer.getDefaultUnicodeEncoding();
601         }
602         return new DataFlavor(
603             "text/plain;charset="+encoding
604             +";class=java.io.InputStream", "Plain Text");
605     }
606 
607     /**
608      * Selects the best text <code>DataFlavor</code> from an array of <code>
609      * DataFlavor</code>s. Only <code>DataFlavor.stringFlavor</code>, and
610      * equivalent flavors, and flavors that have a primary MIME type of "text",
611      * are considered for selection.
612      * <p>
613      * Flavors are first sorted by their MIME types in the following order:
614      * <ul>
615      * <li>"text/sgml"
616      * <li>"text/xml"
617      * <li>"text/html"
618      * <li>"text/rtf"
619      * <li>"text/enriched"
620      * <li>"text/richtext"
621      * <li>"text/uri-list"
622      * <li>"text/tab-separated-values"
623      * <li>"text/t140"
624      * <li>"text/rfc822-headers"
625      * <li>"text/parityfec"
626      * <li>"text/directory"
627      * <li>"text/css"
628      * <li>"text/calendar"
629      * <li>"application/x-java-serialized-object"
630      * <li>"text/plain"
631      * <li>"text/&lt;other&gt;"
632      * </ul>
633      * <p>For example, "text/sgml" will be selected over
634      * "text/html", and <code>DataFlavor.stringFlavor</code> will be chosen
635      * over <code>DataFlavor.plainTextFlavor</code>.
636      * <p>
637      * If two or more flavors share the best MIME type in the array, then that
638      * MIME type will be checked to see if it supports the charset parameter.
639      * <p>
640      * The following MIME types support, or are treated as though they support,
641      * the charset parameter:
642      * <ul>
643      * <li>"text/sgml"
644      * <li>"text/xml"
645      * <li>"text/html"
646      * <li>"text/enriched"
647      * <li>"text/richtext"
648      * <li>"text/uri-list"
649      * <li>"text/directory"
650      * <li>"text/css"
651      * <li>"text/calendar"
652      * <li>"application/x-java-serialized-object"
653      * <li>"text/plain"
654      * </ul>
655      * The following MIME types do not support, or are treated as though they
656      * do not support, the charset parameter:
657      * <ul>
658      * <li>"text/rtf"
659      * <li>"text/tab-separated-values"
660      * <li>"text/t140"
661      * <li>"text/rfc822-headers"
662      * <li>"text/parityfec"
663      * </ul>
664      * For "text/&lt;other&gt;" MIME types, the first time the JRE needs to
665      * determine whether the MIME type supports the charset parameter, it will
666      * check whether the parameter is explicitly listed in an arbitrarily
667      * chosen <code>DataFlavor</code> which uses that MIME type. If so, the JRE
668      * will assume from that point on that the MIME type supports the charset
669      * parameter and will not check again. If the parameter is not explicitly
670      * listed, the JRE will assume from that point on that the MIME type does
671      * not support the charset parameter and will not check again. Because
672      * this check is performed on an arbitrarily chosen
673      * <code>DataFlavor</code>, developers must ensure that all
674      * <code>DataFlavor</code>s with a "text/&lt;other&gt;" MIME type specify
675      * the charset parameter if it is supported by that MIME type. Developers
676      * should never rely on the JRE to substitute the platform's default
677      * charset for a "text/&lt;other&gt;" DataFlavor. Failure to adhere to this
678      * restriction will lead to undefined behavior.
679      * <p>
680      * If the best MIME type in the array does not support the charset
681      * parameter, the flavors which share that MIME type will then be sorted by
682      * their representation classes in the following order:
683      * <code>java.io.InputStream</code>, <code>java.nio.ByteBuffer</code>,
684      * <code>[B</code>, &lt;all others&gt;.
685      * <p>
686      * If two or more flavors share the best representation class, or if no
687      * flavor has one of the three specified representations, then one of those
688      * flavors will be chosen non-deterministically.
689      * <p>
690      * If the best MIME type in the array does support the charset parameter,
691      * the flavors which share that MIME type will then be sorted by their
692      * representation classes in the following order:
693      * <code>java.io.Reader</code>, <code>java.lang.String</code>,
694      * <code>java.nio.CharBuffer</code>, <code>[C</code>, &lt;all others&gt;.
695      * <p>
696      * If two or more flavors share the best representation class, and that
697      * representation is one of the four explicitly listed, then one of those
698      * flavors will be chosen non-deterministically. If, however, no flavor has
699      * one of the four specified representations, the flavors will then be
700      * sorted by their charsets. Unicode charsets, such as "UTF-16", "UTF-8",
701      * "UTF-16BE", "UTF-16LE", and their aliases, are considered best. After
702      * them, the platform default charset and its aliases are selected.
703      * "US-ASCII" and its aliases are worst. All other charsets are chosen in
704      * alphabetical order, but only charsets supported by this implementation
705      * of the Java platform will be considered.
706      * <p>
707      * If two or more flavors share the best charset, the flavors will then
708      * again be sorted by their representation classes in the following order:
709      * <code>java.io.InputStream</code>, <code>java.nio.ByteBuffer</code>,
710      * <code>[B</code>, &lt;all others&gt;.
711      * <p>
712      * If two or more flavors share the best representation class, or if no
713      * flavor has one of the three specified representations, then one of those
714      * flavors will be chosen non-deterministically.
715      *
716      * @param availableFlavors an array of available <code>DataFlavor</code>s
717      * @return the best (highest fidelity) flavor according to the rules
718      *         specified above, or <code>null</code>,
719      *         if <code>availableFlavors</code> is <code>null</code>,
720      *         has zero length, or contains no text flavors
721      * @since 1.3
722      */
723     public static final DataFlavor selectBestTextFlavor(
724                                        DataFlavor[] availableFlavors) {
725         if (availableFlavors == null || availableFlavors.length == 0) {
726             return null;
727         }
728 
729         if (textFlavorComparator == null) {
730             textFlavorComparator = new TextFlavorComparator();
731         }
732 
733         DataFlavor bestFlavor =
734             (DataFlavor)Collections.max(Arrays.asList(availableFlavors),
735                                         textFlavorComparator);
736 
737         if (!bestFlavor.isFlavorTextType()) {
738             return null;
739         }
740 
741         return bestFlavor;
742     }
743 
744     private static Comparator<DataFlavor> textFlavorComparator;
745 
746     static class TextFlavorComparator
747         extends DataTransferer.DataFlavorComparator {
748 
749         /**
750          * Compares two <code>DataFlavor</code> objects. Returns a negative
751          * integer, zero, or a positive integer as the first
752          * <code>DataFlavor</code> is worse than, equal to, or better than the
753          * second.
754          * <p>
755          * <code>DataFlavor</code>s are ordered according to the rules outlined
756          * for <code>selectBestTextFlavor</code>.
757          *
758          * @param obj1 the first <code>DataFlavor</code> to be compared
759          * @param obj2 the second <code>DataFlavor</code> to be compared
760          * @return a negative integer, zero, or a positive integer as the first
761          *         argument is worse, equal to, or better than the second
762          * @throws ClassCastException if either of the arguments is not an
763          *         instance of <code>DataFlavor</code>
764          * @throws NullPointerException if either of the arguments is
765          *         <code>null</code>
766          *
767          * @see #selectBestTextFlavor
768          */
769         public int compare(Object obj1, Object obj2) {
770             DataFlavor flavor1 = (DataFlavor)obj1;
771             DataFlavor flavor2 = (DataFlavor)obj2;
772 
773             if (flavor1.isFlavorTextType()) {
774                 if (flavor2.isFlavorTextType()) {
775                     return super.compare(obj1, obj2);
776                 } else {
777                     return 1;
778                 }
779             } else if (flavor2.isFlavorTextType()) {
780                 return -1;
781             } else {
782                 return 0;
783             }
784         }
785     }
786 
787     /**
788      * Gets a Reader for a text flavor, decoded, if necessary, for the expected
789      * charset (encoding). The supported representation classes are
790      * <code>java.io.Reader</code>, <code>java.lang.String</code>,
791      * <code>java.nio.CharBuffer</code>, <code>[C</code>,
792      * <code>java.io.InputStream</code>, <code>java.nio.ByteBuffer</code>,
793      * and <code>[B</code>.
794      * <p>
795      * Because text flavors which do not support the charset parameter are
796      * encoded in a non-standard format, this method should not be called for
797      * such flavors. However, in order to maintain backward-compatibility,
798      * if this method is called for such a flavor, this method will treat the
799      * flavor as though it supports the charset parameter and attempt to
800      * decode it accordingly. See <code>selectBestTextFlavor</code> for a list
801      * of text flavors which do not support the charset parameter.
802      *
803      * @param transferable the <code>Transferable</code> whose data will be
804      *        requested in this flavor
805      *
806      * @return a <code>Reader</code> to read the <code>Transferable</code>'s
807      *         data
808      *
809      * @exception IllegalArgumentException if the representation class
810      *            is not one of the seven listed above
811      * @exception IllegalArgumentException if the <code>Transferable</code>
812      *            has <code>null</code> data
813      * @exception NullPointerException if the <code>Transferable</code> is
814      *            <code>null</code>
815      * @exception UnsupportedEncodingException if this flavor's representation
816      *            is <code>java.io.InputStream</code>,
817      *            <code>java.nio.ByteBuffer</code>, or <code>[B</code> and
818      *            this flavor's encoding is not supported by this
819      *            implementation of the Java platform
820      * @exception UnsupportedFlavorException if the <code>Transferable</code>
821      *            does not support this flavor
822      * @exception IOException if the data cannot be read because of an
823      *            I/O error
824      * @see #selectBestTextFlavor
825      * @since 1.3
826      */
827     public Reader getReaderForText(Transferable transferable)
828         throws UnsupportedFlavorException, IOException
829     {
830         Object transferObject = transferable.getTransferData(this);
831         if (transferObject == null) {
832             throw new IllegalArgumentException
833                 ("getTransferData() returned null");
834         }
835 
836         if (transferObject instanceof Reader) {
837             return (Reader)transferObject;
838         } else if (transferObject instanceof String) {
839             return new StringReader((String)transferObject);
840         } else if (transferObject instanceof CharBuffer) {
841             CharBuffer buffer = (CharBuffer)transferObject;
842             int size = buffer.remaining();
843             char[] chars = new char[size];
844             buffer.get(chars, 0, size);
845             return new CharArrayReader(chars);
846         } else if (transferObject instanceof char[]) {
847             return new CharArrayReader((char[])transferObject);
848         }
849 
850         InputStream stream = null;
851 
852         if (transferObject instanceof InputStream) {
853             stream = (InputStream)transferObject;
854         } else if (transferObject instanceof ByteBuffer) {
855             ByteBuffer buffer = (ByteBuffer)transferObject;
856             int size = buffer.remaining();
857             byte[] bytes = new byte[size];
858             buffer.get(bytes, 0, size);
859             stream = new ByteArrayInputStream(bytes);
860         } else if (transferObject instanceof byte[]) {
861             stream = new ByteArrayInputStream((byte[])transferObject);
862         }
863 
864         if (stream == null) {
865             throw new IllegalArgumentException("transfer data is not Reader, String, CharBuffer, char array, InputStream, ByteBuffer, or byte array");
866         }
867 
868         String encoding = getParameter("charset");
869         return (encoding == null)
870             ? new InputStreamReader(stream)
871             : new InputStreamReader(stream, encoding);
872     }
873 
874     /**
875      * Returns the MIME type string for this <code>DataFlavor</code>.
876      * @return the MIME type string for this flavor
877      */
878     public String getMimeType() {
879         return (mimeType != null) ? mimeType.toString() : null;
880     }
881 
882     /**
883      * Returns the <code>Class</code> which objects supporting this
884      * <code>DataFlavor</code> will return when this <code>DataFlavor</code>
885      * is requested.
886      * @return the <code>Class</code> which objects supporting this
887      * <code>DataFlavor</code> will return when this <code>DataFlavor</code>
888      * is requested
889      */
890     public Class<?> getRepresentationClass() {
891         return representationClass;
892     }
893 
894     /**
895      * Returns the human presentable name for the data format that this
896      * <code>DataFlavor</code> represents.  This name would be localized
897      * for different countries.
898      * @return the human presentable name for the data format that this
899      *    <code>DataFlavor</code> represents
900      */
901     public String getHumanPresentableName() {
902         return humanPresentableName;
903     }
904 
905     /**
906      * Returns the primary MIME type for this <code>DataFlavor</code>.
907      * @return the primary MIME type of this <code>DataFlavor</code>
908      */
909     public String getPrimaryType() {
910         return (mimeType != null) ? mimeType.getPrimaryType() : null;
911     }
912 
913     /**
914      * Returns the sub MIME type of this <code>DataFlavor</code>.
915      * @return the Sub MIME type of this <code>DataFlavor</code>
916      */
917     public String getSubType() {
918         return (mimeType != null) ? mimeType.getSubType() : null;
919     }
920 
921     /**
922      * Returns the human presentable name for this <code>DataFlavor</code>
923      * if <code>paramName</code> equals "humanPresentableName".  Otherwise
924      * returns the MIME type value associated with <code>paramName</code>.
925      *
926      * @param paramName the parameter name requested
927      * @return the value of the name parameter, or <code>null</code>
928      *  if there is no associated value
929      */
930     public String getParameter(String paramName) {
931         if (paramName.equals("humanPresentableName")) {
932             return humanPresentableName;
933         } else {
934             return (mimeType != null)
935                 ? mimeType.getParameter(paramName) : null;
936         }
937     }
938 
939     /**
940      * Sets the human presentable name for the data format that this
941      * <code>DataFlavor</code> represents. This name would be localized
942      * for different countries.
943      * @param humanPresentableName the new human presentable name
944      */
945     public void setHumanPresentableName(String humanPresentableName) {
946         this.humanPresentableName = humanPresentableName;
947     }
948 
949     /**
950      * {@inheritDoc}
951      * <p>
952      * The equals comparison for the {@code DataFlavor} class is implemented
953      * as follows: Two <code>DataFlavor</code>s are considered equal if and
954      * only if their MIME primary type and subtype and representation class are
955      * equal. Additionally, if the primary type is "text", the subtype denotes
956      * a text flavor which supports the charset parameter, and the
957      * representation class is not <code>java.io.Reader</code>,
958      * <code>java.lang.String</code>, <code>java.nio.CharBuffer</code>, or
959      * <code>[C</code>, the <code>charset</code> parameter must also be equal.
960      * If a charset is not explicitly specified for one or both
961      * <code>DataFlavor</code>s, the platform default encoding is assumed. See
962      * <code>selectBestTextFlavor</code> for a list of text flavors which
963      * support the charset parameter.
964      *
965      * @param o the <code>Object</code> to compare with <code>this</code>
966      * @return <code>true</code> if <code>that</code> is equivalent to this
967      *         <code>DataFlavor</code>; <code>false</code> otherwise
968      * @see #selectBestTextFlavor
969      */
970     public boolean equals(Object o) {
971         return ((o instanceof DataFlavor) && equals((DataFlavor)o));
972     }
973 
974     /**
975      * This method has the same behavior as {@link #equals(Object)}.
976      * The only difference being that it takes a {@code DataFlavor} instance
977      * as a parameter.
978      *
979      * @param that the <code>DataFlavor</code> to compare with
980      *        <code>this</code>
981      * @return <code>true</code> if <code>that</code> is equivalent to this
982      *         <code>DataFlavor</code>; <code>false</code> otherwise
983      * @see #selectBestTextFlavor
984      */
985     public boolean equals(DataFlavor that) {
986         if (that == null) {
987             return false;
988         }
989         if (this == that) {
990             return true;
991         }
992 
993         if (representationClass == null) {
994             if (that.getRepresentationClass() != null) {
995                 return false;
996             }
997         } else {
998             if (!representationClass.equals(that.getRepresentationClass())) {
999                 return false;
1000             }
1001         }
1002 
1003         if (mimeType == null) {
1004             if (that.mimeType != null) {
1005                 return false;
1006             }
1007         } else {
1008             if (!mimeType.match(that.mimeType)) {
1009                 return false;
1010             }
1011 
1012             if ("text".equals(getPrimaryType())) {
1013                 if (DataTransferer.doesSubtypeSupportCharset(this) &&
1014                     representationClass != null &&
1015                     !(isRepresentationClassReader() ||
1016                         String.class.equals(representationClass) ||
1017                         isRepresentationClassCharBuffer() ||
1018                         DataTransferer.charArrayClass.equals(representationClass)))
1019                 {
1020                     String thisCharset =
1021                         DataTransferer.canonicalName(getParameter("charset"));
1022                     String thatCharset =
1023                         DataTransferer.canonicalName(that.getParameter("charset"));
1024                     if (thisCharset == null) {
1025                         if (thatCharset != null) {
1026                             return false;
1027                         }
1028                     } else {
1029                         if (!thisCharset.equals(thatCharset)) {
1030                             return false;
1031                         }
1032                     }
1033                 }
1034 
1035                 if ("html".equals(getSubType()) &&
1036                         this.getParameter("document") != null )
1037                 {
1038                    if (!this.getParameter("document").
1039                             equals(that.getParameter("document")))
1040                     {
1041                         return false;
1042                     }
1043                 }
1044             }
1045         }
1046 
1047         return true;
1048     }
1049 
1050     /**
1051      * Compares only the <code>mimeType</code> against the passed in
1052      * <code>String</code> and <code>representationClass</code> is
1053      * not considered in the comparison.
1054      *
1055      * If <code>representationClass</code> needs to be compared, then
1056      * <code>equals(new DataFlavor(s))</code> may be used.
1057      * @deprecated As inconsistent with <code>hashCode()</code> contract,
1058      *             use <code>isMimeTypeEqual(String)</code> instead.
1059      * @param s the {@code mimeType} to compare.
1060      * @return true if the String (MimeType) is equal; false otherwise or if
1061      *         {@code s} is {@code null}
1062      */
1063     @Deprecated
1064     public boolean equals(String s) {
1065         if (s == null || mimeType == null)
1066             return false;
1067         return isMimeTypeEqual(s);
1068     }
1069 
1070     /**
1071      * Returns hash code for this <code>DataFlavor</code>.
1072      * For two equal <code>DataFlavor</code>s, hash codes are equal.
1073      * For the <code>String</code>
1074      * that matches <code>DataFlavor.equals(String)</code>, it is not
1075      * guaranteed that <code>DataFlavor</code>'s hash code is equal
1076      * to the hash code of the <code>String</code>.
1077      *
1078      * @return a hash code for this <code>DataFlavor</code>
1079      */
1080     public int hashCode() {
1081         int total = 0;
1082 
1083         if (representationClass != null) {
1084             total += representationClass.hashCode();
1085         }
1086 
1087         if (mimeType != null) {
1088             String primaryType = mimeType.getPrimaryType();
1089             if (primaryType != null) {
1090                 total += primaryType.hashCode();
1091             }
1092 
1093             // Do not add subType.hashCode() to the total. equals uses
1094             // MimeType.match which reports a match if one or both of the
1095             // subTypes is '*', regardless of the other subType.
1096 
1097             if ("text".equals(primaryType) &&
1098                 DataTransferer.doesSubtypeSupportCharset(this) &&
1099                 representationClass != null &&
1100                 !(isRepresentationClassReader() ||
1101                   String.class.equals(representationClass) ||
1102                   isRepresentationClassCharBuffer() ||
1103                   DataTransferer.charArrayClass.equals
1104                   (representationClass)))
1105             {
1106                 String charset =
1107                     DataTransferer.canonicalName(getParameter("charset"));
1108                 if (charset != null) {
1109                     total += charset.hashCode();
1110                 }
1111             }
1112         }
1113 
1114         return total;
1115     }
1116 
1117     /**
1118      * Identical to {@link #equals(DataFlavor)}.
1119      *
1120      * @param that the <code>DataFlavor</code> to compare with
1121      *        <code>this</code>
1122      * @return <code>true</code> if <code>that</code> is equivalent to this
1123      *         <code>DataFlavor</code>; <code>false</code> otherwise
1124      * @see #selectBestTextFlavor
1125      * @since 1.3
1126      */
1127     public boolean match(DataFlavor that) {
1128         return equals(that);
1129     }
1130 
1131     /**
1132      * Returns whether the string representation of the MIME type passed in
1133      * is equivalent to the MIME type of this <code>DataFlavor</code>.
1134      * Parameters are not included in the comparison.
1135      *
1136      * @param mimeType the string representation of the MIME type
1137      * @return true if the string representation of the MIME type passed in is
1138      *         equivalent to the MIME type of this <code>DataFlavor</code>;
1139      *         false otherwise
1140      * @throws NullPointerException if mimeType is <code>null</code>
1141      */
1142     public boolean isMimeTypeEqual(String mimeType) {
1143         // JCK Test DataFlavor0117: if 'mimeType' is null, throw NPE
1144         if (mimeType == null) {
1145             throw new NullPointerException("mimeType");
1146         }
1147         if (this.mimeType == null) {
1148             return false;
1149         }
1150         try {
1151             return this.mimeType.match(new MimeType(mimeType));
1152         } catch (MimeTypeParseException mtpe) {
1153             return false;
1154         }
1155     }
1156 
1157     /**
1158      * Compares the <code>mimeType</code> of two <code>DataFlavor</code>
1159      * objects. No parameters are considered.
1160      *
1161      * @param dataFlavor the <code>DataFlavor</code> to be compared
1162      * @return true if the <code>MimeType</code>s are equal,
1163      *  otherwise false
1164      */
1165 
1166     public final boolean isMimeTypeEqual(DataFlavor dataFlavor) {
1167         return isMimeTypeEqual(dataFlavor.mimeType);
1168     }
1169 
1170     /**
1171      * Compares the <code>mimeType</code> of two <code>DataFlavor</code>
1172      * objects.  No parameters are considered.
1173      *
1174      * @return true if the <code>MimeType</code>s are equal,
1175      *  otherwise false
1176      */
1177 
1178     private boolean isMimeTypeEqual(MimeType mtype) {
1179         if (this.mimeType == null) {
1180             return (mtype == null);
1181         }
1182         return mimeType.match(mtype);
1183     }
1184 
1185    /**
1186     * Does the <code>DataFlavor</code> represent a serialized object?
1187     */
1188 
1189     public boolean isMimeTypeSerializedObject() {
1190         return isMimeTypeEqual(javaSerializedObjectMimeType);
1191     }
1192 
1193     public final Class<?> getDefaultRepresentationClass() {
1194         return ioInputStreamClass;
1195     }
1196 
1197     public final String getDefaultRepresentationClassAsString() {
1198         return getDefaultRepresentationClass().getName();
1199     }
1200 
1201    /**
1202     * Does the <code>DataFlavor</code> represent a
1203     * <code>java.io.InputStream</code>?
1204     */
1205 
1206     public boolean isRepresentationClassInputStream() {
1207         return ioInputStreamClass.isAssignableFrom(representationClass);
1208     }
1209 
1210     /**
1211      * Returns whether the representation class for this
1212      * <code>DataFlavor</code> is <code>java.io.Reader</code> or a subclass
1213      * thereof.
1214      *
1215      * @since 1.4
1216      */
1217     public boolean isRepresentationClassReader() {
1218         return java.io.Reader.class.isAssignableFrom(representationClass);
1219     }
1220 
1221     /**
1222      * Returns whether the representation class for this
1223      * <code>DataFlavor</code> is <code>java.nio.CharBuffer</code> or a
1224      * subclass thereof.
1225      *
1226      * @since 1.4
1227      */
1228     public boolean isRepresentationClassCharBuffer() {
1229         return java.nio.CharBuffer.class.isAssignableFrom(representationClass);
1230     }
1231 
1232     /**
1233      * Returns whether the representation class for this
1234      * <code>DataFlavor</code> is <code>java.nio.ByteBuffer</code> or a
1235      * subclass thereof.
1236      *
1237      * @since 1.4
1238      */
1239     public boolean isRepresentationClassByteBuffer() {
1240         return java.nio.ByteBuffer.class.isAssignableFrom(representationClass);
1241     }
1242 
1243    /**
1244     * Returns true if the representation class can be serialized.
1245     * @return true if the representation class can be serialized
1246     */
1247 
1248     public boolean isRepresentationClassSerializable() {
1249         return java.io.Serializable.class.isAssignableFrom(representationClass);
1250     }
1251 
1252    /**
1253     * Returns true if the representation class is <code>Remote</code>.
1254     * @return true if the representation class is <code>Remote</code>
1255     */
1256 
1257     public boolean isRepresentationClassRemote() {
1258         return DataTransferer.isRemote(representationClass);
1259     }
1260 
1261    /**
1262     * Returns true if the <code>DataFlavor</code> specified represents
1263     * a serialized object.
1264     * @return true if the <code>DataFlavor</code> specified represents
1265     *   a Serialized Object
1266     */
1267 
1268     public boolean isFlavorSerializedObjectType() {
1269         return isRepresentationClassSerializable() && isMimeTypeEqual(javaSerializedObjectMimeType);
1270     }
1271 
1272     /**
1273      * Returns true if the <code>DataFlavor</code> specified represents
1274      * a remote object.
1275      * @return true if the <code>DataFlavor</code> specified represents
1276      *  a Remote Object
1277      */
1278 
1279     public boolean isFlavorRemoteObjectType() {
1280         return isRepresentationClassRemote()
1281             && isRepresentationClassSerializable()
1282             && isMimeTypeEqual(javaRemoteObjectMimeType);
1283     }
1284 
1285 
1286    /**
1287     * Returns true if the <code>DataFlavor</code> specified represents
1288     * a list of file objects.
1289     * @return true if the <code>DataFlavor</code> specified represents
1290     *   a List of File objects
1291     */
1292 
1293    public boolean isFlavorJavaFileListType() {
1294         if (mimeType == null || representationClass == null)
1295             return false;
1296         return java.util.List.class.isAssignableFrom(representationClass) &&
1297                mimeType.match(javaFileListFlavor.mimeType);
1298 
1299    }
1300 
1301     /**
1302      * Returns whether this <code>DataFlavor</code> is a valid text flavor for
1303      * this implementation of the Java platform. Only flavors equivalent to
1304      * <code>DataFlavor.stringFlavor</code> and <code>DataFlavor</code>s with
1305      * a primary MIME type of "text" can be valid text flavors.
1306      * <p>
1307      * If this flavor supports the charset parameter, it must be equivalent to
1308      * <code>DataFlavor.stringFlavor</code>, or its representation must be
1309      * <code>java.io.Reader</code>, <code>java.lang.String</code>,
1310      * <code>java.nio.CharBuffer</code>, <code>[C</code>,
1311      * <code>java.io.InputStream</code>, <code>java.nio.ByteBuffer</code>, or
1312      * <code>[B</code>. If the representation is
1313      * <code>java.io.InputStream</code>, <code>java.nio.ByteBuffer</code>, or
1314      * <code>[B</code>, then this flavor's <code>charset</code> parameter must
1315      * be supported by this implementation of the Java platform. If a charset
1316      * is not specified, then the platform default charset, which is always
1317      * supported, is assumed.
1318      * <p>
1319      * If this flavor does not support the charset parameter, its
1320      * representation must be <code>java.io.InputStream</code>,
1321      * <code>java.nio.ByteBuffer</code>, or <code>[B</code>.
1322      * <p>
1323      * See <code>selectBestTextFlavor</code> for a list of text flavors which
1324      * support the charset parameter.
1325      *
1326      * @return <code>true</code> if this <code>DataFlavor</code> is a valid
1327      *         text flavor as described above; <code>false</code> otherwise
1328      * @see #selectBestTextFlavor
1329      * @since 1.4
1330      */
1331     public boolean isFlavorTextType() {
1332         return (DataTransferer.isFlavorCharsetTextType(this) ||
1333                 DataTransferer.isFlavorNoncharsetTextType(this));
1334     }
1335 
1336    /**
1337     * Serializes this <code>DataFlavor</code>.
1338     */
1339 
1340    public synchronized void writeExternal(ObjectOutput os) throws IOException {
1341        if (mimeType != null) {
1342            mimeType.setParameter("humanPresentableName", humanPresentableName);
1343            os.writeObject(mimeType);
1344            mimeType.removeParameter("humanPresentableName");
1345        } else {
1346            os.writeObject(null);
1347        }
1348 
1349        os.writeObject(representationClass);
1350    }
1351 
1352    /**
1353     * Restores this <code>DataFlavor</code> from a Serialized state.
1354     */
1355 
1356    public synchronized void readExternal(ObjectInput is) throws IOException , ClassNotFoundException {
1357        String rcn = null;
1358         mimeType = (MimeType)is.readObject();
1359 
1360         if (mimeType != null) {
1361             humanPresentableName =
1362                 mimeType.getParameter("humanPresentableName");
1363             mimeType.removeParameter("humanPresentableName");
1364             rcn = mimeType.getParameter("class");
1365             if (rcn == null) {
1366                 throw new IOException("no class parameter specified in: " +
1367                                       mimeType);
1368             }
1369         }
1370 
1371         try {
1372             representationClass = (Class)is.readObject();
1373         } catch (OptionalDataException ode) {
1374             if (!ode.eof || ode.length != 0) {
1375                 throw ode;
1376             }
1377             // Ensure backward compatibility.
1378             // Old versions didn't write the representation class to the stream.
1379             if (rcn != null) {
1380                 representationClass =
1381                     DataFlavor.tryToLoadClass(rcn, getClass().getClassLoader());
1382             }
1383         }
1384    }
1385 
1386    /**
1387     * Returns a clone of this <code>DataFlavor</code>.
1388     * @return a clone of this <code>DataFlavor</code>
1389     */
1390 
1391     public Object clone() throws CloneNotSupportedException {
1392         Object newObj = super.clone();
1393         if (mimeType != null) {
1394             ((DataFlavor)newObj).mimeType = (MimeType)mimeType.clone();
1395         }
1396         return newObj;
1397     } // clone()
1398 
1399    /**
1400     * Called on <code>DataFlavor</code> for every MIME Type parameter
1401     * to allow <code>DataFlavor</code> subclasses to handle special
1402     * parameters like the text/plain <code>charset</code>
1403     * parameters, whose values are case insensitive.  (MIME type parameter
1404     * values are supposed to be case sensitive.
1405     * <p>
1406     * This method is called for each parameter name/value pair and should
1407     * return the normalized representation of the <code>parameterValue</code>.
1408     *
1409     * This method is never invoked by this implementation from 1.1 onwards.
1410     *
1411     * @deprecated
1412     */
1413     @Deprecated
1414     protected String normalizeMimeTypeParameter(String parameterName, String parameterValue) {
1415         return parameterValue;
1416     }
1417 
1418    /**
1419     * Called for each MIME type string to give <code>DataFlavor</code> subtypes
1420     * the opportunity to change how the normalization of MIME types is
1421     * accomplished.  One possible use would be to add default
1422     * parameter/value pairs in cases where none are present in the MIME
1423     * type string passed in.
1424     *
1425     * This method is never invoked by this implementation from 1.1 onwards.
1426     *
1427     * @deprecated
1428     */
1429     @Deprecated
1430     protected String normalizeMimeType(String mimeType) {
1431         return mimeType;
1432     }
1433 
1434     /*
1435      * fields
1436      */
1437 
1438     /* placeholder for caching any platform-specific data for flavor */
1439 
1440     transient int       atom;
1441 
1442     /* Mime Type of DataFlavor */
1443 
1444     MimeType            mimeType;
1445 
1446     private String      humanPresentableName;
1447 
1448     /** Java class of objects this DataFlavor represents **/
1449 
1450     private Class<?>       representationClass;
1451 
1452 } // class DataFlavor