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