// $Id: Dimension.java,v 1.13 2004/12/08 18:08:32 caron Exp $
/*
 * Copyright 1997-2004 Unidata Program Center/University Corporation for
 * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
 * support@unidata.ucar.edu.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
package ucar.nc2;

import java.util.ArrayList;
import java.util.List;

/**
 * A Dimension has an index length, and is used to define the array shape of a Variable.
 * It may be shared among Variables, which provides a simple yet powerful way of associating Variables.
 * When a Dimension is shared, it has a unique name within its Group.
 * It may have a coordinate Variable, which gives each index a coordinate value.
 * A private Dimension cannot have a coordinate Variable, so use shared dimensions with coordinates when possible.
 *
 * @author caron
 * @version $Revision: 1.13 $ $Date: 2004/12/08 18:08:32 $
 */

public class Dimension {
  static public Dimension UNLIMITED = new Dimension( "**", 0, true, true, false); // gets reset when file is read or written
  static public Dimension UNKNOWN = new Dimension( "*", -1, true, false, true); // for Sequences

  protected ArrayList coordVars = new ArrayList();
  protected boolean isUnlimited = false;
  protected boolean isUnknown = false;
  protected boolean isShared = true; // shared means its in a group dimension list.
  protected String name;
  protected int length;

  /**
   * Returns the name of this Dimension; may be null.
   * A Dimension with a null name is called "anonymous" and must be private.
   * Dimension names are unique within a Group.
   */
  public String getName() { return name; }

  /**
   * Get the length of the Dimension.
   */
  public int getLength() { return length; }

  /**
   * If unlimited, then the length can increase; otherwise it is immutable.
   */
  public boolean isUnlimited() { return isUnlimited; }

  /**
   * If unknown, then the length is unknown until the data is read.
   */
  public boolean isUnknown() { return isUnknown; }

  /**
   * If this Dimension is shared, or is private to a Variable.
   * All Dimensions in NetcdfFile.getDimensions() or Group.getDimensions() are shared.
   * Dimensions in the Variable.getDimensions() may be shared or private.
   */
  public boolean isShared() { return isShared; }

  /**
   * Get the coordinate variables or coordinate variable aliases if the dimension has any, else return an empty list.
   * A coordinate variable has this as its single dimension, and names this Dimensions's the coordinates.
   * A coordinate variable alias is the same as a coordinate variable, but its name must match the dimension name.
   * If numeric, coordinate axis must be strictly monotonically increasing or decreasing.
   * @see Variable#getCoordinateDimension
   */
  public List getCoordinateVariables() { return coordVars; }

  /**
   * Instances which have same contents are equal.
   * Careful!! this is not object identity !!
   */
  public boolean equals(Object oo) {
    if (this == oo) return true;
    if ( !(oo instanceof Dimension)) return false;
    return hashCode() == oo.hashCode();
  }

  /** Override Object.hashCode() to implement equals. */
  public int hashCode() {
    if (hashCode == 0) {
      int result = 17;
      if (isUnlimited()) result++;
      if (getName() != null)
        result = 37*result + getName().hashCode();
      if (isUnknown()) result++;
      result = 37*result + getLength();
      if (isShared()) result++;
      hashCode = result;
    }
    return hashCode;
  }
  private volatile int hashCode = 0;



  /** String representation. */
  public String toString() {
    return writeCDL( false);
  }

  /** String representation. */
  public String writeCDL(boolean strict) {
    StringBuffer buff = new StringBuffer();
    buff.append("   "+getName());
    if (isUnlimited())
      buff.append(" = UNLIMITED;   // ("+getLength()+" currently)");
    else if (isUnknown())
      buff.append(" = UNKNOWN;" );
    else
      buff.append(" = "+getLength() +";");
    if (!strict && (getCoordinateVariables().size() > 0))
      buff.append("   // (has coord.var)");
    return buff.toString();
  }

  /**
   * Constructor
   * @param name name must be unique within group
   * @param length length, or UNLIMITED.length or UNKNOWN.length
   * @param isShared whether its shared or local to Variable.
   */
  public Dimension(String name, int length, boolean isShared) {
    this.name = name;
    this.length = length;
    this.isShared = isShared;
  }

  /**
   * Constructor
   * @param name name must be unique within group. Can be null only if not shared.
   * @param length length, or UNLIMITED.length or UNKNOWN.length
   * @param isShared whether its shared or local to Variable.
   * @param isUnlimited whether the length can grow.
   * @param isUnknown whether the length is unknown until the data is read.
   */
  public Dimension(String name, int length, boolean isShared, boolean isUnlimited, boolean isUnknown) {
    this.name = name;
    this.length = length;
    this.isShared = isShared;
    this.isUnlimited = isUnlimited;
    this.isUnknown = isUnknown;
  }

  // used to make global dimensions
  protected Dimension(String name, Dimension from) {
    this.name = name;
    this.length = from.length;
    this.isUnlimited = from.isUnlimited;
    this.isUnknown = from.isUnknown;
    this.isShared = from.isShared;
    this.coordVars = from.coordVars;
  }

  /** Add a coordinate variable or coordinate variable alias.
   * Remove previous if matches full name. */
  public void addCoordinateVariable( Variable v) {
    for (int i = 0; i < coordVars.size(); i++) {
      Variable cv = (Variable) coordVars.get(i);
      if (v.getName().equals(cv.getName())) {
        coordVars.remove(cv);
        break;
      }
    }
    coordVars.add(v);
  }
  /** Set whether this is unlimited, meaning length can increase. */
  public void setUnlimited( boolean b) { isUnlimited = b; }
  /** Set whether the length is unknown. */
  public void setUnknown( boolean b) { isUnknown = b; }
  /** Set whether this is shared. */
  public void setShared( boolean b) { isShared = b; }
  /** Set the Dimension length. */
  public void setLength( int n) {
    this.length = n;
    // hashCode = 0;
  }
}

/* Change History:
   $Log: Dimension.java,v $
   Revision 1.13  2004/12/08 18:08:32  caron
   implement _CoordinateAliasForDimension

   Revision 1.12  2004/12/03 04:46:27  caron
   no message

   Revision 1.11  2004/12/01 05:53:44  caron
   ncml pass 2, new convention parsing

   Revision 1.10  2004/10/29 00:14:12  caron
   no message

   Revision 1.9  2004/10/13 19:45:12  caron
   add strict NCDump

   Revision 1.8  2004/10/06 19:03:44  caron
   clean up javadoc
   change useV3 -> useRecordsAsStructure
   remove id, title, from NetcdfFile constructors
   add "in memory" NetcdfFile

   Revision 1.7  2004/08/26 17:55:09  caron
   no message

   Revision 1.6  2004/08/19 19:22:38  edavis
   Make methods for building a Dimension public.

   Revision 1.5  2004/08/18 19:56:42  caron
   2.2 alpha (2)

   Revision 1.4  2004/08/16 20:53:44  caron
   2.2 alpha (2)

   Revision 1.3  2004/07/12 23:40:16  caron
   2.2 alpha 1.0 checkin

   Revision 1.2  2004/07/06 19:28:09  caron
   pre-alpha checkin

   Revision 1.1.1.1  2003/12/04 21:05:27  caron
   checkin 2.2

   Revision 1.1.1.1  2003/06/06 20:13:19  caron
   import nio

   Revision 1.7  2001/08/10 21:18:27  caron
   add close()
 */