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

ucar.nc2.iosp.netcdf3.N3streamWriter Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1998-2018 John Caron and University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */

// $Id: $

package ucar.nc2.iosp.netcdf3;

import ucar.nc2.*;
import ucar.ma2.DataType;
import ucar.nc2.constants.CDM;

import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.io.IOException;
import java.io.DataOutputStream;

/**
 * Common superclass for N3outputStreamWriter and N3channelStreamWriter.
 *  Experimental
 * @author john
 */
public abstract class N3streamWriter {

  ////////////////////////////////////////////////////////////////////////////////////////////////////////
  protected ucar.nc2.NetcdfFile ncfile;
  protected Map vinfoMap = new HashMap();
  protected List vinfoList = new ArrayList(); // output order of the variables
  protected boolean debug=false, debugPos=true, debugWriteData = false;
  protected int recStart, recSize;
  protected boolean usePadding = true;
  protected long filePos = 0;

  protected N3streamWriter(ucar.nc2.NetcdfFile ncfile) {
    this.ncfile = ncfile;
  }

  /**
   * Write the header to a stream.
   *
   * @param stream write to this stream.
   * @param numrec pass in number of record is you know it, else -1 for "streaming" format variant
   * @throws IOException if write fails
   */
  public void writeHeader(DataOutputStream stream, int numrec) throws IOException {

    // make sure ncfile structures were finished
    ncfile.finish();

    // magic number
    stream.write(N3header.MAGIC);
    int count = N3header.MAGIC.length;

    // numrecs
    Dimension udim = ncfile.getUnlimitedDimension();
    if (numrec < 0) {
      numrec = (udim == null) ? 0 : -1; // -1 means "streaming" - calc numrec through file length
    }
    stream.writeInt(numrec);
    count += 4;

    // dims
    List dims = ncfile.getDimensions();
    int numdims = dims.size();
    if (numdims == 0) {
      stream.writeInt(0);
      stream.writeInt(0);
    } else {
      stream.writeInt(N3header.MAGIC_DIM);
      stream.writeInt(numdims);
    }
    count += 8;

    for (int i = 0; i < numdims; i++) {
      Dimension dim = (Dimension) dims.get(i);
      count += writeString(stream, N3iosp.makeValidNetcdfObjectName( dim.getShortName()));
      stream.writeInt(dim.isUnlimited() ? 0 : dim.getLength());
      count += 4;
    }

    // global attributes
    count += writeAtts(stream, ncfile.getGlobalAttributes());

    if (debug) System.out.println("vars header starts at "+count);

    // variables
    List vars = ncfile.getVariables();
    int nvars = vars.size();
    if (nvars == 0) {
      stream.writeInt(0);
      stream.writeInt(0);
    } else {
      stream.writeInt(N3header.MAGIC_VAR);
      stream.writeInt(nvars);
    }
    count += 8;

    /* Note on padding: In the special case of only a single record variable of character, byte, or short
    // type, no padding is used between data values.
    if (nvars == 1) {
      Variable var = vars.get(0);
      DataType dtype = var.getDataType();
      if ((dtype == DataType.CHAR) || (dtype == DataType.BYTE) || (dtype == DataType.SHORT))
        usePadding = false;
    }  */

    // we have to calculate how big the header is before we can actually write it
    // so we set stream = null
    for (int i = 0; i < nvars; i++) {
      Variable var = (Variable) vars.get(i);
      if (var instanceof Structure) continue;
      Vinfo vinfo = writeVar(null, var, 0);
      count += vinfo.hsize;
    }

    // now calculate where things go
    int dataStart = count; // data starts right after the header
    int offset = dataStart; // track data offset
    if (debug) System.out.println(" non-record vars start at "+dataStart);

    // do all non-record variables first
    for (int i = 0; i < nvars; i++) {
      Variable var = (Variable) vars.get(i);
      //if (var instanceof Structure) continue;

      if (!var.isUnlimited()) {
        Vinfo vinfo = writeVar(stream, var, offset);
        vinfoMap.put(var, vinfo);
        if (debugPos)
          System.out.println(" " + var.getNameAndDimensions() + " begin at = " + offset + " end=" + (offset + vinfo.vsize));

        offset += vinfo.vsize;
        vinfoList.add(vinfo);
      }
    }

    if (debug) System.out.println(" record vars start at "+offset);
    recStart = offset; // record variables' data starts here
    recSize = 0;

    // do all record variables
    for (int i = 0; i < nvars; i++) {
      Variable var = (Variable) vars.get(i);

      if (var.isUnlimited()) {
        if (var instanceof Structure) continue;
        Vinfo vinfo = writeVar(stream, var, offset);
        vinfoMap.put(var, vinfo);

        if (debugPos)
          System.out.println(" " + var.getNameAndDimensions() + "(record) begin at = " + offset + " end=" + (offset + vinfo.vsize) + " size=" + vinfo.vsize);

        offset += vinfo.vsize;
        recSize += vinfo.vsize;
        vinfoList.add(vinfo);
      }
    }

    filePos = count;
    if (debugPos) System.out.println("header written filePos= " + filePos+" recsize= "+recSize);
  }

  private Vinfo writeVar(DataOutputStream stream, Variable var, int offset) throws IOException {
    int hsize = 0;
    hsize += writeString(stream, N3iosp.makeValidNetcdfObjectName( var.getShortName()));

    // dimensions
    int vsize = var.getDataType().getSize();
    List dims = var.getDimensions();
    if (null != stream) stream.writeInt(dims.size());
    hsize += 4;

    for (Dimension dim : dims) {
      int dimIndex = findDimensionIndex(dim);
      if (null != stream) stream.writeInt(dimIndex);
      hsize += 4;

      if (!dim.isUnlimited())
        vsize *= dim.getLength();
    }
    int pad =  (usePadding) ? N3header.padding(vsize) : 0;
    vsize += pad;

    // variable attributes
    hsize += writeAtts(stream, var.getAttributes());

    // data type, variable size, beginning file position
    int type = N3header.getType(var.getDataType());
    if (null != stream) {
      stream.writeInt(type);
      stream.writeInt(vsize);
      stream.writeInt(offset);
    }
    hsize += 12;

    //if (debug) out.println(" name= "+name+" type="+type+" vsize="+vsize+" begin= "+begin+" isRecord="+isRecord+"\n");
    return new Vinfo(var, hsize, vsize, offset, pad, var.isUnlimited());
  }

  private int writeAtts(DataOutputStream stream, List atts) throws IOException {
    int natts = atts.size();
    if (null != stream) {
      if (natts == 0) {
        stream.writeInt(0);
        stream.writeInt(0);
      } else {
        stream.writeInt(N3header.MAGIC_ATT);
        stream.writeInt(natts);
      }
    }
    int hsize = 8;

    for (int i = 0; i < natts; i++) {
      Attribute att = atts.get(i);

      hsize += writeString(stream, N3iosp.makeValidNetcdfObjectName( att.getShortName()));
      int type = N3header.getType(att.getDataType());
      if (null != stream) stream.writeInt(type);
      hsize += 4;

      if (type == 2) {
        hsize += writeStringValues(stream, att);
      } else {
        int nelems = att.getLength();
        if (null != stream) stream.writeInt(nelems);
        hsize += 4;

        int nbytes = 0;
        for (int j = 0; j < nelems; j++)
          nbytes += writeAttributeValue(stream, att.getNumericValue(j));
        hsize += nbytes;

        hsize += pad(stream, nbytes, (byte) 0);
      }
    }

    return hsize;
  }

  private int writeStringValues(DataOutputStream stream, Attribute att) throws IOException {
    int n = att.getLength();
    if (n == 1)
      return writeString(stream, att.getStringValue());
    else {
      StringBuilder values = new StringBuilder();
      for (int i = 0; i < n; i++)
        values.append(att.getStringValue(i));
      return writeString(stream, values.toString());
    }
  }

  private int writeAttributeValue(DataOutputStream stream, Number numValue) throws IOException {
    if (numValue instanceof Byte) {
      if (null != stream) stream.write(numValue.byteValue());
      return 1;

    } else if (numValue instanceof Short) {
      if (null != stream) stream.writeShort(numValue.shortValue());
      return 2;

    } else if (numValue instanceof Integer) {
      if (null != stream) stream.writeInt(numValue.intValue());
      return 4;

    } else if (numValue instanceof Float) {
      if (null != stream) stream.writeFloat(numValue.floatValue());
      return 4;

    } else if (numValue instanceof Double) {
      if (null != stream) stream.writeDouble(numValue.doubleValue());
      return 8;
    }

    throw new IllegalStateException("unknown attribute type == " + numValue.getClass().getName());
  }

  // write a string then pad to 4 byte boundary
  private int writeString(DataOutputStream stream, String s) throws IOException {
    byte[] b = s.getBytes(CDM.utf8Charset);
    if (null != stream) {
      stream.writeInt(b.length);
      stream.write(b);
    }
    int n = pad(stream, b.length, (byte) 0);
    return n + 4 + b.length;
  }

  private int findDimensionIndex(Dimension wantDim) {
    List dims = ncfile.getDimensions();
    for (int i = 0; i < dims.size(); i++) {
      Dimension dim = (Dimension) dims.get(i);
      if (dim.equals(wantDim)) return i;
    }
    throw new IllegalStateException("unknown Dimension == " + wantDim);
  }

  // pad to a 4 byte boundary
  protected int pad(DataOutputStream stream, int nbytes, byte fill) throws IOException {
    int pad = N3header.padding(nbytes);
    if (null != stream) {
      for (int i = 0; i < pad; i++)
        stream.write(fill);
    }
    return pad;
  }

  // variable info for reading/writing
  static protected class Vinfo {
    Variable v;
    int hsize; // header size
    int vsize; // size of array in bytes. if isRecord, size per record. includes padding
    int offset; // offset of start of data from start of file
    int pad; // number of padding bytes
    boolean isRecord; // is it a record variable?

    Vinfo(Variable v, int hsize, int vsize, int offset, int pad, boolean isRecord) {
      this.v = v;
      this.hsize = hsize;
      this.vsize = vsize;
      this.offset = offset;
      this.pad = pad;
      this.isRecord = isRecord;
    }

    public String toString() { return v.getFullName()+" vsize= "+vsize+" pad="+pad; }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy