netcdf-c/libdap4/d4printer.c

592 lines
15 KiB
C
Raw Normal View History

Primary change: add dap4 support Specific changes: 1. Add dap4 code: libdap4 and dap4_test. Note that until the d4ts server problem is solved, dap4 is turned off. 2. Modify various files to support dap4 flags: configure.ac, Makefile.am, CMakeLists.txt, etc. 3. Add nc_test/test_common.sh. This centralizes the handling of the locations of various things in the build tree: e.g. where is ncgen.exe located. See nc_test/test_common.sh for details. 4. Modify .sh files to use test_common.sh 5. Obsolete separate oc2 by moving it to be part of netcdf-c. This means replacing code with netcdf-c equivalents. 5. Add --with-testserver to configure.ac to allow override of the servers to be used for --enable-dap-remote-tests. 6. There were multiple versions of nctypealignment code. Try to centralize in libdispatch/doffset.c and include/ncoffsets.h 7. Add a unit test for the ncuri code because of its complexity. 8. Move the findserver code out of libdispatch and into a separate, self contained program in ncdap_test and dap4_test. 9. Move the dispatch header files (nc{3,4}dispatch.h) to .../include because they are now shared by modules. 10. Revamp the handling of TOPSRCDIR and TOPBUILDDIR for shell scripts. 11. Make use of MREMAP if available 12. Misc. minor changes e.g. - #include <config.h> -> #include "config.h" - Add some no-install headers to /include - extern -> EXTERNL and vice versa as needed - misc header cleanup - clean up checking for misc. unix vs microsoft functions 13. Change copyright decls in some files to point to LICENSE file. 14. Add notes to RELEASENOTES.md
2017-03-09 08:01:10 +08:00
/*********************************************************************
2018-12-07 05:24:28 +08:00
* Copyright 2018, UCAR/Unidata
Primary change: add dap4 support Specific changes: 1. Add dap4 code: libdap4 and dap4_test. Note that until the d4ts server problem is solved, dap4 is turned off. 2. Modify various files to support dap4 flags: configure.ac, Makefile.am, CMakeLists.txt, etc. 3. Add nc_test/test_common.sh. This centralizes the handling of the locations of various things in the build tree: e.g. where is ncgen.exe located. See nc_test/test_common.sh for details. 4. Modify .sh files to use test_common.sh 5. Obsolete separate oc2 by moving it to be part of netcdf-c. This means replacing code with netcdf-c equivalents. 5. Add --with-testserver to configure.ac to allow override of the servers to be used for --enable-dap-remote-tests. 6. There were multiple versions of nctypealignment code. Try to centralize in libdispatch/doffset.c and include/ncoffsets.h 7. Add a unit test for the ncuri code because of its complexity. 8. Move the findserver code out of libdispatch and into a separate, self contained program in ncdap_test and dap4_test. 9. Move the dispatch header files (nc{3,4}dispatch.h) to .../include because they are now shared by modules. 10. Revamp the handling of TOPSRCDIR and TOPBUILDDIR for shell scripts. 11. Make use of MREMAP if available 12. Misc. minor changes e.g. - #include <config.h> -> #include "config.h" - Add some no-install headers to /include - extern -> EXTERNL and vice versa as needed - misc header cleanup - clean up checking for misc. unix vs microsoft functions 13. Change copyright decls in some files to point to LICENSE file. 14. Add notes to RELEASENOTES.md
2017-03-09 08:01:10 +08:00
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
*********************************************************************/
#include "d4includes.h"
/**
This provides a simple dap4 metadata -> xml printer.
Used to test the parser
*/
#define NCD4_parse t_parse /* to avoid dup defs */
#include "ncd4types.h"
#include "ncd4.h"
/**************************************************/
typedef struct D4printer {
NCbytes* out;
NCbytes* tmp;
NCD4meta* metadata;
} D4printer;
/**************************************************/
#define CAT(x) ncbytescat(out->out,x)
#define INDENT(x) indent(out,x)
/**************************************************/
/* Forwards */
static void atomicsToString(D4printer*, union ATOMICS* value, nc_type type);
static int hasMetaData(NCD4node* node);
static void indent(D4printer*, int depth);
static int printAttribute(D4printer*, NCD4node* attr, int depth);
static int printDataset(D4printer* out, NCD4node* node, int depth);
static int printDimref(D4printer*, NCD4node* d, int depth);
static int printGroup(D4printer* out, NCD4node* node, int depth);
static int printGroupBody(D4printer* out, NCD4node* node, int depth);
static int printMap(D4printer* out, NCD4node* mapref, int depth);
static int printMetaData(D4printer* out, NCD4node* node, int depth);
static int printNode(D4printer*, NCD4node* node, int depth);
static int printValue(D4printer*, const char* value, int depth);
static int printVariable(D4printer*, NCD4node* var, int depth);
static int printXMLAttributeAtomics(D4printer*, char* name, union ATOMICS* value, nc_type type);
static int printXMLAttributeName(D4printer*, char* name, const char* value);
static int printXMLAttributeSize(D4printer*, char* name, size_t value);
static int printXMLAttributeString(D4printer*, char* name, const char* value);
/**************************************************/
int
NCD4_print(NCD4meta* metadata, NCbytes* output)
{
int ret = NC_NOERR;
D4printer out;
if(metadata == NULL || output == NULL) return THROW(NC_EINVAL);
out.out = output;
out.tmp = ncbytesnew();
out.metadata = metadata;
ret = printNode(&out,metadata->root,0);
ncbytesfree(out.tmp);
return THROW(ret);
}
/*************************************************/
/**
* Print an arbitrary file and its subnodes in xml
* Handling newlines is a bit tricky because they may be
* embedded for e.g. groups, enums,
* etc. So the rule is that the
* last newline is elided and left
* for the caller to print.
* Exceptions: printMetadata
* printDimrefs.
*
* @param out - the output buffer
* @param node - the tree to print
* @param depth - the depth of our code
*/
static int
printNode(D4printer* out, NCD4node* node, int depth)
{
int ret = NC_NOERR;
int i;
char* fqn = NULL;
switch (node->sort) {
case NCD4_GROUP:
if(node->group.isdataset)
printDataset(out,node,depth);
else
printGroup(out,node,depth);
break;
case NCD4_DIM:
INDENT(depth);
CAT("<Dimension");
if(node->name != NULL)
printXMLAttributeName(out, "name", node->name);
printXMLAttributeSize(out, "size", node->dim.size);
if(node->dim.isunlimited)
printXMLAttributeString(out, UCARTAGUNLIM, "1");
CAT("/>");
break;
case NCD4_TYPE:
switch (node->subsort) {
default: break;
case NC_OPAQUE:
INDENT(depth); CAT("<Opaque");
ncbytesclear(out->tmp);
printXMLAttributeName(out, "name", node->name);
if(node->opaque.size > 0)
printXMLAttributeSize(out, "size", node->opaque.size);
CAT("/>");
break;
case NC_ENUM:
INDENT(depth); CAT("<Enumeration");
printXMLAttributeName(out, "name", node->name);
if(node->basetype->subsort <= NC_MAX_ATOMIC_TYPE)
printXMLAttributeName(out, "basetype", node->basetype->name);
else {
char* fqn = NULL;
printXMLAttributeName(out, "basetype", (fqn = NCD4_makeFQN(node->basetype)));
nullfree(fqn);
}
CAT(">\n");
depth++;
for(i=0;i<nclistlength(node->en.econsts);i++) {
NCD4node* ec = (NCD4node*)nclistget(node->en.econsts,i);
INDENT(depth);
CAT("<EnumConst");
printXMLAttributeName(out, "name", ec->name);
printXMLAttributeAtomics(out, "value", &ec->en.ecvalue, node->basetype->subsort);
CAT("/>\n");
}
depth--;
INDENT(depth); CAT("</Enumeration>");
break;
case NC_STRUCT:
INDENT(depth);
CAT("<Structure");
printXMLAttributeName(out, "name", node->name);
CAT(">\n");
depth++;
for(i=0;i<nclistlength(node->vars);i++) {
NCD4node* field = (NCD4node*)nclistget(node->vars,i);
printVariable(out,field,depth);
CAT("\n");
}
if((ret=printMetaData(out,node,depth))) goto done;
depth--;
INDENT(depth);
CAT("</Structure>");
break;
case NC_SEQ:
INDENT(depth);
CAT("<Vlen");
printXMLAttributeName(out, "name", node->name);
printXMLAttributeName(out, "type", (fqn=NCD4_makeFQN(node->basetype)));
if(hasMetaData(node)) {
CAT(">\n");
depth++;
if((ret=printMetaData(out,node,depth))) goto done;
depth--;
INDENT(depth);
CAT("</Vlen>");
} else
CAT("/>");
break;
} break;
case NCD4_VAR: /* Only top-level vars are printed here */
if(ISTOPLEVEL(node)) {
if((ret=printVariable(out,node,depth))) goto done;
CAT("\n");
}
break;
default: abort(); break;
}
done:
nullfree(fqn);
return THROW(ret);
}
static int
printVariable(D4printer* out, NCD4node* var, int depth)
{
int ret = NC_NOERR;
NCD4node* basetype = var->basetype;
char* fqn = NULL;
INDENT(depth); CAT("<");
switch (var->subsort) {
default:
CAT(basetype->name);
printXMLAttributeName(out, "name", var->name);
break;
case NC_ENUM:
CAT("Enum");
printXMLAttributeName(out, "name", var->name);
printXMLAttributeName(out, "enum", (fqn=NCD4_makeFQN(basetype)));
break;
case NC_OPAQUE:
CAT("Opaque");
printXMLAttributeName(out, "name", var->name);
printXMLAttributeName(out, "type", (fqn=NCD4_makeFQN(basetype)));
break;
case NC_SEQ:
CAT("Seq");
printXMLAttributeName(out, "name", var->name);
printXMLAttributeName(out, "type", (fqn=NCD4_makeFQN(basetype)));
break;
case NC_STRUCT:
CAT("Struct");
printXMLAttributeName(out, "name", var->name);
printXMLAttributeName(out, "type", (fqn=NCD4_makeFQN(basetype)));
break;
}
if(hasMetaData(var)) {
CAT(">\n");
depth++;
if((ret=printMetaData(out,var,depth))) goto done;
depth--;
INDENT(depth); CAT("</");
if(basetype->subsort == NC_ENUM)
CAT("Enum");
else if(basetype->subsort == NC_OPAQUE)
CAT("Opaque");
else if(basetype->subsort == NC_STRUCT)
CAT("Struct");
else if(basetype->subsort == NC_SEQ)
CAT("Sequence");
else
CAT(basetype->name);
CAT(">");
} else
CAT("/>");
done:
nullfree(fqn);
return THROW(ret);
}
static int
printDataset(D4printer* out, NCD4node* node, int depth)
{
int ret = NC_NOERR;
CAT("<Dataset\n");
depth++;
INDENT(depth);
printXMLAttributeName(out,"name",node->group.datasetname);
CAT("\n");
INDENT(depth);
printXMLAttributeName(out,"dapVersion",node->group.dapversion);
CAT("\n");
INDENT(depth);
printXMLAttributeName(out,"dmrVersion",node->group.dmrversion);
CAT("\n");
INDENT(depth);
printXMLAttributeName(out,"xmlns","http://xml.opendap.org/ns/DAP/4.0#");
CAT("\n");
INDENT(depth);
printXMLAttributeName(out,"xmlns:dap","http://xml.opendap.org/ns/DAP/4.0#");
depth--;
CAT(">\n");
depth++;
ret = printGroupBody(out,node,depth);
depth--;
INDENT(depth);
CAT("</Dataset>");
return THROW(ret);
}
static int
printGroup(D4printer* out, NCD4node* node, int depth)
{
int ret = NC_NOERR;
INDENT(depth);
CAT("<Group");
printXMLAttributeName(out,"name",node->name);
CAT(">\n");
depth++;
ret = printGroupBody(out,node,depth);
depth--;
INDENT(depth);
CAT("</Group>");
return THROW(ret);
}
static int
printGroupBody(D4printer* out, NCD4node* node, int depth)
{
int ret = NC_NOERR;
int i,ngroups,nvars,ntypes,ndims,nattrs;
ngroups = nclistlength(node->groups);
nvars = nclistlength(node->vars);
ntypes = nclistlength(node->types);
ndims = nclistlength(node->dims);
nattrs = nclistlength(node->attributes);
if(ndims > 0) {
INDENT(depth);
CAT("<Dimensions>\n");
depth++;
for(i=0;i<nclistlength(node->dims);i++) {
NCD4node* dim = (NCD4node*)nclistget(node->dims,i);
printNode(out,dim,depth);
CAT("\n");
}
depth--;
INDENT(depth);
CAT("</Dimensions>\n");
}
if(ntypes > 0) {
INDENT(depth);
CAT("<Types>\n");
depth++;
for(i=0;i<nclistlength(node->types);i++) {
NCD4node* type = (NCD4node*)nclistget(node->types,i);
if(type->subsort <= NC_MAX_ATOMIC_TYPE) continue;
printNode(out,type,depth);
CAT("\n");
}
depth--;
INDENT(depth);
CAT("</Types>\n");
}
if(nvars > 0) {
INDENT(depth);
CAT("<Variables>\n");
depth++;
for(i=0;i<nclistlength(node->vars);i++) {
NCD4node* var = (NCD4node*)nclistget(node->vars,i);
printNode(out,var,depth);
}
depth--;
INDENT(depth);
CAT("</Variables>\n");
}
if(nattrs > 0) {
for(i=0;i<nclistlength(node->attributes);i++) {
NCD4node* attr = (NCD4node*)nclistget(node->attributes,i);
printAttribute(out,attr,depth);
CAT("\n");
}
}
if(ngroups > 0) {
INDENT(depth);
CAT("<Groups>\n");
depth++;
for(i=0;i<nclistlength(node->groups);i++) {
NCD4node* g = (NCD4node*)nclistget(node->groups,i);
printNode(out,g,depth);
CAT("\n");
}
depth--;
INDENT(depth);
CAT("</Groups>\n");
}
return THROW(ret);
}
static int
printMetaData(D4printer* out, NCD4node* node, int depth)
{
int ret = NC_NOERR;
int i;
if(nclistlength(node->dims) > 0) {
for(i=0;i<nclistlength(node->dims);i++) {
NCD4node* dim = (NCD4node*)nclistget(node->dims,i);
printDimref(out,dim,depth);
CAT("\n");
}
}
if(nclistlength(node->maps) > 0) {
for(i=0;i<nclistlength(node->maps);i++) {
NCD4node* mapref = (NCD4node*)nclistget(node->maps,i);
printMap(out,mapref,depth);
CAT("\n");
}
}
if(nclistlength(node->attributes) > 0) {
for(i=0;i<nclistlength(node->attributes);i++) {
NCD4node* attr = (NCD4node*)nclistget(node->attributes,i);
printAttribute(out,attr,depth);
CAT("\n");
}
}
return THROW(ret);
}
static int
printXMLAttributeName(D4printer* out, char* name, const char* value)
{
int ret = NC_NOERR;
char* escaped = NULL;
if(name == NULL) return THROW(ret);
if(value == NULL) value = "";
CAT(" "); CAT(name); CAT("=\"");
/* add xml entity escaping */
escaped = NCD4_entityescape(value);
CAT(escaped);
CAT("\"");
nullfree(escaped);
return THROW(ret);
}
static int
printXMLAttributeString(D4printer* out, char* name, const char* value)
{
int ret = NC_NOERR;
char* escaped = NULL;
if(name == NULL) return THROW(ret);
CAT(" "); CAT(name);
CAT("=");
CAT("\"");
if(value == NULL) value = "";
/* add xml entity escaping */
escaped = NCD4_entityescape(value);
CAT(escaped);
CAT("\"");
nullfree(escaped);
return THROW(ret);
}
static int
printXMLAttributeSize(D4printer* out, char* name, size_t value)
{
union ATOMICS atomics;
atomics.u64[0] = (unsigned long long)value;
return printXMLAttributeAtomics(out,name,&atomics,NC_UINT64);
}
static int
printXMLAttributeAtomics(D4printer* out, char* name, union ATOMICS* value, nc_type type)
{
int ret = NC_NOERR;
CAT(" "); CAT(name); CAT("=\"");
atomicsToString(out,value,type);
CAT(ncbytescontents(out->tmp));
CAT("\"");
return THROW(ret);
}
static int
printAttribute(D4printer* out, NCD4node* attr, int depth)
{
int ret = NC_NOERR;
int i = 0;
char* fqn = NULL;
INDENT(depth); CAT("<Attribute");
printXMLAttributeName(out,"name",attr->name);
if(attr->basetype->subsort <= NC_MAX_ATOMIC_TYPE)
printXMLAttributeName(out,"type",attr->basetype->name);
else {
printXMLAttributeName(out,"type",(fqn = NCD4_makeFQN(attr->basetype)));
}
CAT(">\n");
depth++;
for(i=0;i<nclistlength(attr->attr.values);i++) {
printValue(out,(const char*)nclistget(attr->attr.values,i),depth);
CAT("\n");
}
depth--;
INDENT(depth);
CAT("</Attribute>");
nullfree(fqn);
return THROW(ret);
}
/**
* Print the dimrefs for a variable's dimensions.
* If the variable has a non-whole projection, then use size
* else use the dimension name.
*
* @param var whole dimensions are to be printed
* @throws DapException
*/
static int
printDimref(D4printer* out, NCD4node* d, int depth)
{
char* fqn = NULL;
INDENT(depth);
CAT("<Dim");
printXMLAttributeName(out, "name", (fqn=NCD4_makeFQN(d)));
CAT("/>");
nullfree(fqn);
return THROW(NC_NOERR);
}
static int
printValue(D4printer* out, const char* value, int depth)
{
int ret = NC_NOERR;
INDENT(depth);
CAT("<Value");
printXMLAttributeString(out,"value",value);
CAT("/>");
return THROW(ret);
}
static int
printMap(D4printer* out, NCD4node* mapref, int depth)
{
char* fqn = NULL;
INDENT(depth);
CAT("<Map");
printXMLAttributeName(out, "name", (fqn=NCD4_makeFQN(mapref)));
CAT("/>");
nullfree(fqn);
return THROW(NC_NOERR);
}
/*************************************************/
/* Misc. Static Utilities */
static void
indent(D4printer* out, int depth)
{
while(depth-- > 0) ncbytescat(out->out," ");
}
static int
hasMetaData(NCD4node* node)
{
return (nclistlength(node->dims) > 0
|| nclistlength(node->attributes) > 0
|| nclistlength(node->maps) > 0);
}
static void
atomicsToString(D4printer* out, union ATOMICS* value, nc_type type)
{
char tmp[256];
ncbytesclear(out->tmp);
switch (type) {
case NC_CHAR:
snprintf(tmp,sizeof(tmp),"'%c'",value->i8[0]);
break;
case NC_BYTE:
snprintf(tmp,sizeof(tmp),"%d",value->i8[0]);
break;
case NC_UBYTE:
snprintf(tmp,sizeof(tmp),"%u",value->u8[0]);
break;
case NC_SHORT:
snprintf(tmp,sizeof(tmp),"%d",value->i16[0]);
break;
case NC_USHORT:
snprintf(tmp,sizeof(tmp),"%u",value->u16[0]);
break;
case NC_INT:
snprintf(tmp,sizeof(tmp),"%d",value->i32[0]);
break;
case NC_UINT:
snprintf(tmp,sizeof(tmp),"%u",value->u32[0]);
break;
case NC_INT64:
snprintf(tmp,sizeof(tmp),"%lld",value->i64[0]);
break;
case NC_UINT64:
snprintf(tmp,sizeof(tmp),"%llu",value->u64[0]);
break;
case NC_FLOAT:
snprintf(tmp,sizeof(tmp),"%g",value->f32[0]);
break;
case NC_DOUBLE:
snprintf(tmp,sizeof(tmp),"%g",value->f64[0]);
break;
case NC_STRING:
ncbytescat(out->tmp,"\"");
ncbytescat(out->tmp,value->s[0]);
ncbytescat(out->tmp,"\"");
break;
default: abort();
}
if(type != NC_STRING) ncbytescat(out->tmp,tmp);
ncbytesnull(out->tmp);
}