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

ucar.nc2.ncml.NcmlConstructor Maven / Gradle / Ivy

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

package ucar.nc2.ncml;

import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.XMLOutputter;
import thredds.client.catalog.Catalog;
import ucar.ma2.*;
import ucar.nc2.*;
import ucar.nc2.constants.CDM;

import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
import java.util.StringTokenizer;

/**
 * Populate a NetcdfFile directly from NcML, can be used by IOSPs.
 * All ncml elements are new, not modified.
 * 
 * @author caron
 * @since Feb 26, 2011
 */
public class NcmlConstructor {
  // static private final boolean validate = false;
  static private final boolean debugConstruct = false;
  static private final boolean showParsedXML = false;

  private Formatter errlog = new Formatter();

  public Formatter getErrlog() {
    return errlog;
  }

  /**
   *
   * @param resourceLocation eg "resources/nj22/iosp/ghcnm.ncml"
   * @param target populate this file
   * @return true if success
   * @throws IOException on error
   */
  public boolean populateFromResource(String resourceLocation, NetcdfFile target) throws IOException {
    ClassLoader cl = this.getClass().getClassLoader();
    InputStream is = cl.getResourceAsStream(resourceLocation);
    if (is == null)
      throw new FileNotFoundException(resourceLocation);
    return populate(is, target);
  }

  public boolean populate(String ncml, NetcdfFile target) throws IOException {
    return populate(new ByteArrayInputStream(ncml.getBytes(CDM.utf8Charset)), target);
  }

  public boolean populate(InputStream ncml, NetcdfFile target) throws IOException {
    org.jdom2.Document doc;
    try {
      SAXBuilder builder = new SAXBuilder();
      doc = builder.build(ncml);
    } catch (JDOMException e) {
      throw new IOException(e.getMessage());
    }
    if (showParsedXML) {
      XMLOutputter xmlOut = new XMLOutputter();
      System.out.println("*** NetcdfDataset/showParsedXML = \n" + xmlOut.outputString(doc) + "\n*******");
    }

    Element netcdfElem = doc.getRootElement();
    readGroup(target, target.getRootGroup(), netcdfElem);
    return errlog.toString().length() == 0;
  }

  private void readGroup(NetcdfFile ncfile, Group parent, Element groupElem) throws IOException {

    String name = groupElem.getAttributeValue("name");

    Group g;
    if (parent == ncfile.getRootGroup()) { // special handling
      g = parent;
      
    } else {
      if (name == null) {
        errlog.format("NcML Group name is required (%s)%n", groupElem);
        return;
      }
      g = new Group(ncfile, parent, name);
      parent.addGroup(g);
    }

    // look for attributes
    java.util.List attList = groupElem.getChildren("attribute", Catalog.ncmlNS);
    for (Element attElem : attList) {
      readAtt(g, attElem);
    }

    // look for dimensions
    java.util.List dimList = groupElem.getChildren("dimension", Catalog.ncmlNS);
    for (Element dimElem : dimList) {
      readDim(g, dimElem);
    }

    // look for variables
    java.util.List varList = groupElem.getChildren("variable", Catalog.ncmlNS);
    for (Element varElem : varList) {
      readVariable(ncfile, g, null, varElem);
    }

    // LOOK for typedef enums

    // look for nested groups
    java.util.List groupList = groupElem.getChildren("group", Catalog.ncmlNS);
    for (Element gElem : groupList) {
      readGroup(ncfile, g, gElem);
    }
  }

  /**
   * Read a NcML variable element, and nested elements, when it creates a new Variable.
   *
   * @param ncfile      target dataset
   * @param g       parent Group
   * @param parentS parent Structure
   * @param varElem ncml variable element
   * @return return new Variable
   */
  private Variable readVariable(NetcdfFile ncfile, Group g, Structure parentS, Element varElem) {
    String name = varElem.getAttributeValue("name");
    if (name == null) {
      errlog.format("NcML Variable name is required (%s)%n", varElem);
      return null;
    }

    String type = varElem.getAttributeValue("type");
    if (type == null) {
      errlog.format("NcML variable (%s) must have type attribute", name);
      return null;
    }
    DataType dtype = DataType.getType(type);

    String shape = varElem.getAttributeValue("shape");
    if (shape == null)
      shape = ""; // deprecated, prefer explicit ""

    Variable v;

    if (dtype == DataType.STRUCTURE) {
      Structure s = new Structure(ncfile, g, parentS, name);
      s.setDimensions(shape);
      v = s;
      // look for nested variables
      java.util.List varList = varElem.getChildren("variable", Catalog.ncmlNS);
      for (Element vElem : varList) {
        readVariable(ncfile, g, s, vElem);
      }

    } else if (dtype == DataType.SEQUENCE) {
        Sequence s = new Sequence(ncfile, g, parentS, name);
        v = s;
        // look for nested variables
        java.util.List varList = varElem.getChildren("variable", Catalog.ncmlNS);
        for (Element vElem : varList) {
          readVariable(ncfile, g, s, vElem);
        }

    } else {
      v = new Variable(ncfile, g, parentS, name, dtype, shape);

      // deal with values
      Element valueElem = varElem.getChild("values", Catalog.ncmlNS);
      if (valueElem != null)
        readValues(v, varElem, valueElem);
      // otherwise has fill values.
    }

    // look for attributes
    java.util.List attList = varElem.getChildren("attribute", Catalog.ncmlNS);
    for (Element attElem : attList)
      readAtt(v, attElem);

    if (parentS != null)
      parentS.addMemberVariable(v);
    else
      g.addVariable(v);    

    return v;
  }

  private void readValues(Variable v, Element varElem, Element valuesElem) {

    // check if values are specified by start / increment
    String startS = valuesElem.getAttributeValue("start");
    String incrS = valuesElem.getAttributeValue("increment");
    String nptsS = valuesElem.getAttributeValue("npts");
    int npts = (nptsS == null) ? (int) v.getSize() : Integer.parseInt(nptsS);

    // either start, increment are specified
    if ((startS != null) && (incrS != null)) {
      double start = Double.parseDouble(startS);
      double incr = Double.parseDouble(incrS);
      v.setValues(npts, start, incr);
      return;
    }

    // otherwise values are listed in text
    String values = varElem.getChildText("values", Catalog.ncmlNS);
    String sep = valuesElem.getAttributeValue("separator");
    if (sep == null) sep = " ";

    if (v.getDataType() == DataType.CHAR) {
      int nhave = values.length();
      int nwant = (int) v.getSize();
      char[] data = new char[nwant];
      int min = Math.min(nhave, nwant);
      for (int i = 0; i < min; i++) {
        data[i] = values.charAt(i);
      }
      Array dataArray = Array.factory(DataType.CHAR, v.getShape(), data);
      v.setCachedData(dataArray, true);

    } else {
      // or a list of values
      List valList = new ArrayList<>();
      StringTokenizer tokn = new StringTokenizer(values, sep);
      while (tokn.hasMoreTokens())
        valList.add(tokn.nextToken());
      v.setValues(valList);
    }
  }

  private void readAtt(Object parent, Element attElem) {
    String name = attElem.getAttributeValue("name");
    if (name == null) {
      errlog.format("NcML Attribute name is required (%s)%n", attElem);
      return;
    }

    try {
      ucar.ma2.Array values = NcMLReader.readAttributeValues(attElem);
      Attribute att = new ucar.nc2.Attribute(name, values);
      if (parent instanceof Group)
        ((Group) parent).addAttribute(att);
      else if (parent instanceof Variable)
        ((Variable) parent).addAttribute(att);
    } catch (RuntimeException e) {
      errlog.format("NcML new Attribute Exception: %s att=%s in=%s%n", e.getMessage(), name, parent);
    }
  }

  /**
   * Read an NcML dimension element.
   *
   * @param g       put dimension into this group
   * @param dimElem ncml dimension element
   */
  private void readDim(Group g, Element dimElem) {
    String name = dimElem.getAttributeValue("name");
    if (name == null) {
      errlog.format("NcML Dimension name is required (%s)%n", dimElem);
      return;
    }

    String lengthS = dimElem.getAttributeValue("length");
    String isUnlimitedS = dimElem.getAttributeValue("isUnlimited");
    String isSharedS = dimElem.getAttributeValue("isShared");
    String isUnknownS = dimElem.getAttributeValue("isVariableLength");

    boolean isUnlimited = (isUnlimitedS != null) && isUnlimitedS.equalsIgnoreCase("true");
    boolean isUnknown = (isUnknownS != null) && isUnknownS.equalsIgnoreCase("true");
    boolean isShared = true;
    if ((isSharedS != null) && isSharedS.equalsIgnoreCase("false"))
      isShared = false;

    int len = Integer.parseInt(lengthS);
    if ((isUnknownS != null) && isUnknownS.equalsIgnoreCase("false"))
      len = Dimension.VLEN.getLength();

    Dimension dim = new Dimension(name, len, isShared, isUnlimited, isUnknown);

    if (debugConstruct) System.out.println(" add new dim = " + dim);
    g.addDimension(dim);
  }

}

/*

 /**
   * Copy contents of "src" to "target". skip ones that already exist (by name).
   * Dimensions and Variables are replaced with equivalent elements, but unlimited dimensions are turned into regular dimensions.
   * Attribute doesnt have to be replaced because its immutable, so its copied by reference.
   *
   * @param ncml ncml as an stream
   * @param target transfer to this NetcdfDataset.
   *
  static public void transferDataset(String ncml, NetcdfFile target) throws IOException {
    transferDataset(new ByteArrayInputStream(ncml.getBytes()), target);
  }

  static public void transferDataset(InputStream ncml, NetcdfFile target) throws IOException {
    NetcdfDataset src = NcMLReader.readNcML(ncml, null);
    transferGroup(src, src.getRootGroup(), target.getRootGroup());
  }

  // transfer the objects in src group to the target group
  static private void transferGroup(NetcdfFile ds, Group src, Group targetGroup) {
    // group attributes
    transferGroupAttributes(src, targetGroup);

    // dimensions
    for (Dimension d : src.getDimensions()) {
      if (null == targetGroup.findDimensionLocal(d.getName())) {
        targetGroup.addDimension(d);
      }
    }

    // variables
    for (Variable v : src.getVariables()) {
      targetGroup.addVariable(v);
    }

    // nested groups - check if target already has it
    for (Group srcNested : src.getGroups()) {
      Group nested = targetGroup.findGroup(srcNested.getShortName());
      if (null == nested) {
        nested = new Group(ds, targetGroup, srcNested.getShortName());
        targetGroup.addGroup(nested);
      }
      transferGroup(ds, srcNested, nested);
    }
  }

  /**
   * Copy attributes from src to target, skip ones that already exist (by name)
   * @param src copy from here
   * @param target copy to here
   *
  static public void transferVariableAttributes(Variable src, Variable target) {
    for (Attribute a : src.getAttributes()) {
      if (null == target.findAttribute(a.getName()))
        target.addAttribute(a);
    }
  }

  /**
   * Copy attributes from src to target, skip ones that already exist (by name)
   * @param src copy from here
   * @param target copy to here
   *
  static public void transferGroupAttributes(Group src, Group target) {
    for (Attribute a : src.getAttributes()) {
      if (null == target.findAttribute(a.getName()))
        target.addAttribute(a);
    }
  }

  /**
   * Find the Group in newFile that corresponds (by name) with oldGroup
   *
   * @param newFile look in this NetcdfFile
   * @param oldGroup corresponding (by name) with oldGroup
   * @return corresponding Group, or null if no match.
   *
  static public Group findGroup(NetcdfFile newFile, Group oldGroup) {
    List chain = new ArrayList(5);
    Group g = oldGroup;
    while ( g.getParentGroup() != null) { // skip the root
      chain.add(0, g); // put in front
      g = g.getParentGroup();
    }

    Group newg = newFile.getRootGroup();
    for (Group oldg : chain) {
      newg = newg.findGroup( oldg.getShortName());
      if (newg == null) return null;
    }
    return newg;
  }

 */





© 2015 - 2024 Weber Informatics LLC | Privacy Policy