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 = {
  96         "java.io.InputStream", "java.nio.ByteBuffer", "\"[B\""
  97     };
  98 
  99     /**
 100      * A String representing text/plain MIME type.
 101      */
 102     private static final String TEXT_PLAIN_BASE_TYPE = "text/plain";


 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.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.util.ArrayList;
  37 import java.util.Arrays;
  38 import java.util.Collections;
  39 import java.util.HashMap;
  40 import java.util.HashSet;
  41 import java.util.LinkedHashSet;
  42 import java.util.List;
  43 import java.util.Map;
  44 import java.util.Objects;

  45 import java.util.Set;
  46 



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









  64     /**
  65      * The list of valid, decoded text flavor representation classes, in order
  66      * from best to worst.
  67      */
  68     private static final String[] UNICODE_TEXT_CLASSES = {
  69         "java.io.Reader", "java.lang.String", "java.nio.CharBuffer", "\"[C\""
  70     };
  71 
  72     /**
  73      * The list of valid, encoded text flavor representation classes, in order
  74      * from best to worst.
  75      */
  76     private static final String[] ENCODED_TEXT_CLASSES = {
  77         "java.io.InputStream", "java.nio.ByteBuffer", "\"[B\""
  78     };
  79 
  80     /**
  81      * A String representing text/plain MIME type.
  82      */
  83     private static final String TEXT_PLAIN_BASE_TYPE = "text/plain";


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






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


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


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


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


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