001: /*
002: * @(#)DataFlavor.java 1.22 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.awt.datatransfer;
029:
030: import java.awt.Toolkit;
031: import java.io.*;
032: import java.util.*;
033: import sun.awt.SunToolkit;
034:
035: /**
036: * Each instance represents the opaque concept of a data format as would
037: * appear on a clipboard, during drag and drop, or in a file system.
038: * <p>
039: * <code>DataFlavor</code> objects are constant and never change once
040: * instantiated.
041: * </p>
042: *
043: * @version 1.53, 02/09/01
044: * @author Blake Sullivan
045: * @author Laurence P. G. Cable
046: * @author Jeff Dunn
047: */
048: public class DataFlavor implements Externalizable, Cloneable {
049:
050: private static final long serialVersionUID = 8367026044764648243L;
051: private static final Class ioInputStreamClass = java.io.InputStream.class;
052:
053: /**
054: * tried to load a class from: the bootstrap loader, the system loader,
055: * the context loader (if one is present) and finally the loader specified
056: *
057: * @param fallback the fallback loader
058: *
059: * @throws ClassNotFoundException
060: */
061: private final static Class tryToLoadClass(String className,
062: ClassLoader fallback) throws ClassNotFoundException {
063: ClassLoader systemClassLoader = (ClassLoader) java.security.AccessController
064: .doPrivileged(new java.security.PrivilegedAction() {
065: public Object run() {
066: ClassLoader cl = Thread.currentThread()
067: .getContextClassLoader();
068:
069: return (cl != null) ? cl : ClassLoader
070: .getSystemClassLoader();
071: }
072: });
073:
074: try {
075: return Class.forName(className, true, systemClassLoader);
076: } catch (ClassNotFoundException e2) {
077: if (fallback != null) {
078: return Class.forName(className, true, fallback);
079: } else {
080: throw new ClassNotFoundException(className);
081: }
082: }
083: }
084:
085: /*
086: * private initializer
087: */
088:
089: static private DataFlavor createConstant(Class rc, String prn) {
090: try {
091: return new DataFlavor(rc, prn);
092: } catch (Exception e) {
093: return null;
094: }
095: }
096:
097: /*
098: * private initializer
099: */
100:
101: static private DataFlavor createConstant(String mt, String prn) {
102: try {
103: return new DataFlavor(mt, prn);
104: } catch (Exception e) {
105: return null;
106: }
107: }
108:
109: /**
110: * The DataFlavor representing a Java Unicode String class, where:
111: * <p>
112: * representationClass = java.lang.String<br>
113: * mimeType = "application/x-java-serialized-object"
114: * <p>
115: */
116:
117: public static final DataFlavor stringFlavor = createConstant(
118: java.lang.String.class, "Unicode String");
119:
120: /**
121: * The DataFlavor representing plain text with unicode encoding, where:
122: * <p>
123: * representationClass = InputStream<br>
124: * mimeType = "text/plain; charset=unicode"
125: * <p>
126: * This DataFlavor has been <b>deprecated</b> because (1) Its
127: * representation is an InputStream, an 8-bit based representation,
128: * while Unicode is a 16-bit character set; and (2) The charset "unicode"
129: * is not well-defined. "unicode" implies a particular platform's
130: * implementation of Unicode, not a cross-platform implementation.
131: *
132: * @deprecated as of 1.3. Use <code>DataFlavor.getReaderForText(
133: * Transferable)</code> instead of <code>Transferable.
134: * getTransferData(DataFlavor.plainTextFlavor)</code>.
135: */
136:
137: public static final DataFlavor plainTextFlavor = createConstant(
138: "text/plain; charset=unicode; class=java.io.InputStream",
139: "Plain Text");
140:
141: /**
142: * Constructs a new DataFlavor. This constructor is provided only for
143: * the purpose of supporting the Externalizable interface. It is not
144: * intended for public (client) use.
145: *
146: * @since 1.2
147: */
148: public DataFlavor() {
149: super ();
150: }
151:
152: /**
153: * Cloning constructor. Package-private.
154: */
155: DataFlavor(DataFlavor that) {
156: this .mimeType = null;
157: if (that.mimeType != null) {
158: try {
159: this .mimeType = (MimeType) that.mimeType.clone();
160: } catch (CloneNotSupportedException e) {
161: }
162: }
163: this .representationClass = that.representationClass;
164: this .humanPresentableName = that.humanPresentableName;
165: this .atom = that.atom;
166: } // DataFlavor()
167:
168: /**
169: * Construct a fully specified DataFlavor
170: */
171: private DataFlavor(String primaryType, String subType,
172: MimeTypeParameterList params, Class representationClass,
173: String humanPresentableName) {
174: super ();
175:
176: if (params == null)
177: params = new MimeTypeParameterList();
178:
179: params.set("class", representationClass.getName());
180:
181: if (humanPresentableName == null) {
182: humanPresentableName = (String) params
183: .get("humanPresentableName");
184:
185: if (humanPresentableName == null)
186: humanPresentableName = primaryType + "/" + subType;
187: }
188:
189: try {
190: mimeType = new MimeType(primaryType, subType, params);
191: } catch (MimeTypeParseException mtpe) {
192: throw new IllegalArgumentException(
193: "MimeType Parse Exception: " + mtpe.getMessage());
194: }
195:
196: this .representationClass = representationClass;
197: this .humanPresentableName = humanPresentableName;
198:
199: mimeType.removeParameter("humanPresentableName");
200: }
201:
202: /**
203: * Construct a DataFlavor that represents a Java class
204: * <p>
205: * The returned DataFlavor will have the following characteristics
206: * <p>
207: * representationClass = representationClass<br>
208: * mimeType = application/x-java-serialized-object
209: * <p>
210: * @param representationClass the class used to transfer data in this flavor
211: * @param humanPresentableName the human-readable string used to identify
212: * this flavor.
213: * If this parameter is null then the value of the
214: * the MIME Content Type is used.
215: */
216: public DataFlavor(Class representationClass,
217: String humanPresentableName) {
218: this ("application", "x-java-serialized-object", null,
219: representationClass, humanPresentableName);
220: }
221:
222: /**
223: * Construct a DataFlavor that represents a MimeType
224: * <p>
225: * The returned DataFlavor will have the following characteristics:
226: * <p>
227: * If the mimeType is
228: * "application/x-java-serialized-object; class=<representation class>",
229: * the result is the same as calling
230: * new DataFlavor(Class:forName(<representation class>) as above
231: * <p>
232: * otherwise:
233: * <p>
234: * representationClass = InputStream<br>
235: * mimeType = mimeType
236: * <p>
237: * @param mimeType the string used to identify the MIME type for this flavor.
238: * If the the mimeType does not specify a
239: * "class=" parameter, or if the class is not successfully
240: * loaded, then an IllegalArgumentException is thrown.
241: * @param humanPresentableName the human-readable string used to identify
242: * this flavor.
243: * If this parameter is null then the value of the
244: * the MIME Content Type is used.
245: */
246: public DataFlavor(String mimeType, String humanPresentableName) {
247: super ();
248:
249: try {
250: initialize(mimeType, humanPresentableName, this .getClass()
251: .getClassLoader());
252: } catch (MimeTypeParseException mtpe) {
253: throw new IllegalArgumentException("failed to parse:"
254: + mimeType);
255: } catch (ClassNotFoundException cnfe) {
256: throw new IllegalArgumentException(
257: "cant find specified class: " + cnfe.getMessage());
258: }
259: }
260:
261: /**
262: * common initialization code called from various constructors.
263: *
264: * @param mimeType The MIME Content Type (must have a class= param)
265: * @param humanPresentableName The human Presentable Name or null
266: * @param classLoader The fallback class loader to resolve against
267: *
268: * @throws MimeTypeParseException
269: * @throws ClassNotFoundException
270: *
271: * @see tryToLoadClass
272: */
273: private void initialize(String mimeType,
274: String humanPresentableName, ClassLoader classLoader)
275: throws MimeTypeParseException, ClassNotFoundException {
276:
277: this .mimeType = new MimeType(mimeType); // throws
278:
279: String rcn = getParameter("class");
280:
281: if (rcn == null) {
282: if ("application/x-java-serialized-object"
283: .equals(this .mimeType.getBaseType()))
284:
285: throw new IllegalArgumentException(
286: "no representation class specified for:"
287: + mimeType);
288: else
289: representationClass = java.io.InputStream.class; // default
290: } else { // got a class name
291: representationClass = DataFlavor.tryToLoadClass(rcn,
292: classLoader);
293: }
294:
295: this .mimeType.setParameter("class", representationClass
296: .getName());
297:
298: if (humanPresentableName == null) {
299: humanPresentableName = this .mimeType
300: .getParameter("humanPresentableName");
301: if (humanPresentableName == null)
302: humanPresentableName = this .mimeType.getPrimaryType()
303: + "/" + this .mimeType.getSubType();
304: }
305:
306: this .humanPresentableName = humanPresentableName; // set it.
307:
308: this .mimeType.removeParameter("humanPresentableName"); // just in case
309: }
310:
311: /**
312: * used by clone implementation
313: */
314:
315: private DataFlavor(MimeType mt, Class rc, String hrn, int a) {
316: super ();
317:
318: mimeType = mt;
319: representationClass = rc;
320: humanPresentableName = hrn;
321: atom = a;
322: }
323:
324: /**
325: * String representation of this <code>DataFlavor</code>
326: * and its parameters. The result String contains name of
327: * <code>DataFlavor</code> class, representation class
328: * and Mime type of this Flavor.
329: *
330: * @return string representation of this <code>DataFlavor</code>
331: */
332: public String toString() {
333: String string = getClass().getName();
334: string += "[" + paramString() + "]";
335: return string;
336: }
337:
338: private String paramString() {
339: String params = "";
340: params += "representationclass=";
341: if (representationClass == null) {
342: params += "null";
343: } else {
344: params += representationClass.getName();
345: }
346: params += ";mimetype=";
347: if (mimeType == null) {
348: params += "null";
349: } else {
350: params += mimeType.getBaseType();
351: }
352: return params;
353: }
354:
355: /**
356: * Returns the MIME type string for this DataFlavor
357: */
358: public String getMimeType() {
359: return mimeType.toString();
360: }
361:
362: /**
363: * Returns the Class which objects supporting this DataFlavor
364: * will return when this DataFlavor is requested.
365: */
366: public Class getRepresentationClass() {
367: return representationClass;
368: }
369:
370: /**
371: * Returns the human presentable name for the data foramt that this
372: * DataFlavor represents. This name would be localized for different
373: * countries
374: */
375: public String getHumanPresentableName() {
376: return humanPresentableName;
377: }
378:
379: /**
380: * @return the value of the name parameter
381: */
382:
383: private String getParameter(String paramName) {
384: return paramName.equals("humanPresentableName") ? humanPresentableName
385: : mimeType.getParameter(paramName);
386: }
387:
388: /**
389: * Sets the human presentable name for the data format that this
390: * DataFlavor represents. This name would be localized for different
391: * countries
392: */
393:
394: public void setHumanPresentableName(String humanPresentableName) {
395: this .humanPresentableName = humanPresentableName;
396: }
397:
398: /**
399: * If the object is an instance of DataFlavor, representationClass
400: * and MIME type will be compared.
401: * This method does not use equals(String) method, so it does not
402: * return <code>true</code> for the objects of String type.
403: *
404: * @return if the objects are equal
405: */
406:
407: public boolean equals(Object o) {
408: return ((o instanceof DataFlavor) && equals((DataFlavor) o));
409: }
410:
411: /**
412: * Two DataFlavors are considered equal if and only if their
413: * MIME primary type and subtype and representation class are
414: * equal. Additionally, if the primary type is "text", the
415: * charset parameter must also be equal. If
416: * either DataFlavor is of primary type "text", but no charset
417: * is specified, the platform default charset is assumed for
418: * that DataFlavor.
419: *
420: * @return if the DataFlavors represent exactly the same type.
421: */
422: public boolean equals(DataFlavor that) {
423: if (that == null) {
424: return false;
425: }
426: if (this == that) {
427: return true;
428: }
429:
430: if (representationClass == null) {
431: if (that.getRepresentationClass() != null) {
432: return false;
433: }
434: } else {
435: if (!representationClass.equals(that
436: .getRepresentationClass())) {
437: return false;
438: }
439: }
440:
441: if (mimeType == null) {
442: if (that.mimeType != null) {
443: return false;
444: }
445: } else {
446: if (!mimeType.match(that.mimeType)) {
447: return false;
448: }
449:
450: }
451:
452: return true;
453: }
454:
455: /**
456: * Returns hash code for this <code>DataFlavor</code>.
457: * For two equal DataFlavors, hash codes are equal. For the String
458: * that matches <code>DataFlavor.equals(String)</code>, it is not
459: * guaranteed that DataFlavor's hash code is equal to the hash code
460: * of the String.
461: *
462: * @return a hash code for this DataFlavor
463: */
464: public int hashCode() {
465: int representationClassPortion = 0, mimeTypePortion = 0, charsetPortion = 0;
466:
467: if (representationClass != null) {
468: representationClassPortion = representationClass.hashCode();
469: }
470:
471: if (mimeType != null) {
472: String primaryType = mimeType.getPrimaryType();
473:
474: if (primaryType != null) {
475: mimeTypePortion = primaryType.hashCode();
476: }
477: }
478:
479: int total = representationClassPortion + mimeTypePortion
480: + charsetPortion;
481:
482: return (total != 0) ? total : 25431009;
483: }
484:
485: /**
486: * Returns the primary MIME type for this <code>DataFlavor</code>.
487: * @return the primary MIME type of this <code>DataFlavor</code>
488: */
489:
490: public String getPrimaryType() {
491: return (mimeType != null) ? mimeType.getPrimaryType() : null;
492: }
493:
494: /**
495: * Returns the sub MIME type of this <code>DataFlavor</code>.
496: * @return the Sub MIME type of this <code>DataFlavor</code>
497: */
498:
499: public String getSubType() {
500: return (mimeType != null) ? mimeType.getSubType() : null;
501: }
502:
503: /*
504: * returns the charset parameter for flavors with "text" as the
505: * primary type. If the primary type is text, and no charset
506: * is defined, the default charset for the platform is returned.
507: *
508: * If the primary type is not "text", always returns null.
509: */
510: private String getTextCharset() {
511: String charset = null;
512: if ("text".equals(getPrimaryType())) {
513: charset = getParameter("charset");
514: if (charset == null) {
515: Toolkit toolkit = Toolkit.getDefaultToolkit();
516: if (toolkit instanceof SunToolkit) {
517: charset = ((SunToolkit) toolkit)
518: .getDefaultCharacterEncoding();
519: }
520: }
521: }
522: return charset;
523: } // getTextCharset()
524:
525: /**
526: * Two DataFlavors match if their primary types, subtypes,
527: * and representation classes are all equal. Additionally, if
528: * the primary type is "text", the charset parameter is also
529: * considered. If either DataFlavor is of primary type "text",
530: * but no charset is specified, the platform default charset
531: * is assumed for that DataFlavor.
532: */
533: public boolean match(DataFlavor that) {
534: if (that == null) {
535: return false;
536: }
537:
538: if ((this .mimeType == null) || (that.mimeType == null)) {
539: return false;
540: }
541:
542: String this PrimaryType = this .getPrimaryType();
543: String thatPrimaryType = that.getPrimaryType();
544: if ((this PrimaryType == null) || (thatPrimaryType == null)
545: || (!this PrimaryType.equals(thatPrimaryType))) {
546: return false;
547: }
548:
549: String this SubType = this .getSubType();
550: String thatSubType = that.getSubType();
551: if ((this SubType == null) || (thatSubType == null)
552: || (!this SubType.equals(thatSubType))) {
553: return false;
554: }
555:
556: Class this RepresentationClass = this .getRepresentationClass();
557: Class thatRepresentationClass = that.getRepresentationClass();
558:
559: if ((this RepresentationClass == null)
560: || (thatRepresentationClass == null)
561: || (!this RepresentationClass
562: .equals(thatRepresentationClass))) {
563: return false;
564: }
565:
566: if (this PrimaryType.equals("text")) {
567: String this Charset = this .getTextCharset();
568: String thatCharset = that.getTextCharset();
569: if ((this Charset == null) || (thatCharset == null)
570: || (!this Charset.equals(thatCharset))) {
571: return false;
572: }
573: }
574:
575: return true;
576: } // match()
577:
578: /**
579: * Returns whether the string representation of the MIME type passed in
580: * is equivalent to the MIME type of this <code>DataFlavor</code>.
581: * Parameters are not incuded in the comparison. The comparison may involve
582: * adding default attributes for some MIME types (such as adding
583: * <code>charset=US-ASCII</code> to text/plain MIME types that have
584: * no <code>charset</code> parameter specified).
585: *
586: * @param mimeType the string representation of the MIME type
587: * @return true if the string representation of the MIME type passed in is
588: * equivalent to the MIME type of this <code>DataFlavor</code>;
589: * false otherwise.
590: * @throws NullPointerException if mimeType is <code>null</code>
591: */
592: public boolean isMimeTypeEqual(String mimeType) {
593: // JCK Test DataFlavor0117: if 'mimeType' is null, throw NPE
594: if (mimeType == null) {
595: throw new NullPointerException("mimeType");
596: }
597: if (this .mimeType == null) {
598: return false;
599: }
600: try {
601: return this .mimeType.match(new MimeType(mimeType));
602: } catch (MimeTypeParseException mtpe) {
603: return false;
604: }
605: }
606:
607: /**
608: * Compare the mimeType of two DataFlavor objects
609: * no parameters are considered
610: *
611: * @return if the MimeTypes are equal
612: */
613:
614: public final boolean isMimeTypeEqual(DataFlavor dataFlavor) {
615: return isMimeTypeEqual(dataFlavor.mimeType);
616: }
617:
618: /**
619: * Compare the mimeType of two DataFlavor objects
620: * no parameters are considered
621: *
622: * @return if the MimeTypes are equal
623: */
624:
625: private boolean isMimeTypeEqual(MimeType mtype) {
626: return mimeType.match(mtype);
627: }
628:
629: /**
630: * Serialize this DataFlavor
631: */
632:
633: public synchronized void writeExternal(ObjectOutput os)
634: throws IOException {
635: mimeType.setParameter("humanPresentableName",
636: humanPresentableName);
637: os.writeObject(mimeType);
638: mimeType.removeParameter("humanPresentableName");
639: }
640:
641: /**
642: * restore this DataFlavor from an Serialized state
643: */
644:
645: public synchronized void readExternal(ObjectInput is)
646: throws IOException, ClassNotFoundException {
647: mimeType = (MimeType) is.readObject();
648:
649: humanPresentableName = mimeType
650: .getParameter("humanPresentableName");
651:
652: mimeType.removeParameter("humanPresentableName");
653:
654: String rcn = mimeType.getParameter("class");
655:
656: if (rcn == null)
657: throw new IOException("no class parameter specified in: "
658: + mimeType);
659:
660: representationClass = DataFlavor.tryToLoadClass(rcn, this
661: .getClass().getClassLoader());
662: }
663:
664: /**
665: * @return a clone of this DataFlavor
666: */
667:
668: public Object clone() throws CloneNotSupportedException {
669: DataFlavor clonedFlavor = new DataFlavor(this );
670: return clonedFlavor;
671: } // clone()
672:
673: /**
674: * Called on DataFlavor for every MIME Type parameter to allow DataFlavor
675: * subclasses to handle special parameters like the text/plain charset
676: * parameters, whose values are case insensitive. (MIME type parameter
677: * values are supposed to be case sensitive.
678: * <p>
679: * This method is called for each parameter name/value pair and should
680: * return the normalized representation of the parameterValue
681: *
682: * This method is never invoked by this implementation from 1.1 onwards
683: *
684: * @deprecated
685: */
686: protected String normalizeMimeTypeParameter(String parameterName,
687: String parameterValue) {
688: return parameterValue;
689: }
690:
691: /**
692: * Called for each MIME type string to give DataFlavor subtypes the
693: * opportunity to change how the normalization of MIME types is accomplished.
694: * One possible use would be to add default parameter/value pairs in cases
695: * where none are present in the MIME type string passed in
696: *
697: * This method is never invoked by this implementation from 1.1 onwards
698: *
699: * @deprecated
700: */
701: protected String normalizeMimeType(String mimeType) {
702: return mimeType;
703: }
704:
705: //DEBUG void debugTestMimeEquals(DataFlavor that) {
706: //DEBUG String areThey = "?????";
707: //DEBUG if ((this.mimeType != null) && (that.mimeType != null)) {
708: //DEBUG if (this.mimeType.equals(that.mimeType)) {
709: //DEBUG areThey = " TRUE";
710: //DEBUG } else {
711: //DEBUG areThey = "FALSE";
712: //DEBUG }
713: //DEBUG }
714: //DEBUG System.out.println(areThey + ": " + this.mimeType);
715: //DEBUG System.out.println(" "+ ": " + that.mimeType);
716: //DEBUG }
717:
718: /*
719: * fields
720: */
721:
722: /* placeholder for caching any platform-specific data for flavor */
723:
724: transient int atom;
725:
726: /* Mime Type of DataFlavor */
727:
728: MimeType mimeType;
729:
730: private String humanPresentableName;
731:
732: /** Java class of objects this DataFlavor represents **/
733:
734: private Class representationClass;
735:
736: } // class DataFlavor
|