/*license*\ XBN-Java: Copyright (C) 2014, Jeff Epstein (aliteralmind __DASH__ github __AT__ yahoo __DOT__ com) This software is dual-licensed under the: - Lesser General Public License (LGPL) version 3.0 or, at your option, any later version; - Apache Software License (ASL) version 2.0. Either license may be applied at your discretion. More information may be found at - http://en.wikipedia.org/wiki/Multi-licensing. The text of both licenses is available in the root directory of this project, under the names "LICENSE_lgpl-3.0.txt" and "LICENSE_asl-2.0.txt". The latest copies may be downloaded at: - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt \*license*/ package com.github.xbn.array; import java.util.Arrays; import java.util.List; import java.util.Collections; /** <p>Casts an object that happens to be an array, of either primitives or non-primitives, to the array itself.</p> <p>POSTS TO ANSWER WITH THIS CLASS:<ul> <li>{@code <a href="http://www.codeguru.com/forum/showthread.php?t=349438">http://www.codeguru.com/forum/showthread.php?t=349438</a>}</li> <li>{@code <a href="http://www.velocityreviews.com/forums/t147493-java-5-0-enum-why-not-valueof-int-ordinal.html">http://www.velocityreviews.com/forums/t147493-java-5-0-enum-why-not-valueof-int-ordinal.html</a>}</li> </ul></p> * @see com.github.xbn.array.primitive.PrimitiveArrayFromObjThatIs * @since 0.1.0 * @author Copyright (C) 2014, Jeff Epstein ({@code aliteralmind __DASH__ github __AT__ yahoo __DOT__ com}), dual-licensed under the LGPL (version 3.0 or later) or the ASL (version 2.0). See source code for details. <a href="http://xbnjava.aliteralmind.com">{@code http://xbnjava.aliteralmind.com}</a>, <a href="https://github.com/aliteralmind/xbnjava">{@code https://github.com/aliteralmind/xbnjava}</a> **/ public class GetArrayFromObjectThatIs { /** <p>Represents the type of element in the array.</p> */ public static enum LMNT_TYPE_IS {BOOLEAN, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, CHAR, OBJECT, ARRAY}; /** <p>The second character in an array's {@code getClass().getName()}. The first character is always a square bracket ({@code '<b>[</b>'}).</p> * <p>Equal to {@code {'Z', 'B', 'S', 'I', 'J', 'F', 'D', 'C', 'L', '['}}</p> */ private static final Character[] LMNT_TYPE_CHAR_TWO = {'Z', 'B', 'S', 'I', 'J', 'F', 'D', 'C', 'L', '['}; public static final List<Character> LMNT_TYPE_CHAR_TWO_LIST = Collections.unmodifiableList(Arrays.asList(LMNT_TYPE_CHAR_TWO)); private LMNT_TYPE_IS eLType = null; private Object oa = null; /** <p>Create a new {@code GetArrayFromObjectThatIs}.</p> * <p>Equal to {@link #GetArrayFromObjectThatIs(Object, boolean) this(array_object, true)}</p> */ public GetArrayFromObjectThatIs(Object array_object) { this(array_object, true); } /** <p>Create a new {@code GetArrayFromObjectThatIs}.</p> * @param array_object The object expected to be an array. When null or not an array, {@link #getObject() getObject}{@code ()} returns null, and {@link #isArray() isArray}{@code ()} returns false. When {@code do_crashIfObjNNullAndNotArray} is true, this <i>must</i> be an array (when non-null). * @exception ClassCastException When {@code do_crashIfObjNNullAndNotArray} is true and {@code array_object} is non-null but not an array. */ public GetArrayFromObjectThatIs(Object array_object, boolean do_crashIfObjNNullAndNotArray) { if(array_object == null) { eLType = null; oa = null; return; } String snClass = null; try { snClass = array_object.getClass().getName(); } catch(NullPointerException npx) { throw new NullPointerException("array_object"); } assert snClass.length() >= 1; if(snClass.length() > 1 && snClass.charAt(0) == '[') { char c2 = snClass.charAt(1); for(int i = 0; i < LMNT_TYPE_CHAR_TWO.length; i++) { if(c2 == LMNT_TYPE_CHAR_TWO[i]) { eLType = LMNT_TYPE_IS.values()[i]; break; } } } if(eLType == null) { if(do_crashIfObjNNullAndNotArray) { throw new ClassCastException("array_object is a " + snClass + ". -- do_crashIfObjNNullAndNotArray is true."); } } oa = array_object; assert ((getObject() == null) == (getELType() == null)); } /** <p>Get the array as an {@code Object}.</p> * @return When the {@code array_object} {@link #GetArrayFromObjectThatIs(Object, boolean) constructor} parameter was both non-null and an array: <b>{@code array_object}</b>, exactly as provided. <br/>When either null or not an array: {@code <b>null</b>} * @see #isArray() * @see #getELType() * @see #isEmpty() * @see #getLength() * @see #getAOByte() * @see #getAOShort() * @see #getAOInt() * @see #getAOLong() * @see #getAOFloat() * @see #getAODouble() * @see #getAOBoolean() * @see #getAOChar() * @see #getAOObject() * @see #getAOArray() */ public final Object getObject() { return oa; } /** <p>Is the object an array?.</p> * @return <code>({@link #getObject() getObject}() != null)</code> */ public final boolean isArray() { return (getObject() != null); } /** <p>Get the type of array.</p> * @return When the {@code array_object} {@link #GetArrayFromObjectThatIs(Object, boolean) constructor} parameter was both non-null and an array: One of the values of {@code LMNT_TYPE_IS}. <br/>When either null or not an array: {@code <b>null</b>} * @see #getObject() */ public final LMNT_TYPE_IS getELType() { return eLType; } /** <p>Get the type of array.</p> * @return <code>({@link #getLength() getLength}{@code ()} == 0)</code> * @exception NullPointerException If {@link #isArray() isArray}{@code ()} is false. */ public final boolean isEmpty() { try { return (getLength() == 0); } catch(NullPointerException npx) { assert (!isArray()); throw new NullPointerException("isArray() is false. -- " + npx); } } /** <p>Get the array's length.</p> * @return If {@link #getELType() getELType}{@code ()} is<ul> <li>{@code BOOLEAN}: {@link #getAOBoolean() getAOBoolean}{@code ().length}</li> <li>{@code SHORT}: {@link #getAOShort() getAOShort}{@code ().length}</li> <li>{@code INT}: {@link #getAOInt() getAOInt}{@code ().length}</li> <li>{@code LONG}: {@link #getAOLong() getAOLong}{@code ().length}</li> <li>{@code FLOAT}: {@link #getAOFloat() getAOFloat}{@code ().length}</li> <li>{@code DOUBLE}: {@link #getAODouble() getAODouble}{@code ().length}</li> <li>{@code CHAR}: {@link #getAOChar() getAOChar}{@code ().length}</li> <li>{@code OBJECT}: {@link #getAOObject() getAOObject}{@code ().length}</li> <li>{@code ARRAY}: {@link #getAOArray() getAOArray}{@code ().length}</li> </ul> * @exception NullPointerException If {@link #isArray() isArray}{@code ()} is false. */ public final int getLength() { try { switch(getELType()) { case BOOLEAN: return getAOBoolean().length; case BYTE: return getAOByte().length; case SHORT: return getAOShort().length; case INT: return getAOInt().length; case LONG: return getAOLong().length; case FLOAT: return getAOFloat().length; case DOUBLE: return getAODouble().length; case CHAR: return getAOChar().length; case OBJECT: return getAOObject().length; case ARRAY: return getAOArray().length; } } catch(NullPointerException npx) { assert !isArray(); throw new NullPointerException("isArray() is false."); } throw new NullPointerException("HUH?! -- " + toString()); } public String toString() { String s = "isArray()=" + getObject(); if(isArray()) { s += ", getObject()=[" + getObject() + "], getELType()=" + getELType() + ", isEmpty()=" + isEmpty() + ", getLength()=" + getLength(); } return s; } /** <p>Get the {@code boolean} array.</p> * @return <code>(boolean[]){@link #getObject() getObject}()</code> * @exception NullPointerException If {@link #isArray() isArray}{@code ()} is false. * @exception ClassCastException If {@link #getELType() getELType}{@code ()} is not <code>{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS LMNT_TYPE_IS.}{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS#BOOLEAN}</code> */ public final boolean[] getAOBoolean() { cinArray("Boolean"); try { return (boolean[])getObject(); } catch(ClassCastException ccx) { throw new ClassCastException(getCCXMsg("Boolean")); } } /** <p>Get the {@code byte} array.</p> * @return <code>(byte[]){@link #getObject() getObject}()</code> * @exception NullPointerException If {@link #isArray() isArray}{@code ()} is false. * @exception ClassCastException If {@link #getELType() getELType}{@code ()} is not <code>{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS LMNT_TYPE_IS.}{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS#BYTE}</code> */ public final byte[] getAOByte() { cinArray("Byte"); try { return (byte[])getObject(); } catch(ClassCastException ccx) { throw new ClassCastException(getCCXMsg("Byte")); } } /** <p>Get the {@code byte} array.</p> * @return <code>(byte[]){@link #getObject() getObject}()</code> * @exception NullPointerException If {@link #isArray() isArray}{@code ()} is false. * @exception ClassCastException If {@link #getELType() getELType}{@code ()} is not <code>{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS LMNT_TYPE_IS.}{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS#SHORT}</code> */ public final short[] getAOShort() { cinArray("Short"); try { return (short[])getObject(); } catch(ClassCastException ccx) { throw new ClassCastException(getCCXMsg("Short")); } } /** <p>Get the {@code int} array.</p> * @return <code>(int[]){@link #getObject() getObject}()</code> * @exception NullPointerException If {@link #isArray() isArray}{@code ()} is false. * @exception ClassCastException If {@link #getELType() getELType}{@code ()} is not <code>{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS LMNT_TYPE_IS.}{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS#INT}</code> */ public final int[] getAOInt() { cinArray("Int"); try { return (int[])getObject(); } catch(ClassCastException ccx) { throw new ClassCastException(getCCXMsg("Int")); } } /** <p>Get the {@code long} array.</p> * @return <code>(long[]){@link #getObject() getObject}()</code> * @exception NullPointerException If {@link #isArray() isArray}{@code ()} is false. * @exception ClassCastException If {@link #getELType() getELType}{@code ()} is not <code>{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS LMNT_TYPE_IS.}{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS#LONG}</code> */ public final long[] getAOLong() { cinArray("Long"); try { return (long[])getObject(); } catch(ClassCastException ccx) { throw new ClassCastException(getCCXMsg("Long")); } } /** <p>Get the {@code float} array.</p> * @return <code>(float[]){@link #getObject() getObject}()</code> * @exception NullPointerException If {@link #isArray() isArray}{@code ()} is false. * @exception ClassCastException If {@link #getELType() getELType}{@code ()} is not <code>{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS LMNT_TYPE_IS.}{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS#FLOAT}</code> */ public final float[] getAOFloat() { cinArray("Float"); try { return (float[])getObject(); } catch(ClassCastException ccx) { throw new ClassCastException(getCCXMsg("Float")); } } /** <p>Get the {@code double} array.</p> * @return <code>(double[]){@link #getObject() getObject}()</code> * @exception NullPointerException If {@link #isArray() isArray}{@code ()} is false. * @exception ClassCastException If {@link #getELType() getELType}{@code ()} is not <code>{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS LMNT_TYPE_IS.}{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS#DOUBLE}</code> */ public final double[] getAODouble() { cinArray("Double"); try { return (double[])getObject(); } catch(ClassCastException ccx) { throw new ClassCastException(getCCXMsg("Double")); } } /** <p>Get the {@code char} array.</p> * @return <code>(char[]){@link #getObject() getObject}()</code> * @exception NullPointerException If {@link #isArray() isArray}{@code ()} is false. * @exception ClassCastException If {@link #getELType() getELType}{@code ()} is not <code>{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS LMNT_TYPE_IS.}{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS#CHAR}</code> */ public final char[] getAOChar() { cinArray("Char"); try { return (char[])getObject(); } catch(ClassCastException ccx) { throw new ClassCastException(getCCXMsg("Char")); } } /** <p>Get the {@code java.lang.Object} array.</p> <p>Note that this {@code getAO} function is unique, as it is the only one which that can be called for multiple types. Specifically, when {@link #getELType() getELType}{@code ()} is <i>either</i> <code>{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS LMNT_TYPE_IS.}{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS#OBJECT}} or {@link GetArrayFromObjectThatIs.LMNT_TYPE_IS#ARRAY}</code></p> <p>Note also, that while <br/>     {@code (new Object[] {"Hello", null, (new int[] {1, 2, 3})})} <br/>has an element type of {@code LMNT_TYPE_IS.ARRAY} <br/>     {@code (new Object[][] {(new String[]{"Hello"}), null, (new int[] {1, 2, 3})})} <br/>has an element type of {@code LMNT_TYPE_IS.OBJECT}</p> * @return <code>(Object[]){@link #getObject() getObject}()</code> * @exception NullPointerException If {@link #isArray() isArray}{@code ()} is false. * @exception ClassCastException If {@code getELType()} is not {@code LMNT_TYPE_IS.OBJECT} */ public final Object[] getAOObject() { cinArray("Object"); try { return (Object[])getObject(); } catch(ClassCastException ccx) { throw new ClassCastException(getCCXMsg("Object")); } } /** <p>Get the <i>array</i> array (the double-array).</p> * @return <code>(Object[][]){@link #getObject() getObject}()</code> * @exception NullPointerException If {@link #isArray() isArray}{@code ()} is false. * @exception ClassCastException If {@link #getELType() getELType}{@code ()} is not <code>{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS LMNT_TYPE_IS.}{@link GetArrayFromObjectThatIs.LMNT_TYPE_IS#ARRAY}</code> * @see #getAOObject() "for a special note on double-arrays" */ public final Object[] getAOArray() { cinArray("Array"); try { return (Object[][])getObject(); } catch(ClassCastException ccx) { throw new ClassCastException(getCCXMsg("Array")); } } private void cinArray(String func_type) { if(!isArray()) { throw new NullPointerException("getAO" + func_type + ": isArray() is false."); } } private String getCCXMsg(String func_type) { return "getAO" + func_type + ": getELType() is " + getELType() + "."; } private static final String LINE_SEP = System.getProperty("line.separator", "\n"); /** <p>Testing function.</p> * @param test_name Descriptive name for this test. * @param array_object The object to test. Passed directly to the {@link #GetArrayFromObjectThatIs(Object, boolean) constructor}. * @param do_crashIfObjNNullAndNotArray The other constructor parameter. * @param iselfCmprcxXpctdInCnstr Is a {@code ClassCastException} expected from the constructor? * @param expectedType_nullIfNotArray If null, then {@code array_object} is either null or not an array. Otherwise, this is its element-type. * @param xpctd_len The expected length of the array, if it is one. * @return <b>{@code null}</b> When actual values are as expected. <br/>A descriptive string when actual is unexpected. */ public static final String getMsgIfUnexpected(String test_name, Object array_object, boolean do_crashIfObjNNullAndNotArray, boolean iselfCmprcxXpctdInCnstr, LMNT_TYPE_IS expectedType_nullIfNotArray, int xpctd_len) { String s = ""; GetArrayFromObjectThatIs cota = null; try { cota = new GetArrayFromObjectThatIs(array_object, do_crashIfObjNNullAndNotArray); if(iselfCmprcxXpctdInCnstr) { s += " - iselfCmprcxXpctdInCnstr=true, but no CCX was thrown by the constructor." + LINE_SEP; } } catch(ClassCastException ccx) { if(!iselfCmprcxXpctdInCnstr) { s += " - iselfCmprcxXpctdInCnstr=false, but: " + ccx + LINE_SEP; } } if(s.length() > 0) { return getFailPre(test_name, array_object, expectedType_nullIfNotArray) + s; } if(cota == null) { return null; } if(expectedType_nullIfNotArray == null) { if(cota.isArray()) { s += " - expectedType_nullIfNotArray=null, but isArray()=true" + LINE_SEP; } } else if(expectedType_nullIfNotArray != cota.getELType()) { s += " - expectedType_nullIfNotArray=" + expectedType_nullIfNotArray + ", but getELType()=" + cota.getELType() + "" + LINE_SEP; } if(cota.isArray()) { if((xpctd_len == 0) && !cota.isEmpty()) { s += " - xpctd_len=0, but isEmpty()=false" + LINE_SEP; } if(xpctd_len != cota.getLength()) { s += " - xpctd_len=" + xpctd_len + ", but getLength()=" + cota.getLength() + LINE_SEP; } } if(s.length() > 0) { return getFailPre(test_name, array_object, expectedType_nullIfNotArray) + s; } return null; } private static String getFailPre(String test_name, Object array_object, LMNT_TYPE_IS expectedType_nullIfNotArray) { return "TEST \"" + test_name + "\" FAILED [array_object=\"" + array_object + "\"]:" + LINE_SEP; } }