@Test
 public void canAdaptBiFunction() {
   final Function<Pair<O, O>, Pair<O, O>> function = Tuples.tupled(new BinaryIdentity<O, O>());
   final Pair<O, O> expected = Pair.of(O.ONE, O.ANOTHER);
   final Pair<O, O> got = function.apply(expected);
   Assert.assertEquals(expected, got);
 }
 @Test
 public void canAdaptTriFunction() {
   final Function<Triple<O, O, O>, Triple<O, O, O>> function =
       Tuples.tupled(new TernaryIdentity<>());
   final Triple<O, O, O> expected = Triple.of(O.ONE, O.ANOTHER, O.ANOTHER);
   final Triple<O, O, O> got = function.apply(expected);
   Assert.assertEquals(expected, got);
 }
 @Test
 public void canAdaptBiConsumer() {
   final Box<O> first = Box.empty();
   final Box<O> second = Box.empty();
   final BiConsumer<O, O> spy = Spies.spy(new BinaryNoop<O, O>(), first, second);
   final Consumer<Pair<O, O>> consumer = Tuples.tupled(spy);
   consumer.accept(Pair.of(O.ONE, O.ANOTHER));
   Assert.assertEquals(O.ONE, first.getContent());
   Assert.assertEquals(O.ANOTHER, second.getContent());
 }
 @Test
 public void canAdaptTriConsumer() {
   final Box<O> first = Box.empty();
   final Box<O> second = Box.empty();
   final Box<O> third = Box.empty();
   final TriConsumer<O, O, O> spy = Spies.spy(new TernaryNoop<O, O, O>(), first, second, third);
   final Consumer<Triple<O, O, O>> consumer = Tuples.tupled(spy);
   consumer.accept(Triple.of(O.ONE, O.ANOTHER, O.YET_ANOTHER));
   Assert.assertEquals(O.ONE, first.getContent());
   Assert.assertEquals(O.ANOTHER, second.getContent());
   Assert.assertEquals(O.YET_ANOTHER, third.getContent());
 }
Example #5
0
  @Override
  public int compareTo(Tuple8<T1, T2, T3, T4, T5, T6, T7, T8> other) {
    int result = 0;

    result = Tuples.compare(v1, other.v1);
    if (result != 0) return result;
    result = Tuples.compare(v2, other.v2);
    if (result != 0) return result;
    result = Tuples.compare(v3, other.v3);
    if (result != 0) return result;
    result = Tuples.compare(v4, other.v4);
    if (result != 0) return result;
    result = Tuples.compare(v5, other.v5);
    if (result != 0) return result;
    result = Tuples.compare(v6, other.v6);
    if (result != 0) return result;
    result = Tuples.compare(v7, other.v7);
    if (result != 0) return result;
    result = Tuples.compare(v8, other.v8);
    if (result != 0) return result;

    return result;
  }
Example #6
0
/**
 * A Tuple represents a set of values. Consider a Tuple the same as a database record where every
 * value is a column in that table.
 *
 * <p>A "tuple stream" is a set of Tuple instances passed consecutively through a Pipe assembly.
 *
 * <p>Tuples work in tandem with {@link Fields} and the {@link TupleEntry} classes. A TupleEntry
 * holds an instance of Fields and a Tuple. It allows a tuple to be accessed by its field names, and
 * will help maintain consistent types if any are given on the Fields instance. That is, if a field
 * is specified at an Integer, calling {@link #set(int, Object)} with a String will force the String
 * to be coerced into a Integer instance.
 *
 * <p>For managing custom types, see the {@link CoercibleType} interface which extends {@link Type}.
 *
 * <p>Tuple instances created by user code, by default, are mutable (or modifiable). Tuple instances
 * created by the system are immutable (or unmodifiable, tested by calling {@link
 * #isUnmodifiable()}).
 *
 * <p>For example tuples returned by {@link cascading.operation.FunctionCall#getArguments()}, will
 * always be unmodifiable. Thus they must be copied if they will be changed by user code or cached
 * in the local context. See the Tuple copy constructor, or {@code *Copy()} methods on {@link
 * TupleEntry}.
 *
 * <p>Because a Tuple can hold any Object type, it is suitable for storing custom types. But all
 * custom types must have a serialization support per the underlying framework.
 *
 * <p>For Hadoop, a {@link org.apache.hadoop.io.serializer.Serialization} implementation must be
 * registered with Hadoop. For further performance improvements, see the {@link
 * cascading.tuple.hadoop.SerializationToken} Java annotation.
 *
 * @see org.apache.hadoop.io.serializer.Serialization
 * @see cascading.tuple.hadoop.SerializationToken
 */
public class Tuple implements Comparable<Object>, Iterable<Object>, Serializable {
  /** A constant empty Tuple instance. This instance is immutable. */
  public static final Tuple NULL = Tuples.asUnmodifiable(new Tuple());

  /** Field printDelim */
  private static final String printDelim = "\t";

  /** Field isUnmodifiable */
  protected transient boolean isUnmodifiable = false;

  /** Field elements */
  protected List<Object> elements;

  /**
   * Method size returns a new Tuple instance of the given size with nulls as its element values.
   *
   * @param size of type int
   * @return Tuple
   */
  public static Tuple size(int size) {
    return size(size, null);
  }

  /**
   * Method size returns a new Tuple instance of the given size with the given Comparable as its
   * element values.
   *
   * @param size of type int
   * @param value of type Comparable
   * @return Tuple
   */
  public static Tuple size(int size, Comparable value) {
    Tuple result = new Tuple(new ArrayList<Object>(size));

    for (int i = 0; i < size; i++) result.add(value);

    return result;
  }

  /**
   * Returns a reference to the private elements of the given Tuple.
   *
   * <p>This method is for internal use and is subject to change scope in a future release.
   *
   * @param tuple of type Tuple
   * @return List<Comparable>
   */
  public static List<Object> elements(Tuple tuple) {
    return tuple.elements;
  }

  protected Tuple(List<Object> elements) {
    this.elements = elements;
  }

  /** Constructor Tuple creates a new Tuple instance. */
  public Tuple() {
    this(new ArrayList<Object>());
  }

  /**
   * Copy constructor. Does not nest the given Tuple instance within this new instance. Use {@link
   * #add(Object)}.
   *
   * @param tuple of type Tuple
   */
  @ConstructorProperties({"tuple"})
  public Tuple(Tuple tuple) {
    this(new ArrayList<Object>(tuple.elements));
  }

  /**
   * Constructor Tuple creates a new Tuple instance with all the given values.
   *
   * @param values of type Object...
   */
  @ConstructorProperties({"values"})
  public Tuple(Object... values) {
    this(new ArrayList<Object>(values.length));
    Collections.addAll(elements, values);
  }

  /**
   * Method isUnmodifiable returns true if this Tuple instance is unmodifiable.
   *
   * <p>"Unmodifiable" tuples are generally owned by the system and cannot be changed, nor should
   * they be cached as the internal values may change.
   *
   * @return boolean
   */
  public boolean isUnmodifiable() {
    return isUnmodifiable;
  }

  /**
   * Method get returns the element at the given position.
   *
   * <p>This method will perform no coercion on the element.
   *
   * @param pos of type int
   * @return Object
   */
  public Object getObject(int pos) {
    return elements.get(pos);
  }

  /**
   * Method getChar returns the element at the given position as a char.
   *
   * @param pos of type int
   * @return String
   */
  public char getChar(int pos) {
    return Coercions.CHARACTER.coerce(getObject(pos));
  }

  /**
   * Method getString returns the element at the given position as a String.
   *
   * @param pos of type int
   * @return String
   */
  public String getString(int pos) {
    return Coercions.STRING.coerce(getObject(pos));
  }

  /**
   * Method getFloat returns the element at the given position as a float. Zero if null.
   *
   * @param pos of type int
   * @return float
   */
  public float getFloat(int pos) {
    return Coercions.FLOAT.coerce(getObject(pos));
  }

  /**
   * Method getDouble returns the element at the given position as a double. Zero if null.
   *
   * @param pos of type int
   * @return double
   */
  public double getDouble(int pos) {
    return Coercions.DOUBLE.coerce(getObject(pos));
  }

  /**
   * Method getInteger returns the element at the given position as an int. Zero if null.
   *
   * @param pos of type int
   * @return int
   */
  public int getInteger(int pos) {
    return Coercions.INTEGER.coerce(getObject(pos));
  }

  /**
   * Method getLong returns the element at the given position as an long. Zero if null.
   *
   * @param pos of type int
   * @return long
   */
  public long getLong(int pos) {
    return Coercions.LONG.coerce(getObject(pos));
  }

  /**
   * Method getShort returns the element at the given position as an short. Zero if null.
   *
   * @param pos of type int
   * @return long
   */
  public short getShort(int pos) {
    return Coercions.SHORT.coerce(getObject(pos));
  }

  /**
   * Method getBoolean returns the element at the given position as a boolean. If the value is (case
   * ignored) the string 'true', a {@code true} value will be returned. {@code false} if null.
   *
   * @param pos of type int
   * @return boolean
   */
  public boolean getBoolean(int pos) {
    return Coercions.BOOLEAN.coerce(getObject(pos));
  }

  /**
   * Method get will return a new Tuple instance populated with element values from the given array
   * of positions.
   *
   * @param pos of type int[]
   * @return Tuple
   */
  public Tuple get(int[] pos) {
    if (pos == null || pos.length == 0) return new Tuple(this);

    Tuple results = new Tuple();

    for (int i : pos) results.add(elements.get(i));

    return results;
  }

  /**
   * Method get returns a new Tuple populated with only those values whose field names are specified
   * in the given selector. The declarator Fields instance declares the fields and positions in the
   * current Tuple instance.
   *
   * @param declarator of type Fields
   * @param selector of type Fields
   * @return Tuple
   */
  public Tuple get(Fields declarator, Fields selector) {
    try {
      return get(getPos(declarator, selector));
    } catch (Exception exception) {
      throw new TupleException(
          "unable to select from: " + declarator.print() + ", using selector: " + selector.print(),
          exception);
    }
  }

  public int[] getPos(Fields declarator, Fields selector) {
    if (!declarator.isUnknown() && elements.size() != declarator.size())
      throw new TupleException(
          "field declaration: " + declarator.print() + ", does not match tuple: " + print());

    return declarator.getPos(selector, size());
  }

  /**
   * Method is the inverse of {@link #remove(int[])}.
   *
   * @param pos of type int[]
   * @return Tuple
   */
  public Tuple leave(int[] pos) {
    verifyModifiable();

    Tuple results = remove(pos);

    List<Object> temp = results.elements;
    results.elements = this.elements;
    this.elements = temp;

    return results;
  }

  /**
   * Method clear empties this Tuple instance. A subsequent call to {@link #size()} will return zero
   * ({@code 0}).
   */
  public void clear() {
    verifyModifiable();

    elements.clear();
  }

  /**
   * Method add adds a new element value to this instance.
   *
   * @param value of type Comparable
   */
  public void add(Comparable value) {
    add((Object) value);
  }

  /**
   * Method add adds a new element value to this instance.
   *
   * @param value of type Object
   */
  public void add(Object value) {
    verifyModifiable();

    elements.add(value);
  }

  /**
   * Method addBoolean adds a new element value to this instance.
   *
   * @param value of type boolean
   */
  public void addBoolean(boolean value) {
    verifyModifiable();

    elements.add(value);
  }

  /**
   * Method addShort adds a new element value to this instance.
   *
   * @param value of type short
   */
  public void addShort(short value) {
    verifyModifiable();

    elements.add(value);
  }

  /**
   * Method addInteger adds a new element value to this instance.
   *
   * @param value of type int
   */
  public void addInteger(int value) {
    verifyModifiable();

    elements.add(value);
  }

  /**
   * Method addLong adds a new element value to this instance.
   *
   * @param value of type long
   */
  public void addLong(long value) {
    verifyModifiable();

    elements.add(value);
  }

  /**
   * Method addFloat adds a new element value to this instance.
   *
   * @param value of type float
   */
  public void addFloat(float value) {
    verifyModifiable();

    elements.add(value);
  }

  /**
   * Method addDouble adds a new element value to this instance.
   *
   * @param value of type double
   */
  public void addDouble(double value) {
    verifyModifiable();

    elements.add(value);
  }

  /**
   * Method addString adds a new element value to this instance.
   *
   * @param value of type String
   */
  public void addString(String value) {
    verifyModifiable();

    elements.add(value);
  }

  /**
   * Method addAll adds all given values to this instance.
   *
   * @param values of type Object...
   */
  public void addAll(Object... values) {
    verifyModifiable();

    if (values.length == 1 && values[0] instanceof Tuple) addAll((Tuple) values[0]);
    else Collections.addAll(elements, values);
  }

  /**
   * Method addAll adds all the element values of the given Tuple instance to this instance.
   *
   * @param tuple of type Tuple
   */
  public void addAll(Tuple tuple) {
    verifyModifiable();

    if (tuple != null) elements.addAll(tuple.elements);
  }

  /**
   * Method setAll sets each element value of the given Tuple instance into the corresponding
   * position of this instance.
   *
   * @param tuple of type Tuple
   */
  public void setAll(Tuple tuple) {
    verifyModifiable();

    if (tuple == null) return;

    for (int i = 0; i < tuple.elements.size(); i++) internalSet(i, tuple.elements.get(i));
  }

  /**
   * Method setAll sets each element value of the given Tuple instances into the corresponding
   * position of this instance.
   *
   * <p>All given tuple instances after the first will be offset by the length of the prior tuple
   * instances.
   *
   * @param tuples of type Tuple[]
   */
  public void setAll(Tuple... tuples) {
    verifyModifiable();

    if (tuples.length == 0) return;

    int pos = 0;
    for (int i = 0; i < tuples.length; i++) {
      Tuple tuple = tuples[i];

      if (tuple == null) // being defensive
      continue;

      for (int j = 0; j < tuple.elements.size(); j++) internalSet(pos++, tuple.elements.get(j));
    }
  }

  /**
   * Method set sets the given value to the given index position in this instance.
   *
   * @param index of type int
   * @param value of type Object
   */
  public void set(int index, Object value) {
    verifyModifiable();

    internalSet(index, value);
  }

  /**
   * Method setBoolean sets the given value to the given index position in this instance.
   *
   * @param index of type int
   * @param value of type boolean
   */
  public void setBoolean(int index, boolean value) {
    verifyModifiable();

    internalSet(index, value);
  }

  /**
   * Method setShort sets the given value to the given index position in this instance.
   *
   * @param index of type int
   * @param value of type short
   */
  public void setShort(int index, short value) {
    verifyModifiable();

    internalSet(index, value);
  }

  /**
   * Method setInteger sets the given value to the given index position in this instance.
   *
   * @param index of type int
   * @param value of type int
   */
  public void setInteger(int index, int value) {
    verifyModifiable();

    internalSet(index, value);
  }

  /**
   * Method setLong sets the given value to the given index position in this instance.
   *
   * @param index of type int
   * @param value of type long
   */
  public void setLong(int index, long value) {
    verifyModifiable();

    internalSet(index, value);
  }

  /**
   * Method setFloat sets the given value to the given index position in this instance.
   *
   * @param index of type int
   * @param value of type float
   */
  public void setFloat(int index, float value) {
    verifyModifiable();

    internalSet(index, value);
  }

  /**
   * Method setDouble sets the given value to the given index position in this instance.
   *
   * @param index of type int
   * @param value of type double
   */
  public void setDouble(int index, double value) {
    verifyModifiable();

    internalSet(index, value);
  }

  /**
   * Method setString sets the given value to the given index position in this instance.
   *
   * @param index of type int
   * @param value of type String
   */
  public void setString(int index, String value) {
    verifyModifiable();

    internalSet(index, value);
  }

  protected final void internalSet(int index, Object value) {
    try {
      elements.set(index, value);
    } catch (IndexOutOfBoundsException exception) {
      if (elements.size() != 0)
        throw new TupleException(
            "failed to set a value beyond the end of the tuple elements array, size: "
                + size()
                + " , index: "
                + index);
      else
        throw new TupleException(
            "failed to set a value, tuple may not be initialized with values, is zero length");
    }
  }

  /**
   * Method put places the values of the given tuple into the positions specified by the fields
   * argument. The declarator Fields value declares the fields in this Tuple instance.
   *
   * @param declarator of type Fields
   * @param fields of type Fields
   * @param tuple of type Tuple
   */
  public void put(Fields declarator, Fields fields, Tuple tuple) {
    verifyModifiable();

    int[] pos = getPos(declarator, fields);

    for (int i = 0; i < pos.length; i++) internalSet(pos[i], tuple.getObject(i));
  }

  /**
   * Method remove removes the values specified by the given pos array and returns a new Tuple
   * containing the removed values.
   *
   * @param pos of type int[]
   * @return Tuple
   */
  public Tuple remove(int[] pos) {
    verifyModifiable();

    // calculate offsets to apply when removing values from elements
    int offset[] = new int[pos.length];

    for (int i = 0; i < pos.length; i++) {
      offset[i] = 0;

      for (int j = 0; j < i; j++) {
        if (pos[j] < pos[i]) offset[i]++;
      }
    }

    Tuple results = new Tuple();

    for (int i = 0; i < pos.length; i++) results.add(elements.remove(pos[i] - offset[i]));

    return results;
  }

  /**
   * Method remove removes the values specified by the given selector. The declarator declares the
   * fields in this instance.
   *
   * @param declarator of type Fields
   * @param selector of type Fields
   * @return Tuple
   */
  public Tuple remove(Fields declarator, Fields selector) {
    return remove(getPos(declarator, selector));
  }

  /**
   * Creates a new Tuple from the given positions, but sets the values in the current tuple to null.
   *
   * @param pos of type int[]
   * @return Tuple
   */
  Tuple extract(int[] pos) {
    Tuple results = new Tuple();

    for (int i : pos) results.add(elements.set(i, null));

    return results;
  }

  Tuple nulledCopy(int[] pos) {
    if (pos == null) return size(size());

    Tuple results = new Tuple(this);

    for (int i : pos) results.set(i, null);

    return results;
  }

  /**
   * Sets the values in the given positions to the values from the given Tuple.
   *
   * @param pos of type int[]
   * @param tuple of type Tuple
   */
  void set(int[] pos, Tuple tuple) {
    verifyModifiable();

    if (pos.length != tuple.size())
      throw new TupleException(
          "given tuple not same size as position array: "
              + pos.length
              + ", tuple: "
              + tuple.print());

    int count = 0;
    for (int i : pos) elements.set(i, tuple.elements.get(count++));
  }

  private void set(int[] pos, Type[] types, Tuple tuple, CoercibleType[] coercions) {
    verifyModifiable();

    if (pos.length != tuple.size())
      throw new TupleException(
          "given tuple not same size as position array: "
              + pos.length
              + ", tuple: "
              + tuple.print());

    int count = 0;

    for (int i : pos) {
      Object element = tuple.elements.get(count);

      if (types != null) {
        Type type = types[i];
        element = coercions[count].coerce(element, type);
      }

      elements.set(i, element);

      count++;
    }
  }

  /**
   * Method set sets the values in the given selector positions to the values from the given Tuple.
   *
   * <p>If type information is given, each incoming value from tuple will be coerced from its
   * canonical type to the current type as declared in the declarator.
   *
   * @param declarator of type Fields
   * @param selector of type Fields
   * @param tuple of type Tuple
   */
  public void set(Fields declarator, Fields selector, Tuple tuple) {
    try {
      set(
          declarator.getPos(selector),
          declarator.getTypes(),
          tuple,
          TupleEntry.getCoercions(declarator, tuple));
    } catch (Exception exception) {
      throw new TupleException(
          "unable to set into: " + declarator.print() + ", using selector: " + selector.print(),
          exception);
    }
  }

  protected void set(Fields declarator, Fields selector, Tuple tuple, CoercibleType[] coercions) {
    try {
      set(declarator.getPos(selector), declarator.getTypes(), tuple, coercions);
    } catch (Exception exception) {
      throw new TupleException(
          "unable to set into: " + declarator.print() + ", using selector: " + selector.print(),
          exception);
    }
  }

  /**
   * Method iterator returns an {@link Iterator} over this Tuple instances values.
   *
   * @return Iterator
   */
  public Iterator<Object> iterator() {
    return elements.iterator();
  }

  /**
   * Method isEmpty returns true if this Tuple instance has no values.
   *
   * @return the empty (type boolean) of this Tuple object.
   */
  public boolean isEmpty() {
    return elements.isEmpty();
  }

  /**
   * Method size returns the number of values in this Tuple instance.
   *
   * @return int
   */
  public int size() {
    return elements.size();
  }

  /**
   * Method elements returns a new Object[] array of this Tuple instances values.
   *
   * @return Object[]
   */
  private Object[] elements() {
    return elements.toArray();
  }

  /**
   * Method elements returns the given destination array with the values of This tuple instance.
   *
   * @param destination of type Object[]
   * @return Object[]
   */
  <T> T[] elements(T[] destination) {
    return elements.toArray(destination);
  }

  /**
   * Method getTypes returns an array of the element classes. Null if the element is null.
   *
   * @return the types (type Class[]) of this Tuple object.
   */
  public Class[] getTypes() {
    Class[] types = new Class[elements.size()];

    for (int i = 0; i < elements.size(); i++) {
      Object value = elements.get(i);

      if (value != null) types[i] = value.getClass();
    }

    return types;
  }

  /**
   * Method append appends all the values of the given Tuple instances to a copy of this instance.
   *
   * @param tuples of type Tuple
   * @return Tuple
   */
  public Tuple append(Tuple... tuples) {
    Tuple result = new Tuple(this);

    for (Tuple tuple : tuples) result.addAll(tuple);

    return result;
  }

  /**
   * Method compareTo compares this Tuple to the given Tuple instance.
   *
   * @param other of type Tuple
   * @return int
   */
  public int compareTo(Tuple other) {
    if (other == null || other.elements == null) return 1;

    if (other.elements.size() != this.elements.size())
      return this.elements.size() - other.elements.size();

    for (int i = 0; i < this.elements.size(); i++) {
      Comparable lhs = (Comparable) this.elements.get(i);
      Comparable rhs = (Comparable) other.elements.get(i);

      if (lhs == null && rhs == null) continue;

      if (lhs == null) return -1;
      else if (rhs == null) return 1;

      int c = lhs.compareTo(rhs); // guaranteed to not be null
      if (c != 0) return c;
    }

    return 0;
  }

  public int compareTo(Comparator[] comparators, Tuple other) {
    if (comparators == null) return compareTo(other);

    if (other == null || other.elements == null) return 1;

    if (other.elements.size() != this.elements.size())
      return this.elements.size() - other.elements.size();

    if (comparators.length != this.elements.size())
      throw new IllegalArgumentException("comparator array not same size as tuple elements");

    for (int i = 0; i < this.elements.size(); i++) {
      Object lhs = this.elements.get(i);
      Object rhs = other.elements.get(i);

      int c;

      if (comparators[i] != null) c = comparators[i].compare(lhs, rhs);
      else if (lhs == null && rhs == null) c = 0;
      else if (lhs == null) return -1;
      else if (rhs == null) return 1;
      else c = ((Comparable) lhs).compareTo(rhs); // guaranteed to not be null

      if (c != 0) return c;
    }

    return 0;
  }

  /**
   * Method compareTo implements the {@link Comparable#compareTo(Object)} method.
   *
   * @param other of type Object
   * @return int
   */
  public int compareTo(Object other) {
    if (other instanceof Tuple) return compareTo((Tuple) other);
    else return -1;
  }

  @SuppressWarnings({"ForLoopReplaceableByForEach"})
  @Override
  public boolean equals(Object object) {
    if (!(object instanceof Tuple)) return false;

    Tuple other = (Tuple) object;

    if (this.elements.size() != other.elements.size()) return false;

    for (int i = 0; i < this.elements.size(); i++) {
      Object lhs = this.elements.get(i);
      Object rhs = other.elements.get(i);

      if (lhs == null && rhs == null) continue;

      if (lhs == null || rhs == null) return false;

      if (!lhs.equals(rhs)) return false;
    }

    return true;
  }

  @Override
  public int hashCode() {
    int hash = 1;

    for (Object element : elements) hash = 31 * hash + (element != null ? element.hashCode() : 0);

    return hash;
  }

  @Override
  public String toString() {
    return Util.join(elements, printDelim, true);
  }

  /**
   * Method toString writes this Tuple instance values out to a String delimited by the given String
   * value.
   *
   * @param delim of type String
   * @return String
   */
  public String toString(String delim) {
    return Util.join(elements, delim, true);
  }

  /**
   * Method toString writes this Tuple instance values out to a String delimited by the given String
   * value.
   *
   * @param delim of type String
   * @param printNull of type boolean
   * @return String
   */
  public String toString(String delim, boolean printNull) {
    return Util.join(elements, delim, printNull);
  }

  /**
   * Method format uses the {@link Formatter} class for formatting this tuples values into a new
   * string.
   *
   * @param format of type String
   * @return String
   */
  public String format(String format) {
    return String.format(format, elements());
  }

  /**
   * Method print returns a parsable String representation of this Tuple instance.
   *
   * @return String
   */
  public String print() {
    return printTo(new StringBuffer()).toString();
  }

  public StringBuffer printTo(StringBuffer buffer) {
    buffer.append("[");

    if (elements != null) {
      for (int i = 0; i < elements.size(); i++) {
        Object element = elements.get(i);

        if (element instanceof Tuple) ((Tuple) element).printTo(buffer);
        else if (element == null) // don't quote nulls to distinguish from null strings
        buffer.append(element);
        else buffer.append("\'").append(element).append("\'");

        if (i < elements.size() - 1) buffer.append(", ");
      }
    }

    buffer.append("]");

    return buffer;
  }

  private final void verifyModifiable() {
    if (isUnmodifiable) throw new UnsupportedOperationException("this tuple is unmodifiable");
  }
}
 @Test
 public void canAdaptTernaryPredicate() {
   final Predicate<Triple<O, O, O>> predicate = Tuples.tupled(new TernaryAlways<O, O, O>());
   Assert.assertTrue(predicate.test(Triple.of(O.IGNORED, O.IGNORED, O.IGNORED)));
 }
 @Test
 public void canAdaptBiPredicate() {
   final Predicate<Pair<O, O>> predicate = Tuples.tupled(new BinaryAlways<O, O>());
   Assert.assertTrue(predicate.test(Pair.of(O.IGNORED, O.IGNORED)));
 }