diff --git a/src/main/java/org/cactoos/collection/Immutable.java b/src/main/java/org/cactoos/collection/Immutable.java index fa682e8c8e..a3265130a8 100644 --- a/src/main/java/org/cactoos/collection/Immutable.java +++ b/src/main/java/org/cactoos/collection/Immutable.java @@ -37,6 +37,11 @@ * @todo #898:30min Replace all the Collections.unmodifiableCollection * with the {@link org.cactoos.collection.Immutable} from the cactoos codebase. * That should be done because Elegant Object principles are against static methods. + * @todo #1224:30min Original collection should be copied inside this + * immutable decorator. That should be done because otherwise true immutability + * cannot be achieved. + * see: https://github.com/yegor256/cactoos/issues/1224 and + * https://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html */ @SuppressWarnings( { diff --git a/src/main/java/org/cactoos/list/Immutable.java b/src/main/java/org/cactoos/list/Immutable.java index b345c1fa59..2e6eba6e7d 100644 --- a/src/main/java/org/cactoos/list/Immutable.java +++ b/src/main/java/org/cactoos/list/Immutable.java @@ -25,9 +25,19 @@ import java.util.Collection; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import org.cactoos.Scalar; import org.cactoos.collection.Sliced; +import org.cactoos.iterable.IterableOf; +import org.cactoos.scalar.And; +import org.cactoos.scalar.Folded; +import org.cactoos.scalar.Or; +import org.cactoos.scalar.SumOfInt; +import org.cactoos.scalar.Unchecked; +import org.cactoos.text.TextOf; +import org.cactoos.text.UncheckedText; /** * {@link List} envelope that doesn't allow mutations. @@ -36,6 +46,7 @@ * * @param Element type * @since 1.16 + * @checkstyle ClassDataAbstractionCouplingCheck (500 lines) * @todo #898:30min Replace all the Collections.unmodifiableList * with the {@link org.cactoos.list.Immutable} from the cactoos codebase. * That should be done because Elegant Object principles are against static methods. @@ -54,12 +65,21 @@ public final class Immutable implements List { */ private final List list; + /** + * Ctor. + * @param items Source array + */ + @SafeVarargs + public Immutable(final T... items) { + this(new IterableOf<>(items)); + } + /** * Ctor. * @param src Source collection */ public Immutable(final Collection src) { - this(new ListOf<>(src)); + this(new IterableOf<>(src.iterator())); } /** @@ -67,7 +87,29 @@ public Immutable(final Collection src) { * @param src Source list */ public Immutable(final List src) { - this.list = new ListOf<>(src); + this(new IterableOf<>(src.iterator())); + } + + /** + * Ctor. + * @param src Source iterable + */ + public Immutable(final Iterable src) { + this(() -> { + final List copy = new LinkedList<>(); + for (final T item : src) { + copy.add(item); + } + return copy; + }); + } + + /** + * Ctor. + * @param slr The scalar + */ + public Immutable(final Scalar> slr) { + this.list = new Unchecked<>(slr).value(); } @Override @@ -213,17 +255,46 @@ public List subList(final int start, final int end) { } @Override - public String toString() { - return this.list.toString(); + @edu.umd.cs.findbugs.annotations.SuppressFBWarnings("EQ_UNUSUAL") + public boolean equals(final Object other) { + return new Unchecked<>( + new Or( + () -> other == this, + new And( + () -> other != null, + () -> List.class.isAssignableFrom(other.getClass()), + () -> { + final List compared = (List) other; + final Iterator iterator = compared.iterator(); + return new Unchecked<>( + new And( + (T input) -> input.equals(iterator.next()), + this + ) + ).value(); + } + ) + ) + ).value(); } + // @checkstyle MagicNumberCheck (30 lines) @Override public int hashCode() { - return this.list.hashCode(); + return new Unchecked<>( + new Folded<>( + 42, + (hash, entry) -> new SumOfInt( + () -> 37 * hash, + entry::hashCode + ).value(), + this + ) + ).value(); } @Override - public boolean equals(final Object obj) { - return this.list.equals(obj); + public String toString() { + return new UncheckedText(new TextOf(this)).asString(); } } diff --git a/src/test/java/org/cactoos/list/ImmutableTest.java b/src/test/java/org/cactoos/list/ImmutableTest.java index a7bf8e9531..801d49531e 100644 --- a/src/test/java/org/cactoos/list/ImmutableTest.java +++ b/src/test/java/org/cactoos/list/ImmutableTest.java @@ -28,6 +28,7 @@ import java.util.List; import org.cactoos.collection.CollectionOf; import org.hamcrest.core.IsEqual; +import org.hamcrest.core.IsNot; import org.junit.Test; import org.llorllale.cactoos.matchers.Assertion; import org.llorllale.cactoos.matchers.HasValues; @@ -407,25 +408,58 @@ public void immutableSubList() { } @Test - public void testToString() { + public void notEqualsToObjectOfAnotherType() { new Assertion<>( - "toString() must be equals to original", - new Immutable<>( - new ListOf<>("a", "b", "c") - ).toString(), - new IsEqual<>( - new ListOf<>("a", "b", "c").toString() + "must not equal to object of another type", + new Immutable<>(), + new IsNot<>(new IsEqual<>(new Object())) + ).affirm(); + } + + @Test + public void notEqualsToListWithDifferentElements() { + new Assertion<>( + "must not equal to List with different elements", + new Immutable<>(1, 2), + new IsNot<>( + new IsEqual<>(new ListOf<>(1, 0)) ) ).affirm(); } + @Test + public void isEqualToItself() { + final List list = new Immutable<>(1, 2); + new Assertion<>( + "must be equal to itself", + list, + new IsEqual<>(list) + ).affirm(); + } + + @Test + public void isEqualToListWithTheSameElements() { + new Assertion<>( + "must be equal to List with the same elements", + new Immutable<>(1, 2), + new IsEqual<>(new ListOf<>(1, 2)) + ).affirm(); + } + + @Test + public void equalToEmptyImmutable() { + new Assertion<>( + "empty Immutable must be equal to empty Immutable", + new Immutable<>(), + new IsEqual<>(new Immutable<>()) + ).affirm(); + } + @Test public void testHashCode() { new Assertion<>( - "hashCode() must be equals to original", - new Immutable<>( - new ListOf<>(1, 2, 3) - ).hashCode(), + "hashCode() must be equal to hashCode of the corresponding List", + new Immutable<>(1, 2, 3).hashCode(), new IsEqual<>( new ListOf<>(1, 2, 3).hashCode() ) @@ -433,15 +467,12 @@ public void testHashCode() { } @Test - public void testEquals() { - final ListOf another = new ListOf<>(4, 5, 6); + public void testToString() { new Assertion<>( - "equals() must be equals to original", - new Immutable<>( - new ListOf<>(1, 2, 3) - ).equals(another), + "toString() must be concatenation of nested elements", + new Immutable<>("a", "b", "c").toString(), new IsEqual<>( - new ListOf<>(1, 2, 3).equals(another) + "a, b, c" ) ).affirm(); }