libdap++  Updated for version 3.8.2
AttrTable.cc
Go to the documentation of this file.
00001 // -*- mode: c++; c-basic-offset:4 -*-
00002 
00003 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
00004 // Access Protocol.
00005 
00006 // Copyright (c) 2002,2003 OPeNDAP, Inc.
00007 // Author: James Gallagher <jgallagher@opendap.org>
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Lesser General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2.1 of the License, or (at your option) any later version.
00013 //
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 // Lesser General Public License for more details.
00018 //
00019 // You should have received a copy of the GNU Lesser General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 //
00023 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
00024 
00025 // (c) COPYRIGHT URI/MIT 1994-1999
00026 // Please read the full copyright statement in the file COPYRIGHT_URI.
00027 //
00028 // Authors:
00029 //      jhrg,jimg       James Gallagher <jgallagher@gso.uri.edu>
00030 
00031 // jhrg 7/29/94
00032 
00033 #include "config.h"
00034 
00035 //#define DODS_DEBUG
00036 
00037 static char rcsid[]not_used =
00038 "$Id: AttrTable.cc 25066 2011-11-30 18:39:37Z jimg $";
00039 
00040 #include <cassert>
00041 #include <sstream>
00042 
00043 #include "AttrTable.h"
00044 
00045 #include "util.h"
00046 #include "escaping.h"
00047 
00048 #include "debug.h"
00049 
00050 // Should the www2id and id2www functions be used to encode attribute names?
00051 // Probably not... jhrg 11/16/11
00052 #define WWW_ENCODING 0
00053 
00054 using std::cerr;
00055 using std::string;
00056 using std::endl;
00057 using std::vector;
00058 
00059 namespace libdap {
00060 
00062 static string remove_space_encoding(const string &s)
00063 {
00064     string::size_type pos = s.find("%20");
00065     if (pos != string::npos) {
00066         string n = s;
00067         do {
00068             n.replace(pos, 3, " ");
00069             pos = n.find("%20");
00070         } while (pos != string::npos);
00071         return n;
00072     }
00073     else {
00074         return s;
00075     }
00076 }
00077 
00079 static string add_space_encoding(const string &s)
00080 {
00081     string::size_type pos = s.find(" ");
00082     if (pos != string::npos) {
00083         string n = s;
00084         do {
00085             n.replace(pos, 1, "%20");
00086             pos = n.find(" ");
00087         } while (pos != string::npos);
00088         return n;
00089     }
00090     else {
00091         return s;
00092     }
00093 }
00094 
00098 string AttrType_to_String(const AttrType at)
00099 {
00100     switch (at) {
00101     case Attr_container:
00102         return "Container";
00103     case Attr_byte:
00104         return "Byte";
00105     case Attr_int16:
00106         return "Int16";
00107     case Attr_uint16:
00108         return "UInt16";
00109     case Attr_int32:
00110         return "Int32";
00111     case Attr_uint32:
00112         return "UInt32";
00113     case Attr_float32:
00114         return "Float32";
00115     case Attr_float64:
00116         return "Float64";
00117     case Attr_string:
00118         return "String";
00119     case Attr_url:
00120         return "Url";
00121     case Attr_other_xml:
00122         return "OtherXML";
00123     default:
00124         return "";
00125     }
00126 }
00127 
00128 AttrType String_to_AttrType(const string &s)
00129 {
00130     string s2 = s;
00131     downcase(s2);
00132 
00133     if (s2 == "container")
00134         return Attr_container;
00135     else if (s2 == "byte")
00136         return Attr_byte;
00137     else if (s2 == "int16")
00138         return Attr_int16;
00139     else if (s2 == "uint16")
00140         return Attr_uint16;
00141     else if (s2 == "int32")
00142         return Attr_int32;
00143     else if (s2 == "uint32")
00144         return Attr_uint32;
00145     else if (s2 == "float32")
00146         return Attr_float32;
00147     else if (s2 == "float64")
00148         return Attr_float64;
00149     else if (s2 == "string")
00150         return Attr_string;
00151     else if (s2 == "url")
00152         return Attr_url;
00153     else if (s2 == "otherxml")
00154         return Attr_other_xml;
00155     else
00156         return Attr_unknown;
00157 }
00158 
00161 void AttrTable::clone(const AttrTable &at)
00162 {
00163     d_name = at.d_name;
00164     d_is_global_attribute = at.d_is_global_attribute;
00165 
00166     // Set the parent to null (no parent, not in container)
00167     // since using at.d_parent is semantically incorrect
00168     // and potentially dangerous.
00169     d_parent = 0;
00170 
00171     Attr_citer i = at.attr_map.begin();
00172     Attr_citer ie = at.attr_map.end();
00173     for (; i != ie; ++i) {
00174         // this deep-copies containers recursively
00175         entry *e = new entry(*(*i));
00176         attr_map.push_back(e);
00177 
00178         // If the entry being added was a container,
00179         // set its parent to this to maintain invariant.
00180         if (e->type == Attr_container) {
00181             assert(e->attributes);
00182             e->attributes->d_parent = this;
00183         }
00184     }
00185 }
00186 
00190 AttrTable::AttrTable() :
00191     DapObj(), d_name(""), d_parent(0), attr_map(), d_is_global_attribute(true)
00192 {
00193 }
00194 
00195 AttrTable::AttrTable(const AttrTable &rhs) :
00196     DapObj()
00197 {
00198     clone(rhs);
00199 }
00200 
00201 // Private
00202 void AttrTable::delete_attr_table()
00203 {
00204     for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
00205         delete *i;
00206         *i = 0;
00207     }
00208     attr_map.clear();
00209 }
00210 
00211 AttrTable::~AttrTable()
00212 {
00213     DBG(cerr << "Entering ~AttrTable (" << this << ")" << endl);
00214     delete_attr_table();
00215     DBG(cerr << "Exiting ~AttrTable" << endl);
00216 }
00217 
00218 AttrTable &
00219 AttrTable::operator=(const AttrTable &rhs)
00220 {
00221     if (this != &rhs) {
00222         delete_attr_table();
00223         clone(rhs);
00224     }
00225 
00226     return *this;
00227 }
00229 
00235 unsigned int AttrTable::get_size() const
00236 {
00237     return attr_map.size();
00238 }
00239 
00242 string AttrTable::get_name() const
00243 {
00244     return d_name;
00245 }
00246 
00249 void AttrTable::set_name(const string &n)
00250 {
00251 #if WWW_ENCODING
00252     d_name = www2id(n);
00253 #else
00254     d_name = remove_space_encoding(n);
00255 #endif
00256 }
00257 
00258 #if 0
00259 // This was taken from das.y and could be used here to make the 'dods_errors'
00260 // attribute container like the parser used to. Then again, maybe this feature
00261 // was just BS. jhrg (ticket 1469)
00262 static void add_bad_attribute(AttrTable *attr, const string &type, const string &name, const string &value,
00263         const string &msg) {
00264     // First, if this bad value is already in a *_dods_errors container,
00265     // then just add it. This can happen when the server side processes a DAS
00266     // and then hands it off to a client which does the same.
00267     // Make a new container. Call it <attr's name>_errors. If that container
00268     // already exists, use it.
00269     // Add the attribute.
00270     // Add the error string to an attribute in the container called
00271     // `<name_explanation.'.
00272 
00273     if (attr->get_name().find("_dods_errors") != string::npos) {
00274         attr->append_attr(name, type, value);
00275     }
00276     else {
00277         // I think _dods_errors should be _dap_error. jhrg 11/16/11
00278         string error_cont_name = attr->get_name() + "_dods_errors";
00279         AttrTable *error_cont = attr->get_attr_table(error_cont_name);
00280         if (!error_cont)
00281         error_cont = attr->append_container(error_cont_name);
00282 
00283         error_cont->append_attr(name, type, value);
00284 
00285 #ifndef ATTR_STRING_QUOTE_FIX
00286         error_cont->append_attr(name + "_dap_explanation", "String", "\"" + msg + "\"");
00287 #else
00288         error_cont->append_attr(name + "_dap_explanation", "String", msg);
00289 #endif
00290     }
00291 }
00292 #endif
00293 
00311 unsigned int AttrTable::append_attr(const string &name, const string &type, const string &value)
00312 {
00313     DBG(cerr << "Entering AttrTable::append_attr" << endl);
00314 #if WWW_ENCODING
00315     string lname = www2id(name);
00316 #else
00317     string lname = remove_space_encoding(name);
00318 #endif
00319 
00320     Attr_iter iter = simple_find(lname);
00321 
00322     // If the types don't match OR this attribute is a container, calling
00323     // this mfunc is an error!
00324     if (iter != attr_map.end() && ((*iter)->type != String_to_AttrType(type)))
00325         throw Error(string("An attribute called `") + name + string("' already exists but is of a different type"));
00326     if (iter != attr_map.end() && (get_type(iter) == "Container"))
00327         throw Error(string("An attribute called `") + name + string("' already exists but is a container."));
00328 
00329     if (iter != attr_map.end()) { // Must be a new attribute value; add it.
00330         (*iter)->attr->push_back(value);
00331         return (*iter)->attr->size();
00332     }
00333     else { // Must be a completely new attribute; add it
00334         entry *e = new entry;
00335 
00336         e->name = lname;
00337         e->is_alias = false;
00338         e->type = String_to_AttrType(type); // Record type using standard names.
00339         e->attr = new vector<string> ;
00340         e->attr->push_back(value);
00341 
00342         attr_map.push_back(e);
00343 
00344         return e->attr->size(); // return the length of the attr vector
00345     }
00346 }
00347 
00366 unsigned int AttrTable::append_attr(const string &name, const string &type, vector<string> *values)
00367 {
00368     DBG(cerr << "Entering AttrTable::append_attr(..., vector)" << endl);
00369 #if WWW_ENCODING
00370     string lname = www2id(name);
00371 #else
00372     string lname = remove_space_encoding(name);
00373 #endif
00374     Attr_iter iter = simple_find(lname);
00375 
00376     // If the types don't match OR this attribute is a container, calling
00377     // this mfunc is an error!
00378     if (iter != attr_map.end() && ((*iter)->type != String_to_AttrType(type)))
00379         throw Error(string("An attribute called `") + name + string("' already exists but is of a different type"));
00380     if (iter != attr_map.end() && (get_type(iter) == "Container"))
00381         throw Error(string("An attribute called `") + name + string("' already exists but is a container."));
00382 
00383     if (iter != attr_map.end()) { // Must be new attribute values; add.
00384         vector<string>::iterator i = values->begin();
00385         while (i != values->end())
00386             (*iter)->attr->push_back(*i++);
00387 
00388         return (*iter)->attr->size();
00389     }
00390     else { // Must be a completely new attribute; add it
00391         entry *e = new entry;
00392 
00393         e->name = lname;
00394         e->is_alias = false;
00395         e->type = String_to_AttrType(type); // Record type using standard names.
00396         e->attr = new vector<string> (*values);
00397 
00398         attr_map.push_back(e);
00399 
00400         return e->attr->size(); // return the length of the attr vector
00401     }
00402 }
00403 
00413 AttrTable *
00414 AttrTable::append_container(const string &name)
00415 {
00416     AttrTable *new_at = new AttrTable;
00417     AttrTable *ret = NULL;
00418     try {
00419         ret = append_container(new_at, name);
00420     } catch (Error &e) {
00421         // an error occurred, attribute with that name already exists
00422         delete new_at;
00423         new_at = 0;
00424         throw;
00425     }
00426     return ret;
00427 }
00428 
00443 AttrTable *
00444 AttrTable::append_container(AttrTable *at, const string &name)
00445 {
00446 #if WWW_ENCODING
00447     string lname = www2id(name);
00448 #else
00449     string lname = remove_space_encoding(name);
00450 #endif
00451 
00452     if (simple_find(name) != attr_end())
00453         throw Error(
00454                 string("There already exists a container called `") + name + string("' in this attribute table. (1)"));
00455     DBG(cerr << "Setting appended attribute container name to: "
00456             << lname << endl);
00457     at->set_name(lname);
00458 
00459     entry *e = new entry;
00460     e->name = lname;
00461     e->is_alias = false;
00462     e->type = Attr_container;
00463     e->attributes = at;
00464 
00465     attr_map.push_back(e);
00466 
00467     at->d_parent = this;
00468 
00469     return e->attributes;
00470 }
00471 
00486 void AttrTable::find(const string &target, AttrTable **at, Attr_iter *iter)
00487 {
00488     string::size_type dotpos = target.rfind('.');
00489     if (dotpos != string::npos) {
00490         string container = target.substr(0, dotpos);
00491         string field = target.substr(dotpos + 1);
00492 
00493         *at = find_container(container);
00494         if (*at) {
00495             *iter = (*at)->simple_find(field);
00496         }
00497         else {
00498             *iter = attr_map.end();
00499         }
00500     }
00501     else {
00502         *at = recurrsive_find(target, iter);
00503     }
00504 }
00505 
00517 AttrTable *
00518 AttrTable::recurrsive_find(const string &target, Attr_iter *location)
00519 {
00520     Attr_iter i = attr_begin();
00521     while (i != attr_end()) {
00522         if (target == (*i)->name) {
00523             *location = i;
00524             return this;
00525         }
00526         else if ((*i)->type == Attr_container) {
00527             AttrTable *at = (*i)->attributes->recurrsive_find(target, location);
00528             if (at)
00529                 return at;
00530         }
00531 
00532         ++i;
00533     }
00534 
00535     *location = i;
00536     return 0;
00537 }
00538 
00539 // Made public for callers that want non-recursive find.  [mjohnson 6 oct 09]
00546 AttrTable::Attr_iter AttrTable::simple_find(const string &target)
00547 {
00548     Attr_iter i;
00549     for (i = attr_map.begin(); i != attr_map.end(); ++i) {
00550         if (target == (*i)->name) {
00551             break;
00552         }
00553     }
00554     return i;
00555 }
00556 
00570 AttrTable *
00571 AttrTable::find_container(const string &target)
00572 {
00573     string::size_type dotpos = target.find('.');
00574     if (dotpos != string::npos) {
00575         string container = target.substr(0, dotpos);
00576         string field = target.substr(dotpos + 1);
00577 
00578         AttrTable *at = simple_find_container(container);
00579         return (at) ? at->find_container(field) : 0;
00580     }
00581     else {
00582         return simple_find_container(target);
00583     }
00584 }
00585 
00586 // Made public for callers that want non-recursive find.  [mjohnson 6 oct 09]
00587 AttrTable *
00588 AttrTable::simple_find_container(const string &target)
00589 {
00590     if (get_name() == target)
00591         return this;
00592 
00593     for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
00594         if (is_container(i) && target == (*i)->name) {
00595             return (*i)->attributes;
00596         }
00597     }
00598 
00599     return 0;
00600 }
00601 
00609 
00611 AttrTable *
00612 AttrTable::get_attr_table(const string &name)
00613 {
00614     return find_container(name);
00615 }
00616 
00618 string AttrTable::get_type(const string &name)
00619 {
00620     Attr_iter p = simple_find(name);
00621     return (p != attr_map.end()) ? get_type(p) : (string) "";
00622 }
00623 
00626 AttrType AttrTable::get_attr_type(const string &name)
00627 {
00628     Attr_iter p = simple_find(name);
00629     return (p != attr_map.end()) ? get_attr_type(p) : Attr_unknown;
00630 }
00631 
00639 unsigned int AttrTable::get_attr_num(const string &name)
00640 {
00641     Attr_iter iter = simple_find(name);
00642     return (iter != attr_map.end()) ? get_attr_num(iter) : 0;
00643 }
00644 
00657 vector<string> *
00658 AttrTable::get_attr_vector(const string &name)
00659 {
00660     Attr_iter p = simple_find(name);
00661     return (p != attr_map.end()) ? get_attr_vector(p) : 0;
00662 }
00663 
00680 void AttrTable::del_attr(const string &name, int i)
00681 {
00682 #if WWW_ENCODING
00683     string lname = www2id(name);
00684 #else
00685     string lname = remove_space_encoding(name);
00686 #endif
00687 
00688     Attr_iter iter = simple_find(lname);
00689     if (iter != attr_map.end()) {
00690         if (i == -1) { // Delete the whole attribute
00691             entry *e = *iter;
00692             attr_map.erase(iter);
00693             delete e;
00694             e = 0;
00695         }
00696         else { // Delete one element from attribute array
00697             // Don't try to delete elements from the vector of values if the
00698             // map is a container!
00699             if ((*iter)->type == Attr_container)
00700                 return;
00701 
00702             vector<string> *sxp = (*iter)->attr;
00703 
00704             assert(i >= 0 && i < (int) sxp->size());
00705             sxp->erase(sxp->begin() + i); // rm the element
00706         }
00707     }
00708 }
00709 
00711 
00716 AttrTable::Attr_iter AttrTable::attr_begin()
00717 {
00718     return attr_map.begin();
00719 }
00720 
00724 AttrTable::Attr_iter AttrTable::attr_end()
00725 {
00726     return attr_map.end();
00727 }
00728 
00737 AttrTable::Attr_iter AttrTable::get_attr_iter(int i)
00738 {
00739     return attr_map.begin() + i;
00740 }
00741 
00743 string AttrTable::get_name(Attr_iter iter)
00744 {
00745     assert(iter != attr_map.end());
00746 
00747     return (*iter)->name;
00748 }
00749 
00751 bool AttrTable::is_container(Attr_iter i)
00752 {
00753     return (*i)->type == Attr_container;
00754 }
00755 
00761 AttrTable *
00762 AttrTable::get_attr_table(Attr_iter iter)
00763 {
00764     assert(iter != attr_map.end());
00765     return (*iter)->type == Attr_container ? (*iter)->attributes : 0;
00766 }
00767 
00776 AttrTable::Attr_iter AttrTable::del_attr_table(Attr_iter iter)
00777 {
00778     if ((*iter)->type != Attr_container)
00779         return ++iter;
00780 
00781     // the caller intends to delete/reuse the contained AttrTable,
00782     // so zero it out so it doesn't get deleted before we delete the entry
00783     // [mjohnson]
00784     struct entry* e = *iter;
00785     // container no longer has a parent.
00786     if (e->attributes) {
00787         e->attributes->d_parent = 0;
00788     }
00789     e->attributes = 0;
00790     delete e;
00791 
00792     return attr_map.erase(iter);
00793 }
00794 
00798 string AttrTable::get_type(Attr_iter iter)
00799 {
00800     assert(iter != attr_map.end());
00801     return AttrType_to_String((*iter)->type);
00802 }
00803 
00807 AttrType AttrTable::get_attr_type(Attr_iter iter)
00808 {
00809     return (*iter)->type;
00810 }
00811 
00819 unsigned int AttrTable::get_attr_num(Attr_iter iter)
00820 {
00821     assert(iter != attr_map.end());
00822     return ((*iter)->type == Attr_container) ? (*iter)->attributes->get_size() : (*iter)->attr->size();
00823 }
00824 
00841 string AttrTable::get_attr(Attr_iter iter, unsigned int i)
00842 {
00843     assert(iter != attr_map.end());
00844 
00845     return (*iter)->type == Attr_container ? (string) "None" : (*(*iter)->attr)[i];
00846 }
00847 
00848 string AttrTable::get_attr(const string &name, unsigned int i)
00849 {
00850     Attr_iter p = simple_find(name);
00851     return (p != attr_map.end()) ? get_attr(p, i) : (string) "";
00852 }
00853 
00865 vector<string> *
00866 AttrTable::get_attr_vector(Attr_iter iter)
00867 {
00868     assert(iter != attr_map.end());
00869     return (*iter)->type != Attr_container ? (*iter)->attr : 0;
00870 }
00871 
00872 bool AttrTable::is_global_attribute(Attr_iter iter)
00873 {
00874     assert(iter != attr_map.end());
00875     if ((*iter)->type == Attr_container)
00876         return (*iter)->attributes->is_global_attribute();
00877     else
00878         return (*iter)->is_global;
00879 }
00880 
00881 void AttrTable::set_is_global_attribute(Attr_iter iter, bool ga)
00882 {
00883     assert(iter != attr_map.end());
00884     if ((*iter)->type == Attr_container)
00885         (*iter)->attributes->set_is_global_attribute(ga);
00886     else
00887         (*iter)->is_global = ga;
00888 }
00889 
00891 
00892 // Alias an attribute table. The alias should be added to this object.
00898 void AttrTable::add_container_alias(const string &name, AttrTable *src)
00899 {
00900 #if WWW_ENCODING
00901     string lname = www2id(name);
00902 #else
00903     string lname = remove_space_encoding(name);
00904 #endif
00905 
00906     if (simple_find(lname) != attr_end())
00907         throw Error(string("There already exists a container called `") + name + string("in this attribute table. (2)"));
00908 
00909     entry *e = new entry;
00910     e->name = lname;
00911     e->is_alias = true;
00912     e->aliased_to = src->get_name();
00913     e->type = Attr_container;
00914 
00915     e->attributes = src;
00916 
00917     attr_map.push_back(e);
00918 }
00919 
00932 void AttrTable::add_value_alias(AttrTable *das, const string &name, const string &source)
00933 {
00934 #if WWW_ENCODING
00935     string lname = www2id(name);
00936 #else
00937     string lname = remove_space_encoding(name);
00938 #endif
00939 
00940 #if WWW_ENCODING
00941     string lsource = www2id(source);
00942 #else
00943     string lsource = remove_space_encoding(source);
00944 #endif
00945 
00946     // find the container that holds source and its (sources's) iterator
00947     // within that container. Search at the uppermost level of the attribute
00948     // object to find values defined `above' the current container.
00949     AttrTable *at;
00950     Attr_iter iter;
00951     das->find(lsource, &at, &iter);
00952 
00953     // If source is not found by looking at the topmost level, look in the
00954     // current table (i.e., alias z x where x is in the current container
00955     // won't be found by looking for `x' at the top level). See test case 26
00956     // in das-testsuite.
00957     if (!at || (iter == at->attr_end()) || !*iter) {
00958         find(lsource, &at, &iter);
00959         if (!at || (iter == at->attr_end()) || !*iter)
00960             throw Error(string("Could not find the attribute `") + source + string("' in the attribute object."));
00961     }
00962 
00963     // If we've got a value to alias and it's being added at the top level of
00964     // the DAS, that's an error.
00965     if (at && !at->is_container(iter) && this == das)
00966         throw Error(
00967                 string(
00968                         "A value cannot be aliased to the top level of the DAS;\nOnly containers may be present at that level of the DAS."));
00969 
00970     if (simple_find(lname) != attr_end())
00971         throw Error(string("There already exists a container called `") + name + string("in this attribute table. (3)"));
00972 
00973     entry *e = new entry;
00974     e->name = lname;
00975     e->is_alias = true;
00976     e->aliased_to = lsource;
00977     e->type = get_attr_type(iter);
00978     if (at && e->type == Attr_container)
00979         e->attributes = at->get_attr_table(iter);
00980     else
00981         e->attr = (*iter)->attr;
00982 
00983     attr_map.push_back(e);
00984 }
00985 
00986 // Deprecated
01005 bool AttrTable::attr_alias(const string &alias, AttrTable *at, const string &name)
01006 {
01007     add_value_alias(at, alias, name);
01008     return true;
01009 }
01010 
01018 bool AttrTable::attr_alias(const string &alias, const string &name)
01019 {
01020     return attr_alias(alias, this, name);
01021 }
01022 
01026 void AttrTable::erase()
01027 {
01028     for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
01029         delete *i;
01030         *i = 0;
01031     }
01032 
01033     attr_map.erase(attr_map.begin(), attr_map.end());
01034 
01035     d_name = "";
01036 }
01037 
01038 const string double_quote = "\"";
01039 
01040 // This is here as a result of the problem described in ticket #1163 where
01041 // the data handlers are adding quotes to string attributes so the DAS will
01042 // be printed correctly. But that has the affect of adding the quotes to the
01043 // attribute's _value_ not just it's print representation. As part of the fix
01044 // I made the code here add the quotes if the handlers are fixed (but not if
01045 // handlers are still adding them). The other part of 1163 is to fix all of
01046 // the handlers... What this fix means is that attributes whose values really
01047 // do contain bracketing quotes might be misunderstood, since we're assuming
01048 // those quotes were added by the handlers as a hack to get the output
01049 // formatting correct for the DAS. jhrg 7/30/08
01050 
01051 static void write_string_attribute_for_das(ostream &out, const string &value, const string &term)
01052 {
01053     if (is_quoted(value))
01054         out << value << term;
01055     else
01056         out << double_quote << value << double_quote << term;
01057 }
01058 
01059 #if 0
01060 static void
01061 write_string_attribute_for_das(FILE *out, const string &value, const string &term)
01062 {
01063     if (is_quoted(value))
01064     fprintf(out, "%s%s", value.c_str(), term.c_str());
01065     else
01066     fprintf(out, "\"%s\"%s", value.c_str(), term.c_str());
01067 }
01068 #endif
01069 
01070 // Special treatment for XML: Make sure to escape double quotes when XML is
01071 // printed in a DAS.
01072 static void write_xml_attribute_for_das(ostream &out, const string &value, const string &term)
01073 {
01074     if (is_quoted(value))
01075         out << escape_double_quotes(value) << term;
01076     else
01077         out << double_quote << escape_double_quotes(value) << double_quote << term;
01078 }
01079 
01080 #if 0
01081 static void
01082 write_xml_attribute_for_das(FILE *out, const string &value, const string &term)
01083 {
01084     if (is_quoted(value))
01085     fprintf(out, "%s%s", escape_double_quotes(value).c_str(), term.c_str());
01086     else
01087     fprintf(out, "\"%s\"%s", escape_double_quotes(value).c_str(), term.c_str());
01088 }
01089 #endif
01090 
01093 void AttrTable::simple_print(FILE *out, string pad, Attr_iter i, bool dereference)
01094 {
01095     ostringstream oss;
01096     simple_print(oss, pad, i, dereference);
01097     fwrite(oss.str().data(), 1, oss.str().length(), out);
01098 
01099 #if 0
01100     switch ((*i)->type) {
01101         case Attr_container:
01102 #if WWW_ENCODING
01103         fprintf(out, "%s%s {\n", pad.c_str(), id2www(get_name(i)).c_str());
01104 #else
01105         fprintf(out, "%s%s {\n", pad.c_str(), get_name(i).c_str());
01106 #endif
01107         (*i)->attributes->print(out, pad + "    ", dereference);
01108 
01109         fprintf(out, "%s}\n", pad.c_str());
01110         break;
01111 
01112         case Attr_string: {
01113 #if WWW_ENCODING
01114             fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), id2www(get_name(i)).c_str());
01115 #else
01116             fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), get_name(i).c_str());
01117 #endif
01118             vector<string> *sxp = (*i)->attr;
01119             vector<string>::iterator last = sxp->end() - 1;
01120             for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
01121                 write_string_attribute_for_das(out, *i, ", ");
01122             }
01123             write_string_attribute_for_das(out, *last, ";\n");
01124         }
01125         break;
01126 
01127         case Attr_other_xml: {
01128 #if WWW_ENCODING
01129             fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), id2www(get_name(i)).c_str());
01130 #else
01131             fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), get_name(i).c_str());
01132 #endif
01133             vector<string> *sxp = (*i)->attr;
01134             vector<string>::iterator last = sxp->end() - 1;
01135             for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
01136                 write_xml_attribute_for_das(out, *i, ", ");
01137             }
01138             write_xml_attribute_for_das(out, *last, ";\n");
01139         }
01140         break;
01141 
01142         default: {
01143 #if WWW_ENCODING
01144             fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), id2www(get_name(i)).c_str());
01145 #else
01146             fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), get_name(i).c_str());
01147 #endif
01148 
01149             vector<string> *sxp = (*i)->attr;
01150             vector<string>::iterator last = sxp->end() - 1;
01151             for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
01152                 fprintf(out, "%s%s", (*i).c_str(), ", ");
01153             }
01154             fprintf(out, "%s%s", (*last).c_str(), ";\n");
01155         }
01156         break;
01157     }
01158 #endif
01159 }
01160 
01163 void AttrTable::simple_print(ostream &out, string pad, Attr_iter i, bool dereference)
01164 {
01165     switch ((*i)->type) {
01166     case Attr_container:
01167 #if WWW_ENCODING
01168         out << pad << id2www(get_name(i)) << " {\n";
01169 #else
01170         out << pad << add_space_encoding(get_name(i)) << " {\n";
01171 #endif
01172         (*i)->attributes->print(out, pad + "    ", dereference);
01173         out << pad << "}\n";
01174         break;
01175 
01176     case Attr_string: {
01177 #if WWW_ENCODING
01178         out << pad << get_type(i) << " " << id2www(get_name(i)) << " ";
01179 #else
01180         out << pad << get_type(i) << " " << add_space_encoding(get_name(i)) << " ";
01181 #endif
01182         vector<string> *sxp = (*i)->attr;
01183         vector<string>::iterator last = sxp->end() - 1;
01184         for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
01185             write_string_attribute_for_das(out, *i, ", ");
01186         }
01187         write_string_attribute_for_das(out, *last, ";\n");
01188     }
01189         break;
01190 
01191     case Attr_other_xml: {
01192 #if WWW_ENCODING
01193         out << pad << get_type(i) << " " << id2www(get_name(i)) << " ";
01194 #else
01195         out << pad << get_type(i) << " " << add_space_encoding(get_name(i)) << " ";
01196 #endif
01197         vector<string> *sxp = (*i)->attr;
01198         vector<string>::iterator last = sxp->end() - 1;
01199         for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
01200             write_xml_attribute_for_das(out, *i, ", ");
01201         }
01202         write_xml_attribute_for_das(out, *last, ";\n");
01203     }
01204         break;
01205 
01206     default: {
01207 #if WWW_ENCODING
01208         out << pad << get_type(i) << " " << id2www(get_name(i)) << " ";
01209 #else
01210         out << pad << get_type(i) << " " << add_space_encoding(get_name(i)) << " ";
01211 #endif
01212         vector<string> *sxp = (*i)->attr;
01213         vector<string>::iterator last = sxp->end() - 1;
01214         for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
01215             out << *i << ", ";
01216         }
01217         out << *last << ";\n";
01218     }
01219         break;
01220     }
01221 }
01222 
01233 void AttrTable::print(FILE *out, string pad, bool dereference)
01234 {
01235     ostringstream oss;
01236     print(oss, pad, dereference);
01237     fwrite(oss.str().data(), 1, oss.str().length(), out);
01238 
01239 #if 0
01240     for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
01241         if ((*i)->is_alias) {
01242             if (dereference) {
01243                 simple_print(out, pad, i, dereference);
01244             }
01245             else {
01246 #if WWW_ENCODING
01247                 fprintf(out, "%sAlias %s %s;\n",
01248                         pad.c_str(),
01249                         id2www(get_name(i)).c_str(),
01250                         id2www((*i)->aliased_to).c_str());
01251 #else
01252                 fprintf(out, "%sAlias %s %s;\n",
01253                         pad.c_str(), add_space_encoding(get_name(i)).c_str(), add_space_encoding((*i)->aliased_to).c_str());
01254 
01255 #endif
01256             }
01257         }
01258         else {
01259             simple_print(out, pad, i, dereference);
01260         }
01261     }
01262 #endif
01263 }
01264 
01275 void AttrTable::print(ostream &out, string pad, bool dereference)
01276 {
01277     for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
01278         if ((*i)->is_alias) {
01279             if (dereference) {
01280                 simple_print(out, pad, i, dereference);
01281             }
01282             else {
01283 #if WWW_ENCODING
01284                 out << pad << "Alias " << id2www(get_name(i))
01285                 << " " << id2www((*i)->aliased_to) << ";\n";
01286 #else
01287                 out << pad << "Alias " << add_space_encoding(get_name(i)) << " "
01288                         << add_space_encoding((*i)->aliased_to) << ";\n";
01289 #endif
01290             }
01291         }
01292         else {
01293             simple_print(out, pad, i, dereference);
01294         }
01295     }
01296 }
01297 
01303 void AttrTable::print_xml(FILE *out, string pad, bool /*constrained*/)
01304 {
01305     ostringstream oss;
01306     print_xml(oss, pad);
01307     fwrite(oss.str().data(), 1, oss.str().length(), out);
01308 
01309 #if 0
01310     // Why this works: AttrTable is really a hacked class that used to
01311     // implement a single-level set of attributes. Containers
01312     // were added several years later by dropping in the 'entry' structure.
01313     // It's not a class in its own right; instead accessors from AttrTable
01314     // are used to access information from entry. So... the loop below
01315     // actually iterates over the entries of *this* (which is an instance of
01316     // AttrTable). A container is an entry whose sole value is an AttrTable
01317     // instance. 05/19/03 jhrg
01318     for (Attr_iter i = attr_begin(); i != attr_end(); ++i) {
01319         if ((*i)->is_alias) {
01320             fprintf(out, "%s<Alias name=\"%s\" Attribute=\"%s\"/>\n",
01321                     pad.c_str(), id2xml(get_name(i)).c_str(),
01322                     (*i)->aliased_to.c_str());
01323 
01324         }
01325         else if (is_container(i)) {
01326             fprintf(out, "%s<Attribute name=\"%s\" type=\"%s\">\n",
01327                     pad.c_str(), id2xml(get_name(i)).c_str(),
01328                     get_type(i).c_str());
01329 
01330             get_attr_table(i)->print_xml(out, pad + "    "/*, constrained*/);
01331 
01332             fprintf(out, "%s</Attribute>\n", pad.c_str());
01333         }
01334         else {
01335             fprintf(out, "%s<Attribute name=\"%s\" type=\"%s\">\n",
01336                     pad.c_str(), id2xml(get_name(i)).c_str(), get_type(i).c_str());
01337 
01338             string value_pad = pad + "    ";
01339             // Special handling for the OtherXML attribute type - don't escape
01340             // the XML and don't include the <value> element. Note that there
01341             // cannot be an vector of XML things as can be with the other types.
01342             if (get_attr_type(i) == Attr_other_xml) {
01343                 if (get_attr_num(i) != 1)
01344                 throw Error("OtherXML attributes cannot be vector-valued.");
01345                 fprintf(out, "%s%s\n", value_pad.c_str(), get_attr(i, 0).c_str());
01346             }
01347             else {
01348                 for (unsigned j = 0; j < get_attr_num(i); ++j) {
01349                     fprintf(out, "%s<value>%s</value>\n", value_pad.c_str(),
01350                             id2xml(get_attr(i, j)).c_str());
01351                 }
01352             }
01353             fprintf(out, "%s</Attribute>\n", pad.c_str());
01354         }
01355     }
01356 #endif
01357 }
01358 
01362 void AttrTable::print_xml(ostream &out, string pad, bool /*constrained*/)
01363 {
01364     XMLWriter xml(pad);
01365     print_xml_writer(xml);
01366     out << xml.get_doc();
01367 
01368 #if 0
01369     for (Attr_iter i = attr_begin(); i != attr_end(); ++i) {
01370         if ((*i)->is_alias) {
01371             out << pad << "<Alias name=\"" << id2xml(get_name(i))
01372             << "\" Attribute=\"" << (*i)->aliased_to << "\"/>\n";
01373 
01374         }
01375         else if (is_container(i)) {
01376             out << pad << "<Attribute name=\"" << id2xml(get_name(i))
01377             << "\" type=\"" << get_type(i) << "\">\n";
01378 
01379             get_attr_table(i)->print_xml(out, pad + "    "/*, constrained*/);
01380 
01381             out << pad << "</Attribute>\n";
01382         }
01383         else {
01384             out << pad << "<Attribute name=\"" << id2xml(get_name(i))
01385             << "\" type=\"" << get_type(i) << "\">\n";
01386 
01387             string value_pad = pad + "    ";
01388             if (get_attr_type(i) == Attr_other_xml) {
01389                 if (get_attr_num(i) != 1)
01390                 throw Error("OtherXML attributes cannot be vector-valued.");
01391                 out << value_pad << get_attr(i, 0) << "\n";
01392             }
01393             else {
01394                 string value_pad = pad + "    ";
01395                 for (unsigned j = 0; j < get_attr_num(i); ++j) {
01396                     out << value_pad << "<value>" << id2xml(get_attr(i, j)) << "</value>\n";
01397                 }
01398             }
01399             out << pad << "</Attribute>\n";
01400         }
01401     }
01402 #endif
01403 }
01404 
01409 void AttrTable::print_xml_writer(XMLWriter &xml)
01410 {
01411     for (Attr_iter i = attr_begin(); i != attr_end(); ++i) {
01412         if ((*i)->is_alias) {
01413             if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Alias") < 0)
01414                 throw InternalErr(__FILE__, __LINE__, "Could not write Alias element");
01415             if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name",
01416                     (const xmlChar*) get_name(i).c_str()) < 0)
01417                 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
01418             if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "Attribute",
01419                     (const xmlChar*) (*i)->aliased_to.c_str()) < 0)
01420                 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
01421             if (xmlTextWriterEndElement(xml.get_writer()) < 0)
01422                 throw InternalErr(__FILE__, __LINE__, "Could not end Alias element");
01423         }
01424         else if (is_container(i)) {
01425             if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Attribute") < 0)
01426                 throw InternalErr(__FILE__, __LINE__, "Could not write Attribute element");
01427             if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name",
01428                     (const xmlChar*) get_name(i).c_str()) < 0)
01429                 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
01430             if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "type",
01431                     (const xmlChar*) get_type(i).c_str()) < 0)
01432                 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
01433 
01434             get_attr_table(i)->print_xml_writer(xml);
01435 
01436             if (xmlTextWriterEndElement(xml.get_writer()) < 0)
01437                 throw InternalErr(__FILE__, __LINE__, "Could not end Attribute element");
01438         }
01439         else {
01440             if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Attribute") < 0)
01441                 throw InternalErr(__FILE__, __LINE__, "Could not write Attribute element");
01442             if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name",
01443                     (const xmlChar*) get_name(i).c_str()) < 0)
01444                 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
01445             if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "type",
01446                     (const xmlChar*) get_type(i).c_str()) < 0)
01447                 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
01448 
01449             if (get_attr_type(i) == Attr_other_xml) {
01450                 if (get_attr_num(i) != 1)
01451                     throw Error("OtherXML attributes cannot be vector-valued.");
01452                 // Replaced xmltextWriterWriteString with xmlTextWriterWriteRaw to keep the
01453                 // libxml2 code from escaping the xml (which was breaking all of the inferencing
01454                 // code. jhrg
01455                 if (xmlTextWriterWriteRaw(xml.get_writer(), (const xmlChar*) get_attr(i, 0).c_str()) < 0)
01456                     throw InternalErr(__FILE__, __LINE__, "Could not write OtherXML value");
01457             }
01458             else {
01459                 for (unsigned j = 0; j < get_attr_num(i); ++j) {
01460                     if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "value") < 0)
01461                         throw InternalErr(__FILE__, __LINE__, "Could not write value element");
01462 
01463                     if (xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) get_attr(i, j).c_str()) < 0)
01464                         throw InternalErr(__FILE__, __LINE__, "Could not write attribute value");
01465 
01466                     if (xmlTextWriterEndElement(xml.get_writer()) < 0)
01467                         throw InternalErr(__FILE__, __LINE__, "Could not end value element");
01468                 }
01469             }
01470             if (xmlTextWriterEndElement(xml.get_writer()) < 0)
01471                 throw InternalErr(__FILE__, __LINE__, "Could not end Attribute element");
01472         }
01473     }
01474 }
01475 
01483 void AttrTable::dump(ostream &strm) const
01484 {
01485     strm << DapIndent::LMarg << "AttrTable::dump - (" << (void *) this << ")" << endl;
01486     DapIndent::Indent();
01487     strm << DapIndent::LMarg << "table name: " << d_name << endl;
01488     if (attr_map.size()) {
01489         strm << DapIndent::LMarg << "attributes: " << endl;
01490         DapIndent::Indent();
01491         Attr_citer i = attr_map.begin();
01492         Attr_citer ie = attr_map.end();
01493         for (; i != ie; ++i) {
01494             entry *e = (*i);
01495             string type = AttrType_to_String(e->type);
01496             if (e->is_alias) {
01497                 strm << DapIndent::LMarg << "alias: " << e->name << " aliased to: " << e->aliased_to << endl;
01498             }
01499             else if (e->type == Attr_container) {
01500                 strm << DapIndent::LMarg << "attr: " << e->name << " of type " << type << endl;
01501                 DapIndent::Indent();
01502                 e->attributes->dump(strm);
01503                 DapIndent::UnIndent();
01504             }
01505             else {
01506                 strm << DapIndent::LMarg << "attr: " << e->name << " of type " << type << endl;
01507                 DapIndent::Indent();
01508                 strm << DapIndent::LMarg;
01509                 vector<string>::const_iterator iter = e->attr->begin();
01510                 vector<string>::const_iterator last = e->attr->end() - 1;
01511                 for (; iter != last; ++iter) {
01512                     strm << (*iter) << ", ";
01513                 }
01514                 strm << (*(e->attr->end() - 1)) << endl;
01515                 DapIndent::UnIndent();
01516             }
01517         }
01518         DapIndent::UnIndent();
01519     }
01520     else {
01521         strm << DapIndent::LMarg << "attributes: empty" << endl;
01522     }
01523     if (d_parent) {
01524         strm << DapIndent::LMarg << "parent table:" << d_name << ":" << (void *) d_parent << endl;
01525     }
01526     else {
01527         strm << DapIndent::LMarg << "parent table: none" << d_name << endl;
01528     }
01529     DapIndent::UnIndent();
01530 }
01531 
01532 } // namespace libdap
01533