Package ucar.nc2.ncml

Source Code of ucar.nc2.ncml.Aggregation$Dataset

/*
* 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.ncml;

import thredds.inventory.DateExtractor;
import thredds.inventory.DateExtractorFromName;
import ucar.ma2.*;
import ucar.nc2.*;
import ucar.nc2.units.DateFormatter;
import ucar.nc2.dataset.*;
import ucar.nc2.util.CancelTask;
import ucar.nc2.util.DiskCache2;

import java.util.*;
import java.util.concurrent.Executor;
import java.io.*;

import org.jdom.Element;
import thredds.inventory.MFile;
import thredds.inventory.DatasetCollectionManager;
import ucar.unidata.util.StringUtil;

/**
* Superclass for NcML Aggregation.
*
* An Aggregation acts as a ProxyReader for VariableDS. That, is it must implement:
* <pre>
*   public Array read(Variable mainv);
*   public Array read(Variable mainv, Section section);
* </pre>
*
* @author caron
*/

/* May be out of date
* <h2>Implementation Notes</h2>
* <h3>Caching</h3>
* <ul>
* <li>Case 1. Explicit list / Scan static directories (recheck=null)
* <ul>
* <li>A. AggCaching - keep track of ncoords, coordValues for joinExisting. Read on open, write on close.
* Could skip scan if cache exists.
* <li>B. NetcdfFileCache - write on close if changed (only first time). On sync, recheck = null means wont be reread.
* </ul>
* <li>Case 2. Scan dynamic directories (recheck non-null)
* <ul>
* <li>A. AggCaching - keep track of ncoords, coordValues for joinExisting. Read on open, write on close.
* Could skip scan if cache exists, and recheck time not expired.
* <li>B. NetcdfFileCache - write on close if changed. On sync, if recheck time, then rescan.
* </ul>
* </ul>
* <h3>Aggregation Coordinate Variable (aggCoord) Processing</h3>
* Construction:
* <ol>
* <li> The aggregation element is processed first.
* <li> agg.finish() is called.
* <li> If the user has defined the aggCoord in the NcML, it is then processed, overriding whatever the aggregation has constructed.
* If values are defined, they are cached in the new variable.
* </ol>
* Data Reading:
* <ol>
* <li> If values are cached, agg.read() is never called.
* <li> Each Dataset may have a coordinate value(s) defined in the NcML coordValue attribute.
* <li> If not, the coordinate value(s) is cached when the dataset is opened.
* <li> agg.read() uses those if they exist, else reads and caches.
* </ol>

*/
public abstract class Aggregation {

  static protected enum Type {
    forecastModelRunCollection,
    forecastModelRunSingleCollection,
    joinExisting,
    joinExistingOne, // joinExisting with a DateFormatMark makes it into a joinExistingOne - must have only one coord / file
    joinNew,
    tiled,
    union
  }

  static protected enum TypicalDataset {FIRST, RANDOM, LATEST, PENULTIMATE }
  static protected TypicalDataset typicalDatasetMode;

  static protected org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(Aggregation.class);
  static protected DiskCache2 diskCache2 = null;

  // this is where  persist() reads/writes files
  static public void setPersistenceCache(DiskCache2 dc) {
    diskCache2 = dc;
  }

  // experimental multithreading
  static protected Executor executor;
  static public void setExecutor(Executor exec) {
    executor = exec;
  }

  static public void setTypicalDatasetMode(String mode) {
    if (mode.equalsIgnoreCase("random"))
      typicalDatasetMode = TypicalDataset.RANDOM;
    else if (mode.equalsIgnoreCase("latest"))
      typicalDatasetMode = TypicalDataset.LATEST;
    else if (mode.equalsIgnoreCase("penultimate"))
       typicalDatasetMode = TypicalDataset.PENULTIMATE;
    else if (mode.equalsIgnoreCase("first"))
       typicalDatasetMode = TypicalDataset.FIRST;
     else
      logger.error("Unknown setTypicalDatasetMode= " + mode);
  }

  static protected boolean debug = false, debugOpenFile = false, debugSyncDetail = false, debugProxy = false,
      debugRead = false, debugDateParse = false, debugConvert = false;

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

  protected NetcdfDataset ncDataset; // the aggregation belongs to this dataset
  protected Type type; // the aggregation type
  protected Object spiObject; // pass to NetcdfFile.open()

  protected List<Aggregation.Dataset> explicitDatasets = new ArrayList<Aggregation.Dataset>(); // explicitly created Dataset objects from netcdf elements
  protected List<Aggregation.Dataset> datasets = new ArrayList<Aggregation.Dataset>(); // all : explicit and scanned
  protected DatasetCollectionManager datasetManager; // manages scanning
  protected boolean cacheDirty = true; // aggCache persist file needs updating

  protected String dimName; // the aggregation dimension name

  private Element mergeNcml = null;

  // experimental
  protected String dateFormatMark;
  //protected EnumSet<NetcdfDataset.Enhance> enhance = null; // default no enhancement
  protected boolean isDate = false;
  protected DateFormatter dateFormatter = new DateFormatter();

  /**
   * Create an Aggregation for the given NetcdfDataset.
   * The following addXXXX methods are called, then finish(), before the object is ready for use.
   *
   * @param ncd      Aggregation belongs to this NetcdfDataset
   * @param dimName  the aggregation dimension name
   * @param type     the Aggregation.Type
   * @param recheckS how often to check if files have changes
   */
  protected Aggregation(NetcdfDataset ncd, String dimName, Type type, String recheckS) {
    this.ncDataset = ncd;
    this.dimName = dimName;
    this.type = type;
    datasetManager = new DatasetCollectionManager(recheckS);
  }

  /**
   * Add a nested dataset, specified by an explicit netcdf element.
   * enhance is handled by the reader, so its always false here.
   *
   * @param cacheName   a unique name to use for caching
   * @param location    attribute "location" on the netcdf element
   * @param id          attribute "id" on the netcdf element
   * @param ncoordS     attribute "ncoords" on the netcdf element
   * @param coordValueS attribute "coordValue" on the netcdf element
   * @param sectionSpec attribute "section" on the netcdf element
   * @param reader      factory for reading this netcdf dataset
   */
  public void addExplicitDataset(String cacheName, String location, String id, String ncoordS, String coordValueS, String sectionSpec,
                                 ucar.nc2.util.cache.FileFactory reader) {

    Dataset nested = makeDataset(cacheName, location, id, ncoordS, coordValueS, sectionSpec, null, reader);
    explicitDatasets.add(nested);
  }

  public void addDataset(Dataset nested) {
    explicitDatasets.add(nested);
  }

  /**
   * Add a dataset scan
   *
   * @param crawlableDatasetElement defines a CrawlableDataset, or null
   * @param dirName             scan this directory
   * @param suffix              filter on this suffix (may be null)
   * @param regexpPatternString include if full name matches this regular expression (may be null)
   * @param dateFormatMark      create dates from the filename (may be null)
   * @param enhanceMode         how should files be enhanced
   * @param subdirs             equals "false" if should not descend into subdirectories
   * @param olderThan           files must be older than this time (now - lastModified >= olderThan); must be a time unit, may ne bull
   */
  public void addDatasetScan(Element crawlableDatasetElement, String dirName, String suffix,
          String regexpPatternString, String dateFormatMark, Set<NetcdfDataset.Enhance> enhanceMode, String subdirs, String olderThan) {
    this.dateFormatMark = dateFormatMark;

    datasetManager.addDirectoryScan(dirName, suffix, regexpPatternString, subdirs, olderThan, enhanceMode);

    if (dateFormatMark != null) {
      isDate = true;
      if (type == Type.joinExisting) type = Type.joinExistingOne; // tricky
      DateExtractor dateExtractor = (dateFormatMark == null) ? null : new DateExtractorFromName(dateFormatMark, true);
      datasetManager.setDateExtractor(dateExtractor);
    }

}

  public void addCollection(String spec, String olderThan) throws IOException {
    datasetManager = DatasetCollectionManager.open(spec, olderThan, null);
}

  public void setModifications(Element ncmlMods) {
    this.mergeNcml = ncmlMods;
  }

  /**
   * Get type of aggregation
   *
   * @return type of aggregation
   */
  public Type getType() {
    return type;
  }

  /**
   * Get dimension name to join on
   *
   * @return dimension name or null if type union/tiled
   */
  public String getDimensionName() {
    return dimName;
  }


  protected String getLocation() {
    return ncDataset.getLocation();
  }

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

  public void close() throws IOException {
    persistWrite();
    closeDatasets();
  }

  /**
   * Check to see if its time to rescan directory, and if so, rescan and extend dataset if needed.
   * Note that this just calls sync(), so structural metadata may be modified (!!)
   *
   * @return true if directory was rescanned and dataset may have been updated
   * @throws IOException on io error
   */
  public synchronized boolean syncExtend() throws IOException {
    if (!datasetManager.isRescanNeeded())
      return false;

    return _sync();
  }

  public synchronized boolean sync() throws IOException {
    return datasetManager.isRescanNeeded() && _sync();
  }

  private boolean _sync() throws IOException {
    if (!datasetManager.rescan())
      return false; // nothing changed LOOK what about grib extention ??
    cacheDirty = true;
    closeDatasets();
    makeDatasets(null);

    // rebuild the metadata
    rebuildDataset();
    ncDataset.finish();
    if (ncDataset.getEnhanceMode().contains(NetcdfDataset.Enhance.CoordSystems)) { // force recreation of the coordinate systems
      ncDataset.clearCoordinateSystems();
      ncDataset.enhance(ncDataset.getEnhanceMode());
      ncDataset.finish();
  }

    return true;
  }

  public String getFileTypeId() { // LOOK - should cache ??
     Dataset ds = null;
     NetcdfFile ncfile = null;
     try {
       ds = getTypicalDataset();
       ncfile = ds.acquireFile(null);
       return ncfile.getFileTypeId();

     } catch (Exception e) {
       logger.error("failed to open "+ds);

     } finally {
       if (ds != null) try {
         ds.close(ncfile);
       } catch (IOException e) {
         logger.error("failed to close "+ds);
  }
     }
     return "N/A";
   }

   public String getFileTypeDescription() { // LOOK - should cache ??
     Dataset ds = null;
     NetcdfFile ncfile = null;
     try {
       ds = getTypicalDataset();
       ncfile = ds.acquireFile(null);
       return ncfile.getFileTypeDescription();

     } catch (Exception e) {
       logger.error("failed to open "+ds);

     } finally {
       if (ds != null) try {
         ds.close(ncfile);
       } catch (IOException e) {
         logger.error("failed to close "+ds);
       }
     }
     return "N/A";
   }


  ///////////////////////////////////////////////////////////////////////////////////////////////////////
  // stuff for subclasses to override

  /**
    * Call this to build the dataset objects in the NetcdfDataset
    *
    * @param cancelTask maybe cancel
    * @throws IOException on read error
    */
   protected abstract void buildNetcdfDataset(CancelTask cancelTask) throws IOException;


   /**
    * Call this when rescan has found changed datasets
    *
    * @throws IOException on read error
    */
   protected abstract void rebuildDataset() throws IOException;


  /**
   * Allow information to be make persistent. Overridden in AggregationExisting
   *
   * @throws IOException on error
   */
  public void persistWrite() throws IOException {
  }

  /**
   * read info from the persistent XML file, if it exists; overridden in AggregationExisting
   */
  protected void persistRead() {
  }

  // close any open datasets
  protected void closeDatasets() throws IOException {
   // datasets = null;
  }

  public void getDetailInfo(Formatter f) {
    f.format("  Type=%s%n", type);
    f.format("  dimName=%s%n", dimName);
    f.format("  Datasets%n");
    for (Dataset ds : datasets)
      ds.show(f);
  }


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

  // all elements are processed, finish construction

  public void finish(CancelTask cancelTask) throws IOException {
    datasetManager.scan(cancelTask); // Make the list of Datasets, by scanning if needed.
    cacheDirty = true;
    closeDatasets();
    makeDatasets(cancelTask);

    //ucar.unidata.io.RandomAccessFile.setDebugAccess( true);
    buildNetcdfDataset(cancelTask);
    //ucar.unidata.io.RandomAccessFile.setDebugAccess( false);
  }

  public List<Dataset> getDatasets() {
    return datasets;
  }

  /**
   * Make the list of Datasets, from explicit and scans.
   *
   * @param cancelTask user can cancel
   * @throws IOException on i/o error
   */
  protected void makeDatasets(CancelTask cancelTask) throws IOException {

    // heres where the results will go
    datasets = new ArrayList<Dataset>();

    for (MFile cd : datasetManager.getFiles()) {
      datasets.add( makeDataset(cd));
    }

    // sort using Aggregation.Dataset as Comparator.
    // Sort by date if it exists, else filename.
    Collections.sort(datasets);

      /* optionally extract the date
      String dateCoordS = null;
      if (null != dateFormatMark) {
        String filename = myf.getName(); // LOOK operates on name, not path
        Date dateCoord = DateFromString.getDateUsingDemarkatedCount(filename, dateFormatMark, '#');
        dateCoordS = formatter.toDateTimeStringISO(dateCoord);
        if (debugDateParse) System.out.println("  adding " + myf.getPath() + " date= " + dateCoordS);
      } else {
        if (debugDateParse) System.out.println("  adding " + myf.getPath());
      }

      String location = myf.getPath();
      Aggregation.Dataset ds = makeDataset(location, location, null, null, dateCoordS, null, enhance, null);
      datasets.add(ds);
    }

    // Sort by date if it exists, else filename.
    Collections.sort(datasets, new Comparator<Aggregation.Dataset>() {
      public int compare(Aggregation.Dataset ds1, Aggregation.Dataset ds2) {
        if(ds1.cd == null)
           return ds1.getLocation().compareTo(ds2.getLocation()) ;
        if (ds1.cd.dateCoord != null) // LOOK can we generalize
          return ds1.cd.dateCoord.compareTo(ds2.cd.dateCoord);
        else
          return ds1.cd.file.getName().compareTo(ds2.cd.file.getName());
      }
    });  */

    // add the explicit datasets - these need to be kept in order
    // LOOK - should they be before or after scanned? Does it make sense to mix scan and explicit?
    // AggFmrcSingle sets explicit datasets - the scan is empty
    for (Aggregation.Dataset dataset : explicitDatasets) {
      datasets.add(dataset);
    }

    // check for duplicate location
    Set<String> dset = new HashSet<String>( 2 * datasets.size());
    for (Aggregation.Dataset dataset : datasets) {
      if (dset.contains(dataset.cacheLocation))
        logger.warn("Duplicate dataset in aggregation = "+dataset.cacheLocation);
      dset.add(dataset.cacheLocation);
    }

    if (datasets.size() == 0) {
      throw new IllegalStateException("There are no datasets in the aggregation " + datasetManager);
    }
  }

  /**
   * Open one of the nested datasets as a template for the aggregation dataset.
   *
   * @return a typical Dataset
   * @throws FileNotFoundException if there are no datasets
   */
  protected Dataset getTypicalDataset() throws IOException {
    List<Dataset> nestedDatasets = getDatasets();
    int n = nestedDatasets.size();
    if (n == 0)
      throw new FileNotFoundException("No datasets in this aggregation");

    int select;
    if (typicalDatasetMode == TypicalDataset.LATEST)
      select = n - 1;
    else if (typicalDatasetMode == TypicalDataset.PENULTIMATE)
      select = (n < 2) ? 0 : n - 2;
    else if (typicalDatasetMode == TypicalDataset.FIRST)
      select = 0;
    else // random is default
      select = (n < 2) ? 0 : new Random().nextInt(n);

    return nestedDatasets.get(select);
  }


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

  /**
   * Read an aggregation variable: A variable whose data spans multiple files.
   *
   * @param mainv      the aggregation variable
   * @param cancelTask allow the user to cancel
   * @return the data array
   * @throws IOException
   */
  //public abstract Array read(Variable mainv, CancelTask cancelTask) throws IOException;
  //public abstract Array reallyRead() throws IOException;

  /**
   * Read a section of an aggregation variable.
   *
   * @param mainv      the aggregation variable
   * @param cancelTask allow the user to cancel
   * @param section    read just this section of the data, refers to aggregated Variable's section.
   * @return the data array section
   * @throws IOException
   */
  //public abstract Array read(Variable mainv, Section section, CancelTask cancelTask) throws IOException, InvalidRangeException;
  //public abstract Array reallyRead(Section section) throws IOException, InvalidRangeException;

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

  /**
   * Dataset factory, so subclasses can override
   *
   * @param cacheName   a unique name to use for caching
   * @param location    attribute "location" on the netcdf element
   * @param id          attribute "id" on the netcdf element
   * @param ncoordS     attribute "ncoords" on the netcdf element
   * @param coordValueS attribute "coordValue" on the netcdf element
   * @param sectionSpec attribute "sectionSpec" on the netcdf element
   * @param enhance     open dataset in enhance mode NOT USED
   * @param reader      factory for reading this netcdf dataset
   * @return a Aggregation.Dataset
   */
  protected Dataset makeDataset(String cacheName, String location, String id, String ncoordS, String coordValueS,
          String sectionSpec, EnumSet<NetcdfDataset.Enhance> enhance, ucar.nc2.util.cache.FileFactory reader) {
    return new Dataset(cacheName, location, id, enhance, reader); // overridden in OuterDim, tiled
  }

  protected Dataset makeDataset(MFile dset) {
    return new Dataset(dset);
  }

  /**
   * Encapsolates a NetcdfFile that is a component of the aggregation.
   */
  public class Dataset implements Comparable {
    protected final String location; // location attribute on the netcdf element
    protected String id; // id attribute on the netcdf element

    // deferred opening
    protected String cacheLocation;
    protected ucar.nc2.util.cache.FileFactory reader;
    protected Set<NetcdfDataset.Enhance> enhance; // used by Fmrc to read enhanced datasets

    protected Object extraInfo;

    /**
     * For subclasses.
     *
     * @param location location attribute on the netcdf element
     */
    protected Dataset(String location) {
      this.location = (location == null) ? null : StringUtil.substitute(location, "\\", "/");
    }

    protected Dataset(MFile mfile) {
      this( mfile.getPath());
      this.cacheLocation = location;
      this.enhance = (Set<NetcdfDataset.Enhance>) mfile.getAuxInfo();
    }

    /**
     * Dataset constructor.
     * With this constructor, the actual opening of the dataset is deferred, and done by the reader.
     * Used with explicit netcdf elements, and scanned files.
     *
     * @param cacheLocation a unique name to use for caching
     * @param location  attribute "location" on the netcdf element
     * @param id  attribute "id" on the netcdf element
     * @param enhance   open dataset in enhance mode, may be null NOT USED
     * @param reader    factory for reading this netcdf dataset; if null, use NetcdfDataset.open( location)
     */
    protected Dataset(String cacheLocation, String location, String id, EnumSet<NetcdfDataset.Enhance> enhance, ucar.nc2.util.cache.FileFactory reader) {
      this(location);
      this.cacheLocation = cacheLocation;
      this.id = id;
      //this.enhance = enhance;
      this.reader = reader;
    }

    /**
     * Get the location of this Dataset
     *
     * @return the location of this Dataset
     */
    public String getLocation() {
      return location;
    }

    public String getCacheLocation() {
      return cacheLocation;
    }

    public String getId() {
      if (id != null) return id;
      if (location != null) return location;
      return Integer.toString(this.hashCode());
    }

    public NetcdfFile acquireFile(CancelTask cancelTask) throws IOException {
      if (debugOpenFile) System.out.println(" try to acquire " + cacheLocation);
      long start = System.currentTimeMillis();

      NetcdfFile ncfile = NetcdfDataset.acquireFile(reader, null, cacheLocation, -1, cancelTask, spiObject);

      // must merge NcML before enhancing
      if (mergeNcml != null)
        ncfile = NcMLReader.mergeNcML(ncfile, mergeNcml); // create new dataset
      if (enhance == null || enhance.isEmpty()) {
        if (debugOpenFile) System.out.println(" acquire (no enhance) " + cacheLocation + " took " + (System.currentTimeMillis() - start));
        return ncfile;
      }

      // must enhance
      NetcdfDataset ds;
      if (ncfile instanceof NetcdfDataset) {
        ds = (NetcdfDataset) ncfile;
        ds.enhance(enhance); // enhance "in place", ie modify the NetcdfDataset
      } else {
        ds = new NetcdfDataset(ncfile, enhance); // enhance when wrapping
      }

      if (debugOpenFile) System.out.println(" acquire (enhance) " + cacheLocation + " took " + (System.currentTimeMillis() - start));
      return ds;
    }

    protected void close(NetcdfFile ncfile) throws IOException {
      if (ncfile == null) return;
      cacheVariables(ncfile);
      ncfile.close();
    }

        // overridden in DatasetOuterDimension
    protected void cacheVariables(NetcdfFile ncfile) throws IOException {
    }

    public void show(Formatter f) {
      f.format("   %s%n", location);
    }

    protected Array read(Variable mainv, CancelTask cancelTask) throws IOException {
      NetcdfFile ncd = null;
      try {
        ncd = acquireFile(cancelTask);
        if ((cancelTask != null) && cancelTask.isCancel())
          return null;

        Variable v = findVariable(ncd, mainv);
        if ((mainv == null) || (v == null))
          System.out.println("HEY (mainv == null)");
        if (debugRead)
          System.out.printf("Agg.read %s from %s in %s%n", mainv.getNameAndDimensions(), v.getNameAndDimensions(), getLocation());
        return v.read();

      } finally {
        close( ncd);
      }
    }

    /**
     * Read a section of the local Variable.
     *
     * @param mainv      aggregated Variable
     * @param cancelTask let user cancel
     * @param section    reletive to the local Variable
     * @return the complete Array for mainv
     * @throws IOException           on I/O error
     * @throws InvalidRangeException on section error
     */
    protected Array read(Variable mainv, CancelTask cancelTask, List<Range> section) throws  IOException, InvalidRangeException {
      NetcdfFile ncd = null;
      try {
        ncd = acquireFile(cancelTask);
        if ((cancelTask != null) && cancelTask.isCancel())
          return null;

        Variable v = findVariable(ncd, mainv);
        if (debugRead) {
          Section want = new Section(section);
          System.out.printf("Agg.read(%s) %s from %s in %s%n", want, mainv.getNameAndDimensions(), v.getNameAndDimensions(), getLocation());
        }

        return v.read(section);

      } finally {
        close( ncd);
      }
    }

    protected Variable findVariable(NetcdfFile ncfile, Variable mainV) {
      Variable v = ncfile.findVariable(mainV.getFullNameEscaped());
      if (v == null) {  // might be renamed
        VariableEnhanced ve = (VariableEnhanced) mainV;
        v = ncfile.findVariable(ve.getOriginalName()); // LOOK not escaped
      }
      return v;
    }

    // Datasets with the same locations are equal
    public boolean equals(Object oo) {
      if (this == oo) return true;
      if (!(oo instanceof Dataset)) return false;
      Dataset other = (Dataset) oo;
      if (location != null)
        return location.equals(other.location);
      return super.equals(oo);
    }

    public int hashCode() {
      return (location != null) ? location.hashCode() : super.hashCode();
    }

    public int compareTo(Object o) {
      Dataset other = (Dataset) o;
      return location.compareTo( other.location);
    }
  } // class Dataset

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

  /*
   * All non-agg variables use a proxy to acquire the file before reading.
   * If the variable is caching, read data into cache now.
   * If not caching, VariableEnhanced.setProxyReader() is called.
   *
   * @param typicalDataset read from a "typical dataset"
   * @param newds          containing dataset
   * @throws IOException on i/o error
   */
  protected void setDatasetAcquireProxy(Dataset typicalDataset, NetcdfDataset newds) throws IOException {

    // all normal (non agg) variables must use a proxy to lock the file
    DatasetProxyReader proxy = new DatasetProxyReader(typicalDataset);
    List<Variable> allVars = newds.getRootGroup().getVariables();
    for (Variable v : allVars) {

      if (v.getProxyReader() != v) {
        if (debugProxy) System.out.println(" debugProxy: hasProxyReader " + v.getFullName());
        continue; // dont mess with agg variables
      }

      if (v.isCaching()) {  // cache the small ones
          v.setCachedData( v.read()); // cache the variableDS directly

      } else { // put proxy on the rest
        v.setProxyReader(proxy);
        if (debugProxy) System.out.println(" debugProxy: set proxy on " + v.getFullName());
      }
    }
  }


  protected class DatasetProxyReader implements ProxyReader {
    Dataset dataset;

    DatasetProxyReader(Dataset dataset) {
      this.dataset = dataset;
    }

    public Array reallyRead(Variable mainV, CancelTask cancelTask) throws IOException {
      NetcdfFile ncfile = null;
      try {
        ncfile = dataset.acquireFile(cancelTask);
        if ((cancelTask != null) && cancelTask.isCancel()) return null;
        Variable proxyV = findVariable(ncfile, mainV);
        return proxyV.read();
      } finally {
        dataset.close( ncfile);
      }
    }

    public Array reallyRead(Variable mainV, Section section, CancelTask cancelTask) throws  IOException, InvalidRangeException {
      NetcdfFile ncfile = null;
      try {
        ncfile = dataset.acquireFile(cancelTask);
        Variable proxyV = findVariable(ncfile, mainV);
        if ((cancelTask != null) && cancelTask.isCancel()) return null;
        return proxyV.read(section);

      } finally {
        dataset.close( ncfile);
      }
    }
  }
  protected Variable findVariable(NetcdfFile ncfile, Variable mainV) {
    Variable v = ncfile.findVariable(mainV.getFullNameEscaped());
    if (v == null) {  // might be renamed
      VariableEnhanced ve = (VariableEnhanced) mainV;
      v = ncfile.findVariable(ve.getOriginalName()); // LOOK not escaped
    }
    return v;
  }

}
TOP

Related Classes of ucar.nc2.ncml.Aggregation$Dataset

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.