Understanding the NCML Handler

From OPeNDAP Documentation
⧼opendap2-jumptonavigation⧽

The NCML handler has 53 classes. The classes fall into four broad categories:

Utilities
Used to perform various routine tasks (namespace agg_util)
BES Framework
Used to instantiate the module and provide interface hooks for the BES DAP module (namespace ncml_module)
NCML Parser
A SAX2 parser that uses the NCML text to build one or more DAP objects (DDS, DataDDS, etc.) (namespace ncml_module)
AIS code
Classes that add, modify or remove variables and attributes. (namespace ncml_module)
Aggregation code
Classes that build Join New and Join Existing aggregations (namespace ncml_module)

The following sections list the classes/files that fall into these five categories along with some notes about those classes where appropriate.

In the listing of classes, indentation is used to show parent-child relationships within the module. For classes that inherit from BES framework or libdap classes, child: public parent is used.

Utilities

  1. RCobject - A reference counted object class. This is used to add reference counting to the DDS, etc., objects the handler makes. This could possibly be refactored out of the code in favor of shared_ptr<> if we adopt C++-x11. This class also seems to have support for a memory pool, but comments imply that it's never been implemented.
  2. RCObjectInterface - An interface for the RCObject class.
  3. NCMLUtil
  4. AggregationUtil
  5. DirectoryUtil
  6. NCMLDebug.h - Macros
  7. MyBaseTypeFactory - Used by VariableElement and AggregationElement
  8. NCMLBaseArray: public libdap::Array - An abstract class
    1. NCMLArray<T> - This is only used in MyBaseTypeFactory
  9. DDSAccessInterface - Interface class for any object that can contains a DDS
  10. DDSLoader - Helper class for temporarily hijacking an existing dhi to load a DDX response for one particular file.

BES Framework

  1. NCMLModule: public BESAbstractModule
  2. NCMLRequestHandler: public BESRequestHandler

NCML Parser

The classes the make up the core of the SAX2 parser are:

  1. SaxParser
    1. SimpleLocationParser
    2. OtherXMLParser
  2. SimpleTimeParser - Odd that this is not a child of SaxParser...
  3. XMLHelper
  4. SaxParserWrapper

Those classes are augmented by the Element classes that are used to build the in-memory objects the NCML needs to carry out the AIS or Aggregation tasks specified by a given NCML file. They are:

  1. NCMLElement
    1. AggregationElement
    2. AttributeElement
    3. DimensionElement
    4. ExplicitElement
    5. NetcdfElement
    6. ReadMetadataElement
    7. RemoveElement
    8. ValuesElement
    9. ScanElement
    10. VariableElement
    11. VariableAggElement

AIS Code

  1. RenamedArrayWrapper: public libdap::Array

Aggregation code

  1. AggregationException: public std::runtime_error
  2. Dimension - Struct for holding information about a dimension of data, minimally a name and a cardinality (size)
  3. AggMemberDataset: public RCObject
    1. AggMemberDatasetWithDimensionCacheBase
      1. AggMemberDatasetDDSWrapper
      2. AggMemberDatasetSharedDDSWrapper
      3. AggMemberDatasetUsingLocationRef
  4. ArrayAggregationBase : public libdap::Array
    1. ArrayAggregateOnOuterDimension
    2. ArrayJoinExistingAggregation
  5. GridAggregationBase : public libdap::Grid
    1. GridAggregateOnOuterDimension
    2. GridJoinExistingAggregation

How it works

This is a narrative description of how the handler works. With 49 classes (well, 49 header files), it's pretty complicated. This is an abridged explanation!

To build a DDS response from a NCML file, the handler runs NCMLRequestHandler::ncml_build_dds. The ncml_build_dds() method first parses the NCML text and returns a BESDapResponse object that holds either a BESDDSResponse that in turn holds a DDS. The actual parse operation is done by SaxParserWrapper::parse() once the SaxParserWrapper class is instantiated using an instance of NCMLParser. The NCMLParser object provides the SaxParserWrapper with a response object, response type and filename. The SaxParserWrapper::parse() takes the filename of the NCML to parse (which is redundant since the SaxParserWrapper instance was initializes using the NCMLParser object which has the filename in it as a field. The parse() method returns the BesDapResponse object (a BESDDSResponse that holds a DDS). I don't know why it returns a BesDapResponse with a ... instead of just returning a DDS.

Once the handler has the DDS in the BESDDSResponse in the BesDapresponse, it copies the variables and attributes from it to a the BESDDSResponse that was passed into the NCMLRequestHandler::ncml_build_dds() method via its DHI BESDataHandlerInterface parameter. It then sets the constraint, file name and dataset name and returns, with the DHI all set for use in the rest of the BES pipeline.