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

Print this page




  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";
 103 


 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


 288 
 289                     final LinkedHashSet<DataFlavor> dfs = new LinkedHashSet<>();
 290                     dfs.add(flavor);
 291 
 292                     if ("text".equals(flavor.getPrimaryType())) {
 293                         dfs.addAll(convertMimeTypeToDataFlavors(value));
 294                         store(flavor.mimeType.getBaseType(), key, getTextTypeToNative());
 295                     }
 296 
 297                     for (DataFlavor df : dfs) {
 298                         store(df, key, getFlavorToNative());
 299                         store(key, df, getNativeToFlavor());
 300                     }
 301                 }
 302             }
 303         } catch (IOException e) {
 304             throw new InternalError("Error reading default flavor mapping", e);
 305         }
 306     }
 307 
























































 308     /**
 309      * Stores the listed object under the specified hash key in map. Unlike a
 310      * standard map, the listed object will not replace any object already at
 311      * the appropriate Map location, but rather will be appended to a List
 312      * stored in that location.
 313      */
 314     private <H, L> void store(H hashed, L listed, Map<H, LinkedHashSet<L>> map) {
 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




  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      * The list of valid, decoded text flavor representation classes, in order
  78      * from best to worst.
  79      */
  80     private static final String[] UNICODE_TEXT_CLASSES = {
  81         "java.io.Reader", "java.lang.String", "java.nio.CharBuffer", "\"[C\""
  82     };
  83 
  84     /**
  85      * The list of valid, encoded text flavor representation classes, in order
  86      * from best to worst.
  87      */
  88     private static final String[] ENCODED_TEXT_CLASSES = {
  89         "java.io.InputStream", "java.nio.ByteBuffer", "\"[B\""
  90     };
  91 
  92     /**
  93      * A String representing text/plain MIME type.
  94      */
  95     private static final String TEXT_PLAIN_BASE_TYPE = "text/plain";
  96 


 214         if (isMapInitialized) {
 215             return;
 216         }
 217         isMapInitialized = true;
 218 
 219         InputStream is = SystemFlavorMap.class.getResourceAsStream("/sun/awt/datatransfer/flavormap.properties");
 220         if (is == null) {
 221             throw new InternalError("Default flavor mapping not found");
 222         }
 223 
 224         try (InputStreamReader isr = new InputStreamReader(is);
 225              BufferedReader reader = new BufferedReader(isr)) {
 226             String line;
 227             while ((line = reader.readLine()) != null) {
 228                 line = line.trim();
 229                 if (line.startsWith("#") || line.isEmpty()) continue;
 230                 while (line.endsWith("\\")) {
 231                     line = line.substring(0, line.length() - 1) + reader.readLine().trim();
 232                 }
 233                 int delimiterPosition = line.indexOf('=');
 234                 String key = line.substring(0, delimiterPosition).replaceAll("\\ ", " ");
 235                 String[] values = line.substring(delimiterPosition + 1, line.length()).split(",");
 236                 for (String value : values) {
 237                     try {
 238                         value = loadConvert(value);
 239                         MimeType mime = new MimeType(value);
 240                         if ("text".equals(mime.getPrimaryType())) {
 241                             String charset = mime.getParameter("charset");
 242                             if (DataTransferer.doesSubtypeSupportCharset(mime.getSubType(), charset))
 243                             {
 244                                 // We need to store the charset and eoln
 245                                 // parameters, if any, so that the
 246                                 // DataTransferer will have this information
 247                                 // for conversion into the native format.
 248                                 DataTransferer transferer = DataTransferer.getInstance();
 249                                 if (transferer != null) {
 250                                     transferer.registerTextFlavorProperties(key, charset,
 251                                             mime.getParameter("eoln"),
 252                                             mime.getParameter("terminators"));
 253                                 }
 254                             }
 255 
 256                             // But don't store any of these parameters in the
 257                             // DataFlavor itself for any text natives (even
 258                             // non-charset ones). The SystemFlavorMap will


 282 
 283                     final LinkedHashSet<DataFlavor> dfs = new LinkedHashSet<>();
 284                     dfs.add(flavor);
 285 
 286                     if ("text".equals(flavor.getPrimaryType())) {
 287                         dfs.addAll(convertMimeTypeToDataFlavors(value));
 288                         store(flavor.mimeType.getBaseType(), key, getTextTypeToNative());
 289                     }
 290 
 291                     for (DataFlavor df : dfs) {
 292                         store(df, key, getFlavorToNative());
 293                         store(key, df, getNativeToFlavor());
 294                     }
 295                 }
 296             }
 297         } catch (IOException e) {
 298             throw new InternalError("Error reading default flavor mapping", e);
 299         }
 300     }
 301 
 302     private static String loadConvert(String theString) {
 303         char aChar;
 304         int len = theString.length();
 305         StringBuilder outBuffer = new StringBuilder(len);
 306 
 307         for (int x = 0; x < len; ) {
 308             aChar = theString.charAt(x++);
 309             if (aChar == '\\') {
 310                 aChar = theString.charAt(x++);
 311                 if (aChar == 'u') {
 312                     // Read the xxxx
 313                     int value = 0;
 314                     for (int i = 0; i < 4; i++) {
 315                         aChar = theString.charAt(x++);
 316                         switch (aChar) {
 317                             case '0': case '1': case '2': case '3': case '4':
 318                             case '5': case '6': case '7': case '8': case '9': {
 319                                 value = (value << 4) + aChar - '0';
 320                                 break;
 321                             }
 322                             case 'a': case 'b': case 'c':
 323                             case 'd': case 'e': case 'f': {
 324                                 value = (value << 4) + 10 + aChar - 'a';
 325                                 break;
 326                             }
 327                             case 'A': case 'B': case 'C':
 328                             case 'D': case 'E': case 'F': {
 329                                 value = (value << 4) + 10 + aChar - 'A';
 330                                 break;
 331                             }
 332                             default: {
 333                                 throw new IllegalArgumentException(
 334                                         "Malformed \\uxxxx encoding.");
 335                             }
 336                         }
 337                     }
 338                     outBuffer.append((char)value);
 339                 } else {
 340                     if (aChar == 't') {
 341                         aChar = '\t';
 342                     } else if (aChar == 'r') {
 343                         aChar = '\r';
 344                     } else if (aChar == 'n') {
 345                         aChar = '\n';
 346                     } else if (aChar == 'f') {
 347                         aChar = '\f';
 348                     }
 349                     outBuffer.append(aChar);
 350                 }
 351             } else {
 352                 outBuffer.append(aChar);
 353             }
 354         }
 355         return outBuffer.toString();
 356     }
 357 
 358     /**
 359      * Stores the listed object under the specified hash key in map. Unlike a
 360      * standard map, the listed object will not replace any object already at
 361      * the appropriate Map location, but rather will be appended to a List
 362      * stored in that location.
 363      */
 364     private <H, L> void store(H hashed, L listed, Map<H, LinkedHashSet<L>> map) {
 365         LinkedHashSet<L> list = map.get(hashed);
 366         if (list == null) {
 367             list = new LinkedHashSet<>(1);
 368             map.put(hashed, list);
 369         }
 370         if (!list.contains(listed)) {
 371             list.add(listed);
 372         }
 373     }
 374 
 375     /**
 376      * Semantically equivalent to 'nativeToFlavor.get(nat)'. This method
 377      * handles the case where 'nat' is not found in 'nativeToFlavor'. In that