1. /*
  2. * @(#)DataFlavor.java 1.35 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.awt.datatransfer;
  8. import java.io.Externalizable;
  9. import java.io.ObjectOutput;
  10. import java.io.ObjectInput;
  11. import java.io.IOException;
  12. /**
  13. * Each instance represents the opaque concept of a data format as would
  14. * appear on a clipboard, during drag and drop, or in a file system.
  15. *
  16. * @version 1, 35
  17. * @author Blake Sullivan
  18. * @author Laurence P. G. Cable
  19. */
  20. public class DataFlavor implements Externalizable, Cloneable {
  21. static final long serialVersionUID = 8367026044764648243L;
  22. static final Class ioInputStreamClass = java.io.InputStream.class;
  23. /**
  24. * tried to load a class from: the bootstrap loader, the system loader,
  25. * the context loader (if one is present) and finally the loader specified
  26. *
  27. * @param fallback the fallback loader
  28. *
  29. * @throws ClassNotFoundException
  30. */
  31. protected final static Class tryToLoadClass(String className,
  32. ClassLoader fallback)
  33. throws ClassNotFoundException
  34. {
  35. ClassLoader systemClassLoader = (ClassLoader)
  36. java.security.AccessController.doPrivileged(
  37. new java.security.PrivilegedAction() {
  38. public Object run() {
  39. ClassLoader cl = Thread.currentThread().
  40. getContextClassLoader();
  41. return (cl != null)
  42. ? cl
  43. : ClassLoader.getSystemClassLoader();
  44. }
  45. });
  46. try {
  47. return Class.forName(className, true, systemClassLoader);
  48. } catch (ClassNotFoundException e2) {
  49. if (fallback != null) {
  50. return Class.forName(className, true, fallback);
  51. } else {
  52. throw new ClassNotFoundException(className);
  53. }
  54. }
  55. }
  56. /*
  57. * private initializer
  58. */
  59. static private DataFlavor createConstant(Class rc, String prn) {
  60. try {
  61. return new DataFlavor(rc, prn);
  62. } catch (Exception e) {
  63. return null;
  64. }
  65. }
  66. /*
  67. * private initializer
  68. */
  69. static private DataFlavor createConstant(String mt, String prn) {
  70. try {
  71. return new DataFlavor(mt, prn);
  72. } catch (Exception e) {
  73. return null;
  74. }
  75. }
  76. /**
  77. * The DataFlavor representing a Java Unicode String class, where:
  78. * <p>
  79. * representationClass = java.lang.String<br>
  80. * mimeType = "application/x-java-serialized-object"
  81. * <p>
  82. */
  83. public static final DataFlavor stringFlavor = createConstant(java.lang.String.class, "Unicode String");
  84. /**
  85. * The DataFlavor representing plain text with unicode encoding, where:
  86. * <p>
  87. * representationClass = InputStream<br>
  88. * mimeType = "text/plain; charset=unicode"
  89. * <p>
  90. */
  91. public static final DataFlavor plainTextFlavor = createConstant("text/plain; charset=unicode; class=java.io.InputStream", "Plain Text");
  92. /**
  93. * a MIME Content-Type of application/x-java-serialized-object represents
  94. * a graph of Java object(s) that have been made persistent.
  95. *
  96. * The representation class associated with this DataFlavor identifies
  97. * the Java type of an object returned as a reference from an invocation
  98. * java.awt.datatransfer.getTransferData().
  99. *
  100. */
  101. public static final String javaSerializedObjectMimeType = "application/x-java-serialized-object";
  102. /**
  103. * To transfer a list of files to/from Java (and the underlying
  104. * platform) a DataFlavor of this type/subtype and representation class
  105. * of java.util.List is used.
  106. *
  107. * Each element of the list is required/guaranteed to be of type
  108. * java.io.File.
  109. */
  110. public static final DataFlavor javaFileListFlavor = createConstant("application/x-java-file-list;class=java.util.List", null);
  111. /**
  112. * to transfer a reference to an arbitrary Java object reference that
  113. * has no associated MIME Content-type, across a Transferable interface
  114. * WITHIN THE SAME JVM, a DataFlavor with this type/subtype is used,
  115. * with a representationClass equal to the type of the class/interface
  116. * being passed across the Transferble.
  117. *
  118. * The object reference returned from Transferable.getTransferData()
  119. * for a DataFlavor with this MIME Content-Type is required to be
  120. * an instanceof the representation Class of the DataFlavor.
  121. */
  122. public static final String javaJVMLocalObjectMimeType = "application/x-java-jvm-local-objectref";
  123. /**
  124. * In order to pass a live link to a Remote object via a Drag and Drop
  125. * ACTION_LINK operation a Mime Content Type of application/x-java-remote-object
  126. * should be used, where the representation class of the DataFlavor
  127. * represents the type of the Remote interface to be transferred.
  128. */
  129. public static final String javaRemoteObjectMimeType = "application/x-java-remote-object";
  130. /**
  131. * Construct a fully specified DataFlavor
  132. */
  133. private DataFlavor(String primaryType, String subType, MimeTypeParameterList params, Class representationClass, String humanPresentableName) {
  134. super();
  135. if (params == null) params = new MimeTypeParameterList();
  136. params.set("class", representationClass.getName());
  137. if (humanPresentableName == null) {
  138. humanPresentableName = (String)params.get("humanPresentableName");
  139. if (humanPresentableName == null)
  140. humanPresentableName = primaryType + "/" + subType;
  141. }
  142. try {
  143. mimeType = new MimeType(primaryType, subType, params);
  144. } catch (MimeTypeParseException mtpe) {
  145. throw new IllegalArgumentException("MimeType Parse Exception: " + mtpe.getMessage());
  146. }
  147. this.representationClass = representationClass;
  148. this.humanPresentableName = humanPresentableName;
  149. mimeType.removeParameter("humanPresentableName");
  150. }
  151. /**
  152. * Construct a DataFlavor that represents a Java class
  153. * <p>
  154. * The returned DataFlavor will have the following characteristics
  155. * <p>
  156. * representationClass = representationClass<br>
  157. * mimeType = application/x-java-serialized-object
  158. * <p>
  159. * @param representationClass the class used to transfer data in this flavor
  160. * @param humanPresentableName the human-readable string used to identify
  161. * this flavor.
  162. * If this parameter is null then the value of the
  163. * the MIME Content Type is used.
  164. */
  165. public DataFlavor(Class representationClass, String humanPresentableName) {
  166. this("application", "x-java-serialized-object", null, representationClass, humanPresentableName);
  167. }
  168. /**
  169. * Construct a DataFlavor that represents a MimeType
  170. * <p>
  171. * The returned DataFlavor will have the following characteristics:
  172. * <p>
  173. * If the mimeType is
  174. * "application/x-java-serialized-object; class=<representation class>",
  175. * the result is the same as calling
  176. * new DataFlavor(Class:forName(<representation class>) as above
  177. * <p>
  178. * otherwise:
  179. * <p>
  180. * representationClass = InputStream<br>
  181. * mimeType = mimeType
  182. * <p>
  183. * @param mimeType the string used to identify the MIME type for this flavor.
  184. * If the the mimeType does not specify a
  185. * "class=" parameter, or if the class is not successfully
  186. * loaded, then an IllegalArgumentException is thrown.
  187. * @param humanPresentableName the human-readable string used to identify
  188. * this flavor.
  189. * If this parameter is null then the value of the
  190. * the MIME Content Type is used.
  191. */
  192. public DataFlavor(String mimeType, String humanPresentableName) {
  193. super();
  194. try {
  195. initialize(mimeType, humanPresentableName, this.getClass().getClassLoader());
  196. } catch (MimeTypeParseException mtpe) {
  197. throw new IllegalArgumentException("failed to parse:" + mimeType);
  198. } catch (ClassNotFoundException cnfe) {
  199. throw new IllegalArgumentException("cant find specified class: " + cnfe.getMessage());
  200. }
  201. }
  202. /**
  203. * Construct a DataFlavor that represents a MimeType
  204. * <p>
  205. * The returned DataFlavor will have the following characteristics:
  206. * <p>
  207. * If the mimeType is
  208. * "application/x-java-serialized-object; class=<representation class>",
  209. * the result is the same as calling
  210. * new DataFlavor(Class:forName(<representation class>) as above
  211. * <p>
  212. * otherwise:
  213. * <p>
  214. * representationClass = InputStream<br>
  215. * mimeType = mimeType
  216. * <p>
  217. * @param mimeType the string used to identify the MIME type for this flavor
  218. * @param humanPresentableName the human-readible string used to identify this flavor
  219. */
  220. public DataFlavor(String mimeType, String humanPresentableName, ClassLoader classLoader) throws ClassNotFoundException {
  221. super();
  222. try {
  223. initialize(mimeType, humanPresentableName, classLoader);
  224. } catch (MimeTypeParseException mtpe) {
  225. throw new IllegalArgumentException("failed to parse:" + mimeType);
  226. }
  227. }
  228. /**
  229. * Construct a DataFlavor from a Mime Type string.
  230. * The string must specify a "class=<fully specified Java class name>"
  231. * parameter in order to succeed in constructing a DataFlavor.
  232. *
  233. * @param mimeType the string used to identify the MIME type for this flavor.
  234. * If the the mimeType does not specify a
  235. * "class=" parameter, or if the class is not successfully
  236. * loaded, then an IllegalArgumentException is thrown.
  237. */
  238. public DataFlavor(String mimeType) throws ClassNotFoundException {
  239. super();
  240. try {
  241. initialize(mimeType, null, this.getClass().getClassLoader());
  242. } catch (MimeTypeParseException mtpe) {
  243. throw new IllegalArgumentException("failed to parse:" + mimeType);
  244. }
  245. }
  246. /**
  247. * common initialization code called from various constructors.
  248. *
  249. * @param mimeType The MIME Content Type (must have a class= param)
  250. * @param humanPresentableName The human Presentable Name or null
  251. * @param classLoader The fallback class loader to resolve against
  252. *
  253. * @throws MimeTypeParseException
  254. * @throws ClassNotFoundException
  255. *
  256. * @see tryToLoadClass
  257. */
  258. private void initialize(String mimeType, String humanPresentableName, ClassLoader classLoader) throws MimeTypeParseException, ClassNotFoundException {
  259. this.mimeType = new MimeType(mimeType); // throws
  260. String rcn = getParameter("class");
  261. if (rcn == null) {
  262. if ("application/x-java-serialized-object".equals(this.mimeType.getBaseType()))
  263. throw new IllegalArgumentException("no representation class specified for:" + mimeType);
  264. else
  265. representationClass = java.io.InputStream.class; // default
  266. } else { // got a class name
  267. representationClass = DataFlavor.tryToLoadClass(rcn, classLoader);
  268. }
  269. this.mimeType.setParameter("class", representationClass.getName());
  270. if (humanPresentableName == null) {
  271. humanPresentableName = this.mimeType.getParameter("humanPresentableName");
  272. if (humanPresentableName == null)
  273. humanPresentableName = this.mimeType.getPrimaryType() + "/" + this.mimeType.getSubType();
  274. }
  275. this.humanPresentableName = humanPresentableName; // set it.
  276. this.mimeType.removeParameter("humanPresentableName"); // just in case
  277. }
  278. /**
  279. * used by clone implementation
  280. */
  281. private DataFlavor(MimeType mt, Class rc, String hrn, int a) {
  282. super();
  283. mimeType = mt;
  284. representationClass = rc;
  285. humanPresentableName = hrn;
  286. atom = a;
  287. }
  288. public String toString() {
  289. String string = getClass().getName();
  290. string += "["+paramString()+"]";
  291. return string;
  292. }
  293. private String paramString() {
  294. String params = "";
  295. params += "representationclass=";
  296. if (representationClass == null) {
  297. params += "null";
  298. } else {
  299. params += representationClass.getName();
  300. }
  301. params += ",mimetype=["+getMimeType()+"]";
  302. return params;
  303. }
  304. /**
  305. * Returns the MIME type string for this DataFlavor
  306. */
  307. public String getMimeType() {
  308. return mimeType.toString();
  309. }
  310. /**
  311. * Returns the Class which objects supporting this DataFlavor
  312. * will return when this DataFlavor is requested.
  313. */
  314. public Class getRepresentationClass() {
  315. return representationClass;
  316. }
  317. /**
  318. * Returns the human presentable name for the data foramt that this
  319. * DataFlavor represents. This name would be localized for different
  320. * countries
  321. */
  322. public String getHumanPresentableName() {
  323. return humanPresentableName;
  324. }
  325. /**
  326. * @return the primary MIME type of this DataFlavor
  327. */
  328. public String getPrimaryType() { return mimeType.getPrimaryType(); }
  329. /**
  330. * @return the Sub MIME type of this DataFlavor
  331. */
  332. public String getSubType() { return mimeType.getSubType(); }
  333. /**
  334. * @return the value of the name parameter
  335. */
  336. public String getParameter(String paramName) {
  337. return paramName.equals("humanPresentableName") ? humanPresentableName : mimeType.getParameter(paramName);
  338. }
  339. /**
  340. * Sets the human presentable name for the data format that this
  341. * DataFlavor represents. This name would be localized for different
  342. * countries
  343. */
  344. public void setHumanPresentableName(String humanPresentableName) {
  345. this.humanPresentableName = humanPresentableName;
  346. }
  347. /**
  348. * If the object is an instance of DataFlavor, representationClass will be
  349. * compared while it will not be if the object is a String.
  350. *
  351. * @return if the objects are equal
  352. */
  353. public boolean equals(Object o) {
  354. return ((o instanceof DataFlavor) && equals((DataFlavor)o)) ||
  355. ((o instanceof String) && equals((String)o));
  356. }
  357. /**
  358. * Compare this DataFlavor against another DataFlavor object
  359. * both mimeType and representationClass are considered
  360. *
  361. * @return if the DataFlavors represent the same type.
  362. */
  363. public boolean equals(DataFlavor dataFlavor) {
  364. if (dataFlavor == null)
  365. return false;
  366. if (representationClass == null) {
  367. if (dataFlavor.getRepresentationClass() != null) {
  368. return false;
  369. } else if (mimeType == null) {
  370. return dataFlavor.mimeType == null;
  371. } else return mimeType.match(dataFlavor.mimeType);
  372. } else if (mimeType == null) {
  373. return representationClass.equals(dataFlavor.getRepresentationClass())
  374. && dataFlavor.mimeType == null;
  375. }
  376. return mimeType.match(dataFlavor.mimeType)
  377. && representationClass.equals(dataFlavor.getRepresentationClass());
  378. }
  379. /**
  380. * Compare only the mineType against the passed in String
  381. * and representationClass is not considered in the comparison.
  382. * If representationClass needs to be compared, then
  383. * equals(new DataFlavor(s))
  384. * may be used.
  385. *
  386. * @return if the String (MimeType) is equal
  387. */
  388. public boolean equals(String s) {
  389. if (s == null || mimeType == null)
  390. return false;
  391. return isMimeTypeEqual(s);
  392. }
  393. /**
  394. * Is the string representation of the MIME type passed in equivalent
  395. * to the MIME type of this DataFlavor? Parameters are not incuded in
  396. * the comparison. This may involve adding default
  397. * attributes for some MIME types (like adding charset=US-ASCII to
  398. * text/plain MIME types that have no charset parameter specified)
  399. */
  400. public boolean isMimeTypeEqual(String mimeType) {
  401. try {
  402. return this.mimeType.match(new MimeType(mimeType));
  403. } catch (MimeTypeParseException mtpe) {
  404. return false;
  405. }
  406. }
  407. /**
  408. * Compare the mimeType of two DataFlavor objects
  409. * no parameters are considered
  410. *
  411. * @return if the MimeTypes are equal
  412. */
  413. public final boolean isMimeTypeEqual(DataFlavor dataFlavor) {
  414. return isMimeTypeEqual(dataFlavor.mimeType);
  415. }
  416. /**
  417. * Compare the mimeType of two DataFlavor objects
  418. * no parameters are considered
  419. *
  420. * @return if the MimeTypes are equal
  421. */
  422. private boolean isMimeTypeEqual(MimeType mtype) {
  423. return mimeType.match(mtype);
  424. }
  425. /**
  426. * does the DataFlavor represent a serialized object?
  427. */
  428. public boolean isMimeTypeSerializedObject() {
  429. if (mimeType == null)
  430. return false;
  431. return isMimeTypeEqual(javaSerializedObjectMimeType);
  432. }
  433. /**
  434. * does the DataFlavor represent a java.io.InputStream
  435. */
  436. public boolean isRepresentationClassInputStream() {
  437. return ioInputStreamClass.isAssignableFrom(representationClass);
  438. }
  439. /**
  440. * @return true if the representation class can be serialized
  441. */
  442. public boolean isRepresentationClassSerializable() {
  443. return java.io.Serializable.class.isAssignableFrom(representationClass);
  444. }
  445. /**
  446. * @return true if the representation class is Remote
  447. */
  448. public boolean isRepresentationClassRemote() {
  449. return java.rmi.Remote.class.isAssignableFrom(representationClass);
  450. }
  451. /**
  452. * @return if the DataFlavor specified represents a Serialized Object
  453. */
  454. public boolean isFlavorSerializedObjectType() {
  455. return isRepresentationClassSerializable() && equals(javaSerializedObjectMimeType);
  456. }
  457. /**
  458. * @return if the DataFlavor specified represents a Remote Object
  459. */
  460. public boolean isFlavorRemoteObjectType() {
  461. return isRepresentationClassRemote()
  462. && isRepresentationClassSerializable()
  463. && equals(javaRemoteObjectMimeType);
  464. }
  465. /**
  466. * @return if flavor specified represents a List of File objects
  467. */
  468. public boolean isFlavorJavaFileListType() {
  469. if (mimeType == null || representationClass == null)
  470. return false;
  471. return java.util.List.class.isAssignableFrom(representationClass) &&
  472. mimeType.match(javaFileListFlavor.mimeType);
  473. }
  474. /**
  475. * Serialize this DataFlavor
  476. */
  477. public synchronized void writeExternal(ObjectOutput os) throws IOException {
  478. mimeType.setParameter("humanPresentableName", humanPresentableName);
  479. os.writeObject(mimeType);
  480. mimeType.removeParameter("humanPresentableName");
  481. }
  482. /**
  483. * restore this DataFlavor from an Serialized state
  484. */
  485. public synchronized void readExternal(ObjectInput is) throws IOException , ClassNotFoundException {
  486. mimeType = (MimeType)is.readObject();
  487. humanPresentableName = mimeType.getParameter("humanPresentableName");
  488. mimeType.removeParameter("humanPresentableName");
  489. String rcn = mimeType.getParameter("class");
  490. if (rcn == null) throw new IOException("no class parameter specified in: " + mimeType);
  491. DataFlavor.tryToLoadClass(rcn, this.getClass().getClassLoader());
  492. }
  493. public DataFlavor() { super(); } // this is here for serialization only.
  494. /**
  495. * @return a clone of this DataFlavor
  496. */
  497. public Object clone() throws CloneNotSupportedException {
  498. if (mimeType == null)
  499. return new DataFlavor((MimeType)null, representationClass, humanPresentableName, atom);
  500. return new DataFlavor((MimeType)mimeType.clone(), representationClass, humanPresentableName, atom);
  501. }
  502. /**
  503. * Called on DataFlavor for every MIME Type parameter to allow DataFlavor
  504. * subclasses to handle special parameters like the text/plain charset
  505. * parameters, whose values are case insensitive. (MIME type parameter
  506. * values are supposed to be case sensitive.
  507. * <p>
  508. * This method is called for each parameter name/value pair and should
  509. * return the normalized representation of the parameterValue
  510. *
  511. * This method is never invoked by this implementation from 1.1 onwards
  512. *
  513. * @deprecated
  514. */
  515. protected String normalizeMimeTypeParameter(String parameterName, String parameterValue) {
  516. return parameterName+"="+parameterValue;
  517. }
  518. /**
  519. * Called for each MIME type string to give DataFlavor subtypes the
  520. * opportunity to change how the normalization of MIME types is accomplished.
  521. * One possible use would be to add default parameter/value pairs in cases
  522. * where none are present in the MIME type string passed in
  523. *
  524. * This method is never invoked by this implementation from 1.1 onwards
  525. *
  526. * @deprecated
  527. */
  528. protected String normalizeMimeType(String mimeType) {
  529. return mimeType;
  530. }
  531. /*
  532. * fields
  533. */
  534. /* placeholder for caching any platform-specific data for flavor */
  535. transient int atom;
  536. /* Mime Type of DataFlavor */
  537. MimeType mimeType;
  538. /** Human-presentable name for this DataFlavor. Localizable. **/
  539. private String humanPresentableName;
  540. /** Java class of objects this DataFlavor represents **/
  541. private Class representationClass;
  542. }