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. |