public GridRecord findRecord(int ens, int time, int level) {
    if (hasEnsemble()) {
      return recordTracker[ens * (ntimes * nlevels) + (time * nlevels) + level];
    } else {
      return recordTracker[time * nlevels + level];
    }
  }

public GridRecord findRecord ( int ens , int time , int level ) { if ( hasEnsemble ( ) ) { return recordTracker [ ens * ( ntimes * nlevels ) + ( time * nlevels ) + level ] ; } else { return recordTracker [ time * nlevels + level ] ; } }

Find the grid record for the time and level indices
Canonical ordering is ens, time, level
@param ens   ensemble index
@param time  time index
@param level level index
@return the record or null

Find the grid record for the time and level indices Canonical ordering is ens time level


public String dump() {
    DateFormatter formatter = new DateFormatter();
    Formatter sbuff = new Formatter();
    sbuff.format("%s %d %n", name, records.size());
    for (GridRecord record : records) {
      sbuff.format(" level = %d %f", record.getLevelType1(), record.getLevel1());
      if (null != record.getValidTime())
        sbuff.format(" time = %s", formatter.toDateTimeString(record.getValidTime()));
      sbuff.format("%n");
    }
    return sbuff.toString();
  }

public
String
dump
(
)
{
DateFormatter
formatter
=
new
DateFormatter
(
)
;
Formatter
sbuff
=
new
Formatter
(
)
;
sbuff
.
format
(
"%s %d %n"
,
name
,
records
.
size
(
)
)
;
for
(
GridRecord
record
:
records
)
{
sbuff
.
format
(
" level = %d %f"
,
record
.
getLevelType1
(
)
,
record
.
getLevel1
(
)
)
;
if
(
!=
record
.
getValidTime
(
)
)
sbuff
.
format
(
" time = %s"
,
formatter
.
toDateTimeString
(
record
.
getValidTime
(
)
)
)
;
sbuff
.
format
(
"%n"
)
;
}
return
sbuff
.
toString
(
)
;
}

Dump this variable

@return the variable


protected String makeLongName() {
    Formatter f = new Formatter();
    GridParameter param = lookup.getParameter(firstRecord);
    if (param == null) return null;

    f.format("%s", param.getDescription());

    String levelName = makeLevelName(firstRecord, lookup);
    if (levelName.length() != 0)
      f.format(" @ %s", levelName);

    return f.toString();
  }

protected
String
makeLongName
(
)
{
Formatter
f
=
new
Formatter
(
)
;
GridParameter
param
=
lookup
.
getParameter
(
firstRecord
)
;
if
(
param
==
)
return
;
f
.
format
(
"%s"
,
param
.
getDescription
(
)
)
;
String
levelName
=
makeLevelName
(
firstRecord
,
lookup
)
;
if
(
levelName
.
length
(
)
!=
0
)
f
.
format
(
" @ %s"
,
levelName
)
;
return
f
.
toString
(
)
;
}

Make a long name for the variable

@return long variable name


public Optional<CoverageCoordAxis> subsetByIntervals(List<MAMath.MinMax> lonIntvs, int stride) {
    if (axisType != AxisType.Lon)
      return Optional.empty("subsetByIntervals only for longitude");
    if (!isRegular())
      return Optional.empty("subsetByIntervals only for regular longitude");

    CoordAxisHelper helper = new CoordAxisHelper(this);

    double start = Double.NaN;
    boolean first = true;
    List<RangeIterator> ranges = new ArrayList<>();
    for (MAMath.MinMax lonIntv : lonIntvs) {
      if (first) start = lonIntv.min;
      first = false;

      Optional<RangeIterator> opt = helper.makeRange(lonIntv.min, lonIntv.max, stride);
      if (!opt.isPresent())
        return Optional.empty(opt.getErrorMessage());
      ranges.add(opt.get());
    }

    try {
      RangeComposite compositeRange = new RangeComposite(AxisType.Lon.toString(), ranges);
      int npts = compositeRange.length();
      double end = start + npts * resolution;

      CoverageCoordAxisBuilder builder = new CoverageCoordAxisBuilder(this); // copy
      builder.subset(npts, start, end, resolution, null);
      builder.setRange(null);
      builder.setCompositeRange(compositeRange);

      return Optional.of(new CoverageCoordAxis1D(builder));
    } catch (InvalidRangeException e) {
      return Optional.empty(e.getMessage());
    }
  }

only for longitude, only for regular (do we need a subclass for longitude 1D coords ??

only for longitude only for regular ( do we need a subclass for longitude 1D coords ??


static boolean
    memequal(byte[] b1, byte[] b2, int len)
    {
        if(b1 == b2) return true;
        if(b1 == null || b2 == null) return false;
        if(b1.length < len || b2.length < len) return false;
        for(int i = 0; i < len; i++) {
            if(b1[i] != b2[i]) return false;
        }
        return true;
    }

static boolean memequal ( byte [ ] b1 , byte [ ] b2 , int len ) { if ( b1 == b2 ) return true ; if ( b1 == null || b2 == null ) return false ; if ( b1 . length < len || b2 . length < len ) return false ; for ( int i = 0 ; i < len ; i ++ ) { if ( b1 [ i ] != b2 [ i ] ) return false ; } return true ; }

Not quite memcmp

@param b1
@param b2
@param len
@return


protected DapDataset
    parseDMR(String document)
            throws DapException
    {
        // Parse the dmr
        Dap4Parser parser;
        //if(USEDOM)
        parser = new DOM4Parser(null);
        //else
        //    parser = new DOM4Parser(new DefaultDMRFactory());
        if(PARSEDEBUG)
            parser.setDebugLevel(1);
        try {
            if(!parser.parse(document))
                throw new DapException("DMR Parse failed");
        } catch (SAXException se) {
            throw new DapException(se);
        }
        if(parser.getErrorResponse() != null)
            throw new DapException("Error Response Document not supported");
        DapDataset result = parser.getDMR();
        processAttributes(result);
        return result;
    }

It is common to want to parse a DMR text to a DapDataset,
so provide this utility.

@param document the dmr to parse
@return the parsed dmr
@throws DapException on parse errors

It is common to want to parse a DMR text to a DapDataset so provide this utility .


protected void
    processAttributes(DapDataset dataset)
            throws DapException
    {
        List<DapNode> nodes = dataset.getNodeList();
        for(DapNode node : nodes) {
            switch (node.getSort()) {
            case GROUP:
            case DATASET:
            case VARIABLE:
                Map<String, DapAttribute> attrs = node.getAttributes();
                if(attrs.size() > 0) {
                    List<DapAttribute> suppressed = new ArrayList<>();
                    for(DapAttribute dattr : attrs.values()) {
                        if(suppress(dattr.getShortName()))
                            suppressed.add(dattr);
                    }
                    for(DapAttribute dattr : suppressed) {
                        node.removeAttribute(dattr);
                    }
                }
                break;
            default:
                break; /*ignore*/
            }
        }
        // Try to extract the byte order
        getEndianAttribute(dataset);
    }

protected
void
processAttributes
(
DapDataset
dataset
)
throws
DapException
{
List
<
DapNode
>
nodes
=
dataset
.
getNodeList
(
)
;
for
(
DapNode
node
:
nodes
)
{
switch
(
node
.
getSort
(
)
)
{
case
GROUP
:
case
DATASET
:
case
VARIABLE
:
Map
<
String
,
DapAttribute
>
attrs
=
node
.
getAttributes
(
)
;
if
(
attrs
.
size
(
)
>
0
)
{
List
<
DapAttribute
>
suppressed
=
new
ArrayList
<>
(
)
;
for
(
DapAttribute
dattr
:
attrs
.
values
(
)
)
{
if
(
suppress
(
dattr
.
getShortName
(
)
)
)
suppressed
.
add
(
dattr
)
;
}
for
(
DapAttribute
dattr
:
suppressed
)
{
node
.
removeAttribute
(
dattr
)
;
}
}
break
;
default
:
break
;
/*ignore*/
}
}
// Try to extract the byte order
getEndianAttribute
(
dataset
)
;
}

Walk the dataset tree and remove selected attributes
such as _Unsigned

@param dataset


public void addCoords(List<Coordinate> coords, PartitionCollectionMutable.Partition part) {
    Coordinate runtime = null;
    for (Coordinate coord : coords) {
      switch (coord.getType()) {
        case runtime:
          CoordinateRuntime rtime = (CoordinateRuntime) coord;
          if (runtimeBuilder == null) runtimeBuilder = new CoordinateRuntime.Builder2(rtime.getTimeUnits());
          runtimeBuilder.addAll(coord);
          runtime = coord;
          if (debugPartitionErrors && !duplicateRuntimeMessage && part != null)
            testDuplicateRuntime(rtime, part);
          break;

        case time:
          CoordinateTime time = (CoordinateTime) coord;
          if (timeBuilder == null) timeBuilder = new CoordinateTime.Builder2(coord.getCode(), time.getTimeUnit(), time.getRefDate());
          timeBuilder.addAll(coord);
          break;

        case timeIntv:
          CoordinateTimeIntv timeIntv = (CoordinateTimeIntv) coord;
          if (timeIntvBuilder == null) timeIntvBuilder = new CoordinateTimeIntv.Builder2(null, coord.getCode(), timeIntv.getTimeUnit(), timeIntv.getRefDate());
          timeIntvBuilder.addAll(intervalFilter((CoordinateTimeIntv)coord));
          break;

        case time2D:
          CoordinateTime2D time2D = (CoordinateTime2D) coord;
          if (time2DBuilder == null) time2DBuilder = new CoordinateTime2DUnionizer(time2D.isTimeInterval(), time2D.getTimeUnit(), coord.getCode(), false, logger);
          time2DBuilder.addAll(time2D);
          // debug
          CoordinateRuntime runtimeFrom2D = time2D.getRuntimeCoordinate();
          if (!runtimeFrom2D.equals(runtime))
            logger.warn("HEY CoordinateUnionizer runtimes not equal");
          break;

        case ens:
          if (ensBuilder == null) ensBuilder = new CoordinateEns.Builder2(coord.getCode());
          ensBuilder.addAll(coord);
          break;

        case vert:
          CoordinateVert vertCoord = (CoordinateVert) coord;
          if (vertBuilder == null) vertBuilder = new CoordinateVert.Builder2(coord.getCode(), vertCoord.getVertUnit());
          vertBuilder.addAll(coord);
          break;
      }
    }
  }

only one message per CoordinatePartitionUnionizer instance


public void setGaussianLats(int nparallels, float la1, float la2) {
    log.debug ("la1 {}, la2 {}", la1, la2);
    if (this.gaussLats != null) throw new RuntimeException("Cant modify GdsHorizCoordSys");

    int nlats = (2 * nparallels);
    GaussianLatitudes gaussLats = GaussianLatitudes.factory(nlats);

    int bestStartIndex = 0, bestEndIndex = 0;
    double bestStartDiff = Double.MAX_VALUE;
    double bestEndDiff   = Double.MAX_VALUE;
    for (int i = 0; i < nlats; i++) {
      double diff = Math.abs(gaussLats.latd[i] - la1);
      if (diff < bestStartDiff) {
        bestStartDiff = diff;
        bestStartIndex = i;
      }
      diff = Math.abs(gaussLats.latd[i] - la2);
      if (diff < bestEndDiff) {
        bestEndDiff = diff;
        bestEndIndex = i;
      }
    }

    log.debug ("first pass: bestStartIndex {}, bestEndIndex {}", bestStartIndex, bestEndIndex);

    if (Math.abs(bestEndIndex - bestStartIndex) + 1 != nyRaw) {
      log.warn("GRIB gaussian lats: NP != NY, use NY");  // see email from Toussaint@dkrz.de datafil:
      nlats = nyRaw;
      gaussLats = GaussianLatitudes.factory(nlats);
      bestStartIndex = 0;
      bestEndIndex = nyRaw - 1;
    }
    boolean goesUp = bestEndIndex > bestStartIndex;

    log.debug ("bestStartIndex {}, bestEndIndex {}, goesUp {}", bestStartIndex, bestEndIndex, goesUp);

    // create the data
    int useIndex = bestStartIndex;
    float[] data = new float[nyRaw];
    float[] gaussw = new float[nyRaw];
    for (int i = 0; i < nyRaw; i++) {
      data[i] = (float) gaussLats.latd[useIndex];
      gaussw[i] = (float) gaussLats.gaussw[useIndex];

        log.trace ("i {}, useIndex {}, data {}, gaussw {}", i, useIndex, data[i], gaussw[i]);
      if (goesUp) {
        useIndex++;
      } else {
        useIndex--;
      }
    }

    this.gaussLats = Array.factory(DataType.FLOAT, new int[]{nyRaw}, data);
    this.gaussw    = Array.factory(DataType.FLOAT, new int[]{nyRaw}, gaussw);
  }


private static ParsedSectionSpec parseVariableSelector(Object parent, String selector) throws InvalidRangeException {
    String varNameEsc, indexSelect = null;

    int pos1 = EscapeStrings.indexOf(selector, '(');
    if (pos1 < 0) { // no index
      varNameEsc = selector;
    } else {
      varNameEsc = selector.substring(0, pos1);
      int pos2 = selector.indexOf(')', pos1+1);
      indexSelect = selector.substring(pos1, pos2);
    }
    if (debugSelector)
      System.out.println(" parseVariableSection <" + selector + "> = <" + varNameEsc + ">, <" + indexSelect + ">");

    Variable v = null;
    if (parent instanceof NetcdfFile) { // then varNameEsc = varFullNameEsc (i.e. includes groups)
      NetcdfFile ncfile = (NetcdfFile) parent;
      v = ncfile.findVariable(varNameEsc);

    } else if (parent instanceof Structure) { // then varNameEsc = memberNameEsc (i.e. includes groups)
      Structure s = (Structure) parent;
      v = s.findVariable( NetcdfFile.makeNameUnescaped(varNameEsc) ); // s.findVariable wants unescaped version
    }
    if (v == null)
      throw new IllegalArgumentException(" cant find variable: " + varNameEsc + " in selector=" + selector);

    if (v.getDataType() == DataType.SEQUENCE)
      indexSelect = null; // ignore whatever was sent

    // get the selected Ranges, or all, and add to the list
    Section section;
    if (indexSelect != null) {
      section = new Section(indexSelect);
      section = Section.fill(section, v.getShape()); // Check section has no nulls, set from shape array.
    } else {
      section = v.getShapeAsSection(); // all
    }

    return new ParsedSectionSpec(v, section);
  }

parse variable name and index selector out of the selector String. variable name must be escaped

parse variable name and index selector out of the selector String . variable name must be escaped


public static String makeSectionSpecString(Variable v, List<Range> ranges) throws InvalidRangeException {
    StringBuilder sb = new StringBuilder();
    makeSpec(sb, v, ranges);
    return sb.toString();
  }

public static String makeSectionSpecString ( Variable v , List < Range > ranges ) throws InvalidRangeException { StringBuilder sb = new StringBuilder ( ) ; makeSpec ( sb , v , ranges ) ; return sb . toString ( ) ; }

Make section specification String from a range list for a Variable.
@param v for this Variable.
@param ranges list of Range. Must includes all parent structures. The list be null, meaning use all.
Individual ranges may be null, meaning all for that dimension.
@return section specification String.
@throws InvalidRangeException is specified section doesnt match variable shape

Make section specification String from a range list for a Variable .


private boolean box9(double wantLat, double wantLon, int[] rectIndex) {
    int row = rectIndex[0];
    int minrow = Math.max(row-1, 0);
    int maxrow = Math.min(row+1, nrows);

    int col = rectIndex[1];
    int mincol= Math.max(col-1, 0);
    int maxcol = Math.min(col+1, ncols);

    if (debug) System.out.printf("%n   box9:");
    for (int i=minrow; i<=maxrow; i++)
      for (int j=mincol; j<=maxcol; j++) {
        rectIndex[0] = i;
        rectIndex[1] = j;
        if (contains(wantLat, wantLon,  rectIndex)) return true;
      }

    return false;
  }

private
boolean
box9
(
double
wantLat
,
double
wantLon
,
int
[
]
rectIndex
)
{
int
row
=
rectIndex
[
0
]
;
int
minrow
=
Math
.
max
(
row
1
,
0
)
;
int
maxrow
=
Math
.
min
(
row
+
1
,
nrows
)
;
int
col
=
rectIndex
[
1
]
;
int
mincol
=
Math
.
max
(
col
1
,
0
)
;
int
maxcol
=
Math
.
min
(
col
+
1
,
ncols
)
;
if
(
debug
)
System
.
out
.
printf
(
"%n   box9:"
)
;
for
(
int
i
=
minrow
;
i
<=
maxrow
;
i
++
)
for
(
int
j
=
mincol
;
j
<=
maxcol
;
j
++
)
{
rectIndex
[
0
]
=
i
;
rectIndex
[
1
]
=
j
;
if
(
contains
(
wantLat
,
wantLon
,
rectIndex
)
)
return
true
;
}
return
false
;
}

we think its got to be in one of the 9 boxes around rectIndex


public synchronized void
    close()
    {
        if(closed)
            return; // recursive calls ok
        closed = true; // mark as closed to prevent recursive calls
        if(methodstream != null) {
            try {
                this.methodstream.close(); // May recursr
            } catch (IOException ioe) {/*failure is ok*/}
            this.methodstream = null;
        }
        // Force release underlying connection back to the connection manager
        if(this.lastresponse != null) {
            if(false) {
                try {
                    try {
                        // Attempt to keep connection alive by consuming its remaining content
                        EntityUtils.consume(this.lastresponse.getEntity());
                    } finally {
                        HttpClientUtils.closeQuietly(this.lastresponse); // Paranoia
                    }
                } catch (IOException ignore) {/*ignore*/}
            } else
                HttpClientUtils.closeQuietly(this.lastresponse);
            this.lastresponse = null;
        }
        if(session != null) {
            session.removeMethod(this);
            if(localsession) {
                session.close();
                session = null;
            }
        }
        this.lastrequest = null;
    }

Calling close will force the method to close, and will
force any open stream to terminate. If the session is local,
Then that too will be closed.

Calling close will force the method to close and will force any open stream to terminate . If the session is local Then that too will be closed .


public int execute()
            throws HTTPException
    {
        HttpResponse res = executeRaw();
        if(res != null)
            return res.getStatusLine().getStatusCode();
        else
            throw new HTTPException("HTTPMethod.execute: null response");
    }

public
int
execute
(
)
throws
HTTPException
{
HttpResponse
res
=
executeRaw
(
)
;
if
(
res
!=
)
return
res
.
getStatusLine
(
)
.
getStatusCode
(
)
;
else
throw
new
HTTPException
(
"HTTPMethod.execute: null response"
)
;
}

Create a request, add headers, and content,
then send to HTTPSession to do the bulk of the work.

@return statuscode

Create a request add headers and content then send to HTTPSession to do the bulk of the work .


protected boolean identifyEncodingStation(NetcdfDataset ds, EncodingInfo info, CF.FeatureType ftype, Formatter errlog) {
    // find the obs dimension
    Dimension obsDim = null;
    if (info.time.getRank() > 0)
      obsDim = info.time.getDimension(info.time.getRank() - 1); // may be time(time) or time(stn, obs)
    else if (info.time.getParentStructure() != null) {
      Structure parent = info.time.getParentStructure(); // if time axis is a structure member, try pulling dimension out of parent structure
      obsDim = parent.getDimension(parent.getRank() - 1);
    }
    if (obsDim == null) {
      errlog.format("CFpointObs: must have a non-scalar Time coordinate%n");
      return false;
    }

    // find the station dimension
    if (info.lat.getRank() == 0) {// scalar means single
      info.set(Encoding.single, null, obsDim);
      return true;
    }

    Dimension stnDim = info.lat.getDimension(0);
    if (obsDim == stnDim) {
      info.set(Encoding.flat, null, obsDim); // not used ?
      return true;
    }

    // the raggeds
    if (identifyRaggeds(ds, info, stnDim, obsDim, errlog))
      return true;

    // heres whats left
    if (info.lat.getRank() == 1) {
      //Encoding e = (info.time.getParentStructure() != null) ? Encoding.multiStructure : Encoding.multidim;
      info.set(Encoding.multidim, stnDim, obsDim);
      return true;
    }

    errlog.format("CFpointObs: %s Must have Lat/Lon coordinates of rank 0 or 1%n", ftype);
    return false;
  }


protected String matchAxisTypeAndDimension(NetcdfDataset ds, AxisType type, final Dimension outer) {
    Variable var = CoordSysEvaluator.findCoordByType(ds, type, new CoordSysEvaluator.Predicate() {
      public boolean match(CoordinateAxis axis) {
        if ((outer == null) && (axis.getRank() == 0))
          return true;
        if ((outer != null) && (axis.getRank() == 1) && (outer.equals(axis.getDimension(0))))
          return true;

        // if axis is structure member, try pulling dimension out of parent structure
        if (axis.getParentStructure() != null) {
          Structure parent = axis.getParentStructure();
          if ((outer != null) && (parent.getRank() == 1) && (outer.equals(parent.getDimension(0))))
            return true;
        }
        return false;
      }
    });
    if (var == null) return null;
    return var.getFullName();
  }

class, I don't understand enough of the code base to anticipate implementation artifacts.

class I don t understand enough of the code base to anticipate implementation artifacts .


public void setStream(InputStream zStream) {
    last=0;
    origPtr=0;
    blockSize100k=0;
    blockRandomised=false;
    bsBuff=0;
    bsLive=0;
    mCrc = new CRC();
    nInUse=0;
    bsStream=null;
    streamEnd = false;
    currentChar = -1;
    currentState = START_BLOCK_STATE;
    storedBlockCRC= storedCombinedCRC=0;
    computedBlockCRC= computedCombinedCRC=0;
    i2= count= chPrev= ch2=0;
    i= tPos=0;
    rNToGo = 0;
    rTPos = 0;
    j2=0;
    z=0;

    bsSetStream(zStream);
    initialize();
    if (!streamEnd) { // Handle if initialize does not detect valid bz2 stream
      initBlock();
      setupBlock();
    }
  }

Added 5-30-2006 to allow for resetting of the input used
by this object. This saves in memory allocation costs

Added 5 - 30 - 2006 to allow for resetting of the input used by this object . This saves in memory allocation costs


public int read() {
    if (streamEnd) {
      return -1;
    } else {
      int retChar = currentChar;
      switch (currentState) {
        case START_BLOCK_STATE:
          break;
        case RAND_PART_A_STATE:
          break;
        case RAND_PART_B_STATE:
          setupRandPartB();
          break;
        case RAND_PART_C_STATE:
          setupRandPartC();
          break;
        case NO_RAND_PART_A_STATE:
          break;
        case NO_RAND_PART_B_STATE:
          setupNoRandPartB();
          break;
        case NO_RAND_PART_C_STATE:
          setupNoRandPartC();
          break;
        default:
          break;
      }
      return retChar;
    }
  }

public int read ( ) { if ( streamEnd ) { return - 1 ; } else { int retChar = currentChar ; switch ( currentState ) { case START_BLOCK_STATE : break ; case RAND_PART_A_STATE : break ; case RAND_PART_B_STATE : setupRandPartB ( ) ; break ; case RAND_PART_C_STATE : setupRandPartC ( ) ; break ; case NO_RAND_PART_A_STATE : break ; case NO_RAND_PART_B_STATE : setupNoRandPartB ( ) ; break ; case NO_RAND_PART_C_STATE : setupNoRandPartC ( ) ; break ; default : break ; } return retChar ; } }

Reads the stream.
@throws BZip2ReadException if there is a problem.  InputStream does
not throw an IOException, so we throw a RuntimeException.


public void start(java.awt.Component top, String taskName, int progressMaxCount) {
    // create ProgressMonitor
    pm = new javax.swing.ProgressMonitor(top, taskName, "", 0, progressMaxCount);
    pm.setMillisToDecideToPopup(millisToDecideToPopup);
    pm.setMillisToPopup(millisToPopup);

    // do task in a seperate, non-event, thread
    taskThread = new Thread(task);
    taskThread.start();

    // create timer, whose events happen on the awt event Thread
    ActionListener watcher = new ActionListener() {
      public void actionPerformed(ActionEvent evt) {
        secs++;
        if (pm.isCanceled()) {
          task.cancel();
        } else {
          // indicate progress
          String note = task.getNote();
          pm.setNote(note == null ? secs + " secs" : note);
          int progress = task.getProgress();
          pm.setProgress(progress <= 0 ? secs : progress);
        }

        // need to make sure task acknowledges the cancel; so dont shut down
        // until the task is done
        if (task.isDone()) {
          timer.stop();
          pm.close();
          // Toolkit.getDefaultToolkit().beep();

          if (task.isError()) {
            javax.swing.JOptionPane.showMessageDialog(null, task.getErrorMessage());
          }

          if (task.isSuccess())
            fireEvent(new ActionEvent(this, 0, "success"));
          else if (task.isError())
            fireEvent(new ActionEvent(this, 0, "error"));
          else if (task.isCancel())
            fireEvent(new ActionEvent(this, 0, "cancel"));
          else
            fireEvent(new ActionEvent(this, 0, "done"));
        }
      }
    };

    timer = new javax.swing.Timer(1000, watcher); // every second
    timer.start();
  }

Call this from awt event thread.
The task is run in a background thread.

@param top              put ProgressMonitor on top of this component (may be null)
@param taskName         display name of task
@param progressMaxCount maximum number of Progress indicator

Call this from awt event thread . The task is run in a background thread .


static public URI
    parseToURI(final String u)
            throws URISyntaxException
    {
        StringBuilder buf = new StringBuilder();
        int i = 0;
        while(i < u.length()) {
            char c = u.charAt(i);
            if(c == '\\') {
                if(i + 1 == u.length())
                    throw new URISyntaxException(u, "Trailing '\' at end of url");
                buf.append("%5c");
                i++;
                c = u.charAt(i);
                buf.append(String.format("%%%02x", (int) c));
            } else
                buf.append(c);
            i++;
        }
        return new URI(buf.toString());
    }

static
public
URI
parseToURI
(
final
String
u
)
throws
URISyntaxException
{
StringBuilder
buf
=
new
StringBuilder
(
)
;
int
i
=
0
;
while
(
i
<
u
.
length
(
)
)
{
char
c
=
u
.
charAt
(
i
)
;
if
(
c
==
'
'
)
{
if
(
i
+
1
==
u
.
length
(
)
)
throw
new
URISyntaxException
(
u
,
"Trailing '\' at end of url"
)
;
buf
.
append
(
"%5c"
)
;
i
++
;
c
=
u
.
charAt
(
i
)
;
buf
.
append
(
String
.
format
(
"%%%02x"
,
(
int
)
c
)
)
;
}
else
buf
.
append
(
c
)
;
i
++
;
}
return
new
URI
(
buf
.
toString
(
)
)
;
}

Convert a uri string to an instance of java.net.URI.
The critical thing is that this procedure can handle backslash
escaped uris as well as %xx escaped uris.

@param u the uri to convert
@return The URI corresponding to u.
@throws URISyntaxException

Convert a uri string to an instance of java . net . URI . The critical thing is that this procedure can handle backslash escaped uris as well as %xx escaped uris .


static URI
    uriExclude(final URI uri, URIPart... excludes)
    {
        URIBuilder urib = new URIBuilder();
        EnumSet<URIPart> set = EnumSet.of(excludes[0], excludes);
        for(URIPart part : URIPart.values()) {
            if(set.contains(part)) continue;
            switch (part) {
            case SCHEME:
                urib.setScheme(uri.getScheme());
                break;
            case USERINFO:
                urib.setUserInfo(uri.getUserInfo());
                break;
            case HOST:
                urib.setHost(uri.getHost());
                break;
            case PORT:
                urib.setPort(uri.getPort());
                break;
            case PATH:
                urib.setPath(uri.getPath());
                break;
            case QUERY:
                urib.setCustomQuery(uri.getQuery());
                break;
            case FRAGMENT:
                urib.setFragment(uri.getFragment());
                break;
            }
        }
        try {
            return urib.build();
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException(e.getMessage());
        }
    }

static URI uriExclude ( final URI uri , URIPart ... excludes ) { URIBuilder urib = new URIBuilder ( ) ; EnumSet < URIPart > set = EnumSet . of ( excludes [ 0 ] , excludes ) ; for ( URIPart part : URIPart . values ( ) ) { if ( set . contains ( part ) ) continue ; switch ( part ) { case SCHEME : urib . setScheme ( uri . getScheme ( ) ) ; break ; case USERINFO : urib . setUserInfo ( uri . getUserInfo ( ) ) ; break ; case HOST : urib . setHost ( uri . getHost ( ) ) ; break ; case PORT : urib . setPort ( uri . getPort ( ) ) ; break ; case PATH : urib . setPath ( uri . getPath ( ) ) ; break ; case QUERY : urib . setCustomQuery ( uri . getQuery ( ) ) ; break ; case FRAGMENT : urib . setFragment ( uri . getFragment ( ) ) ; break ; } } try { return urib . build ( ) ; } catch ( URISyntaxException e ) { throw new IllegalArgumentException ( e . getMessage ( ) ) ; } }

Remove selected fields from a  URI producing a new URI

@param uri      the uri to convert
@param excludes the parts to exclude
@return The new URI instance


static public String nullify(String s)
    {
        if(s != null && s.length() == 0) s = null;
        return s;
    }

static public String nullify ( String s ) { if ( s != null && s . length ( ) == 0 ) s = null ; return s ; }

Convert a zero-length string to null

@param s the string to check for length
@return null if s.length() == 0, s otherwise


static public String
    canonjoin(String prefix, String suffix)
    {
        if(prefix == null) prefix = "";
        if(suffix == null) suffix = "";
        prefix = HTTPUtil.canonicalpath(prefix);
        suffix = HTTPUtil.canonicalpath(suffix);
        StringBuilder result = new StringBuilder();
        result.append(prefix);
        int prelen = prefix.length();
        if(prelen > 0 && result.charAt(prelen - 1) != '/') {
            result.append('/');
            prelen++;
        }
        if(suffix.length() > 0 && suffix.charAt(0) == '/')
            result.append(suffix.substring(1));
        else
            result.append(suffix);
        int len = result.length();
        if(len > 0 && result.charAt(len - 1) == '/') {
            result.deleteCharAt(len - 1);
            len--;
        }
        return result.toString();
    }

static public String canonjoin ( String prefix , String suffix ) { if ( prefix == null ) prefix = "" ; if ( suffix == null ) suffix = "" ; prefix = HTTPUtil . canonicalpath ( prefix ) ; suffix = HTTPUtil . canonicalpath ( suffix ) ; StringBuilder result = new StringBuilder ( ) ; result . append ( prefix ) ; int prelen = prefix . length ( ) ; if ( prelen > 0 && result . charAt ( prelen - 1 ) != ' ' ) { result . append ( ' ' ) ; prelen ++ ; } if ( suffix . length ( ) > 0 && suffix . charAt ( 0 ) == ' ' ) result . append ( suffix . substring ( 1 ) ) ; else result . append ( suffix ) ; int len = result . length ( ) ; if ( len > 0 && result . charAt ( len - 1 ) == ' ' ) { result . deleteCharAt ( len - 1 ) ; len -- ; } return result . toString ( ) ; }

Join two string together to form proper path
WITHOUT trailing slash

@param prefix
@param suffix
@return


public boolean accept( CrawlableDataset dataset)
  {
    Date lastModDate = dataset.lastModified();
    if ( lastModDate != null )
    {
      long now = System.currentTimeMillis();
      if ( now - lastModDate.getTime() > lastModifiedLimitInMillis )
        return true;
    }
    return false;
  }

public boolean accept ( CrawlableDataset dataset ) { Date lastModDate = dataset . lastModified ( ) ; if ( lastModDate != null ) { long now = System . currentTimeMillis ( ) ; if ( now - lastModDate . getTime ( ) > lastModifiedLimitInMillis ) return true ; } return false ; }

Accept datasets whose last modified date is at least the
last modified limit of milliseconds in the past.

@param dataset the dataset to filter
@return true if the datasets last modified date is at least lastModifiedLimitInMillis in the past.

Accept datasets whose last modified date is at least the last modified limit of milliseconds in the past .


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;
  }

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 List<String> getFilenames() {
    List<String> result = new ArrayList<>();
    for (MFile file : fileMap.values())
      result.add(file.getPath());
    Collections.sort(result);
    return result;
  }

public List < String > getFilenames ( ) { List < String > result = new ArrayList <> ( ) ; for ( MFile file : fileMap . values ( ) ) result . ( file . getPath ( ) ) ; Collections . sort ( result ) ; return result ; }

The files that comprise the collection.
Actual paths, including the grib cache if used.

@return list of filename.

The files that comprise the collection . Actual paths including the grib cache if used .


void setIndexRaf(RandomAccessFile indexRaf) {
    this.indexRaf = indexRaf;
    if (indexRaf != null) {
      this.indexFilename = indexRaf.getLocation();
    }
  }

void setIndexRaf ( RandomAccessFile indexRaf ) { this . indexRaf = indexRaf ; if ( indexRaf != null ) { this . indexFilename = indexRaf . getLocation ( ) ; } }

public by accident, do not use

@param indexRaf the open raf of the index file


private String getIndexFilepathInCache() {
    File indexFile = GribCdmIndex.makeIndexFile(name, directory);
    return GribIndexCache.getFileOrCache(indexFile.getPath()).getPath();
  }

private String getIndexFilepathInCache ( ) { File indexFile = GribCdmIndex . makeIndexFile ( name , directory ) ; return GribIndexCache . getFileOrCache ( indexFile . getPath ( ) ) . getPath ( ) ; }

get index filename

@return index filename; may not exist; may be in disk cache


public void close() throws java.io.IOException {

    if (indexRaf != null) {
      indexRaf.close();
      indexRaf = null;
    }
  }

public void close ( ) throws java . io . IOException { if ( indexRaf != null ) { indexRaf . close ( ) ; indexRaf = null ; } }


public int ncounters() {
    if (nested == null)
      return 1;
    else {
      int ncounters = 0;
      for (BitCounterCompressed[] counters : nested) {
        if (counters == null) continue;
        for (BitCounterCompressed counter : counters)
          if (counter != null) ncounters += counter.ncounters();
      }
      return ncounters;
    }
  }

public int ncounters ( ) { if ( nested == null ) return 1 ; else { int ncounters = 0 ; for ( BitCounterCompressed [ ] counters : nested ) { if ( counters == null ) continue ; for ( BitCounterCompressed counter : counters ) if ( counter != null ) ncounters += counter . ncounters ( ) ; } return ncounters ; } }

Number of nested fields
@return 1 if no nested fields, otherwise count of nested fields


public Attribute addGlobalAttribute(String name, String value) {
    return addGlobalAttribute( new Attribute(name, value));
  }

public Attribute addGlobalAttribute ( String name , String value ) { return addGlobalAttribute ( new Attribute ( name , value ) ) ; }

Add a Global attribute of type String to the file. Must be in define mode.

@param name  name of attribute.
@param value value of atribute.
@return the created attribute

Add a Global attribute of type String to the file . Must be in define mode .


public Attribute addGlobalAttribute(String name, Array values) {
    return addGlobalAttribute( new Attribute(name, values));
  }

public Attribute addGlobalAttribute ( String name , Array values ) { return addGlobalAttribute ( new Attribute ( name , values ) ) ; }

Add a Global attribute of type Array to the file. Must be in define mode.

@param name   name of attribute.
@param values Array of values
@return the created attribute

Add a Global attribute of type Array to the file . Must be in define mode .


public Attribute deleteGlobalAttribute(String attName) {
    if (!defineMode) throw new UnsupportedOperationException("not in define mode");
    Attribute att = findGlobalAttribute(attName);
    if (null == att) return null;

    rootGroup.remove(att);
    return att;
  }

public
Attribute
deleteGlobalAttribute
(
String
attName
)
{
if
(
!
defineMode
)
throw
new
UnsupportedOperationException
(
"not in define mode"
)
;
Attribute
att
=
findGlobalAttribute
(
attName
)
;
if
(
==
att
)
return
;
rootGroup
.
remove
(
att
)
;
return
att
;
}

Delete a global Attribute. Must be in define mode.
@param attName existing Attribute has this name
@return deleted Attribute, or null if not found

Delete a global Attribute . Must be in define mode .


public Attribute renameGlobalAttribute(String oldName, String newName) {
    if (!defineMode) throw new UnsupportedOperationException("not in define mode");
    Attribute att = findGlobalAttribute(oldName);
    if (null == att) return null;

    rootGroup.remove(att);
    att = new Attribute( newName, att.getValues());
    rootGroup.addAttribute( att);
    return att;
  }

public
Attribute
renameGlobalAttribute
(
String
oldName
,
String
newName
)
{
if
(
!
defineMode
)
throw
new
UnsupportedOperationException
(
"not in define mode"
)
;
Attribute
att
=
findGlobalAttribute
(
oldName
)
;
if
(
==
att
)
return
;
rootGroup
.
remove
(
att
)
;
att
=
new
Attribute
(
newName
,
att
.
getValues
(
)
)
;
rootGroup
.
addAttribute
(
att
)
;
return
att
;
}

Rename a global Attribute. Must be in define mode.
@param oldName existing Attribute has this name
@param newName rename to this
@return renamed Attribute, or null if not found

Rename a global Attribute . Must be in define mode .


public void addVariableAttribute(String varName, String attName, String value) {
    addVariableAttribute(varName, new Attribute(attName, value));
  }

public void addVariableAttribute ( String varName , String attName , String value ) { addVariableAttribute ( varName , new Attribute ( attName , value ) ) ; }

Add an attribute of type String to the named Variable. Must be in define mode.

@param varName name of variable. must already have been added to the file.
@param attName name of attribute.
@param value   String value of atribute.

Add an attribute of type String to the named Variable . Must be in define mode .


public void addVariableAttribute(String varName, String attName, Array value) {
    Attribute att = new Attribute(attName, value);
    addVariableAttribute(varName, att);
  }

public void addVariableAttribute ( String varName , String attName , Array value ) { Attribute att = new Attribute ( attName , value ) ; addVariableAttribute ( varName , att ) ; }

Add an attribute of type Array to the named Variable. Must be in define mode.

@param varName name of variable. IllegalArgumentException if not valid name.
@param attName name of attribute.
@param value   Array of valkues

Add an attribute of type Array to the named Variable . Must be in define mode .


public Attribute deleteVariableAttribute(String varName, String attName) {
    if (!defineMode) throw new UnsupportedOperationException("not in define mode");
    Variable v = findVariable(varName);
    if (v == null) return null;

    Attribute att = v.findAttribute(attName);
    if (null == att) return null;

    v.remove(att);
    return att;
  }

public
Attribute
deleteVariableAttribute
(
String
varName
,
String
attName
)
{
if
(
!
defineMode
)
throw
new
UnsupportedOperationException
(
"not in define mode"
)
;
Variable
v
=
findVariable
(
varName
)
;
if
(
v
==
)
return
;
Attribute
att
=
v
.
findAttribute
(
attName
)
;
if
(
==
att
)
return
;
v
.
remove
(
att
)
;
return
att
;
}

Delete a variable Attribute. Must be in define mode.
@param varName existing Variable name
@param attName existing Attribute has this name
@return deleted Attribute, or null if not found

Delete a variable Attribute . Must be in define mode .


public Attribute renameVariableAttribute(String varName, String attName, String newName) {
    if (!defineMode) throw new UnsupportedOperationException("not in define mode");
    Variable v = findVariable(varName);
    if (v == null) return null;

    Attribute att = v.findAttribute(attName);
    if (null == att) return null;

    v.remove(att);
    att = new Attribute( newName, att.getValues());
    v.addAttribute( att);
    return att;
  }

public
Attribute
renameVariableAttribute
(
String
varName
,
String
attName
,
String
newName
)
{
if
(
!
defineMode
)
throw
new
UnsupportedOperationException
(
"not in define mode"
)
;
Variable
v
=
findVariable
(
varName
)
;
if
(
v
==
)
return
;
Attribute
att
=
v
.
findAttribute
(
attName
)
;
if
(
==
att
)
return
;
v
.
remove
(
att
)
;
att
=
new
Attribute
(
newName
,
att
.
getValues
(
)
)
;
v
.
addAttribute
(
att
)
;
return
att
;
}

Rename a variable Attribute. Must be in define mode.
@param varName existing Variable name
@param attName existing Attribute has this name
@param newName rename to this
@return renamed Attribute, or null if not found

Rename a variable Attribute . Must be in define mode .


protected void makeFileSubType() {
    // determine file type
    Key key = findKey(GempakStation.SLAT);
    if (key == null)
      throw new IllegalStateException("File does not have key="+GempakStation.SLAT);
    String latType = key.type;
    Key dateKey = findKey(DATE);
    if (dateKey != null && !dateKey.type.equals(latType)) {
      if (latType.equals(ROW)) {
        subType = CLIMATE;
      } else {
        subType = STANDARD;
      }
    } else {
      subType = SHIP;
    }
  }

protected
void
makeFileSubType
(
)
{
// determine file type
Key
key
=
findKey
(
GempakStation
.
SLAT
)
;
if
(
key
==
)
throw
new
IllegalStateException
(
"File does not have key="
+
GempakStation
.
SLAT
)
;
String
latType
=
key
.
type
;
Key
dateKey
=
findKey
(
DATE
)
;
if
(
dateKey
!=
&&
!
dateKey
.
type
.
equals
(
latType
)
)
{
if
(
latType
.
equals
(
ROW
)
)
{
subType
=
CLIMATE
;
}
else
{
subType
=
STANDARD
;
}
}
else
{
subType
=
SHIP
;
}
}


private void writeHeadersAndBB() {
		fileOutput += "<wfs:FeatureCollection xsi:schemaLocation=" + WFSXMLHelper.encQuotes("http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd " + namespace + " " + server
					+ "?request=DescribeFeatureType" + WFSXMLHelper.AMPERSAND + "service=wfs" + WFSXMLHelper.AMPERSAND + "version=2.0.0" + WFSXMLHelper.AMPERSAND + "typename="
					+ WFSController.TDSNAMESPACE + "%3A" + ftName)
				+ " xmlns:xsi=" + WFSXMLHelper.encQuotes("http://www.w3.org/2001/XMLSchema-instance")
				+ " xmlns:xlink=" + WFSXMLHelper.encQuotes("http://www.w3.org/1999/xlink")
				+ " xmlns:gml=" + WFSXMLHelper.encQuotes("http://opengis.net/gml/3.2")
				+ " xmlns:fes=" + WFSXMLHelper.encQuotes("http://www.opengis.net/fes/2.0")
				+ " xmlns:ogc=" + WFSXMLHelper.encQuotes("http://www.opengis.net/ogc")
				+ " xmlns:wfs=" + WFSXMLHelper.encQuotes("http://opengis.net/wfs/2.0")
				+ " xmlns:" + WFSController.TDSNAMESPACE +"=" + WFSXMLHelper.encQuotes(namespace)
				+ " xmlns=" + WFSXMLHelper.encQuotes("http://www.opengis.net/wfs/2.0")
				+ " version=\"2.0.0\" numberMatched=" + WFSXMLHelper.encQuotes(String.valueOf(geometries.size())) + " numberReturned="
				+ WFSXMLHelper.encQuotes(String.valueOf(geometries.size())) + ">";

			double[] boundLower;
			double[] boundUpper;
			if(geometries.isEmpty()) {
				boundLower = new double[2]; boundUpper = new double[2];
				boundLower[0] = -180;  boundLower[1] = -90;
				boundUpper[0] = 180; boundUpper[1] = 90;
			}

			else {
				boundLower = geometries.get(0).getBBLower();
				boundUpper = geometries.get(0).getBBUpper();
			}

		   // WFS Bounding Box
			for(SimpleGeometry item: geometries) {

				// Find the overall BB

				// Test Lower
				double[] low = item.getBBLower();
				if(boundLower[0] > low[0]) boundLower[0] = low[0];
				if(boundLower[1] > low[1]) boundLower[1] = low[1];

				// Test Upper
				double[] upper = item.getBBUpper();
				if(boundUpper[0] < upper[0]) boundUpper[0] = upper[0];
				if(boundUpper[1] < upper[1]) boundUpper[1] = upper[1];

				// Add some padding
				boundLower[0] -= 10;  boundLower[1] -= 10;
				boundUpper[0] += 10; boundUpper[1] += 10;
			}

		fileOutput	+= "<wfs:boundedBy>"
			+ "<wfs:Envelope srsName=" + "\"urn:ogc:def:crs:EPSG::4326\"" + ">"
					+ "<wfs:lowerCorner>" + boundLower[0] + " " + boundLower[1] + "</wfs:lowerCorner>"
					+ "<wfs:upperCorner>" + boundUpper[0] + " " + boundUpper[1] + "</wfs:upperCorner>"
			+ "</wfs:Envelope>"
			+ "</wfs:boundedBy>";
	}


public void writeMembers() {
		int index = 1;
		GMLFeatureWriter writer = new GMLFeatureWriter();
		for(SimpleGeometry geometryItem: geometries) {

			// Find bounding box information
			double[] lowerCorner = geometryItem.getBBLower();
			double[] upperCorner = geometryItem.getBBUpper();

			fileOutput
				   += "<wfs:member>"

					// Write Geometry Information
					+ "<" + WFSController.TDSNAMESPACE + ":" + ftName + " gml:id=\"" + ftName + "." + index + "\">"

					// GML Bounding Box
					+ "<gml:boundedBy>"
					+ "<gml:Envelope srsName=" + "\"urn:ogc:def:crs:EPSG::4326\"" + ">"
							+ "<gml:lowerCorner>" +  lowerCorner[0] + " " + lowerCorner[1] + "</gml:lowerCorner>"
							+ "<gml:upperCorner>" +  upperCorner[0] + " " + upperCorner[1] + "</gml:upperCorner>"
					+ "</gml:Envelope>"
					+ "</gml:boundedBy>"

					+ "<" + WFSController.TDSNAMESPACE + ":geometryInformation>";

			//write GML features
			fileOutput += writer.writeFeature(geometryItem);

			// Cap off headers
			fileOutput
					+="</" + WFSController.TDSNAMESPACE + ":geometryInformation>"
					+ "</" + WFSController.TDSNAMESPACE + ":" + ftName +">"
					+ "</wfs:member>";

			index++;
		}
	}

public
void
writeMembers
(
)
{
int
index
=
1
;
GMLFeatureWriter
writer
=
new
GMLFeatureWriter
(
)
;
for
(
SimpleGeometry
geometryItem
:
geometries
)
{
// Find bounding box information
double
[
]
lowerCorner
=
geometryItem
.
getBBLower
(
)
;
double
[
]
upperCorner
=
geometryItem
.
getBBUpper
(
)
;
fileOutput
+=
"<wfs:member>"
// Write Geometry Information
+
"<"
+
WFSController
.
TDSNAMESPACE
+
":"
+
ftName
+
" gml:id=\""
+
ftName
+
"."
+
index
+
"\">"
// GML Bounding Box
+
"<gml:boundedBy>"
+
"<gml:Envelope srsName="
+
"\"urn:ogc:def:crs:EPSG::4326\""
+
">"
+
"<gml:lowerCorner>"
+
lowerCorner
[
0
]
+
" "
+
lowerCorner
[
1
]
+
"</gml:lowerCorner>"
+
"<gml:upperCorner>"
+
upperCorner
[
0
]
+
" "
+
upperCorner
[
1
]
+
"</gml:upperCorner>"
+
"</gml:Envelope>"
+
"</gml:boundedBy>"
+
"<"
+
WFSController
.
TDSNAMESPACE
+
":geometryInformation>"
;
//write GML features
fileOutput
+=
writer
.
writeFeature
(
geometryItem
)
;
// Cap off headers
fileOutput
+=
"</"
+
WFSController
.
TDSNAMESPACE
+
":geometryInformation>"
+
"</"
+
WFSController
.
TDSNAMESPACE
+
":"
+
ftName
+
">"
+
"</wfs:member>"
;
index
++
;
}
}

In the WFS specification for GetFeature each feature type is its own
member and so writeMembers add each member to the fileOutput


public String writeStationObsDatasetXML() {
    XMLOutputter fmt = new XMLOutputter(Format.getPrettyFormat());
    return fmt.outputString(makeStationObsDatasetDocument());
  }

public String writeStationObsDatasetXML ( ) { XMLOutputter fmt = new XMLOutputter ( Format . getPrettyFormat ( ) ) ; return fmt . outputString ( makeStationObsDatasetDocument ( ) ) ; }


public String writeStationCollectionXML() throws IOException {
    XMLOutputter fmt = new XMLOutputter(Format.getPrettyFormat());
    return fmt.outputString(makeStationCollectionDocument());
  }

public String writeStationCollectionXML ( ) throws IOException { XMLOutputter fmt = new XMLOutputter ( Format . getPrettyFormat ( ) ) ; return fmt . outputString ( makeStationCollectionDocument ( ) ) ; }


public void
    writeDSR(String dsr)
            throws IOException
    {
        if(state != State.INITIAL)
            throw new DapException("Attempt to write DSR twice");

        if(dsr == null)
            throw new DapException("Attempt to write empty DSR");

        // Strip off any trailing sequence of CR or LF.
        int len = dsr.length();
        while(len > 0) {
            char c = dsr.charAt(len - 1);
            if(c != '\r' && c != '\n') break;
            len--;
        }
        if(dsr.length() == 0)
            throw new DapException("Attempt to write empty DSR");

        dsr = dsr.substring(0, len) + DapUtil.CRLF;

        // Add <?xml...?> prefix
        dsr = XMLDOCUMENTHEADER + "\n" + dsr;

        // Convert the dsr to UTF-8 and then to byte[]
        byte[] dsr8 = DapUtil.extract(DapUtil.UTF8.encode(dsr));
        sendDXR(dsr8);
        state = State.END;
    }

Write the DSR; do not bother to cache.

@param dsr The DSR string
@throws IOException on IO related errors

Write the DSR ; do not bother to cache .


void
    sendDXR(byte[] dxr8)
            throws IOException
    {
        if(dxr8 == null || dxr8.length == 0)
            return; // do nothing

        if(mode == RequestMode.DMR || mode == RequestMode.DSR) {
            state = State.END;
        } else {//mode == DATA
            // Prefix with chunk header
            int flags = DapUtil.CHUNK_DATA;
            if(this.writeorder == ByteOrder.LITTLE_ENDIAN)
                flags |= DapUtil.CHUNK_LITTLE_ENDIAN;
            chunkheader(dxr8.length, flags, this.header);
            // write the header
            output.write(DapUtil.extract(this.header));
            state = State.DATA;
        }
        // write the DXR
        output.write(dxr8);
        output.flush();
    }

void
sendDXR
(
byte
[
]
dxr8
)
throws
IOException
{
if
(
dxr8
==
||
dxr8
.
length
==
0
)
return
;
// do nothing
if
(
mode
==
RequestMode
.
DMR
||
mode
==
RequestMode
.
DSR
)
{
state
=
State
.
END
;
}
else
{
//mode == DATA
// Prefix with chunk header
int
flags
=
DapUtil
.
CHUNK_DATA
;
if
(
this
.
writeorder
==
ByteOrder
.
LITTLE_ENDIAN
)
flags
|=
DapUtil
.
CHUNK_LITTLE_ENDIAN
;
chunkheader
(
dxr8
.
length
,
flags
,
this
.
header
)
;
// write the header
output
.
write
(
DapUtil
.
extract
(
this
.
header
)
)
;
state
=
State
.
DATA
;
}
// write the DXR
output
.
write
(
dxr8
)
;
output
.
flush
(
)
;
}

Output the specifiedd DMR or DSR or..., but xml only.

@throws IOException on IO related errors

Output the specifiedd DMR or DSR or ... but xml only .


public void
    writeError(int httpcode,
               String msg,
               String cxt,
               String other)
            throws IOException
    {
        dmr8 = null;
        ErrorResponse response = new ErrorResponse(httpcode, msg, cxt, other);
        String errorbody = response.buildXML();
        // Convert the error body into utf8 then to byte[]
        byte[] errbody8 = DapUtil.extract(DapUtil.UTF8.encode(errorbody));
        if(mode == RequestMode.DMR) {
            sendDXR(errbody8);
        } else {//mode == DATA
            // clear any partial chunk
            chunk.clear();
            // create an error header
            int flags = DapUtil.CHUNK_ERROR | DapUtil.CHUNK_END;
            chunkheader(errbody8.length, flags, header);
            output.write(DapUtil.extract(header));
            output.write(errbody8);
            output.flush();
        }
        state = State.ERROR;
    }

Write an error chunk.
If mode == DMR then replaces the dmr
else reset the current chunk thus
losing any partial write.

@param httpcode The httpcode, 0 => ignore
@param msg      The <Error> <Message>, null => ignore
@param cxt      The <Error> <Context>, null => ignore
@param other    The <Error> <OtherInformation>, null => ignore
@throws IOException on IO related errors

Write an error chunk . If mode == DMR then replaces the dmr else reset the current chunk thus losing any partial write .


public void close()
            throws IOException
    {
        if(closed)
            return;
        closed = true;

        if(dmr8 != null) {
            sendDXR(dmr8);
            dmr8 = null;
        }

        if(mode == RequestMode.DMR)
            return; // only DMR should be sent

        // If there is no partial chunk to write then
        // we are done; else verify we can write and write the last
        // chunk; => multiple closes are ok.
        if(chunk == null || chunk.position() == 0)
            return;

        // There is data left to write.
        verifystate(); // are we in a state supporting data write?

        // Force out the current chunk (might be empty)
        // but do not close the underlying output stream
        state = State.DATA; // pretend

        int flags = DapUtil.CHUNK_END;
        writeChunk(flags);
        state = State.END;
        this.output.flush(); // Do not close
        if(this.saveoutput != null) {
            // write to true output target
            this.saveoutput.write(((ByteArrayOutputStream) this.output).toByteArray());
        }
    }

Closes this output stream and releases any system resources
associated with this stream. Except, the underlying stream is not
actually closed; that is left to the servlet level

@throws IOException on IO related errors

Closes this output stream and releases any system resources associated with this stream . Except the underlying stream is not actually closed ; that is left to the servlet level


@Override
    public void
    flush()
            throws IOException
    {
        if(mode == RequestMode.DMR)
            return; // leave to close() to do this
        if(dmr8 != null) {
            sendDXR(dmr8);
            dmr8 = null;
        }
    }

@
Override
public
void
flush
(
)
throws
IOException
{
if
(
mode
==
RequestMode
.
DMR
)
return
;
// leave to close() to do this
if
(
dmr8
!=
)
{
sendDXR
(
dmr8
)
;
dmr8
=
;
}
}

Overload flush to also write out the DMR

@throws IOException


@Override
  public ProjectionImpl constructCopy() {
    ProjectionImpl result = (saveParams == null) ? new UtmProjection(getZone(), isNorth()) : new UtmProjection(saveParams.a, saveParams.f, getZone(), isNorth());
    result.setDefaultMapArea(defaultMapArea);
    result.setName(name);
    return result;
  }

@ Override public ProjectionImpl constructCopy ( ) { ProjectionImpl result = ( saveParams == null ) ? new UtmProjection ( getZone ( ) , isNorth ( ) ) : new UtmProjection ( saveParams . a , saveParams . f , getZone ( ) , isNorth ( ) ) ; result . setDefaultMapArea ( defaultMapArea ) ; result . setName ( name ) ; return result ; }


public boolean contains(int want) {
    if (want < first())
      return false;
    if (want > last())
      return false;
    if (stride == 1) return true;
    return (want - first) % stride == 0;
  }

public boolean contains ( int want ) { if ( want < first ( ) ) return false ; if ( want > last ( ) ) return false ; if ( stride == 1 ) return true ; return ( want - first ) % stride == 0 ; }

Is want contained in this Range?

@param want index in the original Range
@return true if the ith element would be returned by the Range iterator


public int element(int i) throws InvalidRangeException {
    if (i < 0)
      throw new InvalidRangeException("i must be >= 0");
    if (i >= length)
      throw new InvalidRangeException("i must be < length");

    return first + i * stride;
  }

public
int
element
(
int
i
)
throws
InvalidRangeException
{
if
(
i
<
0
)
throw
new
InvalidRangeException
(
"i must be >= 0"
)
;
if
(
i
>=
length
)
throw
new
InvalidRangeException
(
"i must be < length"
)
;
return
first
+
i
*
stride
;
}

Get ith element

@param i index of the element
@return the i-th element of a range.
@throws InvalidRangeException i must be: 0 <= i < length


public Range shiftOrigin(int origin) throws InvalidRangeException {
    if (this == VLEN)
      return VLEN;

    int first = first() - origin;
    int last = last() - origin;
    return new Range(name, first, last, stride);
  }

public Range shiftOrigin ( int origin ) throws InvalidRangeException { if ( this == VLEN ) return VLEN ; int first = first ( ) - origin ; int last = last ( ) - origin ; return new Range ( name , first , last , stride ) ; }

Create a new Range shifting this range by a constant factor.

@param origin subtract this from each element
@return shifted range
@throws InvalidRangeException elements must be nonnegative, 0 <= first <= last

Create a new Range shifting this range by a constant factor .


protected Object readData( Layout index, DataType dataType) throws java.io.IOException {
   return IospHelper.readDataFill(raf, index, dataType, null, -1);
 }

protected Object readData ( Layout index , DataType dataType ) throws java . io . IOException { return IospHelper . readDataFill ( raf , index , dataType , null , - 1 ) ; }

Read data subset from file for a variable, create primitive array.
@param index handles skipping around in the file.
@param dataType dataType of the variable
@return primitive array with data read in

Read data subset from file for a variable create primitive array .


protected long readData( Layout index, DataType dataType, WritableByteChannel out) throws java.io.IOException {
    long count = 0;
    if (dataType.getPrimitiveClassType() == byte.class || dataType == DataType.CHAR) {
      while (index.hasNext()) {
        Layout.Chunk chunk = index.next();
        count += raf.readToByteChannel( out, chunk.getSrcPos(), chunk.getNelems());
      }

    } else if (dataType.getPrimitiveClassType() == short.class) {
      while (index.hasNext()) {
        Layout.Chunk chunk = index.next();
        count += raf.readToByteChannel( out, chunk.getSrcPos(), 2 * chunk.getNelems());
      }

    } else if (dataType.getPrimitiveClassType() == int.class || (dataType == DataType.FLOAT)) {
      while (index.hasNext()) {
        Layout.Chunk chunk = index.next();
        count += raf.readToByteChannel( out, chunk.getSrcPos(), 4 * chunk.getNelems());
      }

    } else if ((dataType == DataType.DOUBLE) || dataType.getPrimitiveClassType() == long.class) {
      while (index.hasNext()) {
        Layout.Chunk chunk = index.next();
        count += raf.readToByteChannel( out, chunk.getSrcPos(), 8 * chunk.getNelems());
      }
    }

    return count;
  }

protected long readData ( Layout index , DataType dataType , WritableByteChannel out ) throws java . io . IOException { long count = 0 ; if ( dataType . getPrimitiveClassType ( ) == byte . class || dataType == DataType . CHAR ) { while ( index . hasNext ( ) ) { Layout . Chunk chunk = index . next ( ) ; count += raf . readToByteChannel ( out , chunk . getSrcPos ( ) , chunk . getNelems ( ) ) ; } } else if ( dataType . getPrimitiveClassType ( ) == short . class ) { while ( index . hasNext ( ) ) { Layout . Chunk chunk = index . next ( ) ; count += raf . readToByteChannel ( out , chunk . getSrcPos ( ) , 2 * chunk . getNelems ( ) ) ; } } else if ( dataType . getPrimitiveClassType ( ) == int . class || ( dataType == DataType . FLOAT ) ) { while ( index . hasNext ( ) ) { Layout . Chunk chunk = index . next ( ) ; count += raf . readToByteChannel ( out , chunk . getSrcPos ( ) , 4 * chunk . getNelems ( ) ) ; } } else if ( ( dataType == DataType . DOUBLE ) || dataType . getPrimitiveClassType ( ) == long . class ) { while ( index . hasNext ( ) ) { Layout . Chunk chunk = index . next ( ) ; count += raf . readToByteChannel ( out , chunk . getSrcPos ( ) , 8 * chunk . getNelems ( ) ) ; } } return count ; }

Read data subset from file for a variable, to WritableByteChannel.
Will send as bigendian, since thats what the underlying file has.
@param index handles skipping around in the file.
@param dataType dataType of the variable

Read data subset from file for a variable to WritableByteChannel . Will send as bigendian since thats what the underlying file has .


public PrimitiveVector subset(int start, int stop, int stride) {
        BooleanPrimitiveVector n = new BooleanPrimitiveVector(getTemplate());
        stride = Math.max(stride, 1);
        stop = Math.max(start, stop);
        int length = 1 + (stop - start) / stride;
        n.setLength(length);

        int count = 0;
        for (int i = start; i <= stop; i += stride) {
            n.setValue(count, vals[i]);
            count++;
        }
        return n;
    }

public PrimitiveVector subset ( int start , int stop , int stride ) { BooleanPrimitiveVector n = new BooleanPrimitiveVector ( getTemplate ( ) ) ; stride = Math . max ( stride , 1 ) ; stop = Math . max ( start , stop ) ; int length = 1 + ( stop - start ) / stride ; n . setLength ( length ) ; int count = 0 ; for ( int i = start ; i <= stop ; i += stride ) { n . setValue ( count , vals [ i ] ) ; count ++ ; } return n ; }

Create a new primitive vector using a subset of the data.

@param start  starting index (i=start)
@param stop  ending index (i<=stop)
@param stride index stride (i+=stride)
@return new primitive vector, of type BooleanPrimitiveVector.

Create a new primitive vector using a subset of the data .


public Iterable<? extends CatalogExt> getCatalogs() {
    if (catalogs == null) readCatalogs();

    List<CatalogExt> result = new ArrayList<>();
    for (CatalogExt ext : catalogs.values())
      result.add(ext);
    Collections.sort(result, (o1, o2) -> o1.getCatRelLocation().compareTo(o2.getCatRelLocation()));    // java 8 lambda, baby
    return result;
  }

public
Iterable
<
extends
CatalogExt
>
getCatalogs
(
)
{
if
(
catalogs
==
)
readCatalogs
(
)
;
List
<
CatalogExt
>
result
=
new
ArrayList
<>
(
)
;
for
(
CatalogExt
ext
:
catalogs
.
values
(
)
)
result
.
(
ext
)
;
Collections
.
sort
(
result
,
(
o1
,
o2
)
->
o1
.
getCatRelLocation
(
)
.
compareTo
(
o2
.
getCatRelLocation
(
)
)
)
;
// java 8 lambda, baby
return
result
;
}


public static void cleanupBefore(String pathname, long trackerNumber) {
    for (long tnum = trackerNumber - 1; tnum > 0; tnum--) {
      File oldDatabaseFile = new File(pathname + datasetName + "." + tnum);
      if (!oldDatabaseFile.exists()) break;
      if (oldDatabaseFile.delete()) {
        catalogInitLog.info("DatasetTrackerChronicle deleted {} ", oldDatabaseFile.getAbsolutePath());
      } else {
        catalogInitLog.error("DatasetTrackerChronicle not able to delete {} ", oldDatabaseFile.getAbsolutePath());
      }
    }
  }


public void setValues(float[] values) {
        vals = values;
        proj = GempakUtil.ST_ITOC(Float.floatToIntBits(vals[1])).trim();
        addParam(PROJ, proj);
        addParam(GDS_KEY, this.toString());
        setParams();
    }

public void setValues ( float [ ] values ) { vals = values ; proj = GempakUtil . ST_ITOC ( Float . floatToIntBits ( vals [ 1 ] ) ) . trim ( ) ; addParam ( PROJ , proj ) ; addParam ( GDS_KEY , this . toString ( ) ) ; setParams ( ) ; }

Set the grid nav block values

@param values   the raw values


LayoutTiled.DataChunkIterator getDataChunkIteratorNoFilter(Section want, int nChunkDim) throws IOException {
    /*
    if (if (debugChunkOrder) ) {
    DataChunkIteratorNoFilter iter = new DataChunkIteratorNoFilter(null, nChunkDim);
    int count = 0;
    int last = -1;
    while (iter.hasNext()) {
      LayoutTiled.DataChunk chunk = iter.next();
      System.out.printf("%d : %d%n", count++, tiling.order(chunk.offset));
      if (tiling.order(chunk.offset) <= last)
        System.out.println("HEY");
      last = tiling.order(chunk.offset);
    }
    }*/

    return new DataChunkIteratorNoFilter(want, nChunkDim);
  }


private double[] makeDoubleArray(int[] ints) {
    double[] newArray = new double[ints.length];
    for (int i = 0; i < ints.length; i++) {
      newArray[i] = ints[i];
    }
    return newArray;
  }

private double [ ] makeDoubleArray ( int [ ] ints ) { double [ ] newArray = new double [ ints . length ] ; for ( int i = 0 ; i < ints . length ; i ++ ) { newArray [ i ] = ints [ i ] ; } return newArray ; }

make a double array out of an int array

@param ints array of ints
@return array of doubles


@RequestMapping("**/datasetBoundaries.xml")
  public void getDatasetBoundaries(NcssParamsBean params, HttpServletRequest req, HttpServletResponse res)
          throws IOException, UnsupportedResponseFormatException {
    SupportedFormat format = SupportedOperation.DATASET_BOUNDARIES_REQUEST.getSupportedFormat(params.getAccept());

    switch (format) {
      case WKT:  getDatasetBoundariesWKT(req, res);     break;
      case JSON: getDatasetBoundariesGeoJSON(req, res); break;
      default: throw new IllegalArgumentException(String.format(
              "Expected %s or %s, but got %s", SupportedFormat.WKT, SupportedFormat.JSON, format));
    }
  }

@
RequestMapping
(
"**/datasetBoundaries.xml"
)
public
void
getDatasetBoundaries
(
NcssParamsBean
params
,
HttpServletRequest
req
,
HttpServletResponse
res
)
throws
IOException
,
UnsupportedResponseFormatException
{
SupportedFormat
format
=
SupportedOperation
.
DATASET_BOUNDARIES_REQUEST
.
getSupportedFormat
(
params
.
getAccept
(
)
)
;
switch
(
format
)
{
case
WKT
:
getDatasetBoundariesWKT
(
req
,
res
)
;
break
;
case
JSON
:
getDatasetBoundariesGeoJSON
(
req
,
res
)
;
break
;
default
:
throw
new
IllegalArgumentException
(
String
.
format
(
"Expected %s or %s, but got %s"
,
SupportedFormat
.
WKT
,
SupportedFormat
.
JSON
,
format
)
)
;
}
}

Supported for backwards compatibility. We prefer that datasetBoundaries.wkt or datasetBoundaries.json are used.

Supported for backwards compatibility . We prefer that datasetBoundaries . wkt or datasetBoundaries . json are used .


private void checkRequestedVars(CoverageCollection gcd, NcssGridParamsBean params)
          throws VariableNotContainedInDatasetException {

    // if var == all --> all variables requested
    if (params.getVar().get(0).equalsIgnoreCase("all")) {
      params.setVar(getAllGridNames(gcd));
      return;
    }

    // Check vars are contained in the grid
    for (String gridName : params.getVar()) {
      Coverage grid = gcd.findCoverage(gridName);
      if (grid == null)
        throw new VariableNotContainedInDatasetException(
                "Variable: " + gridName + " is not contained in the requested dataset");
    }
  }

Checks that all the requested vars exist. If "all", fills out the param.vars with all grid names
Throws exception if some of the variables in the request are not contained in the dataset

Checks that all the requested vars exist . If all fills out the param . vars with all grid names Throws exception if some of the variables in the request are not contained in the dataset


public static DatasetSourceStructure getStructure( String name)
  {
    if ( name == null) return null;
    return (DatasetSourceStructure) hash.get( name);
  }

public static DatasetSourceStructure getStructure ( String name ) { if ( name == null ) return null ; return ( DatasetSourceStructure ) hash . get ( name ) ; }

Find the DatasetSourceStructure that matches this name.
@param name
@return DatasetSourceStructure or null if no match.

Find the DatasetSourceStructure that matches this name .


@Override
    public String
    getResourcePath(DapRequest drq, String location)
            throws DapException
    {
        String realpath;
        if(TdsRequestedDataset.getDatasetManager() != null) {
            realpath = TdsRequestedDataset.getLocationFromRequestPath(location);
        } else {
            assert TdsRequestedDataset.getDatasetManager() == null;
            String prefix = drq.getResourceRoot();
            assert (prefix != null);
            realpath = DapUtil.canonjoin(prefix, location);
        }

        if (!TESTING) {
            if (!TdsRequestedDataset.resourceControlOk(drq.getRequest(), drq.getResponse(), realpath))
                throw new DapException("Not authorized: " + location)
                        .setCode(DapCodes.SC_FORBIDDEN);
        }
        File f = new File(realpath);
        if (!f.exists() || !f.canRead())
            throw new DapException("Not found: " + location)
                    .setCode(DapCodes.SC_NOT_FOUND);
        //ncfile = TdsRequestedDataset.getNetcdfFile(this.request, this.response, path);
        return realpath;
    }


private void makeVerticalDimensions(List<GridVertCoord> vertCoordList, NetcdfFile ncfile, Group group) {
    // find biggest vert coord
    GridVertCoord gvcs0 = null;
    int maxLevels = 0;
    for (GridVertCoord gvcs : vertCoordList) {
      if (gvcs.getNLevels() > maxLevels) {
        gvcs0 = gvcs;
        maxLevels = gvcs.getNLevels();
      }
    }

    int seqno = 1;
    for (GridVertCoord gvcs : vertCoordList) {
      if (gvcs != gvcs0) {
        gvcs.setSequence(seqno++);
      }
      gvcs.addDimensionsToNetcdfFile(ncfile, group);
    }
  }

private
void
makeVerticalDimensions
(
List
<
GridVertCoord
>
vertCoordList
,
NetcdfFile
ncfile
,
Group
group
)
{
// find biggest vert coord
GridVertCoord
gvcs0
=
;
int
maxLevels
=
0
;
for
(
GridVertCoord
gvcs
:
vertCoordList
)
{
if
(
gvcs
.
getNLevels
(
)
>
maxLevels
)
{
gvcs0
=
gvcs
;
maxLevels
=
gvcs
.
getNLevels
(
)
;
}
}
int
seqno
=
1
;
for
(
GridVertCoord
gvcs
:
vertCoordList
)
{
if
(
gvcs
!=
gvcs0
)
{
gvcs
.
setSequence
(
seqno
++
)
;
}
gvcs
.
addDimensionsToNetcdfFile
(
ncfile
,
group
)
;
}
}

Make a vertical dimensions

@param vertCoordList vertCoords all with the same name
@param ncfile        netCDF file to add to
@param group         group in ncfile


boolean readPIB(RandomAccessFile raf) throws IOException {

    this.firstHeader = new AwxFileFirstHeader();

    int pos = 0;
    raf.seek(pos);

    // gini header process
    byte[] buf = new byte[FY_AWX_PIB_LEN];
    int count = raf.read(buf);
    EndianByteBuffer byteBuffer;
    if (count == FY_AWX_PIB_LEN) {
      byteBuffer = new EndianByteBuffer(buf, this.firstHeader.byteOrder);
      this.firstHeader.fillHeader(byteBuffer);

    } else {
      return false;
    }

    if (!((this.firstHeader.fileName.endsWith(".AWX") || this.firstHeader.fileName.endsWith(".awx"))
            && this.firstHeader.firstHeaderLength == FY_AWX_PIB_LEN)) {
      return false;
    }

    // skip the fills of the first record
    //  raf.seek(FY_AWX_PIB_LEN + this.firstHeader.fillSectionLength);
    buf = new byte[this.firstHeader.secondHeaderLength];
    raf.readFully(buf);
    byteBuffer = new EndianByteBuffer(buf, this.firstHeader.byteOrder);
    switch (this.firstHeader.typeOfProduct) {
      case AwxFileFirstHeader.AWX_PRODUCT_TYPE_UNDEFINED:
        throw new UnsupportedDatasetException();
      case AwxFileFirstHeader.AWX_PRODUCT_TYPE_GEOSAT_IMAGE:
        secondHeader = new AwxFileGeoSatelliteSecondHeader();
        secondHeader.fillHeader(byteBuffer);
        break;
      case AwxFileFirstHeader.AWX_PRODUCT_TYPE_POLARSAT_IMAGE:
        throw new UnsupportedDatasetException();

      case AwxFileFirstHeader.AWX_PRODUCT_TYPE_GRID:
        secondHeader = new AwxFileGridProductSecondHeader();
        secondHeader.fillHeader(byteBuffer);

        break;
      case AwxFileFirstHeader.AWX_PRODUCT_TYPE_DISCREET:
        throw new UnsupportedDatasetException();

      case AwxFileFirstHeader.AWX_PRODUCT_TYPE_GRAPH_ANALIYSIS:
        throw new UnsupportedDatasetException();

    }

    return true;

  }

Read the header and populate the ncfile

@param raf
@throws IOException


public boolean finish() {
    boolean ok = true;
    java.util.Iterator iter;
    logger.debug("Now finish " + getName() + " id= " + getID());

    authorityName = null;
    dataType = null;
    dataFormatType = null;
    defaultService = null;

    gc = null;
    tc = null;
    docs = new ArrayList<>();
    metadata = new ArrayList<>();
    properties = new ArrayList<>();

    creators = new ArrayList<>();
    contributors = new ArrayList<>();
    dates = new ArrayList<>();
    keywords = new ArrayList<>();
    projects = new ArrayList<>();
    publishers = new ArrayList<>();
    variables = new ArrayList<>();

    canonicalize(); // canonicalize thredds metadata
    transfer2PublicMetadata(tm, true); // add local metadata
    transfer2PublicMetadata(tmi, true); // add local inherited metadata
    transferInheritable2PublicMetadata((InvDatasetImpl) getParent()); // add inheritable metadata from parents

    // build the expanded access list
    access = new ArrayList<>();

    // add access element if urlPath is specified
    if ((urlPath != null) && (getServiceDefault() != null)) {
      InvAccessImpl a = new InvAccessImpl(this, urlPath, getServiceDefault());
      a.setSize(size);
      a.finish();
      addExpandedAccess(a);
    }

    // add local access elements
    iter = accessLocal.iterator();
    while (iter.hasNext()) {
      InvAccessImpl a = (InvAccessImpl) iter.next();
      a.finish();
      addExpandedAccess(a);
    }

    // recurse into child datasets.
    if (!(this instanceof InvCatalogRef)) {
      for (InvDataset invDataset : this.getDatasets()) {
        InvDatasetImpl curDs = (InvDatasetImpl) invDataset;
        ok &= curDs.finish();
      }
    }

    return ok;
  }

Finish constructing after all elements have been added.
This does the inheritence thing
This can be called again if new elements are added.

@return true if successful.

Finish constructing after all elements have been added . This does the inheritence thing This can be called again if new elements are added .


private void transferInheritable2PublicMetadata(InvDatasetImpl parent) {
    if (parent == null) return;
    logger.debug(" inheritFromParent= " + parent.getID());

    transfer2PublicMetadata(parent.getLocalMetadataInheritable(), true);
    //transfer2PublicMetadata(parent.getCat6Metadata(), true);

    /* look through local metadata, find inherited InvMetadata elements
    ThreddsMetadata tmd = parent.getLocalMetadata();
    Iterator iter = tmd.getMetadata().iterator();
    while (iter.hasNext()) {
      InvMetadata meta = (InvMetadata) iter.next();
      if (meta.isInherited()) {
        if (!meta.isThreddsMetadata()) {
          metadata.add(meta);
        } else {
          if (debugInherit) System.out.println("  inheritMetadata Element " + tmd.isInherited() + " " + meta.isInherited());
          meta.finish(); // make sure XLink is read in.
          transfer2PublicMetadata(meta.getThreddsMetadata(), false);
        }
      }
    } */

    // recurse
    transferInheritable2PublicMetadata((InvDatasetImpl) parent.getParent());
  }

Look for InvMetadata elements in the parent that need to be added to the public metadata of this dataset.
Recurse up through all ancestors.

@param parent transfer from here

Look for InvMetadata elements in the parent that need to be added to the public metadata of this dataset . Recurse up through all ancestors .


public void transferMetadata(InvDatasetImpl fromDs, boolean copyInheritedMetadataFromParents) {
    if (fromDs == null) return;
    logger.debug(" transferMetadata= " + fromDs.getName());

    if (this != fromDs)
      getLocalMetadata().add(fromDs.getLocalMetadata(), false);

    transferInheritableMetadata(fromDs, getLocalMetadataInheritable(), copyInheritedMetadataFromParents);

    setResourceControl(fromDs.getRestrictAccess());
  }

public
void
transferMetadata
(
InvDatasetImpl
fromDs
,
boolean
copyInheritedMetadataFromParents
)
{
if
(
fromDs
==
)
return
;
logger
.
debug
(
" transferMetadata= "
+
fromDs
.
getName
(
)
)
;
if
(
this
!=
fromDs
)
getLocalMetadata
(
)
.
add
(
fromDs
.
getLocalMetadata
(
)
,
false
)
;
transferInheritableMetadata
(
fromDs
,
getLocalMetadataInheritable
(
)
,
copyInheritedMetadataFromParents
)
;
setResourceControl
(
fromDs
.
getRestrictAccess
(
)
)
;
}

Transfer all inheritable metadata from fromDs to the local metadata of this dataset.
Called by InvDatasetScan to transfer inheritable metaddata to the nested catalogRef

@param fromDs transfer from here

Transfer all inheritable metadata from fromDs to the local metadata of this dataset . Called by InvDatasetScan to transfer inheritable metaddata to the nested catalogRef


private void transferInheritableMetadata(InvDatasetImpl fromDs, ThreddsMetadata target,
                                           boolean copyInheritedMetadataFromParents) {
    if (fromDs == null) return;
    logger.debug(" transferInheritedMetadata= " + fromDs.getName());

    target.add(fromDs.getLocalMetadataInheritable(), true);

    /* look through local metadata, find inherited InvMetadata elements
    ThreddsMetadata tmd = fromDs.getLocalMetadata();
    Iterator iter = tmd.getMetadata().iterator();
    while (iter.hasNext()) {
      InvMetadata meta = (InvMetadata) iter.next();
      if (meta.isInherited()) {
        if (!meta.isThreddsMetadata()) {
          tmc.addMetadata( meta);
        } else {
          logger.debug("  transferInheritedMetadata "+meta.hashCode()+" = "+meta);
          meta.finish(); // LOOK ?? make sure XLink is read in.
          tmc.add( meta.getThreddsMetadata(), true);
        }
      }
    }   */

    // now do the same for the parents
    if (copyInheritedMetadataFromParents)
      transferInheritableMetadata((InvDatasetImpl) fromDs.getParent(), target, true);
  }

transfer inherited metadata, consolidating it into target

@param fromDs transfer from here, plus its parents
@param target transfer to here

transfer inherited metadata consolidating it into target


public void setContributors(List<ThreddsMetadata.Contributor> a) {
    List<ThreddsMetadata.Contributor> dest = tm.getContributors();
    for (ThreddsMetadata.Contributor item : a) {
      if (!dest.contains(item))
        dest.add(item);
    }
    hashCode = 0;
  }

public void setContributors ( List < ThreddsMetadata . Contributor > a ) { List < ThreddsMetadata . Contributor > dest = tm . getContributors ( ) ; for ( ThreddsMetadata . Contributor item : a ) { if ( ! dest . contains ( item ) ) dest . add ( item ) ; } hashCode = 0 ; }


public void addDataset(int index, InvDatasetImpl ds) {
    if (ds == null) return;
    ds.setParent(this);
    datasets.add(index, ds);
    hashCode = 0;
  }

public void addDataset ( int index , InvDatasetImpl ds ) { if ( ds == null ) return ; ds . setParent ( this ) ; datasets . add ( index , ds ) ; hashCode = 0 ; }

Add a nested dataset at the location indicated by index.

@param index add at this position
@param ds    add this

Add a nested dataset at the location indicated by index .


public boolean removeDataset(InvDatasetImpl ds) {
    if (this.datasets.remove(ds)) {
      ds.setParent(null);
      InvCatalogImpl cat = (InvCatalogImpl) getParentCatalog();
      if (cat != null)
        cat.removeDatasetByID(ds);
      return (true);
    }
    return (false);
  }

public boolean removeDataset ( InvDatasetImpl ds ) { if ( this . datasets . remove ( ds ) ) { ds . setParent ( null ) ; InvCatalogImpl cat = ( InvCatalogImpl ) getParentCatalog ( ) ; if ( cat != null ) cat . removeDatasetByID ( ds ) ; return ( true ) ; } return ( false ) ; }

Remove the given dataset element from this dataset if it is in the dataset.

@param ds - the dataset element to be removed
@return true if this dataset contained the given dataset element.

Remove the given dataset element from this dataset if it is in the dataset .


public boolean replaceDataset(InvDatasetImpl remove, InvDatasetImpl add) {
    for (int i = 0; i < datasets.size(); i++) {
      InvDataset dataset = datasets.get(i);
      if (dataset.equals(remove)) {
        datasets.set(i, add);
        InvCatalogImpl cat = (InvCatalogImpl) getParentCatalog();
        if (cat != null) {
          cat.removeDatasetByID(remove);
          cat.addDatasetByID(add);
        }
        return true;
      }
    }
    return false;
  }

public boolean replaceDataset ( InvDatasetImpl remove , InvDatasetImpl add ) { for ( int i = 0 ; i < datasets . size ( ) ; i ++ ) { InvDataset dataset = datasets . get ( i ) ; if ( dataset . equals ( remove ) ) { datasets . set ( i , add ) ; InvCatalogImpl cat = ( InvCatalogImpl ) getParentCatalog ( ) ; if ( cat != null ) { cat . removeDatasetByID ( remove ) ; cat . addDatasetByID ( add ) ; } return true ; } } return false ; }

Replace the given dataset if it is a nesetd dataset.

@param remove - the dataset element to be removed
@param add    - the dataset element to be added
@return true on success

Replace the given dataset if it is a nesetd dataset .


public void addService(InvService service) {
    // System.out.println("--add dataset service= "+service.getName());
    servicesLocal.add(service);
    services.add(service);
    // add nested servers
    for (InvService nested : service.getServices()) {
      services.add(nested);
      // System.out.println("--add expanded service= "+nested.getName());
    }
    hashCode = 0;
  }

Add a service to this dataset.

@param service add this service to the dataset
@deprecated add services only to catalog


public void removeService(InvService service) {
    servicesLocal.remove(service);
    services.remove(service);
    // remove nested servers
    for (InvService nested : service.getServices()) {
      services.remove(nested);
    }
  }

public
void
removeService
(
InvService
service
)
{
servicesLocal
.
remove
(
service
)
;
services
.
remove
(
service
)
;
// remove nested servers
for
(
InvService
nested
:
service
.
getServices
(
)
)
{
services
.
remove
(
nested
)
;
}
}

Remove a service from this dataset.

@param service remove this
@deprecated put services in catalog


public void setServicesLocal(java.util.List<InvService> s) {
    this.services = new ArrayList<>();
    this.servicesLocal = new ArrayList<>();

    for (InvService elem : s) {
      addService(elem);
    }
    hashCode = 0;
  }

public void setServicesLocal ( java . util . List < InvService > s ) { this . services = new ArrayList <> ( ) ; this . servicesLocal = new ArrayList <> ( ) ; for ( InvService elem : s ) { addService ( elem ) ; } hashCode = 0 ; }

Set the list of services attached specifically to this dataset.
Discard any previous servies.

@param s list of services.

Set the list of services attached specifically to this dataset . Discard any previous servies .


public boolean removeLocalMetadata(InvMetadata metadata) {
    InvDatasetImpl parentDataset = ((InvDatasetImpl) metadata.getParentDataset());
    List localMdata = parentDataset.getLocalMetadata().getMetadata();
    if (localMdata.contains(metadata)) {
      if (localMdata.remove(metadata)) {
        hashCode = 0; // Need to recalculate the hash code.
        return (true);
      }
    }
    return (false);
  }

public
boolean
removeLocalMetadata
(
InvMetadata
metadata
)
{
InvDatasetImpl
parentDataset
=
(
(
InvDatasetImpl
)
metadata
.
getParentDataset
(
)
)
;
List
localMdata
=
parentDataset
.
getLocalMetadata
(
)
.
getMetadata
(
)
;
if
(
localMdata
.
contains
(
metadata
)
)
{
if
(
localMdata
.
remove
(
metadata
)
)
{
hashCode
=
0
;
// Need to recalculate the hash code.
return
(
true
)
;
}
}
return
(
false
)
;
}

Remove the given InvMetadata from the set of metadata local to this dataset.

@param metadata remove this
@return true if an InvMetadata is removed, false otherwise.

Remove the given InvMetadata from the set of metadata local to this dataset .


public Object getUserProperty(Object key) {
    if (userMap == null) return null;
    return userMap.get(key);
  }

public Object getUserProperty ( Object key ) { if ( userMap == null ) return null ; return userMap . get ( key ) ; }

Look up the User property having the given key

@param key property key
@return User property having the given key, or null


boolean validate(StringBuilder out) {
    this.isValid = true;

    // If log from construction has content, append to validation output msg.
    if (this.msgLog.length() > 0) {
      out.append(this.msgLog);
    }

    // Check that name is not null (it can be an empty string).
    if (this.getName() == null) {
      this.isValid = false;
      out.append(" ** DatasetNamer (1): null value for name is not valid.");
    }

    // Check that addLevel is not null.
    // boolean can't be null
    //if ( this.getAddLevel() == null)
    //{
    //  this.isValid = false;
    //  out.append(" ** DatasetNamer (2): null value for addLevel is not valid.");
    //}

    // Check that type is not null.
    if (this.getType() == null) {
      this.isValid = false;
      out.append(" ** DatasetNamer (3): null value for type is not valid (set with bad string?).");
    }

    if ( this.getType() == DatasetNamerType.REGULAR_EXPRESSION
         && ( this.getMatchPattern() == null || this.getSubstitutePattern() == null ))
    {
      this.isValid = false;
      out.append(" ** DatasetNamer (4): invalid datasetNamer <" + this.getName() + ">;" +
              " type is " + this.getType().toString() + ": matchPattern(" + this.getMatchPattern() + ") and substitutionPattern(" + this.getSubstitutePattern() + ") " +
              "must not be null.");
    }

    if ( this.getType() == DatasetNamerType.DODS_ATTRIBUTE
            && ( this.getAttribContainer() == null || this.getAttribName() == null ) )
    {
      this.isValid = false;
      out.append(" ** DatasetNamer (5): invalid datasetNamer <" + this.getName() + ">;" +
              " type is " + this.getType().toString() + ": attriuteContainer(" + this.getAttribContainer() + ") and attributeName(" + this.getAttribName() + ") must not be null.");
    }

    return (this.isValid);
  }

Validate this DatasetNamer object. Return true if valid, false if invalid.

@param out StringBuffer with validation messages.
@return boolean true if valid, false if invalid

Validate this DatasetNamer object . Return true if valid false if invalid .


public final List<String> getDescriptors() {
    List<String> desc = new ArrayList<String>();
    for (short fxy : descriptors)
      desc.add(Descriptor.makeString(fxy));
    return desc;
  }

public final List < String > getDescriptors ( ) { List < String > desc = new ArrayList < String > ( ) ; for ( short fxy : descriptors ) desc . ( Descriptor . makeString ( fxy ) ) ; return desc ; }

get list of data descriptors as Strings

@return descriptors as List<String>


public static String constructServerPath(HttpServletRequest hsreq) {
		return hsreq.getScheme() + "://" + hsreq.getServerName() + ":" + hsreq.getServerPort() + "/thredds/wfs/";
	}

public static String constructServerPath ( HttpServletRequest hsreq ) { return hsreq . getScheme ( ) + "://" + hsreq . getServerName ( ) + ":" + hsreq . getServerPort ( ) + "/thredds/wfs/" ; }

Constructs the full server URI from a request

@param hsreq The relevant request
@return The URI of the server corresponding to the request


private void getCapabilities(PrintWriter out, HttpServletRequest hsreq, SimpleGeometryCSBuilder sgcs) {
		WFSGetCapabilitiesWriter gcdw = new WFSGetCapabilitiesWriter(out, WFSController.constructServerPath(hsreq));
		gcdw.startXML();
		gcdw.addOperation(WFSRequestType.GetCapabilities); gcdw.addOperation(WFSRequestType.DescribeFeatureType); gcdw.addOperation(WFSRequestType.GetFeature);
		gcdw.writeOperations();

		List<String> seriesNames = sgcs.getGeometrySeriesNames();

		for(String name : seriesNames) {
			gcdw.addFeature(new WFSFeature(TDSNAMESPACE + ":" + name, name));
		}

		gcdw.writeFeatureTypes();
		gcdw.finishXML();
	}

private void getCapabilities ( PrintWriter out , HttpServletRequest hsreq , SimpleGeometryCSBuilder sgcs ) { WFSGetCapabilitiesWriter gcdw = new WFSGetCapabilitiesWriter ( out , WFSController . constructServerPath ( hsreq ) ) ; gcdw . startXML ( ) ; gcdw . addOperation ( WFSRequestType . GetCapabilities ) ; gcdw . addOperation ( WFSRequestType . DescribeFeatureType ) ; gcdw . addOperation ( WFSRequestType . GetFeature ) ; gcdw . writeOperations ( ) ; List < String > seriesNames = sgcs . getGeometrySeriesNames ( ) ; for ( String name : seriesNames ) { gcdw . addFeature ( new WFSFeature ( TDSNAMESPACE + ":" + name , name ) ) ; } gcdw . writeFeatureTypes ( ) ; gcdw . finishXML ( ) ; }

Processes GetCapabilities requests.

@param out
@return


private WFSExceptionWriter getFeature(PrintWriter out, HttpServletRequest hsreq, SimpleGeometryCSBuilder sgcs, String ftName, String fullFtName) {

		List<SimpleGeometry> geometryList = new ArrayList<SimpleGeometry>();

		GeometryType geoT = sgcs.getGeometryType(ftName);

		if(geoT == null){
			return new WFSExceptionWriter("Feature Type of " + fullFtName + " not found.", "GetFeature" , "OperationProcessingFailed");
		}

		try {

			switch(geoT) {
			case POINT:
				Point pt = sgcs.getPoint(ftName, 0);
				int j = 0;
				while(pt != null) {
					geometryList.add(pt);
					j++;
					pt = sgcs.getPoint(ftName, j);
				}
				break;

			case LINE:

				Line line = sgcs.getLine(ftName, 0);
				int k = 0;
				while(line != null) {
					geometryList.add(line);
					k++;
					line = sgcs.getLine(ftName, k);
				}

				break;

			case POLYGON:

				Polygon poly =	sgcs.getPolygon(ftName, 0);
				int i = 0;
				while(poly != null) {
					geometryList.add(poly);
					i++;
					poly = sgcs.getPolygon(ftName, i);
				}

				break;

			}
		}

		// Perhaps will change this to be implemented in the CFPolygon class
		catch(ArrayIndexOutOfBoundsException aout){

		}

		WFSGetFeatureWriter gfdw = new WFSGetFeatureWriter(out, WFSController.constructServerPath(hsreq), WFSController.getXMLNamespaceXMLNSValue(hsreq), geometryList, ftName);
		gfdw.startXML();
		gfdw.writeMembers();
		gfdw.finishXML();

		return null;
	}

Processes GetFeature requests.

@param out
@return


private WFSExceptionWriter checkParametersForError(String request, String version, String service, String typeName) {
		// The SERVICE parameter is required. If not specified, is an error (throw exception through XML).
		if(service != null) {
			// For the WFS servlet it must be WFS if not, write out an InvalidParameterValue exception.
			if(!service.equalsIgnoreCase("WFS")) {
					return new WFSExceptionWriter("WFS Server error. SERVICE parameter must be of value WFS.", "service", "InvalidParameterValue");
			}

		}

		else {
			return new WFSExceptionWriter("WFS server error. SERVICE parameter is required.", "request", "MissingParameterValue");
		}

		// The REQUEST Parameter is required. If not specified, is an error (throw exception through XML).
		if(request != null) {

			// Only go through version checks if NOT a Get Capabilities request, the VERSION parameter is required for all operations EXCEPT GetCapabilities section 7.6.25 of WFS 2.0 Interface Standard
			if(!request.equalsIgnoreCase(WFSRequestType.GetCapabilities.toString())) {

				if(version != null ) {
					// If the version is not failed report exception VersionNegotiationFailed, from OGC Web Services Common Standard section 7.4.1

					// Get each part
					String[] versionParts = version.split("\\.");

						for(int ind = 0; ind < versionParts.length; ind++) {

						// Check if number will throw NumberFormatException if not.
						try {
							Integer.valueOf(versionParts[ind]);
						}

						/* Version parameters are only allowed to consist of numbers and periods. If this is not the case then
						 * It qualifies for InvalidParameterException
						 */
						catch (NumberFormatException excep){
							return new WFSExceptionWriter("WFS server error. VERSION parameter consists of invalid characters.", "version", "InvalidParameterValue");
						}
					}

					/* Now the version parts are all constructed from the parameter
					 * Analyze for correctness.
					 */
					boolean validVersion = false;

					// If just number 2 is specified, assume 2.0.0, pass the check
					if(versionParts.length == 1) if(versionParts[0].equals("2")) validVersion = true;

					// Two or more version parts specified, make sure it's 2.0.#.#...
					if(versionParts.length >= 2) if(versionParts[0].equals("2") && versionParts[1].equals("0")) validVersion = true;

					/* Another exception VersionNegotiationFailed is specified by OGC Web Services Common
					 * for version mismatches. If the version check failed print this exception
					 */
					if(!validVersion){
						return new WFSExceptionWriter("WFS Server error. Version requested is not supported.", null, "VersionNegotiationFailed");
					}
				}

				else {
					return new WFSExceptionWriter("WFS server error. VERSION parameter is required.", "request", "MissingParameterValue");

				}

				// Last check to see if typenames is specified, must be for GetFeature, DescribeFeatureType
				if(typeName == null) {
					return new WFSExceptionWriter("WFS server error. For the specifed request, parameter typename or typenames must be specified.", request, "MissingParameterValue");
				}
			}

			WFSRequestType reqToProc = WFSRequestType.getWFSRequestType(request);
			if(reqToProc == null) return new WFSExceptionWriter("WFS server error. REQUEST parameter is not valid. Possible values: GetCapabilities, "
					+ "DescribeFeatureType, GetFeature", "request", "InvalidParameterValue");

		}

		else{
			return new WFSExceptionWriter("WFS server error. REQUEST parameter is required.", "request", "MissingParameterValue");
		}

		return null;
	}

Checks request parameters for errors.
Will send back an XML Exception if any errors are encountered.

@param request parameter value
@param version parameter value
@param service parameter value
@param actualFTName parameter value
@return an ExceptionWriter if any errors occurred or null if none occurred

Checks request parameters for errors . Will send back an XML Exception if any errors are encountered .


@RequestMapping("**")
	public void httpHandler(HttpServletRequest hsreq, HttpServletResponse hsres) {
		try {

			PrintWriter wr = hsres.getWriter();
			List<String> paramNames = new LinkedList<String>();
			Enumeration<String> paramNamesE = hsreq.getParameterNames();
			while(paramNamesE.hasMoreElements()) paramNames.add(paramNamesE.nextElement());

			// Prepare parameters
			String request = null;
			String version = null;
			String service = null;
			String typeNames = null;
			String datasetReqPath = null;
			String actualPath = null;
			String actualFTName = null;
			NetcdfDataset dataset = null;

			if(hsreq.getServletPath().length() > 4) {
				datasetReqPath = hsreq.getServletPath().substring(4, hsreq.getServletPath().length());
			}

			actualPath = TdsRequestedDataset.getLocationFromRequestPath(datasetReqPath);

			if(actualPath != null) dataset = NetcdfDataset.openDataset(actualPath);
			else return;

			List<CoordinateSystem> csList = dataset.getCoordinateSystems();
			SimpleGeometryCSBuilder cs = new SimpleGeometryCSBuilder(dataset, csList.get(0), null);

			/* Look for parameter names to assign values
			 * in order to avoid casing issues with parameter names (such as a mismatch between reQUEST and request and REQUEST).
			 */
			for(String paramName : paramNames) {
				if(paramName.equalsIgnoreCase("REQUEST")) {
					request = hsreq.getParameter(paramName);
				}

				if(paramName.equalsIgnoreCase("VERSION")) {
					version = hsreq.getParameter(paramName);
				}

				if(paramName.equalsIgnoreCase("SERVICE")) {
					service = hsreq.getParameter(paramName);
				}

				if(paramName.equalsIgnoreCase("TYPENAMES") || paramName.equalsIgnoreCase("TYPENAME")) {
					typeNames = hsreq.getParameter(paramName);

					// Remove namespace header for getFeature
					if(typeNames != null) if(typeNames.length() > TDSNAMESPACE.length()) {
						actualFTName = typeNames.substring(TDSNAMESPACE.length() + 1, typeNames.length());
					}
				}
			}

			WFSExceptionWriter paramError = checkParametersForError(request, version, service, typeNames);
			WFSExceptionWriter requestProcessingError = null;

			// If parameter checks all pass launch the request
			if(paramError == null) {

				WFSRequestType reqToProc = WFSRequestType.getWFSRequestType(request);

				switch(reqToProc) {
					case GetCapabilities:
						getCapabilities(wr, hsreq, cs);
					break;

					case DescribeFeatureType:
						describeFeatureType(wr, hsreq, actualFTName);

					break;

					case GetFeature:
						requestProcessingError = getFeature(wr, hsreq, cs, actualFTName, typeNames);
					break;
				}

			}

			// Parameter checks did not all pass, print the error and return
			else {
				paramError.write(hsres);
				return;
			}

			/* Specifically writes out exceptions that were incurred
			 * while processing requests.
			 */
			if(requestProcessingError != null){
				requestProcessingError.write(hsres);
				return;
			}
		}

		catch(IOException io) {
			throw new RuntimeException("The writer may not have been able to been have retrieved"
					+ " or the requested dataset was not found", io);
		}
	}

A handler for WFS based HTTP requests that sends to other request handlers
to handle the request.

Servlet Path: /wfs/{request}

A handler for WFS based HTTP requests that sends to other request handlers to handle the request .


public String
    getCapabilities(String url)
            throws IOException
    {
        // Save the original url
        String saveurl = this.xuri.getOriginal();
        parseURL(url);
        String fdsurl = buildURL(this.xuri.assemble(XURI.URLALL), DSRSUFFIX, null, null);
        try {
            // Make the request and return an input stream for accessing the databuffer
            // Should fill in context bigendian and stream fields
            InputStream stream = callServer(fdsurl);
            // read the result, convert to string and return.
            byte[] bytes = DapUtil.readbinaryfile(stream);
            String document = new String(bytes, DapUtil.UTF8);
            return document;
        } finally {
            parseURL(saveurl);
        }
    }

Provide a method for getting the capabilities document.

@param url for accessing the document
@throws DapException

Provide a method for getting the capabilities document .


@Override
    public long length() {
        // If the summary is already in the cache, return it.
        // It'll have been added by a listDatasets() call on the parent directory.
        S3ObjectSummary objectSummary = objectSummaryCache.getIfPresent(s3uri);
        if (objectSummary != null) {
            return objectSummary.getSize();
        }

        /* Get the metadata directly from S3. This will be expensive.
         * We get punished hard if length() and/or lastModified() is called on a bunch of datasets without
         * listDatasets() first being called on their parent directory.
         *
         * So, is the right thing to do here "getParentDataset().listDatasets()" and then query the cache again?
         * Perhaps, but listDatasets() throws an IOException, and length() and lastModified() do not.
         * We would have to change their signatures and the upstream client code to make it work.
         */
        ObjectMetadata metadata = threddsS3Client.getObjectMetadata(s3uri);
        if (metadata != null) {
            return metadata.getContentLength();
        } else {
            // "this" may be a collection or non-existent. In both cases, we return 0.
            return 0;
        }
    }

Returns the size of the dataset, in bytes. Will be zero if this dataset is a collection or non-existent.

@return the size of the dataset

Returns the size of the dataset in bytes . Will be zero if this dataset is a collection or non - existent .


@Override
    public Date lastModified() {
        S3ObjectSummary objectSummary = objectSummaryCache.getIfPresent(s3uri);
        if (objectSummary != null) {
            return objectSummary.getLastModified();
        }

        ObjectMetadata metadata = threddsS3Client.getObjectMetadata(s3uri);
        if (metadata != null) {
            return metadata.getLastModified();
        } else {
            // "this" may be a collection or non-existent. In both cases, we return null.
            return null;
        }
    }

Returns the date that the dataset was last modified. Will be null if the dataset is a collection or non-existent.

@return the date that the dataset was last modified

Returns the date that the dataset was last modified . Will be null if the dataset is a collection or non - existent .