001package Torello.Java; 002 003import java.util.Vector; 004import java.util.function.*; 005import java.lang.reflect.Array; 006 007/** 008 * This may be used to check that array have equal lengths - if two or more arrays are expected to 009 * be parallel, but their lengths are not equal, then this exception should be thrown. This class 010 * also provides multiple <CODE>'check'</CODE> methods that will automatically scan for the 011 * specific error cases, and it will provide consistently worded and formatted error messages to 012 * the invoking code. 013 * 014 * <BR /><BR />Note that you may also request that the checker look for nulls, and throw a 015 * {@code NullPointerException} if nulls are found. Furthermore, primitive-arrays may also be 016 * checked. 017 */ 018public class ParallelArrayException extends IllegalArgumentException 019{ 020 /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUIDEX> */ 021 public static final long serialVersionUID = 1; 022 023 /** Constructs a {@code ParallelArrayException} with no detail message. */ 024 public ParallelArrayException() 025 { super(); } 026 027 /** 028 * Constructs an {@code ParallelArrayException} with the specified detail message. 029 * @param message the detail message. 030 */ 031 public ParallelArrayException(String message) 032 { super(message); } 033 034 /** 035 * Constructs a new exception with the specified detail {@code 'message'} and 036 * {@code 'cause'}. 037 * 038 * <BR /><BR /><B CLASS=JDDescLabel>NOTE:</B> 039 * 040 * <BR /><BR />The detail message associated with cause is not automatically incorporated into 041 * this exception's detail message. 042 * 043 * @param message The detail message (which is saved for later retrieval by th 044 * {@code Throwable.getMessage()} method). 045 * 046 * @param cause the cause (which is saved for later retrieval by the 047 * {@code Throwable.getCause()} method). (A null value is permitted, and indicates that the 048 * cause is nonexistent or unknown.) 049 */ 050 public ParallelArrayException(String message, Throwable cause) 051 { super(message); initCause(cause); } 052 053 /** 054 * Constructs a new exception with the specified {@code 'cause'} and a detail message of 055 * {@code (cause==null ? null : cause.toString())} (which typically contains the class 056 * and detail message of cause). This constructor is useful for exceptions that are little 057 * more than wrappers for other throwables. 058 * 059 * @param cause The cause (which is saved for later retrieval by the 060 * {@code Throwable.getCause()} method). (A null value is permitted, and indicates that the 061 * cause is nonexistent or unknown.) 062 */ 063 public ParallelArrayException(Throwable cause) 064 { super(); initCause(cause); } 065 066 // If the array itself was a null pointer, this is all you can do 067 private static void NPE(String name) 068 { throw new NullPointerException("Array '" + name + "' was passed a Null Pointer."); } 069 070 // This ensures that the error messages all look the same (same text), and that I don't 071 // retype this many times. 072 private static void CHECK( 073 Object tArr, int tLen, String tName, 074 Object uArr, int uLen, String uName 075 ) 076 { 077 if (tLen != uLen) throw new ParallelArrayException( 078 "The length of Array '" + tName + "' (" + tArr.getClass().getSimpleName() + ") " + 079 "is " + tLen + "\n" + 080 "The length of Array '" + uName + "' (" + uArr.getClass().getSimpleName() + ") " + 081 "is " + uLen + "\n" + 082 "Unfortunately, these arrays must be parallel, and thus their lengths must be equal." 083 ); 084 } 085 086 /** 087 * This will check that the parameter {@code 'arr1'} (which is presumed to be an array) has 088 * an identical length to that of parameter {@code 'arr2'}. If these two arrays do not have 089 * the same length, a {@code ParallelArrayException} with throw with an error message. 090 * 091 * <BR /><BR />Since the purpose of this code is to generate reasonable error messages, without 092 * having to retype this sort of thing of and again, the 'Variable Name' of the arrays are 093 * required as parameters. The only effects that they have on this code is that they ensure 094 * <I>the output exception messages include those names</I> (The array names are not part of 095 * the error checking process). 096 * 097 * <BR /><BR /><B CLASS=JDDescLabel>NOTE:</B> 098 * 099 * <BR />If for whatever reason, the caller of this method accidentally sends an {@code Object} 100 * to this method which isn't an {@code Array} at all, an exception will throw. There isn't 101 * really a way to guarantee "Compile Time Checking" for this sort of thing. Make sure this 102 * method is for checking that <I><B>Array's are Parallel</I></B>. 103 * 104 * <BR /><BR /><B CLASS=JDDescLabel>FINALLY:</B> 105 * 106 * <BR />Just about any type of array may be passed - including {@code primitive-arrays} 107 * ({@code int[], float[]}) etc... 108 * 109 * <BR /><BR />The method {@link StrReplace#r(String, char[], char[])} makes use of this check 110 * as follows: 111 * 112 * <DIV CLASS=EXAMPLE>{@code 113 * public static String r(String s, char[] matchChars, char[] replaceChars) 114 * { 115 * // Check that these arrays have equal lengths, and if not throw the 116 * // ParallelArrayException. 117 * ParallelArrayException.check(matchChars, "matchChars", replaceChars, "replaceChars"); 118 * ... 119 * } 120 * }</DIV> 121 * 122 * @param arr1 This may be any primitive or {@code Object[]} array. 123 * 124 * @param arr1Name This should be the 'Variable Name' of that array, in your code. This is 125 * merely used for 'Pretty Printing' purposes. 126 * 127 * @param arr2 This may be any other primitive or {@code Object[]} array. 128 * 129 * @param arr2Name This should be the 'Variable Name' of the second array. 130 * 131 * @throws ParallelArrayException This exception throws if the arrays don't have equal 132 * lengths. 133 * 134 * @throws ArrayExpectedError If a non-Array {@code Object} is passed to either of the 135 * Array Parameters. An {@code 'Error'} is thrown, rather than an {@code 'Exception'} since 136 * the purpose of this check is to identify parallel arrays. Providing a non-array reference 137 * to this method signals a flaw in the code itself. 138 */ 139 public static void check 140 (Object arr1, String arr1Name, Object arr2, String arr2Name) 141 { 142 if (arr1 == null) NPE(arr1Name); 143 if (arr2 == null) NPE(arr2Name); 144 145 if (! arr1.getClass().isArray()) throw new ArrayExpectedError 146 ("Array Parameter '" + arr1Name + "' is not an array."); 147 148 if (! arr2.getClass().isArray()) throw new ArrayExpectedError 149 ("Array Parameter '" + arr2Name + "' is not an array."); 150 151 CHECK( 152 arr1, java.lang.reflect.Array.getLength(arr1), arr1Name, 153 arr2, java.lang.reflect.Array.getLength(arr2), arr2Name 154 ); 155 } 156 157 /** 158 * This will check that the parameter {@code 'tArr'} has an identical length to that of 159 * parameter {@code 'arr'} (which is presumed to be an array). If these two arrays do not have 160 * the same length, a {@code ParallelArrayException} with throw with an error message. 161 * 162 * <BR /><BR />This method differs from the previous 163 * {@link #check(Object, String, Object, String)}, in that the Variable-Type Parameter 164 * {@code '<T>'} guarantees that at least one of the parameters must be an array. This 165 * facilitates another check - that for nulls in the array. It may or may not be desired to 166 * check for the prescense of {@code 'null'}, but if it is that can be requested by passing 167 * {@code 'TRUE'} to the parameter {@code 'throwIfHasNulls'}. 168 * 169 * <BR /><BR />The {@link StrReplace#r(boolean, String, String[], char[])} utilizes this method 170 * as below: 171 * 172 * <DIV CLASS=EXAMPLE>{@code 173 * public static String r 174 * (boolean ignoreCase, String s, String[] matchStrs, char[] replaceChars) 175 * { 176 * // Make sure the 'matchStrs' array is parallel to 'replaceChars', and also make sure 177 * // 'matchStr' does not have null elements. If not, throw ParallelArrayException 178 * ParallelArrayException.check 179 * (matchStrs, "matchStrs", true, replaceChars, "replaceChars"); 180 * ... 181 * } 182 * }</DIV> 183 * 184 * @param <T> This type-parameter is merely being utilized to allow <I>any array type</I> 185 * to be received by this method. It is nothing more than a place-holder (similar to the 186 * {@code '?'} for generic classes). 187 * 188 * @param tArr This may be any {@code Object[]} instance array. 189 * 190 * @param tName This should be the 'Variable Name' of that array, in your code. This is 191 * merely used for 'Pretty Printing' purposes. 192 * 193 * @param throwIfHasNulls This parameter requests to check for the presence of a {@code 'null'} 194 * inside the {@code 'tArr'} array, and will throw a {@code NullPointerException} is one is 195 * found. 196 * 197 * @param arr This may be any primitive or {@code Object[]} array. 198 * 199 * @param arrName This should be the 'Variable Name' of the second array. 200 * 201 * @throws ParallelArrayException This exception throws if the arrays don't have equal 202 * lengths. 203 * 204 * @throws NullPointerException This exception throws if the {@code 'tArr'} contains any 205 * {@code 'null'} values. 206 * 207 * @throws ArrayExpectedError If a non-Array {@code Object} is passed to either of the 208 * Array Parameters. An {@code 'Error'} is thrown, rather than an {@code 'Exception'} since 209 * the purpose of this check is to identify parallel arrays. Providing a non-array reference 210 * to this method signals a flaw in the code itself. 211 */ 212 public static <T> void check( 213 T[] tArr, String tName, boolean throwIfHasNulls, 214 Object arr, String arrName 215 ) 216 { 217 if (tArr == null) NPE(tName); 218 if (arr == null) NPE(arrName); 219 220 if (! arr.getClass().isArray()) throw new ArrayExpectedError 221 ("Array Parameter '" + arrName + "' is not an array."); 222 223 CHECK(tArr, tArr.length, tName, arr, Array.getLength(arr), arrName); 224 225 if (throwIfHasNulls) 226 for (int i=0; i < tArr.length; i++) 227 if (tArr[i] == null) 228 throw new NullPointerException( 229 tName + '[' + i + "] (" + tArr.getClass().getSimpleName() + ") " + 230 "contains a null reference." 231 ); 232 } 233 234 /** 235 * This will check that the parameter {@code 'tArr'} has an identical length to that of 236 * parameter {@code 'arr'} (which is presumed to be an array). If these two arrays do not have 237 * the same length, a {@code ParallelArrayException} with throw with an error message. 238 * 239 * <BR /><BR />This method differs from the previous 240 * {@link #check(Object, String, Object, String)}, in that using Variable-Type Parameters 241 * ({@code '<T>'} and {@code '<U>'}) guarantee that both parameters are arrays. The purpose 242 * here is that it facilitates another check - that for the presence of {@code 'nulls'}. It 243 * may or may not be desired to check for the prescense of {@code 'null'} within the arrays, 244 * but if it is, simply pass {@code 'TRUE'} to either of the {@code 'throwIfHasNulls'} 245 * parameters, and the corresponding array will be checked. 246 * 247 * <BR /><BR />This check is used by {@link StrReplace#r(boolean, String, String[], String[])} 248 * as below: 249 * 250 * <DIV CLASS=EXAMPLE>{@code 251 * public static String r 252 * (boolean ignoreCase, String s, String[] matchStrs, String[] replaceStrs) 253 * { 254 * // Make sure these arrays are parallel, and if not throw ParallelArrayException 255 * // If there are any 'null' values in these arrays, throw NullPointerException 256 * ParallelArrayException.check 257 * (matchStrs, "matchStrs", true, replaceStrs, "replaceStrs", true); 258 * ... 259 * } 260 * }</DIV> 261 * 262 * @param <T> This type-parameter is merely being utilized to allow <I>any array type</I> 263 * to be received by this method. It is nothing more than a place-holder (similar to the 264 * {@code '?'} for generic classes). 265 * 266 * @param <U> This type-parameter is merely being utilized to allow <I>any array type</I> 267 * to be received by this method. It is nothing more than a place-holder (similar to the 268 * {@code '?'} for generic classes). 269 * 270 * @param tArr This may be any {@code Object[]} instance array. 271 * 272 * @param tName This should be the 'Variable Name' of that array, in your code. This is 273 * merely used for 'Pretty Printing' purposes. 274 * 275 * @param throwIfTHasNulls This parameter requests to check for the presence of a 276 * {@code 'null'} inside the {@code 'tArr'} array, and will throw a 277 * {@code NullPointerException} is one is found. 278 * 279 * @param uArr This may be any {@code Object[]} instance array. 280 * 281 * @param uName This should be the 'Variable Name' of that array, in your code. This is 282 * merely used for 'Pretty Printing' purposes. 283 * 284 * @param throwIfUHasNulls This parameter requests to check for the presence of a 285 * {@code 'null'} inside the {@code 'uArr'} array, and will throw a 286 * {@code NullPointerException} is one is found. 287 * 288 * @throws ParallelArrayException This exception throws if the arrays don't have equal 289 * lengths. 290 * 291 * @throws NullPointerException This exception throws if the {@code 'tArr'} contains any 292 * {@code 'null'} values. 293 */ 294 public static <T, U> void check( 295 T[] tArr, String tName, boolean throwIfTHasNulls, 296 U[] uArr, String uName, boolean throwIfUHasNulls 297 ) 298 { 299 if (tArr == null) NPE(tName); 300 if (uArr == null) NPE(uName); 301 302 CHECK(tArr, tArr.length, tName, uArr, uArr.length, uName); 303 304 if (throwIfTHasNulls) 305 for (int i=0; i < tArr.length; i++) 306 if (tArr[i] == null) 307 throw new NullPointerException( 308 tName + '[' + i + "] (" + tArr.getClass().getSimpleName() + ") " + 309 "contains a null reference." 310 ); 311 312 if (throwIfUHasNulls) 313 for (int i=0; i < uArr.length; i++) 314 if (uArr[i] == null) 315 throw new NullPointerException( 316 uName + "[" + i + "] (" + uArr.getClass().getSimpleName() + ") " + 317 "contains a null reference." 318 ); 319 } 320}