src/share/classes/java/awt/datatransfer/SystemFlavorMap.java

Print this page




  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  *
  60  * @since 1.2
  61  */
  62 public final class SystemFlavorMap implements FlavorMap, FlavorTable {
  63 
  64     /**
  65      * Constant prefix used to tag Java types converted to native platform
  66      * type.
  67      */
  68     private static String JavaMIME = "JAVA_DATAFLAVOR:";
  69 
  70     /**
  71      * System singleton which maps a thread's ClassLoader to a SystemFlavorMap.
  72      */
  73     private static final WeakHashMap flavorMaps = new WeakHashMap();
  74 
  75     /**
  76      * Copied from java.util.Properties.
  77      */
  78     private static final String keyValueSeparators = "=: \t\r\n\f";
  79     private static final String strictKeyValueSeparators = "=:";
  80     private static final String whiteSpaceChars = " \t\r\n\f";
  81 
  82     /**
  83      * The list of valid, decoded text flavor representation classes, in order
  84      * from best to worst.
  85      */
  86     private static final String[] UNICODE_TEXT_CLASSES = {
  87         "java.io.Reader", "java.lang.String", "java.nio.CharBuffer", "\"[C\""
  88     };
  89 
  90     /**
  91      * The list of valid, encoded text flavor representation classes, in order
  92      * from best to worst.
  93      */


 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<String, List<DataFlavor>> 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 final 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;


 503                     } else if (aChar == 'n') {
 504                         aChar = '\n';
 505                     } else if (aChar == 'f') {
 506                         aChar = '\f';
 507                     }
 508                     outBuffer.append(aChar);
 509                 }
 510             } else {
 511                 outBuffer.append(aChar);
 512             }
 513         }
 514         return outBuffer.toString();
 515     }
 516 
 517     /**
 518      * Stores the listed object under the specified hash key in map. Unlike a
 519      * standard map, the listed object will not replace any object already at
 520      * the appropriate Map location, but rather will be appended to a List
 521      * stored in that location.
 522      */
 523     private void store(Object hashed, Object listed, Map map) {
 524         List list = (List)map.get(hashed);
 525         if (list == null) {
 526             list = new ArrayList(1);
 527             map.put(hashed, list);
 528         }
 529         if (!list.contains(listed)) {
 530             list.add(listed);
 531         }
 532     }
 533 
 534     /**
 535      * Semantically equivalent to 'nativeToFlavor.get(nat)'. This method
 536      * handles the case where 'nat' is not found in 'nativeToFlavor'. In that
 537      * case, a new DataFlavor is synthesized, stored, and returned, if and
 538      * only if the specified native is encoded as a Java MIME type.
 539      */
 540     private List nativeToFlavorLookup(String nat) {
 541         List<DataFlavor> flavors = getNativeToFlavor().get(nat);
 542 
 543         if (nat != null && !disabledMappingGenerationKeys.contains(nat)) {
 544             DataTransferer transferer = DataTransferer.getInstance();
 545             if (transferer != null) {
 546                 List platformFlavors =
 547                     transferer.getPlatformMappingsForNative(nat);
 548                 if (!platformFlavors.isEmpty()) {
 549                     if (flavors != null) {
 550                         platformFlavors.removeAll(new HashSet(flavors));
 551                         // Prepending the platform-specific mappings ensures
 552                         // that the flavors added with
 553                         // addFlavorForUnencodedNative() are at the end of
 554                         // list.
 555                         platformFlavors.addAll(flavors);
 556                     }
 557                     flavors = platformFlavors;
 558                 }
 559             }
 560         }
 561 
 562         if (flavors == null && isJavaMIMEType(nat)) {
 563             String decoded = decodeJavaMIMEType(nat);
 564             DataFlavor flavor = null;
 565 
 566             try {
 567                 flavor = new DataFlavor(decoded);
 568             } catch (Exception e) {
 569                 System.err.println("Exception \"" + e.getClass().getName() +
 570                                    ": " + e.getMessage()  +
 571                                    "\"while constructing DataFlavor for: " +
 572                                    decoded);
 573             }
 574 
 575             if (flavor != null) {
 576                 flavors = new ArrayList(1);
 577                 getNativeToFlavor().put(nat, flavors);
 578                 flavors.add(flavor);
 579                 getFlavorsForNativeCache.remove(nat);
 580                 getFlavorsForNativeCache.remove(null);
 581 
 582                 List natives = (List)getFlavorToNative().get(flavor);
 583                 if (natives == null) {
 584                     natives = new ArrayList(1);
 585                     getFlavorToNative().put(flavor, natives);
 586                 }
 587                 natives.add(nat);
 588                 getNativesForFlavorCache.remove(flavor);
 589                 getNativesForFlavorCache.remove(null);
 590             }
 591         }
 592 
 593         return (flavors != null) ? flavors : new ArrayList(0);
 594     }
 595 
 596     /**
 597      * Semantically equivalent to 'flavorToNative.get(flav)'. This method
 598      * handles the case where 'flav' is not found in 'flavorToNative' depending
 599      * on the value of passes 'synthesize' parameter. If 'synthesize' is
 600      * SYNTHESIZE_IF_NOT_FOUND a native is synthesized, stored, and returned by
 601      * encoding the DataFlavor's MIME type. Otherwise an empty List is returned
 602      * and 'flavorToNative' remains unaffected.
 603      */
 604     private List flavorToNativeLookup(final DataFlavor flav,
 605                                       final boolean synthesize) {
 606         List natives = (List)getFlavorToNative().get(flav);
 607 
 608         if (flav != null && !disabledMappingGenerationKeys.contains(flav)) {
 609             DataTransferer transferer = DataTransferer.getInstance();
 610             if (transferer != null) {
 611                 List platformNatives =
 612                     transferer.getPlatformMappingsForFlavor(flav);
 613                 if (!platformNatives.isEmpty()) {
 614                     if (natives != null) {
 615                         platformNatives.removeAll(new HashSet(natives));
 616                         // Prepend the platform-specific mappings to ensure
 617                         // that the natives added with
 618                         // addUnencodedNativeForFlavor() are at the end of
 619                         // list.
 620                         platformNatives.addAll(natives);
 621                     }
 622                     natives = platformNatives;
 623                 }
 624             }
 625         }
 626 
 627         if (natives == null) {
 628             if (synthesize) {
 629                 String encoded = encodeDataFlavor(flav);
 630                 natives = new ArrayList(1);
 631                 getFlavorToNative().put(flav, natives);
 632                 natives.add(encoded);
 633                 getNativesForFlavorCache.remove(flav);
 634                 getNativesForFlavorCache.remove(null);
 635 
 636                 List<DataFlavor> flavors = getNativeToFlavor().get(encoded);
 637                 if (flavors == null) {
 638                     flavors = new ArrayList(1);
 639                     getNativeToFlavor().put(encoded, flavors);
 640                 }
 641                 flavors.add(flav);
 642                 getFlavorsForNativeCache.remove(encoded);
 643                 getFlavorsForNativeCache.remove(null);
 644             } else {
 645                 natives = new ArrayList(0);
 646             }
 647         }
 648 
 649         return natives;
 650     }
 651 
 652     /**
 653      * Returns a <code>List</code> of <code>String</code> natives to which the
 654      * specified <code>DataFlavor</code> can be translated by the data transfer
 655      * subsystem. The <code>List</code> will be sorted from best native to
 656      * worst. That is, the first native will best reflect data in the specified
 657      * flavor to the underlying native platform.
 658      * <p>
 659      * If the specified <code>DataFlavor</code> is previously unknown to the
 660      * data transfer subsystem and the data transfer subsystem is unable to
 661      * translate this <code>DataFlavor</code> to any existing native, then
 662      * invoking this method will establish a
 663      * mapping in both directions between the specified <code>DataFlavor</code>
 664      * and an encoded version of its MIME type as its native.
 665      *
 666      * @param flav the <code>DataFlavor</code> whose corresponding natives
 667      *        should be returned. If <code>null</code> is specified, all
 668      *        natives currently known to the data transfer subsystem are
 669      *        returned in a non-deterministic order.
 670      * @return a <code>java.util.List</code> of <code>java.lang.String</code>
 671      *         objects which are platform-specific representations of platform-
 672      *         specific data formats
 673      *
 674      * @see #encodeDataFlavor
 675      * @since 1.4
 676      */
 677     public synchronized List<String> getNativesForFlavor(DataFlavor flav) {
 678         List retval = null;
 679 
 680         // Check cache, even for null flav
 681         SoftReference ref = (SoftReference)getNativesForFlavorCache.get(flav);
 682         if (ref != null) {
 683             retval = (List)ref.get();
 684             if (retval != null) {
 685                 // Create a copy, because client code can modify the returned
 686                 // list.
 687                 return new ArrayList(retval);
 688             }
 689         }
 690 
 691         if (flav == null) {
 692             retval = new ArrayList<String>(getNativeToFlavor().keySet());
 693         } else if (disabledMappingGenerationKeys.contains(flav)) {
 694             // In this case we shouldn't synthesize a native for this flavor,
 695             // since its mappings were explicitly specified.
 696             retval = flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND);
 697         } else if (DataTransferer.isFlavorCharsetTextType(flav)) {
 698 
 699             // For text/* flavors, flavor-to-native mappings specified in
 700             // flavormap.properties are stored per flavor's base type.
 701             if ("text".equals(flav.getPrimaryType())) {
 702                 retval = (List)getFlavorToNative().get(flav.mimeType.getBaseType());
 703                 if (retval != null) {
 704                     // To prevent the List stored in the map from modification.
 705                     retval = new ArrayList(retval);
 706                 }
 707             }
 708 
 709             // Also include text/plain natives, but don't duplicate Strings
 710             List textPlainList = (List)getFlavorToNative().get(TEXT_PLAIN_BASE_TYPE);
 711 
 712             if (textPlainList != null && !textPlainList.isEmpty()) {
 713                 // To prevent the List stored in the map from modification.
 714                 // This also guarantees that removeAll() is supported.
 715                 textPlainList = new ArrayList(textPlainList);
 716                 if (retval != null && !retval.isEmpty()) {
 717                     // Use HashSet to get constant-time performance for search.
 718                     textPlainList.removeAll(new HashSet(retval));
 719                     retval.addAll(textPlainList);
 720                 } else {
 721                     retval = textPlainList;
 722                 }
 723             }
 724 
 725             if (retval == null || retval.isEmpty()) {
 726                 retval = flavorToNativeLookup(flav, SYNTHESIZE_IF_NOT_FOUND);
 727             } else {
 728                 // In this branch it is guaranteed that natives explicitly
 729                 // listed for flav's MIME type were added with
 730                 // addUnencodedNativeForFlavor(), so they have lower priority.
 731                 List explicitList =
 732                     flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND);
 733 
 734                 // flavorToNativeLookup() never returns null.
 735                 // It can return an empty List, however.
 736                 if (!explicitList.isEmpty()) {
 737                     // To prevent the List stored in the map from modification.
 738                     // This also guarantees that removeAll() is supported.
 739                     explicitList = new ArrayList(explicitList);
 740                     // Use HashSet to get constant-time performance for search.
 741                     explicitList.removeAll(new HashSet(retval));
 742                     retval.addAll(explicitList);
 743                 }
 744             }
 745         } else if (DataTransferer.isFlavorNoncharsetTextType(flav)) {
 746             retval = (List)getFlavorToNative().get(flav.mimeType.getBaseType());
 747 
 748             if (retval == null || retval.isEmpty()) {
 749                 retval = flavorToNativeLookup(flav, SYNTHESIZE_IF_NOT_FOUND);
 750             } else {
 751                 // In this branch it is guaranteed that natives explicitly
 752                 // listed for flav's MIME type were added with
 753                 // addUnencodedNativeForFlavor(), so they have lower priority.
 754                 List explicitList =
 755                     flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND);
 756 
 757                 // flavorToNativeLookup() never returns null.
 758                 // It can return an empty List, however.
 759                 if (!explicitList.isEmpty()) {
 760                     // To prevent the List stored in the map from modification.
 761                     // This also guarantees that add/removeAll() are supported.
 762                     retval = new ArrayList(retval);
 763                     explicitList = new ArrayList(explicitList);
 764                     // Use HashSet to get constant-time performance for search.
 765                     explicitList.removeAll(new HashSet(retval));
 766                     retval.addAll(explicitList);
 767                 }
 768             }
 769         } else {
 770             retval = flavorToNativeLookup(flav, SYNTHESIZE_IF_NOT_FOUND);
 771         }
 772 
 773         getNativesForFlavorCache.put(flav, new SoftReference(retval));
 774         // Create a copy, because client code can modify the returned list.
 775         return new ArrayList(retval);
 776     }
 777 
 778     /**
 779      * Returns a <code>List</code> of <code>DataFlavor</code>s to which the
 780      * specified <code>String</code> native can be translated by the data
 781      * transfer subsystem. The <code>List</code> will be sorted from best
 782      * <code>DataFlavor</code> to worst. That is, the first
 783      * <code>DataFlavor</code> will best reflect data in the specified
 784      * native to a Java application.
 785      * <p>
 786      * If the specified native is previously unknown to the data transfer
 787      * subsystem, and that native has been properly encoded, then invoking this
 788      * method will establish a mapping in both directions between the specified
 789      * native and a <code>DataFlavor</code> whose MIME type is a decoded
 790      * version of the native.
 791      * <p>
 792      * If the specified native is not a properly encoded native and the
 793      * mappings for this native have not been altered with
 794      * <code>setFlavorsForNative</code>, then the contents of the
 795      * <code>List</code> is platform dependent, but <code>null</code>
 796      * cannot be returned.
 797      *
 798      * @param nat the native whose corresponding <code>DataFlavor</code>s
 799      *        should be returned. If <code>null</code> is specified, all
 800      *        <code>DataFlavor</code>s currently known to the data transfer
 801      *        subsystem are returned in a non-deterministic order.
 802      * @return a <code>java.util.List</code> of <code>DataFlavor</code>
 803      *         objects into which platform-specific data in the specified,
 804      *         platform-specific native can be translated
 805      *
 806      * @see #encodeJavaMIMEType
 807      * @since 1.4
 808      */
 809     public synchronized List<DataFlavor> getFlavorsForNative(String nat) {
 810 
 811         // Check cache, even for null nat
 812         SoftReference ref = (SoftReference)getFlavorsForNativeCache.get(nat);
 813         if (ref != null) {
 814             ArrayList retval = (ArrayList)ref.get();
 815             if (retval != null) {
 816                 return (List)retval.clone();
 817             }
 818         }
 819 
 820         final LinkedHashSet <DataFlavor> returnValue =
 821             new LinkedHashSet<>();
 822 
 823         if (nat == null) {
 824             final List<String> natives = getNativesForFlavor(null);
 825 
 826             for (String n : natives)
 827             {
 828                 final List<DataFlavor> flavors = getFlavorsForNative(n);
 829 
 830                 for (DataFlavor df : flavors)
 831                 {
 832                     returnValue.add(df);
 833                 }
 834             }
 835         } else {
 836 


 842 
 843             final List<DataFlavor> flavorsAndBaseTypes =
 844                 nativeToFlavorLookup(nat);
 845 
 846             for (DataFlavor df : flavorsAndBaseTypes) {
 847                 returnValue.add(df);
 848                 if ("text".equals(df.getPrimaryType())) {
 849                     try {
 850                         returnValue.addAll(
 851                                 convertMimeTypeToDataFlavors(
 852                                         new MimeType(df.getMimeType()
 853                                         ).getBaseType()));
 854                     } catch (MimeTypeParseException e) {
 855                         e.printStackTrace();
 856                     }
 857                 }
 858             }
 859 
 860         }
 861 
 862         final ArrayList arrayList = new ArrayList(returnValue);
 863         getFlavorsForNativeCache.put(nat, new SoftReference(arrayList));
 864         return (List)arrayList.clone();
 865     }
 866 
 867     private static LinkedHashSet<DataFlavor> convertMimeTypeToDataFlavors(
 868         final String baseType) {
 869 
 870         final LinkedHashSet<DataFlavor> returnValue =
 871             new LinkedHashSet<DataFlavor>();
 872 
 873         String subType = null;
 874 
 875         try {
 876             final MimeType mimeType = new MimeType(baseType);
 877             subType = mimeType.getSubType();
 878         } catch (MimeTypeParseException mtpe) {
 879             // Cannot happen, since we checked all mappings
 880             // on load from flavormap.properties.
 881             assert(false);
 882         }
 883 
 884         if (DataTransferer.doesSubtypeSupportCharset(subType, null)) {
 885             if (TEXT_PLAIN_BASE_TYPE.equals(baseType))
 886             {
 887                 returnValue.add(DataFlavor.stringFlavor);
 888             }
 889 
 890             for (String unicodeClassName : UNICODE_TEXT_CLASSES) {
 891                 final String mimeType = baseType + ";charset=Unicode;class=" +


 992      *        data transfer subsystem to their most preferred
 993      *        <code>String</code> natives will be returned.
 994      * @return a <code>java.util.Map</code> of <code>DataFlavor</code>s to
 995      *         <code>String</code> natives
 996      *
 997      * @see #getNativesForFlavor
 998      * @see #encodeDataFlavor
 999      */
1000     public synchronized Map<DataFlavor,String>
1001         getNativesForFlavors(DataFlavor[] flavors)
1002     {
1003         // Use getNativesForFlavor to generate extra natives for text flavors
1004         // and stringFlavor
1005 
1006         if (flavors == null) {
1007             List flavor_list = getFlavorsForNative(null);
1008             flavors = new DataFlavor[flavor_list.size()];
1009             flavor_list.toArray(flavors);
1010         }
1011 
1012         HashMap retval = new HashMap(flavors.length, 1.0f);
1013         for (int i = 0; i < flavors.length; i++) {
1014             List natives = getNativesForFlavor(flavors[i]);
1015             String nat = (natives.isEmpty()) ? null : (String)natives.get(0);
1016             retval.put(flavors[i], nat);
1017         }
1018 
1019         return retval;
1020     }
1021 
1022     /**
1023      * Returns a <code>Map</code> of the specified <code>String</code> natives
1024      * to their most preferred <code>DataFlavor</code>. Each
1025      * <code>DataFlavor</code> value will be the same as the first
1026      * <code>DataFlavor</code> in the List returned by
1027      * <code>getFlavorsForNative</code> for the specified native.
1028      * <p>
1029      * If a specified native is previously unknown to the data transfer
1030      * subsystem, and that native has been properly encoded, then invoking this
1031      * method will establish a mapping in both directions between the specified
1032      * native and a <code>DataFlavor</code> whose MIME type is a decoded
1033      * version of the native.
1034      *
1035      * @param natives an array of <code>String</code>s which will be the
1036      *        key set of the returned <code>Map</code>. If <code>null</code> is
1037      *        specified, a mapping of all supported <code>String</code> natives
1038      *        to their most preferred <code>DataFlavor</code>s will be
1039      *        returned.
1040      * @return a <code>java.util.Map</code> of <code>String</code> natives to
1041      *         <code>DataFlavor</code>s
1042      *
1043      * @see #getFlavorsForNative
1044      * @see #encodeJavaMIMEType
1045      */
1046     public synchronized Map<String,DataFlavor>
1047         getFlavorsForNatives(String[] natives)
1048     {
1049         // Use getFlavorsForNative to generate extra flavors for text natives
1050 
1051         if (natives == null) {
1052             List native_list = getNativesForFlavor(null);
1053             natives = new String[native_list.size()];
1054             native_list.toArray(natives);
1055         }
1056 
1057         HashMap retval = new HashMap(natives.length, 1.0f);
1058         for (int i = 0; i < natives.length; i++) {
1059             List flavors = getFlavorsForNative(natives[i]);
1060             DataFlavor flav = (flavors.isEmpty())
1061                 ? null : (DataFlavor)flavors.get(0);
1062             retval.put(natives[i], flav);
1063         }
1064 
1065         return retval;
1066     }
1067 
1068     /**
1069      * Adds a mapping from the specified <code>DataFlavor</code> (and all
1070      * <code>DataFlavor</code>s equal to the specified <code>DataFlavor</code>)
1071      * to the specified <code>String</code> native.
1072      * Unlike <code>getNativesForFlavor</code>, the mapping will only be
1073      * established in one direction, and the native will not be encoded. To
1074      * establish a two-way mapping, call
1075      * <code>addFlavorForUnencodedNative</code> as well. The new mapping will
1076      * be of lower priority than any existing mapping.
1077      * This method has no effect if a mapping from the specified or equal
1078      * <code>DataFlavor</code> to the specified <code>String</code> native
1079      * already exists.
1080      *
1081      * @param flav the <code>DataFlavor</code> key for the mapping
1082      * @param nat the <code>String</code> native value for the mapping
1083      * @throws NullPointerException if flav or nat is <code>null</code>
1084      *
1085      * @see #addFlavorForUnencodedNative
1086      * @since 1.4
1087      */
1088     public synchronized void addUnencodedNativeForFlavor(DataFlavor flav,
1089                                                          String nat) {
1090         if (flav == null || nat == null) {
1091             throw new NullPointerException("null arguments not permitted");
1092         }
1093 
1094         List natives = (List)getFlavorToNative().get(flav);
1095         if (natives == null) {
1096             natives = new ArrayList(1);
1097             getFlavorToNative().put(flav, natives);
1098         } else if (natives.contains(nat)) {
1099             return;
1100         }
1101         natives.add(nat);
1102         getNativesForFlavorCache.remove(flav);
1103         getNativesForFlavorCache.remove(null);
1104     }
1105 
1106     /**
1107      * Discards the current mappings for the specified <code>DataFlavor</code>
1108      * and all <code>DataFlavor</code>s equal to the specified
1109      * <code>DataFlavor</code>, and creates new mappings to the
1110      * specified <code>String</code> natives.
1111      * Unlike <code>getNativesForFlavor</code>, the mappings will only be
1112      * established in one direction, and the natives will not be encoded. To
1113      * establish two-way mappings, call <code>setFlavorsForNative</code>
1114      * as well. The first native in the array will represent the highest
1115      * priority mapping. Subsequent natives will represent mappings of
1116      * decreasing priority.


1121      * <p>
1122      * It is recommended that client code not reset mappings established by the
1123      * data transfer subsystem. This method should only be used for
1124      * application-level mappings.
1125      *
1126      * @param flav the <code>DataFlavor</code> key for the mappings
1127      * @param natives the <code>String</code> native values for the mappings
1128      * @throws NullPointerException if flav or natives is <code>null</code>
1129      *         or if natives contains <code>null</code> elements
1130      *
1131      * @see #setFlavorsForNative
1132      * @since 1.4
1133      */
1134     public synchronized void setNativesForFlavor(DataFlavor flav,
1135                                                  String[] natives) {
1136         if (flav == null || natives == null) {
1137             throw new NullPointerException("null arguments not permitted");
1138         }
1139 
1140         getFlavorToNative().remove(flav);
1141         for (int i = 0; i < natives.length; i++) {
1142             addUnencodedNativeForFlavor(flav, natives[i]);
1143         }
1144         disabledMappingGenerationKeys.add(flav);
1145         // Clear the cache to handle the case of empty natives.
1146         getNativesForFlavorCache.remove(flav);
1147         getNativesForFlavorCache.remove(null);
1148     }
1149 
1150     /**
1151      * Adds a mapping from a single <code>String</code> native to a single
1152      * <code>DataFlavor</code>. Unlike <code>getFlavorsForNative</code>, the
1153      * mapping will only be established in one direction, and the native will
1154      * not be encoded. To establish a two-way mapping, call
1155      * <code>addUnencodedNativeForFlavor</code> as well. The new mapping will
1156      * be of lower priority than any existing mapping.
1157      * This method has no effect if a mapping from the specified
1158      * <code>String</code> native to the specified or equal
1159      * <code>DataFlavor</code> already exists.
1160      *
1161      * @param nat the <code>String</code> native key for the mapping
1162      * @param flav the <code>DataFlavor</code> value for the mapping
1163      * @throws NullPointerException if nat or flav is <code>null</code>
1164      *
1165      * @see #addUnencodedNativeForFlavor
1166      * @since 1.4
1167      */
1168     public synchronized void addFlavorForUnencodedNative(String nat,
1169                                                          DataFlavor flav) {
1170         if (nat == null || flav == null) {
1171             throw new NullPointerException("null arguments not permitted");
1172         }
1173 
1174         List flavors = (List)getNativeToFlavor().get(nat);
1175         if (flavors == null) {
1176             flavors = new ArrayList(1);
1177             getNativeToFlavor().put(nat, flavors);
1178         } else if (flavors.contains(flav)) {
1179             return;
1180         }
1181         flavors.add(flav);
1182         getFlavorsForNativeCache.remove(nat);
1183         getFlavorsForNativeCache.remove(null);
1184     }
1185 
1186     /**
1187      * Discards the current mappings for the specified <code>String</code>
1188      * native, and creates new mappings to the specified
1189      * <code>DataFlavor</code>s. Unlike <code>getFlavorsForNative</code>, the
1190      * mappings will only be established in one direction, and the natives need
1191      * not be encoded. To establish two-way mappings, call
1192      * <code>setNativesForFlavor</code> as well. The first
1193      * <code>DataFlavor</code> in the array will represent the highest priority
1194      * mapping. Subsequent <code>DataFlavor</code>s will represent mappings of
1195      * decreasing priority.
1196      * <p>


1200      * <p>
1201      * It is recommended that client code not reset mappings established by the
1202      * data transfer subsystem. This method should only be used for
1203      * application-level mappings.
1204      *
1205      * @param nat the <code>String</code> native key for the mappings
1206      * @param flavors the <code>DataFlavor</code> values for the mappings
1207      * @throws NullPointerException if nat or flavors is <code>null</code>
1208      *         or if flavors contains <code>null</code> elements
1209      *
1210      * @see #setNativesForFlavor
1211      * @since 1.4
1212      */
1213     public synchronized void setFlavorsForNative(String nat,
1214                                                  DataFlavor[] flavors) {
1215         if (nat == null || flavors == null) {
1216             throw new NullPointerException("null arguments not permitted");
1217         }
1218 
1219         getNativeToFlavor().remove(nat);
1220         for (int i = 0; i < flavors.length; i++) {
1221             addFlavorForUnencodedNative(nat, flavors[i]);
1222         }
1223         disabledMappingGenerationKeys.add(nat);
1224         // Clear the cache to handle the case of empty flavors.
1225         getFlavorsForNativeCache.remove(nat);
1226         getFlavorsForNativeCache.remove(null);
1227     }
1228 
1229     /**
1230      * Encodes a MIME type for use as a <code>String</code> native. The format
1231      * of an encoded representation of a MIME type is implementation-dependent.
1232      * The only restrictions are:
1233      * <ul>
1234      * <li>The encoded representation is <code>null</code> if and only if the
1235      * MIME type <code>String</code> is <code>null</code>.</li>
1236      * <li>The encoded representations for two non-<code>null</code> MIME type
1237      * <code>String</code>s are equal if and only if these <code>String</code>s
1238      * are equal according to <code>String.equals(Object)</code>.</li>
1239      * </ul>
1240      * <p>
1241      * The reference implementation of this method returns the specified MIME


1304             ? nat.substring(JavaMIME.length(), nat.length()).trim()
1305             : null;
1306     }
1307 
1308     /**
1309      * Decodes a <code>String</code> native for use as a
1310      * <code>DataFlavor</code>.
1311      *
1312      * @param nat the <code>String</code> to decode
1313      * @return the decoded <code>DataFlavor</code>, or <code>null</code> if
1314      *         nat is not an encoded <code>String</code> native
1315      */
1316     public static DataFlavor decodeDataFlavor(String nat)
1317         throws ClassNotFoundException
1318     {
1319         String retval_str = SystemFlavorMap.decodeJavaMIMEType(nat);
1320         return (retval_str != null)
1321             ? new DataFlavor(retval_str)
1322             : null;
1323     }














1324 }


  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  *
  60  * @since 1.2
  61  */
  62 public final class SystemFlavorMap implements FlavorMap, FlavorTable {
  63 
  64     /**
  65      * Constant prefix used to tag Java types converted to native platform
  66      * type.
  67      */
  68     private static String JavaMIME = "JAVA_DATAFLAVOR:";
  69 
  70     /**
  71      * System singleton which maps a thread's ClassLoader to a SystemFlavorMap.
  72      */
  73     private static final WeakHashMap<ClassLoader, FlavorMap> flavorMaps = new WeakHashMap<>();
  74 
  75     /**
  76      * Copied from java.util.Properties.
  77      */
  78     private static final String keyValueSeparators = "=: \t\r\n\f";
  79     private static final String strictKeyValueSeparators = "=:";
  80     private static final String whiteSpaceChars = " \t\r\n\f";
  81 
  82     /**
  83      * The list of valid, decoded text flavor representation classes, in order
  84      * from best to worst.
  85      */
  86     private static final String[] UNICODE_TEXT_CLASSES = {
  87         "java.io.Reader", "java.lang.String", "java.nio.CharBuffer", "\"[C\""
  88     };
  89 
  90     /**
  91      * The list of valid, encoded text flavor representation classes, in order
  92      * from best to worst.
  93      */


 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<String, List<DataFlavor>> 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 final Map<DataFlavor, List<String>> 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<DataFlavor, List<String>> 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<DataFlavor, SoftReference<List<String>>> 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<String, SoftReference<List<DataFlavor>>> 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 = 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;


 503                     } else if (aChar == 'n') {
 504                         aChar = '\n';
 505                     } else if (aChar == 'f') {
 506                         aChar = '\f';
 507                     }
 508                     outBuffer.append(aChar);
 509                 }
 510             } else {
 511                 outBuffer.append(aChar);
 512             }
 513         }
 514         return outBuffer.toString();
 515     }
 516 
 517     /**
 518      * Stores the listed object under the specified hash key in map. Unlike a
 519      * standard map, the listed object will not replace any object already at
 520      * the appropriate Map location, but rather will be appended to a List
 521      * stored in that location.
 522      */
 523     private <H, L> void store(H hashed, L listed, Map<H, List<L>> map) {
 524         List<L> list = map.get(hashed);
 525         if (list == null) {
 526             list = new ArrayList<>(1);
 527             map.put(hashed, list);
 528         }
 529         if (!list.contains(listed)) {
 530             list.add(listed);
 531         }
 532     }
 533 
 534     /**
 535      * Semantically equivalent to 'nativeToFlavor.get(nat)'. This method
 536      * handles the case where 'nat' is not found in 'nativeToFlavor'. In that
 537      * case, a new DataFlavor is synthesized, stored, and returned, if and
 538      * only if the specified native is encoded as a Java MIME type.
 539      */
 540     private List<DataFlavor> nativeToFlavorLookup(String nat) {
 541         List<DataFlavor> flavors = getNativeToFlavor().get(nat);
 542 
 543         if (nat != null && !disabledMappingGenerationKeys.contains(nat)) {
 544             DataTransferer transferer = DataTransferer.getInstance();
 545             if (transferer != null) {
 546                 List<DataFlavor> platformFlavors =
 547                     transferer.getPlatformMappingsForNative(nat);
 548                 if (!platformFlavors.isEmpty()) {
 549                     if (flavors != null) {
 550                         platformFlavors.removeAll(new HashSet<>(flavors));
 551                         // Prepending the platform-specific mappings ensures
 552                         // that the flavors added with
 553                         // addFlavorForUnencodedNative() are at the end of
 554                         // list.
 555                         platformFlavors.addAll(flavors);
 556                     }
 557                     flavors = platformFlavors;
 558                 }
 559             }
 560         }
 561 
 562         if (flavors == null && isJavaMIMEType(nat)) {
 563             String decoded = decodeJavaMIMEType(nat);
 564             DataFlavor flavor = null;
 565 
 566             try {
 567                 flavor = new DataFlavor(decoded);
 568             } catch (Exception e) {
 569                 System.err.println("Exception \"" + e.getClass().getName() +
 570                                    ": " + e.getMessage()  +
 571                                    "\"while constructing DataFlavor for: " +
 572                                    decoded);
 573             }
 574 
 575             if (flavor != null) {
 576                 flavors = new ArrayList<>(1);
 577                 getNativeToFlavor().put(nat, flavors);
 578                 flavors.add(flavor);
 579                 getFlavorsForNativeCache.remove(nat);
 580                 getFlavorsForNativeCache.remove(null);
 581 
 582                 List<String> natives = getFlavorToNative().get(flavor);
 583                 if (natives == null) {
 584                     natives = new ArrayList<>(1);
 585                     getFlavorToNative().put(flavor, natives);
 586                 }
 587                 natives.add(nat);
 588                 getNativesForFlavorCache.remove(flavor);
 589                 getNativesForFlavorCache.remove(null);
 590             }
 591         }
 592 
 593         return (flavors != null) ? flavors : new ArrayList<>(0);
 594     }
 595 
 596     /**
 597      * Semantically equivalent to 'flavorToNative.get(flav)'. This method
 598      * handles the case where 'flav' is not found in 'flavorToNative' depending
 599      * on the value of passes 'synthesize' parameter. If 'synthesize' is
 600      * SYNTHESIZE_IF_NOT_FOUND a native is synthesized, stored, and returned by
 601      * encoding the DataFlavor's MIME type. Otherwise an empty List is returned
 602      * and 'flavorToNative' remains unaffected.
 603      */
 604     private List<String> flavorToNativeLookup(final DataFlavor flav,
 605                                               final boolean synthesize) {
 606         List<String> natives = getFlavorToNative().get(flav);
 607 
 608         if (flav != null && !disabledMappingGenerationKeys.contains(flav)) {
 609             DataTransferer transferer = DataTransferer.getInstance();
 610             if (transferer != null) {
 611                 List<String> platformNatives =
 612                     transferer.getPlatformMappingsForFlavor(flav);
 613                 if (!platformNatives.isEmpty()) {
 614                     if (natives != null) {
 615                         platformNatives.removeAll(new HashSet<>(natives));
 616                         // Prepend the platform-specific mappings to ensure
 617                         // that the natives added with
 618                         // addUnencodedNativeForFlavor() are at the end of
 619                         // list.
 620                         platformNatives.addAll(natives);
 621                     }
 622                     natives = platformNatives;
 623                 }
 624             }
 625         }
 626 
 627         if (natives == null) {
 628             if (synthesize) {
 629                 String encoded = encodeDataFlavor(flav);
 630                 natives = new ArrayList<>(1);
 631                 getFlavorToNative().put(flav, natives);
 632                 natives.add(encoded);
 633                 getNativesForFlavorCache.remove(flav);
 634                 getNativesForFlavorCache.remove(null);
 635 
 636                 List<DataFlavor> flavors = getNativeToFlavor().get(encoded);
 637                 if (flavors == null) {
 638                     flavors = new ArrayList<>(1);
 639                     getNativeToFlavor().put(encoded, flavors);
 640                 }
 641                 flavors.add(flav);
 642                 getFlavorsForNativeCache.remove(encoded);
 643                 getFlavorsForNativeCache.remove(null);
 644             } else {
 645                 natives = new ArrayList<>(0);
 646             }
 647         }
 648 
 649         return natives;
 650     }
 651 
 652     /**
 653      * Returns a <code>List</code> of <code>String</code> natives to which the
 654      * specified <code>DataFlavor</code> can be translated by the data transfer
 655      * subsystem. The <code>List</code> will be sorted from best native to
 656      * worst. That is, the first native will best reflect data in the specified
 657      * flavor to the underlying native platform.
 658      * <p>
 659      * If the specified <code>DataFlavor</code> is previously unknown to the
 660      * data transfer subsystem and the data transfer subsystem is unable to
 661      * translate this <code>DataFlavor</code> to any existing native, then
 662      * invoking this method will establish a
 663      * mapping in both directions between the specified <code>DataFlavor</code>
 664      * and an encoded version of its MIME type as its native.
 665      *
 666      * @param flav the <code>DataFlavor</code> whose corresponding natives
 667      *        should be returned. If <code>null</code> is specified, all
 668      *        natives currently known to the data transfer subsystem are
 669      *        returned in a non-deterministic order.
 670      * @return a <code>java.util.List</code> of <code>java.lang.String</code>
 671      *         objects which are platform-specific representations of platform-
 672      *         specific data formats
 673      *
 674      * @see #encodeDataFlavor
 675      * @since 1.4
 676      */
 677     public synchronized List<String> getNativesForFlavor(DataFlavor flav) {
 678         List<String> retval = null;
 679 
 680         // Check cache, even for null flav
 681         SoftReference<List<String>> ref = getNativesForFlavorCache.get(flav);
 682         if (ref != null) {
 683             retval = ref.get();
 684             if (retval != null) {
 685                 // Create a copy, because client code can modify the returned
 686                 // list.
 687                 return new ArrayList<>(retval);
 688             }
 689         }
 690 
 691         if (flav == null) {
 692             retval = new ArrayList<>(getNativeToFlavor().keySet());
 693         } else if (disabledMappingGenerationKeys.contains(flav)) {
 694             // In this case we shouldn't synthesize a native for this flavor,
 695             // since its mappings were explicitly specified.
 696             retval = flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND);
 697         } else if (DataTransferer.isFlavorCharsetTextType(flav)) {
 698 
 699             // For text/* flavors, flavor-to-native mappings specified in
 700             // flavormap.properties are stored per flavor's base type.
 701             if ("text".equals(flav.getPrimaryType())) {
 702                 retval = getAllNativesForType(flav.mimeType.getBaseType());
 703                 if (retval != null) {
 704                     // To prevent the List stored in the map from modification.
 705                     retval = new ArrayList(retval);
 706                 }
 707             }
 708 
 709             // Also include text/plain natives, but don't duplicate Strings
 710             List<String> textPlainList = getAllNativesForType(TEXT_PLAIN_BASE_TYPE);
 711 
 712             if (textPlainList != null && !textPlainList.isEmpty()) {
 713                 // To prevent the List stored in the map from modification.
 714                 // This also guarantees that removeAll() is supported.
 715                 textPlainList = new ArrayList<>(textPlainList);
 716                 if (retval != null && !retval.isEmpty()) {
 717                     // Use HashSet to get constant-time performance for search.
 718                     textPlainList.removeAll(new HashSet<>(retval));
 719                     retval.addAll(textPlainList);
 720                 } else {
 721                     retval = textPlainList;
 722                 }
 723             }
 724 
 725             if (retval == null || retval.isEmpty()) {
 726                 retval = flavorToNativeLookup(flav, SYNTHESIZE_IF_NOT_FOUND);
 727             } else {
 728                 // In this branch it is guaranteed that natives explicitly
 729                 // listed for flav's MIME type were added with
 730                 // addUnencodedNativeForFlavor(), so they have lower priority.
 731                 List<String> explicitList =
 732                     flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND);
 733 
 734                 // flavorToNativeLookup() never returns null.
 735                 // It can return an empty List, however.
 736                 if (!explicitList.isEmpty()) {
 737                     // To prevent the List stored in the map from modification.
 738                     // This also guarantees that removeAll() is supported.
 739                     explicitList = new ArrayList<>(explicitList);
 740                     // Use HashSet to get constant-time performance for search.
 741                     explicitList.removeAll(new HashSet<>(retval));
 742                     retval.addAll(explicitList);
 743                 }
 744             }
 745         } else if (DataTransferer.isFlavorNoncharsetTextType(flav)) {
 746             retval = getAllNativesForType(flav.mimeType.getBaseType());
 747 
 748             if (retval == null || retval.isEmpty()) {
 749                 retval = flavorToNativeLookup(flav, SYNTHESIZE_IF_NOT_FOUND);
 750             } else {
 751                 // In this branch it is guaranteed that natives explicitly
 752                 // listed for flav's MIME type were added with
 753                 // addUnencodedNativeForFlavor(), so they have lower priority.
 754                 List<String> explicitList =
 755                     flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND);
 756 
 757                 // flavorToNativeLookup() never returns null.
 758                 // It can return an empty List, however.
 759                 if (!explicitList.isEmpty()) {
 760                     // To prevent the List stored in the map from modification.
 761                     // This also guarantees that add/removeAll() are supported.
 762                     retval = new ArrayList<>(retval);
 763                     explicitList = new ArrayList<>(explicitList);
 764                     // Use HashSet to get constant-time performance for search.
 765                     explicitList.removeAll(new HashSet<>(retval));
 766                     retval.addAll(explicitList);
 767                 }
 768             }
 769         } else {
 770             retval = flavorToNativeLookup(flav, SYNTHESIZE_IF_NOT_FOUND);
 771         }
 772 
 773         getNativesForFlavorCache.put(flav, new SoftReference<>(retval));
 774         // Create a copy, because client code can modify the returned list.
 775         return new ArrayList<>(retval);
 776     }
 777 
 778     /**
 779      * Returns a <code>List</code> of <code>DataFlavor</code>s to which the
 780      * specified <code>String</code> native can be translated by the data
 781      * transfer subsystem. The <code>List</code> will be sorted from best
 782      * <code>DataFlavor</code> to worst. That is, the first
 783      * <code>DataFlavor</code> will best reflect data in the specified
 784      * native to a Java application.
 785      * <p>
 786      * If the specified native is previously unknown to the data transfer
 787      * subsystem, and that native has been properly encoded, then invoking this
 788      * method will establish a mapping in both directions between the specified
 789      * native and a <code>DataFlavor</code> whose MIME type is a decoded
 790      * version of the native.
 791      * <p>
 792      * If the specified native is not a properly encoded native and the
 793      * mappings for this native have not been altered with
 794      * <code>setFlavorsForNative</code>, then the contents of the
 795      * <code>List</code> is platform dependent, but <code>null</code>
 796      * cannot be returned.
 797      *
 798      * @param nat the native whose corresponding <code>DataFlavor</code>s
 799      *        should be returned. If <code>null</code> is specified, all
 800      *        <code>DataFlavor</code>s currently known to the data transfer
 801      *        subsystem are returned in a non-deterministic order.
 802      * @return a <code>java.util.List</code> of <code>DataFlavor</code>
 803      *         objects into which platform-specific data in the specified,
 804      *         platform-specific native can be translated
 805      *
 806      * @see #encodeJavaMIMEType
 807      * @since 1.4
 808      */
 809     public synchronized List<DataFlavor> getFlavorsForNative(String nat) {
 810 
 811         // Check cache, even for null nat
 812         SoftReference<List<DataFlavor>> ref = getFlavorsForNativeCache.get(nat);
 813         if (ref != null) {
 814             List<DataFlavor> retval = ref.get();
 815             if (retval != null) {
 816                 return new ArrayList<>(retval);
 817             }
 818         }
 819 
 820         final LinkedHashSet <DataFlavor> returnValue =
 821             new LinkedHashSet<>();
 822 
 823         if (nat == null) {
 824             final List<String> natives = getNativesForFlavor(null);
 825 
 826             for (String n : natives)
 827             {
 828                 final List<DataFlavor> flavors = getFlavorsForNative(n);
 829 
 830                 for (DataFlavor df : flavors)
 831                 {
 832                     returnValue.add(df);
 833                 }
 834             }
 835         } else {
 836 


 842 
 843             final List<DataFlavor> flavorsAndBaseTypes =
 844                 nativeToFlavorLookup(nat);
 845 
 846             for (DataFlavor df : flavorsAndBaseTypes) {
 847                 returnValue.add(df);
 848                 if ("text".equals(df.getPrimaryType())) {
 849                     try {
 850                         returnValue.addAll(
 851                                 convertMimeTypeToDataFlavors(
 852                                         new MimeType(df.getMimeType()
 853                                         ).getBaseType()));
 854                     } catch (MimeTypeParseException e) {
 855                         e.printStackTrace();
 856                     }
 857                 }
 858             }
 859 
 860         }
 861 
 862         final List<DataFlavor> arrayList = new ArrayList<>(returnValue);
 863         getFlavorsForNativeCache.put(nat, new SoftReference<>(arrayList));
 864         return new ArrayList<>(arrayList);
 865     }
 866 
 867     private static Set<DataFlavor> convertMimeTypeToDataFlavors(
 868         final String baseType) {
 869 
 870         final Set<DataFlavor> returnValue = new LinkedHashSet<>();

 871 
 872         String subType = null;
 873 
 874         try {
 875             final MimeType mimeType = new MimeType(baseType);
 876             subType = mimeType.getSubType();
 877         } catch (MimeTypeParseException mtpe) {
 878             // Cannot happen, since we checked all mappings
 879             // on load from flavormap.properties.
 880             assert(false);
 881         }
 882 
 883         if (DataTransferer.doesSubtypeSupportCharset(subType, null)) {
 884             if (TEXT_PLAIN_BASE_TYPE.equals(baseType))
 885             {
 886                 returnValue.add(DataFlavor.stringFlavor);
 887             }
 888 
 889             for (String unicodeClassName : UNICODE_TEXT_CLASSES) {
 890                 final String mimeType = baseType + ";charset=Unicode;class=" +


 991      *        data transfer subsystem to their most preferred
 992      *        <code>String</code> natives will be returned.
 993      * @return a <code>java.util.Map</code> of <code>DataFlavor</code>s to
 994      *         <code>String</code> natives
 995      *
 996      * @see #getNativesForFlavor
 997      * @see #encodeDataFlavor
 998      */
 999     public synchronized Map<DataFlavor,String>
1000         getNativesForFlavors(DataFlavor[] flavors)
1001     {
1002         // Use getNativesForFlavor to generate extra natives for text flavors
1003         // and stringFlavor
1004 
1005         if (flavors == null) {
1006             List flavor_list = getFlavorsForNative(null);
1007             flavors = new DataFlavor[flavor_list.size()];
1008             flavor_list.toArray(flavors);
1009         }
1010 
1011         Map<DataFlavor, String> retval = new HashMap<>(flavors.length, 1.0f);
1012         for (DataFlavor flavor : flavors) {
1013             List<String> natives = getNativesForFlavor(flavor);
1014             String nat = (natives.isEmpty()) ? null : natives.get(0);
1015             retval.put(flavor, nat);
1016         }
1017 
1018         return retval;
1019     }
1020 
1021     /**
1022      * Returns a <code>Map</code> of the specified <code>String</code> natives
1023      * to their most preferred <code>DataFlavor</code>. Each
1024      * <code>DataFlavor</code> value will be the same as the first
1025      * <code>DataFlavor</code> in the List returned by
1026      * <code>getFlavorsForNative</code> for the specified native.
1027      * <p>
1028      * If a specified native is previously unknown to the data transfer
1029      * subsystem, and that native has been properly encoded, then invoking this
1030      * method will establish a mapping in both directions between the specified
1031      * native and a <code>DataFlavor</code> whose MIME type is a decoded
1032      * version of the native.
1033      *
1034      * @param natives an array of <code>String</code>s which will be the
1035      *        key set of the returned <code>Map</code>. If <code>null</code> is
1036      *        specified, a mapping of all supported <code>String</code> natives
1037      *        to their most preferred <code>DataFlavor</code>s will be
1038      *        returned.
1039      * @return a <code>java.util.Map</code> of <code>String</code> natives to
1040      *         <code>DataFlavor</code>s
1041      *
1042      * @see #getFlavorsForNative
1043      * @see #encodeJavaMIMEType
1044      */
1045     public synchronized Map<String,DataFlavor>
1046         getFlavorsForNatives(String[] natives)
1047     {
1048         // Use getFlavorsForNative to generate extra flavors for text natives
1049 
1050         if (natives == null) {
1051             List native_list = getNativesForFlavor(null);
1052             natives = new String[native_list.size()];
1053             native_list.toArray(natives);
1054         }
1055 
1056         Map<String, DataFlavor> retval = new HashMap<>(natives.length, 1.0f);
1057         for (String aNative : natives) {
1058             List<DataFlavor> flavors = getFlavorsForNative(aNative);
1059             DataFlavor flav = (flavors.isEmpty())? null : flavors.get(0);
1060             retval.put(aNative, flav);

1061         }
1062 
1063         return retval;
1064     }
1065 
1066     /**
1067      * Adds a mapping from the specified <code>DataFlavor</code> (and all
1068      * <code>DataFlavor</code>s equal to the specified <code>DataFlavor</code>)
1069      * to the specified <code>String</code> native.
1070      * Unlike <code>getNativesForFlavor</code>, the mapping will only be
1071      * established in one direction, and the native will not be encoded. To
1072      * establish a two-way mapping, call
1073      * <code>addFlavorForUnencodedNative</code> as well. The new mapping will
1074      * be of lower priority than any existing mapping.
1075      * This method has no effect if a mapping from the specified or equal
1076      * <code>DataFlavor</code> to the specified <code>String</code> native
1077      * already exists.
1078      *
1079      * @param flav the <code>DataFlavor</code> key for the mapping
1080      * @param nat the <code>String</code> native value for the mapping
1081      * @throws NullPointerException if flav or nat is <code>null</code>
1082      *
1083      * @see #addFlavorForUnencodedNative
1084      * @since 1.4
1085      */
1086     public synchronized void addUnencodedNativeForFlavor(DataFlavor flav,
1087                                                          String nat) {
1088         if (flav == null || nat == null) {
1089             throw new NullPointerException("null arguments not permitted");
1090         }
1091 
1092         List<String> natives = getFlavorToNative().get(flav);
1093         if (natives == null) {
1094             natives = new ArrayList<>(1);
1095             getFlavorToNative().put(flav, natives);
1096         } else if (natives.contains(nat)) {
1097             return;
1098         }
1099         natives.add(nat);
1100         getNativesForFlavorCache.remove(flav);
1101         getNativesForFlavorCache.remove(null);
1102     }
1103 
1104     /**
1105      * Discards the current mappings for the specified <code>DataFlavor</code>
1106      * and all <code>DataFlavor</code>s equal to the specified
1107      * <code>DataFlavor</code>, and creates new mappings to the
1108      * specified <code>String</code> natives.
1109      * Unlike <code>getNativesForFlavor</code>, the mappings will only be
1110      * established in one direction, and the natives will not be encoded. To
1111      * establish two-way mappings, call <code>setFlavorsForNative</code>
1112      * as well. The first native in the array will represent the highest
1113      * priority mapping. Subsequent natives will represent mappings of
1114      * decreasing priority.


1119      * <p>
1120      * It is recommended that client code not reset mappings established by the
1121      * data transfer subsystem. This method should only be used for
1122      * application-level mappings.
1123      *
1124      * @param flav the <code>DataFlavor</code> key for the mappings
1125      * @param natives the <code>String</code> native values for the mappings
1126      * @throws NullPointerException if flav or natives is <code>null</code>
1127      *         or if natives contains <code>null</code> elements
1128      *
1129      * @see #setFlavorsForNative
1130      * @since 1.4
1131      */
1132     public synchronized void setNativesForFlavor(DataFlavor flav,
1133                                                  String[] natives) {
1134         if (flav == null || natives == null) {
1135             throw new NullPointerException("null arguments not permitted");
1136         }
1137 
1138         getFlavorToNative().remove(flav);
1139         for (String aNative : natives) {
1140             addUnencodedNativeForFlavor(flav, aNative);
1141         }
1142         disabledMappingGenerationKeys.add(flav);
1143         // Clear the cache to handle the case of empty natives.
1144         getNativesForFlavorCache.remove(flav);
1145         getNativesForFlavorCache.remove(null);
1146     }
1147 
1148     /**
1149      * Adds a mapping from a single <code>String</code> native to a single
1150      * <code>DataFlavor</code>. Unlike <code>getFlavorsForNative</code>, the
1151      * mapping will only be established in one direction, and the native will
1152      * not be encoded. To establish a two-way mapping, call
1153      * <code>addUnencodedNativeForFlavor</code> as well. The new mapping will
1154      * be of lower priority than any existing mapping.
1155      * This method has no effect if a mapping from the specified
1156      * <code>String</code> native to the specified or equal
1157      * <code>DataFlavor</code> already exists.
1158      *
1159      * @param nat the <code>String</code> native key for the mapping
1160      * @param flav the <code>DataFlavor</code> value for the mapping
1161      * @throws NullPointerException if nat or flav is <code>null</code>
1162      *
1163      * @see #addUnencodedNativeForFlavor
1164      * @since 1.4
1165      */
1166     public synchronized void addFlavorForUnencodedNative(String nat,
1167                                                          DataFlavor flav) {
1168         if (nat == null || flav == null) {
1169             throw new NullPointerException("null arguments not permitted");
1170         }
1171 
1172         List<DataFlavor> flavors = getNativeToFlavor().get(nat);
1173         if (flavors == null) {
1174             flavors = new ArrayList<>(1);
1175             getNativeToFlavor().put(nat, flavors);
1176         } else if (flavors.contains(flav)) {
1177             return;
1178         }
1179         flavors.add(flav);
1180         getFlavorsForNativeCache.remove(nat);
1181         getFlavorsForNativeCache.remove(null);
1182     }
1183 
1184     /**
1185      * Discards the current mappings for the specified <code>String</code>
1186      * native, and creates new mappings to the specified
1187      * <code>DataFlavor</code>s. Unlike <code>getFlavorsForNative</code>, the
1188      * mappings will only be established in one direction, and the natives need
1189      * not be encoded. To establish two-way mappings, call
1190      * <code>setNativesForFlavor</code> as well. The first
1191      * <code>DataFlavor</code> in the array will represent the highest priority
1192      * mapping. Subsequent <code>DataFlavor</code>s will represent mappings of
1193      * decreasing priority.
1194      * <p>


1198      * <p>
1199      * It is recommended that client code not reset mappings established by the
1200      * data transfer subsystem. This method should only be used for
1201      * application-level mappings.
1202      *
1203      * @param nat the <code>String</code> native key for the mappings
1204      * @param flavors the <code>DataFlavor</code> values for the mappings
1205      * @throws NullPointerException if nat or flavors is <code>null</code>
1206      *         or if flavors contains <code>null</code> elements
1207      *
1208      * @see #setNativesForFlavor
1209      * @since 1.4
1210      */
1211     public synchronized void setFlavorsForNative(String nat,
1212                                                  DataFlavor[] flavors) {
1213         if (nat == null || flavors == null) {
1214             throw new NullPointerException("null arguments not permitted");
1215         }
1216 
1217         getNativeToFlavor().remove(nat);
1218         for (DataFlavor flavor : flavors) {
1219             addFlavorForUnencodedNative(nat, flavor);
1220         }
1221         disabledMappingGenerationKeys.add(nat);
1222         // Clear the cache to handle the case of empty flavors.
1223         getFlavorsForNativeCache.remove(nat);
1224         getFlavorsForNativeCache.remove(null);
1225     }
1226 
1227     /**
1228      * Encodes a MIME type for use as a <code>String</code> native. The format
1229      * of an encoded representation of a MIME type is implementation-dependent.
1230      * The only restrictions are:
1231      * <ul>
1232      * <li>The encoded representation is <code>null</code> if and only if the
1233      * MIME type <code>String</code> is <code>null</code>.</li>
1234      * <li>The encoded representations for two non-<code>null</code> MIME type
1235      * <code>String</code>s are equal if and only if these <code>String</code>s
1236      * are equal according to <code>String.equals(Object)</code>.</li>
1237      * </ul>
1238      * <p>
1239      * The reference implementation of this method returns the specified MIME


1302             ? nat.substring(JavaMIME.length(), nat.length()).trim()
1303             : null;
1304     }
1305 
1306     /**
1307      * Decodes a <code>String</code> native for use as a
1308      * <code>DataFlavor</code>.
1309      *
1310      * @param nat the <code>String</code> to decode
1311      * @return the decoded <code>DataFlavor</code>, or <code>null</code> if
1312      *         nat is not an encoded <code>String</code> native
1313      */
1314     public static DataFlavor decodeDataFlavor(String nat)
1315         throws ClassNotFoundException
1316     {
1317         String retval_str = SystemFlavorMap.decodeJavaMIMEType(nat);
1318         return (retval_str != null)
1319             ? new DataFlavor(retval_str)
1320             : null;
1321     }
1322 
1323     private List<String> getAllNativesForType(String type) {
1324         List<String> retval = null;
1325         for (DataFlavor dataFlavor : convertMimeTypeToDataFlavors(type)) {
1326             List<String> natives = getFlavorToNative().get(dataFlavor);
1327             if (!natives.isEmpty()) {
1328                 if (retval == null) {
1329                     retval = new ArrayList<>();
1330                 }
1331                 retval.addAll(natives);
1332             }
1333         }
1334         return retval;
1335     }
1336 }