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}