All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.n52.matlab.control.extensions.MatlabNumericArray Maven / Gradle / Ivy

The newest version!
package org.n52.matlab.control.extensions;

/*
 * Copyright (c) 2013, Joshua Kaplan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 * following conditions are met:
 *  - Redistributions of source code must retain the above copyright notice, this list of conditions and the following
 *    disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 *    following disclaimer in the documentation and/or other materials provided with the distribution.
 *  - Neither the name of matlabcontrol nor the names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Acts as a MATLAB array of doubles. MATLAB arrays of any numeric type may be represented by a
 * {@code MatlabNumericArray}, but precision may be lost in the process. Dimensions of 2 and greater are supported. (No
 * array in MATLAB has a dimension less than 2.) This representation is a copy of the MATLAB data, not a live view.
 * Retrieving large arrays from MATLAB can result in a {@link OutOfMemoryError}; if this occurs you may want to either
 * retrieve only part of the array from MATLAB or increase your Java Virtual Machine's heap size.
 * 

* Note: Sparse arrays are not supported. Attempts to retrieve a sparse may not throw an exception, but the * result will not be valid. *

* Both the real and imaginary components of a MATLAB array are supported. If the array has no imaginary values then * attempts to access these values will result in a {@link IllegalStateException} being thrown. *

* Arrays in MATLAB are stored in a linear manner. The number and lengths of the dimensions are stored separately from * the real and imaginary value entries. Each dimension has a fixed length. (MATLAB's array implementation is known as * a dope vector.) *

* Java has no multidimensional array type. To support multiple dimensions, Java allows for creating arrays of any data * type, including arrays. (Java's array implementation is known as an Iliffe vector.) A two dimensional array of * {@code double}s, {@code double[][]}, is just an array of {@code double[]}. A result of this is that each * {@code double[]} can have a different length. When not all inner arrays for a given dimension have the same length, * then the array is known as as a jagged array (also known as a ragged array). *

* When an array is retrieved from MATLAB the resulting Java array is never jagged. When a {@code MatlabNumericArray} is * constructed from Java arrays, the arrays provided may be jagged; see the * {@link #MatlabNumericArray(DoubleArrayType, java.lang.Object, java.lang.Object) main constructor} for details. *

* Each instance knows the number of dimensions it represents and can create the corresponding multidimensional Java * array. In order to do this in a type safe manner the methods {@link #getRealArray(DoubleArrayType) getRealArray(...)} * and {@link #getImaginaryArray(DoubleArrayType) getImaginaryArray(...)} exist. Convenience methods exist to easily * retrieve the arrays as two, three, and four dimensional arrays. All of these methods will throw a * {@link ArrayDimensionException} if the array is not actually of that dimension. It is also possible to retrieve * values from the array without converting it to the corresponding multidimensional Java array. This can be done either * by using the index into the underlying linear MATLAB array, or by using the multidimensional indices. Retrieving * values in this manner does not require the computation and memory necessary to create the multidimensional Java * array. *

* While this class mimics the dimension and lengths of a MATLAB array, it uses Java's 0-index convention instead of * MATLAB's 1-index convention. For instance in MATLAB if an array were indexed into as {@code array(3,4,7,2)}, then in * Java to retrieve the same entry the indexing would be performed as {@code array[2][3][6][1]}. *

* Once constructed, this class is unconditionally thread-safe. If the data provided to a constructor is modified while * construction is occurring, problems may occur. * * @see MatlabTypeConverter#setNumericArray(java.lang.String, matlabcontrol.extensions.MatlabNumericArray) * @see MatlabTypeConverter#getNumericArray(java.lang.String) * * @since 4.0.0 * * @author Joshua Kaplan */ public final class MatlabNumericArray { /** * Linear array of real values. */ private final double[] _realValues; /** * Linear array of imaginary values. */ private final double[] _imaginaryValues; /** * The lengths for each dimension of the array. */ private final int[] _lengths; /** * The type of double array. */ private final DoubleArrayType _arrayType; /** * If the array was retrieved from MATLAB. */ private final boolean _fromMatlab; /** * If the array is composed entirely of real values. If this is {@code true} then {@link #_imaginaryValues} will be * {@code null}. */ private final boolean _isReal; /** * Constructs an array from data retrieved from MATLAB. * * @param real * @param imaginary * @param lengths */ MatlabNumericArray(double[] real, double[] imaginary, int[] lengths) { _fromMatlab = true; _realValues = real; _imaginaryValues = imaginary; _isReal = (imaginary == null); _lengths = lengths; _arrayType = DoubleArrayType.getInstance(lengths.length); } /** * Constructs a numeric array from Java arrays that can be transferred to MATLAB. The {@code imaginary} array * may be {@code null}, if so then this array will be real. References to the arrays passed in are not kept, and * modifying the array data after this class has been constructed will have no effect. If the data is modified * concurrently with this class's construction, problems may arise. *

* The arrays may be jagged; however, MATLAB does not support jagged arrays and therefore the arrays will be treated * as if they had uniform length for each dimension. For each dimension the maximum length is determined. If both * the {@code real} and {@code imaginary} arrays are provided then the maximum length per dimension is determined * across both arrays. For parts of the array that have a length less than the maximum length, {@code 0} will be * used. * * @param * @param type may not be {@code null} * @param real may not be {@code null} * @param imaginary may be {@code null}, if {@code null} then this array will be real * @throws NullPointerException */ public MatlabNumericArray(DoubleArrayType type, T real, T imaginary) { //Validate input if(type == null) { throw new NullPointerException("The type of the arrays may not be null."); } if(real == null) { throw new NullPointerException("Real array may not be null."); } _fromMatlab = false; _isReal = (imaginary == null); //Store type _arrayType = type; //Determine lengths _lengths = new int[type.getDimensions()]; int[] realLengths = computeBoundingLengths(real); for(int i = 0; i < realLengths.length; i++) { _lengths[i] = Math.max(_lengths[i], realLengths[i]); } if(imaginary != null) { int[] imaginaryLengths = computeBoundingLengths(imaginary); for(int i = 0; i < imaginaryLengths.length; i++) { _lengths[i] = Math.max(_lengths[i], imaginaryLengths[i]); } } //Linearize arrays _realValues = linearize(real, _lengths); if(imaginary != null) { _imaginaryValues = linearize(imaginary, _lengths); } else { _imaginaryValues = null; } } /** * Convenience constructor, equivalent to {@code new MatlabNumericArray(DoubleArrayType.DIM_2, real, imaginary)}. * * @param real * @param imaginary * @throws NullPointerException */ public MatlabNumericArray(double[][] real, double[][] imaginary) { this(DoubleArrayType.DIM_2, real, imaginary); } /** * Convenience constructor, equivalent to {@code new MatlabNumericArray(DoubleArrayType.DIM_3, real, imaginary)}. * * @param real * @param imaginary * @throws NullPointerException */ public MatlabNumericArray(double[][][] real, double[][][] imaginary) { this(DoubleArrayType.DIM_3, real, imaginary); } /** * Convenience constructor, equivalent to {@code new MatlabNumericArray(DoubleArrayType.DIM_4, real, imaginary)}. * * @param real * @param imaginary * @throws NullPointerException */ public MatlabNumericArray(double[][][][] real, double[][][][] imaginary) { this(DoubleArrayType.DIM_4, real, imaginary); } /** * Computes the total size of an array with lengths specified by {@code lengths}. * * @param lengths * @return */ private static int getTotalSize(int[] lengths) { int size = 0; for(int length : lengths) { if(size == 0) { size = length; } else if(length != 0) { size *= length; } } return size; } /** * Gets the real value at {@code linearIndex} treating this array as the underlying one dimensional array. This * is equivalent to indexing into a MATLAB array with just one subscript. * * @param linearIndex * @return real value at {@code linearIndex} * @throws ArrayIndexOutOfBoundsException */ public double getRealValue(int linearIndex) { return _realValues[linearIndex]; } /** * Gets the imaginary value at {@code linearIndex} treating this array as the underlying one dimensional array. * This is equivalent to indexing into a MATLAB array with just one subscript. * * @param linearIndex * @return imaginary value at {@code linearIndex} * @throws ArrayIndexOutOfBoundsException * @throws IllegalStateException if the array is real */ public double getImaginaryValue(int linearIndex) { if(_isReal) { throw new IllegalStateException("array is real"); } return _imaginaryValues[linearIndex]; } /** * Gets the real value at the specified {@code indices}. The amount of indices provided must be the number of * dimensions in the array. * * @param indices * @return real value at {@code indices} * @throws ArrayDimensionException if number of indices is not the number of dimensions * @throws IndexOutOfBoundsException if the indices are out of bound */ public double getRealValue(int... indices) { return this.getValue(_realValues, indices); } /** * Gets the imaginary value at the specified {@code indices}. The amount of indices provided must be the number of * dimensions in the array. * * @param indices * @return imaginary value at {@code indices} * @throws ArrayDimensionException if number of indices is not the number of dimensions * @throws IndexOutOfBoundsException if the indices are out of bound * @throws IllegalStateException if the array is real */ public double getImaginaryValue(int... indices) { if(_isReal) { throw new IllegalStateException("array is real"); } return this.getValue(_imaginaryValues, indices); } private double getValue(double[] values, int... indices) throws ArrayDimensionException, ArrayIndexOutOfBoundsException { double value; if(indices.length == this.getDimensions()) { //Check the indices are in bounds for(int i = 0; i < indices.length; i++) { if(indices[i] >= _lengths[i]) { throw new IndexOutOfBoundsException("[" + indices[i] + "] is out of bounds for dimension " + i + " where the length is " + _lengths[i]); } } value = values[multidimensionalIndicesToLinearIndex(_lengths, indices)]; } else { throw new ArrayDimensionException(_arrayType.getDimensions(), indices.length); } return value; } /** * The number of dimensions this array has. * * @return number of dimensions */ public int getDimensions() { return _arrayType.getDimensions(); } /** * Returns the lengths of each dimension with respect to their index. In MATLAB the first dimension (0 index in the * returned integer array) is the row length. The second dimension is the column length. The third dimension and * beyond are pages. The length of the returned array will be the number of dimensions returned by * {@link #getDimensions()}. * * @return lengths */ public int[] getLengths() { int[] lengthsCopy = new int[_lengths.length]; System.arraycopy(_lengths, 0, lengthsCopy, 0, _lengths.length); return lengthsCopy; } /** * The length of the underlying linear array. * * @return length */ public int getLength() { return _realValues.length; } private T getAsJavaArray(DoubleArrayType type, double[] values) { if(type.getDimensions() != _arrayType.getDimensions()) { throw new ArrayDimensionException(_arrayType.getDimensions(), type.getDimensions()); } return multidimensionalize(values, type._arrayClass, _lengths); } /** * Returns a {@code double} array of type {@code T} that holds the real values from the MATLAB array. * * @param * @param type * @return * @throws ArrayDimensionException if the array is not of the dimension specified by {@code type} */ public T getRealArray(DoubleArrayType type) { return getAsJavaArray(type, _realValues); } /** * Equivalent to {@code getRealArray(DoubleArrayType.DIM_2)}. * * @return * @throws ArrayDimensionException if the array is not a two dimensional array */ public double[][] getRealArray2D() { return this.getRealArray(DoubleArrayType.DIM_2); } /** * Equivalent to {@code getRealArray(DoubleArrayType.DIM_3)}. * * @return * @throws ArrayDimensionException if the array is not a three dimensional array */ public double[][][] getRealArray3D() { return this.getRealArray(DoubleArrayType.DIM_3); } /** * Equivalent to {@code getRealArray(DoubleArrayType.DIM_4)}. * * @return * @throws ArrayDimensionException if the array is not a four dimensional array */ public double[][][][] getRealArray4D() { return this.getRealArray(DoubleArrayType.DIM_4); } /** * Returns a {@code double} array of type {@code T} that holds the imaginary values from the MATLAB array. * * @param * @param type * @return * @throws ArrayDimensionException if the array is not of the dimension specified by {@code type} * @throws IllegalStateException if the array is real */ public T getImaginaryArray(DoubleArrayType type) { if(_isReal) { throw new IllegalStateException("array is real"); } return getAsJavaArray(type, _imaginaryValues); } /** * Equivalent to {@code getImaginaryArray(DoubleArrayType.DIM_2)}. * * @return * @throws ArrayDimensionException if the array is not a two dimensional array * @throws IllegalStateException if the array is real */ public double[][] getImaginaryArray2D() { return this.getImaginaryArray(DoubleArrayType.DIM_2); } /** * Equivalent to {@code getImaginaryArray(DoubleArrayType.DIM_3)}. * * @return * @throws ArrayDimensionException if the array is not a three dimensional array * @throws IllegalStateException if the array is real */ public double[][][] getImaginaryArray3D() { return this.getImaginaryArray(DoubleArrayType.DIM_3); } /** * Equivalent to {@code getImaginaryArray(DoubleArrayType.DIM_4)}. * * @return * @throws ArrayDimensionException if the array is not a four dimensional array * @throws IllegalStateException if the array is real */ public double[][][][] getImaginaryArray4D() { return this.getImaginaryArray(DoubleArrayType.DIM_4); } /** * Returns {@code true} if the array has no imaginary values, {@code false} otherwise. Equivalent to the MATLAB * {@code isreal} function. * * @return */ public boolean isReal() { return _isReal; } /** * Returns the underlying linear array of real values. Returns the actual array, not a copy. * * @return */ double[] getRealLinearArray() { return _realValues; } /** * Returns the underlying linear array of imaginary values. Returns the actual array, not a copy. * * @return */ double[] getImaginaryLinearArray() { return _imaginaryValues; } /** * Returns a brief description of this array. The exact details of this representation are unspecified and are * subject to change. * * @return */ @Override public String toString() { return "[" + this.getClass() + " dimensions=" + this.getDimensions() + "," + " linearLength=" + this.getLength() + "," + " lengths=" + Arrays.toString(_lengths) + "," + " fromMATLAB=" + _fromMatlab + "]"; } /** * Linear index to multidimensional indices. Similar to MATLAB's (@code ind2sub} function. * * @param lengths the lengths of the array in each dimension * @param linearIndex * @return */ private static int[] linearIndexToMultidimensionalIndices(int[] lengths, int linearIndex) { int[] indices = new int[lengths.length]; if(lengths.length == 1) { indices[0] = linearIndex; } else { int pageSize = lengths[0] * lengths[1]; int pageNumber = linearIndex / pageSize; //Row and column int indexInPage = linearIndex % pageSize; indices[0] = indexInPage % lengths[0]; indices[1] = indexInPage / lengths[0]; //3rd dimension and above int accumSize = 1; for(int dim = 2; dim < lengths.length; dim++) { indices[dim] = (pageNumber / accumSize) % lengths[dim]; accumSize *= lengths[dim]; } } return indices; } /** * Multidimensional indices to linear index. Similar to MATLAB's (@code sub2ind} function. * * @param lengths the lengths of the array in each dimension * @param indices * @return * @throws IllegalArgumentException thrown if the length of {@code lengths} and {@code indices} are not the same */ private static int multidimensionalIndicesToLinearIndex(int[] lengths, int[] indices) { if(lengths.length != indices.length) { throw new IllegalArgumentException("There must be an equal number of lengths [" + lengths.length + "] " + "and indices [" + indices.length + "]"); } int linearIndex = 0; int accumSize = 1; for(int i = 0; i < lengths.length; i++) { linearIndex += accumSize * indices[i]; accumSize *= lengths[i]; } return linearIndex; } /** * Creates a {@code double} array of type {@code T} with the length of each dimension specified by {@code lengths}. * As the array is created it is populated with the values of {@code linearArray}. * * @param * @param linearArray * @param outputArrayType * @param lengths * @return */ private static T multidimensionalize(double[] linearArray, Class outputArrayType, int[] lengths) { return multidimensionalize_internal(linearArray, outputArrayType, lengths, 0, new int[0]); } /** * The real logic of the {@link #multidimensionalize(double[], java.lang.Class, int[])} method. This method * recurs on itself, hence the need for the extra parameters. This method does not store state in any external * variables. * * @param * @param linearArray * @param outputArrayType * @param lengths * @param indexIntoLengths should be {@code 0} initially * @param currIndices should be an empty integer array initially * @return */ private static T multidimensionalize_internal(double[] linearArray, Class outputArrayType, int[] lengths, int indexIntoLengths, int[] currIndices) { Class arrayType = outputArrayType.getComponentType(); int arrayLength = lengths[indexIntoLengths]; T array = (T) Array.newInstance(arrayType, arrayLength); //If what was created holds an array, then fill it if(arrayType.isArray()) { //If the array that was created holds the double array, double[] if(arrayType.equals(double[].class)) { //The index in the multidimensional array being created int[] primitiveArrayIndices = new int[currIndices.length + 2]; System.arraycopy(currIndices, 0, primitiveArrayIndices, 0, currIndices.length); //Iterate over the created array, placing a double[] array in each index for(int i = 0; i < arrayLength; i++) { primitiveArrayIndices[primitiveArrayIndices.length - 2] = i; //Create the primitive array double[] primitiveArray = new double[lengths[lengths.length - 1]]; //Fill the primitive array with values from the linear array for(int j = 0; j < primitiveArray.length; j++) { primitiveArrayIndices[primitiveArrayIndices.length - 1] = j; int linearIndex = multidimensionalIndicesToLinearIndex(lengths, primitiveArrayIndices); primitiveArray[j] = linearArray[linearIndex]; } //Place primitive array into the enclosing array Array.set(array, i, primitiveArray); } } else { //Iterate over the created array, placing an array in each index (using recursion) for(int i = 0; i < arrayLength; i++) { int[] nextIndices = new int[currIndices.length + 1]; System.arraycopy(currIndices, 0, nextIndices, 0, currIndices.length); nextIndices[nextIndices.length - 1] = i; Object innerArray = multidimensionalize_internal(linearArray, arrayType, lengths, indexIntoLengths + 1, nextIndices); Array.set(array, i, innerArray); } } } //If a double[] was just created, then the original request was to create a one dimensional array else { System.arraycopy(linearArray, 0, array, 0, arrayLength); } return array; } /** * Determines the maximum length for each dimension of the array. * * @param array * @return */ private static int[] computeBoundingLengths(Object array) { DoubleArrayType type = DoubleArrayType.getInstanceUnsafe(array.getClass()); int[] maxLengths = new int[type.getDimensions()]; //The length of this array int arrayLength = Array.getLength(array); maxLengths[0] = arrayLength; //If the array holds arrays as its entries if(!array.getClass().getComponentType().equals(double.class)) { //For each entry in the array for(int i = 0; i < arrayLength; i++) { //childLengths' information will be one index ahead of maxLengths int[] childLengths = computeBoundingLengths(Array.get(array, i)); for(int j = 0; j < childLengths.length; j++) { maxLengths[j + 1] = Math.max(maxLengths[j + 1], childLengths[j]); } } } return maxLengths; } /** * Creates a linearized version of the {@code array} with maximum length in each dimension specified by * {@code lengths}. No verification is performed that the dimensions of the array match the length of the lengths * array. * * @param array * @param lengths * @return */ private static double[] linearize(Object array, int[] lengths) { //Create linear array with equal size of the array double[] linearArray = new double[getTotalSize(lengths)]; //Fill linearArray with values from array linearize_internal(linearArray, array, lengths, new int[0]); return linearArray; } /** * Performs the linearization using recursion. * * @param linearArray array to be filled * @param array source array * @param lengths the lengths of the array for the initial array supplied before any recursion * @param currIndices must initially be an empty integer array */ private static void linearize_internal(double[] linearArray, Object array, int[] lengths, int[] currIndices) { //Base case if(array.getClass().equals(double[].class)) { int[] doubleArrayIndices = new int[currIndices.length + 1]; System.arraycopy(currIndices, 0, doubleArrayIndices, 0, currIndices.length); //Fill linear array with contents of the double[] array double[] doubleArray = (double[]) array; for(int i = 0; i < doubleArray.length; i++) { doubleArrayIndices[doubleArrayIndices.length - 1] = i; int linearIndex = multidimensionalIndicesToLinearIndex(lengths, doubleArrayIndices); linearArray[linearIndex] = doubleArray[i]; } } else { int arrayLength = Array.getLength(array); for(int i = 0; i < arrayLength; i++) { int[] nextIndices = new int[currIndices.length + 1]; System.arraycopy(currIndices, 0, nextIndices, 0, currIndices.length); nextIndices[nextIndices.length - 1] = i; linearize_internal(linearArray, Array.get(array, i), lengths, nextIndices); } } } /** * Represents attempting to retrieve or manipulate a MATLAB array as the wrong dimension. */ public static class ArrayDimensionException extends RuntimeException { private static final long serialVersionUID = 0xC400L; private final int _actualNumberOfDimensions; private final int _usedAsNumberOfDimensions; ArrayDimensionException(int actualNumDim, int usedAsNumDim) { super("Array has " + actualNumDim + " dimension(s), it cannot be used as if it had " + usedAsNumDim + " dimension(s)."); _actualNumberOfDimensions = actualNumDim; _usedAsNumberOfDimensions = usedAsNumDim; } /** * The actual number of dimensions the array has. * * @return */ public int getActualNumberOfDimensions() { return _actualNumberOfDimensions; } /** * The number of dimensions that were used when interacting with the array. * * @return */ public int getUsedNumberOfDimensions() { return _usedAsNumberOfDimensions; } } /** * An array type of dimension 2 or greater which holds {@code double}s. Instances for dimensions 2 through 9 are * available as {@code public static} fields. *

* This class is unconditionally thread-safe. * * @param an array of 2 or more dimensions which holds {@code double}s */ public static final class DoubleArrayType { /** * Caches loaded {@code DoubleArrayType}s. */ private static final Map, DoubleArrayType> CLASS_TO_ARRAY_TYPE = new ConcurrentHashMap, DoubleArrayType>(); /** * Representation of {@code double[][]} class. */ public static final DoubleArrayType DIM_2 = getInstance(double[][].class); /** * Representation of {@code double[][][]} class. */ public static final DoubleArrayType DIM_3 = getInstance(double[][][].class); /** * Representation of {@code double[][][][]} class. */ public static final DoubleArrayType DIM_4 = getInstance(double[][][][].class); /** * Representation of {@code double[][][][][]} class. */ public static final DoubleArrayType DIM_5 = getInstance(double[][][][][].class); /** * Representation of {@code double[][][][][][]} class. */ public static final DoubleArrayType DIM_6 = getInstance(double[][][][][][].class); /** * Representation of {@code double[][][][][][][]} class. */ public static final DoubleArrayType DIM_7 = getInstance(double[][][][][][][].class); /** * Representation of {@code double[][][][][][][][]} class. */ public static final DoubleArrayType DIM_8 = getInstance(double[][][][][][][][].class); /** * Representation of {@code double[][][][][][][][][]} class. */ public static final DoubleArrayType DIM_9 = getInstance(double[][][][][][][][][].class); /** * The array class represented. */ private final Class _arrayClass; /** * The number of dimensions of the array type. */ private final int _numDimensions; /** * Constructs a representation of a multidimensional array of {@code double}s. * * @param arrayClass * @throws IllegalArgumentException if the type is not an array holding {@code double}s */ private DoubleArrayType(Class arrayClass) { if(!isDoubleArrayType(arrayClass)) { throw new IllegalArgumentException(arrayClass + " does not hold doubles"); } _arrayClass = arrayClass; _numDimensions = getNumberOfDimensions(arrayClass); } /** * Gets an instance of {@code DoubleArrayType} where {@code T} is the type of {@code arrayType}. {@code T} * must be an array of 1 or more dimensions that holds {@code double}s. This is intended for getting array types * in excess of 9 dimensions, as dimensions 2 through 9 are represented by constants {@code DIM_2 ... DIM_9}. *

* Contrived example usage:
* {@code DoubleArrayType type3D = DoubleArrayType.getInstance(double[][][].class);} * * @param * @param arrayType * * @return * * @throws IllegalArgumentException if the type is not an array holding {@code double}s or the type is of less * than 2 dimensions */ public static DoubleArrayType getInstance(Class arrayType) { if(arrayType.equals(double[].class)) { throw new IllegalArgumentException(arrayType + " not supported, must be 2 or more dimensions"); } return getInstanceUnsafe(arrayType); } /** * Behaves the same as {@link #getInstance(java.lang.Class)} except that {@code double[]} is valid. This is * needed by some of the recursive algorithms in {@code MatlabNumericArray}. */ static DoubleArrayType getInstanceUnsafe(Class arrayType) { if(!CLASS_TO_ARRAY_TYPE.containsKey(arrayType)) { DoubleArrayType type = new DoubleArrayType(arrayType); CLASS_TO_ARRAY_TYPE.put(arrayType, type); } return CLASS_TO_ARRAY_TYPE.get(arrayType); } static DoubleArrayType getInstance(int dimensions) { DoubleArrayType type; //Construct the name of the class String className = ""; for(int i = 0; i < dimensions; i++) { className += "["; } className += "D"; //Retrieve the class, and then getInstance the corresponding DoubleArrayType try { type = getInstanceUnsafe(Class.forName(className)); } catch(ClassNotFoundException e) { type = null; } return type; } /** * The number of dimensions of the array type. * * @return */ public int getDimensions() { return _numDimensions; } /** * The type of array. The array holds {@code double}s, and may be of any dimension 2 or greater. * * @return */ public Class getArrayClass() { return _arrayClass; } /** * If {@code type} is an array of one or more dimensions which holds doubles. * * @param type * @return */ private static boolean isDoubleArrayType(Class type) { boolean isType; if(type.isArray()) { while(type.isArray()) { type = type.getComponentType(); } isType = type.equals(double.class); } else { isType = false; } return isType; } /** * Returns the number of dimensions the array type has. If {@code type} is not a type of array then {@code 0} * will be returned. * * @param type * @return */ private static int getNumberOfDimensions(Class type) { int numDim = 0; while(type.isArray()) { numDim++; type = type.getComponentType(); } return numDim; } /** * Returns a brief description of this double array type. The exact details of this representation are * unspecified and are subject to change. * * @return */ @Override public String toString() { return "[" + this.getClass().getName() + " class=" + _arrayClass + ", dimensions=" + _numDimensions + "]"; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy