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.
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.
The main program turns over control to processdata()
to fetch the DAS and DATADDS.
The top-level operations are as follows.
- Create an OCconnection instance using
oc_open.
- Fetch the DAS using
oc_fetchdas.
- Fetch the DATADDS using
oc_fetchdatadds.
- Merge the DAS into the DDS
(oc_ddsdasmerge).
- Get the root node of the DATADDS parse tree
(oc_getdatadds).
- Get a content instance representing the whole
content retrieved by the DATA DDS fetch above.
(oc_rootcontent).
- Print out the DDS (and the DAS via the merged attributes)
(printdds).
- 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.
- Close the connection
(oc_close).
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.
- Collect a number of useful values
(e.g. node type).
- Check the node type and perform the following depending on that type.
- 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.
- 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.
- 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.
- In all cases, print out any attributes that are attached to this node.
- Also, indent the output based on the depth value.
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.
- Collect a number of useful values
(e.g. node rank).
- Switch on the mode of the passed-in content object.
- Fieldmode:
- 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.
- Obtain the count of the number of fields
using oc_fieldcount
- Iterate over the fields of the current node
and set the fieldcontent object to refer to that ith field
using oc_fieldcontent.
- Push this field subnode onto the path.
- Recurse on printleaves using the new fieldnode
and the field content object.
- Pop the node from the path and continue to the next field.
- When all fields have been processed, free up the fieldcontent
content object using oc_freecontent.
- 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.
- 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.
- Obtain the count of the number of elements in the array
using oc_dimcount
- Iterate over the elements
and set the dimcontent object to refer to that ith element
using oc_dimcontent.
- Recurse on printleaves
using the same node, but the array content object.
- 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.
- When all fields have been processed, free up the fieldcontent
content object using oc_freecontent.
- Dimmode - OC_Primitive:
- 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.
- Obtain the count of the number of elements in the array
using oc_dimcount
- Iterate over the number of elements.
- Now, instead of recursing, oc_getcontent is called
to extract a single item of data.
- When all fields have been processed, free up the fieldcontent
content object using oc_freecontent.
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
- 02/22/2009 - First draft.
/* 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);
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;
}
//////////////////////////////////////////////////
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);
}
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");
}
}
}
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;
}
}
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);
}