This implements two missing methods in DataFlavor: readExternal() and
writeExternal(). This is accompanied by Mauve tests. These tests showed
that the RI serializes an internal class java.awt.datatransfer.MimeType
together with the DataFlavor. So, for maximum compatibility, I added a
helper class for MimeType handling and also serialize this. The tests
cover the (de)serialization of this class too. A couple of peripheral
methods and constructors had to be changed to use the MimeType class
instead of operating on the mime string directly.

2006-10-30  Roman Kennke  <[EMAIL PROTECTED]>

        * java/awt/datatransfer/DataFlavor.java
        (javaFileListFlavor): Don't explicitly specify class.
        (plainTextFlavor): Don't explicitly specify class.
        (mimeType): Changed to type MimeType. Remove final.
        (representationClass): Remove final.
        (DataFlavor): Don't do anything here.
        (DataFlavor(Class,String,String)): Removed.
        (DataFlavor(Class,String)): Initialize here.
        (DataFlavor(String,String,ClassLoader)): Initialize in init().
        (DataFlavor(String,String)): Initialize in init().
        (DataFlavor(String)): Initialize in init().
        (init): New initialization method.
        (getMimeType): Delegate to MimeType.toString().
        (getParameter(String,String)): Removed. Is now done in MimeType.
        (getParameter(String)): Delegate to MimeType.
        (getPrimaryType): Delegate to MimeType.
        (getRepresentationClassFromMime): Removed.
        (getRepresentationClassFromMimeThrows): Removed.
        (getSubType): Delegate to MimeType.
        (hashCode): Take MimeType.toString() for the hashCode.
        (isFlavorRemoveObjectType): Return true only when representation
        class is remove and serializable and the mime type is remote.
        (isFlavorSerializedObjectType): Return true only when representation
        class is serializable and the mime type is serialized.
        (isMimeTypeEqual): Rewritten to delegate to MimeType.matches().
        (isMimeTypeSerializedObject): Delegate to isMimeTypeEqual().
        (readExternal): Implemented stub method.
        (writeExternal): Implemented stub method.
        * java/awt/datatransfer/MimeType.java: New helper class.

/Roman

Index: java/awt/datatransfer/MimeType.java
===================================================================
RCS file: java/awt/datatransfer/MimeType.java
diff -N java/awt/datatransfer/MimeType.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ java/awt/datatransfer/MimeType.java	30 Oct 2006 13:19:17 -0000
@@ -0,0 +1,281 @@
+/* MimeType.java -- A helper class for mime handling in DataFlavor
+   Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.awt.datatransfer;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+/**
+ * A helper class for mime handling in DataFlavor.
+ *
+ * A Mauve test for DataFlavor.writeExternal() shows that a non-public
+ * class java.awt.datatransfer.MimeType gets serialized. This class
+ * is mainly here for serialization compatibility. Of course,
+ * now that we have it here, we can just as well implement some
+ * mime handling facility here.
+ */
+class MimeType
+  implements Externalizable
+{
+
+  /**
+   * The primary type.
+   */
+  private String primaryType;
+
+  /**
+   * The subtype.
+   */
+  private String subType;
+
+  /**
+   * Additional parameters to be appended to the mime string.
+   */
+  private HashMap parameters;
+
+  /**
+   * This is only here for deserialization.
+   */
+  public MimeType()
+  {
+    parameters = new HashMap();
+  }
+
+  /**
+   * Creates a new MimeType object.
+   *
+   * @param mime the mime type
+   */
+  MimeType(String mime)
+    throws MimeTypeParseException
+  {
+    this();
+    parse(mime);
+  }
+
+  /**
+   * Adds a mime parameter.
+   *
+   * @param param the parameter key
+   * @param value the parameter value
+   */
+  void addParameter(String param, String value)
+  {
+    parameters.put(param, value);
+  }
+
+  /**
+   * Removes the parameter with the specified key.
+   *
+   * @param param the parameter to remove
+   */
+  void removeParameter(String param)
+  {
+    parameters.remove(param);
+  }
+
+  /**
+   * Returns the parameter for the <code>key</code>.
+   *
+   * @param key the parameter key
+   * 
+   * @return the parameter for the <code>key</code>
+   */
+  String getParameter(String key)
+  {
+    return (String) parameters.get(key);
+  }
+
+  /**
+   * Returns the primary type.
+   *
+   * @return the primary type
+   */
+  String getPrimaryType()
+  {
+    return primaryType;
+  }
+
+  String getSubType()
+  {
+    return subType;
+  }
+
+  /**
+   * Returns the base type of this mime type. This is the primary
+   * type plus the subtype, separated by '/'.
+   *
+   * @return the base type of this mime type
+   */
+  String getBaseType()
+  {
+    return primaryType + '/' + subType;
+  }
+
+  /**
+   * Returns <code>true</code> if this mime type and another mime type
+   * match. This will be true when their primary types are equal, and their
+   * subtypes are equal (or when either subtype is * ).
+   *
+   * @param other the other mime type
+   *
+   * @return <code>true</code> if the mime types match, <code>false</code>
+   *         otherwise
+   */
+  boolean matches(MimeType other)
+  {
+    boolean match = false;
+    if (other != null)
+      {
+        match = primaryType.equals(other.primaryType)
+                && (subType.equals("*") || other.subType.equals("*")
+                    || subType.equals(other.subType));
+      }
+    return match;
+  }
+
+  /**
+   * Serializes the mime type.
+   *
+   * @param in the input stream to read from
+   *
+   * @throws ClassNotFoundException not thrown here
+   * @throws IOException when something goes wrong on the input stream,
+   *         or when the mime type can't be parsed
+   */
+  public void readExternal(ObjectInput in)
+    throws ClassNotFoundException, IOException
+  {
+    String mime = in.readUTF();
+    parameters.clear();
+    try
+      {
+        parse(mime);
+      }
+    catch (MimeTypeParseException ex)
+      {
+        IOException ioEx = new IOException();
+        ioEx.initCause(ex);
+        throw ioEx;
+      }
+  }
+
+  /**
+   * Serializes this mime type.
+   *
+   * @param out the output stream
+   *
+   * @throws IOException when something goes wrong on the output stream
+   */
+  public void writeExternal(ObjectOutput out)
+    throws IOException
+  {
+    out.writeUTF(toString());
+  }
+
+  /**
+   * Creates a string representation of this mime type.
+   *
+   * @return a string representation of this mime type
+   */
+  public String toString()
+  {
+    StringBuilder s = new StringBuilder();
+    s.append(primaryType);
+    s.append('/');
+    s.append(subType);
+    if (parameters.size() > 0)
+      {
+        Set entries = parameters.entrySet();
+        for (Iterator i = entries.iterator(); i.hasNext();)
+          {
+            s.append("; ");
+            Map.Entry entry = (Map.Entry) i.next();
+            s.append(entry.getKey());
+            s.append('=');
+            s.append(entry.getValue());
+          }
+      }
+    return s.toString();
+  }
+
+  /**
+   * Parses the specified mime type string and initializes the fields
+   * of this object.
+   *
+   * @param mime the mime type string
+   */
+  private void parse(String mime)
+    throws MimeTypeParseException
+  {
+    // FIXME: Maybe implement more sophisticated mime string parsing according
+    // to RFC 2045 and 2046.
+    StringTokenizer tokenizer = new StringTokenizer(mime);
+    try
+      {
+        primaryType = tokenizer.nextToken("/");
+        subType = tokenizer.nextToken("/;");
+      }
+    catch (NoSuchElementException ex)
+      {
+        throw new MimeTypeParseException("Expected / separator");
+      }
+
+    // Add any parameters.
+    while (tokenizer.hasMoreTokens())
+      {
+        String keyValuePair = tokenizer.nextToken(";");
+        int i = keyValuePair.indexOf('=');
+        if (i == -1)
+          throw new MimeTypeParseException("Expected = as parameter separator");
+        String key = keyValuePair.substring(0, i).trim();
+        String value = keyValuePair.substring(i + 1).trim();
+        parameters.put(key, value);
+      }
+  }
+
+}
Index: java/awt/datatransfer/DataFlavor.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/awt/datatransfer/DataFlavor.java,v
retrieving revision 1.32
diff -u -1 -5 -r1.32 DataFlavor.java
--- java/awt/datatransfer/DataFlavor.java	18 Oct 2006 09:42:22 -0000	1.32
+++ java/awt/datatransfer/DataFlavor.java	30 Oct 2006 13:19:17 -0000
@@ -34,80 +34,79 @@
 this exception to your version of the library, but you are not
 obligated to do so.  If you do not wish to do so, delete this
 exception statement from your version. */
 
 
 package java.awt.datatransfer;
 
 import gnu.classpath.NotImplementedException;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
+import java.io.OptionalDataException;
 import java.io.Reader;
 import java.io.Serializable;
 import java.io.StringReader;
 import java.io.UnsupportedEncodingException;
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
 import java.nio.charset.Charset;
 import java.rmi.Remote;
 
 /**
  * This class represents a particular data format used for transferring
  * data via the clipboard.
  *
  * @author Aaron M. Renn ([EMAIL PROTECTED])
  */
 public class DataFlavor implements java.io.Externalizable, Cloneable
 {
   static final long serialVersionUID = 8367026044764648243L;
 
   // FIXME: Serialization: Need to write methods for.
 
   /**
    * This is the data flavor used for tranferring plain text.  The MIME
    * type is "text/plain; charset=unicode".  The representation class
    * is <code>java.io.InputStream</code>.
    *
    * @deprecated The charset unicode is platform specific and InputStream
    * deals with bytes not chars. Use <code>getRederForText()</code>.
    */
   public static final DataFlavor plainTextFlavor = 
-    new DataFlavor(java.io.InputStream.class,
-                   "text/plain; charset=unicode",
+    new DataFlavor("text/plain; charset=unicode; class=java.io.InputStream",
                    "plain unicode text");
 
   /**
    * This is the data flavor used for transferring Java strings.  The
    * MIME type is "application/x-java-serialized-object" and the 
    * representation class is <code>java.lang.String</code>.
    */
   public static final DataFlavor stringFlavor = 
     new DataFlavor(java.lang.String.class, "Java Unicode String");
 
   /**
    * This is a data flavor used for transferring lists of files.  The
    * representation type is a <code>java.util.List</code>, with each 
    * element of the list being a <code>java.io.File</code>.
    */
   public static final DataFlavor javaFileListFlavor = 
-    new DataFlavor(java.util.List.class,
-                   "application/x-java-file-list; class=java.util.List",
+    new DataFlavor("application/x-java-file-list; class=java.util.List",
                    "Java File List");
 
   /**
    * This is an image flavor used for transferring images.  The
    * representation type is a <code>java.awt.Image</code>.
    */
   public static final DataFlavor imageFlavor = 
     new DataFlavor(java.awt.Image.class, "Java Image");
 
   /**
    * This is the MIME type used for transferring a serialized object.
    * The representation class is the type of object be deserialized.
    */
   public static final String javaSerializedObjectMimeType =
     "application/x-java-serialized-object";
@@ -120,34 +119,34 @@
   public static final String javaJVMLocalObjectMimeType =
     "application/x-java-jvm-local-objectref";
 
   /**
    * This is the MIME type used to transfer a link to a remote object.
    * The representation class is the type of object being linked to.
    */
   public static final String javaRemoteObjectMimeType =
     "application/x-java-remote-object";
 
   /*
    * Instance Variables
    */
   
   // The MIME type for this flavor
-  private final String mimeType;
+  private MimeType mimeType;
   
   // The representation class for this flavor
-  private final Class representationClass;
+  private Class representationClass;
   
   // The human readable name of this flavor
   private String humanPresentableName;
 
   /*
    * Static Methods
    */
   
   /**
    * This method attempts to load the named class.  The following class
    * loaders are searched in order: the bootstrap class loader, the
    * system class loader, the context class loader (if it exists), and
    * the specified fallback class loader.
    *
    * @param className The name of the class to load.
@@ -186,86 +185,30 @@
       {
 	ClassLoader loader = Thread.currentThread().getContextClassLoader();
         return Class.forName(className, true, loader);
       }
     catch(ClassNotFoundException cnfe)
       {
 	// Ignored.
       }
  
     if (classLoader != null)
       return Class.forName(className, true, classLoader);
 
     throw new ClassNotFoundException(className);
   }
   
-  private static Class getRepresentationClassFromMimeThrows(String mimeString,
-                                                      ClassLoader classLoader)
-    throws ClassNotFoundException
-    {
-      String classname = getParameter("class", mimeString);
-      if (classname != null)
-        return tryToLoadClass(classname, classLoader);
-      else
-        return java.io.InputStream.class;
-    }
- 
-  // Same as above, but wraps any ClassNotFoundExceptions
-  private static Class getRepresentationClassFromMime(String mimeString,
-                                                      ClassLoader classLoader)
-  {
-    try
-      {
-	return getRepresentationClassFromMimeThrows(mimeString, classLoader);
-      }
-    catch(ClassNotFoundException cnfe)
-      {
-	IllegalArgumentException iae;
-	iae = new IllegalArgumentException("mimeString: "
-					   + mimeString
-					   + " classLoader: "
-					   + classLoader);
-	iae.initCause(cnfe);
-	throw iae;
-      }
-  }
-
-  /**
-   * Returns the value of the named MIME type parameter, or <code>null</code>
-   * if the parameter does not exist. Given the parameter name and the mime
-   * string.
-   *
-   * @param paramName The name of the parameter.
-   * @param mimeString The mime string from where the name should be found.
-   *
-   * @return The value of the parameter or null.
-   */
-  private static String getParameter(String paramName, String mimeString)
-  {
-    int idx = mimeString.indexOf(paramName + "=");
-    if (idx == -1)
-      return(null);
-  
-    String value = mimeString.substring(idx + paramName.length() + 1);
-  
-    idx = value.indexOf(";");
-    if (idx == -1)
-      return(value);
-    else
-      return(value.substring(0, idx));
-  }
-  
   /**
    * XXX - Currently returns <code>plainTextFlavor</code>.
    */
   public static final DataFlavor getTextPlainUnicodeFlavor()
   {
     return plainTextFlavor;
   }
   
   /**
    * Selects the best supported text flavor on this implementation.
    * Returns <code>null</code> when none of the given flavors is liked.
    *
    * The <code>DataFlavor</code> returned the first data flavor in the
    * array that has either a representation class which is (a subclass of)
    * <code>Reader</code> or <code>String</code>, or has a representation
@@ -309,280 +252,314 @@
     // Nothing found
     return null;
   }
 
 
   /*
    * Constructors
    */
   
   /**
    * Empty public constructor needed for externalization.
    * Should not be used for normal instantiation.
    */
   public DataFlavor()
   {
-    mimeType = null;
-    representationClass = null;
-    humanPresentableName = null;
-  }
-
-  /**
-   * Private constructor.
-   */
-  private DataFlavor(Class representationClass,
-                    String mimeType,
-                    String humanPresentableName)
-  {
-    this.representationClass = representationClass;
-    this.mimeType = mimeType;
-
-    // Do some simple validity checks
-    String type = getPrimaryType() + "/" + getSubType();
-    if (type.indexOf('=') != -1
-        || type.indexOf(';') != -1)
-      throw new IllegalArgumentException(mimeType);
-
-    if (humanPresentableName != null)
-      this.humanPresentableName = humanPresentableName;
-    else
-      this.humanPresentableName = mimeType;
+    // Used for deserialization only, nothing to do here. 
   }
 
   /**
    * Initializes a new instance of <code>DataFlavor</code>.  The class
    * and human readable name are specified, the MIME type will be
    * "application/x-java-serialized-object". If the human readable name
    * is not specified (<code>null</code>) then the human readable name
    * will be the same as the MIME type.
    *
    * @param representationClass The representation class for this object.
    * @param humanPresentableName The display name of the object.
    */
   public DataFlavor(Class representationClass, String humanPresentableName)
   {
-    this(representationClass,
-         "application/x-java-serialized-object"
-         + "; class="
-         + representationClass.getName(),
-         humanPresentableName);
+    if (representationClass == null)
+      throw new NullPointerException("representationClass must not be null");
+    try
+      {
+        mimeType = new MimeType(javaSerializedObjectMimeType);
+      }
+    catch (MimeTypeParseException ex)
+      {
+        // Must not happen as we use a constant string.
+        assert false;
+      }
+    if (humanPresentableName == null)
+      humanPresentableName = javaSerializedObjectMimeType;
+    this.humanPresentableName = humanPresentableName;
+    this.representationClass = representationClass;
   }
 
   /**
    * Initializes a new instance of <code>DataFlavor</code> with the
    * specified MIME type and description.  If the MIME type has a
    * "class=&lt;rep class&gt;" parameter then the representation class will
    * be the class name specified. Otherwise the class defaults to
    * <code>java.io.InputStream</code>. If the human readable name
    * is not specified (<code>null</code>) then the human readable name
    * will be the same as the MIME type.
    *
    * @param mimeType The MIME type for this flavor.
    * @param humanPresentableName The display name of this flavor.
    * @param classLoader The class loader for finding classes if the default
    * class loaders do not work.
    *
    * @exception IllegalArgumentException If the representation class
    * specified cannot be loaded.
    * @exception ClassNotFoundException If the class is not loaded.
    */
   public DataFlavor(String mimeType, String humanPresentableName, 
                    ClassLoader classLoader)
     throws ClassNotFoundException
   {
-    this(getRepresentationClassFromMimeThrows(mimeType, classLoader),
-         mimeType, humanPresentableName);
+    init(mimeType, humanPresentableName, classLoader);
   }
 
   /**
    * Initializes a new instance of <code>DataFlavor</code> with the
    * specified MIME type and description.  If the MIME type has a
    * "class=&lt;rep class&gt;" parameter then the representation class will
    * be the class name specified. Otherwise the class defaults to
    * <code>java.io.InputStream</code>. If the human readable name
    * is not specified (<code>null</code>) then the human readable name
    * will be the same as the MIME type. This is the same as calling
    * <code>new DataFlavor(mimeType, humanPresentableName, null)</code>.
    *
    * @param mimeType The MIME type for this flavor.
    * @param humanPresentableName The display name of this flavor.
    *
    * @exception IllegalArgumentException If the representation class
    * specified cannot be loaded.
    */
   public DataFlavor(String mimeType, String humanPresentableName)
   {
-    this(getRepresentationClassFromMime (mimeType, null),
-        mimeType, humanPresentableName);
+    try
+      {
+        init(mimeType, humanPresentableName, getClass().getClassLoader());
+      }
+    catch (ClassNotFoundException ex)
+      {
+        IllegalArgumentException iae =
+          new IllegalArgumentException("Class not found: " + ex.getMessage());
+        iae.initCause(ex);
+        throw iae;
+      }
   }
 
   /**
    * Initializes a new instance of <code>DataFlavor</code> with the specified
    * MIME type.  This type can have a "class=" parameter to specify the
    * representation class, and then the class must exist or an exception will
    * be thrown. If there is no "class=" parameter then the representation class
    * will be <code>java.io.InputStream</code>. This is the same as calling
    * <code>new DataFlavor(mimeType, null)</code>.
    *
    * @param mimeType The MIME type for this flavor.
    *
    * @exception IllegalArgumentException If a class is not specified in
    * the MIME type.
    * @exception ClassNotFoundException If the class cannot be loaded.
    */
   public DataFlavor(String mimeType) throws ClassNotFoundException
   {
-    this(getRepresentationClassFromMimeThrows(mimeType, null),
-	 mimeType, null);
+    init(mimeType, null, getClass().getClassLoader());
+  }
+
+  /**
+   * Called by various constructors to initialize this object.
+   *
+   * @param mime the mime string
+   * @param humanPresentableName the human presentable name
+   * @param loader the class loader to use for loading the representation
+   *        class
+   */
+  private void init(String mime, String humanPresentableName,
+                    ClassLoader loader)
+    throws ClassNotFoundException
+  {
+    if (mime == null)
+      throw new NullPointerException("The mime type must not be null");
+    try
+      {
+        mimeType = new MimeType(mime);
+      }
+    catch (MimeTypeParseException ex)
+      {
+        IllegalArgumentException iae =
+          new IllegalArgumentException("Invalid mime type");
+        iae.initCause(ex);
+        throw iae;
+      }
+    String className = mimeType.getParameter("class");
+    if (className == null)
+      {
+        if (mimeType.getBaseType().equals(javaSerializedObjectMimeType))
+          throw new IllegalArgumentException("Serialized object type must have"
+                                        + " a representation class parameter");
+        else
+          representationClass = java.io.InputStream.class;
+      }
+    else
+      representationClass = tryToLoadClass(className, loader);
+    mimeType.addParameter("class", representationClass.getName());
+
+    if (humanPresentableName == null)
+      {
+        humanPresentableName = mimeType.getParameter("humanPresentableName");
+        if (humanPresentableName == null)
+          humanPresentableName = mimeType.getBaseType();
+      }
+    this.humanPresentableName = humanPresentableName;
   }
 
   /**
    * Returns the MIME type of this flavor.
    *
    * @return The MIME type for this flavor.
    */
   public String getMimeType()
   {
-    return(mimeType);
+    return(mimeType.toString());
   }
 
   /**
    * Returns the representation class for this flavor.
    *
    * @return The representation class for this flavor.
    */
   public Class getRepresentationClass()
   {
     return(representationClass);
   }
 
   /**
    * Returns the human presentable name for this flavor.
    *
    * @return The human presentable name for this flavor.
    */
   public String getHumanPresentableName()
   {
     return(humanPresentableName);
   } 
 
   /**
    * Returns the primary MIME type for this flavor.
    *
    * @return The primary MIME type for this flavor.
    */
   public String getPrimaryType()
   {
-    int idx = mimeType.indexOf("/");
-    if (idx == -1)
-      return(mimeType);
-  
-    return(mimeType.substring(0, idx));
+    return(mimeType.getPrimaryType());
   }
 
   /**
    * Returns the MIME subtype for this flavor.
    *
    * @return The MIME subtype for this flavor.
    */
   public String getSubType()
   {
-    int start = mimeType.indexOf("/");
-    if (start == -1)
-      return "";
-  
-    int end = mimeType.indexOf(";", start + 1);
-    if (end == -1)
-      return mimeType.substring(start + 1);
-    else
-      return mimeType.substring(start + 1, end);
+    return mimeType.getSubType();
   }
 
   /**
    * Returns the value of the named MIME type parameter, or <code>null</code>
    * if the parameter does not exist.
    *
    * @param paramName The name of the paramter.
    *
    * @return The value of the parameter.
    */
   public String getParameter(String paramName)
   {
     if ("humanPresentableName".equals(paramName))
       return getHumanPresentableName();
   
-    return getParameter(paramName, mimeType);
+    return mimeType.getParameter(paramName);
   }
 
   /**
    * Sets the human presentable name to the specified value.
    *
    * @param humanPresentableName The new display name.
    */
   public void setHumanPresentableName(String humanPresentableName)
   {
     this.humanPresentableName = humanPresentableName;
   }
 
   /**
    * Tests the MIME type of this object for equality against the specified
    * MIME type. Ignores parameters.
    *
    * @param mimeType The MIME type to test against.
    *
    * @return <code>true</code> if the MIME type is equal to this object's
    * MIME type (ignoring parameters), <code>false</code> otherwise.
    *
    * @exception NullPointerException If mimeType is null.
    */
   public boolean isMimeTypeEqual(String mimeType)
   {
-    String mime = getMimeType();
-    int i = mime.indexOf(";");
-    if (i != -1)
-      mime = mime.substring(0, i);
-  
-    i = mimeType.indexOf(";");
-    if (i != -1)
-      mimeType = mimeType.substring(0, i);
-  
-    return mime.equals(mimeType);
+    if (mimeType == null)
+      throw new NullPointerException("mimeType must not be null");
+    boolean equal = false;
+    try
+      {
+        if (this.mimeType != null)
+          {
+            MimeType other = new MimeType(mimeType);
+            equal = this.mimeType.matches(other);
+          }
+      }
+    catch (MimeTypeParseException ex)
+      {
+        // Return false in this case.
+      }
+    return equal;
   }
 
   /**
    * Tests the MIME type of this object for equality against the specified
    * data flavor's MIME type
    *
    * @param flavor The flavor to test against.
    *
    * @return <code>true</code> if the flavor's MIME type is equal to this 
    * object's MIME type, <code>false</code> otherwise.
    */
   public final boolean isMimeTypeEqual(DataFlavor flavor)
   {
     return isMimeTypeEqual(flavor.getMimeType());
   }
 
   /**
    * Tests whether or not this flavor represents a serialized object.
    *
    * @return <code>true</code> if this flavor represents a serialized
    * object, <code>false</code> otherwise.
    */
   public boolean isMimeTypeSerializedObject()
   {
-    return mimeType.startsWith(javaSerializedObjectMimeType);
+    return isMimeTypeEqual(javaSerializedObjectMimeType);
   }
 
   /**
    * Tests whether or not this flavor has a representation class of
    * <code>java.io.InputStream</code>.
    *
    * @return <code>true</code> if the representation class of this flavor
    * is <code>java.io.InputStream</code>, <code>false</code> otherwise.
    */
   public boolean isRepresentationClassInputStream()
   {
     return InputStream.class.isAssignableFrom(representationClass);
   }
 
   /**
@@ -604,43 +581,45 @@
    * <code>false</code> otherwise.
    */
   public boolean isRepresentationClassRemote()
   {
     return Remote.class.isAssignableFrom (representationClass);
   }
 
   /**
    * Tests whether or not this flavor represents a serialized object.
    *
    * @return <code>true</code> if this flavor represents a serialized
    * object, <code>false</code> otherwise.
    */
   public boolean isFlavorSerializedObjectType()
   {
-    // FIXME: What is the diff between this and isMimeTypeSerializedObject?
-    return(mimeType.startsWith(javaSerializedObjectMimeType));
+    return isRepresentationClassSerializable()
+           && isMimeTypeEqual(javaSerializedObjectMimeType);
   }
 
   /**
    * Tests whether or not this flavor represents a remote object.
    *
    * @return <code>true</code> if this flavor represents a remote object,
    * <code>false</code> otherwise.
    */
   public boolean isFlavorRemoteObjectType()
   {
-    return(mimeType.startsWith(javaRemoteObjectMimeType));
+    return isRepresentationClassRemote()
+           && isRepresentationClassSerializable()
+           && isMimeTypeEqual(javaRemoteObjectMimeType);
   }
 
   /**
    * Tests whether or not this flavor represents a list of files.
    *
    * @return <code>true</code> if this flavor represents a list of files,
    * <code>false</code> otherwise.
    */
   public boolean isFlavorJavaFileListType()
   {
     if (getPrimaryType().equals(javaFileListFlavor.getPrimaryType())
         && getSubType().equals(javaFileListFlavor.getSubType())
         && javaFileListFlavor.representationClass
 	   .isAssignableFrom(representationClass))
       return true;
@@ -757,31 +736,31 @@
    * @deprecated Not compatible with <code>hashCode()</code>.
    *             Use <code>isMimeTypeEqual()</code>
    */
   public boolean equals(String str)
   {
     return isMimeTypeEqual(str);
   }
 
   /**
    * Returns the hash code for this data flavor.
    * The hash code is based on the (lower case) mime type and the
    * representation class.
    */
   public int hashCode()
   {
-    return mimeType.toLowerCase().hashCode() ^ representationClass.hashCode();
+    return mimeType.toString().hashCode() ^ representationClass.hashCode();
   }
 
   /**
    * Returns <code>true</code> when the given <code>DataFlavor</code>
    * matches this one.
    */
   public boolean match(DataFlavor dataFlavor)
   {
     // XXX - How is this different from equals?
     return equals(dataFlavor);
   }
 
   /**
    * This method exists for backward compatibility.  It simply returns
    * the same name/value pair passed in.
@@ -811,47 +790,80 @@
   protected String normalizeMimeType(String type)
   {
     return type;
   }
 
   /**
    * Serialize this class.
    *
    * @param stream The <code>ObjectOutput</code> stream to serialize to.
    *
    * @exception IOException If an error occurs.
    */
   public void writeExternal(ObjectOutput stream) 
     throws IOException, NotImplementedException
   {
-    // FIXME: Implement me
+    if (mimeType != null)
+      {
+        mimeType.addParameter("humanPresentableName", humanPresentableName);
+        stream.writeObject(mimeType);
+        mimeType.removeParameter("humanPresentableName");
+      }
+    else
+      stream.writeObject(null);
+    stream.writeObject(representationClass);
   }
 
 
   /**
    * De-serialize this class.
    *
    * @param stream The <code>ObjectInput</code> stream to deserialize from.
    *
    * @exception IOException If an error ocurs.
    * @exception ClassNotFoundException If the class for an object being restored
    * cannot be found.
    */
   public void readExternal(ObjectInput stream) 
-    throws IOException, ClassNotFoundException, NotImplementedException
+    throws IOException, ClassNotFoundException
   {
-    // FIXME: Implement me
+    mimeType = (MimeType) stream.readObject();
+    String className = null;
+    if (mimeType != null)
+      {
+        humanPresentableName =
+          mimeType.getParameter("humanPresentableName");
+        mimeType.removeParameter("humanPresentableName");
+        className = mimeType.getParameter("class");
+        if (className == null)
+          throw new IOException("No class in mime type");
+      }
+    try
+      {
+        representationClass = (Class) stream.readObject();
+      }
+    catch (OptionalDataException ex)
+      {
+        if (ex.eof && ex.length == 0)
+          {
+            if (className != null)
+              representationClass = tryToLoadClass(className,
+                                                  getClass().getClassLoader());
+          }
+        else
+          throw ex;
+      }
   }
 
   /**
    * Returns a string representation of this DataFlavor. Including the
    * representation class name, MIME type and human presentable name.
    */
   public String toString()
   {
     return (getClass().getName()
            + "[representationClass=" + getRepresentationClass().getName()
            + ",mimeType=" + getMimeType()
            + ",humanPresentableName=" + getHumanPresentableName()
            + "]");
   }
 

Reply via email to