libdap Updated for version 3.21.0
libdap4 is an implementation of OPeNDAP's DAP protocol.
D4Attributes.cc
1// -*- mode: c++; c-basic-offset:4 -*-
2
3// This file is part of libdap, A C++ implementation of the OPeNDAP Data
4// Access Protocol.
5
6// Copyright (c) 2013 OPeNDAP, Inc.
7// Author: James Gallagher <jgallagher@opendap.org>
8//
9// This library is free software; you can redistribute it and/or
10// modify it under the terms of the GNU Lesser General Public
11// License as published by the Free Software Foundation; either
12// version 2.1 of the License, or (at your option) any later version.
13//
14// This library is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17// Lesser General Public License for more details.
18//
19// You should have received a copy of the GNU Lesser General Public
20// License along with this library; if not, write to the Free Software
21// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22//
23// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
24
25#include "config.h"
26
27#include <algorithm>
28
29#include "D4Attributes.h"
30#include "D4AttributeType.h"
31#include "InternalErr.h"
32
33#include "AttrTable.h"
34
35#include "util.h"
36#include "escaping.h"
37#include "debug.h"
38#include "DapIndent.h"
39
40namespace libdap {
41
45string D4AttributeTypeToString(D4AttributeType at)
46{
47 switch(at) {
48 case attr_null_c:
49 return "null";
50
51 case attr_byte_c:
52 return "Byte";
53
54 case attr_int16_c:
55 return "Int16";
56
57 case attr_uint16_c:
58 return "UInt16";
59
60 case attr_int32_c:
61 return "Int32";
62
63 case attr_uint32_c:
64 return "UInt32";
65
66 case attr_float32_c:
67 return "Float32";
68
69 case attr_float64_c:
70 return "Float64";
71
72 case attr_str_c:
73 return "String";
74
75 case attr_url_c:
76 return "Url";
77
78 // Added for DAP4
79 case attr_int8_c:
80 return "Int8";
81
82 case attr_uint8_c:
83 return "UInt8";
84
85 case attr_int64_c:
86 return "Int64";
87
88 case attr_uint64_c:
89 return "UInt64";
90
91 case attr_enum_c:
92 return "Enum";
93
94 case attr_opaque_c:
95 return "Opaque";
96
97 // These are specific to attributes while the other types are
98 // also supported by the variables. jhrg 4/17/13
99 case attr_container_c:
100 return "Container";
101
102 case attr_otherxml_c:
103 return "OtherXML";
104
105 default:
106 throw InternalErr(__FILE__, __LINE__, "Unsupported attribute type");
107 }
108}
109
110D4AttributeType StringToD4AttributeType(string s)
111{
112 downcase(s);
113
114 if (s == "container")
115 return attr_container_c;
116
117 else if (s == "byte")
118 return attr_byte_c;
119 else if (s == "int8")
120 return attr_int8_c;
121 else if (s == "uint8")
122 return attr_uint8_c;
123 else if (s == "int16")
124 return attr_int16_c;
125 else if (s == "uint16")
126 return attr_uint16_c;
127 else if (s == "int32")
128 return attr_int32_c;
129 else if (s == "uint32")
130 return attr_uint32_c;
131 else if (s == "int64")
132 return attr_int64_c;
133 else if (s == "uint64")
134 return attr_uint64_c;
135
136 else if (s == "float32")
137 return attr_float32_c;
138 else if (s == "float64")
139 return attr_float64_c;
140
141 else if (s == "string")
142 return attr_str_c;
143 else if (s == "url")
144 return attr_url_c;
145 else if (s == "otherxml")
146 return attr_otherxml_c;
147 else
148 return attr_null_c;
149}
150
151void
152D4Attribute::m_duplicate(const D4Attribute &src)
153{
154 d_name = src.d_name;
155 d_type = src.d_type;
156 d_values = src.d_values;
157 if (src.d_attributes)
158 d_attributes = new D4Attributes(*src.d_attributes);
159 else
160 d_attributes = 0;
161}
162
163D4Attribute::D4Attribute(const D4Attribute &src)
164{
165 m_duplicate(src);
166}
167
168D4Attribute::~D4Attribute()
169{
170 delete d_attributes;
171}
172
173D4Attribute &
174D4Attribute::operator=(const D4Attribute &rhs)
175{
176 if (this == &rhs) return *this;
177 m_duplicate(rhs);
178 return *this;
179}
180
181D4Attributes *
182D4Attribute::attributes()
183{
184 if (!d_attributes) d_attributes = new D4Attributes();
185 return d_attributes;
186}
187
196void
198{
199 // for every attribute in at, copy it to this.
200 for (AttrTable::Attr_iter i = at.attr_begin(), e = at.attr_end(); i != e; ++i) {
201 string name = at.get_name(i);
202 AttrType type = at.get_attr_type(i);
203
204 switch (type) {
205 case Attr_container: {
206 D4Attribute *a = new D4Attribute(name, attr_container_c);
207 D4Attributes *attributes = a->attributes(); // allocates a new object
209 add_attribute_nocopy(a);
210 break;
211 }
212 case Attr_byte: {
213 D4Attribute *a = new D4Attribute(name, attr_byte_c);
214 a->add_value_vector(*at.get_attr_vector(i));
215 add_attribute_nocopy(a);
216 break;
217 }
218 case Attr_int16: {
219 D4Attribute *a = new D4Attribute(name, attr_int16_c);
220 a->add_value_vector(*at.get_attr_vector(i));
221 add_attribute_nocopy(a);
222 break;
223 }
224 case Attr_uint16: {
225 D4Attribute *a = new D4Attribute(name, attr_uint16_c);
226 a->add_value_vector(*at.get_attr_vector(i));
227 add_attribute_nocopy(a);
228 break;
229 }
230 case Attr_int32: {
231 D4Attribute *a = new D4Attribute(name, attr_int32_c);
232 a->add_value_vector(*at.get_attr_vector(i));
233 add_attribute_nocopy(a);
234 break;
235 }
236 case Attr_uint32: {
237 D4Attribute *a = new D4Attribute(name, attr_uint32_c);
238 a->add_value_vector(*at.get_attr_vector(i));
239 add_attribute_nocopy(a);
240 break;
241 }
242 case Attr_float32: {
243 D4Attribute *a = new D4Attribute(name, attr_float32_c);
244 a->add_value_vector(*at.get_attr_vector(i));
245 add_attribute_nocopy(a);
246 break;
247 }
248 case Attr_float64: {
249 D4Attribute *a = new D4Attribute(name, attr_float64_c);
250 a->add_value_vector(*at.get_attr_vector(i));
251 add_attribute_nocopy(a);
252 break;
253 }
254 case Attr_string: {
255 D4Attribute *a = new D4Attribute(name, attr_str_c);
256 a->set_utf8_str_flag((*i)->is_utf8_str);
257 a->add_value_vector(*at.get_attr_vector(i));
258 add_attribute_nocopy(a);
259 break;
260 }
261 case Attr_url: {
262 D4Attribute *a = new D4Attribute(name, attr_url_c);
263 a->add_value_vector(*at.get_attr_vector(i));
264 add_attribute_nocopy(a);
265 break;
266 }
267 case Attr_other_xml: {
268 D4Attribute *a = new D4Attribute(name, attr_otherxml_c);
269 a->add_value_vector(*at.get_attr_vector(i));
270 add_attribute_nocopy(a);
271 break;
272 }
273 default:
274 throw InternalErr(__FILE__, __LINE__, "Unknown DAP2 attribute type in D4Attributes::copy_from_dap2()");
275 }
276 }
277}
278
279
280AttrType get_dap2_AttrType(D4AttributeType d4_type) {
281 switch (d4_type) {
282 // These types are common between DAP4 and DAP2.
283 case attr_container_c: { return Attr_container; }
284 case attr_byte_c: { return Attr_byte; }
285 case attr_int16_c: { return Attr_int16; }
286 case attr_uint16_c: { return Attr_uint16; }
287 case attr_int32_c: { return Attr_int32; }
288 case attr_uint32_c: { return Attr_uint32; }
289 case attr_float32_c: { return Attr_float32; }
290 case attr_float64_c: { return Attr_float64; }
291 case attr_str_c: { return Attr_string; }
292 case attr_url_c: { return Attr_url; }
293 case attr_otherxml_c: { return Attr_other_xml; }
294 case attr_uint8_c: { return Attr_byte; }
295
296 // Types exclusive to DAP4
297 case attr_int8_c: { return Attr_int8; }
298 case attr_int64_c: { return Attr_int64; }
299 case attr_uint64_c: { return Attr_uint64; }
300 case attr_enum_c: { return Attr_enum; }
301 case attr_opaque_c: { return Attr_opaque; }
302 default:
303 throw InternalErr(__FILE__, __LINE__, "Unknown DAP4 attribute.");
304 }
305}
306
313{
314 // for every attribute in d4_attrs, copy it to d2_attr_table.
315 for (D4Attributes::D4AttributesIter i = attribute_begin(), e = attribute_end(); i != e; ++i) {
316 string name = (*i)->name();
317 D4AttributeType d4_attr_type = (*i)->type();
318 AttrType d2_attr_type = get_dap2_AttrType(d4_attr_type);
319 string d2_attr_type_name = AttrType_to_String(d2_attr_type);
320
321 switch (d4_attr_type) {
322 case attr_container_c: {
323 AttrTable *child_attr_table = new AttrTable();
324 child_attr_table->set_name(name);
325
326 (*i)->attributes()->transform_attrs_to_dap2(child_attr_table);
327 d2_attr_table->append_container(child_attr_table, name);
328 break;
329 }
330 default: {
331 for (D4Attribute::D4AttributeIter vi = (*i)->value_begin(), ve = (*i)->value_end(); vi != ve; vi++) {
332 d2_attr_table->append_attr(name, d2_attr_type_name, *vi,(*i)->get_utf8_str_flag());
333 }
334
335 break;
336 }
337 }
338 }
339}
340
341#if 0
351void D4Attributes::load_AttrTable(AttrTable *d2_attr_table, D4Attributes *d4_attrs)
352{
353 // for every attribute in d4_attrs, copy it to d2_attr_table.
354 for (D4Attributes::D4AttributesIter i = d4_attrs->attribute_begin(), e = d4_attrs->attribute_end(); i != e; ++i) {
355 string name = (*i)->name();
356 D4AttributeType d4_attr_type = (*i)->type();
357 string d2_attr_type_name = AttrType_to_String(get_dap2_AttrType(d4_attr_type));
358
359#if 0
360 D4Attribute::D4AttributeIter vitr = (*i)->value_begin();
361 D4Attribute::D4AttributeIter end = (*i)->value_end();
362
363 vector<string> values;
364 for (; vitr != end; vitr++) {
365 values.push_back((*vitr));
366 }
367#endif
368
369 switch (d4_attr_type) {
370 case attr_container_c: {
371 // Attr_container
372 AttrTable *child_attr_table = new AttrTable();
373 child_attr_table->set_name(name);
374
375 load_AttrTable(child_attr_table, (*i)->attributes());
376 d2_attr_table->append_container(child_attr_table, name);
377 break;
378 }
379 default: {
380 for (D4Attribute::D4AttributeIter vi = (*i)->value_begin(), ve = (*i)->value_end(); vi != ve; vi++) {
381 d2_attr_table->append_attr(name, d2_attr_type_name, *vi);
382 }
383
384 break;
385 }
386 }
387 }
388}
389
390
398AttrTable *D4Attributes::get_AttrTable(const string name)
399{
400 AttrTable *at = new AttrTable();
402#if 0
403 load_AttrTable(at, this);
404#endif
405 at->set_name(name);
406 return at;
407}
408#endif
409
410D4Attribute *
411D4Attributes::find_depth_first(const string &name, D4AttributesIter i)
412{
413 if (i == attribute_end())
414 return 0;
415 else if ((*i)->name() == name)
416 return *i;
417 else if ((*i)->type() == attr_container_c)
418 return find_depth_first(name, (*i)->attributes()->attribute_begin());
419 else
420 return find_depth_first(name, ++i);
421}
422
423D4Attribute *
424D4Attributes::find(const string &name)
425{
426 return find_depth_first(name, attribute_begin());
427}
428
432D4Attribute *
433D4Attributes::get(const string &fqn)
434{
435 // name1.name2.name3
436 // name1
437 // name1.name2
438 size_t pos = fqn.find('.');
439 string part = fqn.substr(0, pos);
440 string rest= "";
441
442 if (pos != string::npos)
443 rest = fqn.substr(pos + 1);
444
445 DBG(cerr << "part: '" << part << "'; rest: '" << rest << "'" << endl);
446
447 if (!part.empty()) {
448 if (!rest.empty()) {
449 D4AttributesIter i = attribute_begin();
450 while (i != attribute_end()) {
451 if ((*i)->name() == part && (*i)->type() == attr_container_c)
452 return (*i)->attributes()->get(rest);
453 ++i;
454 }
455 }
456 else {
457 D4AttributesIter i = attribute_begin();
458 while (i != attribute_end()) {
459 if ((*i)->name() == part)
460 return (*i);
461 ++i;
462 }
463 }
464 }
465
466 return 0;
467}
468
477void
479{
480 for (auto &attr: d_attrs) {
481 if (attr->name() == name) {
482 delete attr;
483 attr = nullptr;
484 }
485 }
486 d_attrs.erase(remove(d_attrs.begin(), d_attrs.end(), nullptr), d_attrs.end());
487}
488
493void
494D4Attributes::erase(const string &fqn)
495{
496 // name1.name2.name3; part is name1 and rest is name2.name3
497 // name1; part is name1 and rest is ""
498 // name1.name2; part is name1 and rest is name2
499 size_t pos = fqn.find('.');
500 string part = fqn.substr(0, pos);
501 string rest= "";
502
503 if (pos != string::npos)
504 rest = fqn.substr(pos + 1);
505
506 if (!part.empty()) {
507 if (!rest.empty()) {
508 // in this case, we are not looking for a leaf node, so descend the
509 // attribute container hierarchy.
510 for (auto &a: d_attrs) {
511 if (a->name() == part && a->type() == attr_container_c) {
512 a->attributes()->erase(rest);
513 }
514 }
515 }
516 else {
518 }
519 }
520}
521
522void
523D4Attribute::print_dap4(XMLWriter &xml) const
524{
525 if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Attribute") < 0)
526 throw InternalErr(__FILE__, __LINE__, "Could not write Attribute element");
527 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0)
528 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
529 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "type", (const xmlChar*) D4AttributeTypeToString(type()).c_str()) < 0)
530 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for type");
531
532 switch (type()) {
533 case attr_container_c:
534 // Just print xml when containing attributes. KY 2021-03-12
535 if (d_attributes)
536 d_attributes->print_dap4(xml);
537 break;
538
539 case attr_otherxml_c:
540 if (num_values() != 1)
541 throw Error("OtherXML attributes cannot be vector-valued.");
542 if (xmlTextWriterWriteRaw(xml.get_writer(), (const xmlChar*) value(0).c_str()) < 0)
543 throw InternalErr(__FILE__, __LINE__, "Could not write OtherXML value");
544 break;
545
546 case attr_url_c:
547 case attr_str_c: {
548
549 // Need to escape special characters that xml doesn't allow. Note: the XML escaping is
550 // not the same as the das string escaping. See escattr_xml in the escaping.cc for details.
551 // KY 08-22-22
552#if 0
553 D4AttributeCIter i = d_values.begin();//value_begin();
554#endif
555 auto i = d_values.begin();
556 while (i != d_values.end()) {
557 if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Value") < 0)
558 throw InternalErr(__FILE__, __LINE__, "Could not write value element");
559#if 0
560 if (xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) (*i++).c_str()) < 0)
561#endif
562 string s = (get_utf8_str_flag())?(*i++):escattr_xml(*i++);
563 if (xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) s.c_str()) < 0)
564 throw InternalErr(__FILE__, __LINE__, "Could not write attribute value");
565
566 if (xmlTextWriterEndElement(xml.get_writer()) < 0)
567 throw InternalErr(__FILE__, __LINE__, "Could not end value element");
568 }
569 break;
570 }
571
572
573 default: {
574 // Assume only valid types make it into instances
575 D4AttributeCIter i = d_values.begin();//value_begin();
576 while (i != d_values.end()) {
577 if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Value") < 0)
578 throw InternalErr(__FILE__, __LINE__, "Could not write value element");
579
580 if (xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) (*i++).c_str()) < 0)
581 throw InternalErr(__FILE__, __LINE__, "Could not write attribute value");
582
583 if (xmlTextWriterEndElement(xml.get_writer()) < 0)
584 throw InternalErr(__FILE__, __LINE__, "Could not end value element");
585 }
586
587 break;
588 }
589 }
590
591 if (xmlTextWriterEndElement(xml.get_writer()) < 0)
592 throw InternalErr(__FILE__, __LINE__, "Could not end Attribute element");
593}
594
603void
604D4Attribute::dump(ostream &strm) const
605{
606 strm << DapIndent::LMarg << "D4Attribute::dump - (" << (void *)this << ")" << endl;
607
608 DapIndent::Indent() ;
609
610 XMLWriter xml;
611 print_dap4(xml);
612 strm << DapIndent::LMarg << xml.get_doc() << flush;
613
614 DapIndent::UnIndent() ;
615}
616
617
618void
619D4Attributes::print_dap4(XMLWriter &xml) const
620{
621 if (empty())
622 return;
623
624 D4AttributesCIter i = d_attrs.begin();
625 while (i != d_attrs.end()) {
626 (*i++)->print_dap4(xml);
627 }
628}
629
630
637bool D4Attribute::is_dap4_type(const std::string &path, std::vector<std::string> &inventory)
638{
639 bool ima_d4_attr = false;
640 switch(type()){
641 case attr_int8_c:
642 case attr_int64_c:
643 case attr_uint64_c:
644 ima_d4_attr=true;
645 break;
646 case attr_container_c:
647 ima_d4_attr = attributes()->has_dap4_types(path ,inventory);
648 break;
649 default:
650 break;
651 }
652 return ima_d4_attr;
653}
654
661bool D4Attributes::has_dap4_types(const std::string &path, std::vector<std::string> &inventory) const
662{
663 bool has_d4_attr = false;
664 for (const auto attr: attributes()) {
665 string attr_fqn = path + "@" + attr->name();
666 bool isa_d4_attr = attr->is_dap4_type(attr_fqn, inventory);
667 if(isa_d4_attr){
668 inventory.emplace_back(D4AttributeTypeToString(attr->type()) + " " + attr_fqn);
669 }
670 has_d4_attr |= isa_d4_attr;
671 }
672 return has_d4_attr;
673}
674
683void
684D4Attributes::dump(ostream &strm) const
685{
686 strm << DapIndent::LMarg << "D4Attributes::dump - (" << (void *)this << ")" << endl;
687
688 DapIndent::Indent() ;
689
690 XMLWriter xml;
691 print_dap4(xml);
692 strm << DapIndent::LMarg << xml.get_doc() << flush;
693
694 DapIndent::UnIndent() ;
695}
696
697
698} // namespace libdap
699
Contains the attributes for a dataset.
Definition AttrTable.h:154
virtual AttrTable * append_container(const string &name)
Add a container to the attribute table.
Definition AttrTable.cc:554
virtual void set_name(const string &n)
Set the name of this attribute table.
Definition AttrTable.cc:273
virtual AttrTable * get_attr_table(const string &name)
Get an attribute container.
Definition AttrTable.cc:751
virtual Attr_iter attr_end()
Definition AttrTable.cc:863
virtual vector< string > * get_attr_vector(const string &name)
Get a vector-valued attribute.
Definition AttrTable.cc:797
virtual unsigned int append_attr(const string &name, const string &type, const string &value)
Add an attribute to the table.
Definition AttrTable.cc:335
virtual Attr_iter attr_begin()
Definition AttrTable.cc:855
virtual string get_name() const
Get the name of this attribute table.
Definition AttrTable.cc:266
virtual AttrType get_attr_type(const string &name)
Get the type of an attribute.
Definition AttrTable.cc:765
bool is_dap4_type(const std::string &path, std::vector< std::string > &inventory)
virtual void dump(ostream &strm) const
dumps information about this object
void erase(const string &fqn)
Erase the given attribute.
void transform_to_dap4(AttrTable &at)
copy attributes from DAP2 to DAP4
D4Attribute * get(const string &fqn)
const vector< D4Attribute * > & attributes() const
D4AttributesIter attribute_begin()
Get an iterator to the start of the enumerations.
D4AttributesIter attribute_end()
Get an iterator to the end of the enumerations.
void transform_attrs_to_dap2(AttrTable *d2_attr_table)
Copy the attributes from this D4Attributes object to a DAP2 AttrTable.
bool has_dap4_types(const std::string &path, std::vector< std::string > &inventory) const
virtual void dump(ostream &strm) const
dumps information about this object
void erase_named_attribute(const string &name)
Erase an attribute from a specific container This method expects to find 'name' in the D4Attributes o...
A class for error processing.
Definition Error.h:94
A class for software fault reporting.
Definition InternalErr.h:65
top level DAP object to house generic methods
Definition AISConnect.cc:30
string escattr_xml(string s)
Definition escaping.cc:402
void downcase(string &s)
Definition util.cc:566
string AttrType_to_String(const AttrType at)
Definition AttrTable.cc:97
string D4AttributeTypeToString(D4AttributeType at)