Tuple.java

  1. package de.turnertech.tuples;

  2. import java.io.StringWriter;
  3. import java.util.Arrays;
  4. import java.util.Collection;
  5. import java.util.Iterator;
  6. import java.util.LinkedList;
  7. import java.util.List;
  8. import java.util.NoSuchElementException;
  9. import java.util.Objects;

  10. /**
  11.  * <p>n-Tuple, the core of this package. This class models the concept of an untyped
  12.  * n-Tuple of any length. It provides basic functionality for creation and reading
  13.  * of immutable Tuples. It implements the Collection interface in a read only
  14.  * manner. Any calls to functions which would modify the collection will throw
  15.  * exceptions.</p>
  16.  *
  17.  * <p>This implementation does not support null elements!</p>
  18.  */
  19. public class Tuple implements Collection<Object> {
  20.    
  21.     private class TupleIterator implements Iterator<Object> {

  22.         private int currentIndex;

  23.         private final Object[] elements;

  24.         public TupleIterator(Tuple tuple) {
  25.             this.elements = tuple.flatten().toArray();
  26.             currentIndex = 0;
  27.         }

  28.         @Override
  29.         public boolean hasNext() {
  30.             return currentIndex < elements.length;
  31.         }

  32.         @Override
  33.         public Object next() {
  34.             if(currentIndex >= elements.length) {
  35.                 throw new NoSuchElementException("Attempted to iterate beyond the end of a tuple.");
  36.             }
  37.             return elements[currentIndex++];
  38.         }
  39.        
  40.     }

  41.     private final Object[] elements;

  42.     /**
  43.      * Create an empty tuple
  44.      * @return an empty tuple
  45.      */
  46.     public static Tuple0 from() {
  47.         return new Tuple0();
  48.     }

  49.     /**
  50.      * Create a single tuple
  51.      * @param <A> the type of element0
  52.      * @param element0 the element to store
  53.      * @return the tuple
  54.      */
  55.     public static <A> Tuple1<A> from(A element0) {
  56.         return new Tuple1<>(element0);
  57.     }

  58.     /**
  59.      * Create a double tuple
  60.      * @param <A> the type of element0
  61.      * @param <B> the type of element1
  62.      * @param element0 the element to store
  63.      * @param element1 the element to store
  64.      * @return the tuple
  65.      */
  66.     public static <A,B> Tuple2<A,B> from(A element0, B element1) {
  67.         return new Tuple2<>(element0, element1);
  68.     }

  69.     /**
  70.      * Create a triple tuple
  71.      * @param <A> the type of element0
  72.      * @param <B> the type of element1
  73.      * @param <C> the type of element2
  74.      * @param element0 the element to store
  75.      * @param element1 the element to store
  76.      * @param element2 the element to store
  77.      * @return the tuple
  78.      */
  79.     public static <A,B,C> Tuple3<A,B,C> from(A element0, B element1, C element2) {
  80.         return new Tuple3<>(element0, element1, element2);
  81.     }

  82.     /**
  83.      * Constructs a fixed length, non typed tuple.
  84.      *
  85.      * @param elements the elements
  86.      */
  87.     public Tuple(Object... elements) {
  88.         if(elements == null || elements.length == 0) {
  89.             this.elements = new Object[0];
  90.         } else {
  91.             for(Object element : elements) {
  92.                 Objects.requireNonNull(element);
  93.             }
  94.             this.elements = elements;
  95.         }
  96.     }

  97.     /**
  98.      * Can be considered as tuple.flatten().get(...)
  99.      * <pre>
  100.      *(4, (), 8, (((), 6))).get(1) == 8
  101.      * </pre>
  102.      * @param index of the next non null element in the tuple.
  103.      * @return the non null element in the tuple.
  104.      */
  105.     public Object get(int index) {
  106.         if(index < 0 || index >= size()) {
  107.             throw new IndexOutOfBoundsException(index);
  108.         }
  109.         final TupleIterator iterator = new TupleIterator(this);
  110.         for(int i = 0; i < index; ++i) {
  111.             iterator.next();
  112.         }
  113.         return iterator.next();
  114.     }

  115.     /**
  116.      * Gets the element at the index in this tuple, such that:
  117.      * <pre>
  118.      *(4, (), 8, (((), 6))).getElement(1) == ()
  119.      * </pre>
  120.      * @param index the next element in the Tuple
  121.      * @return the element in the Tuple
  122.      */
  123.     public Object getElement(int index) {
  124.         if(index < 0 || index >= length()) {
  125.             throw new IndexOutOfBoundsException(index);
  126.         }
  127.         return elements[index];
  128.     }

  129.     /**
  130.      * Equivelant to tuple.flatten().size() == 0
  131.      */
  132.     public boolean isEmpty() {
  133.         return flatten().elements.length == 0;
  134.     }

  135.     private void flatten(List<Object> out) {
  136.         for(Object element : elements) {
  137.             if(element instanceof Tuple) {
  138.                 ((Tuple)element).flatten(out);
  139.             } else {
  140.                 out.add(element);
  141.             }
  142.         }
  143.     }

  144.     /**
  145.      * <p>Removes all Tuples contained in this Tuple, and return a single "flat" Tuple. For example:</p>
  146.      * <pre>
  147.      *myTuple.toString() = (4, (), 8, (((), 6)))
  148.      *myTuple.flatten().toString() = (4, 8, 6)
  149.      * </pre>
  150.      *
  151.      * @return A tuple containing no sub tuples
  152.      */
  153.     public Tuple flatten() {
  154.         LinkedList<Object> returnCollection = new LinkedList<>();
  155.         this.flatten(returnCollection);
  156.         return new Tuple(returnCollection.toArray());
  157.     }

  158.     /**
  159.      * Uses Objects.toString() to represent this tuple in the for ((), 2, SomeString, etc)
  160.      */
  161.     @Override
  162.     public String toString() {
  163.         final StringWriter stringWriter = new StringWriter();
  164.         stringWriter.write("(");
  165.         if(elements.length > 0) {
  166.             stringWriter.write(Objects.toString(elements[0], "()"));
  167.         }
  168.         for(int i = 1; i < elements.length; ++i) {
  169.             stringWriter.write(", ");
  170.             stringWriter.write(Objects.toString(elements[i], "()"));
  171.         }
  172.         stringWriter.write(")");
  173.         return stringWriter.toString();
  174.     }

  175.     /**
  176.      * Contrary to the rest of the functions, this implementation checks equality
  177.      * of the underlying array, and not equality in terms of "Tuple Equality".
  178.      * e.g. ((), 5) != (5, ()).
  179.      */
  180.     @Override
  181.     public int hashCode() {
  182.         final int prime = 31;
  183.         int result = 1;
  184.         result = prime * result + Arrays.deepHashCode(elements);
  185.         return result;
  186.     }

  187.     /**
  188.      * Contrary to the rest of the functions, this implementation checks equality
  189.      * of the underlying array, and not equality in terms of "Tuple Equality".
  190.      * e.g. ((), 5) != (5, ()).
  191.      */
  192.     @Override
  193.     public boolean equals(Object obj) {
  194.         if (this == obj)
  195.             return true;
  196.         if (obj == null)
  197.             return false;
  198.         if (getClass() != obj.getClass())
  199.             return false;
  200.         Tuple other = (Tuple) obj;
  201.         return Arrays.deepEquals(elements, other.elements);
  202.     }

  203.     /**
  204.      * <p>Note that the size of a Tuple is not the same as for other collections! For a Tuple,
  205.      * the size of the collection is the count of elements contained in this and all sub tuples.
  206.      * For example:</p>
  207.      * <pre>
  208.      *((), 8, ((5)), ()).size() == 2
  209.      * </pre>
  210.      * @see #length()
  211.      * {@inheritDoc}
  212.      */
  213.     @Override
  214.     public int size() {
  215.         int returnSize = 0;
  216.         for(Object element : elements) {
  217.             returnSize += element instanceof Tuple ? ((Tuple)element).size() : 1;
  218.         }
  219.         return returnSize;
  220.     }

  221.     /**
  222.      * <p>Gets the length of the Tuple</p>
  223.      * <pre>
  224.      *((), 8, ((5)), ()).length() == 4
  225.      * </pre>
  226.      * @see #size()
  227.      * @return the length of the tuple
  228.      */
  229.     public int length() {
  230.         return elements.length;
  231.     }

  232.     /**
  233.      * Equivelant to tuple.flatten().contains(...)
  234.      */
  235.     @Override
  236.     public boolean contains(Object o) {
  237.         for(Object element : elements) {
  238.             if(element instanceof Tuple) {
  239.                 if(((Tuple)element).contains(o)) {
  240.                     return true;
  241.                 }
  242.             } else {
  243.                 if(Objects.equals(element, o)) {
  244.                     return true;
  245.                 }
  246.             }
  247.         }
  248.         return false;
  249.     }

  250.     /**
  251.      * Equivelant to tuple.flatten().containsAll(...)
  252.      */
  253.     @Override
  254.     public boolean containsAll(Collection<?> c) {
  255.         return Arrays.asList(this.flatten().toArray()).containsAll(Objects.requireNonNull(c));
  256.     }

  257.     /**
  258.      * <p>Returns a "Tuple Aware" iterator which ignores empty tuples, and iterates through child Tuples. For
  259.      * example given the Tuple ((9, ((), 1)), 1), this Iterator will respond with:</p>
  260.      * <pre>
  261.      *iter.next() == 9
  262.      *iter.next() == 1
  263.      *iter.next() == 1
  264.      *iter.next() throws NoSuchElementException
  265.      * </pre>
  266.      */
  267.     @Override
  268.     public Iterator<Object> iterator() {
  269.         return new TupleIterator(this);
  270.     }

  271.     /**
  272.      * Returns the raw contents of the Tuple, such that they could be used to create another equal Tuple.
  273.      * For example, given the Tuple ((), 1), this function will return an Object[]{Tuple0, 1}.
  274.      */
  275.     @Override
  276.     public Object[] toArray() {
  277.         return elements;
  278.     }

  279.     /**
  280.      * Tuples are multi-typed and do not support 'toArray'
  281.      */
  282.     @Override
  283.     public <T> T[] toArray(T[] a) {
  284.         throw new UnsupportedOperationException("Tuples are multi-typed and do not support 'toArray'");
  285.     }

  286.     /**
  287.      * Tuples are immutable and do not support 'add'
  288.      */
  289.     @Override
  290.     public boolean add(Object e) {
  291.         throw new UnsupportedOperationException("Tuples are immutable and do not support 'add'");
  292.     }

  293.     /**
  294.      * Tuples are immutable and do not support 'remove'
  295.      */
  296.     @Override
  297.     public boolean remove(Object o) {
  298.         throw new UnsupportedOperationException("Tuples are immutable and do not support 'remove'");
  299.     }

  300.     /**
  301.      * Tuples are immutable and do not support 'addAll'
  302.      */
  303.     @Override
  304.     public boolean addAll(Collection<? extends Object> c) {
  305.         throw new UnsupportedOperationException("Tuples are immutable and do not support 'addAll'");
  306.     }

  307.     /**
  308.      * Tuples are immutable and do not support 'removeAll'
  309.      */
  310.     @Override
  311.     public boolean removeAll(Collection<?> c) {
  312.         throw new UnsupportedOperationException("Tuples are immutable and do not support 'removeAll'");
  313.     }

  314.     /**
  315.      * Tuples are immutable and do not support 'retainAll'
  316.      */
  317.     @Override
  318.     public boolean retainAll(Collection<?> c) {
  319.         throw new UnsupportedOperationException("Tuples are immutable and do not support 'retainAll'");
  320.     }

  321.     /**
  322.      * Tuples are immutable and do not support 'clear'
  323.      */
  324.     @Override
  325.     public void clear() {
  326.         throw new UnsupportedOperationException("Tuples are immutable and do not support 'clear'");
  327.     }
  328.    
  329. }