From 0071d180c9252f032819fcb3c91004d5cfdb8630 Mon Sep 17 00:00:00 2001 From: pchelko Date: Wed, 19 Nov 2014 12:45:04 +0400 Subject: [PATCH] 8027148: SystemFlavorMap.getNativesForFlavor returns list of native formats in incorrect order Reviewed-by: anthony, serb --- .../awt/datatransfer/SystemFlavorMap.java | 357 ++++++++---------- .../sun/awt/datatransfer/DataTransferer.java | 11 +- .../classes/sun/awt/X11/XDataTransferer.java | 10 +- .../MappingGenerationTest.java | 183 +++++++++ 4 files changed, 346 insertions(+), 215 deletions(-) create mode 100644 test/java/awt/datatransfer/MappingGenerationTest/MappingGenerationTest.java diff --git a/src/share/classes/java/awt/datatransfer/SystemFlavorMap.java b/src/share/classes/java/awt/datatransfer/SystemFlavorMap.java index a98eea62c..b79847810 100644 --- a/src/share/classes/java/awt/datatransfer/SystemFlavorMap.java +++ b/src/share/classes/java/awt/datatransfer/SystemFlavorMap.java @@ -38,11 +38,13 @@ import java.net.URL; import java.net.MalformedURLException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import sun.awt.AppContext; @@ -101,20 +103,12 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { */ private static final String HTML_TEXT_BASE_TYPE = "text/html"; - /** - * This constant is passed to flavorToNativeLookup() to indicate that a - * a native should be synthesized, stored, and returned by encoding the - * DataFlavor's MIME type in case if the DataFlavor is not found in - * 'flavorToNative' map. - */ - private static final boolean SYNTHESIZE_IF_NOT_FOUND = true; - /** * Maps native Strings to Lists of DataFlavors (or base type Strings for * text DataFlavors). * Do not use the field directly, use getNativeToFlavor() instead. */ - private final Map> nativeToFlavor = new HashMap<>(); + private final Map> nativeToFlavor = new HashMap<>(); /** * Accessor to nativeToFlavor map. Since we use lazy initialization we must @@ -123,7 +117,7 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { * * @return nativeToFlavor */ - private Map> getNativeToFlavor() { + private Map> getNativeToFlavor() { if (!isMapInitialized) { initSystemFlavorMap(); } @@ -135,7 +129,7 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { * native Strings. * Do not use the field directly, use getFlavorToNative() instead. */ - private final Map> flavorToNative = new HashMap<>(); + private final Map> flavorToNative = new HashMap<>(); /** * Accessor to flavorToNative map. Since we use lazy initialization we must @@ -144,29 +138,52 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { * * @return flavorToNative */ - private synchronized Map> getFlavorToNative() { + private synchronized Map> getFlavorToNative() { if (!isMapInitialized) { initSystemFlavorMap(); } return flavorToNative; } + /** + * Maps a text DataFlavor primary mime-type to the native. Used only to store + * standard mappings registered in the flavormap.properties + * Do not use this field directly, use getTextTypeToNative() instead. + */ + private Map> textTypeToNative = new HashMap<>(); + /** * Shows if the object has been initialized. */ private boolean isMapInitialized = false; + /** + * An accessor to textTypeToNative map. Since we use lazy initialization we + * must use this accessor instead of direct access to the field which may not + * be initialized yet. This method will initialize the field if needed. + * + * @return textTypeToNative + */ + private synchronized Map> getTextTypeToNative() { + if (!isMapInitialized) { + initSystemFlavorMap(); + // From this point the map should not be modified + textTypeToNative = Collections.unmodifiableMap(textTypeToNative); + } + return textTypeToNative; + } + /** * Caches the result of getNativesForFlavor(). Maps DataFlavors to - * SoftReferences which reference Lists of String natives. + * SoftReferences which reference LinkedHashSet of String natives. */ - private Map>> getNativesForFlavorCache = new HashMap<>(); + private final SoftCache nativesForFlavorCache = new SoftCache<>(); /** * Caches the result getFlavorsForNative(). Maps String natives to - * SoftReferences which reference Lists of DataFlavors. + * SoftReferences which reference LinkedHashSet of DataFlavors. */ - private Map>> getFlavorsForNativeCache = new HashMap<>(); + private final SoftCache flavorsForNativeCache = new SoftCache<>(); /** * Dynamic mapping generation used for text mappings should not be applied @@ -174,7 +191,7 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { * explicitly specified with setFlavorsForNative() or * setNativesForFlavor(). This keeps all such keys. */ - private Set disabledMappingGenerationKeys = new HashSet(); + private Set disabledMappingGenerationKeys = new HashSet<>(); /** * Returns the default FlavorMap for this thread's ClassLoader. @@ -403,7 +420,7 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { flavor = new DataFlavor(value); } catch (Exception e) { try { - flavor = new DataFlavor(value, (String)null); + flavor = new DataFlavor(value, null); } catch (Exception ee) { ee.printStackTrace(); continue; @@ -411,11 +428,11 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { } final LinkedHashSet dfs = new LinkedHashSet<>(); - dfs.add(flavor); if ("text".equals(flavor.getPrimaryType())) { dfs.addAll(convertMimeTypeToDataFlavors(value)); + store(flavor.mimeType.getBaseType(), key, getTextTypeToNative()); } for (DataFlavor df : dfs) { @@ -504,10 +521,10 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { * the appropriate Map location, but rather will be appended to a List * stored in that location. */ - private void store(H hashed, L listed, Map> map) { - List list = map.get(hashed); + private void store(H hashed, L listed, Map> map) { + LinkedHashSet list = map.get(hashed); if (list == null) { - list = new ArrayList<>(1); + list = new LinkedHashSet<>(1); map.put(hashed, list); } if (!list.contains(listed)) { @@ -521,17 +538,17 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { * case, a new DataFlavor is synthesized, stored, and returned, if and * only if the specified native is encoded as a Java MIME type. */ - private List nativeToFlavorLookup(String nat) { - List flavors = getNativeToFlavor().get(nat); + private LinkedHashSet nativeToFlavorLookup(String nat) { + LinkedHashSet flavors = getNativeToFlavor().get(nat); + if (nat != null && !disabledMappingGenerationKeys.contains(nat)) { DataTransferer transferer = DataTransferer.getInstance(); if (transferer != null) { - List platformFlavors = + LinkedHashSet platformFlavors = transferer.getPlatformMappingsForNative(nat); if (!platformFlavors.isEmpty()) { if (flavors != null) { - platformFlavors.removeAll(new HashSet<>(flavors)); // Prepending the platform-specific mappings ensures // that the flavors added with // addFlavorForUnencodedNative() are at the end of @@ -557,24 +574,22 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { } if (flavor != null) { - flavors = new ArrayList<>(1); + flavors = new LinkedHashSet<>(1); getNativeToFlavor().put(nat, flavors); flavors.add(flavor); - getFlavorsForNativeCache.remove(nat); - getFlavorsForNativeCache.remove(null); + flavorsForNativeCache.remove(nat); - List natives = getFlavorToNative().get(flavor); + LinkedHashSet natives = getFlavorToNative().get(flavor); if (natives == null) { - natives = new ArrayList<>(1); + natives = new LinkedHashSet<>(1); getFlavorToNative().put(flavor, natives); } natives.add(nat); - getNativesForFlavorCache.remove(flavor); - getNativesForFlavorCache.remove(null); + nativesForFlavorCache.remove(flavor); } } - return (flavors != null) ? flavors : new ArrayList<>(0); + return (flavors != null) ? flavors : new LinkedHashSet<>(0); } /** @@ -585,18 +600,18 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { * encoding the DataFlavor's MIME type. Otherwise an empty List is returned * and 'flavorToNative' remains unaffected. */ - private List flavorToNativeLookup(final DataFlavor flav, - final boolean synthesize) { - List natives = getFlavorToNative().get(flav); + private LinkedHashSet flavorToNativeLookup(final DataFlavor flav, + final boolean synthesize) { + + LinkedHashSet natives = getFlavorToNative().get(flav); if (flav != null && !disabledMappingGenerationKeys.contains(flav)) { DataTransferer transferer = DataTransferer.getInstance(); if (transferer != null) { - List platformNatives = + LinkedHashSet platformNatives = transferer.getPlatformMappingsForFlavor(flav); if (!platformNatives.isEmpty()) { if (natives != null) { - platformNatives.removeAll(new HashSet<>(natives)); // Prepend the platform-specific mappings to ensure // that the natives added with // addUnencodedNativeForFlavor() are at the end of @@ -611,26 +626,25 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { if (natives == null) { if (synthesize) { String encoded = encodeDataFlavor(flav); - natives = new ArrayList<>(1); + natives = new LinkedHashSet<>(1); getFlavorToNative().put(flav, natives); natives.add(encoded); - getNativesForFlavorCache.remove(flav); - getNativesForFlavorCache.remove(null); - List flavors = getNativeToFlavor().get(encoded); + LinkedHashSet flavors = getNativeToFlavor().get(encoded); if (flavors == null) { - flavors = new ArrayList<>(1); + flavors = new LinkedHashSet<>(1); getNativeToFlavor().put(encoded, flavors); } flavors.add(flav); - getFlavorsForNativeCache.remove(encoded); - getFlavorsForNativeCache.remove(null); + + nativesForFlavorCache.remove(flav); + flavorsForNativeCache.remove(encoded); } else { - natives = new ArrayList<>(0); + natives = new LinkedHashSet<>(0); } } - return natives; + return new LinkedHashSet<>(natives); } /** @@ -658,103 +672,63 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { * @see #encodeDataFlavor * @since 1.4 */ + @Override public synchronized List getNativesForFlavor(DataFlavor flav) { - List retval = null; - - // Check cache, even for null flav - SoftReference> ref = getNativesForFlavorCache.get(flav); - if (ref != null) { - retval = ref.get(); - if (retval != null) { - // Create a copy, because client code can modify the returned - // list. - return new ArrayList<>(retval); - } + LinkedHashSet retval = nativesForFlavorCache.check(flav); + if (retval != null) { + return new ArrayList<>(retval); } if (flav == null) { - retval = new ArrayList<>(getNativeToFlavor().keySet()); + retval = new LinkedHashSet<>(getNativeToFlavor().keySet()); } else if (disabledMappingGenerationKeys.contains(flav)) { // In this case we shouldn't synthesize a native for this flavor, // since its mappings were explicitly specified. - retval = flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND); + retval = flavorToNativeLookup(flav, false); } else if (DataTransferer.isFlavorCharsetTextType(flav)) { + retval = new LinkedHashSet<>(0); // For text/* flavors, flavor-to-native mappings specified in // flavormap.properties are stored per flavor's base type. if ("text".equals(flav.getPrimaryType())) { - retval = getAllNativesForType(flav.mimeType.getBaseType()); - if (retval != null) { - // To prevent the List stored in the map from modification. - retval = new ArrayList(retval); + LinkedHashSet textTypeNatives = + getTextTypeToNative().get(flav.mimeType.getBaseType()); + if (textTypeNatives != null) { + retval.addAll(textTypeNatives); } } // Also include text/plain natives, but don't duplicate Strings - List textPlainList = getAllNativesForType(TEXT_PLAIN_BASE_TYPE); - - if (textPlainList != null && !textPlainList.isEmpty()) { - // To prevent the List stored in the map from modification. - // This also guarantees that removeAll() is supported. - textPlainList = new ArrayList<>(textPlainList); - if (retval != null && !retval.isEmpty()) { - // Use HashSet to get constant-time performance for search. - textPlainList.removeAll(new HashSet<>(retval)); - retval.addAll(textPlainList); - } else { - retval = textPlainList; - } + LinkedHashSet textTypeNatives = + getTextTypeToNative().get(TEXT_PLAIN_BASE_TYPE); + if (textTypeNatives != null) { + retval.addAll(textTypeNatives); } - if (retval == null || retval.isEmpty()) { - retval = flavorToNativeLookup(flav, SYNTHESIZE_IF_NOT_FOUND); + if (retval.isEmpty()) { + retval = flavorToNativeLookup(flav, true); } else { // In this branch it is guaranteed that natives explicitly // listed for flav's MIME type were added with // addUnencodedNativeForFlavor(), so they have lower priority. - List explicitList = - flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND); - - // flavorToNativeLookup() never returns null. - // It can return an empty List, however. - if (!explicitList.isEmpty()) { - // To prevent the List stored in the map from modification. - // This also guarantees that removeAll() is supported. - explicitList = new ArrayList<>(explicitList); - // Use HashSet to get constant-time performance for search. - explicitList.removeAll(new HashSet<>(retval)); - retval.addAll(explicitList); - } + retval.addAll(flavorToNativeLookup(flav, false)); } } else if (DataTransferer.isFlavorNoncharsetTextType(flav)) { - retval = getAllNativesForType(flav.mimeType.getBaseType()); + retval = getTextTypeToNative().get(flav.mimeType.getBaseType()); if (retval == null || retval.isEmpty()) { - retval = flavorToNativeLookup(flav, SYNTHESIZE_IF_NOT_FOUND); + retval = flavorToNativeLookup(flav, true); } else { // In this branch it is guaranteed that natives explicitly // listed for flav's MIME type were added with // addUnencodedNativeForFlavor(), so they have lower priority. - List explicitList = - flavorToNativeLookup(flav, !SYNTHESIZE_IF_NOT_FOUND); - - // flavorToNativeLookup() never returns null. - // It can return an empty List, however. - if (!explicitList.isEmpty()) { - // To prevent the List stored in the map from modification. - // This also guarantees that add/removeAll() are supported. - retval = new ArrayList<>(retval); - explicitList = new ArrayList<>(explicitList); - // Use HashSet to get constant-time performance for search. - explicitList.removeAll(new HashSet<>(retval)); - retval.addAll(explicitList); - } + retval.addAll(flavorToNativeLookup(flav, false)); } } else { - retval = flavorToNativeLookup(flav, SYNTHESIZE_IF_NOT_FOUND); + retval = flavorToNativeLookup(flav, true); } - getNativesForFlavorCache.put(flav, new SoftReference<>(retval)); + nativesForFlavorCache.put(flav, retval); // Create a copy, because client code can modify the returned list. return new ArrayList<>(retval); } @@ -790,62 +764,38 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { * @see #encodeJavaMIMEType * @since 1.4 */ + @Override public synchronized List getFlavorsForNative(String nat) { - - // Check cache, even for null nat - SoftReference> ref = getFlavorsForNativeCache.get(nat); - if (ref != null) { - List retval = ref.get(); - if (retval != null) { - return new ArrayList<>(retval); - } + LinkedHashSet returnValue = flavorsForNativeCache.check(nat); + if (returnValue != null) { + return new ArrayList<>(returnValue); + } else { + returnValue = new LinkedHashSet<>(); } - final LinkedHashSet returnValue = - new LinkedHashSet<>(); - if (nat == null) { - final List natives = getNativesForFlavor(null); - - for (String n : natives) - { - final List flavors = getFlavorsForNative(n); - - for (DataFlavor df : flavors) - { - returnValue.add(df); - } + for (String n : getNativesForFlavor(null)) { + returnValue.addAll(getFlavorsForNative(n)); } } else { - - final List flavors = nativeToFlavorLookup(nat); - + final LinkedHashSet flavors = nativeToFlavorLookup(nat); if (disabledMappingGenerationKeys.contains(nat)) { - return flavors; + return new ArrayList<>(flavors); } - final List flavorsAndBaseTypes = - nativeToFlavorLookup(nat); + final LinkedHashSet flavorsWithSynthesized = + nativeToFlavorLookup(nat); - for (DataFlavor df : flavorsAndBaseTypes) { + for (DataFlavor df : flavorsWithSynthesized) { returnValue.add(df); if ("text".equals(df.getPrimaryType())) { - try { - returnValue.addAll( - convertMimeTypeToDataFlavors( - new MimeType(df.getMimeType() - ).getBaseType())); - } catch (MimeTypeParseException e) { - e.printStackTrace(); - } + String baseType = df.mimeType.getBaseType(); + returnValue.addAll(convertMimeTypeToDataFlavors(baseType)); } } - } - - final List arrayList = new ArrayList<>(returnValue); - getFlavorsForNativeCache.put(nat, new SoftReference<>(arrayList)); - return new ArrayList<>(arrayList); + flavorsForNativeCache.put(nat, returnValue); + return new ArrayList<>(returnValue); } private static Set convertMimeTypeToDataFlavors( @@ -861,7 +811,6 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { } catch (MimeTypeParseException mtpe) { // Cannot happen, since we checked all mappings // on load from flavormap.properties. - assert(false); } if (DataTransferer.doesSubtypeSupportCharset(subType, null)) { @@ -940,10 +889,10 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { } private static final String [] htmlDocumntTypes = - new String [] {"all", "selection", "fragment"}; + new String [] {"all", "selection", "fragment"}; - private static LinkedHashSet handleHtmlMimeTypes( - String baseType, String mimeType) { + private static LinkedHashSet handleHtmlMimeTypes(String baseType, + String mimeType) { LinkedHashSet returnValues = new LinkedHashSet<>(); @@ -980,14 +929,14 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { * @see #getNativesForFlavor * @see #encodeDataFlavor */ - public synchronized Map - getNativesForFlavors(DataFlavor[] flavors) + @Override + public synchronized Map getNativesForFlavors(DataFlavor[] flavors) { // Use getNativesForFlavor to generate extra natives for text flavors // and stringFlavor if (flavors == null) { - List flavor_list = getFlavorsForNative(null); + List flavor_list = getFlavorsForNative(null); flavors = new DataFlavor[flavor_list.size()]; flavor_list.toArray(flavors); } @@ -1026,15 +975,14 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { * @see #getFlavorsForNative * @see #encodeJavaMIMEType */ - public synchronized Map - getFlavorsForNatives(String[] natives) + @Override + public synchronized Map getFlavorsForNatives(String[] natives) { // Use getFlavorsForNative to generate extra flavors for text natives - if (natives == null) { - List native_list = getNativesForFlavor(null); - natives = new String[native_list.size()]; - native_list.toArray(natives); + List nativesList = getNativesForFlavor(null); + natives = new String[nativesList.size()]; + nativesList.toArray(natives); } Map retval = new HashMap<>(natives.length, 1.0f); @@ -1043,7 +991,6 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { DataFlavor flav = (flavors.isEmpty())? null : flavors.get(0); retval.put(aNative, flav); } - return retval; } @@ -1069,20 +1016,16 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { */ public synchronized void addUnencodedNativeForFlavor(DataFlavor flav, String nat) { - if (flav == null || nat == null) { - throw new NullPointerException("null arguments not permitted"); - } + Objects.requireNonNull(nat, "Null native not permitted"); + Objects.requireNonNull(flav, "Null flavor not permitted"); - List natives = getFlavorToNative().get(flav); + LinkedHashSet natives = getFlavorToNative().get(flav); if (natives == null) { - natives = new ArrayList<>(1); + natives = new LinkedHashSet<>(1); getFlavorToNative().put(flav, natives); - } else if (natives.contains(nat)) { - return; } natives.add(nat); - getNativesForFlavorCache.remove(flav); - getNativesForFlavorCache.remove(null); + nativesForFlavorCache.remove(flav); } /** @@ -1115,18 +1058,15 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { */ public synchronized void setNativesForFlavor(DataFlavor flav, String[] natives) { - if (flav == null || natives == null) { - throw new NullPointerException("null arguments not permitted"); - } + Objects.requireNonNull(natives, "Null natives not permitted"); + Objects.requireNonNull(flav, "Null flavors not permitted"); getFlavorToNative().remove(flav); for (String aNative : natives) { addUnencodedNativeForFlavor(flav, aNative); } disabledMappingGenerationKeys.add(flav); - // Clear the cache to handle the case of empty natives. - getNativesForFlavorCache.remove(flav); - getNativesForFlavorCache.remove(null); + nativesForFlavorCache.remove(flav); } /** @@ -1149,20 +1089,16 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { */ public synchronized void addFlavorForUnencodedNative(String nat, DataFlavor flav) { - if (nat == null || flav == null) { - throw new NullPointerException("null arguments not permitted"); - } + Objects.requireNonNull(nat, "Null native not permitted"); + Objects.requireNonNull(flav, "Null flavor not permitted"); - List flavors = getNativeToFlavor().get(nat); + LinkedHashSet flavors = getNativeToFlavor().get(nat); if (flavors == null) { - flavors = new ArrayList<>(1); + flavors = new LinkedHashSet<>(1); getNativeToFlavor().put(nat, flavors); - } else if (flavors.contains(flav)) { - return; } flavors.add(flav); - getFlavorsForNativeCache.remove(nat); - getFlavorsForNativeCache.remove(null); + flavorsForNativeCache.remove(nat); } /** @@ -1194,18 +1130,15 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { */ public synchronized void setFlavorsForNative(String nat, DataFlavor[] flavors) { - if (nat == null || flavors == null) { - throw new NullPointerException("null arguments not permitted"); - } + Objects.requireNonNull(nat, "Null native not permitted"); + Objects.requireNonNull(flavors, "Null flavors not permitted"); getNativeToFlavor().remove(nat); for (DataFlavor flavor : flavors) { addFlavorForUnencodedNative(nat, flavor); } disabledMappingGenerationKeys.add(nat); - // Clear the cache to handle the case of empty flavors. - getFlavorsForNativeCache.remove(nat); - getFlavorsForNativeCache.remove(null); + flavorsForNativeCache.remove(nat); } /** @@ -1304,17 +1237,29 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { : null; } - private List getAllNativesForType(String type) { - Set retval = null; - for (DataFlavor dataFlavor : convertMimeTypeToDataFlavors(type)) { - List natives = getFlavorToNative().get(dataFlavor); - if (natives != null && !natives.isEmpty()) { - if (retval == null) { - retval = new LinkedHashSet<>(); - } - retval.addAll(natives); + private static final class SoftCache { + Map>> cache; + + public void put(K key, LinkedHashSet value) { + if (cache == null) { + cache = new HashMap<>(1); + } + cache.put(key, new SoftReference<>(value)); + } + + public void remove(K key) { + if (cache == null) return; + cache.remove(null); + cache.remove(key); + } + + public LinkedHashSet check(K key) { + if (cache == null) return null; + SoftReference> ref = cache.get(key); + if (ref != null) { + return ref.get(); } + return null; } - return retval == null ? null : new ArrayList<>(retval); } } diff --git a/src/share/classes/sun/awt/datatransfer/DataTransferer.java b/src/share/classes/sun/awt/datatransfer/DataTransferer.java index b21dab69a..eb3559725 100644 --- a/src/share/classes/sun/awt/datatransfer/DataTransferer.java +++ b/src/share/classes/sun/awt/datatransfer/DataTransferer.java @@ -77,6 +77,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.SortedMap; @@ -273,7 +274,7 @@ public abstract class DataTransferer { * instead, null will be returned. */ public static synchronized DataTransferer getInstance() { - return ((ComponentFactory) Toolkit.getDefaultToolkit()).getDataTransferer(); + return ((SunToolkit) Toolkit.getDefaultToolkit()).getDataTransferer(); } /** @@ -2424,8 +2425,8 @@ search: * If there are no platform-specific mappings for this native, the method * returns an empty List. */ - public List getPlatformMappingsForNative(String nat) { - return new ArrayList(); + public LinkedHashSet getPlatformMappingsForNative(String nat) { + return new LinkedHashSet<>(); } /** @@ -2433,8 +2434,8 @@ search: * If there are no platform-specific mappings for this flavor, the method * returns an empty List. */ - public List getPlatformMappingsForFlavor(DataFlavor df) { - return new ArrayList(); + public LinkedHashSet getPlatformMappingsForFlavor(DataFlavor df) { + return new LinkedHashSet<>(); } /** diff --git a/src/solaris/classes/sun/awt/X11/XDataTransferer.java b/src/solaris/classes/sun/awt/X11/XDataTransferer.java index fe8920d8c..3f97e0488 100644 --- a/src/solaris/classes/sun/awt/X11/XDataTransferer.java +++ b/src/solaris/classes/sun/awt/X11/XDataTransferer.java @@ -45,6 +45,7 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import javax.imageio.ImageIO; @@ -328,8 +329,9 @@ public class XDataTransferer extends DataTransferer { * a valid MIME and return a list of flavors to which the data in this MIME * type can be translated by the Data Transfer subsystem. */ - public List getPlatformMappingsForNative(String nat) { - List flavors = new ArrayList(); + public LinkedHashSet getPlatformMappingsForNative(String nat) { + LinkedHashSet flavors = new LinkedHashSet<>(); + if (nat == null) { return flavors; @@ -389,8 +391,8 @@ public class XDataTransferer extends DataTransferer { * MIME types to which the data in this flavor can be translated by the Data * Transfer subsystem. */ - public List getPlatformMappingsForFlavor(DataFlavor df) { - List natives = new ArrayList(1); + public LinkedHashSet getPlatformMappingsForFlavor(DataFlavor df) { + LinkedHashSet natives = new LinkedHashSet<>(1); if (df == null) { return natives; diff --git a/test/java/awt/datatransfer/MappingGenerationTest/MappingGenerationTest.java b/test/java/awt/datatransfer/MappingGenerationTest/MappingGenerationTest.java new file mode 100644 index 000000000..b0d8587af --- /dev/null +++ b/test/java/awt/datatransfer/MappingGenerationTest/MappingGenerationTest.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.SystemFlavorMap; +import java.util.List; + +/* + @test + @bug 4512530 8027148 + @summary tests that mappings for text flavors are generated properly + @author das@sparc.spb.su area=datatransfer +*/ + +public class MappingGenerationTest { + + private static final SystemFlavorMap fm = + (SystemFlavorMap)SystemFlavorMap.getDefaultFlavorMap(); + + public static void main(String[] args) { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + } + + /** + * Verifies that Lists returned from getNativesForFlavor() and + * getFlavorsForNative() are not modified with a subsequent call + * to addUnencodedNativeForFlavor() and addFlavorForUnencodedNative() + * respectively. + */ + public static void test1() { + DataFlavor df = new DataFlavor("text/plain-test1", null); + String nat = "native1"; + + List natives = fm.getNativesForFlavor(df); + fm.addUnencodedNativeForFlavor(df, nat); + List nativesNew = fm.getNativesForFlavor(df); + if (natives.equals(nativesNew)) { + System.err.println("orig=" + natives); + System.err.println("new=" + nativesNew); + throw new RuntimeException("Test failed"); + } + + List flavors = fm.getFlavorsForNative(nat); + fm.addFlavorForUnencodedNative(nat, df); + List flavorsNew = fm.getFlavorsForNative(nat); + if (flavors.equals(flavorsNew)) { + System.err.println("orig=" + flavors); + System.err.println("new=" + flavorsNew); + throw new RuntimeException("Test failed"); + } + } + + /** + * Verifies that SystemFlavorMap is not affected by modification of + * the Lists returned from getNativesForFlavor() and + * getFlavorsForNative(). + */ + public static void test2() { + DataFlavor df = new DataFlavor("text/plain-test2", null); + String nat = "native2"; + DataFlavor extraDf = new DataFlavor("text/test", null); + + List natives = fm.getNativesForFlavor(df); + natives.add("Should not be here"); + java.util.List nativesNew = fm.getNativesForFlavor(df); + if (natives.equals(nativesNew)) { + System.err.println("orig=" + natives); + System.err.println("new=" + nativesNew); + throw new RuntimeException("Test failed"); + } + + List flavors = fm.getFlavorsForNative(nat); + flavors.add(extraDf); + java.util.List flavorsNew = fm.getFlavorsForNative(nat); + if (flavors.equals(flavorsNew)) { + System.err.println("orig=" + flavors); + System.err.println("new=" + flavorsNew); + throw new RuntimeException("Test failed"); + } + } + + /** + * Verifies that addUnencodedNativeForFlavor() for a particular text flavor + * doesn't affect mappings for other flavors. + */ + public static void test3() { + DataFlavor df1 = new DataFlavor("text/plain-test3", null); + DataFlavor df2 = new DataFlavor("text/plain-test3; charset=Unicode; class=java.io.Reader", null); + String nat = "native3"; + List natives = fm.getNativesForFlavor(df2); + fm.addUnencodedNativeForFlavor(df1, nat); + List nativesNew = fm.getNativesForFlavor(df2); + if (!natives.equals(nativesNew)) { + System.err.println("orig=" + natives); + System.err.println("new=" + nativesNew); + throw new RuntimeException("Test failed"); + } + } + + /** + * Verifies that addUnencodedNativeForFlavor() really adds the specified + * flavor-to-native mapping to the existing mappings. + */ + public static void test4() { + DataFlavor df = new DataFlavor("text/plain-test4; charset=Unicode; class=java.io.Reader", null); + String nat = "native4"; + List natives = fm.getNativesForFlavor(df); + if (!natives.contains(nat)) { + fm.addUnencodedNativeForFlavor(df, nat); + List nativesNew = fm.getNativesForFlavor(df); + natives.add(nat); + if (!natives.equals(nativesNew)) { + System.err.println("orig=" + natives); + System.err.println("new=" + nativesNew); + throw new RuntimeException("Test failed"); + } + } + } + + /** + * Verifies that a flavor doesn't have any flavor-to-native mappings after + * a call to setNativesForFlavor() with this flavor and an empty native + * array as arguments. + */ + public static void test5() { + final DataFlavor flavor = + new DataFlavor("text/plain-TEST5; charset=Unicode", null); + + fm.getNativesForFlavor(flavor); + + fm.setNativesForFlavor(flavor, new String[0]); + + List natives = fm.getNativesForFlavor(flavor); + + if (!natives.isEmpty()) { + System.err.println("natives=" + natives); + throw new RuntimeException("Test failed"); + } + } + + /** + * Verifies that a native doesn't have any native-to-flavor mappings after + * a call to setFlavorsForNative() with this native and an empty flavor + * array as arguments. + */ + public static void test6() { + final String nat = "STRING"; + fm.getFlavorsForNative(nat); + fm.setFlavorsForNative(nat, new DataFlavor[0]); + + List flavors = fm.getFlavorsForNative(nat); + + if (!flavors.isEmpty()) { + System.err.println("flavors=" + flavors); + throw new RuntimeException("Test failed"); + } + } +} -- GitLab