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
49 import sun.awt.AppContext;
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 *
86 * from best to worst.
87 */
88 private static final String[] UNICODE_TEXT_CLASSES = {
89 "java.io.Reader", "java.lang.String", "java.nio.CharBuffer", "\"[C\""
90 };
91
92 /**
93 * The list of valid, encoded text flavor representation classes, in order
94 * from best to worst.
95 */
96 private static final String[] ENCODED_TEXT_CLASSES = {
97 "java.io.InputStream", "java.nio.ByteBuffer", "\"[B\""
98 };
99
100 /**
101 * A String representing text/plain MIME type.
102 */
103 private static final String TEXT_PLAIN_BASE_TYPE = "text/plain";
104
105 /**
106 * This constant is passed to flavorToNativeLookup() to indicate that a
107 * a native should be synthesized, stored, and returned by encoding the
108 * DataFlavor's MIME type in case if the DataFlavor is not found in
109 * 'flavorToNative' map.
110 */
111 private static final boolean SYNTHESIZE_IF_NOT_FOUND = true;
112
113 /**
114 * Maps native Strings to Lists of DataFlavors (or base type Strings for
115 * text DataFlavors).
116 * Do not use the field directly, use getNativeToFlavor() instead.
117 */
118 private Map nativeToFlavor = new HashMap();
119
120 /**
121 * Accessor to nativeToFlavor map. Since we use lazy initialization we must
122 * use this accessor instead of direct access to the field which may not be
123 * initialized yet. This method will initialize the field if needed.
124 *
125 * @return nativeToFlavor
126 */
127 private Map getNativeToFlavor() {
128 if (!isMapInitialized) {
129 initSystemFlavorMap();
130 }
131 return nativeToFlavor;
132 }
133
134 /**
135 * Maps DataFlavors (or base type Strings for text DataFlavors) to Lists of
136 * native Strings.
137 * Do not use the field directly, use getFlavorToNative() instead.
138 */
139 private Map flavorToNative = new HashMap();
140
141 /**
142 * Accessor to flavorToNative map. Since we use lazy initialization we must
143 * use this accessor instead of direct access to the field which may not be
144 * initialized yet. This method will initialize the field if needed.
145 *
146 * @return flavorToNative
147 */
148 private synchronized Map getFlavorToNative() {
149 if (!isMapInitialized) {
150 initSystemFlavorMap();
151 }
152 return flavorToNative;
153 }
154
155 /**
156 * Shows if the object has been initialized.
157 */
158 private boolean isMapInitialized = false;
159
394 mime.removeParameter("terminators");
395 value = mime.toString();
396 }
397 } catch (MimeTypeParseException e) {
398 e.printStackTrace();
399 continue;
400 }
401
402 DataFlavor flavor;
403 try {
404 flavor = new DataFlavor(value);
405 } catch (Exception e) {
406 try {
407 flavor = new DataFlavor(value, (String)null);
408 } catch (Exception ee) {
409 ee.printStackTrace();
410 continue;
411 }
412 }
413
414 // For text/* flavors, store mappings in separate maps to
415 // enable dynamic mapping generation at a run-time.
416 if ("text".equals(flavor.getPrimaryType())) {
417 store(value, key, getFlavorToNative());
418 store(key, value, getNativeToFlavor());
419 } else {
420 store(flavor, key, getFlavorToNative());
421 store(key, flavor, getNativeToFlavor());
422 }
423 }
424 }
425 }
426 }
427
428 /**
429 * Copied from java.util.Properties.
430 */
431 private boolean continueLine (String line) {
432 int slashCount = 0;
433 int index = line.length() - 1;
434 while((index >= 0) && (line.charAt(index--) == '\\')) {
435 slashCount++;
436 }
437 return (slashCount % 2 == 1);
438 }
439
440 /**
441 * Copied from java.util.Properties.
503 * stored in that location.
504 */
505 private void store(Object hashed, Object listed, Map map) {
506 List list = (List)map.get(hashed);
507 if (list == null) {
508 list = new ArrayList(1);
509 map.put(hashed, list);
510 }
511 if (!list.contains(listed)) {
512 list.add(listed);
513 }
514 }
515
516 /**
517 * Semantically equivalent to 'nativeToFlavor.get(nat)'. This method
518 * handles the case where 'nat' is not found in 'nativeToFlavor'. In that
519 * case, a new DataFlavor is synthesized, stored, and returned, if and
520 * only if the specified native is encoded as a Java MIME type.
521 */
522 private List nativeToFlavorLookup(String nat) {
523 List flavors = (List)getNativeToFlavor().get(nat);
524
525 if (nat != null && !disabledMappingGenerationKeys.contains(nat)) {
526 DataTransferer transferer = DataTransferer.getInstance();
527 if (transferer != null) {
528 List platformFlavors =
529 transferer.getPlatformMappingsForNative(nat);
530 if (!platformFlavors.isEmpty()) {
531 if (flavors != null) {
532 platformFlavors.removeAll(new HashSet(flavors));
533 // Prepending the platform-specific mappings ensures
534 // that the flavors added with
535 // addFlavorForUnencodedNative() are at the end of
536 // list.
537 platformFlavors.addAll(flavors);
538 }
539 flavors = platformFlavors;
540 }
541 }
542 }
543
598 // Prepend the platform-specific mappings to ensure
599 // that the natives added with
600 // addUnencodedNativeForFlavor() are at the end of
601 // list.
602 platformNatives.addAll(natives);
603 }
604 natives = platformNatives;
605 }
606 }
607 }
608
609 if (natives == null) {
610 if (synthesize) {
611 String encoded = encodeDataFlavor(flav);
612 natives = new ArrayList(1);
613 getFlavorToNative().put(flav, natives);
614 natives.add(encoded);
615 getNativesForFlavorCache.remove(flav);
616 getNativesForFlavorCache.remove(null);
617
618 List flavors = (List)getNativeToFlavor().get(encoded);
619 if (flavors == null) {
620 flavors = new ArrayList(1);
621 getNativeToFlavor().put(encoded, flavors);
622 }
623 flavors.add(flav);
624 getFlavorsForNativeCache.remove(encoded);
625 getFlavorsForNativeCache.remove(null);
626 } else {
627 natives = new ArrayList(0);
628 }
629 }
630
631 return natives;
632 }
633
634 /**
635 * Returns a <code>List</code> of <code>String</code> natives to which the
636 * specified <code>DataFlavor</code> can be translated by the data transfer
637 * subsystem. The <code>List</code> will be sorted from best native to
638 * worst. That is, the first native will best reflect data in the specified
654 * specific data formats
655 *
656 * @see #encodeDataFlavor
657 * @since 1.4
658 */
659 public synchronized List<String> getNativesForFlavor(DataFlavor flav) {
660 List retval = null;
661
662 // Check cache, even for null flav
663 SoftReference ref = (SoftReference)getNativesForFlavorCache.get(flav);
664 if (ref != null) {
665 retval = (List)ref.get();
666 if (retval != null) {
667 // Create a copy, because client code can modify the returned
668 // list.
669 return new ArrayList(retval);
670 }
671 }
672
673 if (flav == null) {
674 retval = new ArrayList(getNativeToFlavor().keySet());
675 } else if (disabledMappingGenerationKeys.contains(flav)) {
676 // In this case we shouldn't synthesize a native for this flavor,
677 // since its mappings were explicitly specified.
678 retval = flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND);
679 } else if (DataTransferer.isFlavorCharsetTextType(flav)) {
680
681 // For text/* flavors, flavor-to-native mappings specified in
682 // flavormap.properties are stored per flavor's base type.
683 if ("text".equals(flav.getPrimaryType())) {
684 retval = (List)getFlavorToNative().get(flav.mimeType.getBaseType());
685 if (retval != null) {
686 // To prevent the List stored in the map from modification.
687 retval = new ArrayList(retval);
688 }
689 }
690
691 // Also include text/plain natives, but don't duplicate Strings
692 List textPlainList = (List)getFlavorToNative().get(TEXT_PLAIN_BASE_TYPE);
693
694 if (textPlainList != null && !textPlainList.isEmpty()) {
782 * <code>DataFlavor</code>s currently known to the data transfer
783 * subsystem are returned in a non-deterministic order.
784 * @return a <code>java.util.List</code> of <code>DataFlavor</code>
785 * objects into which platform-specific data in the specified,
786 * platform-specific native can be translated
787 *
788 * @see #encodeJavaMIMEType
789 * @since 1.4
790 */
791 public synchronized List<DataFlavor> getFlavorsForNative(String nat) {
792
793 // Check cache, even for null nat
794 SoftReference ref = (SoftReference)getFlavorsForNativeCache.get(nat);
795 if (ref != null) {
796 ArrayList retval = (ArrayList)ref.get();
797 if (retval != null) {
798 return (List)retval.clone();
799 }
800 }
801
802 LinkedList retval = new LinkedList();
803
804 if (nat == null) {
805 List natives = getNativesForFlavor(null);
806 HashSet dups = new HashSet(natives.size());
807
808 for (Iterator natives_iter = natives.iterator();
809 natives_iter.hasNext(); )
810 {
811 List flavors =
812 getFlavorsForNative((String)natives_iter.next());
813 for (Iterator flavors_iter = flavors.iterator();
814 flavors_iter.hasNext(); )
815 {
816 Object flavor = flavors_iter.next();
817 if (dups.add(flavor)) {
818 retval.add(flavor);
819 }
820 }
821 }
822 } else {
823 List flavors = nativeToFlavorLookup(nat);
824
825 if (disabledMappingGenerationKeys.contains(nat)) {
826 return flavors;
827 }
828
829 HashSet dups = new HashSet(flavors.size());
830
831 List flavorsAndbaseTypes = nativeToFlavorLookup(nat);
832
833 for (Iterator flavorsAndbaseTypes_iter =
834 flavorsAndbaseTypes.iterator();
835 flavorsAndbaseTypes_iter.hasNext(); )
836 {
837 Object value = flavorsAndbaseTypes_iter.next();
838 if (value instanceof String) {
839 String baseType = (String)value;
840 String subType = null;
841 try {
842 MimeType mimeType = new MimeType(baseType);
843 subType = mimeType.getSubType();
844 } catch (MimeTypeParseException mtpe) {
845 // Cannot happen, since we checked all mappings
846 // on load from flavormap.properties.
847 assert(false);
848 }
849 if (DataTransferer.doesSubtypeSupportCharset(subType,
850 null)) {
851 if (TEXT_PLAIN_BASE_TYPE.equals(baseType) &&
852 dups.add(DataFlavor.stringFlavor))
853 {
854 retval.add(DataFlavor.stringFlavor);
855 }
856
857 for (int i = 0; i < UNICODE_TEXT_CLASSES.length; i++) {
858 DataFlavor toAdd = null;
859 try {
860 toAdd = new DataFlavor
861 (baseType + ";charset=Unicode;class=" +
862 UNICODE_TEXT_CLASSES[i]);
863 } catch (ClassNotFoundException cannotHappen) {
864 }
865 if (dups.add(toAdd)) {
866 retval.add(toAdd);
867 }
868 }
869
870 for (Iterator charset_iter =
871 DataTransferer.standardEncodings();
872 charset_iter.hasNext(); )
873 {
874 String charset = (String)charset_iter.next();
875
876 for (int i = 0; i < ENCODED_TEXT_CLASSES.length;
877 i++)
878 {
879 DataFlavor toAdd = null;
880 try {
881 toAdd = new DataFlavor
882 (baseType + ";charset=" + charset +
883 ";class=" + ENCODED_TEXT_CLASSES[i]);
884 } catch (ClassNotFoundException cannotHappen) {
885 }
886
887 // Check for equality to plainTextFlavor so
888 // that we can ensure that the exact charset of
889 // plainTextFlavor, not the canonical charset
890 // or another equivalent charset with a
891 // different name, is used.
892 if (toAdd.equals(DataFlavor.plainTextFlavor)) {
893 toAdd = DataFlavor.plainTextFlavor;
894 }
895
896 if (dups.add(toAdd)) {
897 retval.add(toAdd);
898 }
899 }
900 }
901
902 if (TEXT_PLAIN_BASE_TYPE.equals(baseType) &&
903 dups.add(DataFlavor.plainTextFlavor))
904 {
905 retval.add(DataFlavor.plainTextFlavor);
906 }
907 } else {
908 // Non-charset text natives should be treated as
909 // opaque, 8-bit data in any of its various
910 // representations.
911 for (int i = 0; i < ENCODED_TEXT_CLASSES.length; i++) {
912 DataFlavor toAdd = null;
913 try {
914 toAdd = new DataFlavor(baseType +
915 ";class=" + ENCODED_TEXT_CLASSES[i]);
916 } catch (ClassNotFoundException cannotHappen) {
917 }
918
919 if (dups.add(toAdd)) {
920 retval.add(toAdd);
921 }
922 }
923 }
924 } else {
925 DataFlavor flavor = (DataFlavor)value;
926 if (dups.add(flavor)) {
927 retval.add(flavor);
928 }
929 }
930 }
931 }
932
933 ArrayList arrayList = new ArrayList(retval);
934 getFlavorsForNativeCache.put(nat, new SoftReference(arrayList));
935 return (List)arrayList.clone();
936 }
937
938 /**
939 * Returns a <code>Map</code> of the specified <code>DataFlavor</code>s to
940 * their most preferred <code>String</code> native. Each native value will
941 * be the same as the first native in the List returned by
942 * <code>getNativesForFlavor</code> for the specified flavor.
943 * <p>
944 * If a specified <code>DataFlavor</code> is previously unknown to the
945 * data transfer subsystem, then invoking this method will establish a
946 * mapping in both directions between the specified <code>DataFlavor</code>
947 * and an encoded version of its MIME type as its native.
948 *
949 * @param flavors an array of <code>DataFlavor</code>s which will be the
950 * key set of the returned <code>Map</code>. If <code>null</code> is
951 * specified, a mapping of all <code>DataFlavor</code>s known to the
952 * data transfer subsystem to their most preferred
953 * <code>String</code> natives will be returned.
954 * @return a <code>java.util.Map</code> of <code>DataFlavor</code>s to
955 * <code>String</code> natives
|
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.LinkedHashSet;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Set;
48
49 import sun.awt.AppContext;
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 *
86 * from best to worst.
87 */
88 private static final String[] UNICODE_TEXT_CLASSES = {
89 "java.io.Reader", "java.lang.String", "java.nio.CharBuffer", "\"[C\""
90 };
91
92 /**
93 * The list of valid, encoded text flavor representation classes, in order
94 * from best to worst.
95 */
96 private static final String[] ENCODED_TEXT_CLASSES = {
97 "java.io.InputStream", "java.nio.ByteBuffer", "\"[B\""
98 };
99
100 /**
101 * A String representing text/plain MIME type.
102 */
103 private static final String TEXT_PLAIN_BASE_TYPE = "text/plain";
104
105 /**
106 * A String representing text/html MIME type.
107 */
108 private static final String HTML_TEXT_BASE_TYPE = "text/html";
109
110 /**
111 * This constant is passed to flavorToNativeLookup() to indicate that a
112 * a native should be synthesized, stored, and returned by encoding the
113 * DataFlavor's MIME type in case if the DataFlavor is not found in
114 * 'flavorToNative' map.
115 */
116 private static final boolean SYNTHESIZE_IF_NOT_FOUND = true;
117
118 /**
119 * Maps native Strings to Lists of DataFlavors (or base type Strings for
120 * text DataFlavors).
121 * Do not use the field directly, use getNativeToFlavor() instead.
122 */
123 private final Map<String, List<DataFlavor>> nativeToFlavor = new HashMap<>();
124
125 /**
126 * Accessor to nativeToFlavor map. Since we use lazy initialization we must
127 * use this accessor instead of direct access to the field which may not be
128 * initialized yet. This method will initialize the field if needed.
129 *
130 * @return nativeToFlavor
131 */
132 private Map<String, List<DataFlavor>> getNativeToFlavor() {
133 if (!isMapInitialized) {
134 initSystemFlavorMap();
135 }
136 return nativeToFlavor;
137 }
138
139 /**
140 * Maps DataFlavors (or base type Strings for text DataFlavors) to Lists of
141 * native Strings.
142 * Do not use the field directly, use getFlavorToNative() instead.
143 */
144 private final Map flavorToNative = new HashMap();
145
146 /**
147 * Accessor to flavorToNative map. Since we use lazy initialization we must
148 * use this accessor instead of direct access to the field which may not be
149 * initialized yet. This method will initialize the field if needed.
150 *
151 * @return flavorToNative
152 */
153 private synchronized Map getFlavorToNative() {
154 if (!isMapInitialized) {
155 initSystemFlavorMap();
156 }
157 return flavorToNative;
158 }
159
160 /**
161 * Shows if the object has been initialized.
162 */
163 private boolean isMapInitialized = false;
164
399 mime.removeParameter("terminators");
400 value = mime.toString();
401 }
402 } catch (MimeTypeParseException e) {
403 e.printStackTrace();
404 continue;
405 }
406
407 DataFlavor flavor;
408 try {
409 flavor = new DataFlavor(value);
410 } catch (Exception e) {
411 try {
412 flavor = new DataFlavor(value, (String)null);
413 } catch (Exception ee) {
414 ee.printStackTrace();
415 continue;
416 }
417 }
418
419 final LinkedHashSet<DataFlavor> dfs = new LinkedHashSet<>();
420
421 dfs.add(flavor);
422
423 if ("text".equals(flavor.getPrimaryType())) {
424 dfs.addAll(convertMimeTypeToDataFlavors(value));
425 }
426
427 for (DataFlavor df : dfs) {
428 store(df, key, getFlavorToNative());
429 store(key, df, getNativeToFlavor());
430 }
431 }
432 }
433 }
434 }
435
436 /**
437 * Copied from java.util.Properties.
438 */
439 private boolean continueLine (String line) {
440 int slashCount = 0;
441 int index = line.length() - 1;
442 while((index >= 0) && (line.charAt(index--) == '\\')) {
443 slashCount++;
444 }
445 return (slashCount % 2 == 1);
446 }
447
448 /**
449 * Copied from java.util.Properties.
511 * stored in that location.
512 */
513 private void store(Object hashed, Object listed, Map map) {
514 List list = (List)map.get(hashed);
515 if (list == null) {
516 list = new ArrayList(1);
517 map.put(hashed, list);
518 }
519 if (!list.contains(listed)) {
520 list.add(listed);
521 }
522 }
523
524 /**
525 * Semantically equivalent to 'nativeToFlavor.get(nat)'. This method
526 * handles the case where 'nat' is not found in 'nativeToFlavor'. In that
527 * case, a new DataFlavor is synthesized, stored, and returned, if and
528 * only if the specified native is encoded as a Java MIME type.
529 */
530 private List nativeToFlavorLookup(String nat) {
531 List<DataFlavor> flavors = getNativeToFlavor().get(nat);
532
533 if (nat != null && !disabledMappingGenerationKeys.contains(nat)) {
534 DataTransferer transferer = DataTransferer.getInstance();
535 if (transferer != null) {
536 List platformFlavors =
537 transferer.getPlatformMappingsForNative(nat);
538 if (!platformFlavors.isEmpty()) {
539 if (flavors != null) {
540 platformFlavors.removeAll(new HashSet(flavors));
541 // Prepending the platform-specific mappings ensures
542 // that the flavors added with
543 // addFlavorForUnencodedNative() are at the end of
544 // list.
545 platformFlavors.addAll(flavors);
546 }
547 flavors = platformFlavors;
548 }
549 }
550 }
551
606 // Prepend the platform-specific mappings to ensure
607 // that the natives added with
608 // addUnencodedNativeForFlavor() are at the end of
609 // list.
610 platformNatives.addAll(natives);
611 }
612 natives = platformNatives;
613 }
614 }
615 }
616
617 if (natives == null) {
618 if (synthesize) {
619 String encoded = encodeDataFlavor(flav);
620 natives = new ArrayList(1);
621 getFlavorToNative().put(flav, natives);
622 natives.add(encoded);
623 getNativesForFlavorCache.remove(flav);
624 getNativesForFlavorCache.remove(null);
625
626 List<DataFlavor> flavors = getNativeToFlavor().get(encoded);
627 if (flavors == null) {
628 flavors = new ArrayList(1);
629 getNativeToFlavor().put(encoded, flavors);
630 }
631 flavors.add(flav);
632 getFlavorsForNativeCache.remove(encoded);
633 getFlavorsForNativeCache.remove(null);
634 } else {
635 natives = new ArrayList(0);
636 }
637 }
638
639 return natives;
640 }
641
642 /**
643 * Returns a <code>List</code> of <code>String</code> natives to which the
644 * specified <code>DataFlavor</code> can be translated by the data transfer
645 * subsystem. The <code>List</code> will be sorted from best native to
646 * worst. That is, the first native will best reflect data in the specified
662 * specific data formats
663 *
664 * @see #encodeDataFlavor
665 * @since 1.4
666 */
667 public synchronized List<String> getNativesForFlavor(DataFlavor flav) {
668 List retval = null;
669
670 // Check cache, even for null flav
671 SoftReference ref = (SoftReference)getNativesForFlavorCache.get(flav);
672 if (ref != null) {
673 retval = (List)ref.get();
674 if (retval != null) {
675 // Create a copy, because client code can modify the returned
676 // list.
677 return new ArrayList(retval);
678 }
679 }
680
681 if (flav == null) {
682 retval = new ArrayList<String>(getNativeToFlavor().keySet());
683 } else if (disabledMappingGenerationKeys.contains(flav)) {
684 // In this case we shouldn't synthesize a native for this flavor,
685 // since its mappings were explicitly specified.
686 retval = flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND);
687 } else if (DataTransferer.isFlavorCharsetTextType(flav)) {
688
689 // For text/* flavors, flavor-to-native mappings specified in
690 // flavormap.properties are stored per flavor's base type.
691 if ("text".equals(flav.getPrimaryType())) {
692 retval = (List)getFlavorToNative().get(flav.mimeType.getBaseType());
693 if (retval != null) {
694 // To prevent the List stored in the map from modification.
695 retval = new ArrayList(retval);
696 }
697 }
698
699 // Also include text/plain natives, but don't duplicate Strings
700 List textPlainList = (List)getFlavorToNative().get(TEXT_PLAIN_BASE_TYPE);
701
702 if (textPlainList != null && !textPlainList.isEmpty()) {
790 * <code>DataFlavor</code>s currently known to the data transfer
791 * subsystem are returned in a non-deterministic order.
792 * @return a <code>java.util.List</code> of <code>DataFlavor</code>
793 * objects into which platform-specific data in the specified,
794 * platform-specific native can be translated
795 *
796 * @see #encodeJavaMIMEType
797 * @since 1.4
798 */
799 public synchronized List<DataFlavor> getFlavorsForNative(String nat) {
800
801 // Check cache, even for null nat
802 SoftReference ref = (SoftReference)getFlavorsForNativeCache.get(nat);
803 if (ref != null) {
804 ArrayList retval = (ArrayList)ref.get();
805 if (retval != null) {
806 return (List)retval.clone();
807 }
808 }
809
810 final LinkedHashSet <DataFlavor> returnValue =
811 new LinkedHashSet<>();
812
813 if (nat == null) {
814 final List<String> natives = getNativesForFlavor(null);
815
816 for (String n : natives)
817 {
818 final List<DataFlavor> flavors = getFlavorsForNative(n);
819
820 for (DataFlavor df : flavors)
821 {
822 returnValue.add(df);
823 }
824 }
825 } else {
826
827 final List<DataFlavor> flavors = nativeToFlavorLookup(nat);
828
829 if (disabledMappingGenerationKeys.contains(nat)) {
830 return flavors;
831 }
832
833 final List<DataFlavor> flavorsAndBaseTypes =
834 nativeToFlavorLookup(nat);
835
836 for (DataFlavor df : flavorsAndBaseTypes) {
837 returnValue.add(df);
838 if ("text".equals(df.getPrimaryType())) {
839 try {
840 returnValue.addAll(
841 convertMimeTypeToDataFlavors(
842 new MimeType(df.getMimeType()
843 ).getBaseType()));
844 } catch (MimeTypeParseException e) {
845 e.printStackTrace();
846 }
847 }
848 }
849
850 }
851
852 final ArrayList arrayList = new ArrayList(returnValue);
853 getFlavorsForNativeCache.put(nat, new SoftReference(arrayList));
854 return (List)arrayList.clone();
855 }
856
857 private static LinkedHashSet<DataFlavor> convertMimeTypeToDataFlavors(
858 final String baseType) {
859
860 final LinkedHashSet<DataFlavor> returnValue =
861 new LinkedHashSet<DataFlavor>();
862
863 String subType = null;
864
865 try {
866 final MimeType mimeType = new MimeType(baseType);
867 subType = mimeType.getSubType();
868 } catch (MimeTypeParseException mtpe) {
869 // Cannot happen, since we checked all mappings
870 // on load from flavormap.properties.
871 assert(false);
872 }
873
874 if (DataTransferer.doesSubtypeSupportCharset(subType, null)) {
875 if (TEXT_PLAIN_BASE_TYPE.equals(baseType))
876 {
877 returnValue.add(DataFlavor.stringFlavor);
878 }
879
880 for (String unicodeClassName : UNICODE_TEXT_CLASSES) {
881 final String mimeType = baseType + ";charset=Unicode;class=" +
882 unicodeClassName;
883
884 final LinkedHashSet<String> mimeTypes =
885 handleHtmlMimeTypes(baseType, mimeType);
886 for (String mt : mimeTypes) {
887 DataFlavor toAdd = null;
888 try {
889 toAdd = new DataFlavor(mt);
890 } catch (ClassNotFoundException cannotHappen) {
891 }
892 returnValue.add(toAdd);
893 }
894 }
895
896 for (String charset : DataTransferer.standardEncodings()) {
897
898 for (String encodedTextClass : ENCODED_TEXT_CLASSES) {
899 final String mimeType =
900 baseType + ";charset=" + charset +
901 ";class=" + encodedTextClass;
902
903 final LinkedHashSet<String> mimeTypes =
904 handleHtmlMimeTypes(baseType, mimeType);
905
906 for (String mt : mimeTypes) {
907
908 DataFlavor df = null;
909
910 try {
911 df = new DataFlavor(mt);
912 // Check for equality to plainTextFlavor so
913 // that we can ensure that the exact charset of
914 // plainTextFlavor, not the canonical charset
915 // or another equivalent charset with a
916 // different name, is used.
917 if (df.equals(DataFlavor.plainTextFlavor)) {
918 df = DataFlavor.plainTextFlavor;
919 }
920 } catch (ClassNotFoundException cannotHappen) {
921 }
922
923 returnValue.add(df);
924 }
925 }
926 }
927
928 if (TEXT_PLAIN_BASE_TYPE.equals(baseType))
929 {
930 returnValue.add(DataFlavor.plainTextFlavor);
931 }
932 } else {
933 // Non-charset text natives should be treated as
934 // opaque, 8-bit data in any of its various
935 // representations.
936 for (String encodedTextClassName : ENCODED_TEXT_CLASSES) {
937 DataFlavor toAdd = null;
938 try {
939 toAdd = new DataFlavor(baseType +
940 ";class=" + encodedTextClassName);
941 } catch (ClassNotFoundException cannotHappen) {
942 }
943 returnValue.add(toAdd);
944 }
945 }
946 return returnValue;
947 }
948
949 private static final String [] htmlDocumntTypes =
950 new String [] {"all", "selection", "fragment"};
951
952 private static LinkedHashSet<String> handleHtmlMimeTypes(
953 String baseType, String mimeType) {
954
955 LinkedHashSet<String> returnValues = new LinkedHashSet<>();
956
957 if (HTML_TEXT_BASE_TYPE.equals(baseType)) {
958 for (String documentType : htmlDocumntTypes) {
959 returnValues.add(mimeType + ";document=" + documentType);
960 }
961 } else {
962 returnValues.add(mimeType);
963 }
964
965 return returnValues;
966 }
967
968 /**
969 * Returns a <code>Map</code> of the specified <code>DataFlavor</code>s to
970 * their most preferred <code>String</code> native. Each native value will
971 * be the same as the first native in the List returned by
972 * <code>getNativesForFlavor</code> for the specified flavor.
973 * <p>
974 * If a specified <code>DataFlavor</code> is previously unknown to the
975 * data transfer subsystem, then invoking this method will establish a
976 * mapping in both directions between the specified <code>DataFlavor</code>
977 * and an encoded version of its MIME type as its native.
978 *
979 * @param flavors an array of <code>DataFlavor</code>s which will be the
980 * key set of the returned <code>Map</code>. If <code>null</code> is
981 * specified, a mapping of all <code>DataFlavor</code>s known to the
982 * data transfer subsystem to their most preferred
983 * <code>String</code> natives will be returned.
984 * @return a <code>java.util.Map</code> of <code>DataFlavor</code>s to
985 * <code>String</code> natives
|