OC Tutorial

Draft: 02/22/2009
Last Revised: 02/22/2009

Introduction

This document describes in detail a program that uses oc to fetch and print data. The complete program is in Appendix A.

Main Program

Ignoring the program headers, the main program performs the usual argument parsing to collect the DAP URL to dump to standard output. The url name is collected either from the command line or from the environment variable called FILEURL. A typical example might be "file:///home/dennis/testdata/test1" or http://test.opendata.org/testdata/test1". Note that the URL leaves off the ".dds" or ".das" or ".dods" suffix.

Processdata

The main program turns over control to processdata() to fetch the DAS and DATADDS. The top-level operations are as follows.
  1. Create an OCconnection instance using oc_open.
  2. Fetch the DAS using oc_fetchdas.
  3. Fetch the DATADDS using oc_fetchdatadds.
  4. Merge the DAS into the DDS (oc_ddsdasmerge).
  5. Get the root node of the DATADDS parse tree (oc_getdatadds).
  6. Get a content instance representing the whole content retrieved by the DATA DDS fetch above. (oc_rootcontent).
  7. Print out the DDS (and the DAS via the merged attributes) (printdds).
  8. Print out the data contents (printleaves). The term leaf is used here because the code walks to all the primitive typed objects and prints them out.
  9. Close the connection (oc_close).

Print DDS

The printdds() procedure is recursive. It takes as its arguments the connection object, a node in the tree, and a depth indicator to tell how deep in the tree the recursion has gone and to control output indenting. The operation is as follows.
  1. Collect a number of useful values (e.g. node type).
  2. Check the node type and perform the following depending on that type.
    1. If the node type is OC_Primitive, then print out a description of the primitive field: its type, its name, and any dimensions. Return from the printdds() procedure.
    2. If the node type is OC_Sequence, then print out its name, and the number of columns (aka members or fields). Then recursively call the printdds() procedure to print out the fields of the Sequence.
    3. For all other node types (Grid, Structure, Dataset), print out its name, and the number of fields. Then recursively call the printdds() procedure to print out the fields of the Node.
  3. In all cases, print out any attributes that are attached to this node.
  4. Also, indent the output based on the depth value.

Printleaves: Print DDS Content

The printleaves() procedure is recursive and follows the general structure of printdds except that it also must navigate a set of content objects to extract the data for printing. It takes as its arguments the connection object, a node in the tree, and a depth indicator to tell how deep in the tree the recursion has gone and to control output indenting. However, instead of discriminating on node type, this procedure initially discriminates on the content mode. The procedure takes as its arguments the connection object, a node in the tree, a content object, and a list of the parent nodes (a path) as they are processed.

The operation is as follows.

  1. Collect a number of useful values (e.g. node rank).
  2. Switch on the mode of the passed-in content object.
  3. Fieldmode:
    1. Create a new content object (named fieldcontent) to reference the content for the ith field of the current (passed-in) node. This content object is reused for every field.
    2. Obtain the count of the number of fields using oc_fieldcount
    3. Iterate over the fields of the current node and set the fieldcontent object to refer to that ith field using oc_fieldcontent.
    4. Push this field subnode onto the path.
    5. Recurse on printleaves using the new fieldnode and the field content object.
    6. Pop the node from the path and continue to the next field.
    7. When all fields have been processed, free up the fieldcontent content object using oc_freecontent.
  4. Dimmode - OC_Structure: There are two cases here, in one case, the node type is OC_Primitive, which means that we can get data. In the other case, it means the node type is OC_Structure, which means we need to recurse into the fields of the structure. The OC_Structure case is simpler, so it is described first.
    1. Create a new content object (named dimcontent) to reference the content for the ith element of an array. This content object is reused for every element.
    2. Obtain the count of the number of elements in the array using oc_dimcount
    3. Iterate over the elements and set the dimcontent object to refer to that ith element using oc_dimcontent.
    4. Recurse on printleaves using the same node, but the array content object.
    5. Note that we do not push the node on the path again, because the node associated with a dimension content is always the same node.
    6. When all fields have been processed, free up the fieldcontent content object using oc_freecontent.
  5. Dimmode - OC_Primitive:
    1. Create a new content object (named dimcontent) to reference the content for the ith element of an array. This content object is reused for every element. Note that the mode of this new content will be Datamode.
    2. Obtain the count of the number of elements in the array using oc_dimcount
    3. Iterate over the number of elements.
    4. Now, instead of recursing, oc_getcontent is called to extract a single item of data.
    5. When all fields have been processed, free up the fieldcontent content object using oc_freecontent.

Dumpinstance: Print a Set of Data Items

This procedure uses the type argument to determine the print format. The path is printed out to indicate the full path to the variable of which this is a part. The count indicates how many data instances to print. The data is assumed to be contiguous in the memory argument.

Copyright

Copyright 2009, UCAR/Unidata and OPeNDAP, Inc. See the COPYRIGHT file for more information.

Change Log

Appendix A. Code for dump.c

A.1 Headers

/* Copyright 2009, UCAR/Unidata and OPeNDAP, Inc.
   See the COPYRIGHT file for more information. */

#include "ocinternal.h"
#include "ocdata.h"
#include "occontent.h"
#include "ocdebug.h"

char* fileurl;

extern int optind;
extern int opterr;
extern char* optarg;

// Forward
static void usage(void);
static void check_err(int stat);
static void init(void);
static void dumpflags(void);
static char* indent(int);

static int processdata(char*);
static char* field2name(OCnode* node, size_t fieldindex);
static void printdds(OCconnection*, OCnode* inode, int depth);
static void printleaves(OCconnection*, OCnode* node, OCcontent*, List*);
static void dumpinstance(OCnode* node, size_t count, void* memory, List* path);
static void printattributes(OCnode* node);

A.2 Main

int
main(int argc, char **argv)
{
    int c;
    int ncid;

    fileurl = NULL;

    if(argc > 0)
        fileurl = strdup(argv[1]);
    }

    if(fileurl == NULL) fileurl = getenv("FILEURL");

    if(fileurl == NULL) {
        fprintf(stderr,"no file url specified\n");
	exit(1);
    }
    processdata(fileurl);
    return 0;
}

static void
check_err(int stat)
{
    if(stat == OC_NOERR) return;
    fprintf(stderr,"error status returned: (%d) %s\n",stat,ocerrstring(stat));
    fflush(stdout); fflush(stderr);
    exit(1);
}


static char*
indent(int depth)
{
    int size = (depth*2);
    char* blanks = malloc(size+1);
    memset(blanks,' ',size);
    blanks[size] = '\0';
    return blanks;
}

A.3 Fetch DAS and DATADDS

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

static int
processdata(char* fileurl)
{
    int i,j,stat;
    OCconnection* state;
    OCnode* node;
    List* leaves = listnew();
    struct Dimcounter counter;
    List* pathcounters;
    int movepoint;
    OCcontent* rootcontent;
    OCnode* root;
    
    stat = oc_open(&state);
    check_err(stat);

    // DAS may not exist
    stat = oc_fetchdas(state,fileurl);

    stat = oc_fetchdatadds(state,fileurl);
    check_err(stat);

    // merge the das, if any
    if(oc_getdas(state) != NULL) {
        stat = oc_ddsdasmerge(state,MERGEDATADDS);
        check_err(stat);
    }

    // Get the root content
    root = oc_getdatadds(state);
    rootcontent = oc_newcontent(state);
    stat = oc_rootcontent(state,rootcontent);
    check_err(stat);

    // Recursively walk both tree and the content
    // to print the dds and the associated data

    printdds(state,root,0);
    printleaves(state,root,rootcontent,listnew());

    oc_close(state);

}

A.4 Print the DAS and (DATA)DDS

static void
printdds(OCconnection* state, OCnode* node, int depth)
{
    int i,stat;
    char* name;
    OCtype octype;
    int nsubnodes,nattrs,rank;
    OCcontent* content;
    int* dimdata;
    char* aname;
    char** dimnames;
    OCtype primtype;

    octype = node->octype;
    primtype = node->etype;
    name = strdup(node->name);
    nsubnodes = listlength(node->subnodes);
    nattrs = listlength(node->attributes);
    rank = node->array.rank;

    if(octype == OC_Primitive) {
        fprintf(stdout,"[%2d]: %s %s", depth,octypetostring(primtype),name);
        // dump dim info
        for(i=0;iarray.dimensions,i);
            if(dim->name == NULL)
                fprintf(stdout,"[%d]",dim->dim.declsize);
            else
                fprintf(stdout,"[%s=%d]",dim->name,dim->dim.declsize);
        }
        fprintf(stdout,"\n");
        printattributes(node);
    } else if(octype == OC_Sequence) {
        fprintf(stdout,"[%2d]: %s %s |columns|=%d\n",
                depth,octypetostring(octype),name,nsubnodes);
        printattributes(node);
        for(i=0;isubnodes,i);
            printdds(state,subnode,depth+1);
        }
    } else {
        fprintf(stdout,"[%2d]: %s %s |subnodes|=%d\n",
                depth,octypetostring(octype),name,nsubnodes);
        printattributes(node);
        for(i=0;isubnodes,i);
            printdds(state,subnode,depth+1);
        }
    }    
}
static void
printattributes(OCnode* node)
{
    int i,j;
    char tmp[128];
    int nattrs = listlength(node->attributes);

    if(nattrs > 0) {
        for(i=0;iattributes,i);
            char* value = att->values;
            fprintf(stdout,"\t\t%s %s:%s = ",octypetostring(att->etype),node->name,att->name);
            for(j=0;jnvalues;j++) {
                if(j > 0) fputs(" ",stdout);
                octypeprint(att->etype,tmp,sizeof(tmp),value);
                value += octypesize(att->etype);
                fputs(tmp,stdout);
            }
            fprintf(stdout,"\n");
        }
    }
}

A.5 Walk and Print the Content

static void
printleaves(OCconnection* state, OCnode* node, OCcontent* content, OClist* path)
{
    int stat;
    int nsubnodes;
    char* aname;
    char** dimnames;
    int* dimdata;
    char* memory;
    int i,rank,nrows;
    size_t count;

    nsubnodes = listlength(node->subnodes);
    rank = node->array.rank;

    switch (oc_contentmode(state,content)) {
    case Fieldmode:
        {
            OCcontent* fielddata  = oc_newcontent(state);
            for(i=0;isubnodes,i);
                stat = oc_fieldcontent(state,content,fielddata,i);
                check_err(stat);
                oclistpush(path,(ocelem)subnode->name);
                printleaves(state,subnode,fielddata,path);
                oclistpop(path);
            }
            oc_freecontent(state,fielddata);
        }
        break;

    case Dimmode:
        {
            // Walk to each element and dump
            OCcontent* dimdata  = oc_newcontent(state);
            count = oc_dimcount(state,content);
            if(node->octype == OC_Primitive) {
                size_t memsize = octypesize(node->etype)*count;
                char* memory = malloc(memsize);
                size_t offset = 0;
                size_t avail = memsize;
                for(i=0;ietype);
                    avail -= octypesize(node->etype);
                }
                dumpinstance(node,count,memory,path);
            } else { // structure
                for(i=0;ietype),1); 
            stat = oc_getcontent(state,content,memory,octypesize(node->etype),0,1);
            check_err(stat);
            dumpinstance(node,1,memory,path);
        }
        break;

    default: break;
    }
}

A.6 Print a Set of Data Items

static void
dumpinstance(OCnode* node, size_t count, void* memory, OClist* path)
{
    int i;
    char tmp[256];
    size_t delta = octypesize(node->etype);
    Bytebuffer* name;

    // construct the name
    name = bbNew();
    for(i=0;ietype),
                bbDup(name),
                (unsigned long)count);
    for(i=0;ietype,tmp,sizeof(tmp),memory+(i*delta)));
    }
    fprintf(stdout,"\n");
    fflush(stdout);
}