/*
*
* * Copyright 1998-2014 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.grib.collection;
import net.jcip.annotations.Immutable;
import thredds.featurecollection.FeatureCollectionConfig;
import thredds.inventory.CollectionAbstract;
import thredds.inventory.MFile;
import ucar.coord.*;
import ucar.nc2.grib.GdsHorizCoordSys;
import ucar.nc2.grib.GribIndexCache;
import ucar.nc2.grib.GribTables;
import ucar.nc2.grib.grib1.Grib1Gds;
import ucar.nc2.grib.grib1.Grib1ParamTime;
import ucar.nc2.grib.grib1.Grib1SectionProductDefinition;
import ucar.nc2.grib.grib1.Grib1Variable;
import ucar.nc2.grib.grib1.tables.Grib1Customizer;
import ucar.nc2.grib.grib2.*;
import ucar.nc2.grib.grib2.table.Grib2Customizer;
import ucar.nc2.time.CalendarDate;
import ucar.nc2.time.CalendarDateFormatter;
import ucar.nc2.time.CalendarDateRange;
import ucar.nc2.time.CalendarTimeZone;
import ucar.unidata.io.RandomAccessFile;
import ucar.unidata.util.Parameter;
import ucar.unidata.util.StringUtil2;
import java.io.Closeable;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
/**
* A mutable class for writing indices or building GribCollectionImmutable
*
* @author John
* @since 12/1/13
*/
public class GribCollectionMutable implements Closeable {
static private final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(GribCollectionMutable.class);
static public final long MISSING_RECORD = -1;
//////////////////////////////////////////////////////////
static MFile makeIndexMFile(String collectionName, File directory) {
String nameNoBlanks = StringUtil2.replace(collectionName, ' ', "_");
return new GcMFile(directory, nameNoBlanks + CollectionAbstract.NCX_SUFFIX, -1, -1, -1); // LOOK dont know lastMod, size. can it be added later?
}
private static CalendarDateFormatter cf = new CalendarDateFormatter("yyyyMMdd-HHmmss", new CalendarTimeZone("UTC"));
static public String makeName(String collectionName, CalendarDate runtime) {
String nameNoBlanks = StringUtil2.replace(collectionName, ' ', "_");
return nameNoBlanks + "-" + cf.toString(runtime);
}
////////////////////////////////////////////////////////////////
protected String name; // collection name; index filename must be directory/name.ncx2
protected FeatureCollectionConfig config;
protected boolean isGrib1;
protected File directory;
protected String orgDirectory;
// set by the builder
public int version; // the ncx version
public int center, subcenter, master, local; // GRIB 1 uses "local" for table version
public int genProcessType, genProcessId, backProcessId;
public List<Parameter> params; // not used
protected Map<Integer, MFile> fileMap; // all the files used in the GC; key is the index in original collection, GC has subset of them
protected List<Dataset> datasets;
protected List<GribHorizCoordSystem> horizCS; // one for each unique GDS
protected CoordinateRuntime masterRuntime;
protected GribTables cust;
protected int indexVersion;
// not stored in index
protected RandomAccessFile indexRaf; // this is the raf of the index (ncx) file
protected String indexFilename;
protected long lastModified;
protected long fileSize;
public static int countGC;
protected GribCollectionMutable(String name, File directory, FeatureCollectionConfig config, boolean isGrib1) {
countGC++;
this.name = name;
this.directory = directory;
this.config = config;
this.isGrib1 = isGrib1;
if (config == null)
logger.error("HEY GribCollection {} has empty config%n", name);
if (name == null)
logger.error("HEY GribCollection has null name dir={}%n", directory);
}
// for making partition collection
protected void copyInfo(GribCollectionMutable from) {
this.center = from.center;
this.subcenter = from.subcenter;
this.master = from.master;
this.local = from.local;
this.genProcessType = from.genProcessType;
this.genProcessId = from.genProcessId;
this.backProcessId = from.backProcessId;
}
public String getName() {
return name;
}
public File getDirectory() {
return directory;
}
public String getLocation() {
if (indexRaf != null) return indexRaf.getLocation();
return getIndexFilepathInCache();
}
public Collection<MFile> getFiles() {
return fileMap.values();
}
public FeatureCollectionConfig getConfig() {
return config;
}
/**
* The files that comprise the collection.
* Actual paths, including the grib cache if used.
*
* @return list of filename.
*/
public List<String> getFilenames() {
List<String> result = new ArrayList<>();
for (MFile file : fileMap.values())
result.add(file.getPath());
Collections.sort(result);
return result;
}
public File getIndexParentFile() {
if (indexRaf == null) return null;
Path index = Paths.get(indexRaf.getLocation());
Path parent = index.getParent();
return parent.toFile();
}
public String getFilename(int fileno) {
return fileMap.get(fileno).getPath();
}
public List<Dataset> getDatasets() {
return datasets;
}
public Dataset makeDataset(GribCollectionImmutable.Type type) {
Dataset result = new Dataset(type);
datasets.add(result);
return result;
}
public GribCollectionMutable.Dataset getDatasetCanonical() {
for (GribCollectionMutable.Dataset ds : datasets) {
if (ds.gctype != GribCollectionImmutable.Type.Best) return ds;
}
throw new IllegalStateException("GC.getDatasetCanonical failed on=" + name);
}
public GribHorizCoordSystem getHorizCS(int index) {
return horizCS.get(index);
}
protected void makeHorizCS() {
Map<Object, GribHorizCoordSystem> gdsMap = new HashMap<>(); // WTF ?? unique ???
for (Dataset ds : datasets) {
for (GroupGC hcs : ds.groups)
gdsMap.put(hcs.getGdsHash(), hcs.horizCoordSys);
}
horizCS = new ArrayList<>();
for (GribHorizCoordSystem hcs : gdsMap.values())
horizCS.add(hcs);
}
public int findHorizCS(GribHorizCoordSystem hcs) {
return horizCS.indexOf(hcs);
}
public void addHorizCoordSystem(GdsHorizCoordSys hcs, byte[] rawGds, Object gdsHashObject, int predefinedGridDefinition) {
String hcsName = makeHorizCoordSysName(hcs);
// check for user defined group names
String desc = null;
if (config.gribConfig.gdsNamer != null)
desc = config.gribConfig.gdsNamer.get(gdsHashObject.hashCode());
if (desc == null) desc = hcs.makeDescription(); // default desc
horizCS.add(new GribHorizCoordSystem(hcs, rawGds, gdsHashObject, hcsName, desc, predefinedGridDefinition));
}
public void setFileMap(Map<Integer, MFile> fileMap) {
this.fileMap = fileMap;
}
/**
* public by accident, do not use
*
* @param indexRaf the open raf of the index file
*/
void setIndexRaf(RandomAccessFile indexRaf) {
this.indexRaf = indexRaf;
if (indexRaf != null) {
this.indexFilename = indexRaf.getLocation();
}
}
/**
* get index filename
*
* @return index filename; may not exist; may be in disk cache
*/
private String getIndexFilepathInCache() {
File indexFile = GribCdmIndex.makeIndexFile(name, directory);
return GribIndexCache.getFileOrCache(indexFile.getPath()).getPath();
}
// set from GribCollectionBuilderFromIndex.readFromIndex()
public File setOrgDirectory(String orgDirectory) {
this.orgDirectory = orgDirectory;
directory = new File(orgDirectory);
if (!directory.exists()) {
File indexFile = new File(indexFilename);
File parent = indexFile.getParentFile();
if (parent.exists())
directory = parent;
}
return directory;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// stuff for FileCacheable
public void close() throws java.io.IOException {
if (indexRaf != null) {
indexRaf.close();
indexRaf = null;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// these objects are created from the ncx index. lame - should only be in the builder i think
private Set<String> hcsNames = new HashSet<>(5);
private String makeHorizCoordSysName(GdsHorizCoordSys hcs) {
// default id
String base = hcs.makeId();
// ensure uniqueness
String tryit = base;
int count = 1;
while (hcsNames.contains(tryit)) {
count++;
tryit = base + "-" + count;
}
hcsNames.add(tryit);
return tryit;
}
public class Dataset {
public GribCollectionImmutable.Type gctype;
List<GroupGC> groups; // must be kept in order, because PartitionForVariable2D has index into it
public Dataset(GribCollectionImmutable.Type type) {
this.gctype = type;
groups = new ArrayList<>();
}
Dataset(Dataset from) {
this.gctype = from.gctype;
groups = new ArrayList<>(from.groups.size());
}
public GroupGC addGroupCopy(GroupGC from) {
GroupGC g = new GroupGC(from);
groups.add(g);
return g;
}
public List<GroupGC> getGroups() {
return groups;
}
}
public class GroupGC implements Comparable<GroupGC> {
GribHorizCoordSystem horizCoordSys;
List<VariableIndex> variList;
List<Coordinate> coords; // shared coordinates
int[] filenose; // key for GC.fileMap
HashMap<GribCollectionMutable.VariableIndex, GribCollectionMutable.VariableIndex> varMap;
boolean isTwoD = true; // true except for Best (?)
GroupGC() {
this.variList = new ArrayList<>();
this.coords = new ArrayList<>();
}
// copy constructor for PartitionBuilder
GroupGC(GroupGC from) {
this.horizCoordSys = from.horizCoordSys; // reference
this.variList = new ArrayList<>(from.variList.size()); // empty list
this.coords = new ArrayList<>(from.coords.size()); // empty list
this.isTwoD = from.isTwoD;
}
public VariableIndex addVariable(VariableIndex vi) {
variList.add(vi);
return vi;
}
public GribCollectionMutable getGribCollection() {
return GribCollectionMutable.this;
}
public Iterable<VariableIndex> getVariables() {
return variList;
}
public Iterable<Coordinate> getCoordinates() {
return coords;
}
// unique name for Group
public String getId() {
return horizCoordSys.getId();
}
// human readable
public String getDescription() {
return horizCoordSys.getDescription();
}
public byte[] getGdsBytes() {
return horizCoordSys.getRawGds();
}
public Object getGdsHash() {
return horizCoordSys.getGdsHash();
}
@Override
public int compareTo(GroupGC o) {
return getDescription().compareTo(o.getDescription());
}
public List<MFile> getFiles() {
List<MFile> result = new ArrayList<>();
if (filenose == null) return result;
for (int fileno : filenose)
result.add(fileMap.get(fileno));
Collections.sort(result);
return result;
}
public List<String> getFilenames() {
List<String> result = new ArrayList<>();
if (filenose == null) return result;
for (int fileno : filenose)
result.add(fileMap.get(fileno).getPath());
Collections.sort(result);
return result;
}
// get the variable in this group that has same object equality as want
public GribCollectionMutable.VariableIndex findVariableByHash(GribCollectionMutable.VariableIndex want) {
if (varMap == null) {
varMap = new HashMap<>(variList.size() * 2);
for (VariableIndex vi : variList) {
VariableIndex old = varMap.put(vi, vi);
if (old != null) {
logger.error("GribCollectionMutable has duplicate variable hash {} == {}", vi, old);
}
//System.out.printf("%s%n", vi.hashCode());
}
}
GribCollectionMutable.VariableIndex result = varMap.get(want);
/* if (result == null) {
System.out.printf("%s%n", want.hashCode());
for (VariableIndex vi : variList) {
System.out.printf("%s%n", vi.hashCode());
System.out.printf("%s%n", vi.equals(want));
}
} */
return result;
}
private CalendarDateRange dateRange = null;
public CalendarDateRange getCalendarDateRange() {
if (dateRange == null) {
CalendarDateRange result = null;
for (Coordinate coord : coords) {
switch (coord.getType()) {
case time:
case timeIntv:
case time2D:
CoordinateTimeAbstract time = (CoordinateTimeAbstract) coord;
CalendarDateRange range = time.makeCalendarDateRange(null);
if (result == null) result = range;
else result = result.extend(range);
}
}
dateRange = result;
}
return dateRange;
}
public int getNFiles() {
if (filenose == null) return 0;
return filenose.length;
}
public void show(Formatter f) {
f.format("Group %s (%d) isTwoD=%s%n", horizCoordSys.getId(), horizCoordSys.getGdsHash().hashCode(), isTwoD);
f.format(" nfiles %d%n", filenose == null ? 0 : filenose.length);
f.format(" hcs = %s%n", horizCoordSys.getHcs());
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("GroupGC{");
sb.append(GribCollectionMutable.this.getName());
sb.append(" isTwoD=").append(isTwoD);
sb.append('}');
return sb.toString();
}
}
public GribCollectionMutable.VariableIndex makeVariableIndex(GroupGC g, GribTables customizer, int discipline, int center,
int subcenter, byte[] rawPds, List<Integer> index, long recordsPos, int recordsLen) {
return new VariableIndex(g, customizer, discipline, center, subcenter, rawPds, index, recordsPos, recordsLen);
}
public class VariableIndex implements Comparable<VariableIndex> {
public final GroupGC group; // belongs to this group
public final int tableVersion; // grib1 only : can vary by variable
public final int discipline, center, subcenter; // grib2 only
public final byte[] rawPds; // grib1 or grib2
public final long recordsPos; // where the records array is stored in the index. 0 means no records
public final int recordsLen;
public Object gribVariable; // use this to test for object equality
List<Integer> coordIndex; // indexes into group.coords
// derived from pds
public final int category, parameter, levelType, intvType, ensDerivedType, probType;
private String intvName; // eg "mixed intervals, 3 Hour, etc"
public final String probabilityName;
public final boolean isLayer, isEnsemble;
public final int genProcessType;
// stats
public int ndups, nrecords, nmissing;
// temporary storage while building - do not use
List<Coordinate> coords;
private VariableIndex(GroupGC g, GribTables customizer, int discipline, int center, int subcenter, byte[] rawPds,
List<Integer> index, long recordsPos, int recordsLen) {
this.group = g;
this.discipline = discipline;
this.rawPds = rawPds;
this.center = center;
this.subcenter = subcenter;
this.coordIndex = index;
this.recordsPos = recordsPos;
this.recordsLen = recordsLen;
if (isGrib1) {
Grib1Customizer cust = (Grib1Customizer) customizer;
Grib1SectionProductDefinition pds = new Grib1SectionProductDefinition(rawPds);
// quantities that are stored in the pds
this.category = 0;
this.tableVersion = pds.getTableVersion();
this.parameter = pds.getParameterNumber();
this.levelType = pds.getLevelType();
Grib1ParamTime ptime = cust.getParamTime(pds);
if (ptime.isInterval()) {
this.intvType = pds.getTimeRangeIndicator();
} else {
this.intvType = -1;
}
this.isLayer = cust.isLayer(pds.getLevelType());
this.ensDerivedType = -1;
this.probType = -1;
this.probabilityName = null;
this.genProcessType = pds.getGenProcess(); // LOOK process vs process type ??
this.isEnsemble = pds.isEnsemble();
// LOOK config vs serialized config
gribVariable = new Grib1Variable(cust, pds, (Grib1Gds) g.getGdsHash(), config.gribConfig.useTableVersion, config.gribConfig.intvMerge, config.gribConfig.useCenter);
} else {
Grib2Customizer cust2 = (Grib2Customizer) customizer;
Grib2SectionProductDefinition pdss = new Grib2SectionProductDefinition(rawPds);
Grib2Pds pds = pdss.getPDS();
assert pds != null;
this.tableVersion = -1;
// quantities that are stored in the pds
this.category = pds.getParameterCategory();
this.parameter = pds.getParameterNumber();
this.levelType = pds.getLevelType1();
this.intvType = pds.getStatisticalProcessType();
this.isLayer = Grib2Utils.isLayer(pds);
if (pds.isEnsembleDerived()) {
Grib2Pds.PdsEnsembleDerived pdsDerived = (Grib2Pds.PdsEnsembleDerived) pds;
ensDerivedType = pdsDerived.getDerivedForecastType(); // derived type (table 4.7)
} else {
this.ensDerivedType = -1;
}
if (pds.isProbability()) {
Grib2Pds.PdsProbability pdsProb = (Grib2Pds.PdsProbability) pds;
probabilityName = pdsProb.getProbabilityName();
probType = pdsProb.getProbabilityType();
} else {
this.probType = -1;
this.probabilityName = null;
}
this.genProcessType = pds.getGenProcessType();
this.isEnsemble = pds.isEnsemble();
// LOOK config vs serialized config
gribVariable = new Grib2Variable (cust2, discipline, center, subcenter, (Grib2Gds) g.getGdsHash(), pds, config.gribConfig.intvMerge, config.gribConfig.useGenType);
}
}
protected VariableIndex(GroupGC g, VariableIndex other) {
this.group = g;
this.tableVersion = other.tableVersion;
this.discipline = other.discipline;
this.center = other.center;
this.subcenter = other.subcenter;
this.rawPds = other.rawPds;
this.gribVariable = other.gribVariable;
this.coordIndex = new ArrayList<>(other.coordIndex);
this.recordsPos = 0;
this.recordsLen = 0;
this.category = other.category;
this.parameter = other.parameter;
this.levelType = other.levelType;
this.intvType = other.intvType;
this.isLayer = other.isLayer;
this.ensDerivedType = other.ensDerivedType;
this.probabilityName = other.probabilityName;
this.probType = other.probType;
this.genProcessType = other.genProcessType;
this.isEnsemble = other.isEnsemble;
}
public List<Coordinate> getCoordinates() {
List<Coordinate> result = new ArrayList<>(coordIndex.size());
for (int idx : coordIndex)
result.add(group.coords.get(idx));
return result;
}
public Coordinate getCoordinate(Coordinate.Type want) {
for (int idx : coordIndex)
if (group.coords.get(idx).getType() == want)
return group.coords.get(idx);
return null;
}
public int getCoordinateIdx(Coordinate.Type want) {
for (int idx : coordIndex)
if (group.coords.get(idx).getType() == want)
return idx;
return -1;
}
public String getTimeIntvName() {
if (intvName != null) return intvName;
CoordinateTimeIntv timeiCoord = (CoordinateTimeIntv) getCoordinate(Coordinate.Type.timeIntv);
if (timeiCoord != null) {
intvName = timeiCoord.getTimeIntervalName();
return intvName;
}
CoordinateTime2D time2DCoord = (CoordinateTime2D) getCoordinate(Coordinate.Type.time2D);
if (time2DCoord == null || !time2DCoord.isTimeInterval()) return null;
intvName = time2DCoord.getTimeIntervalName();
return intvName;
}
/////////////////////////////
public String id() {
return discipline + "-" + category + "-" + parameter;
}
public int getVarid() {
return (discipline << 16) + (category << 8) + parameter;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("VariableIndex");
sb.append("{tableVersion=").append(tableVersion);
sb.append(", discipline=").append(discipline);
sb.append(", category=").append(category);
sb.append(", parameter=").append(parameter);
sb.append(", levelType=").append(levelType);
sb.append(", intvType=").append(intvType);
sb.append(", ensDerivedType=").append(ensDerivedType);
sb.append(", probType=").append(probType);
sb.append(", intvName='").append(intvName).append('\'');
sb.append(", probabilityName='").append(probabilityName).append('\'');
sb.append(", isLayer=").append(isLayer);
sb.append(", genProcessType=").append(genProcessType);
sb.append(", cdmHash=").append(gribVariable.hashCode());
//sb.append(", partTimeCoordIdx=").append(partTimeCoordIdx);
sb.append('}');
return sb.toString();
}
public String toStringComplete() {
final StringBuilder sb = new StringBuilder();
sb.append("VariableIndex");
sb.append("{tableVersion=").append(tableVersion);
sb.append(", discipline=").append(discipline);
sb.append(", category=").append(category);
sb.append(", parameter=").append(parameter);
sb.append(", levelType=").append(levelType);
sb.append(", intvType=").append(intvType);
sb.append(", ensDerivedType=").append(ensDerivedType);
sb.append(", probType=").append(probType);
sb.append(", intvName='").append(intvName).append('\'');
sb.append(", probabilityName='").append(probabilityName).append('\'');
sb.append(", isLayer=").append(isLayer);
sb.append(", cdmHash=").append(gribVariable.hashCode());
sb.append(", recordsPos=").append(recordsPos);
sb.append(", recordsLen=").append(recordsLen);
sb.append(", group=").append(group.getId());
//sb.append(", partTimeCoordIdx=").append(partTimeCoordIdx);
sb.append("}\n");
/* if (time2runtime == null) sb.append("time2runtime is null");
else {
sb.append("time2runtime=");
for (int idx = 0; idx < time2runtime.getN(); idx++)
sb.append(time2runtime.get(idx)).append(",");
} */
return sb.toString();
}
public String toStringShort() {
Formatter sb = new Formatter();
sb.format("Variable {%d-%d-%d", discipline, category, parameter);
sb.format(", levelType=%d", levelType);
sb.format(", intvType=%d", intvType);
if (intvName != null && intvName.length() > 0) sb.format(" intv=%s", intvName);
if (probabilityName != null && probabilityName.length() > 0) sb.format(" prob=%s", probabilityName);
sb.format(" cdmHash=%d}", gribVariable.hashCode());
return sb.toString();
}
@Override
public int compareTo(VariableIndex o) {
int r = discipline - o.discipline; // LOOK add center, subcenter, version?
if (r != 0) return r;
r = category - o.category;
if (r != 0) return r;
r = parameter - o.parameter;
if (r != 0) return r;
r = levelType - o.levelType;
if (r != 0) return r;
r = intvType - o.intvType;
return r;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof VariableIndex)) return false;
VariableIndex that = (VariableIndex) o;
return gribVariable.equals(that.gribVariable);
}
@Override
public int hashCode() {
return gribVariable.hashCode();
}
} // VariableIndex
@Immutable
public static class Record {
public final int fileno; // which file
public final long pos; // offset on file where data starts
public final long bmsPos; // if non-zero, offset where bms starts
public final int scanMode; // from gds
public Record(int fileno, long pos, long bmsPos, int scanMode) {
this.fileno = fileno;
this.pos = pos;
this.bmsPos = bmsPos;
this.scanMode = scanMode;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("GribCollection.Record{");
sb.append("fileno=").append(fileno);
sb.append(", pos=").append(pos);
sb.append(", bmsPos=").append(bmsPos);
sb.append(", scanMode=").append(scanMode);
sb.append('}');
return sb.toString();
}
}
public void showIndex(Formatter f) {
f.format("Class (%s)%n", getClass().getName());
f.format("%s%n%n", toString());
//f.format(" master runtime coordinate%n");
//masterRuntime.showCoords(f);
//f.format("%n");
for (Dataset ds : datasets) {
f.format("Dataset %s%n", ds.gctype);
for (GroupGC g : ds.groups) {
f.format(" Group %s%n", g.horizCoordSys.getId());
for (VariableIndex v : g.variList) {
f.format(" %s%n", v.toStringShort());
}
}
}
if (fileMap == null) {
f.format("Files empty%n");
} else {
f.format("Files (%d)%n", fileMap.size());
for (int index : fileMap.keySet()) {
f.format(" %d: %s%n", index, fileMap.get(index));
}
f.format("%n");
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("GribCollectionMutable{");
sb.append("\nname='").append(name).append('\'');
sb.append("\n directory=").append(directory);
sb.append("\n config=").append(config);
sb.append("\n isGrib1=").append(isGrib1);
sb.append("\n version=").append(version);
sb.append("\n center=").append(center);
sb.append("\n subcenter=").append(subcenter);
sb.append("\n master=").append(master);
sb.append("\n local=").append(local);
sb.append("\n genProcessType=").append(genProcessType);
sb.append("\n genProcessId=").append(genProcessId);
sb.append("\n backProcessId=").append(backProcessId);
sb.append("\n}");
return sb.toString();
}
public String showLocation() {
return "name="+name+" directory="+directory;
}
public GroupGC makeGroup() {
return new GroupGC();
}
}