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) 2002,2003 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 //
23 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
24
25 // (c) COPYRIGHT URI/MIT 1994-1999
26 // Please read the full copyright statement in the file COPYRIGHT_URI.
27 //
28 // Authors:
29 // jhrg,jimg James Gallagher <jgallagher@gso.uri.edu>
30
31 // jhrg 7/29/94
32
33 #include "config.h"
34
35 #include <cassert>
36 #include <sstream>
37
38 #include "AttrTable.h"
39
40 #include "util.h"
41 #include "escaping.h"
42 #include "DapIndent.h"
43
44 #include "debug.h"
45
46 // Should the www2id and id2www functions be used to encode attribute names?
47 // Probably not... jhrg 11/16/11
48 #define WWW_ENCODING 0
49 // See the note for del_attr_table(). That method now deletes the contained
50 // AttrTable.
51 #define NEW_DEL_ATTR_TABLE_BEHAVIOR 0
52
53 using std::cerr;
54 using std::string;
55 using std::endl;
56 using std::vector;
57
58 namespace libdap {
59
60 /** Remove %20 space encoding */
remove_space_encoding(const string & s)61 string remove_space_encoding(const string &s)
62 {
63 string::size_type pos = s.find("%20");
64 if (pos != string::npos) {
65 string n = s;
66 do {
67 n.replace(pos, 3, " ");
68 pos = n.find("%20");
69 } while (pos != string::npos);
70 return n;
71 }
72 else {
73 return s;
74 }
75 }
76
77 /** Add %20 space encoding. */
add_space_encoding(const string & s)78 string add_space_encoding(const string &s)
79 {
80 string::size_type pos = s.find(" ");
81 if (pos != string::npos) {
82 string n = s;
83 do {
84 n.replace(pos, 1, "%20");
85 pos = n.find(" ");
86 } while (pos != string::npos);
87 return n;
88 }
89 else {
90 return s;
91 }
92 }
93
94 /** Convert an AttrType to it's string representation.
95 @param at The Attribute Type.
96 @return The type's string representation */
AttrType_to_String(const AttrType at)97 string AttrType_to_String(const AttrType at)
98 {
99 switch (at) {
100 case Attr_container:
101 return "Container";
102 case Attr_byte:
103 return "Byte";
104 case Attr_int16:
105 return "Int16";
106 case Attr_uint16:
107 return "UInt16";
108 case Attr_int32:
109 return "Int32";
110 case Attr_uint32:
111 return "UInt32";
112 case Attr_float32:
113 return "Float32";
114 case Attr_float64:
115 return "Float64";
116 case Attr_string:
117 return "String";
118 case Attr_url:
119 return "Url";
120 case Attr_other_xml:
121 return "OtherXML";
122 default:
123 return "";
124 }
125 }
126
String_to_AttrType(const string & s)127 AttrType String_to_AttrType(const string &s)
128 {
129 string s2 = s;
130 downcase(s2);
131
132 if (s2 == "container")
133 return Attr_container;
134 else if (s2 == "byte")
135 return Attr_byte;
136 else if (s2 == "int16")
137 return Attr_int16;
138 else if (s2 == "uint16")
139 return Attr_uint16;
140 else if (s2 == "int32")
141 return Attr_int32;
142 else if (s2 == "uint32")
143 return Attr_uint32;
144 else if (s2 == "float32")
145 return Attr_float32;
146 else if (s2 == "float64")
147 return Attr_float64;
148 else if (s2 == "string")
149 return Attr_string;
150 else if (s2 == "url")
151 return Attr_url;
152 else if (s2 == "otherxml")
153 return Attr_other_xml;
154 else
155 return Attr_unknown;
156 }
157
158 /** Clone the given attribute table in <tt>this</tt>.
159 Protected. */
clone(const AttrTable & at)160 void AttrTable::clone(const AttrTable &at)
161 {
162 d_name = at.d_name;
163 d_is_global_attribute = at.d_is_global_attribute;
164
165 // Set the parent to null (no parent, not in container)
166 // since using at.d_parent is semantically incorrect
167 // and potentially dangerous.
168 d_parent = 0;
169
170 Attr_citer i = at.attr_map.begin();
171 Attr_citer ie = at.attr_map.end();
172 for (; i != ie; ++i) {
173 // this deep-copies containers recursively
174 entry *e = new entry(*(*i));
175 attr_map.push_back(e);
176
177 // If the entry being added was a container,
178 // set its parent to this to maintain invariant.
179 if (e->type == Attr_container) {
180 assert(e->attributes);
181 e->attributes->d_parent = this;
182 }
183 }
184 }
185
186 /** @name Instance management functions */
187
188 //@{
AttrTable()189 AttrTable::AttrTable() :
190 DapObj(), d_name(""), d_parent(0), attr_map(), d_is_global_attribute(true)
191 {
192 }
193
AttrTable(const AttrTable & rhs)194 AttrTable::AttrTable(const AttrTable &rhs) :
195 DapObj()
196 {
197 clone(rhs);
198 }
199
200 // Private
delete_attr_table()201 void AttrTable::delete_attr_table()
202 {
203 for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
204 delete *i;
205 }
206 attr_map.clear();
207 }
208
~AttrTable()209 AttrTable::~AttrTable()
210 {
211 delete_attr_table();
212 }
213
214 AttrTable &
operator =(const AttrTable & rhs)215 AttrTable::operator=(const AttrTable &rhs)
216 {
217 if (this != &rhs) {
218 delete_attr_table();
219 clone(rhs);
220 }
221
222 return *this;
223 }
224 //@}
225
226 /** Attributes that are containers count as one attribute, as do
227 attributes with both scalar and vector values.
228 @return The number of entries.
229 @brief Get the number of entries in this attribute table.
230 */
get_size() const231 unsigned int AttrTable::get_size() const
232 {
233 return attr_map.size();
234 }
235
236 /** @brief Get the name of this attribute table.
237 @return A string containing the name. */
get_name() const238 string AttrTable::get_name() const
239 {
240 return d_name;
241 }
242
243 /** @brief Set the name of this attribute table.
244 @param n The new name of the attribute table. */
set_name(const string & n)245 void AttrTable::set_name(const string &n)
246 {
247 #if WWW_ENCODING
248 d_name = www2id(n);
249 #else
250 d_name = remove_space_encoding(n);
251 #endif
252 }
253
254 #if 0
255 // This was taken from das.y and could be used here to make the 'dods_errors'
256 // attribute container like the parser used to. Then again, maybe this feature
257 // was just BS. jhrg (ticket 1469)
258 static void add_bad_attribute(AttrTable *attr, const string &type, const string &name, const string &value,
259 const string &msg) {
260 // First, if this bad value is already in a *_dods_errors container,
261 // then just add it. This can happen when the server side processes a DAS
262 // and then hands it off to a client which does the same.
263 // Make a new container. Call it <attr's name>_errors. If that container
264 // already exists, use it.
265 // Add the attribute.
266 // Add the error string to an attribute in the container called
267 // `<name_explanation.'.
268
269 if (attr->get_name().find("_dods_errors") != string::npos) {
270 attr->append_attr(name, type, value);
271 }
272 else {
273 // I think _dods_errors should be _dap_error. jhrg 11/16/11
274 string error_cont_name = attr->get_name() + "_dods_errors";
275 AttrTable *error_cont = attr->get_attr_table(error_cont_name);
276 if (!error_cont)
277 error_cont = attr->append_container(error_cont_name);
278
279 error_cont->append_attr(name, type, value);
280
281 #ifndef ATTR_STRING_QUOTE_FIX
282 error_cont->append_attr(name + "_dap_explanation", "String", "\"" + msg + "\"");
283 #else
284 error_cont->append_attr(name + "_dap_explanation", "String", msg);
285 #endif
286 }
287 }
288 #endif
289
290 /** If the given name already refers to an attribute, and the attribute has a
291 value, the given value is appended to the attribute vector. Calling this
292 function repeatedly is the way to append to an attribute vector.
293
294 The function throws an Error if the attribute is a container,
295 or if the type of the input value does not match the existing attribute's
296 type. Use <tt>append_container()</tt> to add container attributes.
297
298 This method performs a simple search for <tt>name</tt> in this attribute
299 table only; sub-tables are not searched and the dot notation is not
300 recognized.
301
302 @brief Add an attribute to the table.
303 @return Returns the length of the added attribute value.
304 @param name The name of the attribute to add or modify.
305 @param type The type of the attribute to add or modify.
306 @param value The value to add to the attribute table. */
append_attr(const string & name,const string & type,const string & value)307 unsigned int AttrTable::append_attr(const string &name, const string &type, const string &value)
308 {
309 DBG(cerr << "Entering AttrTable::append_attr" << endl);
310 #if WWW_ENCODING
311 string lname = www2id(name);
312 #else
313 string lname = remove_space_encoding(name);
314 #endif
315
316 Attr_iter iter = simple_find(lname);
317
318 // If the types don't match OR this attribute is a container, calling
319 // this mfunc is an error!
320 if (iter != attr_map.end() && ((*iter)->type != String_to_AttrType(type)))
321 throw Error(string("An attribute called `") + name + string("' already exists but is of a different type"));
322 if (iter != attr_map.end() && (get_type(iter) == "Container"))
323 throw Error(string("An attribute called `") + name + string("' already exists but is a container."));
324
325 if (iter != attr_map.end()) { // Must be a new attribute value; add it.
326 (*iter)->attr->push_back(value);
327 return (*iter)->attr->size();
328 }
329 else { // Must be a completely new attribute; add it
330 entry *e = new entry;
331
332 e->name = lname;
333 e->is_alias = false;
334 e->type = String_to_AttrType(type); // Record type using standard names.
335 e->attr = new vector<string> ;
336 e->attr->push_back(value);
337
338 attr_map.push_back(e);
339
340 return e->attr->size(); // return the length of the attr vector
341 }
342 }
343
344 /** This version of append_attr() takes a vector<string> of values.
345 If the given name already refers to an attribute, and the attribute has
346 values, append the new values to the existing ones.
347
348 The function throws an Error if the attribute is a container,
349 or if the type of the input value does not match the existing attribute's
350 type. Use <tt>append_container()</tt> to add container attributes.
351
352 This method performs a simple search for <tt>name</tt> in this attribute
353 table only; sub-tables are not searched and the dot notation is not
354 recognized.
355
356 @brief Add an attribute to the table.
357 @return Returns the length of the added attribute value.
358 @param name The name of the attribute to add or modify.
359 @param type The type of the attribute to add or modify.
360 @param values A vector of values. Note: The vector is COPIED, not stored. */
361
append_attr(const string & name,const string & type,vector<string> * values)362 unsigned int AttrTable::append_attr(const string &name, const string &type, vector<string> *values)
363 {
364 DBG(cerr << "Entering AttrTable::append_attr(..., vector)" << endl);
365 #if WWW_ENCODING
366 string lname = www2id(name);
367 #else
368 string lname = remove_space_encoding(name);
369 #endif
370 Attr_iter iter = simple_find(lname);
371
372 // If the types don't match OR this attribute is a container, calling
373 // this mfunc is an error!
374 if (iter != attr_map.end() && ((*iter)->type != String_to_AttrType(type)))
375 throw Error(string("An attribute called `") + name + string("' already exists but is of a different type"));
376 if (iter != attr_map.end() && (get_type(iter) == "Container"))
377 throw Error(string("An attribute called `") + name + string("' already exists but is a container."));
378
379 if (iter != attr_map.end()) { // Must be new attribute values; add.
380 vector<string>::iterator i = values->begin();
381 while (i != values->end())
382 (*iter)->attr->push_back(*i++);
383
384 return (*iter)->attr->size();
385 }
386 else { // Must be a completely new attribute; add it
387 entry *e = new entry;
388
389 e->name = lname;
390 e->is_alias = false;
391 e->type = String_to_AttrType(type); // Record type using standard names.
392 e->attr = new vector<string> (*values);
393
394 attr_map.push_back(e);
395
396 return e->attr->size(); // return the length of the attr vector
397 }
398 }
399
400 /** Create and append an attribute container to this AttrTable. If this
401 attribute table already contains an attribute container called
402 <tt>name</tt> an exception is thrown. Return a pointer to the new container.
403
404 @brief Add a container to the attribute table.
405 @param name The name of the container to create.
406 @return A pointer to the new AttrTable object.
407 */
408
409 AttrTable *
append_container(const string & name)410 AttrTable::append_container(const string &name)
411 {
412 AttrTable *new_at = new AttrTable;
413 AttrTable *ret = NULL;
414 try {
415 ret = append_container(new_at, name);
416 } catch (Error &e) {
417 // an error occurred, attribute with that name already exists
418 delete new_at;
419 new_at = 0;
420 throw;
421 }
422 return ret;
423 }
424
425 /** Append a new attribute container to this attribute table. The new
426 container is <tt>at</tt> and its name is set to
427 <tt>name</tt>. If this attribute
428 table already contains an attribute container called
429 <tt>name</tt> an exception is thrown.
430
431 @note The value of \e name will override the name of \e at set using the
432 set_name() method.
433
434 @brief Add a container to the attribute table.
435 @param at A pointer to the new attribute table to append.
436 @param name The name of the new attribute table.
437 @return A pointer to the new AttrTable object.
438 */
439 AttrTable *
append_container(AttrTable * at,const string & name)440 AttrTable::append_container(AttrTable *at, const string &name)
441 {
442 #if WWW_ENCODING
443 string lname = www2id(name);
444 #else
445 string lname = remove_space_encoding(name);
446 #endif
447
448 if (simple_find(name) != attr_end())
449 throw Error("There already exists a container called '" + name + "' in this attribute table (" + at->get_name() + "). (1)");
450
451 DBG(cerr << "Setting appended attribute container name to: " << lname << endl);
452 at->set_name(lname);
453
454 entry *e = new entry;
455 e->name = lname;
456 e->is_alias = false;
457 e->type = Attr_container;
458 e->attributes = at;
459
460 attr_map.push_back(e);
461
462 at->d_parent = this;
463
464 return e->attributes;
465 }
466
467 /** Look for an attribute or an attribute container. If used to search
468 for an attribute container, this method returns the container's \e
469 parent using the value-result parameter \c at and a reference to the
470 container using the iterator value-result parameter \c iter. If used
471 to search for an attribute, the attribute's container is returned using
472 \c at; the attribute itself can be accessed using the iterator \c iter.
473
474 @param target The name (using dot notation) of the attribute or
475 container to find.
476 @param at A value-result used to return the attribute container in
477 which \c target was found. Null if \c target was not found.
478 @param iter The iterator which will reference the attribute found.
479 Can be used to access \c target from within \c at. References
480 dim_end() within \c at if the attribute or container does not exist. */
find(const string & target,AttrTable ** at,Attr_iter * iter)481 void AttrTable::find(const string &target, AttrTable **at, Attr_iter *iter)
482 {
483 string::size_type dotpos = target.rfind('.');
484 if (dotpos != string::npos) {
485 string container = target.substr(0, dotpos);
486 string field = target.substr(dotpos + 1);
487
488 *at = find_container(container);
489 if (*at) {
490 *iter = (*at)->simple_find(field);
491 }
492 else {
493 *iter = attr_map.end();
494 }
495 }
496 else {
497 *at = recurrsive_find(target, iter);
498 }
499 }
500
501 /** This method scans for attributes using recursion to look inside containers
502 even when the name of the attribute is not fully qualified. It starts
503 looking in itself and descends into its children depth first. It will find
504 attributes and attribute containers.
505
506 @param target Look for the attribute with this name.
507 @param location A value-result parameter. This returns an iterator to the
508 attribute within the returned AttrTable object
509 @return Returns a pointer to the AttrTable which holds \e target, or null
510 if \e target is not found. In the latter case, the value of \e location is
511 attr_end() for this AttrTable. */
512 AttrTable *
recurrsive_find(const string & target,Attr_iter * location)513 AttrTable::recurrsive_find(const string &target, Attr_iter *location)
514 {
515 Attr_iter i = attr_begin();
516 while (i != attr_end()) {
517 if (target == (*i)->name) {
518 *location = i;
519 return this;
520 }
521 else if ((*i)->type == Attr_container) {
522 AttrTable *at = (*i)->attributes->recurrsive_find(target, location);
523 if (at)
524 return at;
525 }
526
527 ++i;
528 }
529
530 *location = i;
531 return 0;
532 }
533
534 // Made public for callers that want non-recursive find. [mjohnson 6 oct 09]
535 /** Look in this AttrTable for the attribute called \c name. If found return
536 an Attr_iter which references it, otherwise return the end iterator for
537 this AttrTable.
538
539 @param target The name of the attribute.
540 @return An Attr_iter which references \c target. */
simple_find(const string & target)541 AttrTable::Attr_iter AttrTable::simple_find(const string &target)
542 {
543 Attr_iter i;
544 for (i = attr_map.begin(); i != attr_map.end(); ++i) {
545 if (target == (*i)->name) {
546 break;
547 }
548 }
549 return i;
550 }
551
552 /** Look in this attribute table for an attribute container named
553 <tt>target</tt>. The search starts at this attribute table;
554 <tt>target</tt> should
555 use the dot notation to name containers held within children of this
556 attribute table.
557
558 To search the entire DAS object, make sure to invoke this method from
559 that object.
560
561 @brief Find an attribute with a given name.
562 @param target The attribute container to find.
563 @return A pointer to the attribute table or null if the container
564 cannot be found. */
565 AttrTable *
find_container(const string & target)566 AttrTable::find_container(const string &target)
567 {
568 string::size_type dotpos = target.find('.');
569 if (dotpos != string::npos) {
570 string container = target.substr(0, dotpos);
571 string field = target.substr(dotpos + 1);
572
573 AttrTable *at = simple_find_container(container);
574 return (at) ? at->find_container(field) : 0;
575 }
576 else {
577 return simple_find_container(target);
578 }
579 }
580
581 // Made public for callers that want non-recursive find. [mjohnson 6 oct 09]
582 AttrTable *
simple_find_container(const string & target)583 AttrTable::simple_find_container(const string &target)
584 {
585 if (get_name() == target)
586 return this;
587
588 for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
589 if (is_container(i) && target == (*i)->name) {
590 return (*i)->attributes;
591 }
592 }
593
594 return 0;
595 }
596
597 /** Each of the following accessors get information using the name of an
598 attribute. They perform a simple search for the name in this
599 attribute table only; sub-tables are not searched and the dot
600 notation is not recognized.
601
602 @name Accessors using an attribute name */
603 //@{
604
605 /** @brief Get an attribute container. */
606 AttrTable *
get_attr_table(const string & name)607 AttrTable::get_attr_table(const string &name)
608 {
609 return find_container(name);
610 }
611
612 /** @brief Get the type name of an attribute within this attribute table. */
get_type(const string & name)613 string AttrTable::get_type(const string &name)
614 {
615 Attr_iter p = simple_find(name);
616 return (p != attr_map.end()) ? get_type(p) : (string) "";
617 }
618
619 /** @brief Get the type of an attribute.
620 @return The <tt>AttrType</tt> value describing the attribute. */
get_attr_type(const string & name)621 AttrType AttrTable::get_attr_type(const string &name)
622 {
623 Attr_iter p = simple_find(name);
624 return (p != attr_map.end()) ? get_attr_type(p) : Attr_unknown;
625 }
626
627 /** If the indicated attribute is a container attribute, this function
628 returns the number of attributes in <i>its</i> attribute table. If the
629 indicated attribute is not a container, the method returns the number
630 of values for the attribute (1 for a scalar attribute, N for a vector
631 attribute value).
632 @brief Get the number of attributes in this container.
633 */
get_attr_num(const string & name)634 unsigned int AttrTable::get_attr_num(const string &name)
635 {
636 Attr_iter iter = simple_find(name);
637 return (iter != attr_map.end()) ? get_attr_num(iter) : 0;
638 }
639
640 /** Get a pointer to the vector of values associated with the attribute
641 referenced by Pix <tt>p</tt> or named <tt>name</tt>.
642
643 Note that all values in an attribute table are stored as string data.
644 They may be converted to a more appropriate internal format by the
645 calling program.
646
647 @return If the indicated attribute is a container, this function
648 returns the null pointer. Otherwise returns a pointer to the
649 the attribute vector value.
650 @brief Get a vector-valued attribute.
651 */
652 vector<string> *
get_attr_vector(const string & name)653 AttrTable::get_attr_vector(const string &name)
654 {
655 Attr_iter p = simple_find(name);
656 return (p != attr_map.end()) ? get_attr_vector(p) : 0;
657 }
658
659 /** Delete the attribute named <tt>name</tt>. If <tt>i</tt> is given, and
660 the attribute has a vector value, delete the <tt>i</tt>$^th$
661 element of the vector.
662
663 You can use this function to delete container attributes, although
664 the <tt>i</tt> parameter has no meaning for that operation.
665
666 @brief Deletes an attribute.
667 @param name The name of the attribute to delete. This can be an
668 attribute of any type, including containers. However, this method
669 looks only in this attribute table and does not recognize the dot
670 notation.
671 @param i If the named attribute is a vector, and <tt>i</tt> is
672 non-negative, the i-th entry in the vector is deleted, and the
673 array is repacked. If <tt>i</tt> equals -1 (the default), the
674 entire attribute is deleted. */
del_attr(const string & name,int i)675 void AttrTable::del_attr(const string &name, int i)
676 {
677 #if WWW_ENCODING
678 string lname = www2id(name);
679 #else
680 string lname = remove_space_encoding(name);
681 #endif
682
683 Attr_iter iter = simple_find(lname);
684 if (iter != attr_map.end()) {
685 if (i == -1) { // Delete the whole attribute
686 entry *e = *iter;
687 attr_map.erase(iter);
688 delete e;
689 e = 0;
690 }
691 else { // Delete one element from attribute array
692 // Don't try to delete elements from the vector of values if the
693 // map is a container!
694 if ((*iter)->type == Attr_container)
695 return;
696
697 vector<string> *sxp = (*iter)->attr;
698
699 assert(i >= 0 && i < (int) sxp->size());
700 sxp->erase(sxp->begin() + i); // rm the element
701 }
702 }
703 }
704
705 //@} Accessors using an attribute name
706
707 /** @name get information using an iterator */
708 //@{
709 /** Get an iterator to the first entry in this attribute table.
710 @return Attr_iter; references the end of the array if empty list. */
attr_begin()711 AttrTable::Attr_iter AttrTable::attr_begin()
712 {
713 return attr_map.begin();
714 }
715
716 /** Get an iterator to the end attribute table. Does not point to
717 the last attribute in the table
718 @return Attr_iter */
attr_end()719 AttrTable::Attr_iter AttrTable::attr_end()
720 {
721 return attr_map.end();
722 }
723
724 /** Given an index \c i, return the \c Attr_iter to the corresponding
725 element. This method provides a way to use all the methods that take an
726 \c Attr_iter using a simple integer index. Use the get_attr_num() or
727 get_size() methods to determine how many items the AttrTable contains.
728
729 @param i The index
730 @return The corresponding Attr_iter
731 @see get_attr_num, get_size */
get_attr_iter(int i)732 AttrTable::Attr_iter AttrTable::get_attr_iter(int i)
733 {
734 return attr_map.begin() + i;
735 }
736
737 /** Returns the name of the attribute referenced by \e iter. */
get_name(Attr_iter iter)738 string AttrTable::get_name(Attr_iter iter)
739 {
740 assert(iter != attr_map.end());
741
742 return (*iter)->name;
743 }
744
745 /** Returns true if the attribute referenced by \e i is a container. */
is_container(Attr_iter i)746 bool AttrTable::is_container(Attr_iter i)
747 {
748 return (*i)->type == Attr_container;
749 }
750
751 /** Get the attribute container referenced by \e iter. If no
752 such container exists, then return a reference to the end of the
753 table.
754 @param iter Reference to a table contained by this object.
755 @return The child attribute table. */
756 AttrTable *
get_attr_table(Attr_iter iter)757 AttrTable::get_attr_table(Attr_iter iter)
758 {
759 assert(iter != attr_map.end());
760 return (*iter)->type == Attr_container ? (*iter)->attributes : 0;
761 }
762
763 /** Delete the iterator. Since AttrTable stores pointers to AttrTable
764 objects, the caller should be sure to delete the AttrTable itself.
765 The caller will gain control of the AttrTable* located at
766 get_attr_table(iter) prior to this call.
767
768 @note The original semantics of this methods were odd. The caller was
769 responsible for deleting the AttrTable, but if they did that before calling
770 this, then memory corruption would happen (because this code accesses a
771 field of the table). If the caller did not delete the table, memory leaked.
772 The only correct way to call the method was to grab the pointer, call this
773 and then delete the pointer. I added a call to delete the contained
774 AttrTable pointer, which changes the behavior of this, but probably in a
775 way that will fix leaks in existing code. This change can be reverted by
776 setting NEW_DEL_ATTR_TABLE_BEHAVIOR to false. jhrg 4/26/13
777
778 @note calling this method <b>invalidates</b> the iterator \e iter.
779 @param iter points to the entry to be deleted.
780 @return The Attr_iter for the element following \e iter */
del_attr_table(Attr_iter iter)781 AttrTable::Attr_iter AttrTable::del_attr_table(Attr_iter iter)
782 {
783 if ((*iter)->type != Attr_container)
784 return ++iter;
785
786 // the caller intends to delete/reuse the contained AttrTable,
787 // so zero it out so it doesn't get deleted before we delete the entry
788 // [mjohnson]
789 struct entry *e = *iter;
790 // container no longer has a parent.
791 if (e->attributes) {
792 e->attributes->d_parent = 0;
793
794 #if NEW_DEL_ATTR_TABLE_BEHAVIOR
795 delete e->attributes;
796 #endif
797 e->attributes = 0;
798 }
799
800 delete e;
801
802 return attr_map.erase(iter);
803 }
804
805 /** Get the type name of an attribute referenced by \e iter.
806 @param iter Reference to the Attribute.
807 @return A string with the name of this attribute datatype. */
get_type(Attr_iter iter)808 string AttrTable::get_type(Attr_iter iter)
809 {
810 assert(iter != attr_map.end());
811 return AttrType_to_String((*iter)->type);
812 }
813
814 /** Get the type of the attribute referenced by \e iter.
815 @param iter
816 @return The datatype of this attribute in an instance of AttrType. */
get_attr_type(Attr_iter iter)817 AttrType AttrTable::get_attr_type(Attr_iter iter)
818 {
819 return (*iter)->type;
820 }
821
822 /** If the attribute referenced by \e iter is a container attribute, this
823 method returns the number of attributes in its attribute table.
824 If the indicated attribute is not a container, the method returns the
825 number of values for the attribute (1 for a scalar attribute, N for a
826 vector attribute value).
827 @param iter Reference to an attribute
828 @return The number of elements in the attribute. */
get_attr_num(Attr_iter iter)829 unsigned int AttrTable::get_attr_num(Attr_iter iter)
830 {
831 assert(iter != attr_map.end());
832 return ((*iter)->type == Attr_container) ? (*iter)->attributes->get_size() : (*iter)->attr->size();
833 }
834
835 /** Returns the value of an attribute. If the attribute has a vector
836 value, you can indicate which is the desired value with the index
837 argument, \e i. If the argument is omitted, the first value is
838 returned. If the attribute has only a single value, the index
839 argument is ignored. If \e i is greater than the number of
840 elements in the attribute, an error is produced.
841
842 All values in an attribute table are stored as string data. They may
843 be converted to a more appropriate internal format by the calling
844 program.
845
846 @param iter Reference to an attribute
847 @param i The attribute value index, zero-based. Default value: 0
848 @return If the indicated attribute is a container, this function
849 returns the string ``None''. If using a name to refer to the attribute
850 and the named attribute does not exist, return the empty string. */
get_attr(Attr_iter iter,unsigned int i)851 string AttrTable::get_attr(Attr_iter iter, unsigned int i)
852 {
853 assert(iter != attr_map.end());
854
855 return (*iter)->type == Attr_container ? (string) "None" : (*(*iter)->attr)[i];
856 }
857
get_attr(const string & name,unsigned int i)858 string AttrTable::get_attr(const string &name, unsigned int i)
859 {
860 Attr_iter p = simple_find(name);
861 return (p != attr_map.end()) ? get_attr(p, i) : (string) "";
862 }
863
864 /** Returns a pointer to the vector of values associated with the
865 attribute referenced by iterator \e iter.
866
867 Note that all values in an attribute table are stored as string data.
868 They may be converted to a more appropriate internal format by the
869 calling program.
870
871 @param iter Reference to the Attribute.
872 @return If the indicated attribute is a container, this function
873 returns the null pointer. Otherwise returns a pointer to the
874 the attribute vector value. */
875 vector<string> *
get_attr_vector(Attr_iter iter)876 AttrTable::get_attr_vector(Attr_iter iter)
877 {
878 assert(iter != attr_map.end());
879 return (*iter)->type != Attr_container ? (*iter)->attr : 0;
880 }
881
is_global_attribute(Attr_iter iter)882 bool AttrTable::is_global_attribute(Attr_iter iter)
883 {
884 assert(iter != attr_map.end());
885 if ((*iter)->type == Attr_container)
886 return (*iter)->attributes->is_global_attribute();
887 else
888 return (*iter)->is_global;
889 }
890
set_is_global_attribute(Attr_iter iter,bool ga)891 void AttrTable::set_is_global_attribute(Attr_iter iter, bool ga)
892 {
893 assert(iter != attr_map.end());
894 if ((*iter)->type == Attr_container)
895 (*iter)->attributes->set_is_global_attribute(ga);
896 else
897 (*iter)->is_global = ga;
898 }
899
900 //@} Accessors that use an iterator
901
902 // Alias an attribute table. The alias should be added to this object.
903 /** @brief Add an alias to a container held by this attribute table.
904 @param name The name of the alias. May <i>not</i> use dot notation.
905 @param src The existing attribute container to alias.
906 @exception Error if an attribute, container or alias called
907 <tt>name</tt> already exists in this attribute table. */
add_container_alias(const string & name,AttrTable * src)908 void AttrTable::add_container_alias(const string &name, AttrTable *src)
909 {
910 #if WWW_ENCODING
911 string lname = www2id(name);
912 #else
913 string lname = remove_space_encoding(name);
914 #endif
915
916 if (simple_find(lname) != attr_end())
917 throw Error(string("There already exists a container called `") + name + string("in this attribute table. (2)"));
918
919 entry *e = new entry;
920 e->name = lname;
921 e->is_alias = true;
922 e->aliased_to = src->get_name();
923 e->type = Attr_container;
924
925 e->attributes = src;
926
927 attr_map.push_back(e);
928 }
929
930 /** Assume \e source names an attribute value in some container. Add an alias
931 \e name for that value in this object.
932
933 @brief Add an alias for an attribute.
934
935 @param das
936 @param name The name of the alias. May <i>not</i> use dot notation.
937 @param source The name of the attribute to alias. May use dot
938 notation.
939 @exception Error if the attribute table already contains an
940 attribute, container or alias called <tt>name</tt> or if an
941 attribute called <tt>source</tt> does not exist. */
add_value_alias(AttrTable * das,const string & name,const string & source)942 void AttrTable::add_value_alias(AttrTable *das, const string &name, const string &source)
943 {
944 #if WWW_ENCODING
945 string lname = www2id(name);
946 #else
947 string lname = remove_space_encoding(name);
948 #endif
949
950 #if WWW_ENCODING
951 string lsource = www2id(source);
952 #else
953 string lsource = remove_space_encoding(source);
954 #endif
955
956 // find the container that holds source and its (sources's) iterator
957 // within that container. Search at the uppermost level of the attribute
958 // object to find values defined `above' the current container.
959 AttrTable *at;
960 Attr_iter iter;
961 das->find(lsource, &at, &iter);
962
963 // If source is not found by looking at the topmost level, look in the
964 // current table (i.e., alias z x where x is in the current container
965 // won't be found by looking for `x' at the top level). See test case 26
966 // in das-testsuite.
967 if (!at || (iter == at->attr_end()) || !*iter) {
968 find(lsource, &at, &iter);
969 if (!at || (iter == at->attr_end()) || !*iter)
970 throw Error(string("Could not find the attribute `") + source + string("' in the attribute object."));
971 }
972
973 // If we've got a value to alias and it's being added at the top level of
974 // the DAS, that's an error.
975 if (at && !at->is_container(iter) && this == das)
976 throw Error(
977 string(
978 "A value cannot be aliased to the top level of the DAS;\nOnly containers may be present at that level of the DAS."));
979
980 if (simple_find(lname) != attr_end())
981 throw Error(string("There already exists a container called `") + name + string("in this attribute table. (3)"));
982
983 entry *e = new entry;
984 e->name = lname;
985 e->is_alias = true;
986 e->aliased_to = lsource;
987 e->type = get_attr_type(iter);
988 if (at && e->type == Attr_container)
989 e->attributes = at->get_attr_table(iter);
990 else
991 e->attr = (*iter)->attr;
992
993 attr_map.push_back(e);
994 }
995
996 // Deprecated
997 /** Once an alias is
998 inserted into an attribute table, reading the attributes for
999 <i>alias</i> will return those stored for <i>name</i>.
1000
1001 Two forms for this function exist: one searches for <i>name</i>
1002 in the AttrTable referenced by <i>at</i> while the other uses
1003 <tt>this</tt>. You can use <tt>DAS::get_attr_table()</tt> to
1004 get the attribute table for an arbitrary name.
1005
1006 @brief Adds an alias to the set of attributes.
1007 @see get_attr_table
1008 @deprecated The current alias design is flawed. It is impossible to map
1009 this onto the XML implementation where the DAS and DDS information are
1010 combined in one object.
1011 @param alias The alias to insert into the attribute table.
1012 @param name The name of the already-existing attribute to which
1013 the alias will refer.
1014 @param at An attribute table in which to insert the alias. */
attr_alias(const string & alias,AttrTable * at,const string & name)1015 bool AttrTable::attr_alias(const string &alias, AttrTable *at, const string &name)
1016 {
1017 add_value_alias(at, alias, name);
1018 return true;
1019 }
1020
1021 /** @deprecated The current alias design is flawed. It is impossible to map
1022 this onto the XML implementation where the DAS and DDS information are
1023 combined in one object.
1024
1025 @param alias The alias to insert into the attribute table.
1026 @param name The name of the already-existing attribute to which
1027 the alias will refer. */
attr_alias(const string & alias,const string & name)1028 bool AttrTable::attr_alias(const string &alias, const string &name)
1029 {
1030 return attr_alias(alias, this, name);
1031 }
1032
1033 /** Erase the entire attribute table. This returns an AttrTable to the empty
1034 state that's the same as the object generated by the null constructor.
1035 @brief Erase the attribute table. */
erase()1036 void AttrTable::erase()
1037 {
1038 for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
1039 delete *i;
1040 *i = 0;
1041 }
1042
1043 attr_map.erase(attr_map.begin(), attr_map.end());
1044
1045 d_name = "";
1046 }
1047
1048 const string double_quote = "\"";
1049
1050 // This is here as a result of the problem described in ticket #1163 where
1051 // the data handlers are adding quotes to string attributes so the DAS will
1052 // be printed correctly. But that has the affect of adding the quotes to the
1053 // attribute's _value_ not just it's print representation. As part of the fix
1054 // I made the code here add the quotes if the handlers are fixed (but not if
1055 // handlers are still adding them). The other part of 1163 is to fix all of
1056 // the handlers... What this fix means is that attributes whose values really
1057 // do contain bracketing quotes might be misunderstood, since we're assuming
1058 // those quotes were added by the handlers as a hack to get the output
1059 // formatting correct for the DAS. jhrg 7/30/08
1060
write_string_attribute_for_das(ostream & out,const string & value,const string & term)1061 static void write_string_attribute_for_das(ostream &out, const string &value, const string &term)
1062 {
1063 if (is_quoted(value))
1064 out << value << term;
1065 else
1066 out << double_quote << value << double_quote << term;
1067 }
1068
1069 #if 0
1070 static void
1071 write_string_attribute_for_das(FILE *out, const string &value, const string &term)
1072 {
1073 if (is_quoted(value))
1074 fprintf(out, "%s%s", value.c_str(), term.c_str());
1075 else
1076 fprintf(out, "\"%s\"%s", value.c_str(), term.c_str());
1077 }
1078 #endif
1079
1080 // Special treatment for XML: Make sure to escape double quotes when XML is
1081 // printed in a DAS.
write_xml_attribute_for_das(ostream & out,const string & value,const string & term)1082 static void write_xml_attribute_for_das(ostream &out, const string &value, const string &term)
1083 {
1084 if (is_quoted(value))
1085 out << escape_double_quotes(value) << term;
1086 else
1087 out << double_quote << escape_double_quotes(value) << double_quote << term;
1088 }
1089
1090 #if 0
1091 static void
1092 write_xml_attribute_for_das(FILE *out, const string &value, const string &term)
1093 {
1094 if (is_quoted(value))
1095 fprintf(out, "%s%s", escape_double_quotes(value).c_str(), term.c_str());
1096 else
1097 fprintf(out, "\"%s\"%s", escape_double_quotes(value).c_str(), term.c_str());
1098 }
1099 #endif
1100
1101 /** A simple printer that does nothing fancy with aliases.
1102 Protected. */
simple_print(FILE * out,string pad,Attr_iter i,bool dereference)1103 void AttrTable::simple_print(FILE *out, string pad, Attr_iter i, bool dereference)
1104 {
1105 ostringstream oss;
1106 simple_print(oss, pad, i, dereference);
1107 fwrite(oss.str().data(), 1, oss.str().length(), out);
1108
1109 #if 0
1110 switch ((*i)->type) {
1111 case Attr_container:
1112 #if WWW_ENCODING
1113 fprintf(out, "%s%s {\n", pad.c_str(), id2www(get_name(i)).c_str());
1114 #else
1115 fprintf(out, "%s%s {\n", pad.c_str(), get_name(i).c_str());
1116 #endif
1117 (*i)->attributes->print(out, pad + " ", dereference);
1118
1119 fprintf(out, "%s}\n", pad.c_str());
1120 break;
1121
1122 case Attr_string: {
1123 #if WWW_ENCODING
1124 fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), id2www(get_name(i)).c_str());
1125 #else
1126 fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), get_name(i).c_str());
1127 #endif
1128 vector<string> *sxp = (*i)->attr;
1129 vector<string>::iterator last = sxp->end() - 1;
1130 for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
1131 write_string_attribute_for_das(out, *i, ", ");
1132 }
1133 write_string_attribute_for_das(out, *last, ";\n");
1134 }
1135 break;
1136
1137 case Attr_other_xml: {
1138 #if WWW_ENCODING
1139 fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), id2www(get_name(i)).c_str());
1140 #else
1141 fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), get_name(i).c_str());
1142 #endif
1143 vector<string> *sxp = (*i)->attr;
1144 vector<string>::iterator last = sxp->end() - 1;
1145 for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
1146 write_xml_attribute_for_das(out, *i, ", ");
1147 }
1148 write_xml_attribute_for_das(out, *last, ";\n");
1149 }
1150 break;
1151
1152 default: {
1153 #if WWW_ENCODING
1154 fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), id2www(get_name(i)).c_str());
1155 #else
1156 fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), get_name(i).c_str());
1157 #endif
1158
1159 vector<string> *sxp = (*i)->attr;
1160 vector<string>::iterator last = sxp->end() - 1;
1161 for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
1162 fprintf(out, "%s%s", (*i).c_str(), ", ");
1163 }
1164 fprintf(out, "%s%s", (*last).c_str(), ";\n");
1165 }
1166 break;
1167 }
1168 #endif
1169 }
1170
1171 /** A simple printer that does nothing fancy with aliases.
1172 Protected. */
simple_print(ostream & out,string pad,Attr_iter i,bool dereference)1173 void AttrTable::simple_print(ostream &out, string pad, Attr_iter i, bool dereference)
1174 {
1175 switch ((*i)->type) {
1176 case Attr_container:
1177 #if WWW_ENCODING
1178 out << pad << id2www(get_name(i)) << " {\n";
1179 #else
1180 out << pad << add_space_encoding(get_name(i)) << " {\n";
1181 #endif
1182 (*i)->attributes->print(out, pad + " ", dereference);
1183 out << pad << "}\n";
1184 break;
1185
1186 case Attr_string: {
1187 #if WWW_ENCODING
1188 out << pad << get_type(i) << " " << id2www(get_name(i)) << " ";
1189 #else
1190 out << pad << get_type(i) << " " << add_space_encoding(get_name(i)) << " ";
1191 #endif
1192 vector<string> *sxp = (*i)->attr;
1193 vector<string>::iterator last = sxp->end() - 1;
1194 for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
1195 write_string_attribute_for_das(out, *i, ", ");
1196 }
1197 write_string_attribute_for_das(out, *last, ";\n");
1198 }
1199 break;
1200
1201 case Attr_other_xml: {
1202 #if WWW_ENCODING
1203 out << pad << get_type(i) << " " << id2www(get_name(i)) << " ";
1204 #else
1205 out << pad << get_type(i) << " " << add_space_encoding(get_name(i)) << " ";
1206 #endif
1207 vector<string> *sxp = (*i)->attr;
1208 vector<string>::iterator last = sxp->end() - 1;
1209 for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
1210 write_xml_attribute_for_das(out, *i, ", ");
1211 }
1212 write_xml_attribute_for_das(out, *last, ";\n");
1213 }
1214 break;
1215
1216 default: {
1217 #if WWW_ENCODING
1218 out << pad << get_type(i) << " " << id2www(get_name(i)) << " ";
1219 #else
1220 out << pad << get_type(i) << " " << add_space_encoding(get_name(i)) << " ";
1221 #endif
1222 vector<string> *sxp = (*i)->attr;
1223 vector<string>::iterator last = sxp->end() - 1;
1224 for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
1225 out << *i << ", ";
1226 }
1227 out << *last << ";\n";
1228 }
1229 break;
1230 }
1231 }
1232
1233 /** Prints an ASCII representation of the attribute table to the
1234 indicated FILE pointer. The \c pad argument is prefixed to each
1235 line of the output to provide control of indentation.
1236
1237 @brief Prints the attribute table.
1238 @param out Print to the given output FILE.
1239 @param pad Indent elements of a table using this string of spaces. By
1240 default this is a string of four spaces
1241 @param dereference If true, follow aliases. Default is false. */
1242
print(FILE * out,string pad,bool dereference)1243 void AttrTable::print(FILE *out, string pad, bool dereference)
1244 {
1245 ostringstream oss;
1246 print(oss, pad, dereference);
1247 fwrite(oss.str().data(), 1, oss.str().length(), out);
1248
1249 #if 0
1250 for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
1251 if ((*i)->is_alias) {
1252 if (dereference) {
1253 simple_print(out, pad, i, dereference);
1254 }
1255 else {
1256 #if WWW_ENCODING
1257 fprintf(out, "%sAlias %s %s;\n",
1258 pad.c_str(),
1259 id2www(get_name(i)).c_str(),
1260 id2www((*i)->aliased_to).c_str());
1261 #else
1262 fprintf(out, "%sAlias %s %s;\n",
1263 pad.c_str(), add_space_encoding(get_name(i)).c_str(), add_space_encoding((*i)->aliased_to).c_str());
1264
1265 #endif
1266 }
1267 }
1268 else {
1269 simple_print(out, pad, i, dereference);
1270 }
1271 }
1272 #endif
1273 }
1274
1275 /** Prints an ASCII representation of the attribute table to the
1276 indicated output stream. The \c pad argument is prefixed to each
1277 line of the output to provide control of indentation.
1278
1279 @brief Prints the attribute table.
1280 @param out Print to the given output stream.
1281 @param pad Indent elements of a table using this string of spaces. By
1282 default this is a string of four spaces
1283 @param dereference If true, follow aliases. Default is false. */
1284
print(ostream & out,string pad,bool dereference)1285 void AttrTable::print(ostream &out, string pad, bool dereference)
1286 {
1287 for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
1288 if ((*i)->is_alias) {
1289 if (dereference) {
1290 simple_print(out, pad, i, dereference);
1291 }
1292 else {
1293 #if WWW_ENCODING
1294 out << pad << "Alias " << id2www(get_name(i))
1295 << " " << id2www((*i)->aliased_to) << ";\n";
1296 #else
1297 out << pad << "Alias " << add_space_encoding(get_name(i)) << " "
1298 << add_space_encoding((*i)->aliased_to) << ";\n";
1299 #endif
1300 }
1301 }
1302 else {
1303 simple_print(out, pad, i, dereference);
1304 }
1305 }
1306 }
1307
1308 /** Print the attribute table in XML.
1309 @param out Destination
1310 @param pad Indent lines of text/xml this much. Default is four spaces.
1311 @param constrained Not used
1312 @deprecated */
print_xml(FILE * out,string pad,bool)1313 void AttrTable::print_xml(FILE *out, string pad, bool /*constrained*/)
1314 {
1315 XMLWriter xml(pad);
1316 print_xml_writer(xml);
1317 fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
1318
1319 #if OLD_XML_MOETHODS
1320 ostringstream oss;
1321 print_xml(oss, pad);
1322 fwrite(oss.str().data(), 1, oss.str().length(), out);
1323 #endif
1324
1325 #if 0
1326 // Why this works: AttrTable is really a hacked class that used to
1327 // implement a single-level set of attributes. Containers
1328 // were added several years later by dropping in the 'entry' structure.
1329 // It's not a class in its own right; instead accessors from AttrTable
1330 // are used to access information from entry. So... the loop below
1331 // actually iterates over the entries of *this* (which is an instance of
1332 // AttrTable). A container is an entry whose sole value is an AttrTable
1333 // instance. 05/19/03 jhrg
1334 for (Attr_iter i = attr_begin(); i != attr_end(); ++i) {
1335 if ((*i)->is_alias) {
1336 fprintf(out, "%s<Alias name=\"%s\" Attribute=\"%s\"/>\n",
1337 pad.c_str(), id2xml(get_name(i)).c_str(),
1338 (*i)->aliased_to.c_str());
1339
1340 }
1341 else if (is_container(i)) {
1342 fprintf(out, "%s<Attribute name=\"%s\" type=\"%s\">\n",
1343 pad.c_str(), id2xml(get_name(i)).c_str(),
1344 get_type(i).c_str());
1345
1346 get_attr_table(i)->print_xml(out, pad + " "/*, constrained*/);
1347
1348 fprintf(out, "%s</Attribute>\n", pad.c_str());
1349 }
1350 else {
1351 fprintf(out, "%s<Attribute name=\"%s\" type=\"%s\">\n",
1352 pad.c_str(), id2xml(get_name(i)).c_str(), get_type(i).c_str());
1353
1354 string value_pad = pad + " ";
1355 // Special handling for the OtherXML attribute type - don't escape
1356 // the XML and don't include the <value> element. Note that there
1357 // cannot be an vector of XML things as can be with the other types.
1358 if (get_attr_type(i) == Attr_other_xml) {
1359 if (get_attr_num(i) != 1)
1360 throw Error("OtherXML attributes cannot be vector-valued.");
1361 fprintf(out, "%s%s\n", value_pad.c_str(), get_attr(i, 0).c_str());
1362 }
1363 else {
1364 for (unsigned j = 0; j < get_attr_num(i); ++j) {
1365 fprintf(out, "%s<value>%s</value>\n", value_pad.c_str(),
1366 id2xml(get_attr(i, j)).c_str());
1367 }
1368 }
1369 fprintf(out, "%s</Attribute>\n", pad.c_str());
1370 }
1371 }
1372 #endif
1373 }
1374
1375 /**
1376 * @deprecated
1377 */
print_xml(ostream & out,string pad,bool)1378 void AttrTable::print_xml(ostream &out, string pad, bool /*constrained*/)
1379 {
1380 XMLWriter xml(pad);
1381 print_xml_writer(xml);
1382 out << xml.get_doc();
1383
1384 #if 0
1385 for (Attr_iter i = attr_begin(); i != attr_end(); ++i) {
1386 if ((*i)->is_alias) {
1387 out << pad << "<Alias name=\"" << id2xml(get_name(i))
1388 << "\" Attribute=\"" << (*i)->aliased_to << "\"/>\n";
1389
1390 }
1391 else if (is_container(i)) {
1392 out << pad << "<Attribute name=\"" << id2xml(get_name(i))
1393 << "\" type=\"" << get_type(i) << "\">\n";
1394
1395 get_attr_table(i)->print_xml(out, pad + " "/*, constrained*/);
1396
1397 out << pad << "</Attribute>\n";
1398 }
1399 else {
1400 out << pad << "<Attribute name=\"" << id2xml(get_name(i))
1401 << "\" type=\"" << get_type(i) << "\">\n";
1402
1403 string value_pad = pad + " ";
1404 if (get_attr_type(i) == Attr_other_xml) {
1405 if (get_attr_num(i) != 1)
1406 throw Error("OtherXML attributes cannot be vector-valued.");
1407 out << value_pad << get_attr(i, 0) << "\n";
1408 }
1409 else {
1410 string value_pad = pad + " ";
1411 for (unsigned j = 0; j < get_attr_num(i); ++j) {
1412 out << value_pad << "<value>" << id2xml(get_attr(i, j)) << "</value>\n";
1413 }
1414 }
1415 out << pad << "</Attribute>\n";
1416 }
1417 }
1418 #endif
1419 }
1420
1421 /** Print the attribute table in XML.
1422 @param out Destination stream
1423 @param pad Indent lines of text/xml this much. Default is four spaces.
1424 @param constrained Not used */
print_xml_writer(XMLWriter & xml)1425 void AttrTable::print_xml_writer(XMLWriter &xml)
1426 {
1427 for (Attr_iter i = attr_begin(); i != attr_end(); ++i) {
1428 if ((*i)->is_alias) {
1429 if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Alias") < 0)
1430 throw InternalErr(__FILE__, __LINE__, "Could not write Alias element");
1431 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name",
1432 (const xmlChar*) get_name(i).c_str()) < 0)
1433 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1434 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "Attribute",
1435 (const xmlChar*) (*i)->aliased_to.c_str()) < 0)
1436 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1437 if (xmlTextWriterEndElement(xml.get_writer()) < 0)
1438 throw InternalErr(__FILE__, __LINE__, "Could not end Alias element");
1439 }
1440 else if (is_container(i)) {
1441 if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Attribute") < 0)
1442 throw InternalErr(__FILE__, __LINE__, "Could not write Attribute element");
1443 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name",
1444 (const xmlChar*) get_name(i).c_str()) < 0)
1445 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1446 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "type",
1447 (const xmlChar*) get_type(i).c_str()) < 0)
1448 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1449
1450 get_attr_table(i)->print_xml_writer(xml);
1451
1452 if (xmlTextWriterEndElement(xml.get_writer()) < 0)
1453 throw InternalErr(__FILE__, __LINE__, "Could not end Attribute element");
1454 }
1455 else {
1456 if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Attribute") < 0)
1457 throw InternalErr(__FILE__, __LINE__, "Could not write Attribute element");
1458 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name",
1459 (const xmlChar*) get_name(i).c_str()) < 0)
1460 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1461 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "type",
1462 (const xmlChar*) get_type(i).c_str()) < 0)
1463 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1464
1465 if (get_attr_type(i) == Attr_other_xml) {
1466 if (get_attr_num(i) != 1)
1467 throw Error("OtherXML attributes cannot be vector-valued.");
1468 // Replaced xmltextWriterWriteString with xmlTextWriterWriteRaw to keep the
1469 // libxml2 code from escaping the xml (which was breaking all of the inferencing
1470 // code. jhrg
1471 if (xmlTextWriterWriteRaw(xml.get_writer(), (const xmlChar*) get_attr(i, 0).c_str()) < 0)
1472 throw InternalErr(__FILE__, __LINE__, "Could not write OtherXML value");
1473 }
1474 else {
1475 for (unsigned j = 0; j < get_attr_num(i); ++j) {
1476 if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "value") < 0)
1477 throw InternalErr(__FILE__, __LINE__, "Could not write value element");
1478
1479 if (xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) get_attr(i, j).c_str()) < 0)
1480 throw InternalErr(__FILE__, __LINE__, "Could not write attribute value");
1481
1482 if (xmlTextWriterEndElement(xml.get_writer()) < 0)
1483 throw InternalErr(__FILE__, __LINE__, "Could not end value element");
1484 }
1485 }
1486 if (xmlTextWriterEndElement(xml.get_writer()) < 0)
1487 throw InternalErr(__FILE__, __LINE__, "Could not end Attribute element");
1488 }
1489 }
1490 }
1491
1492 /** Write the DAP4 XML representation for this attribute table. This
1493 * method is used to build the DAP4 DMR response object.
1494 *
1495 * @param xml An XMLWriter that will do the serialization
1496 */
1497 void
print_dap4(XMLWriter & xml)1498 AttrTable::print_dap4(XMLWriter &xml)
1499 {
1500 print_xml_writer(xml);
1501 }
1502
1503 /** @brief dumps information about this object
1504 *
1505 * Displays the pointer value of this instance and all attributes stored
1506 *
1507 * @param strm C++ i/o stream to dump the information to
1508 * @return void
1509 */
dump(ostream & strm) const1510 void AttrTable::dump(ostream &strm) const
1511 {
1512 strm << DapIndent::LMarg << "AttrTable::dump - (" << (void *) this << ")" << endl;
1513 DapIndent::Indent();
1514 strm << DapIndent::LMarg << "table name: " << d_name << endl;
1515 if (attr_map.size()) {
1516 strm << DapIndent::LMarg << "attributes: " << endl;
1517 DapIndent::Indent();
1518 Attr_citer i = attr_map.begin();
1519 Attr_citer ie = attr_map.end();
1520 for (; i != ie; ++i) {
1521 entry *e = (*i);
1522 string type = AttrType_to_String(e->type);
1523 if (e->is_alias) {
1524 strm << DapIndent::LMarg << "alias: " << e->name << " aliased to: " << e->aliased_to << endl;
1525 }
1526 else if (e->type == Attr_container) {
1527 strm << DapIndent::LMarg << "attr: " << e->name << " of type " << type << endl;
1528 DapIndent::Indent();
1529 e->attributes->dump(strm);
1530 DapIndent::UnIndent();
1531 }
1532 else {
1533 strm << DapIndent::LMarg << "attr: " << e->name << " of type " << type << endl;
1534 DapIndent::Indent();
1535 strm << DapIndent::LMarg;
1536 vector<string>::const_iterator iter = e->attr->begin();
1537 vector<string>::const_iterator last = e->attr->end() - 1;
1538 for (; iter != last; ++iter) {
1539 strm << (*iter) << ", ";
1540 }
1541 strm << (*(e->attr->end() - 1)) << endl;
1542 DapIndent::UnIndent();
1543 }
1544 }
1545 DapIndent::UnIndent();
1546 }
1547 else {
1548 strm << DapIndent::LMarg << "attributes: empty" << endl;
1549 }
1550 if (d_parent) {
1551 strm << DapIndent::LMarg << "parent table:" << d_name << ":" << (void *) d_parent << endl;
1552 }
1553 else {
1554 strm << DapIndent::LMarg << "parent table: none" << d_name << endl;
1555 }
1556 DapIndent::UnIndent();
1557 }
1558
1559 } // namespace libdap
1560
1561