--- old/src/share/classes/java/awt/datatransfer/DataFlavor.java 2014-04-02 13:13:57.000000000 +0400 +++ new/src/share/classes/java/awt/datatransfer/DataFlavor.java 2014-04-02 13:13:57.000000000 +0400 @@ -25,13 +25,27 @@ 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 static sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION; /** @@ -501,7 +515,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) { @@ -1006,17 +1020,13 @@ } 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(getParameter("charset")); String thatCharset = - DataTransferer.canonicalName(that.getParameter("charset")); + DataTransferer.canonicalName(that.getParameter("charset")); if (thisCharset == null) { if (thatCharset != null) { return false; @@ -1028,13 +1038,17 @@ } } - if ("html".equals(getSubType()) && - this.getParameter("document") != null ) - { - if (!this.getParameter("document"). - equals(that.getParameter("document"))) - { - return false; + if ("html".equals(getSubType())) { + String thisDocument = this.getParameter("document"); + String thatDocument = that.getParameter("document"); + if (thisDocument == null) { + if (thatDocument != null) { + return false; + } + } else { + if (!thisDocument.equals(thatDocument)) { + return false; + } } } } @@ -1090,18 +1104,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 +1194,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 --- /dev/null 2014-04-02 13:13:58.000000000 +0400 +++ new/test/java/awt/datatransfer/DataFlavor/EqualsHashCodeSymmetryTest/EqualsHashCodeSymmetryTest.java 2014-04-02 13:13:58.000000000 +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); + } + } +}