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

Print this page




   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.awt.datatransfer;
  27 
  28 import java.awt.Toolkit;
  29 
  30 import java.io.BufferedInputStream;
  31 import java.io.InputStream;
  32 import java.lang.ref.SoftReference;
  33 
  34 import java.io.BufferedReader;
  35 import java.io.File;
  36 import java.io.InputStreamReader;
  37 import java.io.IOException;
  38 
  39 import java.net.URL;
  40 import java.net.MalformedURLException;
  41 
  42 import java.util.ArrayList;
  43 import java.util.Arrays;
  44 import java.util.Collections;
  45 import java.util.HashMap;
  46 import java.util.HashSet;
  47 import java.util.LinkedHashSet;
  48 import java.util.List;
  49 import java.util.Map;
  50 import java.util.Objects;
  51 import java.util.Properties;
  52 import java.util.Set;
  53 
  54 import sun.awt.AppContext;
  55 import sun.awt.datatransfer.DataTransferer;
  56 
  57 /**
  58  * The SystemFlavorMap is a configurable map between "natives" (Strings), which
  59  * correspond to platform-specific data formats, and "flavors" (DataFlavors),
  60  * which correspond to platform-independent MIME types. This mapping is used
  61  * by the data transfer subsystem to transfer data between Java and native
  62  * applications, and between Java applications in separate VMs.
  63  *
  64  * @since 1.2
  65  */
  66 public final class SystemFlavorMap implements FlavorMap, FlavorTable {
  67 
  68     /**
  69      * Constant prefix used to tag Java types converted to native platform
  70      * type.
  71      */
  72     private static String JavaMIME = "JAVA_DATAFLAVOR:";
  73 
  74     private static final Object FLAVOR_MAP_KEY = new Object();
  75 
  76     /**
  77      * Copied from java.util.Properties.
  78      */
  79     private static final String keyValueSeparators = "=: \t\r\n\f";
  80     private static final String strictKeyValueSeparators = "=:";
  81     private static final String whiteSpaceChars = " \t\r\n\f";
  82 
  83     /**
  84      * The list of valid, decoded text flavor representation classes, in order
  85      * from best to worst.
  86      */
  87     private static final String[] UNICODE_TEXT_CLASSES = {
  88         "java.io.Reader", "java.lang.String", "java.nio.CharBuffer", "\"[C\""
  89     };
  90 
  91     /**
  92      * The list of valid, encoded text flavor representation classes, in order
  93      * from best to worst.
  94      */
  95     private static final String[] ENCODED_TEXT_CLASSES = {


 181      * SoftReferences which reference LinkedHashSet of String natives.
 182      */
 183     private final SoftCache<DataFlavor, String> nativesForFlavorCache = new SoftCache<>();
 184 
 185     /**
 186      * Caches the result getFlavorsForNative(). Maps String natives to
 187      * SoftReferences which reference LinkedHashSet of DataFlavors.
 188      */
 189     private final SoftCache<String, DataFlavor> flavorsForNativeCache = new SoftCache<>();
 190 
 191     /**
 192      * Dynamic mapping generation used for text mappings should not be applied
 193      * to the DataFlavors and String natives for which the mappings have been
 194      * explicitly specified with setFlavorsForNative() or
 195      * setNativesForFlavor(). This keeps all such keys.
 196      */
 197     private Set<Object> disabledMappingGenerationKeys = new HashSet<>();
 198 
 199     /**
 200      * Returns the default FlavorMap for this thread's ClassLoader.

 201      * @return the default FlavorMap for this thread's ClassLoader
 202      */
 203     public static FlavorMap getDefaultFlavorMap() {
 204         AppContext context = AppContext.getAppContext();
 205         FlavorMap fm = (FlavorMap) context.get(FLAVOR_MAP_KEY);
 206         if (fm == null) {
 207             fm = new SystemFlavorMap();
 208             context.put(FLAVOR_MAP_KEY, fm);
 209         }
 210         return fm;
 211     }
 212 
 213     private SystemFlavorMap() {
 214     }
 215 
 216     /**
 217      * Initializes a SystemFlavorMap by reading flavormap.properties
 218      * For thread-safety must be called under lock on this.
 219      */
 220     private void initSystemFlavorMap() {
 221         if (isMapInitialized) {
 222             return;
 223         }
 224         isMapInitialized = true;
 225 
 226         InputStream is = SystemFlavorMap.class.getResourceAsStream("/sun/awt/datatransfer/flavormap.properties");
 227         if (is == null) {
 228             throw new InternalError("Default flavor mapping not found");
 229         }
 230 
 231         try (InputStreamReader isr = new InputStreamReader(is);
 232              BufferedReader reader = new BufferedReader(isr)) {
 233             String line;
 234             while ((line = reader.readLine()) != null) {
 235                 line = line.trim();
 236                 if (line.startsWith("#") || line.isEmpty()) continue;
 237                 while (line.endsWith("\\")) {
 238                     line = line.substring(0, line.length() - 1) + reader.readLine().trim();
 239                 }
 240                 int delimiterPosition = line.indexOf('=');
 241                 String key = line.substring(0, delimiterPosition).replace("\\ ", " ");
 242                 String[] values = line.substring(delimiterPosition + 1, line.length()).split(",");
 243                 for (String value : values) {
 244                     try {
 245                         MimeType mime = new MimeType(value);
 246                         if ("text".equals(mime.getPrimaryType())) {
 247                             String charset = mime.getParameter("charset");
 248                             if (DataTransferer.doesSubtypeSupportCharset(mime.getSubType(), charset))
 249                             {
 250                                 // We need to store the charset and eoln
 251                                 // parameters, if any, so that the
 252                                 // DataTransferer will have this information
 253                                 // for conversion into the native format.
 254                                 DataTransferer transferer = DataTransferer.getInstance();
 255                                 if (transferer != null) {
 256                                     transferer.registerTextFlavorProperties(key, charset,


 257                                             mime.getParameter("eoln"),
 258                                             mime.getParameter("terminators"));
 259                                 }
 260                             }
 261 
 262                             // But don't store any of these parameters in the
 263                             // DataFlavor itself for any text natives (even
 264                             // non-charset ones). The SystemFlavorMap will
 265                             // synthesize the appropriate mappings later.
 266                             mime.removeParameter("charset");
 267                             mime.removeParameter("class");
 268                             mime.removeParameter("eoln");
 269                             mime.removeParameter("terminators");
 270                             value = mime.toString();
 271                         }
 272                     } catch (MimeTypeParseException e) {
 273                         e.printStackTrace();
 274                         continue;
 275                     }
 276 


 315         LinkedHashSet<L> list = map.get(hashed);
 316         if (list == null) {
 317             list = new LinkedHashSet<>(1);
 318             map.put(hashed, list);
 319         }
 320         if (!list.contains(listed)) {
 321             list.add(listed);
 322         }
 323     }
 324 
 325     /**
 326      * Semantically equivalent to 'nativeToFlavor.get(nat)'. This method
 327      * handles the case where 'nat' is not found in 'nativeToFlavor'. In that
 328      * case, a new DataFlavor is synthesized, stored, and returned, if and
 329      * only if the specified native is encoded as a Java MIME type.
 330      */
 331     private LinkedHashSet<DataFlavor> nativeToFlavorLookup(String nat) {
 332         LinkedHashSet<DataFlavor> flavors = getNativeToFlavor().get(nat);
 333 
 334         if (nat != null && !disabledMappingGenerationKeys.contains(nat)) {
 335             DataTransferer transferer = DataTransferer.getInstance();
 336             if (transferer != null) {
 337                 LinkedHashSet<DataFlavor> platformFlavors =
 338                         transferer.getPlatformMappingsForNative(nat);
 339                 if (!platformFlavors.isEmpty()) {
 340                     if (flavors != null) {
 341                         // Prepending the platform-specific mappings ensures
 342                         // that the flavors added with
 343                         // addFlavorForUnencodedNative() are at the end of
 344                         // list.
 345                         platformFlavors.addAll(flavors);
 346                     }
 347                     flavors = platformFlavors;
 348                 }
 349             }
 350         }
 351 
 352         if (flavors == null && isJavaMIMEType(nat)) {
 353             String decoded = decodeJavaMIMEType(nat);
 354             DataFlavor flavor = null;
 355 
 356             try {
 357                 flavor = new DataFlavor(decoded);
 358             } catch (Exception e) {


 378             }
 379         }
 380 
 381         return (flavors != null) ? flavors : new LinkedHashSet<>(0);
 382     }
 383 
 384     /**
 385      * Semantically equivalent to 'flavorToNative.get(flav)'. This method
 386      * handles the case where 'flav' is not found in 'flavorToNative' depending
 387      * on the value of passes 'synthesize' parameter. If 'synthesize' is
 388      * SYNTHESIZE_IF_NOT_FOUND a native is synthesized, stored, and returned by
 389      * encoding the DataFlavor's MIME type. Otherwise an empty List is returned
 390      * and 'flavorToNative' remains unaffected.
 391      */
 392     private LinkedHashSet<String> flavorToNativeLookup(final DataFlavor flav,
 393                                                        final boolean synthesize) {
 394 
 395         LinkedHashSet<String> natives = getFlavorToNative().get(flav);
 396 
 397         if (flav != null && !disabledMappingGenerationKeys.contains(flav)) {
 398             DataTransferer transferer = DataTransferer.getInstance();
 399             if (transferer != null) {
 400                 LinkedHashSet<String> platformNatives =
 401                     transferer.getPlatformMappingsForFlavor(flav);
 402                 if (!platformNatives.isEmpty()) {
 403                     if (natives != null) {
 404                         // Prepend the platform-specific mappings to ensure
 405                         // that the natives added with
 406                         // addUnencodedNativeForFlavor() are at the end of
 407                         // list.
 408                         platformNatives.addAll(natives);
 409                     }
 410                     natives = platformNatives;
 411                 }
 412             }
 413         }
 414 
 415         if (natives == null) {
 416             if (synthesize) {
 417                 String encoded = encodeDataFlavor(flav);
 418                 natives = new LinkedHashSet<>(1);
 419                 getFlavorToNative().put(flav, natives);
 420                 natives.add(encoded);
 421 


 457      * @return a <code>java.util.List</code> of <code>java.lang.String</code>
 458      *         objects which are platform-specific representations of platform-
 459      *         specific data formats
 460      *
 461      * @see #encodeDataFlavor
 462      * @since 1.4
 463      */
 464     @Override
 465     public synchronized List<String> getNativesForFlavor(DataFlavor flav) {
 466         LinkedHashSet<String> retval = nativesForFlavorCache.check(flav);
 467         if (retval != null) {
 468             return new ArrayList<>(retval);
 469         }
 470 
 471         if (flav == null) {
 472             retval = new LinkedHashSet<>(getNativeToFlavor().keySet());
 473         } else if (disabledMappingGenerationKeys.contains(flav)) {
 474             // In this case we shouldn't synthesize a native for this flavor,
 475             // since its mappings were explicitly specified.
 476             retval = flavorToNativeLookup(flav, false);
 477         } else if (DataTransferer.isFlavorCharsetTextType(flav)) {
 478             retval = new LinkedHashSet<>(0);
 479 
 480             // For text/* flavors, flavor-to-native mappings specified in
 481             // flavormap.properties are stored per flavor's base type.
 482             if ("text".equals(flav.getPrimaryType())) {
 483                 LinkedHashSet<String> textTypeNatives =
 484                         getTextTypeToNative().get(flav.mimeType.getBaseType());
 485                 if (textTypeNatives != null) {
 486                     retval.addAll(textTypeNatives);
 487                 }
 488             }
 489 
 490             // Also include text/plain natives, but don't duplicate Strings
 491             LinkedHashSet<String> textTypeNatives =
 492                     getTextTypeToNative().get(TEXT_PLAIN_BASE_TYPE);
 493             if (textTypeNatives != null) {
 494                 retval.addAll(textTypeNatives);
 495             }
 496 
 497             if (retval.isEmpty()) {
 498                 retval = flavorToNativeLookup(flav, true);
 499             } else {
 500                 // In this branch it is guaranteed that natives explicitly
 501                 // listed for flav's MIME type were added with
 502                 // addUnencodedNativeForFlavor(), so they have lower priority.
 503                 retval.addAll(flavorToNativeLookup(flav, false));
 504             }
 505         } else if (DataTransferer.isFlavorNoncharsetTextType(flav)) {
 506             retval = getTextTypeToNative().get(flav.mimeType.getBaseType());
 507 
 508             if (retval == null || retval.isEmpty()) {
 509                 retval = flavorToNativeLookup(flav, true);
 510             } else {
 511                 // In this branch it is guaranteed that natives explicitly
 512                 // listed for flav's MIME type were added with
 513                 // addUnencodedNativeForFlavor(), so they have lower priority.
 514                 retval.addAll(flavorToNativeLookup(flav, false));
 515             }
 516         } else {
 517             retval = flavorToNativeLookup(flav, true);
 518         }
 519 
 520         nativesForFlavorCache.put(flav, retval);
 521         // Create a copy, because client code can modify the returned list.
 522         return new ArrayList<>(retval);
 523     }
 524 
 525     /**


 585         }
 586         flavorsForNativeCache.put(nat, returnValue);
 587         return new ArrayList<>(returnValue);
 588     }
 589 
 590     private static Set<DataFlavor> convertMimeTypeToDataFlavors(
 591         final String baseType) {
 592 
 593         final Set<DataFlavor> returnValue = new LinkedHashSet<>();
 594 
 595         String subType = null;
 596 
 597         try {
 598             final MimeType mimeType = new MimeType(baseType);
 599             subType = mimeType.getSubType();
 600         } catch (MimeTypeParseException mtpe) {
 601             // Cannot happen, since we checked all mappings
 602             // on load from flavormap.properties.
 603         }
 604 
 605         if (DataTransferer.doesSubtypeSupportCharset(subType, null)) {
 606             if (TEXT_PLAIN_BASE_TYPE.equals(baseType))
 607             {
 608                 returnValue.add(DataFlavor.stringFlavor);
 609             }
 610 
 611             for (String unicodeClassName : UNICODE_TEXT_CLASSES) {
 612                 final String mimeType = baseType + ";charset=Unicode;class=" +
 613                                             unicodeClassName;
 614 
 615                 final LinkedHashSet<String> mimeTypes =
 616                     handleHtmlMimeTypes(baseType, mimeType);
 617                 for (String mt : mimeTypes) {
 618                     DataFlavor toAdd = null;
 619                     try {
 620                         toAdd = new DataFlavor(mt);
 621                     } catch (ClassNotFoundException cannotHappen) {
 622                     }
 623                     returnValue.add(toAdd);
 624                 }
 625             }
 626 
 627             for (String charset : DataTransferer.standardEncodings()) {
 628 
 629                 for (String encodedTextClass : ENCODED_TEXT_CLASSES) {
 630                     final String mimeType =
 631                             baseType + ";charset=" + charset +
 632                             ";class=" + encodedTextClass;
 633 
 634                     final LinkedHashSet<String> mimeTypes =
 635                         handleHtmlMimeTypes(baseType, mimeType);
 636 
 637                     for (String mt : mimeTypes) {
 638 
 639                         DataFlavor df = null;
 640 
 641                         try {
 642                             df = new DataFlavor(mt);
 643                             // Check for equality to plainTextFlavor so
 644                             // that we can ensure that the exact charset of
 645                             // plainTextFlavor, not the canonical charset
 646                             // or another equivalent charset with a
 647                             // different name, is used.




   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.awt.datatransfer.DataFlavorUtil;
  29 import sun.awt.datatransfer.DesktopDatatransferServices;



  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.util.ArrayList;

  37 import java.util.Collections;
  38 import java.util.HashMap;
  39 import java.util.HashSet;
  40 import java.util.LinkedHashSet;
  41 import java.util.List;
  42 import java.util.Map;
  43 import java.util.Objects;

  44 import java.util.Set;
  45 



  46 /**
  47  * The SystemFlavorMap is a configurable map between "natives" (Strings), which
  48  * correspond to platform-specific data formats, and "flavors" (DataFlavors),
  49  * which correspond to platform-independent MIME types. This mapping is used
  50  * by the data transfer subsystem to transfer data between Java and native
  51  * applications, and between Java applications in separate VMs.
  52  *
  53  * @since 1.2
  54  */
  55 public final class SystemFlavorMap implements FlavorMap, FlavorTable {
  56 
  57     /**
  58      * Constant prefix used to tag Java types converted to native platform
  59      * type.
  60      */
  61     private static String JavaMIME = "JAVA_DATAFLAVOR:";
  62 


  63     /**
  64      * Copied from java.util.Properties.
  65      */
  66     private static final String keyValueSeparators = "=: \t\r\n\f";
  67     private static final String strictKeyValueSeparators = "=:";
  68     private static final String whiteSpaceChars = " \t\r\n\f";
  69 
  70     /**
  71      * The list of valid, decoded text flavor representation classes, in order
  72      * from best to worst.
  73      */
  74     private static final String[] UNICODE_TEXT_CLASSES = {
  75         "java.io.Reader", "java.lang.String", "java.nio.CharBuffer", "\"[C\""
  76     };
  77 
  78     /**
  79      * The list of valid, encoded text flavor representation classes, in order
  80      * from best to worst.
  81      */
  82     private static final String[] ENCODED_TEXT_CLASSES = {


 168      * SoftReferences which reference LinkedHashSet of String natives.
 169      */
 170     private final SoftCache<DataFlavor, String> nativesForFlavorCache = new SoftCache<>();
 171 
 172     /**
 173      * Caches the result getFlavorsForNative(). Maps String natives to
 174      * SoftReferences which reference LinkedHashSet of DataFlavors.
 175      */
 176     private final SoftCache<String, DataFlavor> flavorsForNativeCache = new SoftCache<>();
 177 
 178     /**
 179      * Dynamic mapping generation used for text mappings should not be applied
 180      * to the DataFlavors and String natives for which the mappings have been
 181      * explicitly specified with setFlavorsForNative() or
 182      * setNativesForFlavor(). This keeps all such keys.
 183      */
 184     private Set<Object> disabledMappingGenerationKeys = new HashSet<>();
 185 
 186     /**
 187      * Returns the default FlavorMap for this thread's ClassLoader.
 188      *
 189      * @return the default FlavorMap for this thread's ClassLoader
 190      */
 191     public static FlavorMap getDefaultFlavorMap() {
 192         return DataFlavorUtil.getDesktopServices().getFlavorMap(SystemFlavorMap::new);






 193     }
 194 
 195     private SystemFlavorMap() {
 196     }
 197 
 198     /**
 199      * Initializes a SystemFlavorMap by reading flavormap.properties
 200      * For thread-safety must be called under lock on this.
 201      */
 202     private void initSystemFlavorMap() {
 203         if (isMapInitialized) {
 204             return;
 205         }
 206         isMapInitialized = true;
 207 
 208         InputStream is = SystemFlavorMap.class.getResourceAsStream("/sun/awt/datatransfer/flavormap.properties");
 209         if (is == null) {
 210             throw new InternalError("Default flavor mapping not found");
 211         }
 212 
 213         try (InputStreamReader isr = new InputStreamReader(is);
 214              BufferedReader reader = new BufferedReader(isr)) {
 215             String line;
 216             while ((line = reader.readLine()) != null) {
 217                 line = line.trim();
 218                 if (line.startsWith("#") || line.isEmpty()) continue;
 219                 while (line.endsWith("\\")) {
 220                     line = line.substring(0, line.length() - 1) + reader.readLine().trim();
 221                 }
 222                 int delimiterPosition = line.indexOf('=');
 223                 String key = line.substring(0, delimiterPosition).replace("\\ ", " ");
 224                 String[] values = line.substring(delimiterPosition + 1, line.length()).split(",");
 225                 for (String value : values) {
 226                     try {
 227                         MimeType mime = new MimeType(value);
 228                         if ("text".equals(mime.getPrimaryType())) {
 229                             String charset = mime.getParameter("charset");
 230                             if (DataFlavorUtil.doesSubtypeSupportCharset(mime.getSubType(), charset))
 231                             {
 232                                 // We need to store the charset and eoln
 233                                 // parameters, if any, so that the
 234                                 // DataTransferer will have this information
 235                                 // for conversion into the native format.
 236                                 DesktopDatatransferServices services =
 237                                         DataFlavorUtil.getDesktopServices();
 238                                 if (services.isDesktopPresent()) {
 239                                     services.registerTextFlavorProperties(
 240                                             key, charset,
 241                                             mime.getParameter("eoln"),
 242                                             mime.getParameter("terminators"));
 243                                 }
 244                             }
 245 
 246                             // But don't store any of these parameters in the
 247                             // DataFlavor itself for any text natives (even
 248                             // non-charset ones). The SystemFlavorMap will
 249                             // synthesize the appropriate mappings later.
 250                             mime.removeParameter("charset");
 251                             mime.removeParameter("class");
 252                             mime.removeParameter("eoln");
 253                             mime.removeParameter("terminators");
 254                             value = mime.toString();
 255                         }
 256                     } catch (MimeTypeParseException e) {
 257                         e.printStackTrace();
 258                         continue;
 259                     }
 260 


 299         LinkedHashSet<L> list = map.get(hashed);
 300         if (list == null) {
 301             list = new LinkedHashSet<>(1);
 302             map.put(hashed, list);
 303         }
 304         if (!list.contains(listed)) {
 305             list.add(listed);
 306         }
 307     }
 308 
 309     /**
 310      * Semantically equivalent to 'nativeToFlavor.get(nat)'. This method
 311      * handles the case where 'nat' is not found in 'nativeToFlavor'. In that
 312      * case, a new DataFlavor is synthesized, stored, and returned, if and
 313      * only if the specified native is encoded as a Java MIME type.
 314      */
 315     private LinkedHashSet<DataFlavor> nativeToFlavorLookup(String nat) {
 316         LinkedHashSet<DataFlavor> flavors = getNativeToFlavor().get(nat);
 317 
 318         if (nat != null && !disabledMappingGenerationKeys.contains(nat)) {
 319             DesktopDatatransferServices desktopServices = DataFlavorUtil.getDesktopServices();
 320             if (desktopServices.isDesktopPresent()) {
 321                 LinkedHashSet<DataFlavor> platformFlavors =
 322                         desktopServices.getPlatformMappingsForNative(nat);
 323                 if (!platformFlavors.isEmpty()) {
 324                     if (flavors != null) {
 325                         // Prepending the platform-specific mappings ensures
 326                         // that the flavors added with
 327                         // addFlavorForUnencodedNative() are at the end of
 328                         // list.
 329                         platformFlavors.addAll(flavors);
 330                     }
 331                     flavors = platformFlavors;
 332                 }
 333             }
 334         }
 335 
 336         if (flavors == null && isJavaMIMEType(nat)) {
 337             String decoded = decodeJavaMIMEType(nat);
 338             DataFlavor flavor = null;
 339 
 340             try {
 341                 flavor = new DataFlavor(decoded);
 342             } catch (Exception e) {


 362             }
 363         }
 364 
 365         return (flavors != null) ? flavors : new LinkedHashSet<>(0);
 366     }
 367 
 368     /**
 369      * Semantically equivalent to 'flavorToNative.get(flav)'. This method
 370      * handles the case where 'flav' is not found in 'flavorToNative' depending
 371      * on the value of passes 'synthesize' parameter. If 'synthesize' is
 372      * SYNTHESIZE_IF_NOT_FOUND a native is synthesized, stored, and returned by
 373      * encoding the DataFlavor's MIME type. Otherwise an empty List is returned
 374      * and 'flavorToNative' remains unaffected.
 375      */
 376     private LinkedHashSet<String> flavorToNativeLookup(final DataFlavor flav,
 377                                                        final boolean synthesize) {
 378 
 379         LinkedHashSet<String> natives = getFlavorToNative().get(flav);
 380 
 381         if (flav != null && !disabledMappingGenerationKeys.contains(flav)) {
 382             DesktopDatatransferServices desktopServices = DataFlavorUtil.getDesktopServices();
 383             if (desktopServices.isDesktopPresent()) {
 384                 LinkedHashSet<String> platformNatives =
 385                         desktopServices.getPlatformMappingsForFlavor(flav);
 386                 if (!platformNatives.isEmpty()) {
 387                     if (natives != null) {
 388                         // Prepend the platform-specific mappings to ensure
 389                         // that the natives added with
 390                         // addUnencodedNativeForFlavor() are at the end of
 391                         // list.
 392                         platformNatives.addAll(natives);
 393                     }
 394                     natives = platformNatives;
 395                 }
 396             }
 397         }
 398 
 399         if (natives == null) {
 400             if (synthesize) {
 401                 String encoded = encodeDataFlavor(flav);
 402                 natives = new LinkedHashSet<>(1);
 403                 getFlavorToNative().put(flav, natives);
 404                 natives.add(encoded);
 405 


 441      * @return a <code>java.util.List</code> of <code>java.lang.String</code>
 442      *         objects which are platform-specific representations of platform-
 443      *         specific data formats
 444      *
 445      * @see #encodeDataFlavor
 446      * @since 1.4
 447      */
 448     @Override
 449     public synchronized List<String> getNativesForFlavor(DataFlavor flav) {
 450         LinkedHashSet<String> retval = nativesForFlavorCache.check(flav);
 451         if (retval != null) {
 452             return new ArrayList<>(retval);
 453         }
 454 
 455         if (flav == null) {
 456             retval = new LinkedHashSet<>(getNativeToFlavor().keySet());
 457         } else if (disabledMappingGenerationKeys.contains(flav)) {
 458             // In this case we shouldn't synthesize a native for this flavor,
 459             // since its mappings were explicitly specified.
 460             retval = flavorToNativeLookup(flav, false);
 461         } else if (DataFlavorUtil.isFlavorCharsetTextType(flav)) {
 462             retval = new LinkedHashSet<>(0);
 463 
 464             // For text/* flavors, flavor-to-native mappings specified in
 465             // flavormap.properties are stored per flavor's base type.
 466             if ("text".equals(flav.getPrimaryType())) {
 467                 LinkedHashSet<String> textTypeNatives =
 468                         getTextTypeToNative().get(flav.mimeType.getBaseType());
 469                 if (textTypeNatives != null) {
 470                     retval.addAll(textTypeNatives);
 471                 }
 472             }
 473 
 474             // Also include text/plain natives, but don't duplicate Strings
 475             LinkedHashSet<String> textTypeNatives =
 476                     getTextTypeToNative().get(TEXT_PLAIN_BASE_TYPE);
 477             if (textTypeNatives != null) {
 478                 retval.addAll(textTypeNatives);
 479             }
 480 
 481             if (retval.isEmpty()) {
 482                 retval = flavorToNativeLookup(flav, true);
 483             } else {
 484                 // In this branch it is guaranteed that natives explicitly
 485                 // listed for flav's MIME type were added with
 486                 // addUnencodedNativeForFlavor(), so they have lower priority.
 487                 retval.addAll(flavorToNativeLookup(flav, false));
 488             }
 489         } else if (DataFlavorUtil.isFlavorNoncharsetTextType(flav)) {
 490             retval = getTextTypeToNative().get(flav.mimeType.getBaseType());
 491 
 492             if (retval == null || retval.isEmpty()) {
 493                 retval = flavorToNativeLookup(flav, true);
 494             } else {
 495                 // In this branch it is guaranteed that natives explicitly
 496                 // listed for flav's MIME type were added with
 497                 // addUnencodedNativeForFlavor(), so they have lower priority.
 498                 retval.addAll(flavorToNativeLookup(flav, false));
 499             }
 500         } else {
 501             retval = flavorToNativeLookup(flav, true);
 502         }
 503 
 504         nativesForFlavorCache.put(flav, retval);
 505         // Create a copy, because client code can modify the returned list.
 506         return new ArrayList<>(retval);
 507     }
 508 
 509     /**


 569         }
 570         flavorsForNativeCache.put(nat, returnValue);
 571         return new ArrayList<>(returnValue);
 572     }
 573 
 574     private static Set<DataFlavor> convertMimeTypeToDataFlavors(
 575         final String baseType) {
 576 
 577         final Set<DataFlavor> returnValue = new LinkedHashSet<>();
 578 
 579         String subType = null;
 580 
 581         try {
 582             final MimeType mimeType = new MimeType(baseType);
 583             subType = mimeType.getSubType();
 584         } catch (MimeTypeParseException mtpe) {
 585             // Cannot happen, since we checked all mappings
 586             // on load from flavormap.properties.
 587         }
 588 
 589         if (DataFlavorUtil.doesSubtypeSupportCharset(subType, null)) {
 590             if (TEXT_PLAIN_BASE_TYPE.equals(baseType))
 591             {
 592                 returnValue.add(DataFlavor.stringFlavor);
 593             }
 594 
 595             for (String unicodeClassName : UNICODE_TEXT_CLASSES) {
 596                 final String mimeType = baseType + ";charset=Unicode;class=" +
 597                                             unicodeClassName;
 598 
 599                 final LinkedHashSet<String> mimeTypes =
 600                     handleHtmlMimeTypes(baseType, mimeType);
 601                 for (String mt : mimeTypes) {
 602                     DataFlavor toAdd = null;
 603                     try {
 604                         toAdd = new DataFlavor(mt);
 605                     } catch (ClassNotFoundException cannotHappen) {
 606                     }
 607                     returnValue.add(toAdd);
 608                 }
 609             }
 610 
 611             for (String charset : DataFlavorUtil.standardEncodings()) {
 612 
 613                 for (String encodedTextClass : ENCODED_TEXT_CLASSES) {
 614                     final String mimeType =
 615                             baseType + ";charset=" + charset +
 616                             ";class=" + encodedTextClass;
 617 
 618                     final LinkedHashSet<String> mimeTypes =
 619                         handleHtmlMimeTypes(baseType, mimeType);
 620 
 621                     for (String mt : mimeTypes) {
 622 
 623                         DataFlavor df = null;
 624 
 625                         try {
 626                             df = new DataFlavor(mt);
 627                             // Check for equality to plainTextFlavor so
 628                             // that we can ensure that the exact charset of
 629                             // plainTextFlavor, not the canonical charset
 630                             // or another equivalent charset with a
 631                             // different name, is used.