History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/awt/datatransfer/SystemFlavorMap.java (1 of 18352 files). Show all

Unified · Split

java/pkg/java/awt/datatransfer/SystemFlavorMap.java+1299−0
@@ -0,0 +1,1299 @@
1+/*
2+ * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package java.awt.datatransfer;
27+
28+import java.awt.Toolkit;
29+
30+import java.lang.ref.SoftReference;
31+
32+import java.io.BufferedReader;
33+import java.io.File;
34+import java.io.InputStreamReader;
35+import java.io.IOException;
36+
37+import java.net.URL;
38+import java.net.MalformedURLException;
39+
40+import java.util.ArrayList;
41+import java.util.HashMap;
42+import java.util.HashSet;
43+import java.util.Iterator;
44+import java.util.LinkedList;
45+import java.util.List;
46+import java.util.Map;
47+import java.util.Set;
48+import java.util.WeakHashMap;
49+
50+import sun.awt.datatransfer.DataTransferer;
51+
52+/**
53+ * The SystemFlavorMap is a configurable map between "natives" (Strings), which
54+ * correspond to platform-specific data formats, and "flavors" (DataFlavors),
55+ * which correspond to platform-independent MIME types. This mapping is used
56+ * by the data transfer subsystem to transfer data between Java and native
57+ * applications, and between Java applications in separate VMs.
58+ * <p>
59+ * In the Sun reference implementation, the default SystemFlavorMap is
60+ * initialized by the file <code>jre/lib/flavormap.properties</code> and the
61+ * contents of the URL referenced by the AWT property
62+ * <code>AWT.DnD.flavorMapFileURL</code>. See <code>flavormap.properties</code>
63+ * for details.
64+ *
65+ * @since 1.2
66+ */
67+public final class SystemFlavorMap implements FlavorMap, FlavorTable {
68+
69+ /**
70+ * Constant prefix used to tag Java types converted to native platform
71+ * type.
72+ */
73+ private static String JavaMIME = "JAVA_DATAFLAVOR:";
74+
75+ /**
76+ * System singleton which maps a thread's ClassLoader to a SystemFlavorMap.
77+ */
78+ private static final WeakHashMap flavorMaps = new WeakHashMap();
79+
80+ /**
81+ * Copied from java.util.Properties.
82+ */
83+ private static final String keyValueSeparators = "=: \t\r\n\f";
84+ private static final String strictKeyValueSeparators = "=:";
85+ private static final String whiteSpaceChars = " \t\r\n\f";
86+
87+ /**
88+ * The list of valid, decoded text flavor representation classes, in order
89+ * from best to worst.
90+ */
91+ private static final String[] UNICODE_TEXT_CLASSES = {
92+ "java.io.Reader", "java.lang.String", "java.nio.CharBuffer", "\"[C\""
93+ };
94+
95+ /**
96+ * The list of valid, encoded text flavor representation classes, in order
97+ * from best to worst.
98+ */
99+ private static final String[] ENCODED_TEXT_CLASSES = {
100+ "java.io.InputStream", "java.nio.ByteBuffer", "\"[B\""
101+ };
102+
103+ /**
104+ * A String representing text/plain MIME type.
105+ */
106+ private static final String TEXT_PLAIN_BASE_TYPE = "text/plain";
107+
108+ /**
109+ * This constant is passed to flavorToNativeLookup() to indicate that a
110+ * a native should be synthesized, stored, and returned by encoding the
111+ * DataFlavor's MIME type in case if the DataFlavor is not found in
112+ * 'flavorToNative' map.
113+ */
114+ private static final boolean SYNTHESIZE_IF_NOT_FOUND = true;
115+
116+ /**
117+ * Maps native Strings to Lists of DataFlavors (or base type Strings for
118+ * text DataFlavors).
119+ * Do not use the field directly, use getNativeToFlavor() instead.
120+ */
121+ private Map nativeToFlavor = new HashMap();
122+
123+ /**
124+ * Accessor to nativeToFlavor map. Since we use lazy initialization we must
125+ * use this accessor instead of direct access to the field which may not be
126+ * initialized yet. This method will initialize the field if needed.
127+ *
128+ * @return nativeToFlavor
129+ */
130+ private Map getNativeToFlavor() {
131+ if (!isMapInitialized) {
132+ initSystemFlavorMap();
133+ }
134+ return nativeToFlavor;
135+ }
136+
137+ /**
138+ * Maps DataFlavors (or base type Strings for text DataFlavors) to Lists of
139+ * native Strings.
140+ * Do not use the field directly, use getFlavorToNative() instead.
141+ */
142+ private Map flavorToNative = new HashMap();
143+
144+ /**
145+ * Accessor to flavorToNative map. Since we use lazy initialization we must
146+ * use this accessor instead of direct access to the field which may not be
147+ * initialized yet. This method will initialize the field if needed.
148+ *
149+ * @return flavorToNative
150+ */
151+ private synchronized Map getFlavorToNative() {
152+ if (!isMapInitialized) {
153+ initSystemFlavorMap();
154+ }
155+ return flavorToNative;
156+ }
157+
158+ /**
159+ * Shows if the object has been initialized.
160+ */
161+ private boolean isMapInitialized = false;
162+
163+ /**
164+ * Caches the result of getNativesForFlavor(). Maps DataFlavors to
165+ * SoftReferences which reference Lists of String natives.
166+ */
167+ private Map getNativesForFlavorCache = new HashMap();
168+
169+ /**
170+ * Caches the result getFlavorsForNative(). Maps String natives to
171+ * SoftReferences which reference Lists of DataFlavors.
172+ */
173+ private Map getFlavorsForNativeCache = new HashMap();
174+
175+ /**
176+ * Dynamic mapping generation used for text mappings should not be applied
177+ * to the DataFlavors and String natives for which the mappings have been
178+ * explicitly specified with setFlavorsForNative() or
179+ * setNativesForFlavor(). This keeps all such keys.
180+ */
181+ private Set disabledMappingGenerationKeys = new HashSet();
182+
183+ /**
184+ * Returns the default FlavorMap for this thread's ClassLoader.
185+ */
186+ public static FlavorMap getDefaultFlavorMap() {
187+ ClassLoader contextClassLoader =
188+ Thread.currentThread().getContextClassLoader();
189+ if (contextClassLoader == null) {
190+ contextClassLoader = ClassLoader.getSystemClassLoader();
191+ }
192+
193+ FlavorMap fm;
194+
195+ synchronized(flavorMaps) {
196+ fm = (FlavorMap)flavorMaps.get(contextClassLoader);
197+ if (fm == null) {
198+ fm = new SystemFlavorMap();
199+ flavorMaps.put(contextClassLoader, fm);
200+ }
201+ }
202+
203+ return fm;
204+ }
205+
206+ private SystemFlavorMap() {
207+ }
208+
209+ /**
210+ * Initializes a SystemFlavorMap by reading flavormap.properties and
211+ * AWT.DnD.flavorMapFileURL.
212+ * For thread-safety must be called under lock on this.
213+ */
214+ private void initSystemFlavorMap() {
215+ if (isMapInitialized) {
216+ return;
217+ }
218+
219+ isMapInitialized = true;
220+ BufferedReader flavormapDotProperties =
221+ java.security.AccessController.doPrivileged(
222+ new java.security.PrivilegedAction<BufferedReader>() {
223+ public BufferedReader run() {
224+ String fileName =
225+ System.getProperty("java.home") +
226+ File.separator +
227+ "lib" +
228+ File.separator +
229+ "flavormap.properties";
230+ try {
231+ return new BufferedReader
232+ (new InputStreamReader
233+ (new File(fileName).toURI().toURL().openStream(), "ISO-8859-1"));
234+ } catch (MalformedURLException e) {
235+ System.err.println("MalformedURLException:" + e + " while loading default flavormap.properties file:" + fileName);
236+ } catch (IOException e) {
237+ System.err.println("IOException:" + e + " while loading default flavormap.properties file:" + fileName);
238+ }
239+ return null;
240+ }
241+ });
242+
243+ BufferedReader flavormapURL =
244+ java.security.AccessController.doPrivileged(
245+ new java.security.PrivilegedAction<BufferedReader>() {
246+ public BufferedReader run() {
247+ String url = Toolkit.getProperty("AWT.DnD.flavorMapFileURL", null);
248+
249+ if (url == null) {
250+ return null;
251+ }
252+
253+ try {
254+ return new BufferedReader
255+ (new InputStreamReader
256+ (new URL(url).openStream(), "ISO-8859-1"));
257+ } catch (MalformedURLException e) {
258+ System.err.println("MalformedURLException:" + e + " while reading AWT.DnD.flavorMapFileURL:" + url);
259+ } catch (IOException e) {
260+ System.err.println("IOException:" + e + " while reading AWT.DnD.flavorMapFileURL:" + url);
261+ }
262+ return null;
263+ }
264+ });
265+
266+ if (flavormapDotProperties != null) {
267+ try {
268+ parseAndStoreReader(flavormapDotProperties);
269+ } catch (IOException e) {
270+ System.err.println("IOException:" + e + " while parsing default flavormap.properties file");
271+ }
272+ }
273+
274+ if (flavormapURL != null) {
275+ try {
276+ parseAndStoreReader(flavormapURL);
277+ } catch (IOException e) {
278+ System.err.println("IOException:" + e + " while parsing AWT.DnD.flavorMapFileURL");
279+ }
280+ }
281+ }
282+ /**
283+ * Copied code from java.util.Properties. Parsing the data ourselves is the
284+ * only way to handle duplicate keys and values.
285+ */
286+ private void parseAndStoreReader(BufferedReader in) throws IOException {
287+ while (true) {
288+ // Get next line
289+ String line = in.readLine();
290+ if (line == null) {
291+ return;
292+ }
293+
294+ if (line.length() > 0) {
295+ // Continue lines that end in slashes if they are not comments
296+ char firstChar = line.charAt(0);
297+ if (firstChar != '#' && firstChar != '!') {
298+ while (continueLine(line)) {
299+ String nextLine = in.readLine();
300+ if (nextLine == null) {
301+ nextLine = "";
302+ }
303+ String loppedLine =
304+ line.substring(0, line.length() - 1);
305+ // Advance beyond whitespace on new line
306+ int startIndex = 0;
307+ for(; startIndex < nextLine.length(); startIndex++) {
308+ if (whiteSpaceChars.
309+ indexOf(nextLine.charAt(startIndex)) == -1)
310+ {
311+ break;
312+ }
313+ }
314+ nextLine = nextLine.substring(startIndex,
315+ nextLine.length());
316+ line = loppedLine+nextLine;
317+ }
318+
319+ // Find start of key
320+ int len = line.length();
321+ int keyStart = 0;
322+ for(; keyStart < len; keyStart++) {
323+ if(whiteSpaceChars.
324+ indexOf(line.charAt(keyStart)) == -1) {
325+ break;
326+ }
327+ }
328+
329+ // Blank lines are ignored
330+ if (keyStart == len) {
331+ continue;
332+ }
333+
334+ // Find separation between key and value
335+ int separatorIndex = keyStart;
336+ for(; separatorIndex < len; separatorIndex++) {
337+ char currentChar = line.charAt(separatorIndex);
338+ if (currentChar == '\\') {
339+ separatorIndex++;
340+ } else if (keyValueSeparators.
341+ indexOf(currentChar) != -1) {
342+ break;
343+ }
344+ }
345+
346+ // Skip over whitespace after key if any
347+ int valueIndex = separatorIndex;
348+ for (; valueIndex < len; valueIndex++) {
349+ if (whiteSpaceChars.
350+ indexOf(line.charAt(valueIndex)) == -1) {
351+ break;
352+ }
353+ }
354+
355+ // Skip over one non whitespace key value separators if any
356+ if (valueIndex < len) {
357+ if (strictKeyValueSeparators.
358+ indexOf(line.charAt(valueIndex)) != -1) {
359+ valueIndex++;
360+ }
361+ }
362+
363+ // Skip over white space after other separators if any
364+ while (valueIndex < len) {
365+ if (whiteSpaceChars.
366+ indexOf(line.charAt(valueIndex)) == -1) {
367+ break;
368+ }
369+ valueIndex++;
370+ }
371+
372+ String key = line.substring(keyStart, separatorIndex);
373+ String value = (separatorIndex < len)
374+ ? line.substring(valueIndex, len)
375+ : "";
376+
377+ // Convert then store key and value
378+ key = loadConvert(key);
379+ value = loadConvert(value);
380+
381+ try {
382+ MimeType mime = new MimeType(value);
383+ if ("text".equals(mime.getPrimaryType())) {
384+ String charset = mime.getParameter("charset");
385+ if (DataTransferer.doesSubtypeSupportCharset
386+ (mime.getSubType(), charset))
387+ {
388+ // We need to store the charset and eoln
389+ // parameters, if any, so that the
390+ // DataTransferer will have this information
391+ // for conversion into the native format.
392+ DataTransferer transferer =
393+ DataTransferer.getInstance();
394+ if (transferer != null) {
395+ transferer.registerTextFlavorProperties
396+ (key, charset,
397+ mime.getParameter("eoln"),
398+ mime.getParameter("terminators"));
399+ }
400+ }
401+
402+ // But don't store any of these parameters in the
403+ // DataFlavor itself for any text natives (even
404+ // non-charset ones). The SystemFlavorMap will
405+ // synthesize the appropriate mappings later.
406+ mime.removeParameter("charset");
407+ mime.removeParameter("class");
408+ mime.removeParameter("eoln");
409+ mime.removeParameter("terminators");
410+ value = mime.toString();
411+ }
412+ } catch (MimeTypeParseException e) {
413+ e.printStackTrace();
414+ continue;
415+ }
416+
417+ DataFlavor flavor;
418+ try {
419+ flavor = new DataFlavor(value);
420+ } catch (Exception e) {
421+ try {
422+ flavor = new DataFlavor(value, (String)null);
423+ } catch (Exception ee) {
424+ ee.printStackTrace();
425+ continue;
426+ }
427+ }
428+
429+ // For text/* flavors, store mappings in separate maps to
430+ // enable dynamic mapping generation at a run-time.
431+ if ("text".equals(flavor.getPrimaryType())) {
432+ store(value, key, getFlavorToNative());
433+ store(key, value, getNativeToFlavor());
434+ } else {
435+ store(flavor, key, getFlavorToNative());
436+ store(key, flavor, getNativeToFlavor());
437+ }
438+ }
439+ }
440+ }
441+ }
442+
443+ /**
444+ * Copied from java.util.Properties.
445+ */
446+ private boolean continueLine (String line) {
447+ int slashCount = 0;
448+ int index = line.length() - 1;
449+ while((index >= 0) && (line.charAt(index--) == '\\')) {
450+ slashCount++;
451+ }
452+ return (slashCount % 2 == 1);
453+ }
454+
455+ /**
456+ * Copied from java.util.Properties.
457+ */
458+ private String loadConvert(String theString) {
459+ char aChar;
460+ int len = theString.length();
461+ StringBuilder outBuffer = new StringBuilder(len);
462+
463+ for (int x = 0; x < len; ) {
464+ aChar = theString.charAt(x++);
465+ if (aChar == '\\') {
466+ aChar = theString.charAt(x++);
467+ if (aChar == 'u') {
468+ // Read the xxxx
469+ int value = 0;
470+ for (int i = 0; i < 4; i++) {
471+ aChar = theString.charAt(x++);
472+ switch (aChar) {
473+ case '0': case '1': case '2': case '3': case '4':
474+ case '5': case '6': case '7': case '8': case '9': {
475+ value = (value << 4) + aChar - '0';
476+ break;
477+ }
478+ case 'a': case 'b': case 'c':
479+ case 'd': case 'e': case 'f': {
480+ value = (value << 4) + 10 + aChar - 'a';
481+ break;
482+ }
483+ case 'A': case 'B': case 'C':
484+ case 'D': case 'E': case 'F': {
485+ value = (value << 4) + 10 + aChar - 'A';
486+ break;
487+ }
488+ default: {
489+ throw new IllegalArgumentException(
490+ "Malformed \\uxxxx encoding.");
491+ }
492+ }
493+ }
494+ outBuffer.append((char)value);
495+ } else {
496+ if (aChar == 't') {
497+ aChar = '\t';
498+ } else if (aChar == 'r') {
499+ aChar = '\r';
500+ } else if (aChar == 'n') {
501+ aChar = '\n';
502+ } else if (aChar == 'f') {
503+ aChar = '\f';
504+ }
505+ outBuffer.append(aChar);
506+ }
507+ } else {
508+ outBuffer.append(aChar);
509+ }
510+ }
511+ return outBuffer.toString();
512+ }
513+
514+ /**
515+ * Stores the listed object under the specified hash key in map. Unlike a
516+ * standard map, the listed object will not replace any object already at
517+ * the appropriate Map location, but rather will be appended to a List
518+ * stored in that location.
519+ */
520+ private void store(Object hashed, Object listed, Map map) {
521+ List list = (List)map.get(hashed);
522+ if (list == null) {
523+ list = new ArrayList(1);
524+ map.put(hashed, list);
525+ }
526+ if (!list.contains(listed)) {
527+ list.add(listed);
528+ }
529+ }
530+
531+ /**
532+ * Semantically equivalent to 'nativeToFlavor.get(nat)'. This method
533+ * handles the case where 'nat' is not found in 'nativeToFlavor'. In that
534+ * case, a new DataFlavor is synthesized, stored, and returned, if and
535+ * only if the specified native is encoded as a Java MIME type.
536+ */
537+ private List nativeToFlavorLookup(String nat) {
538+ List flavors = (List)getNativeToFlavor().get(nat);
539+
540+ if (nat != null && !disabledMappingGenerationKeys.contains(nat)) {
541+ DataTransferer transferer = DataTransferer.getInstance();
542+ if (transferer != null) {
543+ List platformFlavors =
544+ transferer.getPlatformMappingsForNative(nat);
545+ if (!platformFlavors.isEmpty()) {
546+ if (flavors != null) {
547+ platformFlavors.removeAll(new HashSet(flavors));
548+ // Prepending the platform-specific mappings ensures
549+ // that the flavors added with
550+ // addFlavorForUnencodedNative() are at the end of
551+ // list.
552+ platformFlavors.addAll(flavors);
553+ }
554+ flavors = platformFlavors;
555+ }
556+ }
557+ }
558+
559+ if (flavors == null && isJavaMIMEType(nat)) {
560+ String decoded = decodeJavaMIMEType(nat);
561+ DataFlavor flavor = null;
562+
563+ try {
564+ flavor = new DataFlavor(decoded);
565+ } catch (Exception e) {
566+ System.err.println("Exception \"" + e.getClass().getName() +
567+ ": " + e.getMessage() +
568+ "\"while constructing DataFlavor for: " +
569+ decoded);
570+ }
571+
572+ if (flavor != null) {
573+ flavors = new ArrayList(1);
574+ getNativeToFlavor().put(nat, flavors);
575+ flavors.add(flavor);
576+ getFlavorsForNativeCache.remove(nat);
577+ getFlavorsForNativeCache.remove(null);
578+
579+ List natives = (List)getFlavorToNative().get(flavor);
580+ if (natives == null) {
581+ natives = new ArrayList(1);
582+ getFlavorToNative().put(flavor, natives);
583+ }
584+ natives.add(nat);
585+ getNativesForFlavorCache.remove(flavor);
586+ getNativesForFlavorCache.remove(null);
587+ }
588+ }
589+
590+ return (flavors != null) ? flavors : new ArrayList(0);
591+ }
592+
593+ /**
594+ * Semantically equivalent to 'flavorToNative.get(flav)'. This method
595+ * handles the case where 'flav' is not found in 'flavorToNative' depending
596+ * on the value of passes 'synthesize' parameter. If 'synthesize' is
597+ * SYNTHESIZE_IF_NOT_FOUND a native is synthesized, stored, and returned by
598+ * encoding the DataFlavor's MIME type. Otherwise an empty List is returned
599+ * and 'flavorToNative' remains unaffected.
600+ */
601+ private List flavorToNativeLookup(final DataFlavor flav,
602+ final boolean synthesize) {
603+ List natives = (List)getFlavorToNative().get(flav);
604+
605+ if (flav != null && !disabledMappingGenerationKeys.contains(flav)) {
606+ DataTransferer transferer = DataTransferer.getInstance();
607+ if (transferer != null) {
608+ List platformNatives =
609+ transferer.getPlatformMappingsForFlavor(flav);
610+ if (!platformNatives.isEmpty()) {
611+ if (natives != null) {
612+ platformNatives.removeAll(new HashSet(natives));
613+ // Prepend the platform-specific mappings to ensure
614+ // that the natives added with
615+ // addUnencodedNativeForFlavor() are at the end of
616+ // list.
617+ platformNatives.addAll(natives);
618+ }
619+ natives = platformNatives;
620+ }
621+ }
622+ }
623+
624+ if (natives == null) {
625+ if (synthesize) {
626+ String encoded = encodeDataFlavor(flav);
627+ natives = new ArrayList(1);
628+ getFlavorToNative().put(flav, natives);
629+ natives.add(encoded);
630+ getNativesForFlavorCache.remove(flav);
631+ getNativesForFlavorCache.remove(null);
632+
633+ List flavors = (List)getNativeToFlavor().get(encoded);
634+ if (flavors == null) {
635+ flavors = new ArrayList(1);
636+ getNativeToFlavor().put(encoded, flavors);
637+ }
638+ flavors.add(flav);
639+ getFlavorsForNativeCache.remove(encoded);
640+ getFlavorsForNativeCache.remove(null);
641+ } else {
642+ natives = new ArrayList(0);
643+ }
644+ }
645+
646+ return natives;
647+ }
648+
649+ /**
650+ * Returns a <code>List</code> of <code>String</code> natives to which the
651+ * specified <code>DataFlavor</code> can be translated by the data transfer
652+ * subsystem. The <code>List</code> will be sorted from best native to
653+ * worst. That is, the first native will best reflect data in the specified
654+ * flavor to the underlying native platform.
655+ * <p>
656+ * If the specified <code>DataFlavor</code> is previously unknown to the
657+ * data transfer subsystem and the data transfer subsystem is unable to
658+ * translate this <code>DataFlavor</code> to any existing native, then
659+ * invoking this method will establish a
660+ * mapping in both directions between the specified <code>DataFlavor</code>
661+ * and an encoded version of its MIME type as its native.
662+ *
663+ * @param flav the <code>DataFlavor</code> whose corresponding natives
664+ * should be returned. If <code>null</code> is specified, all
665+ * natives currently known to the data transfer subsystem are
666+ * returned in a non-deterministic order.
667+ * @return a <code>java.util.List</code> of <code>java.lang.String</code>
668+ * objects which are platform-specific representations of platform-
669+ * specific data formats
670+ *
671+ * @see #encodeDataFlavor
672+ * @since 1.4
673+ */
674+ public synchronized List<String> getNativesForFlavor(DataFlavor flav) {
675+ List retval = null;
676+
677+ // Check cache, even for null flav
678+ SoftReference ref = (SoftReference)getNativesForFlavorCache.get(flav);
679+ if (ref != null) {
680+ retval = (List)ref.get();
681+ if (retval != null) {
682+ // Create a copy, because client code can modify the returned
683+ // list.
684+ return new ArrayList(retval);
685+ }
686+ }
687+
688+ if (flav == null) {
689+ retval = new ArrayList(getNativeToFlavor().keySet());
690+ } else if (disabledMappingGenerationKeys.contains(flav)) {
691+ // In this case we shouldn't synthesize a native for this flavor,
692+ // since its mappings were explicitly specified.
693+ retval = flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND);
694+ } else if (DataTransferer.isFlavorCharsetTextType(flav)) {
695+
696+ // For text/* flavors, flavor-to-native mappings specified in
697+ // flavormap.properties are stored per flavor's base type.
698+ if ("text".equals(flav.getPrimaryType())) {
699+ retval = (List)getFlavorToNative().get(flav.mimeType.getBaseType());
700+ if (retval != null) {
701+ // To prevent the List stored in the map from modification.
702+ retval = new ArrayList(retval);
703+ }
704+ }
705+
706+ // Also include text/plain natives, but don't duplicate Strings
707+ List textPlainList = (List)getFlavorToNative().get(TEXT_PLAIN_BASE_TYPE);
708+
709+ if (textPlainList != null && !textPlainList.isEmpty()) {
710+ // To prevent the List stored in the map from modification.
711+ // This also guarantees that removeAll() is supported.
712+ textPlainList = new ArrayList(textPlainList);
713+ if (retval != null && !retval.isEmpty()) {
714+ // Use HashSet to get constant-time performance for search.
715+ textPlainList.removeAll(new HashSet(retval));
716+ retval.addAll(textPlainList);
717+ } else {
718+ retval = textPlainList;
719+ }
720+ }
721+
722+ if (retval == null || retval.isEmpty()) {
723+ retval = flavorToNativeLookup(flav, SYNTHESIZE_IF_NOT_FOUND);
724+ } else {
725+ // In this branch it is guaranteed that natives explicitly
726+ // listed for flav's MIME type were added with
727+ // addUnencodedNativeForFlavor(), so they have lower priority.
728+ List explicitList =
729+ flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND);
730+
731+ // flavorToNativeLookup() never returns null.
732+ // It can return an empty List, however.
733+ if (!explicitList.isEmpty()) {
734+ // To prevent the List stored in the map from modification.
735+ // This also guarantees that removeAll() is supported.
736+ explicitList = new ArrayList(explicitList);
737+ // Use HashSet to get constant-time performance for search.
738+ explicitList.removeAll(new HashSet(retval));
739+ retval.addAll(explicitList);
740+ }
741+ }
742+ } else if (DataTransferer.isFlavorNoncharsetTextType(flav)) {
743+ retval = (List)getFlavorToNative().get(flav.mimeType.getBaseType());
744+
745+ if (retval == null || retval.isEmpty()) {
746+ retval = flavorToNativeLookup(flav, SYNTHESIZE_IF_NOT_FOUND);
747+ } else {
748+ // In this branch it is guaranteed that natives explicitly
749+ // listed for flav's MIME type were added with
750+ // addUnencodedNativeForFlavor(), so they have lower priority.
751+ List explicitList =
752+ flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND);
753+
754+ // flavorToNativeLookup() never returns null.
755+ // It can return an empty List, however.
756+ if (!explicitList.isEmpty()) {
757+ // To prevent the List stored in the map from modification.
758+ // This also guarantees that add/removeAll() are supported.
759+ retval = new ArrayList(retval);
760+ explicitList = new ArrayList(explicitList);
761+ // Use HashSet to get constant-time performance for search.
762+ explicitList.removeAll(new HashSet(retval));
763+ retval.addAll(explicitList);
764+ }
765+ }
766+ } else {
767+ retval = flavorToNativeLookup(flav, SYNTHESIZE_IF_NOT_FOUND);
768+ }
769+
770+ getNativesForFlavorCache.put(flav, new SoftReference(retval));
771+ // Create a copy, because client code can modify the returned list.
772+ return new ArrayList(retval);
773+ }
774+
775+ /**
776+ * Returns a <code>List</code> of <code>DataFlavor</code>s to which the
777+ * specified <code>String</code> native can be translated by the data
778+ * transfer subsystem. The <code>List</code> will be sorted from best
779+ * <code>DataFlavor</code> to worst. That is, the first
780+ * <code>DataFlavor</code> will best reflect data in the specified
781+ * native to a Java application.
782+ * <p>
783+ * If the specified native is previously unknown to the data transfer
784+ * subsystem, and that native has been properly encoded, then invoking this
785+ * method will establish a mapping in both directions between the specified
786+ * native and a <code>DataFlavor</code> whose MIME type is a decoded
787+ * version of the native.
788+ * <p>
789+ * If the specified native is not a properly encoded native and the
790+ * mappings for this native have not been altered with
791+ * <code>setFlavorsForNative</code>, then the contents of the
792+ * <code>List</code> is platform dependent, but <code>null</code>
793+ * cannot be returned.
794+ *
795+ * @param nat the native whose corresponding <code>DataFlavor</code>s
796+ * should be returned. If <code>null</code> is specified, all
797+ * <code>DataFlavor</code>s currently known to the data transfer
798+ * subsystem are returned in a non-deterministic order.
799+ * @return a <code>java.util.List</code> of <code>DataFlavor</code>
800+ * objects into which platform-specific data in the specified,
801+ * platform-specific native can be translated
802+ *
803+ * @see #encodeJavaMIMEType
804+ * @since 1.4
805+ */
806+ public synchronized List<DataFlavor> getFlavorsForNative(String nat) {
807+
808+ // Check cache, even for null nat
809+ SoftReference ref = (SoftReference)getFlavorsForNativeCache.get(nat);
810+ if (ref != null) {
811+ ArrayList retval = (ArrayList)ref.get();
812+ if (retval != null) {
813+ return (List)retval.clone();
814+ }
815+ }
816+
817+ LinkedList retval = new LinkedList();
818+
819+ if (nat == null) {
820+ List natives = getNativesForFlavor(null);
821+ HashSet dups = new HashSet(natives.size());
822+
823+ for (Iterator natives_iter = natives.iterator();
824+ natives_iter.hasNext(); )
825+ {
826+ List flavors =
827+ getFlavorsForNative((String)natives_iter.next());
828+ for (Iterator flavors_iter = flavors.iterator();
829+ flavors_iter.hasNext(); )
830+ {
831+ Object flavor = flavors_iter.next();
832+ if (dups.add(flavor)) {
833+ retval.add(flavor);
834+ }
835+ }
836+ }
837+ } else {
838+ List flavors = nativeToFlavorLookup(nat);
839+
840+ if (disabledMappingGenerationKeys.contains(nat)) {
841+ return flavors;
842+ }
843+
844+ HashSet dups = new HashSet(flavors.size());
845+
846+ List flavorsAndbaseTypes = nativeToFlavorLookup(nat);
847+
848+ for (Iterator flavorsAndbaseTypes_iter =
849+ flavorsAndbaseTypes.iterator();
850+ flavorsAndbaseTypes_iter.hasNext(); )
851+ {
852+ Object value = flavorsAndbaseTypes_iter.next();
853+ if (value instanceof String) {
854+ String baseType = (String)value;
855+ String subType = null;
856+ try {
857+ MimeType mimeType = new MimeType(baseType);
858+ subType = mimeType.getSubType();
859+ } catch (MimeTypeParseException mtpe) {
860+ // Cannot happen, since we checked all mappings
861+ // on load from flavormap.properties.
862+ assert(false);
863+ }
864+ if (DataTransferer.doesSubtypeSupportCharset(subType,
865+ null)) {
866+ if (TEXT_PLAIN_BASE_TYPE.equals(baseType) &&
867+ dups.add(DataFlavor.stringFlavor))
868+ {
869+ retval.add(DataFlavor.stringFlavor);
870+ }
871+
872+ for (int i = 0; i < UNICODE_TEXT_CLASSES.length; i++) {
873+ DataFlavor toAdd = null;
874+ try {
875+ toAdd = new DataFlavor
876+ (baseType + ";charset=Unicode;class=" +
877+ UNICODE_TEXT_CLASSES[i]);
878+ } catch (ClassNotFoundException cannotHappen) {
879+ }
880+ if (dups.add(toAdd)) {
881+ retval.add(toAdd);
882+ }
883+ }
884+
885+ for (Iterator charset_iter =
886+ DataTransferer.standardEncodings();
887+ charset_iter.hasNext(); )
888+ {
889+ String charset = (String)charset_iter.next();
890+
891+ for (int i = 0; i < ENCODED_TEXT_CLASSES.length;
892+ i++)
893+ {
894+ DataFlavor toAdd = null;
895+ try {
896+ toAdd = new DataFlavor
897+ (baseType + ";charset=" + charset +
898+ ";class=" + ENCODED_TEXT_CLASSES[i]);
899+ } catch (ClassNotFoundException cannotHappen) {
900+ }
901+
902+ // Check for equality to plainTextFlavor so
903+ // that we can ensure that the exact charset of
904+ // plainTextFlavor, not the canonical charset
905+ // or another equivalent charset with a
906+ // different name, is used.
907+ if (toAdd.equals(DataFlavor.plainTextFlavor)) {
908+ toAdd = DataFlavor.plainTextFlavor;
909+ }
910+
911+ if (dups.add(toAdd)) {
912+ retval.add(toAdd);
913+ }
914+ }
915+ }
916+
917+ if (TEXT_PLAIN_BASE_TYPE.equals(baseType) &&
918+ dups.add(DataFlavor.plainTextFlavor))
919+ {
920+ retval.add(DataFlavor.plainTextFlavor);
921+ }
922+ } else {
923+ // Non-charset text natives should be treated as
924+ // opaque, 8-bit data in any of its various
925+ // representations.
926+ for (int i = 0; i < ENCODED_TEXT_CLASSES.length; i++) {
927+ DataFlavor toAdd = null;
928+ try {
929+ toAdd = new DataFlavor(baseType +
930+ ";class=" + ENCODED_TEXT_CLASSES[i]);
931+ } catch (ClassNotFoundException cannotHappen) {
932+ }
933+
934+ if (dups.add(toAdd)) {
935+ retval.add(toAdd);
936+ }
937+ }
938+ }
939+ } else {
940+ DataFlavor flavor = (DataFlavor)value;
941+ if (dups.add(flavor)) {
942+ retval.add(flavor);
943+ }
944+ }
945+ }
946+ }
947+
948+ ArrayList arrayList = new ArrayList(retval);
949+ getFlavorsForNativeCache.put(nat, new SoftReference(arrayList));
950+ return (List)arrayList.clone();
951+ }
952+
953+ /**
954+ * Returns a <code>Map</code> of the specified <code>DataFlavor</code>s to
955+ * their most preferred <code>String</code> native. Each native value will
956+ * be the same as the first native in the List returned by
957+ * <code>getNativesForFlavor</code> for the specified flavor.
958+ * <p>
959+ * If a specified <code>DataFlavor</code> is previously unknown to the
960+ * data transfer subsystem, then invoking this method will establish a
961+ * mapping in both directions between the specified <code>DataFlavor</code>
962+ * and an encoded version of its MIME type as its native.
963+ *
964+ * @param flavors an array of <code>DataFlavor</code>s which will be the
965+ * key set of the returned <code>Map</code>. If <code>null</code> is
966+ * specified, a mapping of all <code>DataFlavor</code>s known to the
967+ * data transfer subsystem to their most preferred
968+ * <code>String</code> natives will be returned.
969+ * @return a <code>java.util.Map</code> of <code>DataFlavor</code>s to
970+ * <code>String</code> natives
971+ *
972+ * @see #getNativesForFlavor
973+ * @see #encodeDataFlavor
974+ */
975+ public synchronized Map<DataFlavor,String>
976+ getNativesForFlavors(DataFlavor[] flavors)
977+ {
978+ // Use getNativesForFlavor to generate extra natives for text flavors
979+ // and stringFlavor
980+
981+ if (flavors == null) {
982+ List flavor_list = getFlavorsForNative(null);
983+ flavors = new DataFlavor[flavor_list.size()];
984+ flavor_list.toArray(flavors);
985+ }
986+
987+ HashMap retval = new HashMap(flavors.length, 1.0f);
988+ for (int i = 0; i < flavors.length; i++) {
989+ List natives = getNativesForFlavor(flavors[i]);
990+ String nat = (natives.isEmpty()) ? null : (String)natives.get(0);
991+ retval.put(flavors[i], nat);
992+ }
993+
994+ return retval;
995+ }
996+
997+ /**
998+ * Returns a <code>Map</code> of the specified <code>String</code> natives
999+ * to their most preferred <code>DataFlavor</code>. Each
1000+ * <code>DataFlavor</code> value will be the same as the first
1001+ * <code>DataFlavor</code> in the List returned by
1002+ * <code>getFlavorsForNative</code> for the specified native.
1003+ * <p>
1004+ * If a specified native is previously unknown to the data transfer
1005+ * subsystem, and that native has been properly encoded, then invoking this
1006+ * method will establish a mapping in both directions between the specified
1007+ * native and a <code>DataFlavor</code> whose MIME type is a decoded
1008+ * version of the native.
1009+ *
1010+ * @param natives an array of <code>String</code>s which will be the
1011+ * key set of the returned <code>Map</code>. If <code>null</code> is
1012+ * specified, a mapping of all supported <code>String</code> natives
1013+ * to their most preferred <code>DataFlavor</code>s will be
1014+ * returned.
1015+ * @return a <code>java.util.Map</code> of <code>String</code> natives to
1016+ * <code>DataFlavor</code>s
1017+ *
1018+ * @see #getFlavorsForNative
1019+ * @see #encodeJavaMIMEType
1020+ */
1021+ public synchronized Map<String,DataFlavor>
1022+ getFlavorsForNatives(String[] natives)
1023+ {
1024+ // Use getFlavorsForNative to generate extra flavors for text natives
1025+
1026+ if (natives == null) {
1027+ List native_list = getNativesForFlavor(null);
1028+ natives = new String[native_list.size()];
1029+ native_list.toArray(natives);
1030+ }
1031+
1032+ HashMap retval = new HashMap(natives.length, 1.0f);
1033+ for (int i = 0; i < natives.length; i++) {
1034+ List flavors = getFlavorsForNative(natives[i]);
1035+ DataFlavor flav = (flavors.isEmpty())
1036+ ? null : (DataFlavor)flavors.get(0);
1037+ retval.put(natives[i], flav);
1038+ }
1039+
1040+ return retval;
1041+ }
1042+
1043+ /**
1044+ * Adds a mapping from the specified <code>DataFlavor</code> (and all
1045+ * <code>DataFlavor</code>s equal to the specified <code>DataFlavor</code>)
1046+ * to the specified <code>String</code> native.
1047+ * Unlike <code>getNativesForFlavor</code>, the mapping will only be
1048+ * established in one direction, and the native will not be encoded. To
1049+ * establish a two-way mapping, call
1050+ * <code>addFlavorForUnencodedNative</code> as well. The new mapping will
1051+ * be of lower priority than any existing mapping.
1052+ * This method has no effect if a mapping from the specified or equal
1053+ * <code>DataFlavor</code> to the specified <code>String</code> native
1054+ * already exists.
1055+ *
1056+ * @param flav the <code>DataFlavor</code> key for the mapping
1057+ * @param nat the <code>String</code> native value for the mapping
1058+ * @throws NullPointerException if flav or nat is <code>null</code>
1059+ *
1060+ * @see #addFlavorForUnencodedNative
1061+ * @since 1.4
1062+ */
1063+ public synchronized void addUnencodedNativeForFlavor(DataFlavor flav,
1064+ String nat) {
1065+ if (flav == null || nat == null) {
1066+ throw new NullPointerException("null arguments not permitted");
1067+ }
1068+
1069+ List natives = (List)getFlavorToNative().get(flav);
1070+ if (natives == null) {
1071+ natives = new ArrayList(1);
1072+ getFlavorToNative().put(flav, natives);
1073+ } else if (natives.contains(nat)) {
1074+ return;
1075+ }
1076+ natives.add(nat);
1077+ getNativesForFlavorCache.remove(flav);
1078+ getNativesForFlavorCache.remove(null);
1079+ }
1080+
1081+ /**
1082+ * Discards the current mappings for the specified <code>DataFlavor</code>
1083+ * and all <code>DataFlavor</code>s equal to the specified
1084+ * <code>DataFlavor</code>, and creates new mappings to the
1085+ * specified <code>String</code> natives.
1086+ * Unlike <code>getNativesForFlavor</code>, the mappings will only be
1087+ * established in one direction, and the natives will not be encoded. To
1088+ * establish two-way mappings, call <code>setFlavorsForNative</code>
1089+ * as well. The first native in the array will represent the highest
1090+ * priority mapping. Subsequent natives will represent mappings of
1091+ * decreasing priority.
1092+ * <p>
1093+ * If the array contains several elements that reference equal
1094+ * <code>String</code> natives, this method will establish new mappings
1095+ * for the first of those elements and ignore the rest of them.
1096+ * <p>
1097+ * It is recommended that client code not reset mappings established by the
1098+ * data transfer subsystem. This method should only be used for
1099+ * application-level mappings.
1100+ *
1101+ * @param flav the <code>DataFlavor</code> key for the mappings
1102+ * @param natives the <code>String</code> native values for the mappings
1103+ * @throws NullPointerException if flav or natives is <code>null</code>
1104+ * or if natives contains <code>null</code> elements
1105+ *
1106+ * @see #setFlavorsForNative
1107+ * @since 1.4
1108+ */
1109+ public synchronized void setNativesForFlavor(DataFlavor flav,
1110+ String[] natives) {
1111+ if (flav == null || natives == null) {
1112+ throw new NullPointerException("null arguments not permitted");
1113+ }
1114+
1115+ getFlavorToNative().remove(flav);
1116+ for (int i = 0; i < natives.length; i++) {
1117+ addUnencodedNativeForFlavor(flav, natives[i]);
1118+ }
1119+ disabledMappingGenerationKeys.add(flav);
1120+ // Clear the cache to handle the case of empty natives.
1121+ getNativesForFlavorCache.remove(flav);
1122+ getNativesForFlavorCache.remove(null);
1123+ }
1124+
1125+ /**
1126+ * Adds a mapping from a single <code>String</code> native to a single
1127+ * <code>DataFlavor</code>. Unlike <code>getFlavorsForNative</code>, the
1128+ * mapping will only be established in one direction, and the native will
1129+ * not be encoded. To establish a two-way mapping, call
1130+ * <code>addUnencodedNativeForFlavor</code> as well. The new mapping will
1131+ * be of lower priority than any existing mapping.
1132+ * This method has no effect if a mapping from the specified
1133+ * <code>String</code> native to the specified or equal
1134+ * <code>DataFlavor</code> already exists.
1135+ *
1136+ * @param nat the <code>String</code> native key for the mapping
1137+ * @param flav the <code>DataFlavor</code> value for the mapping
1138+ * @throws NullPointerException if nat or flav is <code>null</code>
1139+ *
1140+ * @see #addUnencodedNativeForFlavor
1141+ * @since 1.4
1142+ */
1143+ public synchronized void addFlavorForUnencodedNative(String nat,
1144+ DataFlavor flav) {
1145+ if (nat == null || flav == null) {
1146+ throw new NullPointerException("null arguments not permitted");
1147+ }
1148+
1149+ List flavors = (List)getNativeToFlavor().get(nat);
1150+ if (flavors == null) {
1151+ flavors = new ArrayList(1);
1152+ getNativeToFlavor().put(nat, flavors);
1153+ } else if (flavors.contains(flav)) {
1154+ return;
1155+ }
1156+ flavors.add(flav);
1157+ getFlavorsForNativeCache.remove(nat);
1158+ getFlavorsForNativeCache.remove(null);
1159+ }
1160+
1161+ /**
1162+ * Discards the current mappings for the specified <code>String</code>
1163+ * native, and creates new mappings to the specified
1164+ * <code>DataFlavor</code>s. Unlike <code>getFlavorsForNative</code>, the
1165+ * mappings will only be established in one direction, and the natives need
1166+ * not be encoded. To establish two-way mappings, call
1167+ * <code>setNativesForFlavor</code> as well. The first
1168+ * <code>DataFlavor</code> in the array will represent the highest priority
1169+ * mapping. Subsequent <code>DataFlavor</code>s will represent mappings of
1170+ * decreasing priority.
1171+ * <p>
1172+ * If the array contains several elements that reference equal
1173+ * <code>DataFlavor</code>s, this method will establish new mappings
1174+ * for the first of those elements and ignore the rest of them.
1175+ * <p>
1176+ * It is recommended that client code not reset mappings established by the
1177+ * data transfer subsystem. This method should only be used for
1178+ * application-level mappings.
1179+ *
1180+ * @param nat the <code>String</code> native key for the mappings
1181+ * @param flavors the <code>DataFlavor</code> values for the mappings
1182+ * @throws NullPointerException if nat or flavors is <code>null</code>
1183+ * or if flavors contains <code>null</code> elements
1184+ *
1185+ * @see #setNativesForFlavor
1186+ * @since 1.4
1187+ */
1188+ public synchronized void setFlavorsForNative(String nat,
1189+ DataFlavor[] flavors) {
1190+ if (nat == null || flavors == null) {
1191+ throw new NullPointerException("null arguments not permitted");
1192+ }
1193+
1194+ getNativeToFlavor().remove(nat);
1195+ for (int i = 0; i < flavors.length; i++) {
1196+ addFlavorForUnencodedNative(nat, flavors[i]);
1197+ }
1198+ disabledMappingGenerationKeys.add(nat);
1199+ // Clear the cache to handle the case of empty flavors.
1200+ getFlavorsForNativeCache.remove(nat);
1201+ getFlavorsForNativeCache.remove(null);
1202+ }
1203+
1204+ /**
1205+ * Encodes a MIME type for use as a <code>String</code> native. The format
1206+ * of an encoded representation of a MIME type is implementation-dependent.
1207+ * The only restrictions are:
1208+ * <ul>
1209+ * <li>The encoded representation is <code>null</code> if and only if the
1210+ * MIME type <code>String</code> is <code>null</code>.</li>
1211+ * <li>The encoded representations for two non-<code>null</code> MIME type
1212+ * <code>String</code>s are equal if and only if these <code>String</code>s
1213+ * are equal according to <code>String.equals(Object)</code>.</li>
1214+ * </ul>
1215+ * <p>
1216+ * Sun's reference implementation of this method returns the specified MIME
1217+ * type <code>String</code> prefixed with <code>JAVA_DATAFLAVOR:</code>.
1218+ *
1219+ * @param mimeType the MIME type to encode
1220+ * @return the encoded <code>String</code>, or <code>null</code> if
1221+ * mimeType is <code>null</code>
1222+ */
1223+ public static String encodeJavaMIMEType(String mimeType) {
1224+ return (mimeType != null)
1225+ ? JavaMIME + mimeType
1226+ : null;
1227+ }
1228+
1229+ /**
1230+ * Encodes a <code>DataFlavor</code> for use as a <code>String</code>
1231+ * native. The format of an encoded <code>DataFlavor</code> is
1232+ * implementation-dependent. The only restrictions are:
1233+ * <ul>
1234+ * <li>The encoded representation is <code>null</code> if and only if the
1235+ * specified <code>DataFlavor</code> is <code>null</code> or its MIME type
1236+ * <code>String</code> is <code>null</code>.</li>
1237+ * <li>The encoded representations for two non-<code>null</code>
1238+ * <code>DataFlavor</code>s with non-<code>null</code> MIME type
1239+ * <code>String</code>s are equal if and only if the MIME type
1240+ * <code>String</code>s of these <code>DataFlavor</code>s are equal
1241+ * according to <code>String.equals(Object)</code>.</li>
1242+ * </ul>
1243+ * <p>
1244+ * Sun's reference implementation of this method returns the MIME type
1245+ * <code>String</code> of the specified <code>DataFlavor</code> prefixed
1246+ * with <code>JAVA_DATAFLAVOR:</code>.
1247+ *
1248+ * @param flav the <code>DataFlavor</code> to encode
1249+ * @return the encoded <code>String</code>, or <code>null</code> if
1250+ * flav is <code>null</code> or has a <code>null</code> MIME type
1251+ */
1252+ public static String encodeDataFlavor(DataFlavor flav) {
1253+ return (flav != null)
1254+ ? SystemFlavorMap.encodeJavaMIMEType(flav.getMimeType())
1255+ : null;
1256+ }
1257+
1258+ /**
1259+ * Returns whether the specified <code>String</code> is an encoded Java
1260+ * MIME type.
1261+ *
1262+ * @param str the <code>String</code> to test
1263+ * @return <code>true</code> if the <code>String</code> is encoded;
1264+ * <code>false</code> otherwise
1265+ */
1266+ public static boolean isJavaMIMEType(String str) {
1267+ return (str != null && str.startsWith(JavaMIME, 0));
1268+ }
1269+
1270+ /**
1271+ * Decodes a <code>String</code> native for use as a Java MIME type.
1272+ *
1273+ * @param nat the <code>String</code> to decode
1274+ * @return the decoded Java MIME type, or <code>null</code> if nat is not
1275+ * an encoded <code>String</code> native
1276+ */
1277+ public static String decodeJavaMIMEType(String nat) {
1278+ return (isJavaMIMEType(nat))
1279+ ? nat.substring(JavaMIME.length(), nat.length()).trim()
1280+ : null;
1281+ }
1282+
1283+ /**
1284+ * Decodes a <code>String</code> native for use as a
1285+ * <code>DataFlavor</code>.
1286+ *
1287+ * @param nat the <code>String</code> to decode
1288+ * @return the decoded <code>DataFlavor</code>, or <code>null</code> if
1289+ * nat is not an encoded <code>String</code> native
1290+ */
1291+ public static DataFlavor decodeDataFlavor(String nat)
1292+ throws ClassNotFoundException
1293+ {
1294+ String retval_str = SystemFlavorMap.decodeJavaMIMEType(nat);
1295+ return (retval_str != null)
1296+ ? new DataFlavor(retval_str)
1297+ : null;
1298+ }
1299+}