001    /*--------------------------------------------------------------------------+
002    $Id: ImmutablePair.java 26268 2010-02-18 10:44:30Z juergens $
003    |                                                                          |
004    | Copyright 2005-2010 Technische Universitaet Muenchen                     |
005    |                                                                          |
006    | Licensed under the Apache License, Version 2.0 (the "License");          |
007    | you may not use this file except in compliance with the License.         |
008    | You may obtain a copy of the License at                                  |
009    |                                                                          |
010    |    http://www.apache.org/licenses/LICENSE-2.0                            |
011    |                                                                          |
012    | Unless required by applicable law or agreed to in writing, software      |
013    | distributed under the License is distributed on an "AS IS" BASIS,        |
014    | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
015    | See the License for the specific language governing permissions and      |
016    | limitations under the License.                                           |
017    +--------------------------------------------------------------------------*/
018    package edu.tum.cs.commons.collections;
019    
020    import edu.tum.cs.commons.clone.CloneUtils;
021    import edu.tum.cs.commons.clone.DeepCloneException;
022    import edu.tum.cs.commons.clone.IDeepCloneable;
023    
024    /**
025     * Simple readonly pair class.
026     * 
027     * @author hummelb
028     * @author $Author: juergens $
029     * @version $Rev: 26268 $
030     * @levd.rating GREEN Hash: 8C6F4981DA02F9D8D8CFF01534F52D67
031     */
032    public class ImmutablePair<S, T> implements Cloneable, IDeepCloneable,
033                    Comparable<ImmutablePair<S, T>> {
034    
035            /** The first element. */
036            protected S first;
037    
038            /** The second element. */
039            protected T second;
040    
041            /** Constructor. */
042            public ImmutablePair(S first, T second) {
043                    this.first = first;
044                    this.second = second;
045            }
046    
047            /** Copy constructor. */
048            public ImmutablePair(ImmutablePair<S, T> p) {
049                    this.first = p.first;
050                    this.second = p.second;
051            }
052    
053            /** Returns the first element of the pair. */
054            public S getFirst() {
055                    return first;
056            }
057    
058            /** Returns the second element of the pair. */
059            public T getSecond() {
060                    return second;
061            }
062    
063            /** {@inheritDoc} */
064            @Override
065            public boolean equals(Object obj) {
066                    if (!(obj instanceof ImmutablePair<?, ?>)) {
067                            return false;
068                    }
069                    ImmutablePair<?, ?> p = (ImmutablePair<?, ?>) obj;
070                    return areEqual(first, p.first) && areEqual(second, p.second);
071            }
072    
073            /** Returns true if either both are <code>null</code> or they are equal. */
074            private boolean areEqual(Object o1, Object o2) {
075                    if (o1 == null) {
076                            return o2 == null;
077                    }
078                    return o1.equals(o2);
079            }
080    
081            /**
082             * {@inheritDoc}
083             * <p>
084             * The hash code is based on the hash code of the first and second members.
085             */
086            @Override
087            public int hashCode() {
088                    int firstCode = 1;
089                    if (first != null) {
090                            firstCode = first.hashCode();
091                    }
092    
093                    int secondCode = 1;
094                    if (second != null) {
095                            secondCode = second.hashCode();
096                    }
097    
098                    return firstCode + 1013 * secondCode;
099            }
100    
101            /** {@inheritDoc} */
102            @Override
103            public String toString() {
104                    return "(" + first + "," + second + ")";
105            }
106    
107            /** {@inheritDoc} */
108            @Override
109            protected ImmutablePair<S, T> clone() {
110                    return new ImmutablePair<S, T>(this);
111            }
112    
113            /** {@inheritDoc} */
114            @SuppressWarnings("unchecked")
115            public ImmutablePair<S, T> deepClone() throws DeepCloneException {
116                    S newFirst = (S) CloneUtils.cloneAsDeepAsPossible(first);
117                    T newSecond = (T) CloneUtils.cloneAsDeepAsPossible(second);
118                    return new ImmutablePair<S, T>(newFirst, newSecond);
119            }
120    
121            /**
122             * {@inheritDoc}
123             * <p>
124             * Compare based on first element. Use second element only if first elements
125             * are equal. Null entries are sorted to the top.
126             */
127            public int compareTo(ImmutablePair<S, T> pair) {
128                    int cmp = objCompare(first, pair.first);
129                    if (cmp != 0) {
130                            return cmp;
131                    }
132                    return objCompare(second, pair.second);
133            }
134    
135            /**
136             * Performs comparison on two arbitrary objects of the same type.
137             */
138            @SuppressWarnings("unchecked")
139            private <O> int objCompare(O o1, O o2) {
140                    if (o1 == null) {
141                            if (o2 == null) {
142                                    return 0;
143                            }
144                            return -1;
145                    } else if (o2 == null) {
146                            return 1;
147                    }
148    
149                    if ((o1 instanceof Comparable) && (o2 instanceof Comparable)) {
150                            try {
151                                    ((Comparable<Object>) o1).compareTo(o2);
152                            } catch (ClassCastException e) {
153                                    // somehow failed, so continue and treat as if not comparable
154                            }
155                    }
156    
157                    // compare using hash code, so we get at least an approximation of an
158                    // ordering
159                    int h1 = o1.hashCode();
160                    int h2 = o2.hashCode();
161    
162                    if (h1 == h2) {
163                            return 0;
164                    }
165                    if (h1 < h2) {
166                            return -1;
167                    }
168                    return 1;
169            }
170    }