# HG changeset patch # User pchelko # Date 1396515759 -14400 # Node ID 818b357f7778a4a7f34df24577b344dcaf49eaaa # Parent 53e618ceb567a37eaae38ea752f96f5dd61a0f6d 8038999: In Java 8 java.awt.datatransfer.DataFlavor.equals is no longer symmetric Reviewed-by: anthony, serb diff -r 53e618ceb567 -r 818b357f7778 jdk/src/share/classes/java/awt/datatransfer/DataFlavor.java --- a/jdk/src/share/classes/java/awt/datatransfer/DataFlavor.java Thu Apr 03 11:19:10 2014 +0400 +++ b/jdk/src/share/classes/java/awt/datatransfer/DataFlavor.java Thu Apr 03 13:02:39 2014 +0400 @@ -25,13 +25,28 @@ package java.awt.datatransfer; -import java.io.*; -import java.nio.*; -import java.util.*; - import sun.awt.datatransfer.DataTransferer; import sun.reflect.misc.ReflectUtil; +import java.io.ByteArrayInputStream; +import java.io.CharArrayReader; +import java.io.Externalizable; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.OptionalDataException; +import java.io.Reader; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Objects; + import static sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION; /** @@ -501,7 +516,7 @@ * @throws ClassNotFoundException * @throws NullPointerException if mimeType is null * - * @see tryToLoadClass + * @see #tryToLoadClass */ private void initialize(String mimeType, String humanPresentableName, ClassLoader classLoader) throws MimeTypeParseException, ClassNotFoundException { if (mimeType == null) { @@ -986,14 +1001,8 @@ return true; } - if (representationClass == null) { - if (that.getRepresentationClass() != null) { - return false; - } - } else { - if (!representationClass.equals(that.getRepresentationClass())) { - return false; - } + if (!Objects.equals(this.getRepresentationClass(), that.getRepresentationClass())) { + return false; } if (mimeType == null) { @@ -1006,34 +1015,22 @@ } if ("text".equals(getPrimaryType())) { - if (DataTransferer.doesSubtypeSupportCharset(this) && - representationClass != null && - !(isRepresentationClassReader() || - String.class.equals(representationClass) || - isRepresentationClassCharBuffer() || - char[].class.equals(representationClass))) - { + if (DataTransferer.doesSubtypeSupportCharset(this) + && representationClass != null + && !isStandardTextRepresentationClass()) { String thisCharset = - DataTransferer.canonicalName(getParameter("charset")); + DataTransferer.canonicalName(this.getParameter("charset")); String thatCharset = - DataTransferer.canonicalName(that.getParameter("charset")); - if (thisCharset == null) { - if (thatCharset != null) { - return false; - } - } else { - if (!thisCharset.equals(thatCharset)) { - return false; - } + DataTransferer.canonicalName(that.getParameter("charset")); + if (!Objects.equals(thisCharset, thatCharset)) { + return false; } } - if ("html".equals(getSubType()) && - this.getParameter("document") != null ) - { - if (!this.getParameter("document"). - equals(that.getParameter("document"))) - { + if ("html".equals(getSubType())) { + String thisDocument = this.getParameter("document"); + String thatDocument = that.getParameter("document"); + if (!Objects.equals(thisDocument, thatDocument)) { return false; } } @@ -1090,18 +1087,21 @@ // MimeType.match which reports a match if one or both of the // subTypes is '*', regardless of the other subType. - if ("text".equals(primaryType) && - DataTransferer.doesSubtypeSupportCharset(this) && - representationClass != null && - !(isRepresentationClassReader() || - String.class.equals(representationClass) || - isRepresentationClassCharBuffer() || - char[].class.equals(representationClass))) - { - String charset = - DataTransferer.canonicalName(getParameter("charset")); - if (charset != null) { - total += charset.hashCode(); + if ("text".equals(primaryType)) { + if (DataTransferer.doesSubtypeSupportCharset(this) + && representationClass != null + && !isStandardTextRepresentationClass()) { + String charset = DataTransferer.canonicalName(getParameter("charset")); + if (charset != null) { + total += charset.hashCode(); + } + } + + if ("html".equals(getSubType())) { + String document = this.getParameter("document"); + if (document != null) { + total += document.hashCode(); + } } } } @@ -1177,6 +1177,20 @@ return mimeType.match(mtype); } + /** + * Checks if the representation class is one of the standard text + * representation classes. + * + * @return true if the representation class is one of the standard text + * representation classes, otherwise false + */ + private boolean isStandardTextRepresentationClass() { + return isRepresentationClassReader() + || String.class.equals(representationClass) + || isRepresentationClassCharBuffer() + || char[].class.equals(representationClass); + } + /** * Does the DataFlavor represent a serialized object? * @return whether or not a serialized object is represented diff -r 53e618ceb567 -r 818b357f7778 jdk/test/java/awt/datatransfer/DataFlavor/EqualsHashCodeSymmetryTest/EqualsHashCodeSymmetryTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/awt/datatransfer/DataFlavor/EqualsHashCodeSymmetryTest/EqualsHashCodeSymmetryTest.java Thu Apr 03 13:02:39 2014 +0400 @@ -0,0 +1,88 @@ +/* + * 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; + +/** + * @test + * @bug 8038999 + * @summary DataFlavor.equals is not symmetric + * @author Petr Pchelko + */ +public class EqualsHashCodeSymmetryTest { + + private static final DataFlavor[] dataFlavors = { + DataFlavor.stringFlavor, + DataFlavor.imageFlavor, + DataFlavor.javaFileListFlavor, + DataFlavor.allHtmlFlavor, + DataFlavor.selectionHtmlFlavor, + DataFlavor.fragmentHtmlFlavor, + createFlavor("text/html; class=java.lang.String"), + new DataFlavor(String.class, "My test flavor number 1"), + new DataFlavor(String.class, "My test flavor number 2"), + new DataFlavor(StringBuilder.class, "My test flavor number 1") + }; + + public static void main(String[] args) { + testEqualsSymmetry(); + testEqualsHashCodeConsistency(); + testSimpleCollision(); + } + + private static void testEqualsSymmetry() { + for (DataFlavor flavor1 : dataFlavors) { + for (DataFlavor flavor2 : dataFlavors) { + if (flavor1.equals(flavor2) != flavor2.equals(flavor1)) { + throw new RuntimeException( + String.format("Equals is not symmetric for %s and %s", flavor1, flavor2)); + } + } + } + } + + private static void testEqualsHashCodeConsistency() { + for (DataFlavor flavor1 : dataFlavors) { + for (DataFlavor flavor2 : dataFlavors) { + if ((flavor1.equals(flavor2) && flavor1.hashCode() != flavor2.hashCode())) { + throw new RuntimeException( + String.format("Equals and hash code not consistent for %s and %s", flavor1, flavor2)); + } + } + } + } + + private static void testSimpleCollision() { + if (createFlavor("text/html; class=java.lang.String").hashCode() == DataFlavor.allHtmlFlavor.hashCode()) { + throw new RuntimeException("HashCode collision because the document parameter is not used"); + } + } + + private static DataFlavor createFlavor(String mime) { + try { + return new DataFlavor(mime); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } +}