001/* DataFlavor.java -- A type of data to transfer via the clipboard.
002   Copyright (C) 1999, 2001, 2004, 2005, 2006 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package java.awt.datatransfer;
040
041import java.io.ByteArrayInputStream;
042import java.io.IOException;
043import java.io.InputStream;
044import java.io.InputStreamReader;
045import java.io.ObjectInput;
046import java.io.ObjectOutput;
047import java.io.OptionalDataException;
048import java.io.Reader;
049import java.io.Serializable;
050import java.io.StringReader;
051import java.io.UnsupportedEncodingException;
052import java.nio.ByteBuffer;
053import java.nio.CharBuffer;
054import java.nio.charset.Charset;
055import java.rmi.Remote;
056
057/**
058 * This class represents a particular data format used for transferring
059 * data via the clipboard.
060 *
061 * @author Aaron M. Renn (arenn@urbanophile.com)
062 */
063public class DataFlavor implements java.io.Externalizable, Cloneable
064{
065  static final long serialVersionUID = 8367026044764648243L;
066
067  // FIXME: Serialization: Need to write methods for.
068
069  /**
070   * This is the data flavor used for tranferring plain text.  The MIME
071   * type is "text/plain; charset=unicode".  The representation class
072   * is <code>java.io.InputStream</code>.
073   *
074   * @deprecated The charset unicode is platform specific and InputStream
075   * deals with bytes not chars. Use <code>getRederForText()</code>.
076   */
077  public static final DataFlavor plainTextFlavor = 
078    new DataFlavor("text/plain; charset=unicode; class=java.io.InputStream",
079                   "plain unicode text");
080
081  /**
082   * This is the data flavor used for transferring Java strings.  The
083   * MIME type is "application/x-java-serialized-object" and the 
084   * representation class is <code>java.lang.String</code>.
085   */
086  public static final DataFlavor stringFlavor = 
087    new DataFlavor(java.lang.String.class, "Java Unicode String");
088
089  /**
090   * This is a data flavor used for transferring lists of files.  The
091   * representation type is a <code>java.util.List</code>, with each 
092   * element of the list being a <code>java.io.File</code>.
093   */
094  public static final DataFlavor javaFileListFlavor = 
095    new DataFlavor("application/x-java-file-list; class=java.util.List",
096                   "Java File List");
097
098  /**
099   * This is an image flavor used for transferring images.  The
100   * representation type is a <code>java.awt.Image</code>.
101   */
102  public static final DataFlavor imageFlavor = 
103    new DataFlavor(java.awt.Image.class, "Java Image");
104
105  /**
106   * This is the MIME type used for transferring a serialized object.
107   * The representation class is the type of object be deserialized.
108   */
109  public static final String javaSerializedObjectMimeType =
110    "application/x-java-serialized-object";
111
112  /**
113   * This is the MIME type used to transfer a Java object reference within
114   * the same JVM.  The representation class is the class of the object
115   * being transferred.
116   */
117  public static final String javaJVMLocalObjectMimeType =
118    "application/x-java-jvm-local-objectref";
119
120  /**
121   * This is the MIME type used to transfer a link to a remote object.
122   * The representation class is the type of object being linked to.
123   */
124  public static final String javaRemoteObjectMimeType =
125    "application/x-java-remote-object";
126
127  /*
128   * Instance Variables
129   */
130  
131  // The MIME type for this flavor
132  private MimeType mimeType;
133  
134  // The representation class for this flavor
135  private Class<?> representationClass;
136  
137  // The human readable name of this flavor
138  private String humanPresentableName;
139
140  /*
141   * Static Methods
142   */
143  
144  /**
145   * This method attempts to load the named class.  The following class
146   * loaders are searched in order: the bootstrap class loader, the
147   * system class loader, the context class loader (if it exists), and
148   * the specified fallback class loader.
149   *
150   * @param className The name of the class to load.
151   * @param classLoader The class loader to use if all others fail, which
152   * may be <code>null</code>.
153   *
154   * @exception ClassNotFoundException If the class cannot be loaded.
155   */
156  protected static final Class<?> tryToLoadClass(String className,
157                                                 ClassLoader classLoader)
158    throws ClassNotFoundException
159  {
160    // Bootstrap
161    try
162      {
163        return Class.forName(className);
164      }
165    catch(ClassNotFoundException cnfe)
166      {
167        // Ignored.
168      }
169  
170    // System
171    try
172      {
173        ClassLoader loader = ClassLoader.getSystemClassLoader();
174        return Class.forName(className, true, loader);
175      }
176    catch(ClassNotFoundException cnfe)
177      {
178        // Ignored.
179      }
180 
181    // Context
182    try
183      {
184        ClassLoader loader = Thread.currentThread().getContextClassLoader();
185        return Class.forName(className, true, loader);
186      }
187    catch(ClassNotFoundException cnfe)
188      {
189        // Ignored.
190      }
191 
192    if (classLoader != null)
193      return Class.forName(className, true, classLoader);
194
195    throw new ClassNotFoundException(className);
196  }
197  
198  /**
199   * XXX - Currently returns <code>plainTextFlavor</code>.
200   */
201  public static final DataFlavor getTextPlainUnicodeFlavor()
202  {
203    return plainTextFlavor;
204  }
205  
206  /**
207   * Selects the best supported text flavor on this implementation.
208   * Returns <code>null</code> when none of the given flavors is liked.
209   *
210   * The <code>DataFlavor</code> returned the first data flavor in the
211   * array that has either a representation class which is (a subclass of)
212   * <code>Reader</code> or <code>String</code>, or has a representation
213   * class which is (a subclass of) <code>InputStream</code> and has a
214   * primary MIME type of "text" and has an supported encoding.
215   */
216  public static final DataFlavor 
217    selectBestTextFlavor(DataFlavor[] availableFlavors)
218  {
219    for(int i = 0; i < availableFlavors.length; i++)
220      {
221        DataFlavor df = availableFlavors[i];
222        Class c = df.representationClass;
223  
224        // A Reader or String is good.
225        if ((Reader.class.isAssignableFrom(c))
226           || (String.class.isAssignableFrom(c)))
227      return df;
228  
229        // A InputStream is good if the mime primary type is "text"
230        if ((InputStream.class.isAssignableFrom(c))
231           && ("text".equals(df.getPrimaryType())))
232          {
233            String encoding = availableFlavors[i].getParameter("charset");
234            if (encoding == null)
235              encoding = "us-ascii";
236            Reader r = null;
237            try
238              {
239                // Try to construct a dummy reader with the found encoding
240                r = new InputStreamReader
241                      (new ByteArrayInputStream(new byte[0]), encoding);
242              }
243            catch(UnsupportedEncodingException uee) { /* ignore */ }
244
245            if (r != null)
246              return df;
247          }
248      }
249  
250    // Nothing found
251    return null;
252  }
253
254
255  /*
256   * Constructors
257   */
258  
259  /**
260   * Empty public constructor needed for externalization.
261   * Should not be used for normal instantiation.
262   */
263  public DataFlavor()
264  {
265    // Used for deserialization only, nothing to do here. 
266  }
267
268  /**
269   * Initializes a new instance of <code>DataFlavor</code>.  The class
270   * and human readable name are specified, the MIME type will be
271   * "application/x-java-serialized-object". If the human readable name
272   * is not specified (<code>null</code>) then the human readable name
273   * will be the same as the MIME type.
274   *
275   * @param representationClass The representation class for this object.
276   * @param humanPresentableName The display name of the object.
277   */
278  public DataFlavor(Class<?> representationClass, String humanPresentableName)
279  {
280    if (representationClass == null)
281      throw new NullPointerException("representationClass must not be null");
282    try
283      {
284        mimeType = new MimeType(javaSerializedObjectMimeType);
285      }
286    catch (MimeTypeParseException ex)
287      {
288        // Must not happen as we use a constant string.
289        assert false;
290      }
291    if (humanPresentableName == null)
292      humanPresentableName = javaSerializedObjectMimeType;
293    this.humanPresentableName = humanPresentableName;
294    this.representationClass = representationClass;
295  }
296
297  /**
298   * Initializes a new instance of <code>DataFlavor</code> with the
299   * specified MIME type and description.  If the MIME type has a
300   * "class=&lt;rep class&gt;" parameter then the representation class will
301   * be the class name specified. Otherwise the class defaults to
302   * <code>java.io.InputStream</code>. If the human readable name
303   * is not specified (<code>null</code>) then the human readable name
304   * will be the same as the MIME type.
305   *
306   * @param mimeType The MIME type for this flavor.
307   * @param humanPresentableName The display name of this flavor.
308   * @param classLoader The class loader for finding classes if the default
309   * class loaders do not work.
310   *
311   * @exception IllegalArgumentException If the representation class
312   * specified cannot be loaded.
313   * @exception ClassNotFoundException If the class is not loaded.
314   */
315  public DataFlavor(String mimeType, String humanPresentableName, 
316                   ClassLoader classLoader)
317    throws ClassNotFoundException
318  {
319    init(mimeType, humanPresentableName, classLoader);
320  }
321
322  /**
323   * Initializes a new instance of <code>DataFlavor</code> with the
324   * specified MIME type and description.  If the MIME type has a
325   * "class=&lt;rep class&gt;" parameter then the representation class will
326   * be the class name specified. Otherwise the class defaults to
327   * <code>java.io.InputStream</code>. If the human readable name
328   * is not specified (<code>null</code>) then the human readable name
329   * will be the same as the MIME type. This is the same as calling
330   * <code>new DataFlavor(mimeType, humanPresentableName, null)</code>.
331   *
332   * @param mimeType The MIME type for this flavor.
333   * @param humanPresentableName The display name of this flavor.
334   *
335   * @exception IllegalArgumentException If the representation class
336   * specified cannot be loaded.
337   */
338  public DataFlavor(String mimeType, String humanPresentableName)
339  {
340    try
341      {
342        init(mimeType, humanPresentableName, getClass().getClassLoader());
343      }
344    catch (ClassNotFoundException ex)
345      {
346        IllegalArgumentException iae =
347          new IllegalArgumentException("Class not found: " + ex.getMessage());
348        iae.initCause(ex);
349        throw iae;
350      }
351  }
352
353  /**
354   * Initializes a new instance of <code>DataFlavor</code> with the specified
355   * MIME type.  This type can have a "class=" parameter to specify the
356   * representation class, and then the class must exist or an exception will
357   * be thrown. If there is no "class=" parameter then the representation class
358   * will be <code>java.io.InputStream</code>. This is the same as calling
359   * <code>new DataFlavor(mimeType, null)</code>.
360   *
361   * @param mimeType The MIME type for this flavor.
362   *
363   * @exception IllegalArgumentException If a class is not specified in
364   * the MIME type.
365   * @exception ClassNotFoundException If the class cannot be loaded.
366   */
367  public DataFlavor(String mimeType) throws ClassNotFoundException
368  {
369    init(mimeType, null, getClass().getClassLoader());
370  }
371
372  /**
373   * Called by various constructors to initialize this object.
374   *
375   * @param mime the mime string
376   * @param humanPresentableName the human presentable name
377   * @param loader the class loader to use for loading the representation
378   *        class
379   */
380  private void init(String mime, String humanPresentableName,
381                    ClassLoader loader)
382    throws ClassNotFoundException
383  {
384    if (mime == null)
385      throw new NullPointerException("The mime type must not be null");
386    try
387      {
388        mimeType = new MimeType(mime);
389      }
390    catch (MimeTypeParseException ex)
391      {
392        IllegalArgumentException iae =
393          new IllegalArgumentException("Invalid mime type");
394        iae.initCause(ex);
395        throw iae;
396      }
397    String className = mimeType.getParameter("class");
398    if (className == null)
399      {
400        if (mimeType.getBaseType().equals(javaSerializedObjectMimeType))
401          throw new IllegalArgumentException("Serialized object type must have"
402                                        + " a representation class parameter");
403        else
404          representationClass = java.io.InputStream.class;
405      }
406    else
407      representationClass = tryToLoadClass(className, loader);
408    mimeType.addParameter("class", representationClass.getName());
409
410    if (humanPresentableName == null)
411      {
412        humanPresentableName = mimeType.getParameter("humanPresentableName");
413        if (humanPresentableName == null)
414          humanPresentableName = mimeType.getBaseType();
415      }
416    this.humanPresentableName = humanPresentableName;
417  }
418
419  /**
420   * Returns the MIME type of this flavor.
421   *
422   * @return The MIME type for this flavor.
423   */
424  public String getMimeType()
425  {
426    return(mimeType.toString());
427  }
428
429  /**
430   * Returns the representation class for this flavor.
431   *
432   * @return The representation class for this flavor.
433   */
434  public Class<?> getRepresentationClass()
435  {
436    return(representationClass);
437  }
438
439  /**
440   * Returns the human presentable name for this flavor.
441   *
442   * @return The human presentable name for this flavor.
443   */
444  public String getHumanPresentableName()
445  {
446    return(humanPresentableName);
447  } 
448
449  /**
450   * Returns the primary MIME type for this flavor.
451   *
452   * @return The primary MIME type for this flavor.
453   */
454  public String getPrimaryType()
455  {
456    return(mimeType.getPrimaryType());
457  }
458
459  /**
460   * Returns the MIME subtype for this flavor.
461   *
462   * @return The MIME subtype for this flavor.
463   */
464  public String getSubType()
465  {
466    return mimeType.getSubType();
467  }
468
469  /**
470   * Returns the value of the named MIME type parameter, or <code>null</code>
471   * if the parameter does not exist.
472   *
473   * @param paramName The name of the paramter.
474   *
475   * @return The value of the parameter.
476   */
477  public String getParameter(String paramName)
478  {
479    if ("humanPresentableName".equals(paramName))
480      return getHumanPresentableName();
481  
482    return mimeType.getParameter(paramName);
483  }
484
485  /**
486   * Sets the human presentable name to the specified value.
487   *
488   * @param humanPresentableName The new display name.
489   */
490  public void setHumanPresentableName(String humanPresentableName)
491  {
492    this.humanPresentableName = humanPresentableName;
493  }
494
495  /**
496   * Tests the MIME type of this object for equality against the specified
497   * MIME type. Ignores parameters.
498   *
499   * @param mimeType The MIME type to test against.
500   *
501   * @return <code>true</code> if the MIME type is equal to this object's
502   * MIME type (ignoring parameters), <code>false</code> otherwise.
503   *
504   * @exception NullPointerException If mimeType is null.
505   */
506  public boolean isMimeTypeEqual(String mimeType)
507  {
508    if (mimeType == null)
509      throw new NullPointerException("mimeType must not be null");
510    boolean equal = false;
511    try
512      {
513        if (this.mimeType != null)
514          {
515            MimeType other = new MimeType(mimeType);
516            equal = this.mimeType.matches(other);
517          }
518      }
519    catch (MimeTypeParseException ex)
520      {
521        // Return false in this case.
522      }
523    return equal;
524  }
525
526  /**
527   * Tests the MIME type of this object for equality against the specified
528   * data flavor's MIME type
529   *
530   * @param flavor The flavor to test against.
531   *
532   * @return <code>true</code> if the flavor's MIME type is equal to this 
533   * object's MIME type, <code>false</code> otherwise.
534   */
535  public final boolean isMimeTypeEqual(DataFlavor flavor)
536  {
537    return isMimeTypeEqual(flavor.getMimeType());
538  }
539
540  /**
541   * Tests whether or not this flavor represents a serialized object.
542   *
543   * @return <code>true</code> if this flavor represents a serialized
544   * object, <code>false</code> otherwise.
545   */
546  public boolean isMimeTypeSerializedObject()
547  {
548    return isMimeTypeEqual(javaSerializedObjectMimeType);
549  }
550
551  /**
552   * Tests whether or not this flavor has a representation class of
553   * <code>java.io.InputStream</code>.
554   *
555   * @return <code>true</code> if the representation class of this flavor
556   * is <code>java.io.InputStream</code>, <code>false</code> otherwise.
557   */
558  public boolean isRepresentationClassInputStream()
559  {
560    return InputStream.class.isAssignableFrom(representationClass);
561  }
562
563  /**
564   * Tests whether the representation class for this flavor is
565   * serializable.
566   *
567   * @return <code>true</code> if the representation class is serializable,
568   * <code>false</code> otherwise.
569   */
570  public boolean isRepresentationClassSerializable()
571  {
572    return Serializable.class.isAssignableFrom(representationClass);
573  }
574
575  /**
576   * Tests whether the representation class for his flavor is remote.
577   *
578   * @return <code>true</code> if the representation class is remote,
579   * <code>false</code> otherwise.
580   */
581  public boolean isRepresentationClassRemote()
582  {
583    return Remote.class.isAssignableFrom (representationClass);
584  }
585
586  /**
587   * Tests whether or not this flavor represents a serialized object.
588   *
589   * @return <code>true</code> if this flavor represents a serialized
590   * object, <code>false</code> otherwise.
591   */
592  public boolean isFlavorSerializedObjectType()
593  {
594    return isRepresentationClassSerializable()
595           && isMimeTypeEqual(javaSerializedObjectMimeType);
596  }
597
598  /**
599   * Tests whether or not this flavor represents a remote object.
600   *
601   * @return <code>true</code> if this flavor represents a remote object,
602   * <code>false</code> otherwise.
603   */
604  public boolean isFlavorRemoteObjectType()
605  {
606    return isRepresentationClassRemote()
607           && isRepresentationClassSerializable()
608           && isMimeTypeEqual(javaRemoteObjectMimeType);
609  }
610
611  /**
612   * Tests whether or not this flavor represents a list of files.
613   *
614   * @return <code>true</code> if this flavor represents a list of files,
615   * <code>false</code> otherwise.
616   */
617  public boolean isFlavorJavaFileListType()
618  {
619    if (getPrimaryType().equals(javaFileListFlavor.getPrimaryType())
620        && getSubType().equals(javaFileListFlavor.getSubType())
621        && javaFileListFlavor.representationClass
622           .isAssignableFrom(representationClass))
623      return true;
624  
625    return false ;
626  }
627
628  /**
629   * Returns a copy of this object.
630   *
631   * @return A copy of this object.
632   *
633   * @exception CloneNotSupportedException If the object's class does not support
634   * the Cloneable interface. Subclasses that override the clone method can also
635   * throw this exception to indicate that an instance cannot be cloned.
636   */
637  public Object clone () throws CloneNotSupportedException
638  {
639    // FIXME - This cannot be right.
640    try
641      {
642        return super.clone();
643      }
644    catch(Exception e)
645      {
646        return null;
647      }
648  }
649
650  /**
651   * This method test the specified <code>DataFlavor</code> for equality
652   * against this object.  This will be true if the MIME type and
653   * representation class are the equal. If the primary type is 'text'
654   * then also the value of the charset parameter is compared. In such a
655   * case when the charset parameter isn't given then the charset is
656   * assumed to be equal to the default charset of the platform.  All
657   * other parameters are ignored.
658   *
659   * @param flavor The <code>DataFlavor</code> to test against.
660   *
661   * @return <code>true</code> if the flavor is equal to this object,
662   * <code>false</code> otherwise.
663   */
664  public boolean equals(DataFlavor flavor)
665  {
666    if (flavor == null)
667      return false;
668
669    String primary = getPrimaryType();
670    if (! primary.equals(flavor.getPrimaryType()))
671      return false;
672
673    String sub = getSubType();
674    if (! sub.equals(flavor.getSubType()))
675      return false;
676
677    if (! this.representationClass.equals(flavor.representationClass))
678      return false;
679
680    if (primary.equals("text"))
681      if (! isRepresentationClassCharBuffer()
682          && ! isRepresentationClassReader()
683          && representationClass != java.lang.String.class
684          && ! (representationClass.isArray()
685                && representationClass.getComponentType() == Character.TYPE))
686        {
687          String charset = getParameter("charset");
688          String otherset = flavor.getParameter("charset");
689          String defaultset = Charset.defaultCharset().name();
690
691          if (charset == null || charset.equals(defaultset))
692            return (otherset == null || otherset.equals(defaultset));
693
694          return charset.equals(otherset);
695        }
696  
697    return true;
698  }
699
700  /**
701   * This method test the specified <code>Object</code> for equality
702   * against this object.  This will be true if the following conditions
703   * are met:
704   * <p>
705   * <ul>
706   * <li>The object is not <code>null</code>.</li>
707   * <li>The object is an instance of <code>DataFlavor</code>.</li>
708   * <li>The object's MIME type and representation class are equal to
709   * this object's.</li>
710   * </ul>
711   *
712   * @param obj The <code>Object</code> to test against.
713   *
714   * @return <code>true</code> if the flavor is equal to this object,
715   * <code>false</code> otherwise.
716   */
717  public boolean equals(Object obj)
718  {
719    if (! (obj instanceof DataFlavor))
720      return false;
721  
722    return equals((DataFlavor) obj);
723  }
724
725  /**
726   * Tests whether or not the specified string is equal to the MIME type
727   * of this object.
728   *
729   * @param str The string to test against.
730   *
731   * @return <code>true</code> if the string is equal to this object's MIME
732   * type, <code>false</code> otherwise.
733   *
734   * @deprecated Not compatible with <code>hashCode()</code>.
735   *             Use <code>isMimeTypeEqual()</code>
736   */
737  public boolean equals(String str)
738  {
739    return isMimeTypeEqual(str);
740  }
741
742  /**
743   * Returns the hash code for this data flavor.
744   * The hash code is based on the (lower case) mime type and the
745   * representation class.
746   */
747  public int hashCode()
748  {
749    return mimeType.toString().hashCode() ^ representationClass.hashCode();
750  }
751
752  /**
753   * Returns <code>true</code> when the given <code>DataFlavor</code>
754   * matches this one.
755   */
756  public boolean match(DataFlavor dataFlavor)
757  {
758    // XXX - How is this different from equals?
759    return equals(dataFlavor);
760  }
761
762  /**
763   * This method exists for backward compatibility.  It simply returns
764   * the same name/value pair passed in.
765   *
766   * @param name The parameter name.
767   * @param value The parameter value.
768   *
769   * @return The name/value pair.
770   *
771   * @deprecated
772   */
773  protected String normalizeMimeTypeParameter(String name, String value)
774  {
775    return name + "=" + value;
776  }
777
778  /**
779   * This method exists for backward compatibility.  It simply returns
780   * the MIME type string unchanged.
781   *
782   * @param type The MIME type.
783   * 
784   * @return The MIME type.
785   *
786   * @deprecated
787   */
788  protected String normalizeMimeType(String type)
789  {
790    return type;
791  }
792
793  /**
794   * Serialize this class.
795   *
796   * @param stream The <code>ObjectOutput</code> stream to serialize to.
797   *
798   * @exception IOException If an error occurs.
799   */
800  public void writeExternal(ObjectOutput stream) 
801    throws IOException
802  {
803    if (mimeType != null)
804      {
805        mimeType.addParameter("humanPresentableName", humanPresentableName);
806        stream.writeObject(mimeType);
807        mimeType.removeParameter("humanPresentableName");
808      }
809    else
810      stream.writeObject(null);
811    stream.writeObject(representationClass);
812  }
813
814
815  /**
816   * De-serialize this class.
817   *
818   * @param stream The <code>ObjectInput</code> stream to deserialize from.
819   *
820   * @exception IOException If an error ocurs.
821   * @exception ClassNotFoundException If the class for an object being restored
822   * cannot be found.
823   */
824  public void readExternal(ObjectInput stream) 
825    throws IOException, ClassNotFoundException
826  {
827    mimeType = (MimeType) stream.readObject();
828    String className = null;
829    if (mimeType != null)
830      {
831        humanPresentableName =
832          mimeType.getParameter("humanPresentableName");
833        mimeType.removeParameter("humanPresentableName");
834        className = mimeType.getParameter("class");
835        if (className == null)
836          throw new IOException("No class in mime type");
837      }
838    try
839      {
840        representationClass = (Class) stream.readObject();
841      }
842    catch (OptionalDataException ex)
843      {
844        if (ex.eof && ex.length == 0)
845          {
846            if (className != null)
847              representationClass = tryToLoadClass(className,
848                                                  getClass().getClassLoader());
849          }
850        else
851          throw ex;
852      }
853  }
854
855  /**
856   * Returns a string representation of this DataFlavor. Including the
857   * representation class name, MIME type and human presentable name.
858   */
859  public String toString()
860  {
861    return (getClass().getName()
862           + "[representationClass=" + getRepresentationClass().getName()
863           + ",mimeType=" + getMimeType()
864           + ",humanPresentableName=" + getHumanPresentableName()
865           + "]");
866  }
867
868  /**
869   * XXX - Currently returns <code>java.io.InputStream</code>.
870   *
871   * @since 1.3
872   */
873  public final Class<?> getDefaultRepresentationClass()
874  {
875    return java.io.InputStream.class;
876  }
877
878  /**
879   * XXX - Currently returns <code>java.io.InputStream</code>.
880   */
881  public final String getDefaultRepresentationClassAsString()
882  {
883    return getDefaultRepresentationClass().getName();
884  }
885
886  /**
887   * Creates a <code>Reader</code> for a given <code>Transferable</code>.
888   *
889   * If the representation class is a (subclass of) <code>Reader</code>
890   * then an instance of the representation class is returned. If the
891   * representatation class is a <code>String</code> then a
892   * <code>StringReader</code> is returned. And if the representation class
893   * is a (subclass of) <code>InputStream</code> and the primary MIME type
894   * is "text" then a <code>InputStreamReader</code> for the correct charset
895   * encoding is returned.
896   *
897   * @param transferable The <code>Transferable</code> for which a text
898   *                     <code>Reader</code> is requested.
899   *
900   * @exception IllegalArgumentException If the representation class is not one
901   * of the seven listed above or the Transferable has null data.
902   * @exception NullPointerException If the Transferable is null.
903   * @exception UnsupportedFlavorException when the transferable doesn't
904   * support this <code>DataFlavor</code>. Or if the representable class
905   * isn't a (subclass of) <code>Reader</code>, <code>String</code>,
906   * <code>InputStream</code> and/or the primary MIME type isn't "text".
907   * @exception IOException when any IOException occurs.
908   * @exception UnsupportedEncodingException if the "charset" isn't supported
909   * on this platform.
910   */
911  public Reader getReaderForText(Transferable transferable)
912    throws UnsupportedFlavorException, IOException
913  {
914      if (!transferable.isDataFlavorSupported(this))
915          throw new UnsupportedFlavorException(this);
916  
917      if (Reader.class.isAssignableFrom(representationClass))
918          return (Reader)transferable.getTransferData(this);
919  
920      if (String.class.isAssignableFrom(representationClass))
921          return new StringReader((String)transferable.getTransferData(this));
922  
923      if (InputStream.class.isAssignableFrom(representationClass)
924          && "text".equals(getPrimaryType()))
925        {
926          InputStream in = (InputStream)transferable.getTransferData(this);
927          String encoding = getParameter("charset");
928          if (encoding == null)
929              encoding = "us-ascii";
930          return new InputStreamReader(in, encoding);
931        }
932  
933      throw new UnsupportedFlavorException(this);
934  }
935
936  /**
937   * Returns whether the representation class for this DataFlavor is
938   * @see java.nio.ByteBuffer or a subclass thereof.
939   *
940   * @since 1.4
941   */
942  public boolean isRepresentationClassByteBuffer()
943  {
944    return ByteBuffer.class.isAssignableFrom(representationClass);
945  }
946
947  /**
948   * Returns whether the representation class for this DataFlavor is
949   * @see java.nio.CharBuffer or a subclass thereof.
950   *
951   * @since 1.4
952   */
953  public boolean isRepresentationClassCharBuffer()
954  {
955    return CharBuffer.class.isAssignableFrom(representationClass);
956  }
957
958  /**
959   * Returns whether the representation class for this DataFlavor is
960   * @see java.io.Reader or a subclass thereof.
961   *
962   * @since 1.4
963   */
964  public boolean isRepresentationClassReader()
965  {
966    return Reader.class.isAssignableFrom(representationClass);
967  }
968  
969  /**
970   * Returns whether this <code>DataFlavor</code> is a valid text flavor for
971   * this implementation of the Java platform. Only flavors equivalent to
972   * <code>DataFlavor.stringFlavor</code> and <code>DataFlavor</code>s with
973   * a primary MIME type of "text" can be valid text flavors.
974   * <p>
975   * If this flavor supports the charset parameter, it must be equivalent to
976   * <code>DataFlavor.stringFlavor</code>, or its representation must be
977   * <code>java.io.Reader</code>, <code>java.lang.String</code>,
978   * <code>java.nio.CharBuffer</code>, <code>java.io.InputStream</code> or 
979   * <code>java.nio.ByteBuffer</code>,
980   * If the representation is <code>java.io.InputStream</code> or 
981   * <code>java.nio.ByteBuffer</code>, then this flavor's <code>charset</code> 
982   * parameter must be supported by this implementation of the Java platform. 
983   * If a charset is not specified, then the platform default charset, which 
984   * is always supported, is assumed.
985   * <p>
986   * If this flavor does not support the charset parameter, its
987   * representation must be <code>java.io.InputStream</code>,
988   * <code>java.nio.ByteBuffer</code>.
989   * <p>
990   * See <code>selectBestTextFlavor</code> for a list of text flavors which
991   * support the charset parameter.
992   *
993   * @return <code>true</code> if this <code>DataFlavor</code> is a valid
994   *         text flavor as described above; <code>false</code> otherwise
995   * @see #selectBestTextFlavor
996   * @since 1.4
997   */
998  public boolean isFlavorTextType() {
999    // FIXME: I'm not 100% sure if this implementation does the same like sun's does    
1000    if(equals(DataFlavor.stringFlavor) || getPrimaryType().equals("text"))
1001      {
1002        String charset = getParameter("charset");
1003        Class c = getRepresentationClass();
1004        if(charset != null) 
1005          {            
1006            if(Reader.class.isAssignableFrom(c) 
1007                || CharBuffer.class.isAssignableFrom(c) 
1008                || String.class.isAssignableFrom(c)) 
1009              {
1010                return true;
1011              }
1012            else if(InputStream.class.isAssignableFrom(c)
1013                    || ByteBuffer.class.isAssignableFrom(c))
1014              {
1015                return Charset.isSupported(charset);
1016              }
1017          }
1018        else if(InputStream.class.isAssignableFrom(c)
1019            || ByteBuffer.class.isAssignableFrom(c))
1020          {
1021            return true;
1022          }
1023      }
1024    return false;
1025  }
1026} // class DataFlavor
1027