001package Torello.Java.Additional; 002 003import Torello.Java.*; 004import Torello.JavaDoc.LinkJavaSource; 005 006/** 007 * This is a parent class of the 'Multiple Return Type' classes. ({@code Ret0 ... Ret8}) This 008 * class provides some basic functionality to its descendants, namely: {@link #toString()}, 009 * {@link #hashCode()} and {@link #equals(Object)}. 010 */ 011public abstract class MultiType 012 implements java.io.Serializable, Cloneable 013{ 014 /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */ 015 protected static final long serialVersionUID = 1; 016 017 MultiType() { } 018 019 020 // This, admittedly is a little obnoxious, but it allows there to be a "parent" super-type of 021 // both RetN and TupleN. Since only one of these is Read-Write, their "get-array" method is 022 // not completely identical. The Immutable-ReadOnly one can cache the array, while the 023 // Read-Write version has to recreate the array everytime. 024 025 abstract Object[] asArrayInternal(); 026 abstract Object[] asArrayMain(); 027 028 /** 029 * All implementations of this {@code abstract} can indicate which of the {@link RetN} or 030 * {@link TupleN} instances they are, by returning the number of fields they hold using this 031 * method. 032 * 033 * @return The number of fields contained by the class that is implementing {@code 'this'} 034 * instance. So for example, an instance of {@link Tuple5} would produce {@code '5'}, if this 035 * method were called; and {@link Ret3} would produce {@code '3'}, and so on and so forth. 036 */ 037 public abstract int n(); 038 039 /** 040 * Retrieve the contents of the instance-descendant class, as an array. 041 * 042 * @return Returns the {@code 'this'} instance' fields {@code 'a', 'b', 'c'} etc... as an 043 * {@code Object[]} array. 044 */ 045 public Object[] asArray() 046 { return asArrayMain(); } 047 048 /** 049 * This will return an instance of {@code Object} which represents the 050 * <CODE>i<SUP>th</SUP></CODE> instance-field from whatever {@link RetN} or {@link TupleN} 051 * implementation this method has been invoked. 052 * 053 * <BR /><BR />If {@code 'this'} instance were a {@link Ret5}, and {@code '3'} were passed to 054 * parameter {@code 'i'}, then the value in {@link Ret5#c} would be returned. 055 * 056 * <BR /><BR />If a call to {@code 'get'} is made from {@link Tuple2}, and {@code '3'} were 057 * passed to {@code 'i'}, then an {@code IndexOutOfBoundsException} would throw. 058 * 059 * @param i This specifies which field of the instance is being requested. 060 * 061 * <BR /><BR /><DIV CLASS=JDHint> 062 * <B STYLE='color:red;'>Important:</B> Unlike a Java array, when a {@code '1'} is passed to 063 * this parameter, it is requesting the first field in this instance. Passing a value of 064 * {@code '0'} shall cause an {@code IndexOutOfBoundsException} throw. 065 * </DIV> 066 * 067 * @return This returns the <CODE>i<SUP>th</SUP></CODE> field of this class. 068 * 069 * @throws IndexOutOfBoundsException if {@code 'i'} is zero or negative, or greater than 070 * the value of {@code 'N'} for whichever {@link RetN} or {@link TupleN} class is being used. 071 * 072 * <BR /><BR />If {@code 'this'} is an instance of {@link Tuple5}, then a value of 6 or greater 073 * would force this exception to throw. 074 * 075 * @see #get(int, Class) 076 * @see #GET(int) 077 */ 078 public abstract Object get(int i); 079 080 // This does a check for the above method that is needed by both TupleN and RetN 081 void CHECK_GET(int i) 082 { 083 if (i < 1) 084 { 085 // This is actually unreachable code, because Ret0's & Tuple0's "get" check for this 086 // case, and throw the exception themselves. I'm leaving this here anyways, as a 087 // reminder to what is going on. (By "unreachable" - I'm talking about the first 'if' 088 // branch, not the 'else') 089 090 if (n() == 0) throw new IndexOutOfBoundsException 091 ("You may not invoke 'get' on a Tuple or Ret of size 0"); 092 093 else throw new IndexOutOfBoundsException( 094 "You have passed " + i + " to parameter i. This number must be " + 095 ((n() > 1) 096 ? ("between 1 and " + n()) 097 : "must be exactly 1") 098 ); 099 } 100 101 else if (i > n()) 102 { 103 // Same as above, this 'if' will never execute, because Ret0.get() checks for this 104 // already itselve (as does Tuple0.get). The following 'else' would execute if the 105 // user screwed up, and called "get(i)", with an 'i' that was out of range. 106 107 if (n() == 0) throw new IndexOutOfBoundsException 108 ("You may not invoke 'get' on a Tuple or Ret of size 0"); 109 110 else throw new IndexOutOfBoundsException( 111 "You have requested the " + StrPrint.ordinalIndicator(i) + " field of " + 112 "this class instance-fields, but unfortunately there " + 113 ((n() > 1) 114 ? ("are only " + n() + " fields.") 115 : ("is only 1 field.") 116 ) 117 ); 118 } 119 } 120 121 /** 122 * This provides a quick way to cast the field to the requested class. This may be quicker 123 * typing than using an actual cast, because it will not generate a compiler warning about 124 * unchecked casts. If the cast fails, it will throw the usual {@code ClassCastException}. 125 * 126 * @param i This specifies which field of the implementing {@code RetN} is being requested. 127 * 128 * <BR /><BR /><DIV CLASS=JDHint> 129 * <B STYLE='color:red;'>Important:</B> Unlike a Java array, when a {@code '1'} is passed to 130 * this parameter, it is requesting the first field in the {@code RetN}. Passing a value of 131 * {@code '0'} shall case an {@code IndexOutOfBoundsException} exception. 132 * </DIV> 133 * 134 * @return This returns the <CODE>i<SUP>th</SUP></CODE> field of this class. 135 * 136 * @throws IndexOutOfBoundsException if {@code 'i'} is zero or negative, or greater than 137 * the value of {@code 'N'} for whichever {@code RetN} class is being used. If 138 * {@code 'this'} is an instance of {@code Ret5}, then a value of 6 or greater will cause this 139 * exception to throw. 140 * 141 * @throws ClassCastException If the field cannot be cast to the class provided. 142 * 143 * @see #get(int) 144 * @see #GET(int) 145 */ 146 public final <T> T get(int i, Class<T> c) { return c.cast(get(i)); } 147 148 /** 149 * This is <I><B STYLE='color:red;'>more magic</B></I> with Java's Type-Inferencing being 150 * applied to a Generic-Cast. Note that this method works a lot like (but not identical to) 151 * the later Java {@code 'var'} syntax. Here, Java's Type-Inference Mechanism (inside the 152 * compile-time, not run-time, logic) will cast the result of this method to whatever type is 153 * on the left hand-side of the assignment. 154 * 155 * <BR /><BR /><DIV CLASS=JDHint> 156 * <B STYLE='color:red;'>Type-Inferencing:</B> Remember that the Java-Compiler will output a 157 * Compile-Time Error if it appears that it cannot infer type-parameter {@code 'T'}. 158 * </DIV> 159 * 160 * <BR /><DIV CLASS=JDHintAlt> 161 * <B STYLE='color:red;'>Run-Time Casting:</B> This method will throw a Run-Time 162 * {@code 'ClassCastException'}, if the cast fails. 163 * </DIV> 164 * 165 * @param <T> This Type-Parameter is inferred by whatever assignment is taking place, or however 166 * the result of this method is being applied, programatically. More can be read about Java's 167 * Compile-Time Type-Inferencing Mechanism on the Oracle Website. 168 * 169 * @param i This specifies which field of the implementing {@code RetN} is being requested. 170 * 171 * <BR /><BR /><DIV CLASS=JDHint> 172 * <B STYLE='color:red;'>Important:</B> Unlike a Java array, when a {@code '1'} is passed to 173 * this parameter, it is requesting the first field in the {@code RetN}. Passing a value of 174 * {@code '0'} shall case an {@code IndexOutOfBoundsException} exception. 175 * </DIV> 176 * 177 * @return This returns the <CODE>i<SUP>th</SUP></CODE> field of this class. 178 * 179 * @throws IndexOutOfBoundsException if {@code 'i'} is zero or negative, or greater than 180 * the value of {@code 'N'} for whichever {@code RetN} class is being used. If 181 * {@code 'this'} is an instance of {@code Ret5}, then a value of 6 or greater will cause this 182 * exception to throw. 183 * 184 * @throws ClassCastException If the <CODE>i<SUP>th</SUP></CODE> field of this class cannot be 185 * cast to <I>the type that is inferred for Type-Parameter {@code 'T'}.</I> 186 */ 187 @SuppressWarnings("unchecked") 188 public final <T> T GET(int i) { return (T) get(i); } 189 190 /** 191 * Compares {@code 'this'} with another Java Object for equality. 192 * @param other Any Java Object. 193 * @return If {@code 'this'} instance is equal to the {@code 'other'} instance. 194 */ 195 public boolean equals(Object other) 196 { 197 if (this == other) return true; 198 199 if (! (other instanceof MultiType)) return false; 200 201 MultiType o = (MultiType) other; 202 203 if (o.n() != this.n()) return false; 204 205 Object[] theseFields = asArray(); 206 Object[] thoseFields = o.asArray(); 207 208 209 // Keep this in mind - Three lines ago, the value of n() was already checked! 210 // if (theseFields.length != thoseFields.length) throw new UnreachableError(); 211 212 for (int i=0; i < theseFields.length; i++) 213 214 if (theseFields[i] == thoseFields[i]) 215 continue; 216 217 else if ( 218 ((theseFields[i] != null) && (thoseFields[i] != null)) 219 && 220 (theseFields[i].getClass() == thoseFields[i].getClass()) 221 && 222 theseFields[i].equals(thoseFields[i]) 223 ) 224 continue; 225 226 else 227 return false; 228 229 return true; 230 } 231 232 /** 233 * Builds a hash-code to fulfill Java's {@code java.lang.Object} requirement. This variant of 234 * a hash function simply computes a hashcode for the first two non-null fields of this 235 * instance, and returns their sum. 236 * 237 * <BR /><BR />If there aren't at least two non-null fields in this instance, then the hashcode 238 * for however many have been computed (READ: either 0, or 1) is returned. 239 * 240 * @return a hash-code that may be used for sets and maps like {@code java.util.Hashtable} and 241 * {@code java.util.HashSet}. 242 */ 243 @Override 244 public int hashCode() 245 { 246 // This is the value returned. 247 int hashCode = 0; 248 int count = 0; 249 250 for (Object field : asArray()) 251 252 if (field != null) 253 { 254 hashCode += field.hashCode(); 255 256 // Once two Hash Code's have been computed return the 'SUM' of them. 257 if (++count == 2) return hashCode; 258 } 259 260 return hashCode; 261 } 262 263 /** 264 * Converts this instance of the implementing {@code RetN} to a {@code String}. 265 * @return This instance-object as a {@code String}. 266 */ 267 @Override 268 @LinkJavaSource(handle="MultiTypeToString") 269 public String toString() 270 { return MultiTypeToString.run(this.asArray(), this.n()); } 271 272 /** 273 * Clones the contents of {@code 'this'} instance of. 274 * @return A clone copy of {@code 'this'} object. 275 */ 276 public abstract Object clone(); 277}