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}