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