diff --git a/src/main/java/org/apache/commons/lang3/ImmutablePair.java b/src/main/java/org/apache/commons/lang3/ImmutablePair.java new file mode 100644 index 000000000..b74396001 --- /dev/null +++ b/src/main/java/org/apache/commons/lang3/ImmutablePair.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.util.Map; + +/** + * Immutable concrete manifestation of the {@link Pair} type. + * + *

#ThreadSafe# if the objects are threadsafe

+ * @since Lang 3.0 + * @author Matt Benson + * @version $Id$ + * + * @param left generic type + * @param right generic type + */ +public class ImmutablePair extends Pair { + /** Serialization version */ + private static final long serialVersionUID = 4954918890077093841L; + + /** Left object */ + public final L left; + /** Right object */ + public final R right; + + /** + * Create a new ImmutablePair instance. + * + * @param left + * @param right + */ + public ImmutablePair(L left, R right) { + super(); + this.left = left; + this.right = right; + } + + /** + * {@inheritDoc} + */ + @Override + public L getLeftElement() { + return left; + } + + /** + * {@inheritDoc} + */ + @Override + public R getRightElement() { + return right; + } + + /** + * {@link Map.Entry#setValue(Object)} implementation. + * @throws UnsupportedOperationException + */ + public R setValue(R arg0) { + throw new UnsupportedOperationException(); + } + + /** + * Static fluent creation method for an {@link ImmutablePair}: + * ImmutablePair.of(left, right) + * @param + * @param + * @param left + * @param right + * @return ImmutablePair(left, right) + */ + public static ImmutablePair of(L left, R right) { + return new ImmutablePair(left, right); + } +} diff --git a/src/main/java/org/apache/commons/lang3/MutablePair.java b/src/main/java/org/apache/commons/lang3/MutablePair.java new file mode 100644 index 000000000..009f99314 --- /dev/null +++ b/src/main/java/org/apache/commons/lang3/MutablePair.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.util.Map; + +/** + * Mutable concrete manifestation of the {@link Pair} type. + * + *

#ThreadSafe# if the objects are threadsafe

+ * @since Lang 3.0 + * @author Matt Benson + * @version $Id$ + * + * @param left generic type + * @param right generic type + */ +public class MutablePair extends Pair { + /** Serialization version */ + private static final long serialVersionUID = 4954918890077093841L; + + private L leftElement; + private R rightElement; + + /** + * Create a new MutablePair instance. + */ + public MutablePair() { + super(); + } + + /** + * Create a new MutablePair instance. + * + * @param leftElement + * @param rightElement + */ + public MutablePair(L leftElement, R rightElement) { + super(); + this.leftElement = leftElement; + this.rightElement = rightElement; + } + + /** + * {@inheritDoc} + */ + @Override + public L getLeftElement() { + return leftElement; + } + + /** + * Set the left element of the pair. + * @param leftElement + */ + public void setLeftElement(L leftElement) { + this.leftElement = leftElement; + } + + /** + * {@inheritDoc} + */ + @Override + public R getRightElement() { + return rightElement; + } + + /** + * Set the right element of the pair. + * @param rightElement + */ + public void setRightElement(R rightElement) { + this.rightElement = rightElement; + } + + /** + * Implement {@link Map.Entry#setValue(Object)}. + * @param value value (rightElement) to set + */ + public R setValue(R value) { + R result = getRightElement(); + setRightElement(value); + return result; + } + + /** + * Static fluent creation method for a {@link MutablePair}: + * MutablePair.of(left, right) + * @param + * @param + * @param left + * @param right + * @return MutablePair(left, right) + */ + public static MutablePair of(L left, R right) { + return new MutablePair(left, right); + } +} diff --git a/src/main/java/org/apache/commons/lang3/Pair.java b/src/main/java/org/apache/commons/lang3/Pair.java index 1485474e6..f66cfc5b2 100644 --- a/src/main/java/org/apache/commons/lang3/Pair.java +++ b/src/main/java/org/apache/commons/lang3/Pair.java @@ -17,35 +17,47 @@ package org.apache.commons.lang3; import java.io.Serializable; +import java.util.Map; import org.apache.commons.lang3.builder.HashCodeBuilder; /** - * A basic immutable Object pair. - * - *

#ThreadSafe# if the objects are threadsafe

+ * Abstract Pair (or 2-element Tuple). + * * @since Lang 3.0 * @author Matt Benson * @version $Id$ */ -public final class Pair implements Serializable { +public abstract class Pair implements Serializable, Map.Entry { /** Serialization version */ private static final long serialVersionUID = 4954918890077093841L; - /** Left object */ - public final L left; - - /** Right object */ - public final R right; + /** + * Get the "left" element of the pair. + * @return L + */ + public abstract L getLeftElement(); /** - * Create a new Pair instance. - * @param left - * @param right + * Get the "right" element of the pair. + * @return */ - public Pair(L left, R right) { - this.left = left; - this.right = right; + public abstract R getRightElement(); + + /** + * Return {@link #getLeftElement()} as a {@link Map.Entry}'s key. + * @return L + */ + public final L getKey() { + return getLeftElement(); + } + + /** + * Return {@link #getRightElement()} as a {@link Map.Entry}'s value. + * @return R + */ + public R getValue() { + return getRightElement(); } /** @@ -60,7 +72,8 @@ public final class Pair implements Serializable { return false; } Pair other = (Pair) obj; - return ObjectUtils.equals(left, other.left) && ObjectUtils.equals(right, other.right); + return ObjectUtils.equals(getLeftElement(), other.getLeftElement()) + && ObjectUtils.equals(getRightElement(), other.getRightElement()); } /** @@ -68,7 +81,9 @@ public final class Pair implements Serializable { */ @Override public int hashCode() { - return new HashCodeBuilder().append(left).append(right).toHashCode(); + // TODO should the hashCodeBuilder be seeded per concrete type? + return new HashCodeBuilder().append(getLeftElement()).append(getRightElement()) + .toHashCode(); } /** @@ -76,24 +91,25 @@ public final class Pair implements Serializable { */ @Override public String toString() { - StringBuilder builder = new StringBuilder(); + StringBuilder builder = new StringBuilder(ClassUtils.getShortClassName(this, null)); builder.append("("); - builder.append(left); + builder.append(getLeftElement()); builder.append(","); - builder.append(right); + builder.append(getRightElement()); builder.append(")"); return builder.toString(); } /** - * Static fluent creation method for a Pair: Pair.of(left, right) + * Static fluent creation method for a {@link Pair}: + * Pair.of(left, right) * @param * @param * @param left * @param right - * @return Pair(left, right) + * @return ImmutablePair(left, right) */ public static Pair of(L left, R right) { - return new Pair(left, right); + return new ImmutablePair(left, right); } } diff --git a/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java b/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java index e52329cf1..75238cccc 100644 --- a/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java +++ b/src/main/java/org/apache/commons/lang3/builder/EqualsBuilder.java @@ -159,7 +159,7 @@ public class EqualsBuilder implements Builder { static boolean isRegistered(Object lhs, Object rhs) { Set> registry = getRegistry(); Pair pair = getRegisterPair(lhs, rhs); - Pair swappedPair = Pair.of(pair.right, pair.left); + Pair swappedPair = Pair.of(pair.getLeftElement(), pair.getRightElement()); return registry != null && (registry.contains(pair) || registry.contains(swappedPair)); diff --git a/src/test/java/org/apache/commons/lang3/ImmutablePairTest.java b/src/test/java/org/apache/commons/lang3/ImmutablePairTest.java new file mode 100644 index 000000000..90a91b736 --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/ImmutablePairTest.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNull; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import org.junit.Test; + +/** + * Test the Pair class. + * @author Matt Benson + * @version $Id$ + */ +public class ImmutablePairTest { + + @Test + public void testBasic() throws Exception { + ImmutablePair pair = new ImmutablePair(0, "foo"); + assertEquals(0, pair.left.intValue()); + assertEquals(0, pair.getLeftElement().intValue()); + assertEquals("foo", pair.right); + assertEquals("foo", pair.getRightElement()); + ImmutablePair pair2 = new ImmutablePair(null, "bar"); + assertNull(pair2.left); + assertNull(pair2.getLeftElement()); + assertEquals("bar", pair2.right); + assertEquals("bar", pair2.getRightElement()); + } + + @Test + public void testPairOf() throws Exception { + ImmutablePair pair = ImmutablePair.of(0, "foo"); + assertEquals(0, pair.left.intValue()); + assertEquals(0, pair.getLeftElement().intValue()); + assertEquals("foo", pair.right); + assertEquals("foo", pair.getRightElement()); + ImmutablePair pair2 = ImmutablePair.of(null, "bar"); + assertNull(pair2.left); + assertNull(pair2.getLeftElement()); + assertEquals("bar", pair2.right); + assertEquals("bar", pair2.getRightElement()); + } + + @Test + public void testEquals() throws Exception { + assertEquals(ImmutablePair.of(null, "foo"), ImmutablePair.of(null, "foo")); + assertFalse(ImmutablePair.of("foo", 0).equals(ImmutablePair.of("foo", null))); + assertFalse(ImmutablePair.of("foo", "bar").equals(ImmutablePair.of("xyz", "bar"))); + + ImmutablePair p = ImmutablePair.of("foo", "bar"); + assertTrue(p.equals(p)); + assertFalse(p.equals(new Object())); + } + + @Test + public void testHashCode() throws Exception { + assertEquals(ImmutablePair.of(null, "foo").hashCode(), ImmutablePair.of(null, "foo").hashCode()); + } + + @Test + public void testToString() throws Exception { + assertEquals("ImmutablePair(null,null)", ImmutablePair.of(null, null).toString()); + assertEquals("ImmutablePair(null,two)", ImmutablePair.of(null, "two").toString()); + assertEquals("ImmutablePair(one,null)", ImmutablePair.of("one", null).toString()); + assertEquals("ImmutablePair(one,two)", ImmutablePair.of("one", "two").toString()); + } + + @Test + @SuppressWarnings("unchecked") + public void testSerialization() throws Exception { + ImmutablePair origPair = ImmutablePair.of(0, "foo"); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(origPair); + ImmutablePair deserializedPair = (ImmutablePair) new ObjectInputStream( + new ByteArrayInputStream(baos.toByteArray())).readObject(); + assertEquals(origPair, deserializedPair); + assertEquals(origPair.hashCode(), deserializedPair.hashCode()); + } +} diff --git a/src/test/java/org/apache/commons/lang3/MutablePairTest.java b/src/test/java/org/apache/commons/lang3/MutablePairTest.java new file mode 100644 index 000000000..79649e6cb --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/MutablePairTest.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNull; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import org.junit.Test; + +/** + * Test the MutablePair class. + * @author Matt Benson + * @version $Id$ + */ +public class MutablePairTest { + + @Test + public void testBasic() throws Exception { + MutablePair pair = new MutablePair(0, "foo"); + assertEquals(0, pair.getLeftElement().intValue()); + assertEquals("foo", pair.getRightElement()); + MutablePair pair2 = new MutablePair(null, "bar"); + assertNull(pair2.getLeftElement()); + assertEquals("bar", pair2.getRightElement()); + } + + @Test + public void testDefault() throws Exception { + MutablePair pair = new MutablePair(); + assertNull(pair.getLeftElement()); + assertNull(pair.getRightElement()); + } + + @Test + public void testMutate() throws Exception { + MutablePair pair = new MutablePair(0, "foo"); + pair.setLeftElement(42); + pair.setRightElement("bar"); + assertEquals(42, pair.getLeftElement().intValue()); + assertEquals("bar", pair.getRightElement()); + } + + @Test + public void testPairOf() throws Exception { + MutablePair pair = MutablePair.of(0, "foo"); + assertEquals(0, pair.getLeftElement().intValue()); + assertEquals("foo", pair.getRightElement()); + MutablePair pair2 = MutablePair.of(null, "bar"); + assertNull(pair2.getLeftElement()); + assertEquals("bar", pair2.getRightElement()); + } + + @Test + public void testEquals() throws Exception { + assertEquals(MutablePair.of(null, "foo"), MutablePair.of(null, "foo")); + assertFalse(MutablePair.of("foo", 0).equals(MutablePair.of("foo", null))); + assertFalse(MutablePair.of("foo", "bar").equals(MutablePair.of("xyz", "bar"))); + + MutablePair p = MutablePair.of("foo", "bar"); + assertTrue(p.equals(p)); + assertFalse(p.equals(new Object())); + } + + @Test + public void testHashCode() throws Exception { + assertEquals(MutablePair.of(null, "foo").hashCode(), MutablePair.of(null, "foo").hashCode()); + } + + @Test + public void testToString() throws Exception { + assertEquals("MutablePair(null,null)", MutablePair.of(null, null).toString()); + assertEquals("MutablePair(null,two)", MutablePair.of(null, "two").toString()); + assertEquals("MutablePair(one,null)", MutablePair.of("one", null).toString()); + assertEquals("MutablePair(one,two)", MutablePair.of("one", "two").toString()); + } + + @Test + @SuppressWarnings("unchecked") + public void testSerialization() throws Exception { + MutablePair origPair = MutablePair.of(0, "foo"); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(origPair); + MutablePair deserializedPair = (MutablePair) new ObjectInputStream( + new ByteArrayInputStream(baos.toByteArray())).readObject(); + assertEquals(origPair, deserializedPair); + assertEquals(origPair.hashCode(), deserializedPair.hashCode()); + } +} diff --git a/src/test/java/org/apache/commons/lang3/PairTest.java b/src/test/java/org/apache/commons/lang3/PairTest.java index c6d40e296..730e7dfa8 100644 --- a/src/test/java/org/apache/commons/lang3/PairTest.java +++ b/src/test/java/org/apache/commons/lang3/PairTest.java @@ -18,13 +18,10 @@ package org.apache.commons.lang3; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; +import java.util.HashSet; import org.junit.Test; @@ -35,60 +32,30 @@ import org.junit.Test; */ public class PairTest { - @Test - public void testBasic() throws Exception { - Pair pair = new Pair(0, "foo"); - assertEquals(0, pair.left.intValue()); - assertEquals("foo", pair.right); - Pair pair2 = new Pair(null, "bar"); - assertNull(pair2.left); - assertEquals("bar", pair2.right); - } - @Test public void testPairOf() throws Exception { Pair pair = Pair.of(0, "foo"); - assertEquals(0, pair.left.intValue()); - assertEquals("foo", pair.right); + assertTrue(pair instanceof ImmutablePair); + assertEquals(0, ((ImmutablePair) pair).left.intValue()); + assertEquals("foo", ((ImmutablePair) pair).right); Pair pair2 = Pair.of(null, "bar"); - assertNull(pair2.left); - assertEquals("bar", pair2.right); + assertTrue(pair2 instanceof ImmutablePair); + assertNull(((ImmutablePair) pair2).left); + assertEquals("bar", ((ImmutablePair) pair2).right); } @Test - public void testEquals() throws Exception { - assertEquals(Pair.of(null, "foo"), Pair.of(null, "foo")); - assertFalse(Pair.of("foo", 0).equals(Pair.of("foo", null))); - assertFalse(Pair.of("foo", "bar").equals(Pair.of("xyz", "bar"))); + public void testCompatibility() throws Exception { + Pair pair = ImmutablePair.of(0, "foo"); + Pair pair2 = MutablePair.of(0, "foo"); + assertEquals(pair, pair2); + assertEquals(pair.hashCode(), pair2.hashCode()); + HashSet> set = new HashSet>(); + set.add(pair); + assertTrue(set.contains(pair2)); - Pair p = Pair.of("foo", "bar"); - assertTrue(p.equals(p)); - assertFalse(p.equals(new Object())); - } - - @Test - public void testHashCode() throws Exception { - assertEquals(Pair.of(null, "foo").hashCode(), Pair.of(null, "foo").hashCode()); - } - - @Test - public void testToString() throws Exception { - assertEquals("(null,null)", Pair.of(null, null).toString()); - assertEquals("(null,two)", Pair.of(null, "two").toString()); - assertEquals("(one,null)", Pair.of("one", null).toString()); - assertEquals("(one,two)", Pair.of("one", "two").toString()); - } - - @Test - @SuppressWarnings("unchecked") - public void testSerialization() throws Exception { - Pair origPair = Pair.of(0, "foo"); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream out = new ObjectOutputStream(baos); - out.writeObject(origPair); - Pair deserializedPair = (Pair) new ObjectInputStream( - new ByteArrayInputStream(baos.toByteArray())).readObject(); - assertEquals(origPair, deserializedPair); - assertEquals(origPair.hashCode(), deserializedPair.hashCode()); + pair2.setValue("bar"); + assertFalse(pair.equals(pair2)); + assertFalse(pair.hashCode() == pair2.hashCode()); } }