Source for java.awt.datatransfer.DataFlavor

   1: /* DataFlavor.java -- A type of data to transfer via the clipboard.
   2:    Copyright (C) 1999, 2001, 2004, 2005, 2006 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package java.awt.datatransfer;
  40: 
  41: import java.io.ByteArrayInputStream;
  42: import java.io.IOException;
  43: import java.io.InputStream;
  44: import java.io.InputStreamReader;
  45: import java.io.ObjectInput;
  46: import java.io.ObjectOutput;
  47: import java.io.OptionalDataException;
  48: import java.io.Reader;
  49: import java.io.Serializable;
  50: import java.io.StringReader;
  51: import java.io.UnsupportedEncodingException;
  52: import java.nio.ByteBuffer;
  53: import java.nio.CharBuffer;
  54: import java.nio.charset.Charset;
  55: import java.rmi.Remote;
  56: 
  57: /**
  58:  * This class represents a particular data format used for transferring
  59:  * data via the clipboard.
  60:  *
  61:  * @author Aaron M. Renn (arenn@urbanophile.com)
  62:  */
  63: public class DataFlavor implements java.io.Externalizable, Cloneable
  64: {
  65:   static final long serialVersionUID = 8367026044764648243L;
  66: 
  67:   // FIXME: Serialization: Need to write methods for.
  68: 
  69:   /**
  70:    * This is the data flavor used for tranferring plain text.  The MIME
  71:    * type is "text/plain; charset=unicode".  The representation class
  72:    * is <code>java.io.InputStream</code>.
  73:    *
  74:    * @deprecated The charset unicode is platform specific and InputStream
  75:    * deals with bytes not chars. Use <code>getRederForText()</code>.
  76:    */
  77:   public static final DataFlavor plainTextFlavor = 
  78:     new DataFlavor("text/plain; charset=unicode; class=java.io.InputStream",
  79:                    "plain unicode text");
  80: 
  81:   /**
  82:    * This is the data flavor used for transferring Java strings.  The
  83:    * MIME type is "application/x-java-serialized-object" and the 
  84:    * representation class is <code>java.lang.String</code>.
  85:    */
  86:   public static final DataFlavor stringFlavor = 
  87:     new DataFlavor(java.lang.String.class, "Java Unicode String");
  88: 
  89:   /**
  90:    * This is a data flavor used for transferring lists of files.  The
  91:    * representation type is a <code>java.util.List</code>, with each 
  92:    * element of the list being a <code>java.io.File</code>.
  93:    */
  94:   public static final DataFlavor javaFileListFlavor = 
  95:     new DataFlavor("application/x-java-file-list; class=java.util.List",
  96:                    "Java File List");
  97: 
  98:   /**
  99:    * 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