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}