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