///////////////////////////////////////////////////////////////////////////// // Copyright (c) 1998, California Institute of Technology. // ALL RIGHTS RESERVED. U.S. Government Sponsorship acknowledged. // // Please read the full copyright notice in the file COPYRIGHT // in this directory. // // Author: Jake Hamby, NASA/Jet Propulsion Laboratory // Jake.Hamby@jpl.nasa.gov ///////////////////////////////////////////////////////////////////////////// package dods.dap; import java.io.DataInputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Enumeration; import java.util.Vector; import dods.dap.Server.InvalidParameterException; /** * This class is used to hold arrays of other DODS data. The elements of the * array can be simple or compound data types. There is no limit on the * number of dimensions an array can have, or on the size of each dimension. * <p> * If desired, the user can give each dimension of an array a name. You can, * for example, have a 360x180 array of temperatures, covering the whole * globe with one-degree squares. In this case, you could name the first * dimension "Longitude" and the second dimension "Latitude". This can * help prevent a great deal of confusion. * <p> * The <code>DArray</code> is used as part of the <code>DGrid</code> class, * where the dimension names are crucial to its structure. The dimension names * correspond to "Map" vectors, holding the actual values for that column of * the array. * <p> * Each array dimension carries with it its own projection information. The * projection inforamtion takes the form of three integers: the start, stop, * and stride values. This is clearest with an example. Consider a * one-dimensional array 10 elements long. If the start value of the * dimension constraint is 3, then the constrained array appears to be seven * elements long. If the stop value is changed to 7, then the array appears * to be five elements long. If the stride is changed to two, the array will * appear to be 3 elements long. Array constraints are written as * <code>[start:stride:stop]</code>. * * <code><pre> * A = [1 2 3 4 5 6 7 8 9 10] * A[3::] = [4 5 6 7 8 9 10] * A[3::7] = [4 5 6 7 8] * A[3:2:7] = [4 6 8] * A[0:3:9] = [1 4 7 10] * </pre></code> * * NB: DODS uses zero-based indexing. * * @version $Revision: 1.3 $ * @author jehamby * @see DGrid * @see DVector * @see BaseType */ public class DArray extends DVector implements Cloneable { /** A Vector of DArrayDimension information (i.e. the shape) */ private Vector dimVector; /** Constructs a new <code>DArray</code>. */ public DArray() { this(null); } /** * Constructs a new <code>DArray</code> with name <code>n</code>. * @param n the name of the variable. */ public DArray(String n) { super(n); dimVector = new Vector(); } /** * Returns a clone of this <code>DArray</code>. A deep copy is performed * on all data inside the variable. * * @return a clone of this <code>DArray</code>. */ public Object clone() { DArray a = (DArray)super.clone(); a.dimVector = new Vector(); for(int i=0; i<dimVector.size(); i++) { DArrayDimension d = (DArrayDimension)dimVector.elementAt(i); a.dimVector.addElement(d.clone()); } return a; } /** * Returns the DODS type name of the class instance as a <code>String</code>. * @return the DODS type name of the class instance as a <code>String</code>. */ public String getTypeName() { return "Array"; } /** * Checks for internal consistency. For <code>DArray</code>, verify that * the dimension vector is not empty. * * @param all for complex constructor types, this flag indicates whether to * check the semantics of the member variables, too. * @exception BadSemanticsException if semantics are bad, explains why. * @see BaseType#checkSemantics(boolean) */ public void checkSemantics(boolean all) throws BadSemanticsException { super.checkSemantics(all); if (dimVector.isEmpty()) throw new BadSemanticsException("An array variable must have dimensions"); } /** * Write the variable's declaration in a C-style syntax. This * function is used to create textual representation of the Data * Descriptor Structure (DDS). See <em>The DODS User Manual</em> for * information about this structure. * * @param os The <code>PrintWriter</code> on which to print the * declaration. * @param space Each line of the declaration will begin with the * characters in this string. Usually used for leading spaces. * @param print_semi a boolean value indicating whether to print a * semicolon at the end of the declaration. * * @see BaseType#printDecl(PrintWriter, String, boolean) */ public void printDecl(PrintWriter os, String space, boolean print_semi, boolean constrained) { // BEWARE! Since printDecl()is (multiplely) overloaded in BaseType and // all of the different signatures of printDecl() in BaseType lead to // one signature, we must be careful to override that SAME signature // here. That way all calls to printDecl() for this object lead to // this implementation. //os.println("DArray.printDecl()"); getPrimitiveVector().printDecl(os, space, false, constrained); for(Enumeration e = dimVector.elements(); e.hasMoreElements(); ) { DArrayDimension d = (DArrayDimension)e.nextElement(); os.print("["); if(d.getName() != null) os.print(d.getName() + " = "); os.print(d.getSize() + "]"); } if(print_semi) os.println(";"); } /** * Prints the value of the variable, with its declaration. This * function is primarily intended for debugging DODS applications and * text-based clients such as geturl. * * @param os the <code>PrintWriter</code> on which to print the value. * @param space this value is passed to the <code>printDecl</code> method, * and controls the leading spaces of the output. * @param print_decl_p a boolean value controlling whether the * variable declaration is printed as well as the value. * @see BaseType#printVal(PrintWriter, String, boolean) */ public void printVal(PrintWriter os, String space, boolean print_decl_p) { // print the declaration if print decl is true. // for each dimension, // for each element, // print the array given its shape, number of dimensions. // Add the `;' if (print_decl_p) { printDecl(os, space, false); os.print(" = "); } int dims = numDimensions(); int shape[] = new int[dims]; int i = 0; for (Enumeration e = dimVector.elements(); e.hasMoreElements(); ) { DArrayDimension d = (DArrayDimension)e.nextElement(); shape[i++] = d.getSize(); } printArray(os, 0, dims, shape, 0); if (print_decl_p) os.println(";"); } /** * Print an array. This is a private member function. * @param os is the stream used for writing * @param index is the index of VEC to start printing * @param dims is the number of dimensions in the array * @param shape holds the size of the dimensions of the array. * @param offset holds the current offset into the dimension array. * @return the number of elements written. */ private int printArray(PrintWriter os, int index, int dims, int shape[], int offset) { if (dims == 1) { os.print("{"); for(int i=0; i<shape[offset]-1; i++) { getPrimitiveVector().printSingleVal(os, index++); os.print(", "); } getPrimitiveVector().printSingleVal(os, index++); os.print("}"); return index; } else { os.print("{"); for(int i=0; i<shape[offset]-1; i++) { index = printArray(os, index, dims-1, shape, offset+1); os.print(","); } index = printArray(os, index, dims-1, shape, offset+1); os.print("}"); return index; } } /** * Given a size and a name, this function adds a dimension to the * array. For example, if the <code>DArray</code> is already 10 elements * long, calling <code>appendDim</code> with a size of 5 will transform the * array into a 10x5 matrix. Calling it again with a size of 2 will * create a 10x5x2 array, and so on. * * @param size the size of the desired new dimension. * @param name the name of the new dimension. */ public void appendDim(int size, String name) { DArrayDimension newDim = new DArrayDimension(size, name); dimVector.addElement(newDim); } /** * Add a dimension to the array. Same as <code>appendDim(size, null)</code>. * @param size the size of the desired new dimension. * @see DArray#appendDim(int, String) */ public void appendDim(int size) { appendDim(size, null); } /** * Returns an <code>Enumeration</code> of <code>DArrayDimension</code>s * in this array. * @return an <code>Enumeration</code> of <code>DArrayDimension</code>s * in this array. */ public final Enumeration getDimensions() { return dimVector.elements(); } /** * Returns the number of dimensions in this array. * @return the number of dimensions in this array. */ public final int numDimensions() { return dimVector.size(); } /** Returns the <code>DArrayDimension</code> object for * the dimension requested. It makes sure that the dimension requested * exists. */ public DArrayDimension getDimension(int dimension) throws InvalidParameterException { // QC the passed dimension if (dimension < dimVector.size() ) return((DArrayDimension)dimVector.get(dimension)); else throw new InvalidParameterException("DArray.getDimension(): Bad dimension request: dimension > # of dimensions"); } /** Returns the <code>DArrayDimension</code> object for * the first dimension. */ public DArrayDimension getFirstDimension(){ return((DArrayDimension)dimVector.get(0)); } }