Issue Details (XML | Word | Printable)

Key: JCR-320
Type: Bug Bug
Status: Open Open
Priority: Minor Minor
Assignee: Jukka Zitting
Reporter: Piyush Purang
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
Jackrabbit Content Repository

BinaryValue equals fails for two objects with two different byte arrays that contain the same bytes.

Created: 16/Feb/06 03:37 AM   Updated: 14/Feb/08 01:46 PM
Return to search
Component/s: jackrabbit-core
Affects Version/s: 0.9, 1.0, 1.0.1
Fix Version/s: None

Time Tracking:
Not Specified

Issue Links:
Duplicate
 


 Description  « Hide
http://svn.apache.org/repos/asf/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/value/BinaryValue.java


Here is the present implementation

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof BinaryValue) {
            BinaryValue other = (BinaryValue) obj;
            if (text == other.text && stream == other.stream
                    && streamData == other.streamData) {
                return true;
            }
            // stream, streamData and text are mutually exclusive,
            // i.e. only one of them can be non-null
            if (stream != null) {
                return stream.equals(other.stream);
            } else if (streamData != null) {
                return streamData.equals(other.streamData);
            } else {
                return text.equals(other.text);
            }
        }
        return false;
    }


Here are the problems with the present implementation


1. streamData.equals(other.streamData ) will fail miserably.

2. too many return statements! I guess no one ran a checkstyle on it.

3. return stream.equals(other.stream); will always be false unless both have been created with the same InputStream!

I wrote some testcases in SetValueBinaryTest

    public void testBinaryValueEquals() throws Exception {
        BinaryValue binaryValue1 = null;
        BinaryValue binaryValue2 = null;

        // initialize some binary value
        data = createRandomString(10).getBytes();
        binaryValue1 = (BinaryValue) superuser.getValueFactory().createValue(new ByteArrayInputStream(data));
        binaryValue2 = (BinaryValue) superuser.getValueFactory ().createValue(new ByteArrayInputStream(data));
        //ideallly setup a test harness that tests all the cases as defined by the contract in Object.equals()
        assertTrue( binaryValue1.equals(binaryValue2));
        assertTrue( binaryValue1.equals(binaryValue1));
        assertFalse( binaryValue1.equals(null));
    }


    public void testBinaryValueEquals2() throws Exception {
        String str = createRandomString(10);
        BinaryValue binaryValue1 = new BinaryValue(str.getBytes());
        BinaryValue binaryValue2 = new BinaryValue(str.getBytes());

        assertTrue( binaryValue1.equals(binaryValue2));
        assertTrue( binaryValue1.equals(binaryValue1));
        assertFalse( binaryValue1.equals(null));
    }
    
    public void testBinaryValueEquals3() throws Exception {
        String str1 = "Some string xyz";
        String str2 = new StringBuffer().append("Some string xyz").toString();
        BinaryValue binaryValue1 = new BinaryValue(str1);
        BinaryValue binaryValue2 = new BinaryValue(str2);

        assertTrue( binaryValue1.equals(binaryValue2));
        assertTrue( binaryValue1.equals(binaryValue1));
        assertFalse( binaryValue1.equals(null));
    }


They were written quickly but with the present implementation the first two fail at the very first assert* statement which for stream I can understand (as we are basically propogating InputStream's equals contract) but for byte arrays I can't agree with this behaviour unless it is so intended. It behaves the best when BinaryVlaue wraps a string i.e. testBinaryValueEquals3() passes without trouble.

I propose a new implementation where I am not very convinced with the behaviour when we have streams being wrapped. If it should fail for the streams then we should change the documentation for the method. As for making it work right when streams are involved .. well the streams will have to be read and compared.

 
public boolean equals(Object obj) {
        boolean result = false;
        if (this == obj) {
            result = true;
        } else if (obj instanceof BinaryValue) {
            BinaryValue other = (BinaryValue) obj;
            // only one of text, stream and streamData are set at any given point
            if (text != null) {
                result = text.equals(other.text);
            } else if (stream != null) {
                result = stream.equals(other.stream);
            } else if (streamData != null) {
                result = Arrays.equals(streamData, other.streamData);
            } else {
                // all values are null; test that the other object (which could be us)
                // also has everything set to null!
                result = (other.text == null && other.stream == null
                        && other.streamData == null);
            }
        }
        return result;
    }

This implementation of course fails at the first assert* of the first test method testBinaryValueEquals (It will pass the other two assert*s).

And if this new implementation doesn't fit the bill and an alternative isn't needed then just skip implementing equals.

One more thought running through my mind is - if text, stream and data are mutually exclusive why don't we have different classes for each of them? (Try and wrap a stringValue into a binary value...)

I have also noticed that the hashCodes all return 0's throughtout the package. In the case that the hashCode can't keep up with the contract of equal I would propose throwing an UnsupportedOpertaionException. And if not then declare it in the BasValue as it will be inherited (unless this leads the QA tools to report infringement of the rule that when you define equals you need to redefine hashCode - checkstyle does that).

The value package doesn't have any tests for it.. or did I miss them?

 All   Comments   Work Log   Change History   Subversion Commits      Sort Order: Ascending order - Click to sort in descending order
Repository Revision Date User Message
ASF #423585 Wed Jul 19 20:13:45 UTC 2006 jukka JCR-320: Test case for binary value equality.
Files Changed
ADD /jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/value
ADD /jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/value/BinaryValueTest.java
ADD /jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/value/TestAll.java

Repository Revision Date User Message
ASF #493220 Fri Jan 05 22:49:17 UTC 2007 jukka JCR-320: Moved org.apache.jackrabbit.value tests to jackrabbit-jcr-commons
Files Changed
MODIFY /jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/value/BinaryValueTest.java
DEL /jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/value
ADD /jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/value (from /jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/value)

Repository Revision Date User Message
ASF #769371 Tue Apr 28 12:30:12 UTC 2009 reschke JCR-320: remove BinaryValueTest#testBinaryValueEquals from known.issues, because the problematic test cases already have been commented out earlier
Files Changed
MODIFY /jackrabbit/trunk/jackrabbit-core/pom.xml