AttrTable.cc
// -*- mode: c++; c-basic-offset:4 -*-
// This file is part of libdap, A C++ implementation of the OPeNDAP Data
// Access Protocol.
// Copyright (c) 2002,2003 OPeNDAP, Inc.
// Author: James Gallagher <jgallagher@opendap.org>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
// (c) COPYRIGHT URI/MIT 1994-1999
// Please read the full copyright statement in the file COPYRIGHT_URI.
//
// Authors:
// jhrg,jimg James Gallagher <jgallagher@gso.uri.edu>
// jhrg 7/29/94
#include "config.h"
#include <cassert>
#include <sstream>
#include "AttrTable.h"
#include "DapIndent.h"
#include "escaping.h"
#include "util.h"
#include "debug.h"
// Should the www2id and id2www functions be used to encode attribute names?
// Probably not... jhrg 11/16/11
#define WWW_ENCODING 0
// See the note for del_attr_table(). That method now deletes the contained
// AttrTable.
#define NEW_DEL_ATTR_TABLE_BEHAVIOR 0
using std::cerr;
using std::endl;
using std::string;
using std::vector;
namespace libdap {
string remove_space_encoding(const string &s) {
string::size_type pos = s.find("%20");
if (pos != string::npos) {
string n = s;
do {
n.replace(pos, 3, " ");
pos = n.find("%20");
} while (pos != string::npos);
return n;
} else {
return s;
}
}
string add_space_encoding(const string &s) {
string::size_type pos = s.find(" ");
if (pos != string::npos) {
string n = s;
do {
n.replace(pos, 1, "%20");
pos = n.find(" ");
} while (pos != string::npos);
return n;
} else {
return s;
}
}
string AttrType_to_String(const AttrType at) {
switch (at) {
case Attr_container:
return "Container";
case Attr_byte:
return "Byte";
case Attr_int16:
return "Int16";
case Attr_uint16:
return "UInt16";
case Attr_int32:
return "Int32";
case Attr_uint32:
return "UInt32";
case Attr_float32:
return "Float32";
case Attr_float64:
return "Float64";
case Attr_string:
return "String";
case Attr_url:
return "Url";
case Attr_other_xml:
return "OtherXML";
//
// Added for DAP4
case Attr_int8:
return "Int8";
case Attr_uint8:
return "UInt8";
case Attr_int64:
return "Int64";
case Attr_uint64:
return "UInt64";
case Attr_enum:
return "Enumeration";
case Attr_opaque:
return "Opaque";
default:
return "";
}
}
AttrType String_to_AttrType(const string &s) {
string s2 = s;
downcase(s2);
if (s2 == "container")
return Attr_container;
else if (s2 == "byte")
return Attr_byte;
else if (s2 == "int16")
return Attr_int16;
else if (s2 == "uint16")
return Attr_uint16;
else if (s2 == "int32")
return Attr_int32;
else if (s2 == "uint32")
return Attr_uint32;
else if (s2 == "float32")
return Attr_float32;
else if (s2 == "float64")
return Attr_float64;
else if (s2 == "string")
return Attr_string;
else if (s2 == "url")
return Attr_url;
else if (s2 == "otherxml")
return Attr_other_xml;
//
// Added for DAP4
else if (s2 == "int8")
return Attr_int8;
else if (s2 == "uint8")
return Attr_uint8;
else if (s2 == "int64")
return Attr_int64;
else if (s2 == "uint64")
return Attr_uint64;
else if (s2 == "enumeration")
return Attr_enum;
else if (s2 == "opaque")
return Attr_opaque;
else
return Attr_unknown;
}
void AttrTable::clone(const AttrTable &at) {
d_name = at.d_name;
d_is_global_attribute = at.d_is_global_attribute;
// Set the parent to null (no parent, not in container)
// since using at.d_parent is semantically incorrect
// and potentially dangerous.
d_parent = 0;
Attr_citer i = at.attr_map.begin();
Attr_citer ie = at.attr_map.end();
for (; i != ie; ++i) {
// this deep-copies containers recursively
entry *e = new entry(*(*i));
attr_map.push_back(e);
// If the entry being added was a container,
// set its parent to this to maintain invariant.
if (e->type == Attr_container) {
assert(e->attributes);
e->attributes->d_parent = this;
}
}
}
AttrTable::AttrTable() : DapObj(), d_name(""), d_parent(0), attr_map(), d_is_global_attribute(true) {}
AttrTable::AttrTable(const AttrTable &rhs) : DapObj() { clone(rhs); }
// Private
void AttrTable::delete_attr_table() {
for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
delete *i;
}
attr_map.clear();
}
AttrTable::~AttrTable() { delete_attr_table(); }
AttrTable &AttrTable::operator=(const AttrTable &rhs) {
if (this != &rhs) {
delete_attr_table();
clone(rhs);
}
return *this;
}
unsigned int AttrTable::get_size() const { return attr_map.size(); }
string AttrTable::get_name() const { return d_name; }
void AttrTable::set_name(const string &n) {
#if WWW_ENCODING
d_name = www2id(n);
#else
d_name = remove_space_encoding(n);
#endif
}
#if 0
// This was taken from das.y and could be used here to make the 'dods_errors'
// attribute container like the parser used to. Then again, maybe this feature
// was just BS. jhrg (ticket 1469)
static void add_bad_attribute(AttrTable *attr, const string &type, const string &name, const string &value,
const string &msg) {
// First, if this bad value is already in a *_dods_errors container,
// then just add it. This can happen when the server side processes a DAS
// and then hands it off to a client which does the same.
// Make a new container. Call it <attr's name>_errors. If that container
// already exists, use it.
// Add the attribute.
// Add the error string to an attribute in the container called
// `<name_explanation.'.
if (attr->get_name().find("_dods_errors") != string::npos) {
attr->append_attr(name, type, value);
}
else {
// I think _dods_errors should be _dap_error. jhrg 11/16/11
string error_cont_name = attr->get_name() + "_dods_errors";
AttrTable *error_cont = attr->get_attr_table(error_cont_name);
if (!error_cont)
error_cont = attr->append_container(error_cont_name);
error_cont->append_attr(name, type, value);
#ifndef ATTR_STRING_QUOTE_FIX
error_cont->append_attr(name + "_dap_explanation", "String", "\"" + msg + "\"");
#else
error_cont->append_attr(name + "_dap_explanation", "String", msg);
#endif
}
}
#endif
unsigned int AttrTable::append_attr(const string &name, const string &type, const string &value) {
DBG(cerr << "Entering AttrTable::append_attr" << endl);
#if WWW_ENCODING
string lname = www2id(name);
#else
string lname = remove_space_encoding(name);
#endif
Attr_iter iter = simple_find(lname);
// If the types don't match OR this attribute is a container, calling
// this mfunc is an error!
if (iter != attr_map.end() && ((*iter)->type != String_to_AttrType(type)))
throw Error(string("An attribute called `") + name + string("' already exists but is of a different type"));
if (iter != attr_map.end() && (get_type(iter) == "Container"))
throw Error(string("An attribute called `") + name + string("' already exists but is a container."));
if (iter != attr_map.end()) { // Must be a new attribute value; add it.
(*iter)->attr->push_back(value);
return (*iter)->attr->size();
} else { // Must be a completely new attribute; add it
entry *e = new entry;
e->name = lname;
e->is_alias = false;
e->type = String_to_AttrType(type); // Record type using standard names.
e->attr = new vector<string>;
e->attr->push_back(value);
attr_map.push_back(e);
return e->attr->size(); // return the length of the attr vector
}
}
unsigned int AttrTable::append_attr(const string &name, const string &type, vector<string> *values) {
DBG(cerr << "Entering AttrTable::append_attr(..., vector)" << endl);
#if WWW_ENCODING
string lname = www2id(name);
#else
string lname = remove_space_encoding(name);
#endif
Attr_iter iter = simple_find(lname);
// If the types don't match OR this attribute is a container, calling
// this mfunc is an error!
if (iter != attr_map.end() && ((*iter)->type != String_to_AttrType(type)))
throw Error(string("An attribute called `") + name + string("' already exists but is of a different type"));
if (iter != attr_map.end() && (get_type(iter) == "Container"))
throw Error(string("An attribute called `") + name + string("' already exists but is a container."));
if (iter != attr_map.end()) { // Must be new attribute values; add.
vector<string>::iterator i = values->begin();
while (i != values->end())
(*iter)->attr->push_back(*i++);
return (*iter)->attr->size();
} else { // Must be a completely new attribute; add it
entry *e = new entry;
e->name = lname;
e->is_alias = false;
e->type = String_to_AttrType(type); // Record type using standard names.
e->attr = new vector<string>(*values);
attr_map.push_back(e);
return e->attr->size(); // return the length of the attr vector
}
}
unsigned int AttrTable::append_attr(const string &name, const string &type, const string &value, bool is_utf8_str) {
DBG(cerr << "Entering AttrTable::append_attr" << endl);
#if WWW_ENCODING
string lname = www2id(name);
#else
string lname = remove_space_encoding(name);
#endif
Attr_iter iter = simple_find(lname);
// If the types don't match OR this attribute is a container, calling
// this mfunc is an error!
if (iter != attr_map.end() && ((*iter)->type != String_to_AttrType(type)))
throw Error(string("An attribute called `") + name + string("' already exists but is of a different type"));
if (iter != attr_map.end() && (get_type(iter) == "Container"))
throw Error(string("An attribute called `") + name + string("' already exists but is a container."));
if (iter != attr_map.end()) { // Must be a new attribute value; add it.
(*iter)->attr->push_back(value);
(*iter)->is_utf8_str = is_utf8_str;
return (*iter)->attr->size();
} else { // Must be a completely new attribute; add it
entry *e = new entry;
e->name = lname;
e->is_alias = false;
e->type = String_to_AttrType(type); // Record type using standard names.
e->is_utf8_str = is_utf8_str;
e->attr = new vector<string>;
e->attr->push_back(value);
attr_map.push_back(e);
return e->attr->size(); // return the length of the attr vector
}
}
unsigned int AttrTable::append_attr(const string &name, const string &type, vector<string> *values, bool is_utf8_str) {
DBG(cerr << "Entering AttrTable::append_attr(..., vector)" << endl);
#if WWW_ENCODING
string lname = www2id(name);
#else
string lname = remove_space_encoding(name);
#endif
Attr_iter iter = simple_find(lname);
// If the types don't match OR this attribute is a container, calling
// this mfunc is an error!
if (iter != attr_map.end() && ((*iter)->type != String_to_AttrType(type)))
throw Error(string("An attribute called `") + name + string("' already exists but is of a different type"));
if (iter != attr_map.end() && (get_type(iter) == "Container"))
throw Error(string("An attribute called `") + name + string("' already exists but is a container."));
if (iter != attr_map.end()) { // Must be new attribute values; add.
vector<string>::iterator i = values->begin();
while (i != values->end())
(*iter)->attr->push_back(*i++);
(*iter)->is_utf8_str = is_utf8_str;
return (*iter)->attr->size();
} else { // Must be a completely new attribute; add it
entry *e = new entry;
e->name = lname;
e->is_alias = false;
e->type = String_to_AttrType(type); // Record type using standard names.
e->is_utf8_str = is_utf8_str;
e->attr = new vector<string>(*values);
attr_map.push_back(e);
return e->attr->size(); // return the length of the attr vector
}
}
AttrTable *AttrTable::append_container(const string &name) {
AttrTable *new_at = new AttrTable;
AttrTable *ret = NULL;
try {
ret = append_container(new_at, name);
} catch (Error &e) {
// an error occurred, attribute with that name already exists
delete new_at;
new_at = 0;
throw;
}
return ret;
}
AttrTable *AttrTable::append_container(AttrTable *at, const string &name) {
#if WWW_ENCODING
string lname = www2id(name);
#else
string lname = remove_space_encoding(name);
#endif
if (simple_find(name) != attr_end())
throw Error("There already exists a container called '" + name + "' in this attribute table (" +
at->get_name() + "). (1)");
DBG(cerr << "Setting appended attribute container name to: " << lname << endl);
at->set_name(lname);
entry *e = new entry;
e->name = lname;
e->is_alias = false;
e->type = Attr_container;
e->attributes = at;
attr_map.push_back(e);
at->d_parent = this;
return e->attributes;
}
void AttrTable::find(const string &target, AttrTable **at, Attr_iter *iter) {
string::size_type dotpos = target.rfind('.');
if (dotpos != string::npos) {
string container = target.substr(0, dotpos);
string field = target.substr(dotpos + 1);
*at = find_container(container);
if (*at) {
*iter = (*at)->simple_find(field);
} else {
*iter = attr_map.end();
}
} else {
*at = recurrsive_find(target, iter);
}
}
AttrTable *AttrTable::recurrsive_find(const string &target, Attr_iter *location) {
Attr_iter i = attr_begin();
while (i != attr_end()) {
if (target == (*i)->name) {
*location = i;
return this;
} else if ((*i)->type == Attr_container) {
AttrTable *at = (*i)->attributes->recurrsive_find(target, location);
if (at)
return at;
}
++i;
}
*location = i;
return 0;
}
// Made public for callers that want non-recursive find. [mjohnson 6 oct 09]
AttrTable::Attr_iter AttrTable::simple_find(const string &target) {
Attr_iter i;
for (i = attr_map.begin(); i != attr_map.end(); ++i) {
if (target == (*i)->name) {
break;
}
}
return i;
}
AttrTable *AttrTable::find_container(const string &target) {
string::size_type dotpos = target.find('.');
if (dotpos != string::npos) {
string container = target.substr(0, dotpos);
string field = target.substr(dotpos + 1);
AttrTable *at = simple_find_container(container);
return (at) ? at->find_container(field) : 0;
} else {
return simple_find_container(target);
}
}
// Made public for callers that want non-recursive find. [mjohnson 6 oct 09]
AttrTable *AttrTable::simple_find_container(const string &target) {
if (get_name() == target)
return this;
for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
if (is_container(i) && target == (*i)->name) {
return (*i)->attributes;
}
}
return 0;
}
AttrTable *AttrTable::get_attr_table(const string &name) { return find_container(name); }
string AttrTable::get_type(const string &name) {
Attr_iter p = simple_find(name);
return (p != attr_map.end()) ? get_type(p) : (string) "";
}
AttrType AttrTable::get_attr_type(const string &name) {
Attr_iter p = simple_find(name);
return (p != attr_map.end()) ? get_attr_type(p) : Attr_unknown;
}
unsigned int AttrTable::get_attr_num(const string &name) {
Attr_iter iter = simple_find(name);
return (iter != attr_map.end()) ? get_attr_num(iter) : 0;
}
vector<string> *AttrTable::get_attr_vector(const string &name) {
Attr_iter p = simple_find(name);
return (p != attr_map.end()) ? get_attr_vector(p) : 0;
}
void AttrTable::del_attr(const string &name, int i) {
#if WWW_ENCODING
string lname = www2id(name);
#else
string lname = remove_space_encoding(name);
#endif
Attr_iter iter = simple_find(lname);
if (iter != attr_map.end()) {
if (i == -1) { // Delete the whole attribute
entry *e = *iter;
attr_map.erase(iter);
delete e;
e = 0;
} else { // Delete one element from attribute array
// Don't try to delete elements from the vector of values if the
// map is a container!
if ((*iter)->type == Attr_container)
return;
vector<string> *sxp = (*iter)->attr;
assert(i >= 0 && i < (int)sxp->size());
sxp->erase(sxp->begin() + i); // rm the element
}
}
}
AttrTable::Attr_iter AttrTable::attr_begin() { return attr_map.begin(); }
AttrTable::Attr_iter AttrTable::attr_end() { return attr_map.end(); }
AttrTable::Attr_iter AttrTable::get_attr_iter(int i) { return attr_map.begin() + i; }
string AttrTable::get_name(Attr_iter iter) {
assert(iter != attr_map.end());
return (*iter)->name;
}
bool AttrTable::is_container(Attr_iter i) { return (*i)->type == Attr_container; }
AttrTable *AttrTable::get_attr_table(Attr_iter iter) {
assert(iter != attr_map.end());
return (*iter)->type == Attr_container ? (*iter)->attributes : 0;
}
AttrTable::Attr_iter AttrTable::del_attr_table(Attr_iter iter) {
if ((*iter)->type != Attr_container)
return ++iter;
// the caller intends to delete/reuse the contained AttrTable,
// so zero it out so it doesn't get deleted before we delete the entry
// [mjohnson]
struct entry *e = *iter;
// container no longer has a parent.
if (e->attributes) {
e->attributes->d_parent = 0;
#if NEW_DEL_ATTR_TABLE_BEHAVIOR
delete e->attributes;
#endif
e->attributes = 0;
}
delete e;
return attr_map.erase(iter);
}
string AttrTable::get_type(Attr_iter iter) {
assert(iter != attr_map.end());
return AttrType_to_String((*iter)->type);
}
AttrType AttrTable::get_attr_type(Attr_iter iter) { return (*iter)->type; }
unsigned int AttrTable::get_attr_num(Attr_iter iter) {
assert(iter != attr_map.end());
return ((*iter)->type == Attr_container) ? (*iter)->attributes->get_size() : (*iter)->attr->size();
}
string AttrTable::get_attr(Attr_iter iter, unsigned int i) {
assert(iter != attr_map.end());
return (*iter)->type == Attr_container ? (string) "None" : (*(*iter)->attr)[i];
}
string AttrTable::get_attr(const string &name, unsigned int i) {
Attr_iter p = simple_find(name);
return (p != attr_map.end()) ? get_attr(p, i) : (string) "";
}
vector<string> *AttrTable::get_attr_vector(Attr_iter iter) {
assert(iter != attr_map.end());
return (*iter)->type != Attr_container ? (*iter)->attr : 0;
}
bool AttrTable::is_global_attribute(Attr_iter iter) {
assert(iter != attr_map.end());
if ((*iter)->type == Attr_container)
return (*iter)->attributes->is_global_attribute();
else
return (*iter)->is_global;
}
void AttrTable::set_is_global_attribute(Attr_iter iter, bool ga) {
assert(iter != attr_map.end());
if ((*iter)->type == Attr_container)
(*iter)->attributes->set_is_global_attribute(ga);
else
(*iter)->is_global = ga;
}
// Alias an attribute table. The alias should be added to this object.
void AttrTable::add_container_alias(const string &name, AttrTable *src) {
#if WWW_ENCODING
string lname = www2id(name);
#else
string lname = remove_space_encoding(name);
#endif
if (simple_find(lname) != attr_end())
throw Error(string("There already exists a container called `") + name +
string("in this attribute table. (2)"));
entry *e = new entry;
e->name = lname;
e->is_alias = true;
e->aliased_to = src->get_name();
e->type = Attr_container;
e->attributes = src;
attr_map.push_back(e);
}
void AttrTable::add_value_alias(AttrTable *das, const string &name, const string &source) {
#if WWW_ENCODING
string lname = www2id(name);
#else
string lname = remove_space_encoding(name);
#endif
#if WWW_ENCODING
string lsource = www2id(source);
#else
string lsource = remove_space_encoding(source);
#endif
// find the container that holds source and its (sources's) iterator
// within that container. Search at the uppermost level of the attribute
// object to find values defined `above' the current container.
AttrTable *at;
Attr_iter iter;
das->find(lsource, &at, &iter);
// If source is not found by looking at the topmost level, look in the
// current table (i.e., alias z x where x is in the current container
// won't be found by looking for `x' at the top level). See test case 26
// in das-testsuite.
if (!at || (iter == at->attr_end()) || !*iter) {
find(lsource, &at, &iter);
if (!at || (iter == at->attr_end()) || !*iter)
throw Error(string("Could not find the attribute `") + source + string("' in the attribute object."));
}
// If we've got a value to alias and it's being added at the top level of
// the DAS, that's an error.
if (at && !at->is_container(iter) && this == das)
throw Error(string("A value cannot be aliased to the top level of the DAS;\nOnly containers may be present at "
"that level of the DAS."));
if (simple_find(lname) != attr_end())
throw Error(string("There already exists a container called `") + name +
string("in this attribute table. (3)"));
entry *e = new entry;
e->name = lname;
e->is_alias = true;
e->aliased_to = lsource;
e->type = get_attr_type(iter);
if (at && e->type == Attr_container)
e->attributes = at->get_attr_table(iter);
else
e->attr = (*iter)->attr;
attr_map.push_back(e);
}
// Deprecated
bool AttrTable::attr_alias(const string &alias, AttrTable *at, const string &name) {
add_value_alias(at, alias, name);
return true;
}
bool AttrTable::attr_alias(const string &alias, const string &name) { return attr_alias(alias, this, name); }
bool AttrTable::has_dap4_types(const std::string &path, std::vector<std::string> &inventory) const {
bool has_d4_attr = false;
for (auto attr : attr_map) {
string attr_fqn = path + "@" + attr->name;
bool isa_d4_attr = attr->is_dap4_type(attr_fqn, inventory);
if (isa_d4_attr) {
inventory.emplace_back(AttrType_to_String(attr->type) + " " + attr_fqn);
}
has_d4_attr |= isa_d4_attr;
}
return has_d4_attr;
}
void AttrTable::erase() {
for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
delete *i;
*i = 0;
}
attr_map.erase(attr_map.begin(), attr_map.end());
d_name = "";
}
const string double_quote = "\"";
// This is here as a result of the problem described in ticket #1163 where
// the data handlers are adding quotes to string attributes so the DAS will
// be printed correctly. But that has the affect of adding the quotes to the
// attribute's _value_ not just it's print representation. As part of the fix
// I made the code here add the quotes if the handlers are fixed (but not if
// handlers are still adding them). The other part of 1163 is to fix all of
// the handlers... What this fix means is that attributes whose values really
// do contain bracketing quotes might be misunderstood, since we're assuming
// those quotes were added by the handlers as a hack to get the output
// formatting correct for the DAS. jhrg 7/30/08
static void write_string_attribute_for_das(ostream &out, const string &value, const string &term, bool is_utf8_str) {
string esc_value = is_utf8_str ? value : escattr(value);
#if 0
string esc_value = escattr(value);
#endif
// The value is already escaped so the following check is not necessary. KY 2022-08-22
#if 0
if (is_quoted(esc_value))
out << esc_value << term;
else
#endif
out << double_quote << esc_value << double_quote << term;
}
#if 0
static void
write_string_attribute_for_das(FILE *out, const string &value, const string &term)
{
if (is_quoted(value))
fprintf(out, "%s%s", value.c_str(), term.c_str());
else
fprintf(out, "\"%s\"%s", value.c_str(), term.c_str());
}
#endif
// Special treatment for XML: Make sure to escape double quotes when XML is
// printed in a DAS.
static void write_xml_attribute_for_das(ostream &out, const string &value, const string &term) {
if (is_quoted(value))
out << escape_double_quotes(value) << term;
else
out << double_quote << escape_double_quotes(value) << double_quote << term;
}
#if 0
static void
write_xml_attribute_for_das(FILE *out, const string &value, const string &term)
{
if (is_quoted(value))
fprintf(out, "%s%s", escape_double_quotes(value).c_str(), term.c_str());
else
fprintf(out, "\"%s\"%s", escape_double_quotes(value).c_str(), term.c_str());
}
#endif
void AttrTable::simple_print(FILE *out, string pad, Attr_iter i, bool dereference) {
ostringstream oss;
simple_print(oss, pad, i, dereference);
fwrite(oss.str().data(), 1, oss.str().length(), out);
#if 0
switch ((*i)->type) {
case Attr_container:
#if WWW_ENCODING
fprintf(out, "%s%s {\n", pad.c_str(), id2www(get_name(i)).c_str());
#else
fprintf(out, "%s%s {\n", pad.c_str(), get_name(i).c_str());
#endif
(*i)->attributes->print(out, pad + " ", dereference);
fprintf(out, "%s}\n", pad.c_str());
break;
case Attr_string: {
#if WWW_ENCODING
fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), id2www(get_name(i)).c_str());
#else
fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), get_name(i).c_str());
#endif
vector<string> *sxp = (*i)->attr;
vector<string>::iterator last = sxp->end() - 1;
for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
write_string_attribute_for_das(out, *i, ", ");
}
write_string_attribute_for_das(out, *last, ";\n");
}
break;
case Attr_other_xml: {
#if WWW_ENCODING
fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), id2www(get_name(i)).c_str());
#else
fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), get_name(i).c_str());
#endif
vector<string> *sxp = (*i)->attr;
vector<string>::iterator last = sxp->end() - 1;
for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
write_xml_attribute_for_das(out, *i, ", ");
}
write_xml_attribute_for_das(out, *last, ";\n");
}
break;
default: {
#if WWW_ENCODING
fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), id2www(get_name(i)).c_str());
#else
fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), get_name(i).c_str());
#endif
vector<string> *sxp = (*i)->attr;
vector<string>::iterator last = sxp->end() - 1;
for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
fprintf(out, "%s%s", (*i).c_str(), ", ");
}
fprintf(out, "%s%s", (*last).c_str(), ";\n");
}
break;
}
#endif
}
void AttrTable::simple_print(ostream &out, string pad, Attr_iter i, bool dereference) {
switch ((*i)->type) {
case Attr_container:
#if WWW_ENCODING
out << pad << id2www(get_name(i)) << " {\n";
#else
out << pad << add_space_encoding(get_name(i)) << " {\n";
#endif
(*i)->attributes->print(out, pad + " ", dereference);
out << pad << "}\n";
break;
case Attr_string: {
#if WWW_ENCODING
out << pad << get_type(i) << " " << id2www(get_name(i)) << " ";
#else
out << pad << get_type(i) << " " << add_space_encoding(get_name(i)) << " ";
#endif
vector<string> *sxp = (*i)->attr;
vector<string>::iterator last = sxp->end() - 1;
for (vector<string>::iterator i_s = sxp->begin(); i_s != last; ++i_s) {
write_string_attribute_for_das(out, *i_s, ", ", (*i)->is_utf8_str);
}
write_string_attribute_for_das(out, *last, ";\n", (*i)->is_utf8_str);
// The following code is intended to replace the code above.
// However, it causes the obscure test failure at HDF5 handler's t_big_str.h5 DAS test.
// it also causes the 17th etc NcML tests failed.
// Leave here and may check this later. KY 2022-09-22
#if 0
for (const auto &attr:(*sxp)) {
if (attr != (*sxp).back())
write_string_attribute_for_das(out, attr, ", ",(*i)->is_utf8_str);
else
write_string_attribute_for_das(out, attr, ";\n",(*i)->is_utf8_str);
}
#endif
} break;
case Attr_other_xml: {
#if WWW_ENCODING
out << pad << get_type(i) << " " << id2www(get_name(i)) << " ";
#else
out << pad << get_type(i) << " " << add_space_encoding(get_name(i)) << " ";
#endif
vector<string> *sxp = (*i)->attr;
vector<string>::iterator last = sxp->end() - 1;
for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
write_xml_attribute_for_das(out, *i, ", ");
}
write_xml_attribute_for_das(out, *last, ";\n");
} break;
default: {
#if WWW_ENCODING
out << pad << get_type(i) << " " << id2www(get_name(i)) << " ";
#else
out << pad << get_type(i) << " " << add_space_encoding(get_name(i)) << " ";
#endif
vector<string> *sxp = (*i)->attr;
vector<string>::iterator last = sxp->end() - 1;
for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
out << *i << ", ";
}
out << *last << ";\n";
} break;
}
}
void AttrTable::print(FILE *out, string pad, bool dereference) {
ostringstream oss;
print(oss, pad, dereference);
fwrite(oss.str().data(), 1, oss.str().length(), out);
#if 0
for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
if ((*i)->is_alias) {
if (dereference) {
simple_print(out, pad, i, dereference);
}
else {
#if WWW_ENCODING
fprintf(out, "%sAlias %s %s;\n",
pad.c_str(),
id2www(get_name(i)).c_str(),
id2www((*i)->aliased_to).c_str());
#else
fprintf(out, "%sAlias %s %s;\n",
pad.c_str(), add_space_encoding(get_name(i)).c_str(), add_space_encoding((*i)->aliased_to).c_str());
#endif
}
}
else {
simple_print(out, pad, i, dereference);
}
}
#endif
}
void AttrTable::print(ostream &out, string pad, bool dereference) {
for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
if ((*i)->is_alias) {
if (dereference) {
simple_print(out, pad, i, dereference);
} else {
#if WWW_ENCODING
out << pad << "Alias " << id2www(get_name(i)) << " " << id2www((*i)->aliased_to) << ";\n";
#else
out << pad << "Alias " << add_space_encoding(get_name(i)) << " " << add_space_encoding((*i)->aliased_to)
<< ";\n";
#endif
}
} else {
simple_print(out, pad, i, dereference);
}
}
}
void AttrTable::print_xml(FILE *out, string pad, bool /*constrained*/) {
XMLWriter xml(pad);
print_xml_writer(xml);
fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
#if OLD_XML_MOETHODS
ostringstream oss;
print_xml(oss, pad);
fwrite(oss.str().data(), 1, oss.str().length(), out);
#endif
#if 0
// Why this works: AttrTable is really a hacked class that used to
// implement a single-level set of attributes. Containers
// were added several years later by dropping in the 'entry' structure.
// It's not a class in its own right; instead accessors from AttrTable
// are used to access information from entry. So... the loop below
// actually iterates over the entries of *this* (which is an instance of
// AttrTable). A container is an entry whose sole value is an AttrTable
// instance. 05/19/03 jhrg
for (Attr_iter i = attr_begin(); i != attr_end(); ++i) {
if ((*i)->is_alias) {
fprintf(out, "%s<Alias name=\"%s\" Attribute=\"%s\"/>\n",
pad.c_str(), id2xml(get_name(i)).c_str(),
(*i)->aliased_to.c_str());
}
else if (is_container(i)) {
fprintf(out, "%s<Attribute name=\"%s\" type=\"%s\">\n",
pad.c_str(), id2xml(get_name(i)).c_str(),
get_type(i).c_str());
get_attr_table(i)->print_xml(out, pad + " "/*, constrained*/);
fprintf(out, "%s</Attribute>\n", pad.c_str());
}
else {
fprintf(out, "%s<Attribute name=\"%s\" type=\"%s\">\n",
pad.c_str(), id2xml(get_name(i)).c_str(), get_type(i).c_str());
string value_pad = pad + " ";
// Special handling for the OtherXML attribute type - don't escape
// the XML and don't include the <value> element. Note that there
// cannot be an vector of XML things as can be with the other types.
if (get_attr_type(i) == Attr_other_xml) {
if (get_attr_num(i) != 1)
throw Error("OtherXML attributes cannot be vector-valued.");
fprintf(out, "%s%s\n", value_pad.c_str(), get_attr(i, 0).c_str());
}
else {
for (unsigned j = 0; j < get_attr_num(i); ++j) {
fprintf(out, "%s<value>%s</value>\n", value_pad.c_str(),
id2xml(get_attr(i, j)).c_str());
}
}
fprintf(out, "%s</Attribute>\n", pad.c_str());
}
}
#endif
}
void AttrTable::print_xml(ostream &out, string pad, bool /*constrained*/) {
XMLWriter xml(pad);
print_xml_writer(xml);
out << xml.get_doc();
#if 0
for (Attr_iter i = attr_begin(); i != attr_end(); ++i) {
if ((*i)->is_alias) {
out << pad << "<Alias name=\"" << id2xml(get_name(i))
<< "\" Attribute=\"" << (*i)->aliased_to << "\"/>\n";
}
else if (is_container(i)) {
out << pad << "<Attribute name=\"" << id2xml(get_name(i))
<< "\" type=\"" << get_type(i) << "\">\n";
get_attr_table(i)->print_xml(out, pad + " "/*, constrained*/);
out << pad << "</Attribute>\n";
}
else {
out << pad << "<Attribute name=\"" << id2xml(get_name(i))
<< "\" type=\"" << get_type(i) << "\">\n";
string value_pad = pad + " ";
if (get_attr_type(i) == Attr_other_xml) {
if (get_attr_num(i) != 1)
throw Error("OtherXML attributes cannot be vector-valued.");
out << value_pad << get_attr(i, 0) << "\n";
}
else {
string value_pad = pad + " ";
for (unsigned j = 0; j < get_attr_num(i); ++j) {
out << value_pad << "<value>" << id2xml(get_attr(i, j)) << "</value>\n";
}
}
out << pad << "</Attribute>\n";
}
}
#endif
}
void AttrTable::print_xml_writer(XMLWriter &xml) {
for (Attr_iter i = attr_begin(); i != attr_end(); ++i) {
if ((*i)->is_alias) {
if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar *)"Alias") < 0)
throw InternalErr(__FILE__, __LINE__, "Could not write Alias element");
if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"name",
(const xmlChar *)get_name(i).c_str()) < 0)
throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"Attribute",
(const xmlChar *)(*i)->aliased_to.c_str()) < 0)
throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
if (xmlTextWriterEndElement(xml.get_writer()) < 0)
throw InternalErr(__FILE__, __LINE__, "Could not end Alias element");
} else if (is_container(i)) {
if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar *)"Attribute") < 0)
throw InternalErr(__FILE__, __LINE__, "Could not write Attribute element");
if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"name",
(const xmlChar *)get_name(i).c_str()) < 0)
throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"type",
(const xmlChar *)get_type(i).c_str()) < 0)
throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
get_attr_table(i)->print_xml_writer(xml);
if (xmlTextWriterEndElement(xml.get_writer()) < 0)
throw InternalErr(__FILE__, __LINE__, "Could not end Attribute element");
} else {
if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar *)"Attribute") < 0)
throw InternalErr(__FILE__, __LINE__, "Could not write Attribute element");
if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"name",
(const xmlChar *)get_name(i).c_str()) < 0)
throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"type",
(const xmlChar *)get_type(i).c_str()) < 0)
throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
if (get_attr_type(i) == Attr_other_xml) {
if (get_attr_num(i) != 1)
throw Error("OtherXML attributes cannot be vector-valued.");
// Replaced xmltextWriterWriteString with xmlTextWriterWriteRaw to keep the
// libxml2 code from escaping the xml (which was breaking all of the inferencing
// code. jhrg
if (xmlTextWriterWriteRaw(xml.get_writer(), (const xmlChar *)get_attr(i, 0).c_str()) < 0)
throw InternalErr(__FILE__, __LINE__, "Could not write OtherXML value");
}
// Need to escape special characters that xml doesn't allow. Note: the XML escaping is
// not the same as the das string escaping. See escattr_xml in the escaping.cc for details.
// KY 08-22-22
else if (get_attr_type(i) == Attr_string || get_attr_type(i) == Attr_url) {
for (unsigned j = 0; j < get_attr_num(i); ++j) {
if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar *)"value") < 0)
throw InternalErr(__FILE__, __LINE__, "Could not write value element");
string s = ((*i)->is_utf8_str) ? get_attr(i, j) : escattr_xml(get_attr(i, j));
if (xmlTextWriterWriteString(xml.get_writer(), (const xmlChar *)s.c_str()) < 0)
throw InternalErr(__FILE__, __LINE__, "Could not write attribute value");
if (xmlTextWriterEndElement(xml.get_writer()) < 0)
throw InternalErr(__FILE__, __LINE__, "Could not end value element");
}
} else {
for (unsigned j = 0; j < get_attr_num(i); ++j) {
if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar *)"value") < 0)
throw InternalErr(__FILE__, __LINE__, "Could not write value element");
if (xmlTextWriterWriteString(xml.get_writer(), (const xmlChar *)get_attr(i, j).c_str()) < 0)
throw InternalErr(__FILE__, __LINE__, "Could not write attribute value");
if (xmlTextWriterEndElement(xml.get_writer()) < 0)
throw InternalErr(__FILE__, __LINE__, "Could not end value element");
}
}
if (xmlTextWriterEndElement(xml.get_writer()) < 0)
throw InternalErr(__FILE__, __LINE__, "Could not end Attribute element");
}
}
}
void AttrTable::print_dap4(XMLWriter &xml) { print_xml_writer(xml); }
void AttrTable::dump(ostream &strm) const {
strm << DapIndent::LMarg << "AttrTable::dump - (" << (void *)this << ")" << endl;
DapIndent::Indent();
strm << DapIndent::LMarg << "table name: " << d_name << endl;
if (attr_map.size()) {
strm << DapIndent::LMarg << "attributes: " << endl;
DapIndent::Indent();
Attr_citer i = attr_map.begin();
Attr_citer ie = attr_map.end();
for (; i != ie; ++i) {
entry *e = (*i);
string type = AttrType_to_String(e->type);
if (e->is_alias) {
strm << DapIndent::LMarg << "alias: " << e->name << " aliased to: " << e->aliased_to << endl;
} else if (e->type == Attr_container) {
strm << DapIndent::LMarg << "attr: " << e->name << " of type " << type << endl;
DapIndent::Indent();
e->attributes->dump(strm);
DapIndent::UnIndent();
} else {
strm << DapIndent::LMarg << "attr: " << e->name << " of type " << type << endl;
DapIndent::Indent();
strm << DapIndent::LMarg;
vector<string>::const_iterator iter = e->attr->begin();
vector<string>::const_iterator last = e->attr->end() - 1;
for (; iter != last; ++iter) {
strm << (*iter) << ", ";
}
strm << (*(e->attr->end() - 1)) << endl;
DapIndent::UnIndent();
}
}
DapIndent::UnIndent();
} else {
strm << DapIndent::LMarg << "attributes: empty" << endl;
}
if (d_parent) {
strm << DapIndent::LMarg << "parent table:" << d_name << ":" << (void *)d_parent << endl;
} else {
strm << DapIndent::LMarg << "parent table: none" << d_name << endl;
}
DapIndent::UnIndent();
}
} // namespace libdap
libdap::AttrTable
Contains the attributes for a dataset.
Definition AttrTable.h:150
libdap::AttrTable::append_container
virtual AttrTable * append_container(const string &name)
Add a container to the attribute table.
Definition AttrTable.cc:517
libdap::AttrTable::simple_print
void simple_print(FILE *out, string pad, Attr_iter i, bool dereference)
Definition AttrTable.cc:1178
libdap::AttrTable::get_attr_num
virtual unsigned int get_attr_num(const string &name)
Get the number of attributes in this container.
Definition AttrTable.cc:721
libdap::AttrTable::has_dap4_types
bool has_dap4_types(const std::string &path, std::vector< std::string > &inventory) const
Definition AttrTable.cc:1089
libdap::AttrTable::attr_alias
virtual bool attr_alias(const string &alias, AttrTable *at, const string &name)
Adds an alias to the set of attributes.
Definition AttrTable.cc:1069
libdap::AttrTable::is_container
virtual bool is_container(Attr_iter iter)
Definition AttrTable.cc:818
libdap::AttrTable::find
virtual void find(const string &target, AttrTable **at, Attr_iter *iter)
Definition AttrTable.cc:586
libdap::AttrTable::set_name
virtual void set_name(const string &n)
Set the name of this attribute table.
Definition AttrTable.cc:247
libdap::AttrTable::get_attr_table
virtual AttrTable * get_attr_table(const string &name)
Get an attribute container.
Definition AttrTable.cc:699
libdap::AttrTable::clone
void clone(const AttrTable &at)
Definition AttrTable.cc:182
libdap::AttrTable::attr_end
virtual Attr_iter attr_end()
Definition AttrTable.cc:798
libdap::AttrTable::print_xml
virtual void print_xml(FILE *out, string pad=" ", bool constrained=false)
Definition AttrTable.cc:1392
libdap::AttrTable::get_type
virtual string get_type(const string &name)
Get the type name of an attribute within this attribute table.
Definition AttrTable.cc:702
libdap::AttrTable::get_attr_vector
virtual vector< string > * get_attr_vector(const string &name)
Get a vector-valued attribute.
Definition AttrTable.cc:738
libdap::AttrTable::add_value_alias
virtual void add_value_alias(AttrTable *at, const string &name, const string &source)
Add an alias for an attribute.
Definition AttrTable.cc:997
libdap::AttrTable::append_attr
virtual unsigned int append_attr(const string &name, const string &type, const string &value)
Add an attribute to the table.
Definition AttrTable.cc:308
libdap::AttrTable::attr_begin
virtual Attr_iter attr_begin()
Definition AttrTable.cc:793
libdap::AttrTable::get_attr_iter
virtual Attr_iter get_attr_iter(int i)
Definition AttrTable.cc:808
libdap::AttrTable::print_dap4
void print_dap4(XMLWriter &xml)
Definition AttrTable.cc:1586
libdap::AttrTable::del_attr
virtual void del_attr(const string &name, int i=-1)
Deletes an attribute.
Definition AttrTable.cc:759
libdap::AttrTable::get_name
virtual string get_name() const
Get the name of this attribute table.
Definition AttrTable.cc:243
libdap::AttrTable::erase
virtual void erase()
Erase the attribute table.
Definition AttrTable.cc:1105
libdap::AttrTable::print_xml_writer
void print_xml_writer(XMLWriter &xml)
Definition AttrTable.cc:1502
libdap::AttrTable::del_attr_table
virtual Attr_iter del_attr_table(Attr_iter iter)
Definition AttrTable.cc:848
libdap::AttrTable::print
virtual void print(FILE *out, string pad=" ", bool dereference=false)
Prints the attribute table.
Definition AttrTable.cc:1327
libdap::AttrTable::add_container_alias
virtual void add_container_alias(const string &name, AttrTable *src)
Add an alias to a container held by this attribute table.
Definition AttrTable.cc:963
libdap::AttrTable::get_size
virtual unsigned int get_size() const
Get the number of entries in this attribute table.
Definition AttrTable.cc:239
libdap::AttrTable::dump
virtual void dump(ostream &strm) const
dumps information about this object
Definition AttrTable.cc:1595
libdap::AttrTable::find_container
virtual AttrTable * find_container(const string &target)
Find an attribute with a given name.
Definition AttrTable.cc:663
libdap::AttrTable::simple_find
Attr_iter simple_find(const string &target)
Definition AttrTable.cc:640
libdap::AttrTable::get_attr_type
virtual AttrType get_attr_type(const string &name)
Get the type of an attribute.
Definition AttrTable.cc:709
libdap::AttrTable::recurrsive_find
virtual AttrTable * recurrsive_find(const string &target, Attr_iter *location)
Definition AttrTable.cc:614
libdap::DapObj
libdap base object for common functionality of libdap objects
Definition DapObj.h:49
libdap::Error
A class for error processing.
Definition Error.h:92
libdap::InternalErr
A class for software fault reporting.
Definition InternalErr.h:61
libdap::XMLWriter
Definition XMLWriter.h:39
libdap
top level DAP object to house generic methods
Definition AlarmHandler.h:35
libdap::escattr
string escattr(string s)
Definition escaping.cc:342
libdap::escattr_xml
string escattr_xml(string s)
Definition escaping.cc:375
libdap::www2id
string www2id(const string &in, const string &escape, const string &except)
Definition escaping.cc:202
libdap::id2xml
string id2xml(string in, const string &not_allowed)
Definition escaping.cc:253
libdap::add_space_encoding
string add_space_encoding(const string &s)
Definition AttrTable.cc:76
libdap::downcase
void downcase(string &s)
Definition util.cc:544
libdap::AttrType_to_String
string AttrType_to_String(const AttrType at)
Definition AttrTable.cc:93
libdap::escape_double_quotes
string escape_double_quotes(string source)
Definition escaping.cc:466
libdap::remove_space_encoding
string remove_space_encoding(const string &s)
Definition AttrTable.cc:61
libdap::AttrType
AttrType
Definition AttrTable.h:78
libdap::is_quoted
bool is_quoted(const string &s)
Definition util.cc:554
libdap::id2www
string id2www(string in, const string &allowable)
Definition escaping.cc:143
libdap::AttrTable::entry
Definition AttrTable.h:158