< prev index next >

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

Print this page


   1 /*
   2  * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.awt.datatransfer;
  27 
  28 import sun.datatransfer.DataFlavorUtil;
  29 import sun.datatransfer.DesktopDatatransferService;
  30 
  31 import java.io.BufferedReader;
  32 import java.io.IOException;
  33 import java.io.InputStream;
  34 import java.io.InputStreamReader;
  35 import java.lang.ref.SoftReference;
  36 import java.security.AccessController;
  37 import java.security.PrivilegedAction;
  38 import java.util.ArrayList;
  39 import java.util.Collections;
  40 import java.util.HashMap;
  41 import java.util.HashSet;
  42 import java.util.LinkedHashSet;
  43 import java.util.List;
  44 import java.util.Map;
  45 import java.util.Objects;
  46 import java.util.Set;
  47 



  48 /**
  49  * The SystemFlavorMap is a configurable map between "natives" (Strings), which
  50  * correspond to platform-specific data formats, and "flavors" (DataFlavors),
  51  * which correspond to platform-independent MIME types. This mapping is used
  52  * by the data transfer subsystem to transfer data between Java and native
  53  * applications, and between Java applications in separate VMs.
  54  *
  55  * @since 1.2
  56  */
  57 public final class SystemFlavorMap implements FlavorMap, FlavorTable {
  58 
  59     /**
  60      * Constant prefix used to tag Java types converted to native platform
  61      * type.
  62      */
  63     private static String JavaMIME = "JAVA_DATAFLAVOR:";
  64 
  65     private static final Object FLAVOR_MAP_KEY = new Object();
  66 
  67     /**
  68      * The list of valid, decoded text flavor representation classes, in order
  69      * from best to worst.
  70      */
  71     private static final String[] UNICODE_TEXT_CLASSES = {
  72         "java.io.Reader", "java.lang.String", "java.nio.CharBuffer", "\"[C\""
  73     };
  74 
  75     /**
  76      * The list of valid, encoded text flavor representation classes, in order
  77      * from best to worst.
  78      */
  79     private static final String[] ENCODED_TEXT_CLASSES = {
  80         "java.io.InputStream", "java.nio.ByteBuffer", "\"[B\""
  81     };
  82 
  83     /**
  84      * A String representing text/plain MIME type.
  85      */
  86     private static final String TEXT_PLAIN_BASE_TYPE = "text/plain";
  87 
  88     /**
  89      * A String representing text/html MIME type.
  90      */
  91     private static final String HTML_TEXT_BASE_TYPE = "text/html";
  92 
  93     /**
  94      * Maps native Strings to Lists of DataFlavors (or base type Strings for
  95      * text DataFlavors).
  96      * Do not use the field directly, use getNativeToFlavor() instead.

  97      */
  98     private final Map<String, LinkedHashSet<DataFlavor>> nativeToFlavor = new HashMap<>();
  99 
 100     /**
 101      * Accessor to nativeToFlavor map.  Since we use lazy initialization we must
 102      * use this accessor instead of direct access to the field which may not be
 103      * initialized yet.  This method will initialize the field if needed.
 104      *
 105      * @return nativeToFlavor
 106      */
 107     private Map<String, LinkedHashSet<DataFlavor>> getNativeToFlavor() {
 108         if (!isMapInitialized) {
 109             initSystemFlavorMap();
 110         }
 111         return nativeToFlavor;
 112     }
 113 
 114     /**
 115      * Maps DataFlavors (or base type Strings for text DataFlavors) to Lists of
 116      * native Strings.
 117      * Do not use the field directly, use getFlavorToNative() instead.

 118      */
 119     private final Map<DataFlavor, LinkedHashSet<String>> flavorToNative = new HashMap<>();
 120 
 121     /**
 122      * Accessor to flavorToNative map.  Since we use lazy initialization we must
 123      * use this accessor instead of direct access to the field which may not be
 124      * initialized yet.  This method will initialize the field if needed.
 125      *
 126      * @return flavorToNative
 127      */
 128     private synchronized Map<DataFlavor, LinkedHashSet<String>> getFlavorToNative() {
 129         if (!isMapInitialized) {
 130             initSystemFlavorMap();
 131         }
 132         return flavorToNative;
 133     }
 134 
 135     /**
 136      * Maps a text DataFlavor primary mime-type to the native. Used only to store
 137      * standard mappings registered in the flavormap.properties
 138      * Do not use this field directly, use getTextTypeToNative() instead.

 139      */
 140     private Map<String, LinkedHashSet<String>> textTypeToNative = new HashMap<>();
 141 
 142     /**
 143      * Shows if the object has been initialized.
 144      */
 145     private boolean isMapInitialized = false;
 146 
 147     /**
 148      * An accessor to textTypeToNative map.  Since we use lazy initialization we
 149      * must use this accessor instead of direct access to the field which may not
 150      * be initialized yet. This method will initialize the field if needed.
 151      *
 152      * @return textTypeToNative
 153      */
 154     private synchronized Map<String, LinkedHashSet<String>> getTextTypeToNative() {
 155         if (!isMapInitialized) {
 156             initSystemFlavorMap();
 157             // From this point the map should not be modified
 158             textTypeToNative = Collections.unmodifiableMap(textTypeToNative);
 159         }
 160         return textTypeToNative;
 161     }
 162 
 163     /**
 164      * Caches the result of getNativesForFlavor(). Maps DataFlavors to
 165      * SoftReferences which reference LinkedHashSet of String natives.
 166      */
 167     private final SoftCache<DataFlavor, String> nativesForFlavorCache = new SoftCache<>();
 168 
 169     /**
 170      * Caches the result getFlavorsForNative(). Maps String natives to
 171      * SoftReferences which reference LinkedHashSet of DataFlavors.
 172      */
 173     private final SoftCache<String, DataFlavor> flavorsForNativeCache = new SoftCache<>();
 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<Object> disabledMappingGenerationKeys = new HashSet<>();
 182 
 183     /**
 184      * Returns the default FlavorMap for this thread's ClassLoader.
 185      *
 186      * @return the default FlavorMap for this thread's ClassLoader
 187      */
 188     public static FlavorMap getDefaultFlavorMap() {
 189         return DataFlavorUtil.getDesktopService().getFlavorMap(SystemFlavorMap::new);
 190     }
 191 
 192     private SystemFlavorMap() {
 193     }
 194 
 195     /**
 196      * Initializes a SystemFlavorMap by reading flavormap.properties
 197      * For thread-safety must be called under lock on this.
 198      */
 199     private void initSystemFlavorMap() {
 200         if (isMapInitialized) {
 201             return;
 202         }
 203         isMapInitialized = true;
 204 
 205         InputStream is = AccessController.doPrivileged(
 206             (PrivilegedAction<InputStream>) () -> {
 207                 return SystemFlavorMap.class.getResourceAsStream(
 208                         "/sun/datatransfer/resources/flavormap.properties");
 209             });
 210         if (is == null) {
 211             throw new InternalError("Default flavor mapping not found");
 212         }
 213 
 214         try (InputStreamReader isr = new InputStreamReader(is);
 215              BufferedReader reader = new BufferedReader(isr)) {
 216             String line;
 217             while ((line = reader.readLine()) != null) {


 349     }
 350 
 351     /**
 352      * Stores the listed object under the specified hash key in map. Unlike a
 353      * standard map, the listed object will not replace any object already at
 354      * the appropriate Map location, but rather will be appended to a List
 355      * stored in that location.
 356      */
 357     private <H, L> void store(H hashed, L listed, Map<H, LinkedHashSet<L>> map) {
 358         LinkedHashSet<L> list = map.get(hashed);
 359         if (list == null) {
 360             list = new LinkedHashSet<>(1);
 361             map.put(hashed, list);
 362         }
 363         if (!list.contains(listed)) {
 364             list.add(listed);
 365         }
 366     }
 367 
 368     /**
 369      * Semantically equivalent to 'nativeToFlavor.get(nat)'. This method
 370      * handles the case where 'nat' is not found in 'nativeToFlavor'. In that
 371      * case, a new DataFlavor is synthesized, stored, and returned, if and
 372      * only if the specified native is encoded as a Java MIME type.
 373      */
 374     private LinkedHashSet<DataFlavor> nativeToFlavorLookup(String nat) {
 375         LinkedHashSet<DataFlavor> flavors = getNativeToFlavor().get(nat);
 376 
 377         if (nat != null && !disabledMappingGenerationKeys.contains(nat)) {
 378             DesktopDatatransferService desktopService = DataFlavorUtil.getDesktopService();
 379             if (desktopService.isDesktopPresent()) {
 380                 LinkedHashSet<DataFlavor> platformFlavors =
 381                         desktopService.getPlatformMappingsForNative(nat);
 382                 if (!platformFlavors.isEmpty()) {
 383                     if (flavors != null) {
 384                         // Prepending the platform-specific mappings ensures
 385                         // that the flavors added with
 386                         // addFlavorForUnencodedNative() are at the end of
 387                         // list.
 388                         platformFlavors.addAll(flavors);
 389                     }
 390                     flavors = platformFlavors;
 391                 }
 392             }


 463                 natives.add(encoded);
 464 
 465                 LinkedHashSet<DataFlavor> flavors = getNativeToFlavor().get(encoded);
 466                 if (flavors == null) {
 467                     flavors = new LinkedHashSet<>(1);
 468                     getNativeToFlavor().put(encoded, flavors);
 469                 }
 470                 flavors.add(flav);
 471 
 472                 nativesForFlavorCache.remove(flav);
 473                 flavorsForNativeCache.remove(encoded);
 474             } else {
 475                 natives = new LinkedHashSet<>(0);
 476             }
 477         }
 478 
 479         return new LinkedHashSet<>(natives);
 480     }
 481 
 482     /**
 483      * Returns a <code>List</code> of <code>String</code> natives to which the
 484      * specified <code>DataFlavor</code> can be translated by the data transfer
 485      * subsystem. The <code>List</code> will be sorted from best native to
 486      * worst. That is, the first native will best reflect data in the specified
 487      * flavor to the underlying native platform.
 488      * <p>
 489      * If the specified <code>DataFlavor</code> is previously unknown to the
 490      * data transfer subsystem and the data transfer subsystem is unable to
 491      * translate this <code>DataFlavor</code> to any existing native, then
 492      * invoking this method will establish a
 493      * mapping in both directions between the specified <code>DataFlavor</code>
 494      * and an encoded version of its MIME type as its native.
 495      *
 496      * @param flav the <code>DataFlavor</code> whose corresponding natives
 497      *        should be returned. If <code>null</code> is specified, all
 498      *        natives currently known to the data transfer subsystem are
 499      *        returned in a non-deterministic order.
 500      * @return a <code>java.util.List</code> of <code>java.lang.String</code>
 501      *         objects which are platform-specific representations of platform-
 502      *         specific data formats
 503      *
 504      * @see #encodeDataFlavor
 505      * @since 1.4
 506      */
 507     @Override
 508     public synchronized List<String> getNativesForFlavor(DataFlavor flav) {
 509         LinkedHashSet<String> retval = nativesForFlavorCache.check(flav);
 510         if (retval != null) {
 511             return new ArrayList<>(retval);
 512         }
 513 
 514         if (flav == null) {
 515             retval = new LinkedHashSet<>(getNativeToFlavor().keySet());
 516         } else if (disabledMappingGenerationKeys.contains(flav)) {
 517             // In this case we shouldn't synthesize a native for this flavor,
 518             // since its mappings were explicitly specified.
 519             retval = flavorToNativeLookup(flav, false);
 520         } else if (DataFlavorUtil.isFlavorCharsetTextType(flav)) {
 521             retval = new LinkedHashSet<>(0);
 522 
 523             // For text/* flavors, flavor-to-native mappings specified in


 549             retval = getTextTypeToNative().get(flav.mimeType.getBaseType());
 550 
 551             if (retval == null || retval.isEmpty()) {
 552                 retval = flavorToNativeLookup(flav, true);
 553             } else {
 554                 // In this branch it is guaranteed that natives explicitly
 555                 // listed for flav's MIME type were added with
 556                 // addUnencodedNativeForFlavor(), so they have lower priority.
 557                 retval.addAll(flavorToNativeLookup(flav, false));
 558             }
 559         } else {
 560             retval = flavorToNativeLookup(flav, true);
 561         }
 562 
 563         nativesForFlavorCache.put(flav, retval);
 564         // Create a copy, because client code can modify the returned list.
 565         return new ArrayList<>(retval);
 566     }
 567 
 568     /**
 569      * Returns a <code>List</code> of <code>DataFlavor</code>s to which the
 570      * specified <code>String</code> native can be translated by the data
 571      * transfer subsystem. The <code>List</code> will be sorted from best
 572      * <code>DataFlavor</code> to worst. That is, the first
 573      * <code>DataFlavor</code> will best reflect data in the specified
 574      * native to a Java application.
 575      * <p>
 576      * If the specified native is previously unknown to the data transfer
 577      * subsystem, and that native has been properly encoded, then invoking this
 578      * method will establish a mapping in both directions between the specified
 579      * native and a <code>DataFlavor</code> whose MIME type is a decoded
 580      * version of the native.
 581      * <p>
 582      * If the specified native is not a properly encoded native and the
 583      * mappings for this native have not been altered with
 584      * <code>setFlavorsForNative</code>, then the contents of the
 585      * <code>List</code> is platform dependent, but <code>null</code>
 586      * cannot be returned.
 587      *
 588      * @param nat the native whose corresponding <code>DataFlavor</code>s
 589      *        should be returned. If <code>null</code> is specified, all
 590      *        <code>DataFlavor</code>s currently known to the data transfer
 591      *        subsystem are returned in a non-deterministic order.
 592      * @return a <code>java.util.List</code> of <code>DataFlavor</code>
 593      *         objects into which platform-specific data in the specified,
 594      *         platform-specific native can be translated
 595      *
 596      * @see #encodeJavaMIMEType
 597      * @since 1.4
 598      */
 599     @Override
 600     public synchronized List<DataFlavor> getFlavorsForNative(String nat) {
 601         LinkedHashSet<DataFlavor> returnValue = flavorsForNativeCache.check(nat);
 602         if (returnValue != null) {
 603             return new ArrayList<>(returnValue);
 604         } else {
 605             returnValue = new LinkedHashSet<>();
 606         }
 607 
 608         if (nat == null) {
 609             for (String n : getNativesForFlavor(null)) {
 610                 returnValue.addAll(getFlavorsForNative(n));
 611             }
 612         } else {
 613             final LinkedHashSet<DataFlavor> flavors = nativeToFlavorLookup(nat);
 614             if (disabledMappingGenerationKeys.contains(nat)) {
 615                 return new ArrayList<>(flavors);


 724     private static final String [] htmlDocumentTypes =
 725             new String [] {"all", "selection", "fragment"};
 726 
 727     private static LinkedHashSet<String> handleHtmlMimeTypes(String baseType,
 728                                                              String mimeType) {
 729 
 730         LinkedHashSet<String> returnValues = new LinkedHashSet<>();
 731 
 732         if (HTML_TEXT_BASE_TYPE.equals(baseType)) {
 733             for (String documentType : htmlDocumentTypes) {
 734                 returnValues.add(mimeType + ";document=" + documentType);
 735             }
 736         } else {
 737             returnValues.add(mimeType);
 738         }
 739 
 740         return returnValues;
 741     }
 742 
 743     /**
 744      * Returns a <code>Map</code> of the specified <code>DataFlavor</code>s to
 745      * their most preferred <code>String</code> native. Each native value will
 746      * be the same as the first native in the List returned by
 747      * <code>getNativesForFlavor</code> for the specified flavor.
 748      * <p>
 749      * If a specified <code>DataFlavor</code> is previously unknown to the
 750      * data transfer subsystem, then invoking this method will establish a
 751      * mapping in both directions between the specified <code>DataFlavor</code>
 752      * and an encoded version of its MIME type as its native.
 753      *
 754      * @param flavors an array of <code>DataFlavor</code>s which will be the
 755      *        key set of the returned <code>Map</code>. If <code>null</code> is
 756      *        specified, a mapping of all <code>DataFlavor</code>s known to the
 757      *        data transfer subsystem to their most preferred
 758      *        <code>String</code> natives will be returned.
 759      * @return a <code>java.util.Map</code> of <code>DataFlavor</code>s to
 760      *         <code>String</code> natives
 761      *
 762      * @see #getNativesForFlavor
 763      * @see #encodeDataFlavor
 764      */
 765     @Override
 766     public synchronized Map<DataFlavor,String> getNativesForFlavors(DataFlavor[] flavors)
 767     {
 768         // Use getNativesForFlavor to generate extra natives for text flavors
 769         // and stringFlavor
 770 
 771         if (flavors == null) {
 772             List<DataFlavor> flavor_list = getFlavorsForNative(null);
 773             flavors = new DataFlavor[flavor_list.size()];
 774             flavor_list.toArray(flavors);
 775         }
 776 
 777         Map<DataFlavor, String> retval = new HashMap<>(flavors.length, 1.0f);
 778         for (DataFlavor flavor : flavors) {
 779             List<String> natives = getNativesForFlavor(flavor);
 780             String nat = (natives.isEmpty()) ? null : natives.get(0);
 781             retval.put(flavor, nat);
 782         }
 783 
 784         return retval;
 785     }
 786 
 787     /**
 788      * Returns a <code>Map</code> of the specified <code>String</code> natives
 789      * to their most preferred <code>DataFlavor</code>. Each
 790      * <code>DataFlavor</code> value will be the same as the first
 791      * <code>DataFlavor</code> in the List returned by
 792      * <code>getFlavorsForNative</code> for the specified native.
 793      * <p>
 794      * If a specified native is previously unknown to the data transfer
 795      * subsystem, and that native has been properly encoded, then invoking this
 796      * method will establish a mapping in both directions between the specified
 797      * native and a <code>DataFlavor</code> whose MIME type is a decoded
 798      * version of the native.
 799      *
 800      * @param natives an array of <code>String</code>s which will be the
 801      *        key set of the returned <code>Map</code>. If <code>null</code> is
 802      *        specified, a mapping of all supported <code>String</code> natives
 803      *        to their most preferred <code>DataFlavor</code>s will be
 804      *        returned.
 805      * @return a <code>java.util.Map</code> of <code>String</code> natives to
 806      *         <code>DataFlavor</code>s
 807      *






 808      * @see #getFlavorsForNative
 809      * @see #encodeJavaMIMEType
 810      */
 811     @Override
 812     public synchronized Map<String,DataFlavor> getFlavorsForNatives(String[] natives)
 813     {
 814         // Use getFlavorsForNative to generate extra flavors for text natives
 815         if (natives == null) {
 816             List<String> nativesList = getNativesForFlavor(null);
 817             natives = new String[nativesList.size()];
 818             nativesList.toArray(natives);
 819         }
 820 
 821         Map<String, DataFlavor> retval = new HashMap<>(natives.length, 1.0f);
 822         for (String aNative : natives) {
 823             List<DataFlavor> flavors = getFlavorsForNative(aNative);
 824             DataFlavor flav = (flavors.isEmpty())? null : flavors.get(0);
 825             retval.put(aNative, flav);
 826         }
 827         return retval;
 828     }
 829 
 830     /**
 831      * Adds a mapping from the specified <code>DataFlavor</code> (and all
 832      * <code>DataFlavor</code>s equal to the specified <code>DataFlavor</code>)
 833      * to the specified <code>String</code> native.
 834      * Unlike <code>getNativesForFlavor</code>, the mapping will only be
 835      * established in one direction, and the native will not be encoded. To
 836      * establish a two-way mapping, call
 837      * <code>addFlavorForUnencodedNative</code> as well. The new mapping will
 838      * be of lower priority than any existing mapping.
 839      * This method has no effect if a mapping from the specified or equal
 840      * <code>DataFlavor</code> to the specified <code>String</code> native
 841      * already exists.
 842      *
 843      * @param flav the <code>DataFlavor</code> key for the mapping
 844      * @param nat the <code>String</code> native value for the mapping
 845      * @throws NullPointerException if flav or nat is <code>null</code>
 846      *
 847      * @see #addFlavorForUnencodedNative
 848      * @since 1.4
 849      */
 850     public synchronized void addUnencodedNativeForFlavor(DataFlavor flav,
 851                                                          String nat) {
 852         Objects.requireNonNull(nat, "Null native not permitted");
 853         Objects.requireNonNull(flav, "Null flavor not permitted");
 854 
 855         LinkedHashSet<String> natives = getFlavorToNative().get(flav);
 856         if (natives == null) {
 857             natives = new LinkedHashSet<>(1);
 858             getFlavorToNative().put(flav, natives);
 859         }
 860         natives.add(nat);
 861         nativesForFlavorCache.remove(flav);
 862     }
 863 
 864     /**
 865      * Discards the current mappings for the specified <code>DataFlavor</code>
 866      * and all <code>DataFlavor</code>s equal to the specified
 867      * <code>DataFlavor</code>, and creates new mappings to the
 868      * specified <code>String</code> natives.
 869      * Unlike <code>getNativesForFlavor</code>, the mappings will only be
 870      * established in one direction, and the natives will not be encoded. To
 871      * establish two-way mappings, call <code>setFlavorsForNative</code>
 872      * as well. The first native in the array will represent the highest
 873      * priority mapping. Subsequent natives will represent mappings of
 874      * decreasing priority.
 875      * <p>
 876      * If the array contains several elements that reference equal
 877      * <code>String</code> natives, this method will establish new mappings
 878      * for the first of those elements and ignore the rest of them.
 879      * <p>
 880      * It is recommended that client code not reset mappings established by the
 881      * data transfer subsystem. This method should only be used for
 882      * application-level mappings.
 883      *
 884      * @param flav the <code>DataFlavor</code> key for the mappings
 885      * @param natives the <code>String</code> native values for the mappings
 886      * @throws NullPointerException if flav or natives is <code>null</code>
 887      *         or if natives contains <code>null</code> elements
 888      *
 889      * @see #setFlavorsForNative
 890      * @since 1.4
 891      */
 892     public synchronized void setNativesForFlavor(DataFlavor flav,
 893                                                  String[] natives) {
 894         Objects.requireNonNull(natives, "Null natives not permitted");
 895         Objects.requireNonNull(flav, "Null flavors not permitted");
 896 
 897         getFlavorToNative().remove(flav);
 898         for (String aNative : natives) {
 899             addUnencodedNativeForFlavor(flav, aNative);
 900         }
 901         disabledMappingGenerationKeys.add(flav);
 902         nativesForFlavorCache.remove(flav);
 903     }
 904 
 905     /**
 906      * Adds a mapping from a single <code>String</code> native to a single
 907      * <code>DataFlavor</code>. Unlike <code>getFlavorsForNative</code>, the
 908      * mapping will only be established in one direction, and the native will
 909      * not be encoded. To establish a two-way mapping, call
 910      * <code>addUnencodedNativeForFlavor</code> as well. The new mapping will
 911      * be of lower priority than any existing mapping.
 912      * This method has no effect if a mapping from the specified
 913      * <code>String</code> native to the specified or equal
 914      * <code>DataFlavor</code> already exists.
 915      *
 916      * @param nat the <code>String</code> native key for the mapping
 917      * @param flav the <code>DataFlavor</code> value for the mapping
 918      * @throws NullPointerException if nat or flav is <code>null</code>
 919      *




 920      * @see #addUnencodedNativeForFlavor
 921      * @since 1.4
 922      */
 923     public synchronized void addFlavorForUnencodedNative(String nat,
 924                                                          DataFlavor flav) {
 925         Objects.requireNonNull(nat, "Null native not permitted");
 926         Objects.requireNonNull(flav, "Null flavor not permitted");
 927 
 928         LinkedHashSet<DataFlavor> flavors = getNativeToFlavor().get(nat);
 929         if (flavors == null) {
 930             flavors = new LinkedHashSet<>(1);
 931             getNativeToFlavor().put(nat, flavors);
 932         }
 933         flavors.add(flav);
 934         flavorsForNativeCache.remove(nat);
 935     }
 936 
 937     /**
 938      * Discards the current mappings for the specified <code>String</code>
 939      * native, and creates new mappings to the specified
 940      * <code>DataFlavor</code>s. Unlike <code>getFlavorsForNative</code>, the
 941      * mappings will only be established in one direction, and the natives need
 942      * not be encoded. To establish two-way mappings, call
 943      * <code>setNativesForFlavor</code> as well. The first
 944      * <code>DataFlavor</code> in the array will represent the highest priority
 945      * mapping. Subsequent <code>DataFlavor</code>s will represent mappings of
 946      * decreasing priority.
 947      * <p>
 948      * If the array contains several elements that reference equal
 949      * <code>DataFlavor</code>s, this method will establish new mappings
 950      * for the first of those elements and ignore the rest of them.
 951      * <p>
 952      * It is recommended that client code not reset mappings established by the
 953      * data transfer subsystem. This method should only be used for
 954      * application-level mappings.
 955      *
 956      * @param nat the <code>String</code> native key for the mappings
 957      * @param flavors the <code>DataFlavor</code> values for the mappings
 958      * @throws NullPointerException if nat or flavors is <code>null</code>
 959      *         or if flavors contains <code>null</code> elements
 960      *
 961      * @see #setNativesForFlavor
 962      * @since 1.4
 963      */
 964     public synchronized void setFlavorsForNative(String nat,
 965                                                  DataFlavor[] flavors) {
 966         Objects.requireNonNull(nat, "Null native not permitted");
 967         Objects.requireNonNull(flavors, "Null flavors not permitted");
 968 
 969         getNativeToFlavor().remove(nat);
 970         for (DataFlavor flavor : flavors) {
 971             addFlavorForUnencodedNative(nat, flavor);
 972         }
 973         disabledMappingGenerationKeys.add(nat);
 974         flavorsForNativeCache.remove(nat);
 975     }
 976 
 977     /**
 978      * Encodes a MIME type for use as a <code>String</code> native. The format
 979      * of an encoded representation of a MIME type is implementation-dependent.
 980      * The only restrictions are:
 981      * <ul>
 982      * <li>The encoded representation is <code>null</code> if and only if the
 983      * MIME type <code>String</code> is <code>null</code>.</li>
 984      * <li>The encoded representations for two non-<code>null</code> MIME type
 985      * <code>String</code>s are equal if and only if these <code>String</code>s
 986      * are equal according to <code>String.equals(Object)</code>.</li>
 987      * </ul>
 988      * <p>
 989      * The reference implementation of this method returns the specified MIME
 990      * type <code>String</code> prefixed with <code>JAVA_DATAFLAVOR:</code>.
 991      *
 992      * @param mimeType the MIME type to encode
 993      * @return the encoded <code>String</code>, or <code>null</code> if
 994      *         mimeType is <code>null</code>
 995      */
 996     public static String encodeJavaMIMEType(String mimeType) {
 997         return (mimeType != null)
 998             ? JavaMIME + mimeType
 999             : null;
1000     }
1001 
1002     /**
1003      * Encodes a <code>DataFlavor</code> for use as a <code>String</code>
1004      * native. The format of an encoded <code>DataFlavor</code> is
1005      * implementation-dependent. The only restrictions are:
1006      * <ul>
1007      * <li>The encoded representation is <code>null</code> if and only if the
1008      * specified <code>DataFlavor</code> is <code>null</code> or its MIME type
1009      * <code>String</code> is <code>null</code>.</li>
1010      * <li>The encoded representations for two non-<code>null</code>
1011      * <code>DataFlavor</code>s with non-<code>null</code> MIME type
1012      * <code>String</code>s are equal if and only if the MIME type
1013      * <code>String</code>s of these <code>DataFlavor</code>s are equal
1014      * according to <code>String.equals(Object)</code>.</li>
1015      * </ul>
1016      * <p>
1017      * The reference implementation of this method returns the MIME type
1018      * <code>String</code> of the specified <code>DataFlavor</code> prefixed
1019      * with <code>JAVA_DATAFLAVOR:</code>.
1020      *
1021      * @param flav the <code>DataFlavor</code> to encode
1022      * @return the encoded <code>String</code>, or <code>null</code> if
1023      *         flav is <code>null</code> or has a <code>null</code> MIME type
1024      */
1025     public static String encodeDataFlavor(DataFlavor flav) {
1026         return (flav != null)
1027             ? SystemFlavorMap.encodeJavaMIMEType(flav.getMimeType())
1028             : null;
1029     }
1030 
1031     /**
1032      * Returns whether the specified <code>String</code> is an encoded Java
1033      * MIME type.
1034      *
1035      * @param str the <code>String</code> to test
1036      * @return <code>true</code> if the <code>String</code> is encoded;
1037      *         <code>false</code> otherwise
1038      */
1039     public static boolean isJavaMIMEType(String str) {
1040         return (str != null && str.startsWith(JavaMIME, 0));
1041     }
1042 
1043     /**
1044      * Decodes a <code>String</code> native for use as a Java MIME type.
1045      *
1046      * @param nat the <code>String</code> to decode
1047      * @return the decoded Java MIME type, or <code>null</code> if nat is not
1048      *         an encoded <code>String</code> native
1049      */
1050     public static String decodeJavaMIMEType(String nat) {
1051         return (isJavaMIMEType(nat))
1052             ? nat.substring(JavaMIME.length(), nat.length()).trim()
1053             : null;
1054     }
1055 
1056     /**
1057      * Decodes a <code>String</code> native for use as a
1058      * <code>DataFlavor</code>.
1059      *
1060      * @param nat the <code>String</code> to decode
1061      * @return the decoded <code>DataFlavor</code>, or <code>null</code> if
1062      *         nat is not an encoded <code>String</code> native
1063      * @throws ClassNotFoundException if the class of the data flavor
1064      * is not loaded
1065      */
1066     public static DataFlavor decodeDataFlavor(String nat)
1067         throws ClassNotFoundException
1068     {
1069         String retval_str = SystemFlavorMap.decodeJavaMIMEType(nat);
1070         return (retval_str != null)
1071             ? new DataFlavor(retval_str)
1072             : null;
1073     }
1074 
1075     private static final class SoftCache<K, V> {
1076         Map<K, SoftReference<LinkedHashSet<V>>> cache;
1077 
1078         public void put(K key, LinkedHashSet<V> value) {
1079             if (cache == null) {
1080                 cache = new HashMap<>(1);
1081             }
1082             cache.put(key, new SoftReference<>(value));
1083         }
1084 
   1 /*
   2  * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.awt.datatransfer;
  27 



  28 import java.io.BufferedReader;
  29 import java.io.IOException;
  30 import java.io.InputStream;
  31 import java.io.InputStreamReader;
  32 import java.lang.ref.SoftReference;
  33 import java.security.AccessController;
  34 import java.security.PrivilegedAction;
  35 import java.util.ArrayList;
  36 import java.util.Collections;
  37 import java.util.HashMap;
  38 import java.util.HashSet;
  39 import java.util.LinkedHashSet;
  40 import java.util.List;
  41 import java.util.Map;
  42 import java.util.Objects;
  43 import java.util.Set;
  44 
  45 import sun.datatransfer.DataFlavorUtil;
  46 import sun.datatransfer.DesktopDatatransferService;
  47 
  48 /**
  49  * The SystemFlavorMap is a configurable map between "natives" (Strings), which
  50  * correspond to platform-specific data formats, and "flavors" (DataFlavors),
  51  * which correspond to platform-independent MIME types. This mapping is used by
  52  * the data transfer subsystem to transfer data between Java and native
  53  * applications, and between Java applications in separate VMs.
  54  *
  55  * @since 1.2
  56  */
  57 public final class SystemFlavorMap implements FlavorMap, FlavorTable {
  58 
  59     /**
  60      * Constant prefix used to tag Java types converted to native platform type.

  61      */
  62     private static String JavaMIME = "JAVA_DATAFLAVOR:";
  63 
  64     private static final Object FLAVOR_MAP_KEY = new Object();
  65 
  66     /**
  67      * The list of valid, decoded text flavor representation classes, in order
  68      * from best to worst.
  69      */
  70     private static final String[] UNICODE_TEXT_CLASSES = {
  71         "java.io.Reader", "java.lang.String", "java.nio.CharBuffer", "\"[C\""
  72     };
  73 
  74     /**
  75      * The list of valid, encoded text flavor representation classes, in order
  76      * from best to worst.
  77      */
  78     private static final String[] ENCODED_TEXT_CLASSES = {
  79         "java.io.InputStream", "java.nio.ByteBuffer", "\"[B\""
  80     };
  81 
  82     /**
  83      * A String representing text/plain MIME type.
  84      */
  85     private static final String TEXT_PLAIN_BASE_TYPE = "text/plain";
  86 
  87     /**
  88      * A String representing text/html MIME type.
  89      */
  90     private static final String HTML_TEXT_BASE_TYPE = "text/html";
  91 
  92     /**
  93      * Maps native Strings to Lists of DataFlavors (or base type Strings for
  94      * text DataFlavors).
  95      * <p>
  96      * Do not use the field directly, use {@link #getNativeToFlavor} instead.
  97      */
  98     private final Map<String, LinkedHashSet<DataFlavor>> nativeToFlavor = new HashMap<>();
  99 
 100     /**
 101      * Accessor to nativeToFlavor map. Since we use lazy initialization we must
 102      * use this accessor instead of direct access to the field which may not be
 103      * initialized yet. This method will initialize the field if needed.
 104      *
 105      * @return nativeToFlavor
 106      */
 107     private Map<String, LinkedHashSet<DataFlavor>> getNativeToFlavor() {
 108         if (!isMapInitialized) {
 109             initSystemFlavorMap();
 110         }
 111         return nativeToFlavor;
 112     }
 113 
 114     /**
 115      * Maps DataFlavors (or base type Strings for text DataFlavors) to Lists of
 116      * native Strings.
 117      * <p>
 118      * Do not use the field directly, use {@link #getFlavorToNative} instead.
 119      */
 120     private final Map<DataFlavor, LinkedHashSet<String>> flavorToNative = new HashMap<>();
 121 
 122     /**
 123      * Accessor to flavorToNative map. Since we use lazy initialization we must
 124      * use this accessor instead of direct access to the field which may not be
 125      * initialized yet. This method will initialize the field if needed.
 126      *
 127      * @return flavorToNative
 128      */
 129     private synchronized Map<DataFlavor, LinkedHashSet<String>> getFlavorToNative() {
 130         if (!isMapInitialized) {
 131             initSystemFlavorMap();
 132         }
 133         return flavorToNative;
 134     }
 135 
 136     /**
 137      * Maps a text DataFlavor primary mime-type to the native. Used only to
 138      * store standard mappings registered in the {@code flavormap.properties}.
 139      * <p>
 140      * Do not use this field directly, use {@link #getTextTypeToNative} instead.
 141      */
 142     private Map<String, LinkedHashSet<String>> textTypeToNative = new HashMap<>();
 143 
 144     /**
 145      * Shows if the object has been initialized.
 146      */
 147     private boolean isMapInitialized = false;
 148 
 149     /**
 150      * An accessor to textTypeToNative map. Since we use lazy initialization we
 151      * must use this accessor instead of direct access to the field which may
 152      * not be initialized yet. This method will initialize the field if needed.
 153      *
 154      * @return textTypeToNative
 155      */
 156     private synchronized Map<String, LinkedHashSet<String>> getTextTypeToNative() {
 157         if (!isMapInitialized) {
 158             initSystemFlavorMap();
 159             // From this point the map should not be modified
 160             textTypeToNative = Collections.unmodifiableMap(textTypeToNative);
 161         }
 162         return textTypeToNative;
 163     }
 164 
 165     /**
 166      * Caches the result of getNativesForFlavor(). Maps DataFlavors to
 167      * SoftReferences which reference LinkedHashSet of String natives.
 168      */
 169     private final SoftCache<DataFlavor, String> nativesForFlavorCache = new SoftCache<>();
 170 
 171     /**
 172      * Caches the result getFlavorsForNative(). Maps String natives to
 173      * SoftReferences which reference LinkedHashSet of DataFlavors.
 174      */
 175     private final SoftCache<String, DataFlavor> flavorsForNativeCache = new SoftCache<>();
 176 
 177     /**
 178      * Dynamic mapping generation used for text mappings should not be applied
 179      * to the DataFlavors and String natives for which the mappings have been
 180      * explicitly specified with {@link #setFlavorsForNative} or
 181      * {@link #setNativesForFlavor}. This keeps all such keys.
 182      */
 183     private Set<Object> disabledMappingGenerationKeys = new HashSet<>();
 184 
 185     /**
 186      * Returns the default FlavorMap for this thread's ClassLoader.
 187      *
 188      * @return the default FlavorMap for this thread's ClassLoader
 189      */
 190     public static FlavorMap getDefaultFlavorMap() {
 191         return DataFlavorUtil.getDesktopService().getFlavorMap(SystemFlavorMap::new);
 192     }
 193 
 194     private SystemFlavorMap() {
 195     }
 196 
 197     /**
 198      * Initializes a SystemFlavorMap by reading {@code flavormap.properties}.
 199      * For thread-safety must be called under lock on {@code this}.
 200      */
 201     private void initSystemFlavorMap() {
 202         if (isMapInitialized) {
 203             return;
 204         }
 205         isMapInitialized = true;
 206 
 207         InputStream is = AccessController.doPrivileged(
 208             (PrivilegedAction<InputStream>) () -> {
 209                 return SystemFlavorMap.class.getResourceAsStream(
 210                         "/sun/datatransfer/resources/flavormap.properties");
 211             });
 212         if (is == null) {
 213             throw new InternalError("Default flavor mapping not found");
 214         }
 215 
 216         try (InputStreamReader isr = new InputStreamReader(is);
 217              BufferedReader reader = new BufferedReader(isr)) {
 218             String line;
 219             while ((line = reader.readLine()) != null) {


 351     }
 352 
 353     /**
 354      * Stores the listed object under the specified hash key in map. Unlike a
 355      * standard map, the listed object will not replace any object already at
 356      * the appropriate Map location, but rather will be appended to a List
 357      * stored in that location.
 358      */
 359     private <H, L> void store(H hashed, L listed, Map<H, LinkedHashSet<L>> map) {
 360         LinkedHashSet<L> list = map.get(hashed);
 361         if (list == null) {
 362             list = new LinkedHashSet<>(1);
 363             map.put(hashed, list);
 364         }
 365         if (!list.contains(listed)) {
 366             list.add(listed);
 367         }
 368     }
 369 
 370     /**
 371      * Semantically equivalent to 'nativeToFlavor.get(nat)'. This method handles
 372      * the case where 'nat' is not found in 'nativeToFlavor'. In that case, a
 373      * new DataFlavor is synthesized, stored, and returned, if and only if the
 374      * specified native is encoded as a Java MIME type.
 375      */
 376     private LinkedHashSet<DataFlavor> nativeToFlavorLookup(String nat) {
 377         LinkedHashSet<DataFlavor> flavors = getNativeToFlavor().get(nat);
 378 
 379         if (nat != null && !disabledMappingGenerationKeys.contains(nat)) {
 380             DesktopDatatransferService desktopService = DataFlavorUtil.getDesktopService();
 381             if (desktopService.isDesktopPresent()) {
 382                 LinkedHashSet<DataFlavor> platformFlavors =
 383                         desktopService.getPlatformMappingsForNative(nat);
 384                 if (!platformFlavors.isEmpty()) {
 385                     if (flavors != null) {
 386                         // Prepending the platform-specific mappings ensures
 387                         // that the flavors added with
 388                         // addFlavorForUnencodedNative() are at the end of
 389                         // list.
 390                         platformFlavors.addAll(flavors);
 391                     }
 392                     flavors = platformFlavors;
 393                 }
 394             }


 465                 natives.add(encoded);
 466 
 467                 LinkedHashSet<DataFlavor> flavors = getNativeToFlavor().get(encoded);
 468                 if (flavors == null) {
 469                     flavors = new LinkedHashSet<>(1);
 470                     getNativeToFlavor().put(encoded, flavors);
 471                 }
 472                 flavors.add(flav);
 473 
 474                 nativesForFlavorCache.remove(flav);
 475                 flavorsForNativeCache.remove(encoded);
 476             } else {
 477                 natives = new LinkedHashSet<>(0);
 478             }
 479         }
 480 
 481         return new LinkedHashSet<>(natives);
 482     }
 483 
 484     /**
 485      * Returns a {@code List} of {@code String} natives to which the specified
 486      * {@code DataFlavor} can be translated by the data transfer subsystem. The
 487      * {@code List} will be sorted from best native to worst. That is, the first
 488      * native will best reflect data in the specified flavor to the underlying
 489      * native platform.
 490      * <p>
 491      * If the specified {@code DataFlavor} is previously unknown to the data
 492      * transfer subsystem and the data transfer subsystem is unable to translate
 493      * this {@code DataFlavor} to any existing native, then invoking this method
 494      * will establish a mapping in both directions between the specified
 495      * {@code DataFlavor} and an encoded version of its MIME type as its native.
 496      *
 497      * @param  flav the {@code DataFlavor} whose corresponding natives should be
 498      *         returned. If {@code null} is specified, all natives currently
 499      *         known to the data transfer subsystem are returned in a
 500      *         non-deterministic order.
 501      * @return a {@code java.util.List} of {@code java.lang.String} objects
 502      *         which are platform-specific representations of platform-specific
 503      *         data formats


 504      * @see #encodeDataFlavor
 505      * @since 1.4
 506      */
 507     @Override
 508     public synchronized List<String> getNativesForFlavor(DataFlavor flav) {
 509         LinkedHashSet<String> retval = nativesForFlavorCache.check(flav);
 510         if (retval != null) {
 511             return new ArrayList<>(retval);
 512         }
 513 
 514         if (flav == null) {
 515             retval = new LinkedHashSet<>(getNativeToFlavor().keySet());
 516         } else if (disabledMappingGenerationKeys.contains(flav)) {
 517             // In this case we shouldn't synthesize a native for this flavor,
 518             // since its mappings were explicitly specified.
 519             retval = flavorToNativeLookup(flav, false);
 520         } else if (DataFlavorUtil.isFlavorCharsetTextType(flav)) {
 521             retval = new LinkedHashSet<>(0);
 522 
 523             // For text/* flavors, flavor-to-native mappings specified in


 549             retval = getTextTypeToNative().get(flav.mimeType.getBaseType());
 550 
 551             if (retval == null || retval.isEmpty()) {
 552                 retval = flavorToNativeLookup(flav, true);
 553             } else {
 554                 // In this branch it is guaranteed that natives explicitly
 555                 // listed for flav's MIME type were added with
 556                 // addUnencodedNativeForFlavor(), so they have lower priority.
 557                 retval.addAll(flavorToNativeLookup(flav, false));
 558             }
 559         } else {
 560             retval = flavorToNativeLookup(flav, true);
 561         }
 562 
 563         nativesForFlavorCache.put(flav, retval);
 564         // Create a copy, because client code can modify the returned list.
 565         return new ArrayList<>(retval);
 566     }
 567 
 568     /**
 569      * Returns a {@code List} of {@code DataFlavor}s to which the specified
 570      * {@code String} native can be translated by the data transfer subsystem.
 571      * The {@code List} will be sorted from best {@code DataFlavor} to worst.
 572      * That is, the first {@code DataFlavor} will best reflect data in the
 573      * specified native to a Java application.

 574      * <p>
 575      * If the specified native is previously unknown to the data transfer
 576      * subsystem, and that native has been properly encoded, then invoking this
 577      * method will establish a mapping in both directions between the specified
 578      * native and a {@code DataFlavor} whose MIME type is a decoded version of
 579      * the native.
 580      * <p>
 581      * If the specified native is not a properly encoded native and the mappings
 582      * for this native have not been altered with {@code setFlavorsForNative},
 583      * then the contents of the {@code List} is platform dependent, but
 584      * {@code null} cannot be returned.
 585      *
 586      * @param  nat the native whose corresponding {@code DataFlavor}s should be
 587      *         returned. If {@code null} is specified, all {@code DataFlavor}s
 588      *         currently known to the data transfer subsystem are returned in a
 589      *         non-deterministic order.
 590      * @return a {@code java.util.List} of {@code DataFlavor} objects into which
 591      *         platform-specific data in the specified, platform-specific native
 592      *         can be translated


 593      * @see #encodeJavaMIMEType
 594      * @since 1.4
 595      */
 596     @Override
 597     public synchronized List<DataFlavor> getFlavorsForNative(String nat) {
 598         LinkedHashSet<DataFlavor> returnValue = flavorsForNativeCache.check(nat);
 599         if (returnValue != null) {
 600             return new ArrayList<>(returnValue);
 601         } else {
 602             returnValue = new LinkedHashSet<>();
 603         }
 604 
 605         if (nat == null) {
 606             for (String n : getNativesForFlavor(null)) {
 607                 returnValue.addAll(getFlavorsForNative(n));
 608             }
 609         } else {
 610             final LinkedHashSet<DataFlavor> flavors = nativeToFlavorLookup(nat);
 611             if (disabledMappingGenerationKeys.contains(nat)) {
 612                 return new ArrayList<>(flavors);


 721     private static final String [] htmlDocumentTypes =
 722             new String [] {"all", "selection", "fragment"};
 723 
 724     private static LinkedHashSet<String> handleHtmlMimeTypes(String baseType,
 725                                                              String mimeType) {
 726 
 727         LinkedHashSet<String> returnValues = new LinkedHashSet<>();
 728 
 729         if (HTML_TEXT_BASE_TYPE.equals(baseType)) {
 730             for (String documentType : htmlDocumentTypes) {
 731                 returnValues.add(mimeType + ";document=" + documentType);
 732             }
 733         } else {
 734             returnValues.add(mimeType);
 735         }
 736 
 737         return returnValues;
 738     }
 739 
 740     /**
 741      * Returns a {@code Map} of the specified {@code DataFlavor}s to their most
 742      * preferred {@code String} native. Each native value will be the same as
 743      * the first native in the List returned by {@code getNativesForFlavor} for
 744      * the specified flavor.
 745      * <p>
 746      * If a specified {@code DataFlavor} is previously unknown to the data
 747      * transfer subsystem, then invoking this method will establish a mapping in
 748      * both directions between the specified {@code DataFlavor} and an encoded
 749      * version of its MIME type as its native.
 750      *
 751      * @param  flavors an array of {@code DataFlavor}s which will be the key set
 752      *         of the returned {@code Map}. If {@code null} is specified, a
 753      *         mapping of all {@code DataFlavor}s known to the data transfer
 754      *         subsystem to their most preferred {@code String} natives will be
 755      *         returned.
 756      * @return a {@code java.util.Map} of {@code DataFlavor}s to {@code String}
 757      *         natives

 758      * @see #getNativesForFlavor
 759      * @see #encodeDataFlavor
 760      */
 761     @Override
 762     public synchronized Map<DataFlavor,String> getNativesForFlavors(DataFlavor[] flavors)
 763     {
 764         // Use getNativesForFlavor to generate extra natives for text flavors
 765         // and stringFlavor
 766 
 767         if (flavors == null) {
 768             List<DataFlavor> flavor_list = getFlavorsForNative(null);
 769             flavors = new DataFlavor[flavor_list.size()];
 770             flavor_list.toArray(flavors);
 771         }
 772 
 773         Map<DataFlavor, String> retval = new HashMap<>(flavors.length, 1.0f);
 774         for (DataFlavor flavor : flavors) {
 775             List<String> natives = getNativesForFlavor(flavor);
 776             String nat = (natives.isEmpty()) ? null : natives.get(0);
 777             retval.put(flavor, nat);
 778         }
 779 
 780         return retval;
 781     }
 782 
 783     /**
 784      * Returns a {@code Map} of the specified {@code String} natives to their
 785      * most preferred {@code DataFlavor}. Each {@code DataFlavor} value will be
 786      * the same as the first {@code DataFlavor} in the List returned by
 787      * {@code getFlavorsForNative} for the specified native.

 788      * <p>
 789      * If a specified native is previously unknown to the data transfer
 790      * subsystem, and that native has been properly encoded, then invoking this
 791      * method will establish a mapping in both directions between the specified
 792      * native and a {@code DataFlavor} whose MIME type is a decoded version of
 793      * the native.








 794      *
 795      * @param  natives an array of {@code String}s which will be the key set of
 796      *         the returned {@code Map}. If {@code null} is specified, a mapping
 797      *         of all supported {@code String} natives to their most preferred
 798      *         {@code DataFlavor}s will be returned.
 799      * @return a {@code java.util.Map} of {@code String} natives to
 800      *         {@code DataFlavor}s
 801      * @see #getFlavorsForNative
 802      * @see #encodeJavaMIMEType
 803      */
 804     @Override
 805     public synchronized Map<String,DataFlavor> getFlavorsForNatives(String[] natives)
 806     {
 807         // Use getFlavorsForNative to generate extra flavors for text natives
 808         if (natives == null) {
 809             List<String> nativesList = getNativesForFlavor(null);
 810             natives = new String[nativesList.size()];
 811             nativesList.toArray(natives);
 812         }
 813 
 814         Map<String, DataFlavor> retval = new HashMap<>(natives.length, 1.0f);
 815         for (String aNative : natives) {
 816             List<DataFlavor> flavors = getFlavorsForNative(aNative);
 817             DataFlavor flav = (flavors.isEmpty())? null : flavors.get(0);
 818             retval.put(aNative, flav);
 819         }
 820         return retval;
 821     }
 822 
 823     /**
 824      * Adds a mapping from the specified {@code DataFlavor} (and all
 825      * {@code DataFlavor}s equal to the specified {@code DataFlavor}) to the
 826      * specified {@code String} native. Unlike {@code getNativesForFlavor}, the
 827      * mapping will only be established in one direction, and the native will
 828      * not be encoded. To establish a two-way mapping, call
 829      * {@code addFlavorForUnencodedNative} as well. The new mapping will be of
 830      * lower priority than any existing mapping. This method has no effect if a
 831      * mapping from the specified or equal {@code DataFlavor} to the specified
 832      * {@code String} native already exists.
 833      *
 834      * @param  flav the {@code DataFlavor} key for the mapping
 835      * @param  nat the {@code String} native value for the mapping
 836      * @throws NullPointerException if flav or nat is {@code null}



 837      * @see #addFlavorForUnencodedNative
 838      * @since 1.4
 839      */
 840     public synchronized void addUnencodedNativeForFlavor(DataFlavor flav,
 841                                                          String nat) {
 842         Objects.requireNonNull(nat, "Null native not permitted");
 843         Objects.requireNonNull(flav, "Null flavor not permitted");
 844 
 845         LinkedHashSet<String> natives = getFlavorToNative().get(flav);
 846         if (natives == null) {
 847             natives = new LinkedHashSet<>(1);
 848             getFlavorToNative().put(flav, natives);
 849         }
 850         natives.add(nat);
 851         nativesForFlavorCache.remove(flav);
 852     }
 853 
 854     /**
 855      * Discards the current mappings for the specified {@code DataFlavor} and
 856      * all {@code DataFlavor}s equal to the specified {@code DataFlavor}, and
 857      * creates new mappings to the specified {@code String} natives. Unlike
 858      * {@code getNativesForFlavor}, the mappings will only be established in one
 859      * direction, and the natives will not be encoded. To establish two-way
 860      * mappings, call {@code setFlavorsForNative} as well. The first native in
 861      * the array will represent the highest priority mapping. Subsequent natives
 862      * will represent mappings of decreasing priority.


 863      * <p>
 864      * If the array contains several elements that reference equal
 865      * {@code String} natives, this method will establish new mappings for the
 866      * first of those elements and ignore the rest of them.
 867      * <p>
 868      * It is recommended that client code not reset mappings established by the
 869      * data transfer subsystem. This method should only be used for
 870      * application-level mappings.
 871      *
 872      * @param  flav the {@code DataFlavor} key for the mappings
 873      * @param  natives the {@code String} native values for the mappings
 874      * @throws NullPointerException if flav or natives is {@code null} or if
 875      *         natives contains {@code null} elements

 876      * @see #setFlavorsForNative
 877      * @since 1.4
 878      */
 879     public synchronized void setNativesForFlavor(DataFlavor flav,
 880                                                  String[] natives) {
 881         Objects.requireNonNull(natives, "Null natives not permitted");
 882         Objects.requireNonNull(flav, "Null flavors not permitted");
 883 
 884         getFlavorToNative().remove(flav);
 885         for (String aNative : natives) {
 886             addUnencodedNativeForFlavor(flav, aNative);
 887         }
 888         disabledMappingGenerationKeys.add(flav);
 889         nativesForFlavorCache.remove(flav);
 890     }
 891 
 892     /**
 893      * Adds a mapping from a single {@code String} native to a single
 894      * {@code DataFlavor}. Unlike {@code getFlavorsForNative}, the mapping will
 895      * only be established in one direction, and the native will not be encoded.
 896      * To establish a two-way mapping, call {@code addUnencodedNativeForFlavor}
 897      * as well. The new mapping will be of lower priority than any existing
 898      * mapping. This method has no effect if a mapping from the specified
 899      * {@code String} native to the specified or equal {@code DataFlavor}
 900      * already exists.





 901      *
 902      * @param  nat the {@code String} native key for the mapping
 903      * @param  flav the {@code DataFlavor} value for the mapping
 904      * @throws NullPointerException if {@code nat} or {@code flav} is
 905      *         {@code null}
 906      * @see #addUnencodedNativeForFlavor
 907      * @since 1.4
 908      */
 909     public synchronized void addFlavorForUnencodedNative(String nat,
 910                                                          DataFlavor flav) {
 911         Objects.requireNonNull(nat, "Null native not permitted");
 912         Objects.requireNonNull(flav, "Null flavor not permitted");
 913 
 914         LinkedHashSet<DataFlavor> flavors = getNativeToFlavor().get(nat);
 915         if (flavors == null) {
 916             flavors = new LinkedHashSet<>(1);
 917             getNativeToFlavor().put(nat, flavors);
 918         }
 919         flavors.add(flav);
 920         flavorsForNativeCache.remove(nat);
 921     }
 922 
 923     /**
 924      * Discards the current mappings for the specified {@code String} native,
 925      * and creates new mappings to the specified {@code DataFlavor}s. Unlike
 926      * {@code getFlavorsForNative}, the mappings will only be established in one
 927      * direction, and the natives need not be encoded. To establish two-way
 928      * mappings, call {@code setNativesForFlavor} as well. The first
 929      * {@code DataFlavor} in the array will represent the highest priority
 930      * mapping. Subsequent {@code DataFlavor}s will represent mappings of

 931      * decreasing priority.
 932      * <p>
 933      * If the array contains several elements that reference equal
 934      * {@code DataFlavor}s, this method will establish new mappings for the
 935      * first of those elements and ignore the rest of them.
 936      * <p>
 937      * It is recommended that client code not reset mappings established by the
 938      * data transfer subsystem. This method should only be used for
 939      * application-level mappings.
 940      *
 941      * @param  nat the {@code String} native key for the mappings
 942      * @param  flavors the {@code DataFlavor} values for the mappings
 943      * @throws NullPointerException if {@code nat} or {@code flavors} is
 944      *         {@code null} or if {@code flavors} contains {@code null} elements

 945      * @see #setNativesForFlavor
 946      * @since 1.4
 947      */
 948     public synchronized void setFlavorsForNative(String nat,
 949                                                  DataFlavor[] flavors) {
 950         Objects.requireNonNull(nat, "Null native not permitted");
 951         Objects.requireNonNull(flavors, "Null flavors not permitted");
 952 
 953         getNativeToFlavor().remove(nat);
 954         for (DataFlavor flavor : flavors) {
 955             addFlavorForUnencodedNative(nat, flavor);
 956         }
 957         disabledMappingGenerationKeys.add(nat);
 958         flavorsForNativeCache.remove(nat);
 959     }
 960 
 961     /**
 962      * Encodes a MIME type for use as a {@code String} native. The format of an
 963      * encoded representation of a MIME type is implementation-dependent. The
 964      * only restrictions are:
 965      * <ul>
 966      * <li>The encoded representation is {@code null} if and only if the MIME
 967      *     type {@code String} is {@code null}</li>
 968      * <li>The encoded representations for two non-{@code null} MIME type
 969      *     {@code String}s are equal if and only if these {@code String}s are
 970      *     equal according to {@code String.equals(Object)}</li>
 971      * </ul>

 972      * The reference implementation of this method returns the specified MIME
 973      * type {@code String} prefixed with {@code JAVA_DATAFLAVOR:}.
 974      *
 975      * @param  mimeType the MIME type to encode
 976      * @return the encoded {@code String}, or {@code null} if {@code mimeType}
 977      *         is {@code null}
 978      */
 979     public static String encodeJavaMIMEType(String mimeType) {
 980         return (mimeType != null)
 981             ? JavaMIME + mimeType
 982             : null;
 983     }
 984 
 985     /**
 986      * Encodes a {@code DataFlavor} for use as a {@code String} native. The
 987      * format of an encoded {@code DataFlavor} is implementation-dependent. The
 988      * only restrictions are:
 989      * <ul>
 990      * <li>The encoded representation is {@code null} if and only if the
 991      *     specified {@code DataFlavor} is {@code null} or its MIME type
 992      *     {@code String} is {@code null}</li>
 993      * <li>The encoded representations for two non-{@code null}
 994      *     {@code DataFlavor}s with non-{@code null} MIME type {@code String}s
 995      *     are equal if and only if the MIME type {@code String}s of these
 996      *     {@code DataFlavor}s are equal according to
 997      *     {@code String.equals(Object)}</li>
 998      * </ul>

 999      * The reference implementation of this method returns the MIME type
1000      * {@code String} of the specified {@code DataFlavor} prefixed with
1001      * {@code JAVA_DATAFLAVOR:}.
1002      *
1003      * @param  flav the {@code DataFlavor} to encode
1004      * @return the encoded {@code String}, or {@code null} if {@code flav} is
1005      *         {@code null} or has a {@code null} MIME type
1006      */
1007     public static String encodeDataFlavor(DataFlavor flav) {
1008         return (flav != null)
1009             ? SystemFlavorMap.encodeJavaMIMEType(flav.getMimeType())
1010             : null;
1011     }
1012 
1013     /**
1014      * Returns whether the specified {@code String} is an encoded Java MIME
1015      * type.
1016      *
1017      * @param  str the {@code String} to test
1018      * @return {@code true} if the {@code String} is encoded; {@code false}
1019      *         otherwise
1020      */
1021     public static boolean isJavaMIMEType(String str) {
1022         return (str != null && str.startsWith(JavaMIME, 0));
1023     }
1024 
1025     /**
1026      * Decodes a {@code String} native for use as a Java MIME type.
1027      *
1028      * @param  nat the {@code String} to decode
1029      * @return the decoded Java MIME type, or {@code null} if {@code nat} is not
1030      *         an encoded {@code String} native
1031      */
1032     public static String decodeJavaMIMEType(String nat) {
1033         return (isJavaMIMEType(nat))
1034             ? nat.substring(JavaMIME.length(), nat.length()).trim()
1035             : null;
1036     }
1037 
1038     /**
1039      * Decodes a {@code String} native for use as a {@code DataFlavor}.

1040      *
1041      * @param  nat the {@code String} to decode
1042      * @return the decoded {@code DataFlavor}, or {@code null} if {@code nat} is
1043      *         not an encoded {@code String} native
1044      * @throws ClassNotFoundException if the class of the data flavor is not
1045      *         loaded
1046      */
1047     public static DataFlavor decodeDataFlavor(String nat)
1048         throws ClassNotFoundException
1049     {
1050         String retval_str = SystemFlavorMap.decodeJavaMIMEType(nat);
1051         return (retval_str != null)
1052             ? new DataFlavor(retval_str)
1053             : null;
1054     }
1055 
1056     private static final class SoftCache<K, V> {
1057         Map<K, SoftReference<LinkedHashSet<V>>> cache;
1058 
1059         public void put(K key, LinkedHashSet<V> value) {
1060             if (cache == null) {
1061                 cache = new HashMap<>(1);
1062             }
1063             cache.put(key, new SoftReference<>(value));
1064         }
1065 
< prev index next >