Package ucar.nc2.jni.netcdf

Source Code of ucar.nc2.jni.netcdf.JniIosp

/*
* Copyright 1998-2009 University Corporation for Atmospheric Research/Unidata
*
* Portions of this software were developed by the Unidata Program at the
* University Corporation for Atmospheric Research.
*
* Access and use of this software shall impose the following obligations
* and understandings on the user. The user is granted the right, without
* any fee or cost, to use, copy, modify, alter, enhance and distribute
* this software, and any derivative works thereof, and its supporting
* documentation for any purpose whatsoever, provided that this entire
* notice appears in all copies of the software, derivative works and
* supporting documentation.  Further, UCAR requests that the user credit
* UCAR/Unidata in any publications that result from the use of this
* software or in any product that includes this software. The names UCAR
* and/or Unidata, however, may not be used in any advertising or publicity
* to endorse or promote any products or commercial entity unless specific
* written permission is obtained from UCAR/Unidata. The user also
* understands that UCAR/Unidata is not obligated to provide the user with
* any support, consulting, training or assistance of any kind with regard
* to the use, operation and performance of this software nor to provide
* the user with any updates, revisions, new versions or "bug fixes."
*
* THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "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 UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
*/

package ucar.nc2.jni.netcdf;

import ucar.nc2.iosp.AbstractIOServiceProvider;
import ucar.nc2.iosp.IOServiceProvider;
import ucar.nc2.iosp.IospHelper;
import ucar.nc2.*;
import ucar.nc2.util.CancelTask;
import ucar.unidata.io.RandomAccessFile;
import ucar.ma2.*;

import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.nio.IntBuffer;

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.NativeLongByReference;

/**
* IOSP for reading netcdf files through jni interface to netcdf4 library
*
* @author caron
* @since Oct 30, 2008
*/
public class JniIosp extends AbstractIOServiceProvider {
  static private org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(JniIosp.class);
  private static boolean debug = false, debugCompoundAtt= false, debugEnumAtt = false;

  public boolean isValidFile(RandomAccessFile raf) throws IOException {
    return false;
  }

  public String getFileTypeId() {
    return "netCDF";
  }

  public String getFileTypeDescription() {
    return "Netcdf/JNI";
  }

  private NetcdfFile ncfile;
  private int ncid = -1, format;
  private boolean isClosed = false;

  public void open(RandomAccessFile raf, NetcdfFile ncfile, CancelTask cancelTask) throws IOException {
    load(); // load jni
    this.ncfile = ncfile;

    // open
    if (debug) System.out.println("open " + ncfile.getLocation());
    IntByReference ncidp = new IntByReference();
    int ret = nc4.nc_open(ncfile.getLocation(), 0, ncidp);
    if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
    ncid = ncidp.getValue();

    // format
    IntByReference formatp = new IntByReference();
    ret = nc4.nc_inq_format(ncid, formatp);
    if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
    format = formatp.getValue();
    System.out.printf("open %s id=%d format=%d %n", ncfile.getLocation(), ncid, format);

    // read root group
    makeGroup(ncid, new Group4(ncfile.getRootGroup(), null));

    ncfile.finish();
  }

  private void makeGroup(int grpid, Group4 g4) throws IOException {
    makeDimensions(grpid, g4);
    makeUserTypes(grpid, g4.g);

    // group attributes
    IntByReference ngattsp = new IntByReference();
    int ret = nc4.nc_inq_natts(grpid, ngattsp);
    if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
    List<Attribute> gatts = makeAttributes(grpid, NCLibrary.NC_GLOBAL, ngattsp.getValue(), null);
    for (Attribute att : gatts) {
      ncfile.addAttribute(g4.g, att);
      if (debug) System.out.printf(" add Global Attribute %s %n", att);
    }

    makeVariables(grpid, g4.g);

    if (format == NCLibrary.NC_FORMAT_NETCDF4) {
      // read subordinate groups
      IntByReference numgrps = new IntByReference();
      ret = nc4.nc_inq_grps(grpid, numgrps, Pointer.NULL);
      if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
      int[] grids = new int[numgrps.getValue()];
      ret = nc4.nc_inq_grps(grpid, numgrps, grids);
      if (ret != 0) throw new IOException(nc4.nc_strerror(ret));

      for (int i = 0; i < grids.length; i++) {
        byte[] name = new byte[NCLibrary.NC_MAX_NAME + 1];
        ret = nc4.nc_inq_grpname(grids[i], name);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        Group child = new Group(ncfile, g4.g, makeString(name));
        g4.g.addGroup(child);
        makeGroup(grids[i], new Group4(child, g4));
      }
    }

  }

  private void makeDimensions(int grpid, Group4 g4) throws IOException {
    IntByReference ndimsp = new IntByReference();
    int ret = nc4.nc_inq_ndims(grpid, ndimsp);
    if (ret != 0) throw new IOException(nc4.nc_strerror(ret));

    int[] dimids = new int[ndimsp.getValue()];
    ret = nc4.nc_inq_dimids(grpid, ndimsp, dimids, 0);
    if (ret != 0) throw new IOException(nc4.nc_strerror(ret));

    IntByReference nunlimdimsp = new IntByReference();
    int[] unlimdimids = new int[NCLibrary.NC_MAX_DIMS];
    ret = nc4.nc_inq_unlimdims(grpid, nunlimdimsp, unlimdimids);

    int ndims = ndimsp.getValue();
    for (int i = 0; i < ndims; i++) {
      byte[] name = new byte[NCLibrary.NC_MAX_NAME + 1];
      NativeLongByReference lenp = new NativeLongByReference();
      ret = nc4.nc_inq_dim(grpid, dimids[i], name, lenp);
      if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
      String dname = makeString(name);

      boolean isUnlimited = containsInt(nunlimdimsp.getValue(), unlimdimids, i);
      Dimension dim = new Dimension(dname, lenp.getValue().intValue(), true, isUnlimited, false);
      ncfile.addDimension(g4.g, dim);
      if (debug) System.out.printf(" add Dimension %s (%d) %n", dim, dimids[i]);
    }
  }

  private boolean containsInt(int n, int[] have, int want) {
    for (int i = 0; i < n; i++) {
      if (have[i] == want) return true;
    }
    return false;
  }

  private String makeString(byte[] b) throws IOException {
    // null terminates
    int count = 0;
    while (count < b.length) {
      if (b[count] == 0) break;
      count++; // dont include the terminating 0
    }

    // copy if its small
    if (count < b.length / 2) {
      byte[] bb = new byte[count];
      System.arraycopy(b, 0, bb, 0, count);
      b = bb;
    }

    return new String(b, 0, count, "UTF-8"); // all strings are considered to be UTF-8 unicode.
  }

  // follow what happpens in the Java side
  private String makeAttString(byte[] b) throws IOException {
    // null terminates
    int count = 0;
    while (count < b.length) {
      if (b[count] == 0) break;
      count++; // dont include the terminating 0
    }

    return new String(b, 0, count, "UTF-8"); // all strings are considered to be UTF-8 unicode.

   /*
    char[] carray = new char[count];
    for (int i=0; i<count; i++)
      carray[i] = (char) DataType.unsignedByteToShort(b[i]);

    return new String(carray); */
  }


  private List<Attribute> makeAttributes(int grpid, int varid, int natts, Variable v) throws IOException {
    List<Attribute> result = new ArrayList<Attribute>(natts);

    for (int attnum = 0; attnum < natts; attnum++) {

      byte[] name = new byte[NCLibrary.NC_MAX_NAME + 1];
      int ret = nc4.nc_inq_attname(grpid, varid, attnum, name);
      if (ret != 0)
        throw new IOException(nc4.nc_strerror(ret) + " varid=" + varid + " attnum=" + attnum);
      String attname = makeString(name);

      IntByReference xtypep = new IntByReference();
      ret = nc4.nc_inq_atttype(grpid, varid, attname, xtypep);
      if (ret != 0)
        throw new IOException(nc4.nc_strerror(ret) + " varid=" + varid + "attnum=" + attnum);

      NativeLongByReference lenp = new NativeLongByReference();
      ret = nc4.nc_inq_attlen(grpid, varid, attname, lenp);
      if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
      int len = lenp.getValue().intValue();

      Array values = null;
      int type = xtypep.getValue();
      switch (type) {
        case NCLibrary.NC_UBYTE:
          byte[] valbu = new byte[len];
          ret = nc4.nc_get_att_uchar(grpid, varid, attname, valbu);
          if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
          values = Array.factory(DataType.BYTE.getPrimitiveClassType(), new int[]{len}, valbu);
          break;

        case NCLibrary.NC_BYTE:
          byte[] valb = new byte[len];
          ret = nc4.nc_get_att_schar(grpid, varid, attname, valb);
          if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
          values = Array.factory(DataType.BYTE.getPrimitiveClassType(), new int[]{len}, valb);
          break;

        case NCLibrary.NC_CHAR:
          byte[] text = new byte[len];
          ret = nc4.nc_get_att_text(grpid, varid, attname, text);
          if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
          Attribute att = new Attribute(attname, makeAttString(text));
          result.add(att);
          break;

        case NCLibrary.NC_DOUBLE:
          double[] vald = new double[len];
          ret = nc4.nc_get_att_double(grpid, varid, attname, vald);
          if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
          values = Array.factory(DataType.DOUBLE.getPrimitiveClassType(), new int[]{len}, vald);
          break;

        case NCLibrary.NC_FLOAT:
          float[] valf = new float[len];
          ret = nc4.nc_get_att_float(grpid, varid, attname, valf);
          if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
          values = Array.factory(DataType.FLOAT.getPrimitiveClassType(), new int[]{len}, valf);
          break;

        case NCLibrary.NC_UINT:
          int[] valiu = new int[len];
          ret = nc4.nc_get_att_uint(grpid, varid, attname, valiu);
          if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
          values = Array.factory(DataType.INT.getPrimitiveClassType(), new int[]{len}, valiu);
          break;

        case NCLibrary.NC_INT:
          int[] vali = new int[len];
          ret = nc4.nc_get_att_int(grpid, varid, attname, vali);
          if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
          values = Array.factory(DataType.INT.getPrimitiveClassType(), new int[]{len}, vali);
          break;

        case NCLibrary.NC_UINT64:
          long[] vallu = new long[len];
          ret = nc4.nc_get_att_ulonglong(grpid, varid, attname, vallu);
          if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
          values = Array.factory(DataType.LONG.getPrimitiveClassType(), new int[]{len}, vallu);
          break;

        case NCLibrary.NC_INT64:
          long[] vall = new long[len];
          ret = nc4.nc_get_att_longlong(grpid, varid, attname, vall);
          if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
          values = Array.factory(DataType.LONG.getPrimitiveClassType(), new int[]{len}, vall);
          break;

        case NCLibrary.NC_USHORT:
          short[] valsu = new short[len];
          ret = nc4.nc_get_att_ushort(grpid, varid, attname, valsu);
          if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
          values = Array.factory(DataType.SHORT.getPrimitiveClassType(), new int[]{len}, valsu);
          break;

        case NCLibrary.NC_SHORT:
          short[] vals = new short[len];
          ret = nc4.nc_get_att_short(grpid, varid, attname, vals);
          if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
          values = Array.factory(DataType.SHORT.getPrimitiveClassType(), new int[]{len}, vals);
          break;

        case NCLibrary.NC_STRING:
          String[] valss = new String[len];
          ret = nc4.nc_get_att_string(grpid, varid, attname, valss);
          if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
          values = Array.factory(String.class, new int[]{len}, valss);
          break;

        default:
          UserType userType = userTypes.get(type);
          if (userType == null) {
            log.warn("Unsupported attribute data type == " + type);
            continue;

          } else if (userType.userType == NCLibrary.NC_ENUM) {
            result.add( readEnumAttValues(grpid, varid, attname, len, userType));
            continue;

          } else if (userType.userType == NCLibrary.NC_OPAQUE) {
            result.add( readOpaqueAttValues(grpid, varid, attname, len, userType));
            continue;

          } else if (userType.userType == NCLibrary.NC_VLEN) {
            values = readVlenAttValues( grpid, varid, attname, len, userType);

          } else if (userType.userType == NCLibrary.NC_COMPOUND) {
            readCompoundAttValues(grpid, varid, attname, len, userType, result, v);
            continue;

          } else {
            log.warn("Unsupported attribute data type == " + userType);
            continue;
          }
      }

      if (values != null) {
        Attribute att = new Attribute(attname, values);
        result.add(att);
      }
    }

    return result;
  }

  Array readVlenAttValues(int grpid, int varid, String attname, int len, UserType userType) throws IOException {
    NCLibrary.Vlen_t[] vlen = new NCLibrary.Vlen_t[len];
    int ret = nc4.nc_get_att(grpid, varid, attname, vlen);    // vlen
    if (ret != 0) throw new IOException(nc4.nc_strerror(ret));

    int count = 0;
    for (int i = 0; i < len; i++)
      count += vlen[i].len;

    switch (userType.baseTypeid) {
      case NCLibrary.NC_INT:
        Array intArray = Array.factory(DataType.INT, new int[]{count});
        IndexIterator iter = intArray.getIndexIterator();
        for (int i = 0; i < len; i++) {
          //System.out.print(" len=" + vlen[i].len + "; p= " + vlen[i].p + ";");
          int[] ba = vlen[i].p.getIntArray(0, vlen[i].len);
          for (int j = 0; j < ba.length; j++) {
            //System.out.print(" " + ba[j]);
            iter.setIntNext(ba[j]);
          }
          //System.out.println();
        }
        return intArray;

      case NCLibrary.NC_FLOAT:
        Array fArray = Array.factory(DataType.FLOAT, new int[]{count});
        iter = fArray.getIndexIterator();
        for (int i = 0; i < len; i++) {
          float[] ba = vlen[i].p.getFloatArray(0, vlen[i].len);
          for (int j = 0; j < ba.length; j++)
            iter.setFloatNext(ba[j]);
        }
        return fArray;

    }
    return null;
  }

  private Attribute readEnumAttValues(int grpid, int varid, String attname, int len, UserType userType) throws IOException {
    int ret;

    DataType dtype = convertDataType(userType.baseTypeid);
    int elemSize = dtype.getSize();

    ByteBuffer bb = ByteBuffer.allocate(len * elemSize);
    ret = nc4.nc_get_att(grpid, varid, attname, bb);
    if (ret != 0) throw new IOException(nc4.nc_strerror(ret));

    Array data = convertByteBuffer(bb, userType.baseTypeid, new int[] {len});
    IndexIterator ii = data.getIndexIterator();

    if (len == 1) {
      String val = userType.e.lookupEnumString( ii.getIntNext());
      return new Attribute(attname, val);

    } else {
      ArrayObject.D1 attArray = (ArrayObject.D1) Array.factory(DataType.STRING, new int[]{len});
      for (int i = 0; i < len; i++) {
        int val = ii.getIntNext();
        String vals = userType.e.lookupEnumString( val);
        if (vals == null)
          throw new IOException("Illegal enum val " +val + " for attribute " + attname);
        attArray.set(i, vals);
      }
      return new Attribute(attname, attArray);
    }
  }

  private Array convertByteBuffer(ByteBuffer bb, int baseType, int shape[]) throws IOException {

    switch (baseType) {
      case NCLibrary.NC_BYTE:
      case NCLibrary.NC_UBYTE:
        Array sArray = Array.factory(DataType.BYTE, shape, bb.array());
        return (baseType == NCLibrary.NC_BYTE) ? sArray : MAMath.convertUnsigned(sArray);

      case NCLibrary.NC_SHORT:
      case NCLibrary.NC_USHORT:
        ShortBuffer sb = bb.asShortBuffer();
        sArray = Array.factory(DataType.SHORT, shape, sb.array());
        return (baseType == NCLibrary.NC_SHORT) ? sArray : MAMath.convertUnsigned(sArray);


      case NCLibrary.NC_INT:
      case NCLibrary.NC_UINT:
        IntBuffer ib = bb.asIntBuffer();
        sArray = Array.factory(DataType.INT, shape, ib.array());
        return (baseType == NCLibrary.NC_INT) ? sArray : MAMath.convertUnsigned(sArray);
    }

    return null;
  }

  private Attribute readOpaqueAttValues(int grpid, int varid, String attname, int len, UserType userType) throws IOException {
    int total = len * userType.size;
    ByteBuffer bb = ByteBuffer.allocate(total);
    int ret = nc4.nc_get_att(grpid, varid, attname, bb);
    if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
    return new Attribute(attname, Array.factory(DataType.BYTE, new int[] {total}, bb.array()));
  }

  /////////////////////////////////////////////////////////////////////////////

  private void makeVariables(int grpid, Group g) throws IOException {
    IntByReference nvarsp = new IntByReference();
    int ret = nc4.nc_inq_nvars(grpid, nvarsp);
    if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
    int nvars = nvarsp.getValue();
    if (debug) System.out.printf(" nvars= %d %n", nvars);

    int[] varids = new int[nvars];
    ret = nc4.nc_inq_varids(grpid, nvarsp, varids);

    for (int i = 0; i < varids.length; i++) {
      int varno = varids[i];
      if (varno != i) System.out.printf("HEY varno=%d i=%d%n", varno, i);

      byte[] name = new byte[NCLibrary.NC_MAX_NAME + 1];
      IntByReference xtypep = new IntByReference();
      IntByReference ndimsp = new IntByReference();
      int[] dimids = new int[NCLibrary.NC_MAX_DIMS];
      IntByReference nattsp = new IntByReference();

      ret = nc4.nc_inq_var(grpid, varno, name, xtypep, ndimsp, dimids, nattsp);
      if (ret != 0)
        throw new IOException(nc4.nc_strerror(ret));

      // figure out the datatype
      int typeid = xtypep.getValue();
      DataType dtype = convertDataType(typeid);

      String vname = makeString(name);
      Vinfo vinfo = new Vinfo(grpid, varno, typeid);

      // figure out the dimensions
      String dimList = makeDimList(grpid, ndimsp.getValue(), dimids);
      UserType utype = userTypes.get(typeid);
      if (utype != null) {
        vinfo.utype = utype;
        if (utype.userType == NCLibrary.NC_VLEN)
          dimList = dimList +" *";
      }

      Variable v;
      if (dtype != DataType.STRUCTURE) {
        v = new Variable(ncfile, g, null, vname, dtype, dimList);

      } else {
        Structure s = new Structure(ncfile, g, null, vname);
        s.setDimensions( dimList);
        v = s;

        if (utype.flds == null)
          utype.readFields();
        for (Field f : utype.flds) {
          s.addMemberVariable(f.makeMemberVariable(g, s));
        }
      }

      // create the Variable
      ncfile.addVariable(g, v);
      v.setSPobject(vinfo);
      if (dtype.isEnum()) {
        EnumTypedef enumTypedef = g.findEnumeration(utype.name);
        v.setEnumTypedef(enumTypedef);
      }

      if (isUnsigned(typeid))
        v.addAttribute(new Attribute("_Unsigned","true"));

      // read Variable attributes
      List<Attribute> atts = makeAttributes(grpid, varno, nattsp.getValue(), v);
      for (Attribute att : atts) {
        v.addAttribute(att);
        //if (debug) System.out.printf(" add Variable Attribute %s %n",att);
      }

      if (debug) System.out.printf(" add Variable %s %n", v);
    }
  }

  private String makeDimList(int grpid, int ndimsp, int[] dims) throws IOException {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < ndimsp; i++) {
      byte[] name = new byte[NCLibrary.NC_MAX_NAME + 1];
      int ret = nc4.nc_inq_dimname(grpid, dims[i], name);
      if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
      String dname = makeString(name);
      sb.append(dname);
      sb.append(" ");
    }
    return sb.toString();
  }

  private class Vinfo {
    int grpid, varid, typeid;
    UserType utype; // may be null

    Vinfo(int grpid, int varid, int typeid) {
      this.grpid = grpid;
      this.varid = varid;
      this.typeid = typeid;
    }
  }

  //////////////////////////////////////////////////////////////////////////
  private class Group4 {
    Group g;
    Group4 parent;
    int[] dimids;
    Group4( Group g, Group4 parent) {
      this.g = g;
      this.parent = parent;
    }
  }

  private class UserType {
    int grpid;
    int typeid;
    String name;
    int size; // the size of the user defined type
    int baseTypeid; // the base typeid for vlen and enum types
    long nfields; // the number of fields for enum and compound types
    int userType; // the class of the user defined type: NC_VLEN, NC_OPAQUE, NC_ENUM, or NC_COMPOUND.

    EnumTypedef e;
    List<Field> flds;

    UserType(int grpid, int typeid, String name, long size, int baseTypeid, long nfields, int userType) {
      this.grpid = grpid;
      this.typeid = typeid;
      this.name = name;
      this.size = (int) size;
      this.baseTypeid = baseTypeid;
      this.nfields = nfields;
      this.userType = userType;
    }

    void setEnum(EnumTypedef e) {
      this.e = e;
    }

    void addField(Field fld) {
      if (flds == null)
        flds = new ArrayList<Field>(10);
      flds.add(fld);
    }

    public String toString() {
      return "name='"+name+"' id="+getDataTypeName(typeid)+" userType="+getDataTypeName(userType)+
              " baseType="+getDataTypeName(baseTypeid);
    }

    void readFields() throws IOException {
      for (int fldidx=0; fldidx<nfields; fldidx++) {
        byte[] fldname = new byte[NCLibrary.NC_MAX_NAME + 1];
        IntByReference field_typeidp = new IntByReference();
        IntByReference ndimsp = new IntByReference();
        IntByReference dim_sizesp = new IntByReference();
        NativeLongByReference offsetp = new NativeLongByReference();

        int[] dims = new int[NCLibrary.NC_MAX_DIMS];
        int ret = nc4.nc_inq_compound_field(grpid, typeid, fldidx, fldname, offsetp, field_typeidp, ndimsp, dims);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));

        Field fld = new Field(grpid, typeid, fldidx, makeString(fldname), offsetp.getValue().intValue(),
                field_typeidp.getValue(), ndimsp.getValue(), dims);

        addField( fld);
        System.out.println(" add field= "+fld);
      }
    }
  }

  private class Field {
    int grpid;
    int typeid;
    int fldidx;
    String name;
    int offset;
    int fldtypeid;
    int ndims;
    int[] dims;

    DataType dtype;
    int total_size;
    Array data;

    // grpid, varid, fldidx, fldname, offsetp, field_typeidp, ndimsp, dim_sizesp
    Field(int grpid, int typeid, int fldidx, String name, int offset, int fldtypeid, int ndims, int[] dims) {
      this.grpid = grpid;
      this.typeid = typeid;
      this.fldidx = fldidx;
      this.name = name;
      this.offset = offset;
      this.fldtypeid = fldtypeid;
      this.ndims = ndims;
      this.dims = new int[ndims];
      System.arraycopy(dims, 0, this.dims, 0, ndims);

      dtype = convertDataType(fldtypeid);
      Section s = new Section( dims);
      total_size = (int) s.computeSize() * dtype.getSize();

      if (isVlen(fldtypeid)) {
        int[] edims = new int[dims.length+1];
        System.arraycopy(edims,0,dims,0,dims.length);
        edims[dims.length] = -1;
        dims = edims;
      }
    }

    public String toString() {
      return "name='"+name+" fldtypeid="+getDataTypeName(fldtypeid)+" ndims="+ndims+" offset="+offset;
    }

    Variable makeMemberVariable(Group g, Structure parent) {
      Variable v = new Variable(ncfile, g, parent, name);
      v.setDataType( convertDataType(fldtypeid));
      if (isUnsigned(fldtypeid))
        v.addAttribute(new Attribute("_Unsigned","true"));

      try {
        v.setDimensionsAnonymous(dims);
      } catch (InvalidRangeException e) {
        e.printStackTrace()//To change body of catch statement use File | Settings | File Templates.
      }
      return v;
    }

  }

  private Map<Integer, UserType> userTypes = new HashMap<Integer, UserType>();

  private void makeUserTypes(int grpid, Group g) throws IOException {
    // find user types in this group
    IntByReference ntypesp = new IntByReference();
    int ret = nc4.nc_inq_typeids(grpid, ntypesp, Pointer.NULL);
    if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
    int ntypes = ntypesp.getValue();
    if (ntypes == 0) return;
    int[] xtypes = new int[ntypes];
    ret = nc4.nc_inq_typeids(grpid, ntypesp, xtypes);
    if (ret != 0) throw new IOException(nc4.nc_strerror(ret));

    // for each defined "user type", get information, store in Map
    for (int typeid : xtypes) {
      byte[] nameb = new byte[NCLibrary.NC_MAX_NAME + 1];
      NativeLongByReference sizep = new NativeLongByReference();
      IntByReference baseType = new IntByReference();
      NativeLongByReference nfieldsp = new NativeLongByReference();
      IntByReference classp = new IntByReference();

      ret = nc4.nc_inq_user_type(grpid, typeid, nameb, sizep, baseType, nfieldsp, classp); // size_t
      if (ret != 0) throw new IOException(nc4.nc_strerror(ret));

      String name = makeString(nameb);
      int utype = classp.getValue();
      System.out.printf(" user type=%d name=%s size=%d baseType=%d nfields=%d class=%d %n ",
          typeid, name, sizep.getValue().longValue(), baseType.getValue(), nfieldsp.getValue().longValue(), utype);

      UserType ut = new UserType(grpid, typeid, name, sizep.getValue().longValue(), baseType.getValue(),
          nfieldsp.getValue().longValue(), classp.getValue());
      userTypes.put(typeid, ut);

      if (utype == NCLibrary.NC_ENUM) {
        Map<Integer, String> map = makeEnum(grpid, typeid);
        EnumTypedef e = new EnumTypedef(name, map);
        g.addEnumeration(e);
        ut.setEnum(e);

     } else if (utype == NCLibrary.NC_OPAQUE) {
        byte[] nameo = new byte[NCLibrary.NC_MAX_NAME + 1];
        NativeLongByReference sizep2 = new NativeLongByReference();
        ret = nc4.nc_inq_opaque(grpid, typeid, nameo, sizep2);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        String nameos = makeString(nameo);

        // doesnt seem to be any new info
        System.out.printf("   opaque type=%d name=%s size=%d %n ",
            typeid, nameos, sizep2.getValue().longValue());
      }
    }
  }

  private Map<Integer, String> makeEnum(int grpid, int xtype) throws IOException {
    byte[] nameb = new byte[NCLibrary.NC_MAX_NAME + 1];
    IntByReference baseType = new IntByReference();
    NativeLongByReference baseSize = new NativeLongByReference();
    NativeLongByReference numMembers = new NativeLongByReference();

    int ret = nc4.nc_inq_enum(grpid, xtype, nameb, baseType, baseSize, numMembers);
    if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
    int nmembers = numMembers.getValue().intValue();
    String name = makeString(nameb);

    //System.out.printf(" type=%d name=%s baseType=%d baseType=%d numMembers=%d %n ",
    //    xtype, name, baseType.getValue(), baseSize.getValue().longValue(), nmembers);
    Map<Integer, String> map = new HashMap<Integer, String>(2 * nmembers);

    for (int i = 0; i < nmembers; i++) {
      byte[] mnameb = new byte[NCLibrary.NC_MAX_NAME + 1];
      IntByReference value = new IntByReference();
      ret = nc4.nc_inq_enum_member(grpid, xtype, i, mnameb, value); // void *
      if (ret != 0) throw new IOException(nc4.nc_strerror(ret));

      String mname = makeString(mnameb);
      //System.out.printf(" member name=%s value=%d %n ",  mname, value.getValue());
      map.put(value.getValue(), mname);
    }

    return map;
  }

  /////////////////////////////////////////////////////////////////////////////////

  public Array readData(Variable v2, Section section) throws IOException, InvalidRangeException {
    Vinfo vinfo = (Vinfo) v2.getSPobject();
    Array values = null;
    int vlen = (int) v2.getSize();
    int len = (int) section.computeSize();
    if (vlen == len) return read(v2); // entire array

    switch (v2.getDataType()) {
      case BYTE:
        byte[] valb = new byte[len];
        // int nc_get_vars_schar(int ncid, int varid, int[] startp, int[] countp, int[] stridep, byte[] ip); // size_t, ptrdiff_t
        int ret = nc4.nc_get_vars_schar(vinfo.grpid, vinfo.varid,
            convert(section.getOrigin()), convert(section.getShape()), section.getStride(), valb);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        values = Array.factory(DataType.BYTE.getPrimitiveClassType(), section.getShape(), valb);
        break;

      case CHAR:
        byte[] valc = new byte[len];
        ret = nc4.nc_get_vars_text(vinfo.grpid, vinfo.varid,
            convert(section.getOrigin()), convert(section.getShape()), section.getStride(), valc);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        values = Array.factory(DataType.CHAR.getPrimitiveClassType(), section.getShape(), IospHelper.convertByteToChar(valc));
        break;

      case DOUBLE:
        double[] vald = new double[len];
        ret = nc4.nc_get_vars_double(vinfo.grpid, vinfo.varid,
            convert(section.getOrigin()), convert(section.getShape()), section.getStride(), vald);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        values = Array.factory(DataType.DOUBLE.getPrimitiveClassType(), section.getShape(), vald);
        break;

      case FLOAT:
        float[] valf = new float[len];
        ret = nc4.nc_get_vars_float(vinfo.grpid, vinfo.varid,
            convert(section.getOrigin()), convert(section.getShape()), section.getStride(), valf);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        values = Array.factory(DataType.FLOAT.getPrimitiveClassType(), section.getShape(), valf);
        break;

      case INT:
        int[] vali = new int[len];
        ret = nc4.nc_get_vars_int(vinfo.grpid, vinfo.varid,
            convert(section.getOrigin()), convert(section.getShape()), section.getStride(), vali);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        values = Array.factory(DataType.INT.getPrimitiveClassType(), section.getShape(), vali);
        break;

      case LONG:
        long[] vall = new long[len];
        ret = nc4.nc_get_vars_longlong(vinfo.grpid, vinfo.varid,
            convert(section.getOrigin()), convert(section.getShape()), section.getStride(), vall);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        values = Array.factory(DataType.LONG.getPrimitiveClassType(), section.getShape(), vall);
        break;

      case SHORT:
        short[] vals = new short[len];
        ret = nc4.nc_get_vars_short(vinfo.grpid, vinfo.varid,
            convert(section.getOrigin()), convert(section.getShape()), section.getStride(), vals);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        values = Array.factory(DataType.SHORT.getPrimitiveClassType(), section.getShape(), vals);
        break;

      default:
        throw new IOException("Unsupported data type = " + v2.getDataType());
    }

    return values;
  }

  private long[] convert(int[] from) {
    long[] to = new long[from.length];
    for (int i = 0; i < from.length; i++)
      to[i] = from[i];
    return to;
  }

  /* private Array read(Variable v2) throws IOException, InvalidRangeException {
    Vinfo vinfo = (Vinfo) v2.getSPobject();
    Array values = null;
    int len = (int) v2.getSize();

    switch (vinfo.typeid) {
      case ENUM1:
      case BYTE:
        byte[] valb = (byte[]) read(vinfo.grpid, vinfo.varid, vinfo.typeid, len);
        values = Array.factory( DataType.BYTE.getPrimitiveClassType(), v2.getShape(), valb);
        break;

      case CHAR:
        char[] valc = (char[] )read(vinfo.grpid, vinfo.varid, vinfo.typeid, len);
        values = Array.factory(DataType.CHAR.getPrimitiveClassType(), v2.getShape(), valc);
        break;

      case DOUBLE:
        double[] vald = (double[] )read(vinfo.grpid, vinfo.varid, vinfo.typeid, len);
        values = Array.factory(DataType.DOUBLE.getPrimitiveClassType(), v2.getShape(), vald);
        break;

      case FLOAT:
        float[] valf = (float[] )read(vinfo.grpid, vinfo.varid, vinfo.typeid, len);
        values = Array.factory(DataType.FLOAT.getPrimitiveClassType(), v2.getShape(), valf);
        break;

      case ENUM4:
      case INT:
        int[] vali = (int[]) read(vinfo.grpid, vinfo.varid, vinfo.typeid, len);
        values = Array.factory(DataType.INT.getPrimitiveClassType(), v2.getShape(), vali);
        break;

      case LONG:
        long[] vall = (long[] )read(vinfo.grpid, vinfo.varid, vinfo.typeid, len);
        values = Array.factory(DataType.LONG.getPrimitiveClassType(), v2.getShape(), vall);
        break;

      case ENUM2:
      case SHORT:
        short[] vals = (short[] )read(vinfo.grpid, vinfo.varid, vinfo.typeid, len);
        values = Array.factory(DataType.SHORT.getPrimitiveClassType(), v2.getShape(), vals);
        break;

      case STRING:
        String[] valss = (String[] )read(vinfo.grpid, vinfo.varid, vinfo.typeid, len);
        values = Array.factory(DataType.STRING.getPrimitiveClassType(), v2.getShape(), valss);
        break;

      case NCLibrary.NC_OPAQUE:
        UserType userType = userTypes.get( vinfo.typeid);
        values = readOpaque(vinfo.grpid, vinfo.varid, len, userType.size);
        break;

      case STRUCTURE:
        userType = userTypes.get( vinfo.typeid);
        values = readCompound(vinfo.grpid, vinfo.varid, len, userType);
        break;

      default:
         throw new IOException("Unsupported data type = " + v2.getDataType()+ " utype= "+vinfo.utype);
    }

    return values;
  }  */

  private Array read(Variable v2) throws IOException, InvalidRangeException {
    Vinfo vinfo = (Vinfo) v2.getSPobject();
    int len = (int) v2.getSize();
    return read(vinfo.grpid, vinfo.varid, vinfo.typeid, len, v2.getShape());
  }

  private Array read(int grpid, int varid, int typeid, int len, int[] shape) throws IOException, InvalidRangeException {
    int ret;

    switch (typeid) {

      case NCLibrary.NC_UBYTE:
        byte[] valbu = new byte[len];
        ret = nc4.nc_get_var_ubyte(grpid, varid, valbu);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        return Array.factory( DataType.BYTE.getPrimitiveClassType(), shape, valbu);

      case NCLibrary.NC_BYTE:
        byte[] valb = new byte[len];
        ret = nc4.nc_get_var_schar(grpid, varid, valb);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        return Array.factory( DataType.BYTE.getPrimitiveClassType(), shape, valb);

      case NCLibrary.NC_CHAR:
        byte[] valc = new byte[len];
        ret = nc4.nc_get_var_text(grpid, varid, valc);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        char[] cvals = IospHelper.convertByteToChar(valc);
        return Array.factory(DataType.CHAR.getPrimitiveClassType(), shape, cvals);

      case NCLibrary.NC_DOUBLE:
        double[] vald = new double[len];
        ret = nc4.nc_get_var_double(grpid, varid, vald);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        return Array.factory(DataType.DOUBLE.getPrimitiveClassType(), shape, vald);

      case NCLibrary.NC_FLOAT:
        float[] valf = new float[len];
        ret = nc4.nc_get_var_float(grpid, varid, valf);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        return Array.factory(DataType.FLOAT.getPrimitiveClassType(), shape, valf);

      case NCLibrary.NC_INT:
        int[] vali = new int[len];
        ret = nc4.nc_get_var_int(grpid, varid, vali);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        return Array.factory(DataType.INT.getPrimitiveClassType(), shape, vali);

      case NCLibrary.NC_INT64:
        long[] vall = new long[len];
        ret = nc4.nc_get_var_longlong(grpid, varid, vall);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        return Array.factory(DataType.LONG.getPrimitiveClassType(), shape, vall);

      case NCLibrary.NC_UINT64:
        long[] vallu = new long[len];
        ret = nc4.nc_get_var_ulonglong(grpid, varid, vallu);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        return Array.factory(DataType.LONG.getPrimitiveClassType(), shape, vallu);

      case NCLibrary.NC_SHORT:
        short[] vals = new short[len];
        ret = nc4.nc_get_var_short(grpid, varid, vals);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        return Array.factory(DataType.SHORT.getPrimitiveClassType(), shape, vals);

      case NCLibrary.NC_USHORT:
        short[] valsu = new short[len];
        ret = nc4.nc_get_var_ushort(grpid, varid, valsu);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        return Array.factory(DataType.SHORT.getPrimitiveClassType(), shape, valsu);

      case NCLibrary.NC_STRING:
        String[] valss = new String[len];
        ret = nc4.nc_get_var_string(grpid, varid, valss);
        if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
        return Array.factory(DataType.STRING.getPrimitiveClassType(), shape, valss);

      default:
        UserType userType = userTypes.get(typeid);
        if (userType == null) {
          throw new IOException("Unsupported data type == " + typeid);

        } else if (userType.userType == NCLibrary.NC_ENUM) {
          return readEnum(grpid, varid, userType.baseTypeid, len, shape);

        } else if (userType.userType == NCLibrary.NC_VLEN) {
          return readVlen(grpid, varid, len, userType);

        } else if (userType.userType == NCLibrary.NC_OPAQUE) {
          return readOpaque(grpid, varid, len, userType.size);

         } else  if (userType.userType == NCLibrary.NC_COMPOUND) {
          return readCompound(grpid, varid, len, userType);
        }

        throw new IOException("Unsupported type = " + typeid+ " userType= "+userType);
    }

  }

  private Array readCompound(int grpid, int varid, int len, UserType userType) throws IOException {
    int buffSize = len * userType.size;
    ByteBuffer bbuff = ByteBuffer.allocate(buffSize);
    int ret = nc4.nc_get_var(grpid, varid, bbuff);
    if (ret != 0) throw new IOException(nc4.nc_strerror(ret));

    decodeCompoundData(len, userType, bbuff);

    StructureMembers sm = new StructureMembers(userType.name);
    for (Field fld : userType.flds) {
      sm.addMember( fld.name, null, null, fld.dtype, fld.dims);
    }

    ArrayStructureMA asma = new ArrayStructureMA(sm, new int[] {len});
    for (Field fld : userType.flds) {
      asma.setMemberArray( fld.name, fld.data);
    }

    return asma;
  }

  private void readCompoundAttValues(int grpid, int varid, String attname, int len, UserType userType,
          List<Attribute> result, Variable v) throws IOException {

    int buffSize = len * userType.size;
    ByteBuffer bbuff = ByteBuffer.allocate(buffSize);
    int ret = nc4.nc_get_att(grpid, varid, attname, bbuff);
    if (ret != 0) throw new IOException(nc4.nc_strerror(ret));

    decodeCompoundData(len, userType, bbuff);

    // is its a Structure, distribute to matching fields
    if ((v != null) && (v instanceof Structure)) {
      Structure s = (Structure) v;
      for (Field fld : userType.flds) {
        Variable mv = s.findVariable(fld.name);
        if (mv != null)
          mv.addAttribute(new Attribute(attname,  fld.data));
        else
          result.add( new Attribute(attname+"."+fld.name,  fld.data));
      }

    } else {

      for (Field fld : userType.flds)
        result.add( new Attribute(attname+"."+fld.name,  fld.data));
    }

  }
 
  // LOOK: placing results in the fld of the userType - not for production
  private void decodeCompoundData(int len, UserType userType, ByteBuffer bbuff) throws IOException {
    if (userType.flds == null)
      userType.readFields();

    bbuff.order(ByteOrder.LITTLE_ENDIAN);

    for (Field fld : userType.flds)
      fld.data = Array.factory( convertDataType(fld.fldtypeid), new int[] { len});

    for (int i=0; i<len; i++) {
      int record_start = i * userType.size;

      for (Field fld : userType.flds) {
        int pos = record_start + fld.offset;

        switch (fld.fldtypeid) {
          case NCLibrary.NC_UBYTE:
          case NCLibrary.NC_BYTE:
            byte bval = bbuff.get(pos);
            if (debugCompoundAtt) System.out.println("bval= "+bval);
            fld.data.setByte(i, bval);
            continue;
          case NCLibrary.NC_USHORT:
          case NCLibrary.NC_SHORT:
            short sval = bbuff.getShort(pos);
            if (debugCompoundAtt) System.out.println("sval= "+sval);
            fld.data.setShort(i, sval);
            continue;
          case NCLibrary.NC_UINT:
          case NCLibrary.NC_INT:
            int ival = bbuff.getInt(pos);
            if (debugCompoundAtt) System.out.println("ival= "+ival);
            fld.data.setInt(i, ival);
            continue;
          case NCLibrary.NC_UINT64:
          case NCLibrary.NC_INT64:
            long lval = bbuff.getLong(pos);
            if (debugCompoundAtt) System.out.println("lval= "+lval);
            fld.data.setLong(i, lval);
            continue;
          case NCLibrary.NC_FLOAT:
            float fval = bbuff.getFloat(pos);
            if (debugCompoundAtt) System.out.println("fval= "+fval);
            fld.data.setFloat(i, fval);
            continue;
          case NCLibrary.NC_DOUBLE:
            double dval = bbuff.getDouble(pos);
            if (debugCompoundAtt) System.out.println("dval= "+dval);
            fld.data.setDouble(i, dval);
            continue;

          default:
            UserType subUserType = userTypes.get(fld.fldtypeid);
            if (subUserType == null) {
              throw new IOException("Unsupported compound fld.fldtypeid == " + fld.fldtypeid);

            } else if (userType.userType == NCLibrary.NC_ENUM) {

            } else if (userType.userType == NCLibrary.NC_VLEN) {
              //return readVlen(grpid, varid, len, userType);

            } else if (userType.userType == NCLibrary.NC_OPAQUE) {
              //return readOpaque(grpid, varid, len, userType.size);

             } else  if (userType.userType == NCLibrary.NC_COMPOUND) {
              //return readCompound(grpid, varid, len, userType);
            }

            System.out.println("UNSUPPORTED compound fld.fldtypeid= "+fld.fldtypeid);
            continue;
        } // switch on fld type
      } // loop over fields
    } // loop over len

  }


  Array readVlen(int grpid, int varid, int len, UserType userType) throws IOException {
    NCLibrary.Vlen_t[] vlen = new NCLibrary.Vlen_t[len];
    int ret = nc4.nc_get_var(grpid, varid, vlen);  
    if (ret != 0) throw new IOException(nc4.nc_strerror(ret));

    //DataType dtype = convertDataType(userType.baseTypeid);
    //ArrayObject.D1 vlenArray = new ArrayObject.D1( dtype.getPrimitiveClassType(), len);
    Object[] data = new Object[len];
    switch (userType.baseTypeid) {
      case NCLibrary.NC_INT:
        for (int i = 0; i < len; i++) {
          int slen = vlen[i].len;
          int[] ba = vlen[i].p.getIntArray(0, slen);
          data[i] = Array.factory(DataType.INT, new int[]{slen}, ba);
        }
        break;
      case NCLibrary.NC_FLOAT:
        for (int i = 0; i < len; i++) {
          int slen = vlen[i].len;
          float[] ba = vlen[i].p.getFloatArray(0, slen);
          data[i] = Array.factory(DataType.FLOAT, new int[]{slen}, ba);
        }
        break;
      default:
        throw new UnsupportedOperationException("Vlen type "+userType.baseTypeid+" = "+convertDataType(userType.baseTypeid));
    }
    // if scalar, return just the len Array
    return (Array) ((len == 1) ? data[0] : new ArrayObject(data[0].getClass(), new int[]{len}, data));
  }

  // opaques use ArrayObjects of ByteBuffer
  private Array readOpaque(int grpid, int varid, int len, int size) throws IOException, InvalidRangeException {
    int ret;

    ByteBuffer bb = ByteBuffer.allocate(len*size);
    ret = nc4.nc_get_var(grpid, varid, bb);
    if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
    byte[] entire = bb.array();

    ArrayObject values = new ArrayObject(ByteBuffer.class, new int[]{len});
    int count = 0;
    IndexIterator ii = values.getIndexIterator();
    while (ii.hasNext()) {
      ii.setObjectNext( ByteBuffer.wrap(entire, count*size, size));
    }

    return values;
  }

  private Array readEnum(int grpid, int varid, int baseType, int len, int[] shape) throws IOException, InvalidRangeException {
    int ret;

    DataType dtype = convertDataType(baseType);
    int elemSize = dtype.getSize();

    ByteBuffer bb = ByteBuffer.allocate(len * elemSize);
    ret = nc4.nc_get_var(grpid, varid, bb);
    if (ret != 0) throw new IOException(nc4.nc_strerror(ret));

    switch (baseType) {
      case NCLibrary.NC_BYTE:
      case NCLibrary.NC_UBYTE:
        return Array.factory( DataType.BYTE.getPrimitiveClassType(), shape, bb.array());

      case NCLibrary.NC_SHORT:
      case NCLibrary.NC_USHORT:
        ShortBuffer sb = bb.asShortBuffer();
        return Array.factory( DataType.BYTE.getPrimitiveClassType(), shape, sb.array());

      case NCLibrary.NC_INT:
      case NCLibrary.NC_UINT:
        IntBuffer ib = bb.asIntBuffer();
        return Array.factory( DataType.BYTE.getPrimitiveClassType(), shape, ib.array());
    }

    return null;
  }

  public void close() throws IOException {
    if (isClosed) return;
    if (ncid < 0) return;
    int ret = nc4.nc_close(ncid);
    if (ret != 0) throw new IOException(nc4.nc_strerror(ret));
    isClosed = true;
  }

  private boolean isUnsigned(int type) {
    return (type == NCLibrary.NC_UBYTE) || (type == NCLibrary.NC_USHORT) ||
            (type == NCLibrary.NC_UINT) || (type == NCLibrary.NC_UINT64);
  }

  private boolean isVlen(int type) {
    UserType userType = userTypes.get(type);
    return (userType == null) ? false : (userType.userType == NCLibrary.NC_VLEN);
  }

  private DataType convertDataType(int type) {
    switch (type) {
      case NCLibrary.NC_BYTE:
      case NCLibrary.NC_UBYTE:
        return DataType.BYTE;

      case NCLibrary.NC_CHAR:
        return DataType.CHAR;

      case NCLibrary.NC_SHORT:
      case NCLibrary.NC_USHORT:
        return DataType.SHORT;

      case NCLibrary.NC_INT:
      case NCLibrary.NC_UINT:
        return DataType.INT;

      case NCLibrary.NC_INT64:
      case NCLibrary.NC_UINT64:
        return DataType.LONG;

      case NCLibrary.NC_FLOAT:
        return DataType.FLOAT;

      case NCLibrary.NC_DOUBLE:
        return DataType.DOUBLE;

      case NCLibrary.NC_ENUM:
        return DataType.ENUM1;

      case NCLibrary.NC_STRING:
        return DataType.STRING;

      default:
        UserType userType = userTypes.get(type);
        if (userType == null)
          throw new IllegalArgumentException("unknown type == " + type);

        switch (userType.userType) {
          case NCLibrary.NC_ENUM:
            return DataType.ENUM1;

          case NCLibrary.NC_COMPOUND:
            return DataType.STRUCTURE;

          case NCLibrary.NC_OPAQUE:
            return DataType.OPAQUE;

          case NCLibrary.NC_VLEN:
            return convertDataType(userType.baseTypeid);
        }
        throw new IllegalArgumentException("unknown type == " + type);
    }
  }

  private String getDataTypeName(int type) {
    switch (type) {
      case NCLibrary.NC_BYTE: return "byte";
      case NCLibrary.NC_UBYTE: return "ubyte";
      case NCLibrary.NC_CHAR: return "char";
      case NCLibrary.NC_SHORT: return "short";
      case NCLibrary.NC_USHORT: return "ushort";
      case NCLibrary.NC_INT: return "int";
      case NCLibrary.NC_UINT: return "uint";
      case NCLibrary.NC_INT64: return "long";
      case NCLibrary.NC_UINT64: return "ulong";
      case NCLibrary.NC_FLOAT: return "float";
      case NCLibrary.NC_DOUBLE: return "double";
      case NCLibrary.NC_ENUM: return "enum";
      case NCLibrary.NC_STRING: return "string";
      case NCLibrary.NC_COMPOUND: return "struct";
      case NCLibrary.NC_OPAQUE: return "opaque";
      case NCLibrary.NC_VLEN: return "vlen";

      default:
        UserType userType = userTypes.get(type);
        if (userType == null)
          return "unknown type " + type;

        switch (userType.userType) {
          case NCLibrary.NC_ENUM: return "userType-enum";
          case NCLibrary.NC_COMPOUND: return "userType-struct";
          case NCLibrary.NC_OPAQUE: return "userType-opaque";
          case NCLibrary.NC_VLEN: return "userType-vlen";
        }
        return "unknown userType " + userType.userType;
    }
  }

  /////////////////////////////////////////////////////////////////////////

  private static NCLibrary nc4;

  private NCLibrary load() {
    if (nc4 == null) {
      //String dir = "C:/cdev/libpath/";
      String dir = "C:/dev/tds/thredds/lib/binary/win32/";
      System.setProperty("jna.library.path", dir);

      System.load(dir + "zlib1.dll");
      System.load(dir + "szlibdll.dll");
      System.load(dir + "hdf5dll.dll");
      System.load(dir + "hdf5_hldll.dll");
      //System.load(dir + "netcdf.dll");

      Native.setProtected(true);
      nc4 = (NCLibrary) Native.loadLibrary("netcdf", NCLibrary.class);
      System.out.printf(" Netcdf nc_inq_libvers=%s isProtecetd=%s %n ", nc4.nc_inq_libvers(), Native.isProtected());
    }

    return nc4;
  }

  private static class MyNetcdfFile extends NetcdfFile {
    MyNetcdfFile(IOServiceProvider spi) {
      this.spi = spi;
    }
  }

  public NetcdfFile open(String location) throws Exception {
    MyNetcdfFile ncfile = new MyNetcdfFile(this);
    ncfile.setLocation(location);
    try {
      open(null, ncfile, null);
    } catch (Exception e) {
      close(); // make sure that the file gets closed
      throw e;
    }
    return ncfile;
  }

  public static void main(String args[]) throws Exception {
    JniIosp iosp = new JniIosp();
    NetcdfFile ncfile = iosp.open("C:/data/test2.nc");
    System.out.println("" + ncfile);
  }

}
TOP

Related Classes of ucar.nc2.jni.netcdf.JniIosp

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.