/*
* Copyright (c) 1998 - 2011. 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 thredds.catalog;
import net.jcip.annotations.ThreadSafe;
import thredds.featurecollection.FeatureCollectionConfig;
import thredds.inventory.*;
import ucar.coord.CoordinateRuntime;
import ucar.nc2.constants.FeatureType;
import ucar.nc2.dataset.NetcdfDataset;
import ucar.nc2.dt.grid.GridCoordSys;
import ucar.nc2.ft.FeatureDataset;
import ucar.nc2.grib.GdsHorizCoordSys;
import ucar.nc2.grib.collection.*;
import ucar.nc2.time.CalendarDate;
import ucar.nc2.time.CalendarDateRange;
import ucar.unidata.geoloc.LatLonRect;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
/**
* Implement FeatureCollection GRIB - a collection of Grib1 or Grib2 files that are served as Grids.
*
* catalogs see makeCatalog()
* path/catalog.xml // top catalog
* path/partitionName/catalog.xml
* path/partitionName/../partitionName/catalog.xml
* path/latest.xml // latest (resolver)
*
* datasets see findDataset()
* path/dataset (dataset = BEST, TWOD, TP, "") // top collection, single group
* path/dataset/groupName // top collection, multiple group
* path/partitionName/dataset // partition, single group
* path/partitionName/../partitionName/dataset
* path/partitionName/dataset/groupName // partition, multiple group
* path/partitionName/../partitionName/dataset/groupName
*
* files
* path/partitionName/../partitionName/FILES/filename
* @author caron
* @since 4/15/11
*/
@ThreadSafe
public class InvDatasetFcGrib extends InvDatasetFeatureCollection {
static private final String COLLECTION = "collection";
static private final String BEST_DATASET = GribCollectionImmutable.Type.Best.toString();
static private final String TWOD_DATASET = GribCollectionImmutable.Type.TwoD.toString();
static private final String TP_DATASET = GribCollectionImmutable.Type.TP.toString();
/////////////////////////////////////////////////////////////////////////////
protected class StateGrib extends State {
GribCollectionImmutable gribCollection; // top level
GribCollectionImmutable latest;
String latestPath;
protected StateGrib(StateGrib from) {
super(from);
if (from != null) {
this.gribCollection = from.gribCollection;
this.latest = from.latest;
this.latestPath = from.latestPath;
}
}
@Override
public State copy() {
return new StateGrib(this);
}
}
//////////////////////////////////////////////////////////////////////////////
public InvDatasetFcGrib(InvDatasetImpl parent, FeatureCollectionConfig config) {
super(parent, config);
Formatter errlog = new Formatter();
CollectionSpecParser sp = config.getCollectionSpecParser(errlog);
topDirectory = sp.getRootDir();
String errs = errlog.toString();
if (errs.length() > 0) logger.warn("{}: CollectionManager parse error = {} ", name, errs);
tmi.setDataType(FeatureType.GRID); // override GRIB
state = new StateGrib(null);
finish();
}
@Override
public FeatureDataset getFeatureDataset() {
return null;
}
@Override
protected void _showStatus(Formatter f, boolean summaryOnly, String type) throws IOException {
StateGrib localState;
synchronized (lock) {
localState = (StateGrib) state;
}
if (localState.gribCollection != null) {
if (summaryOnly)
localState.gribCollection.showStatusSummary(f, type);
else
localState.gribCollection.showStatus(f);
}
}
@Override
protected void updateCollection(State state, CollectionUpdateType force) {
try {
StateGrib localState = (StateGrib) state;
GribCollectionImmutable previous = localState.gribCollection;
GribCollectionImmutable previousLatest = localState.latest;
localState.latest = null; // will get updated next time its asked for
localState.gribCollection = GribCdmIndex.openGribCollection(this.config, force, logger);
if (localState.gribCollection == null)
logger.error("InvDatasetFcGrib.updateCollection failed "+this.config);
logger.debug("{}: GribCollection object was recreated", getName());
if (previous != null) previous.close(); // LOOK may be another thread using - other thread will fail
if (previousLatest != null) previousLatest.close();
} catch (IOException ioe) {
logger.error("GribFc updateCollection", ioe);
}
}
/////////////////////////////////////////////////////////////////////////
private String makeCollectionShortName(String collectionName) {
String topCollectionName = config.collectionName;
if (collectionName.equals(topCollectionName)) return topCollectionName;
if (collectionName.startsWith(topCollectionName)) {
return getName()+collectionName.substring(topCollectionName.length());
}
return collectionName;
}
private InvDatasetImpl makeDatasetFromCollection( boolean isTop, InvCatalogImpl parentCatalog, String parentCollectionName, GribCollectionImmutable fromGc) throws IOException {
if (fromGc == null)
throw new FileNotFoundException("Grib Collection '"+getCollectionName()+"' does not exist or is empty");
String dsName = isTop ? getName() : makeCollectionShortName(fromGc.getName());
InvDatasetImpl result = new InvDatasetImpl(this);
result.setName(dsName);
result.setParent(null);
InvDatasetImpl parent = (InvDatasetImpl) this.getParent();
if (parent != null)
result.transferMetadata(parent, true); // make all inherited metadata local
String tpath = getPath()+"/"+COLLECTION;
ThreddsMetadata tmi = result.getLocalMetadataInheritable();
tmi.addVariableMapLink(makeMetadataLink(tpath, VARIABLES));
tmi.setServiceName(Virtual_Services);
tmi.setDataFormatType(fromGc.isGrib1 ? DataFormatType.GRIB1 : DataFormatType.GRIB2);
tmi.addProperties(fromGc.getGlobalAttributes());
String pathStart = parentCollectionName == null ? getPath() : getPath()+"/"+parentCollectionName;
for (GribCollectionImmutable.Dataset ds : fromGc.getDatasets()) {
boolean isSingleGroup = ds.getGroupsSize() == 1;
Iterable<GribCollectionImmutable.GroupGC> groups = ds.getGroups();
if (ds.getType() == GribCollectionImmutable.Type.TwoD) {
if (config.gribConfig.hasDatasetType(FeatureCollectionConfig.GribDatasetType.TwoD)) {
InvDatasetImpl twoD = new InvDatasetImpl(this, getDatasetNameTwoD(result.getName()));
String path = pathStart + "/" + TWOD_DATASET;
twoD.setUrlPath(path);
twoD.tm.addDocumentation("summary", "Two time dimensions: reference and forecast; full access to all GRIB records");
result.addDataset(twoD);
// Collections.sort(groups);
makeDatasetsFromGroups(twoD, groups, isSingleGroup);
}
} else if (ds.getType() == GribCollectionImmutable.Type.Best) {
if (config.gribConfig.hasDatasetType(FeatureCollectionConfig.GribDatasetType.Best)) {
InvDatasetImpl best = new InvDatasetImpl(this, getDatasetNameBest(result.getName()));
String path = pathStart + "/" + BEST_DATASET;
best.setUrlPath(path);
best.tm.addDocumentation("summary", "Single time dimension: for each forecast time, use GRIB record with smallest offset from reference time");
result.addDataset(best);
makeDatasetsFromGroups(best, groups, isSingleGroup);
}
} else if (ds.getType() == GribCollectionImmutable.Type.TP) {
InvDatasetImpl tp = new InvDatasetImpl(this, getDatasetNameTP(result.getName()));
String path = pathStart + "/" + TP_DATASET;
tp.setUrlPath(path);
tp.tm.addDocumentation("summary", "Multiple reference, single time Grib Partition");
result.addDataset(tp);
makeDatasetsFromGroups(tp, groups, isSingleGroup);
} else { // not a partition
// must be a file partition with only one file
boolean isFilePartition = config.ptype == FeatureCollectionConfig.PartitionType.file;
boolean onlyOneFile = isFilePartition && fromGc.getFiles().size() == 1;
if (onlyOneFile) {
parentCatalog.addService(orgService);
tmi.setServiceName(this.orgService.getName());
}
result.setUrlPath(pathStart);
if (ds.getType() == GribCollectionImmutable.Type.SRC) {
CoordinateRuntime runCoord = fromGc.getMasterRuntime();
assert runCoord.getSize() == 1;
CalendarDate runtime = runCoord.getFirstDate();
result.tm.addDocumentation("summary", "Single reference time Grib Collection");
result.tmi.addDocumentation("Reference Time", runtime.toString());
} else if (ds.getType() == GribCollectionImmutable.Type.MRSTC) {
result.tm.addDocumentation("summary", "Multiple reference time Grib Collection");
}
makeDatasetsFromGroups(result, groups, isSingleGroup);
if (!onlyOneFile && config.gribConfig.hasDatasetType(FeatureCollectionConfig.GribDatasetType.Files)) {
//String filesPath = pathStart + "/" + FILES;
//InvCatalogRef filesRef = new InvCatalogRef(this, FILES, getCatalogHref(filesPath));
//filesRef.finish();
addFileDatasets(parentCatalog, result, fromGc); // , config.gribConfig.hasDatasetType(FeatureCollectionConfig.GribDatasetType.Latest));
}
}
}
if (fromGc instanceof PartitionCollectionImmutable) {
if (config.gribConfig.hasDatasetType(FeatureCollectionConfig.GribDatasetType.Latest) && parentCollectionName == null) { // latest for top only
InvDatasetImpl ds = new InvDatasetImpl(this, getDatasetNameLatest(result.getName()));
ds.setUrlPath(LATEST_DATASET_CATALOG);
// ds.setID(getPath() + "/" + FILES + "/" + LATEST_DATASET_CATALOG);
ds.setServiceName(LATEST_SERVICE);
ds.finish();
result.addDataset(ds);
//this.addService(InvService.latest);
}
PartitionCollectionImmutable pc = (PartitionCollectionImmutable) fromGc;
for (PartitionCollectionImmutable.Partition partition : pc.getPartitionsSorted()) {
InvDatasetImpl partDs = makeDatasetFromPartition(this, parentCollectionName, partition, pc.isPartitionOfPartitions());
result.addDataset(partDs);
}
}
result.finish();
return result;
}
private void makeDatasetsFromGroups(InvDatasetImpl parent, Iterable<GribCollectionImmutable.GroupGC> groups, boolean isSingleGroup) {
for (GribCollectionImmutable.GroupGC group : groups) {
InvDatasetImpl ds = isSingleGroup ? parent : new InvDatasetImpl(this, group.getDescription());
String groupId = isSingleGroup ? null : group.getId();
String dpath = isSingleGroup ? parent.getUrlPath() : parent.getUrlPath() + "/" + groupId;
ds.setID(dpath);
ds.setUrlPath(dpath);
if (!isSingleGroup) {
parent.addDataset(ds);
}
ds.tmi.setGeospatialCoverage(extractGeospatial(group));
ds.tmi.setTimeCoverage(group.makeCalendarDateRange());
ds.tmi.addVariableMapLink(makeMetadataLink(dpath, VARIABLES));
/* if (gribConfig.hasDatasetType(FeatureCollectionConfig.GribDatasetType.TwoD)) {
InvDatasetImpl twoD = new InvDatasetImpl(this, getTwodDatasetName());
String path = dpath + "/" + TWOD_DATASET;
twoD.setUrlPath(path);
twoD.setID(path);
twoD.tmi.addVariableMapLink(makeMetadataLink(path, VARIABLES));
ds.addDataset(twoD);
}
if (gribConfig.hasDatasetType(FeatureCollectionConfig.GribDatasetType.Best)) {
InvDatasetImpl best = new InvDatasetImpl(this, getBestDatasetName());
String path = dpath + "/" + BEST_DATASET;
best.setUrlPath(path);
best.setID(path);
best.tmi.addVariableMapLink(makeMetadataLink(path, VARIABLES));
ds.addDataset(best);
}
if (gribConfig.hasDatasetType(FeatureCollectionConfig.GribDatasetType.Files)) {
String name = isSingleGroup ? FILES : groupId + "/" + FILES;
InvCatalogRef filesCat = new InvCatalogRef(this, FILES, getCatalogHref(name));
filesCat.finish();
ds.addDataset(filesCat);
} */
}
// remove the urlPath on the parent if multiple groups;
// cannot get a dataset with multiple groups in it
if (!isSingleGroup) {
parent.setUrlPath(null);
}
}
private InvDatasetImpl makeDatasetFromPartition(InvDatasetImpl parent, String parentCollectionName, PartitionCollectionImmutable.Partition partition, boolean isPofP) throws IOException {
InvDatasetImpl result;
String dsName = makeCollectionShortName(partition.getName());
// if (isPofP) { // make a catRef
String startPath = parentCollectionName == null ? getPath() : getPath() + "/" + parentCollectionName;
String dpath = startPath + "/" + partition.getName();
result = new InvCatalogRef(parent, dsName, buildCatalogServiceHref(dpath)); // LOOK could be plain if not PoP ??
result.setID(dpath);
result.setUrlPath(dpath);
result.setServiceName(Virtual_Services);
/* } else { // make a InvDatasetImpl
try (GribCollectionImmutable gc = partition.getGribCollection()) {
result = makeDatasetFromCollection(false, parentCollectionName, gc);
}
} */
//result.tmi.setGeospatialCoverage(extractGeospatial(group));
//result.tmi.setTimeCoverage(group.getCalendarDateRange());
return result;
}
/* file datasets of the partition catalog are InvCatalogRef pointing to "FileCatalogs"
private void addFileDatasets(InvDatasetImpl parent, String prefix) {
String name = (prefix == null) ? FILES : prefix + "/" + FILES;
InvCatalogRef ds = new InvCatalogRef(this, FILES, getCatalogHref(name));
ds.finish();
parent.addDataset(ds);
} */
// this catalog lists the individual files comprising the collection.
protected void addFileDatasets(InvCatalogImpl parentCatalog, InvDatasetImpl parent, GribCollectionImmutable fromGc) throws IOException {
List<MFile> mfiles = new ArrayList<>(fromGc.getFiles());
Collections.sort(mfiles);
InvDatasetImpl filesParent = new InvDatasetImpl(this, "Raw Files");
filesParent.tmi.setServiceName(Download_Services);
parent.addDataset(filesParent);
if (parentCatalog != null) parentCatalog.addService(makeDownloadService());
for (MFile mfile : mfiles) {
InvDatasetImpl ds = new InvDatasetImpl(this, mfile.getName());
String lpath = parent.getUrlPath() + "/" + FILES + "/" + mfile.getName(); // LOOK wrong
ds.setUrlPath(lpath);
ds.setID(lpath);
ds.tm.setDataSize(mfile.getLength());
// ds.tm.setXXXX(mfile.getLastModified());
ds.finish();
filesParent.addDataset(ds);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////
@Override
protected InvCatalogImpl makeCatalogTop(URI catURI, State localState) throws IOException, URISyntaxException {
InvCatalogImpl parentCatalog = (InvCatalogImpl) getParentCatalog();
InvCatalogImpl mainCatalog = new InvCatalogImpl(getName(), parentCatalog.getVersion(), catURI);
if (config.gribConfig.hasDatasetType(FeatureCollectionConfig.GribDatasetType.Latest))
mainCatalog.addService(InvService.latest);
mainCatalog.addDataset(((StateGrib) localState).top);
// mainCatalog.addService(orgService);
mainCatalog.addService(virtualService);
mainCatalog.finish();
return mainCatalog;
}
// called by DataRootHandler.makeDynamicCatalog() when a catref is requested
// see top javadoc for possible URLs
@Override
public InvCatalogImpl makeCatalog(String match, String reqPath, URI catURI) throws IOException {
StateGrib localState = (StateGrib) checkState();
if (localState == null) return null; // not ready yet maybe
try {
// case 0
if ((match == null) || (match.length() == 0)) {
return makeCatalogTop(catURI, localState); // top catalog : uses state.top previously made in checkState()
}
// case 1
if (localState.gribCollection instanceof PartitionCollectionImmutable) {
String[] paths = match.split("/");
PartitionCollectionImmutable pc = (PartitionCollectionImmutable) localState.gribCollection;
return makeCatalogFromPartition(pc, paths, 0, catURI);
}
} catch (Exception e) {
logger.error("Error making catalog for " + configPath, e);
}
return null;
}
private InvCatalogImpl makeCatalogFromPartition(PartitionCollectionImmutable pc, String[] paths, int idx, URI catURI) throws IOException {
if (paths.length < idx+1) return null;
PartitionCollectionImmutable.Partition pcp = pc.getPartitionByName(paths[idx]);
if (pcp == null) return null;
try (GribCollectionImmutable gc = pcp.getGribCollection()) {
if (paths.length > idx+1 && gc instanceof PartitionCollectionImmutable) {
PartitionCollectionImmutable pcNested = (PartitionCollectionImmutable) gc;
PartitionCollectionImmutable.Partition pcpNested = pcNested.getPartitionByName(paths[idx+1]);
if (pcpNested != null) // recurse
return makeCatalogFromPartition(pcNested, paths, idx + 1, catURI);
}
// build the parent name
int i = 0;
StringBuilder parentName = new StringBuilder();
for (String s : paths) {
if (i++ > 0) parentName.append("/");
parentName.append(s);
}
return makeCatalogFromCollection(gc, parentName.toString(), catURI);
}
}
private InvCatalogImpl makeCatalogFromCollection(GribCollectionImmutable fromGc, String parentCollectionName, URI catURI) throws IOException { // }, URISyntaxException {
InvCatalogImpl result = new InvCatalogImpl(makeCollectionShortName(fromGc.getName()), getParentCatalog().getVersion(), catURI); // LOOK is catURL right ??
// result.addService(orgService);
result.addService(virtualService);
InvDatasetImpl ds = makeDatasetFromCollection(false, result, parentCollectionName, fromGc);
result.addDataset(ds);
// String serviceName = ds.getServiceName(); LAME - cant do this way, needs service already added -fix in cat2; YEAH right, cat2
result.finish();
return result;
}
/////////////////////////////////////
@Override
protected void makeDatasetTop(State state) throws IOException {
StateGrib localState = (StateGrib) state;
localState.top = makeDatasetFromCollection(true, null, null, localState.gribCollection);
}
// path/latest.xml
@Override
public InvCatalogImpl makeLatest(String matchPath, String reqPath, URI catURI) throws IOException {
StateGrib localState = (StateGrib) checkState();
if (!(localState.gribCollection instanceof PartitionCollectionImmutable)) return null;
PartitionCollectionImmutable pc = (PartitionCollectionImmutable) localState.gribCollection;
if (localState.latest == null) {
List<String> paths = new ArrayList<>();
GribCollectionImmutable latest = pc.getLatestGribCollection(paths);
if (latest == null) return null;
latest.close(); // doesnt need to be open
// make pathname
Formatter f = new Formatter();
int count = 0;
for (String p : paths) {
if (count++ > 0) f.format("/");
f.format("%s",p);
}
synchronized (lock) {
localState.latest = latest;
localState.latestPath = f.toString();
}
}
return makeCatalogFromCollection(localState.latest, localState.latestPath, catURI);
}
///////////////////////////////////////////////////////////////////////////
private ThreddsMetadata.GeospatialCoverage extractGeospatial(Iterable<GribCollectionImmutable.GroupGC> groups) {
ThreddsMetadata.GeospatialCoverage gcAll = null;
for (GribCollectionImmutable.GroupGC group : groups) {
ThreddsMetadata.GeospatialCoverage gc = extractGeospatial(group);
if (gcAll == null) gcAll = gc;
else gcAll.extend(gc);
}
return gcAll;
}
private ThreddsMetadata.GeospatialCoverage extractGeospatial(GribCollectionImmutable.GroupGC group) {
GdsHorizCoordSys gdsCoordSys = group.getGdsHorizCoordSys();
LatLonRect llbb = GridCoordSys.getLatLonBoundingBox(gdsCoordSys.proj, gdsCoordSys.getStartX(), gdsCoordSys.getStartY(),
gdsCoordSys.getEndX(), gdsCoordSys.getEndY());
ThreddsMetadata.GeospatialCoverage gc = new ThreddsMetadata.GeospatialCoverage();
if (llbb != null)
gc.setBoundingBox(llbb);
if (gdsCoordSys.isLatLon()) {
gc.setLonResolution(gdsCoordSys.dx);
gc.setLatResolution(gdsCoordSys.dy);
}
return gc;
}
private CalendarDateRange extractCalendarDateRange(Iterable<GribCollectionImmutable.GroupGC> groups) {
CalendarDateRange gcAll = null;
for (GribCollectionImmutable.GroupGC group : groups) {
CalendarDateRange gc = group.makeCalendarDateRange();
if (gcAll == null) gcAll = gc;
else gcAll.extend(gc);
}
return gcAll;
}
//////////////////////////////////////////////////////////////////////////////
protected String getDatasetNameLatest(String dsName) {
if (config.gribConfig != null && config.gribConfig.latestNamer != null) {
return config.gribConfig.latestNamer;
} else {
return "Latest Collection for "+dsName;
}
}
protected String getDatasetNameBest(String dsName) {
if (config.gribConfig != null && config.gribConfig.bestNamer != null) {
return config.gribConfig.bestNamer;
} else {
return "Best "+dsName +" Time Series";
}
}
protected String getDatasetNameTP(String dsName) {
return "Full Collection Dataset";
}
protected String getDatasetNameTwoD(String dsName) {
return "Full Collection (Reference / Forecast Time) Dataset";
}
@Override
public File getFile(String remaining) {
try {
StateGrib localState = (StateGrib) checkState();
int pos = remaining.lastIndexOf("/");
final String filename = (pos >= 0) && (remaining.length() > 1) ? remaining.substring(pos+1) : remaining;
MFile result = (MFile) findDataset(remaining, localState.gribCollection, new DatasetCreator() {
@Override
public Object obtain(GribCollectionImmutable gc, GribCollectionImmutable.Dataset ds, GribCollectionImmutable.GroupGC group) throws IOException {
return gc.findMFileByName(filename);
}
});
if (result == null) return null;
return new File(result.getPath());
} catch (IOException iow ) {
return null;
}
}
@Override
public ucar.nc2.dt.grid.GridDataset getGridDataset(String matchPath) throws IOException {
StateGrib localState = (StateGrib) checkState();
return (ucar.nc2.dt.grid.GridDataset) findDataset(matchPath, localState.gribCollection, new DatasetCreator() {
@Override
public Object obtain(GribCollectionImmutable gc, GribCollectionImmutable.Dataset ds, GribCollectionImmutable.GroupGC group) throws IOException {
return gc.getGridDataset(ds, group, null, config, null, logger);
}
});
}
@Override
public NetcdfDataset getNetcdfDataset(String matchPath) throws IOException {
StateGrib localState = (StateGrib) checkState();
return (NetcdfDataset) findDataset(matchPath, localState.gribCollection, new DatasetCreator() {
@Override
public Object obtain(GribCollectionImmutable gc, GribCollectionImmutable.Dataset ds, GribCollectionImmutable.GroupGC group) throws IOException {
return gc.getNetcdfDataset(ds, group, null, config, null, logger);
}
});
}
// see top javadoc for possible URLs
// returns visitor.obtain(), either a GridDataset or a NetcdfDataset
private Object findDataset(String matchPath, GribCollectionImmutable topCollection, DatasetCreator visit) throws IOException {
// LOOK Do we need to strip off a GC in case of bookmarked URLs?
String[] paths = matchPath.split("/");
List<String> pathList = (paths.length < 1) ? new ArrayList<String>() : Arrays.asList(paths);
DatasetAndGroup dg = findDatasetAndGroup(pathList, topCollection);
if (dg != null)
return visit.obtain(topCollection, dg.ds, dg.group);
if (!(topCollection instanceof PartitionCollectionImmutable)) return null;
PartitionCollectionImmutable pc = (PartitionCollectionImmutable) topCollection;
return findDatasetPartition(visit, pc, pathList);
}
private static class DatasetAndGroup {
GribCollectionImmutable.Dataset ds;
GribCollectionImmutable.GroupGC group;
private DatasetAndGroup(GribCollectionImmutable.Dataset ds, GribCollectionImmutable.GroupGC group) {
this.ds = ds;
this.group = group;
}
}
/* case 0: path/dataset (dataset = "") // single dataset
case 1: path/dataset (dataset = BEST, TWOD, TP) // single group
case 2: path/dataset/groupName
case 3: path/groupName // single dataset not sure this is actually used ??
*/
private DatasetAndGroup findDatasetAndGroup(List<String> paths, GribCollectionImmutable gc) {
if (paths.size() < 1 || paths.get(0).length() == 0) { // case 0: use first dataset,group in the collection
GribCollectionImmutable.Dataset ds = gc.getDataset(0);
GribCollectionImmutable.GroupGC dg = ds.getGroup(0);
return new DatasetAndGroup(ds, dg);
}
GribCollectionImmutable.Dataset ds = gc.getDatasetByTypeName(paths.get(0));
if (ds != null) {
boolean isSingleGroup = ds.getGroupsSize() == 1;
if (paths.size() == 1) { // case 1
if (!isSingleGroup) return null;
GribCollectionImmutable.GroupGC g = ds.getGroup(0);
return new DatasetAndGroup(ds, g);
}
if (paths.size() == 2) { // case 2
String groupName = paths.get(1);
GribCollectionImmutable.GroupGC g = ds.findGroupById(groupName);
if (g != null)
return new DatasetAndGroup(ds, g);
else
return null;
}
}
if (paths.size() == 1) { // case 3
String groupName = paths.get(0);
ds = gc.getDataset(0);
GribCollectionImmutable.GroupGC g = ds.findGroupById(groupName);
if (g != null)
return new DatasetAndGroup(ds, g);
}
return null;
}
private Object findDatasetPartition(DatasetCreator visit, PartitionCollectionImmutable outerPartition, List<String> pathList) throws IOException {
int n = pathList.size();
if (pathList.size() < 1) return null;
PartitionCollectionImmutable.Partition pcp = outerPartition.getPartitionByName(pathList.get(0));
if (pcp == null) {
DatasetAndGroup dg = findDatasetAndGroup(pathList, outerPartition);
if (dg != null)
return visit.obtain(outerPartition, dg.ds, dg.group);
else
return null;
}
try (GribCollectionImmutable gc = pcp.getGribCollection()) {
if (gc instanceof PartitionCollectionImmutable) {
PartitionCollectionImmutable pcNested = (PartitionCollectionImmutable) gc;
return findDatasetPartition(visit, pcNested, pathList.subList(1, n));
}
DatasetAndGroup dg = findDatasetAndGroup(pathList.subList(1, n), gc);
if (dg != null)
return visit.obtain(gc, dg.ds, dg.group);
else {
GribCollectionImmutable.Dataset ds = gc.getDataset(0);
GribCollectionImmutable.GroupGC group = ds.getGroup(0);
return visit.obtain(gc, ds, group);
}
}
}
private interface DatasetCreator { // Visitor pattern
Object obtain(GribCollectionImmutable gc, GribCollectionImmutable.Dataset ds, GribCollectionImmutable.GroupGC group) throws IOException;
}
}