    001// Copyright 2010 The Apache Software Foundation
    002//
    003// Licensed under the Apache License, Version 2.0 (the "License");
    004// you may not use this file except in compliance with the License.
    005// You may obtain a copy of the License at
    006//
    007// http://www.apache.org/licenses/LICENSE-2.0
    008//
    009// Unless required by applicable law or agreed to in writing, software
    010// distributed under the License is distributed on an "AS IS" BASIS,
    011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012// See the License for the specific language governing permissions and
    013// limitations under the License.
    014
    015package org.apache.tapestry5.func;
    016
    017import java.util.Objects;
    018
    019/**
    020 * A Tuple holds two values of two different types.
    021 *
    022 * @param <A> first type
    023 * @param <B> second type
    024 * @since 5.3
    025 */
    026public class Tuple<A, B>
    027{
    028    public final A first;
    029
    030    public final B second;
    031
    032    public Tuple(A first, B second)
    033    {
    034        this.first = first;
    035        this.second = second;
    036    }
    037
    038    public static <X, Y> Tuple<X, Y> create(X first, Y second)
    039    {
    040        return new Tuple<X, Y>(first, second);
    041    }
    042
    043    /**
    044     * Returns the values of the tuple, separated by commas, enclosed in parenthesis. Example:
    045     * <code>("Ace", "Spades")</code>.
    046     */
    047    @Override
    048    public String toString()
    049    {
    050        StringBuilder builder = new StringBuilder("(");
    051
    052        builder.append(String.valueOf(first));
    053        builder.append(", ");
    054        builder.append(String.valueOf(second));
    055
    056        extendDescription(builder);
    057
    058        return builder.append(')').toString();
    059    }
    060
    061    /**
    062     * Overriden in subclasses to write additional values into the
    063     * description.
    064     *
    065     * @param builder
    066     */
    067    protected void extendDescription(StringBuilder builder)
    068    {
    069    }
    070
    071    /** Utility for comparing two values, either of which may be null. */
    072    static boolean isEqual(Object left, Object right)
    073    {
    074        return left == right || (left != null && left.equals(right));
    075    }
    076
    077    /**
    078     * Compares this Tuple to another object. Equality is defined by: other object is not null,
    079     * is same class as this Tuple, and all values are themselves equal.
    080     */
    081    @Override
    082    public boolean equals(Object obj)
    083    {
    084        if (obj == this)
    085            return true;
    086
    087        if (obj == null || !(obj.getClass() == getClass()))
    088            return false;
    089
    090        return isMatch(obj);
    091    }
    092
    093    /**
    094     * Returns a hash code value for the tuple, based on its values.
    095     *
    096     * @return a hash code value for this tuple.
    097     * @since 5.7
    098     */
    099    @Override
    100    public int hashCode()
    101    {
    102        return Objects.hash(this.first, this.second);
    103    }
    104
    105    /**
    106     * The heart of {@link #equals(Object)}; the other object is the same class as this object.
    107     *
    108     * @param other
    109     *            other tuple to compare
    110     * @return true if all values stored in tuple match
    111     */
    112    protected boolean isMatch(Object other)
    113    {
    114        Tuple<?, ?> tuple = (Tuple<?, ?>) other;
    115
    116        return isEqual(first, tuple.first) && isEqual(second, tuple.second);
    117    }
    118}