// $Id: NetcdfFileWriteable.java,v 1.8 2004/09/22 18:44: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 ucar.ma2.*;

import java.util.*;
import java.io.IOException;

/**
 * Create/Write netCDF files. <p>
 * Because of the limitations of the underlying implementation, netcdf
 * files can only have Dimensions, Attributes and Variables added to it
 * at creation time. Thus, when a file is first opened, it in is "define mode"
 * where these may added. Once create() is called, you can no longer add, delete, or modify
 * the Dimensions, Attributes or Variables. <p>
 * After create has been called you can then write the Variables' data values.
 *
 * @see NetcdfFile
 * @author caron
 * @version $Revision: 1.8 $ $Date: 2004/09/22 18:44:32 $
 */

public class NetcdfFileWriteable extends NetcdfFile {
  private HashMap varHash = new HashMap(50);
  private boolean defineMode;
  private boolean fill = false;
  private boolean hasUnlimited = false; // has an unlimited dimension

  /** Open an existing Netcdf file for writing data.
   *  Cannot add new objects, you can only read/write data to existing Variables.
   *  @param location name of existing file to open.
   */
  public NetcdfFileWriteable(String location) throws IOException {
    super();
    this.location = location;
    ucar.unidata.io.RandomAccessFile raf = new ucar.unidata.io.RandomAccessFile(location, "rw");
    spi = SPFactory.getServiceProvider();
    spi.open(raf, this, null);
    defineMode = false;
  }

  /** Create a new Netcdf file, put it into define mode. Make calls to addXXX(), then
   *  when all objects are added, call create(). You cannot read or write data until create() is called.
   *  @param location name of new file to open; if it exists, will overwrite it.
   *  @param fill  if true, the data is first written with fill values.
   *  Leave false if you expect to write all data values, set to true if you want to be
   *  sure that unwritten data values have the fill value in it. (default is false)
  */
  public NetcdfFileWriteable(String location, boolean fill) {
    super();
    this.location = location;
    this.fill = fill;
    defineMode = true;
  }

  /** Open a new Netcdf file, put it into define mode.
   *  @deprecated use NetcdfFileWriteable(String filename) or
   *    NetcdfFileWriteable(String filename, boolean fill)
   */
  public NetcdfFileWriteable() {
    super();
    defineMode = true;
  }

  /** Set the filename of a new file to be created: call before calling create().
   * @param filename name of new file to create.
   * @deprecated use NetcdfFileWriteable(String filename, boolean fill);
   */
  public void setName( String filename) { this.location = filename; }

  /** Set the fill flag: call before calling create().
   *  If true, the data is first written with fill values.
   *  Default is fill = false.
   *  Leave false if you expect to write all data values, set to true if you want to be
   *  sure that unwritten data values have the fill value in it.
   *  @param fill set fill mode true or false
   *  @deprecated use NetcdfFileWriteable(String filename, boolean fill);
   */
  public void setFill( boolean fill) { this.fill = fill; }

  ////////////////////////////////////////////
  //// use these calls to create a new file

  /**
    * Add a Dimension to the file. Must be in define mode.
    * @param dimName name of dimension
    * @param length size of dimension.
    * @return the created dimension
    */
   public Dimension addDimension(String dimName, int length) {
     if (!defineMode) throw new UnsupportedOperationException("not in define mode");
     Dimension dim = new Dimension( dimName, length, true, false, false);
     super.addDimension( null, dim);
     return dim;
   }

  /**
    * Add a Dimension to the file. Must be in define mode.
    * @param dimName name of dimension
    * @param length size of dimension.
    * @param isShared if dimension is shared
    * @param isUnlimited if dimension is unlimited
    * @param isUnknown if dimension is unknown
    * @return the created dimension
    */
   public Dimension addDimension(String dimName, int length, boolean isShared, boolean isUnlimited, boolean isUnknown) {
     if (!defineMode) throw new UnsupportedOperationException("not in define mode");
     Dimension dim = new Dimension( dimName, length, isShared, isUnlimited, isUnknown);
     super.addDimension( null, dim);
     return dim;
   }

  /**
   * Add a Global attribute of type String to the file. Must be in define mode.
   *
   * @param name name of attribute.
   * @param value value of atribute.
   */
  public void addGlobalAttribute(String name, String value) {
    if (!defineMode) throw new UnsupportedOperationException("not in define mode");
    super.addAttribute( null, new Attribute(name, value));
  }

  /**
   * Add a Global attribute of type Number to the file. Must be in define mode.
   * @param name name of attribute.
   * @param value must be of type Float, Double, Integer, Short or Byte
   */
  public void addGlobalAttribute(String name, Number value) {
    if (!defineMode) throw new UnsupportedOperationException("not in define mode");
    super.addAttribute( null, new Attribute(name, value));
  }

  /**
   * Add a Global attribute of type Array to the file. Must be in define mode.
   * @param name name of attribute.
   * @param values Array of values
   */
  public void addGlobalAttribute(String name, Array values) {
    if (!defineMode) throw new UnsupportedOperationException("not in define mode");
    super.addAttribute( null, new Attribute(name, values));
  }
  /**
   * Add a Global attribute of type Array to the file. Must be in define mode.
   * @param name name of attribute.
   * @param value must be 1D array of double, float, int, short, char, or byte
   * @deprecated use addGlobalAttribute(String name, Array value);
   *
  public void addGlobalAttribute(String name, Object value) {
    if (!defineMode)
      throw new UnsupportedOperationException("not in define mode");
    Attribute att = new Attribute(name);
    att.setValueOld( value);
    super.addGlobalAttribute( att);
  } */

  /**
   * Add a variable to the file. Must be in define mode.
   * @param varName name of Variable, must be unique with the file.
   * @param componentType type of underlying element: String, double or Double, etc.
   * @param dims array of Dimensions for the variable, must already have been added.
   * @deprecated use addVariable(String varName, DataType dataType, ArrayList dims);
   */
  public void addVariable(String varName, Class componentType, Dimension[] dims) {
    ArrayList list = new ArrayList();
    for (int i=0; i<dims.length; i++)
      list.add( dims[i]);

    addVariable( varName, DataType.getType( componentType), list);
  }

  /**
   * Add a variable to the file. Must be in define mode.
   * @param varName name of Variable, must be unique with the file.
   * @param dataType type of underlying element
   * @param dims list of Dimensions for the variable, must already have been added.
   */
  public void addVariable(String varName, DataType dataType, Dimension[] dims) {
    ArrayList list = new ArrayList();
    for (int i=0; i<dims.length; i++)
      list.add( dims[i]);

    addVariable( varName, dataType, list);
  }

  /**
   * Add a variable to the file. Must be in define mode.
   * @param varName name of Variable, must be unique with the file.
   * @param dataType type of underlying element
   * @param dims list of Dimensions for the variable, must already have been added.
   */
  public void addVariable(String varName, DataType dataType, ArrayList dims) {
    if (!defineMode)
      throw new UnsupportedOperationException("not in define mode");

    Variable v = new Variable(this, rootGroup, null, varName);
    v.setDataType( dataType);
    v.setDimensions( dims);
    varHash.put( varName, v);

    super.addVariable( null, v);
  }

  /**
   * Add an attribute to the named Variable. Must be in define mode.
   *
   * @param varName name of variable. must already have been added to the file.
   * @param att Attribute to add.
   */
  public void addVariableAttribute(String varName, Attribute att) {
    if (!defineMode)
      throw new UnsupportedOperationException("not in define mode");

    Variable v = (Variable) varHash.get( varName);
    if (null == v)
      throw new IllegalArgumentException("addVariableAttribute variable name not found = <"+ varName+">");
    v.addAttribute( att);
  }

  /**
   * Add an attribute of type String to the named Variable. Must be in define mode.
   *
   * @param varName name of variable. must already have been added to the file.
   * @param attName name of attribute.
   * @param value String value of atribute.
   */
  public void addVariableAttribute(String varName, String attName, String value) {
    addVariableAttribute( varName, new Attribute( attName, value));
  }


  /**
   * Add an attribute of type Number to the named Variable. Must be in define mode.
   *
   * @param varName name of attribute. IllegalArgumentException if not valid name.
   * @param attName name of attribute.
   * @param value must be of type Float, Double, Integer, Short or Byte
   */
  public void addVariableAttribute(String varName, String attName, Number value) {
    addVariableAttribute( varName, new Attribute( attName, value));
  }

  /**
   * Add an attribute of type Array to the named Variable. Must be in define mode.
   *
   * @param varName name of attribute. IllegalArgumentException if not valid name.
   * @param attName name of attribute.
   * @param value Array of valkues
   */
  public void addVariableAttribute(String varName, String attName, Array value) {
    Attribute att = new Attribute(attName);
    att.setValues( value);
    addVariableAttribute( varName, att);
  }

  /**
   * Add an attribute of type Array to the named Variable. Must be in define mode.
   *
   * @param varName name of attribute. IllegalArgumentException if not valid name.
   * @param attName name of attribute.
   * @param value must be 1D array of double, float, int, short, char, or byte
   * @deprecated use addVariableAttribute(String varName, String attName, Array value);
   *
  public void addVariableAttribute(String varName, String attName, Object value) {
    Attribute att = new Attribute(attName);
    att.setValueOld( value);
    addVariableAttribute( varName, att);
  } */

  /**
   * After you have added all of the Dimensions, Variables, and Attributes,
   * call create() to actually create the file. You must be in define mode.
   * After this call, you are no longer in define mode, and cannot return to it.
   */
  public void create() throws java.io.IOException {
    if (!defineMode)
      throw new UnsupportedOperationException("not in define mode");

    spi = SPFactory.getServiceProvider();
    spi.create( location, this, fill);

    defineMode = false;
  }

  ////////////////////////////////////////////
  //// use these calls to write to the file

  /** Write data to the named variable, origin assumed to be 0. Must not be in define mode.
   * @param varName name of variable. IllegalArgumentException if variable name does not exist.
   * @param values write this array; must be same type and rank as Variable
   * @throws IOException
   */
  public void write(String varName, Array values) throws java.io.IOException, InvalidRangeException {
    write(varName, new int[ values.getRank()], values);
  }

  /** Write data to the named variable. Must not be in define mode.
   * @param varName name of variable. IllegalArgumentException if variable name does not exist.
   * @param origin offset within the variable to start writing.
   * @param values write this array; must be same type and rank as Variable
   * @throws IOException
   */
  public void write(String varName, int [] origin, Array values) throws java.io.IOException, InvalidRangeException {
    if (defineMode)
      throw new UnsupportedOperationException("in define mode");
    ucar.nc2.Variable v2 = findVariable( varName);
    if (v2 == null)
      throw new IllegalArgumentException("NetcdfFileWriteable.write illegal variable name = "+ varName);
    spi.writeData(v2, Range.factory(origin, values.getShape()), values);
    v2.invalidateCache();
  }

  /**
   * Flush anything written to disk.
   */
  public void flush() throws java.io.IOException {
    spi.flush();
  }

  /** close the file. */
  public synchronized void close() throws java.io.IOException {
    flush();
    spi.close();
    isClosed = true;
  }

}

/* Change History:
   $Log: NetcdfFileWriteable.java,v $
   Revision 1.8  2004/09/22 18:44:32  caron
   move common to ucar.unidata

   Revision 1.7  2004/09/22 13:46:36  caron
   *** empty log message ***

   Revision 1.6  2004/08/26 17:55:10  caron
   no message

   Revision 1.5  2004/08/17 19:20:04  caron
   2.2 alpha (2)

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

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

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

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

   Revision 1.15  2003/06/03 20:06:05  caron
   fix javadocs

   Revision 1.14  2003/04/08 15:06:21  caron
   nc2 version 2.1

   Revision 1.13  2002/05/24 00:09:12  caron
   add flush()

   Revision 1.12  2002/04/22 22:54:56  caron
   use ArrayAdapter2.java

   Revision 1.11  2001/08/10 21:18:28  caron
   add close()

   Revision 1.10  2001/05/01 15:10:34  caron
   clean up VariableStandardized.java; add HTTP access

   Revision 1.9  2001/02/21 21:21:25  caron
   add VariableStandardized

   Revision 1.8  2001/02/06 22:18:33  caron
   final changes for beta .9

   Revision 1.7  2001/01/25 01:08:55  caron
   release nc2

   Revision 1.6  2000/09/19 16:25:52  caron
   NetcdfFileWriteable complete, better docs

   Revision 1.5  2000/08/18 04:14:37  russ
   Licensed under GNU LGPL.

   Revision 1.4  2000/08/17 23:44:47  caron
   prepare for c**right clobbering


   Revision 1.2  2000/06/30 16:47:40  caron
   minor revs for GDV release

   Revision 1.1  2000/04/26 21:02:41  caron
   writeable Netcdf files

 */

