Coverage Report - com.jcabi.immutable.Array
 
Classes in this File Line Coverage Branch Coverage Complexity
Array
42%
46/109
30%
14/46
2.118
 
 1  
 /**
 2  
  * Copyright (c) 2012-2014, jcabi.com
 3  
  * All rights reserved.
 4  
  *
 5  
  * Redistribution and use in source and binary forms, with or without
 6  
  * modification, are permitted provided that the following conditions
 7  
  * are met: 1) Redistributions of source code must retain the above
 8  
  * copyright notice, this list of conditions and the following
 9  
  * disclaimer. 2) Redistributions in binary form must reproduce the above
 10  
  * copyright notice, this list of conditions and the following
 11  
  * disclaimer in the documentation and/or other materials provided
 12  
  * with the distribution. 3) Neither the name of the jcabi.com nor
 13  
  * the names of its contributors may be used to endorse or promote
 14  
  * products derived from this software without specific prior written
 15  
  * permission.
 16  
  *
 17  
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  
  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 19  
  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 20  
  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 21  
  * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 22  
  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 23  
  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 24  
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 25  
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 26  
  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 27  
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 28  
  * OF THE POSSIBILITY OF SUCH DAMAGE.
 29  
  */
 30  
 package com.jcabi.immutable;
 31  
 
 32  
 import com.jcabi.aspects.Immutable;
 33  
 import com.jcabi.aspects.Loggable;
 34  
 import java.util.Arrays;
 35  
 import java.util.Collection;
 36  
 import java.util.Collections;
 37  
 import java.util.Iterator;
 38  
 import java.util.LinkedList;
 39  
 import java.util.List;
 40  
 import java.util.ListIterator;
 41  
 
 42  
 /**
 43  
  * Array as an object.
 44  
  *
 45  
  * <p>This class is truly immutable. This means that it never changes
 46  
  * its encapsulated values and is annotated with {@code &#64;Immutable}
 47  
  * annotation.
 48  
  *
 49  
  * @param <T> Value key type
 50  
  * @author Yegor Bugayenko (yegor@tpc2.com)
 51  
  * @version $Id$
 52  
  * @since 0.1
 53  
  */
 54  
 @Immutable
 55  
 @Loggable(Loggable.DEBUG)
 56  
 @SuppressWarnings({ "unchecked", "PMD.TooManyMethods" })
 57  
 public final class Array<T> implements List<T> {
 58  
 
 59  
     /**
 60  
      * All values.
 61  
      */
 62  
     @Immutable.Array
 63  
     private final transient T[] values;
 64  
 
 65  
     /**
 66  
      * Public ctor, for an zero-length empty array.
 67  
      */
 68  
     public Array() {
 69  3
         this((T[]) new Object[0]);
 70  3
     }
 71  
 
 72  
     /**
 73  
      * Public ctor, from an array of values.
 74  
      * @param list Items to encapsulate
 75  
      */
 76  26
     public Array(final T... list) {
 77  26
         this.values = list;
 78  26
     }
 79  
 
 80  
     /**
 81  
      * Public ctor.
 82  
      * @param list Items to encapsulate
 83  
      * @since 0.12
 84  
      */
 85  2
     public Array(final Iterable<T> list) {
 86  2
         if (list == null) {
 87  0
             throw new IllegalArgumentException(
 88  
                 "list of objects can't be NULL"
 89  
             );
 90  
         }
 91  2
         if (list instanceof Array) {
 92  0
             this.values = ((Array<T>) list).values;
 93  2
         } else if (list instanceof Collection) {
 94  2
             final Collection<T> col = Collection.class.cast(list);
 95  2
             this.values = (T[]) new Object[col.size()];
 96  2
             col.toArray(this.values);
 97  2
         } else {
 98  0
             final Collection<T> items = new LinkedList<T>();
 99  0
             for (final T item : list) {
 100  0
                 items.add(item);
 101  0
             }
 102  0
             this.values = (T[]) new Object[items.size()];
 103  0
             items.toArray(this.values);
 104  
         }
 105  2
     }
 106  
 
 107  
     /**
 108  
      * Make a new one with an extra entry, at the end of array (will be
 109  
      * extended by one extra element).
 110  
      * @param value The value
 111  
      * @return New vector
 112  
      */
 113  
     public Array<T> with(final T value) {
 114  9
         if (value == null) {
 115  0
             throw new IllegalArgumentException(
 116  
                 "argument of Array#with() can't be NULL"
 117  
             );
 118  
         }
 119  9
         final T[] items = (T[]) new Object[this.values.length + 1];
 120  9
         System.arraycopy(this.values, 0, items, 0, this.values.length);
 121  9
         items[this.values.length] = value;
 122  9
         return new Array<T>(items);
 123  
     }
 124  
 
 125  
     /**
 126  
      * Make a new extra entries, at the end of array.
 127  
      * @param vals The values
 128  
      * @return New vector
 129  
      */
 130  
     public Array<T> with(final Iterable<T> vals) {
 131  0
         if (vals == null) {
 132  0
             throw new IllegalArgumentException(
 133  
                 "arguments of Array#with() can't be NULL"
 134  
             );
 135  
         }
 136  
         final Array<T> array;
 137  0
         if (vals instanceof Collection) {
 138  0
             final T[] items = (T[]) new Object[
 139  
                 this.values.length + Collection.class.cast(vals).size()
 140  
             ];
 141  0
             System.arraycopy(this.values, 0, items, 0, this.values.length);
 142  0
             int idx = this.values.length;
 143  0
             for (final T value : vals) {
 144  0
                 items[idx] = value;
 145  0
                 ++idx;
 146  0
             }
 147  0
             array = new Array<T>(items);
 148  0
         } else {
 149  0
             final Collection<T> list = new LinkedList<T>();
 150  0
             list.addAll(Arrays.asList(this.values));
 151  0
             for (final T value : vals) {
 152  0
                 list.add(value);
 153  0
             }
 154  0
             array = new Array<T>(list);
 155  
         }
 156  0
         return array;
 157  
     }
 158  
 
 159  
     /**
 160  
      * Make a new one with an extra entry at the given position.
 161  
      * @param pos Position to replace
 162  
      * @param value The value
 163  
      * @return New array
 164  
      */
 165  
     public Array<T> with(final int pos, final T value) {
 166  3
         if (value == null) {
 167  0
             throw new IllegalArgumentException(
 168  
                 "second argument of Array#with() can't be NULL"
 169  
             );
 170  
         }
 171  3
         final T[] temp = (T[]) new Object[
 172  
             Math.max(this.values.length, pos + 1)
 173  
         ];
 174  3
         System.arraycopy(this.values, 0, temp, 0, this.values.length);
 175  3
         temp[pos] = value;
 176  3
         return new Array<T>(temp);
 177  
     }
 178  
 
 179  
     /**
 180  
      * Make a new array, without this element.
 181  
      *
 182  
      * <p>The method throws {@link ArrayIndexOutOfBoundsException} if such
 183  
      * position is absent in the array.
 184  
      *
 185  
      * @param idx The position to remove
 186  
      * @return New array
 187  
      */
 188  
     public Array<T> without(final int idx) {
 189  4
         if (idx >= this.values.length) {
 190  0
             throw new ArrayIndexOutOfBoundsException(
 191  
                 String.format(
 192  
                     "index %d is out of bounds: [0..%d]",
 193  
                     idx, this.values.length
 194  
                 )
 195  
             );
 196  
         }
 197  4
         if (idx < 0) {
 198  0
             throw new ArrayIndexOutOfBoundsException(
 199  
                 String.format("index can't be negative: %d", idx)
 200  
             );
 201  
         }
 202  4
         final T[] items = (T[]) new Object[this.values.length - 1];
 203  4
         System.arraycopy(this.values, 0, items, 0, idx);
 204  4
         System.arraycopy(
 205  
             this.values, idx + 1, items, idx, this.values.length - idx - 1
 206  
         );
 207  4
         return new Array<T>(items);
 208  
     }
 209  
 
 210  
     /**
 211  
      * Make a new array, without this element (or the same array if such
 212  
      * an element is absent).
 213  
      * @param item The element to remove
 214  
      * @return New array
 215  
      * @since 1.4
 216  
      */
 217  
     public Array<T> less(final T item) {
 218  3
         int idx = -1;
 219  7
         for (int pos = 0; pos < this.values.length; ++pos) {
 220  5
             if (this.values[pos].equals(item)) {
 221  1
                 idx = pos;
 222  1
                 break;
 223  
             }
 224  
         }
 225  
         final Array<T> array;
 226  3
         if (idx >= 0) {
 227  1
             array = this.without(idx);
 228  
         } else {
 229  2
             array = this;
 230  
         }
 231  3
         return array;
 232  
     }
 233  
 
 234  
     @Override
 235  
     public int hashCode() {
 236  0
         return Arrays.hashCode(this.values);
 237  
     }
 238  
 
 239  
     @Override
 240  
     public boolean equals(final Object object) {
 241  0
         return object instanceof Array
 242  
             && Arrays.deepEquals(this.values, Array.class.cast(object).values);
 243  
     }
 244  
 
 245  
     @Override
 246  
     public String toString() {
 247  0
         final StringBuilder text = new StringBuilder(0);
 248  0
         for (final T item : this.values) {
 249  0
             if (text.length() > 0) {
 250  0
                 text.append(", ");
 251  
             }
 252  0
             text.append(item);
 253  
         }
 254  0
         return text.toString();
 255  
     }
 256  
 
 257  
     @Override
 258  
     public int size() {
 259  2
         return this.values.length;
 260  
     }
 261  
 
 262  
     @Override
 263  
     public boolean isEmpty() {
 264  1
         return this.values.length == 0;
 265  
     }
 266  
 
 267  
     @Override
 268  
     public boolean contains(final Object key) {
 269  0
         return Arrays.asList(this.values).contains(key);
 270  
     }
 271  
 
 272  
     @Override
 273  
     public Iterator<T> iterator() {
 274  4
         return Collections.unmodifiableList(
 275  
             Arrays.asList(this.values)
 276  
         ).iterator();
 277  
     }
 278  
 
 279  
     @Override
 280  
     public Object[] toArray() {
 281  0
         final Object[] array = new Object[this.values.length];
 282  0
         System.arraycopy(this.values, 0, array, 0, this.values.length);
 283  0
         return array;
 284  
     }
 285  
 
 286  
     @Override
 287  
     public <T> T[] toArray(final T[] array) {
 288  
         final T[] target;
 289  0
         if (array.length == this.values.length) {
 290  0
             target = array;
 291  
         } else {
 292  0
             target = (T[]) new Object[this.values.length];
 293  
         }
 294  0
         System.arraycopy(this.values, 0, target, 0, this.values.length);
 295  0
         return target;
 296  
     }
 297  
 
 298  
     @Override
 299  
     public boolean add(final T element) {
 300  0
         throw new UnsupportedOperationException(
 301  
             "add(): Array is immutable"
 302  
         );
 303  
     }
 304  
 
 305  
     @Override
 306  
     public boolean remove(final Object obj) {
 307  0
         throw new UnsupportedOperationException(
 308  
             "remove(): Array is immutable"
 309  
         );
 310  
     }
 311  
 
 312  
     @Override
 313  
     public boolean containsAll(final Collection<?> col) {
 314  0
         return Arrays.asList(this.values).containsAll(col);
 315  
     }
 316  
 
 317  
     @Override
 318  
     public boolean addAll(final Collection<? extends T> col) {
 319  0
         throw new UnsupportedOperationException(
 320  
             "addAll(): Array is immutable"
 321  
         );
 322  
     }
 323  
 
 324  
     @Override
 325  
     public boolean retainAll(final Collection<?> col) {
 326  0
         throw new UnsupportedOperationException(
 327  
             "retainAll(): Array is immutable"
 328  
         );
 329  
     }
 330  
 
 331  
     @Override
 332  
     public boolean removeAll(final Collection<?> col) {
 333  0
         throw new UnsupportedOperationException(
 334  
             "removeAll(): Array is immutable"
 335  
         );
 336  
     }
 337  
 
 338  
     @Override
 339  
     public void clear() {
 340  0
         throw new UnsupportedOperationException(
 341  
             "clear(): Array is immutable"
 342  
         );
 343  
     }
 344  
 
 345  
     @Override
 346  
     public boolean addAll(final int index, final Collection<? extends T> col) {
 347  0
         throw new UnsupportedOperationException(
 348  
             "addAll(): Array is immutable, can't change"
 349  
         );
 350  
     }
 351  
 
 352  
     @Override
 353  
     public T get(final int index) {
 354  0
         if (index < 0 || index >= this.values.length) {
 355  0
             throw new IndexOutOfBoundsException(
 356  
                 String.format(
 357  
                     "index %d is out of bounds, length=%d",
 358  
                     index,
 359  
                     this.values.length
 360  
                 )
 361  
             );
 362  
         }
 363  0
         return this.values[index];
 364  
     }
 365  
 
 366  
     @Override
 367  
     public T set(final int index, final T element) {
 368  0
         throw new UnsupportedOperationException(
 369  
             "set(idx): Array is immutable"
 370  
         );
 371  
     }
 372  
 
 373  
     @Override
 374  
     public void add(final int index, final T element) {
 375  0
         throw new UnsupportedOperationException(
 376  
             "add(idx): Array is immutable"
 377  
         );
 378  
     }
 379  
 
 380  
     @Override
 381  
     public T remove(final int index) {
 382  0
         throw new UnsupportedOperationException(
 383  
             "remove(idx): Array is immutable"
 384  
         );
 385  
     }
 386  
 
 387  
     @Override
 388  
     public int indexOf(final Object obj) {
 389  4
         return Arrays.asList(this.values).indexOf(obj);
 390  
     }
 391  
 
 392  
     @Override
 393  
     public int lastIndexOf(final Object obj) {
 394  0
         return Arrays.asList(this.values).lastIndexOf(obj);
 395  
     }
 396  
 
 397  
     @Override
 398  
     public ListIterator<T> listIterator() {
 399  1
         return Collections.unmodifiableList(
 400  
             Arrays.asList(this.values)
 401  
         ).listIterator();
 402  
     }
 403  
 
 404  
     @Override
 405  
     public ListIterator<T> listIterator(final int index) {
 406  1
         return Collections.unmodifiableList(
 407  
             Arrays.asList(this.values)
 408  
         ).listIterator(index);
 409  
     }
 410  
 
 411  
     @Override
 412  
     public List<T> subList(final int from, final int till) {
 413  1
         return Collections.unmodifiableList(
 414  
             Arrays.asList(this.values).subList(from, till)
 415  
         );
 416  
     }
 417  
 
 418  
 }