libdap  Updated for version 3.17.0
AttrTable.cc
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  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 #include <cassert>
00036 #include <sstream>
00037 
00038 #include "AttrTable.h"
00039 
00040 #include "util.h"
00041 #include "escaping.h"
00042 
00043 #include "debug.h"
00044 
00045 // Should the www2id and id2www functions be used to encode attribute names?
00046 // Probably not... jhrg 11/16/11
00047 #define WWW_ENCODING 0
00048 // See the note for del_attr_table(). That method now deletes the contained
00049 // AttrTable.
00050 #define NEW_DEL_ATTR_TABLE_BEHAVIOR 0
00051 
00052 using std::cerr;
00053 using std::string;
00054 using std::endl;
00055 using std::vector;
00056 
00057 namespace libdap {
00058 
00060 static string remove_space_encoding(const string &s)
00061 {
00062     string::size_type pos = s.find("%20");
00063     if (pos != string::npos) {
00064         string n = s;
00065         do {
00066             n.replace(pos, 3, " ");
00067             pos = n.find("%20");
00068         } while (pos != string::npos);
00069         return n;
00070     }
00071     else {
00072         return s;
00073     }
00074 }
00075 
00077 static string add_space_encoding(const string &s)
00078 {
00079     string::size_type pos = s.find(" ");
00080     if (pos != string::npos) {
00081         string n = s;
00082         do {
00083             n.replace(pos, 1, "%20");
00084             pos = n.find(" ");
00085         } while (pos != string::npos);
00086         return n;
00087     }
00088     else {
00089         return s;
00090     }
00091 }
00092 
00096 string AttrType_to_String(const AttrType at)
00097 {
00098     switch (at) {
00099     case Attr_container:
00100         return "Container";
00101     case Attr_byte:
00102         return "Byte";
00103     case Attr_int16:
00104         return "Int16";
00105     case Attr_uint16:
00106         return "UInt16";
00107     case Attr_int32:
00108         return "Int32";
00109     case Attr_uint32:
00110         return "UInt32";
00111     case Attr_float32:
00112         return "Float32";
00113     case Attr_float64:
00114         return "Float64";
00115     case Attr_string:
00116         return "String";
00117     case Attr_url:
00118         return "Url";
00119     case Attr_other_xml:
00120         return "OtherXML";
00121     default:
00122         return "";
00123     }
00124 }
00125 
00126 AttrType String_to_AttrType(const string &s)
00127 {
00128     string s2 = s;
00129     downcase(s2);
00130 
00131     if (s2 == "container")
00132         return Attr_container;
00133     else if (s2 == "byte")
00134         return Attr_byte;
00135     else if (s2 == "int16")
00136         return Attr_int16;
00137     else if (s2 == "uint16")
00138         return Attr_uint16;
00139     else if (s2 == "int32")
00140         return Attr_int32;
00141     else if (s2 == "uint32")
00142         return Attr_uint32;
00143     else if (s2 == "float32")
00144         return Attr_float32;
00145     else if (s2 == "float64")
00146         return Attr_float64;
00147     else if (s2 == "string")
00148         return Attr_string;
00149     else if (s2 == "url")
00150         return Attr_url;
00151     else if (s2 == "otherxml")
00152         return Attr_other_xml;
00153     else
00154         return Attr_unknown;
00155 }
00156 
00159 void AttrTable::clone(const AttrTable &at)
00160 {
00161     d_name = at.d_name;
00162     d_is_global_attribute = at.d_is_global_attribute;
00163 
00164     // Set the parent to null (no parent, not in container)
00165     // since using at.d_parent is semantically incorrect
00166     // and potentially dangerous.
00167     d_parent = 0;
00168 
00169     Attr_citer i = at.attr_map.begin();
00170     Attr_citer ie = at.attr_map.end();
00171     for (; i != ie; ++i) {
00172         // this deep-copies containers recursively
00173         entry *e = new entry(*(*i));
00174         attr_map.push_back(e);
00175 
00176         // If the entry being added was a container,
00177         // set its parent to this to maintain invariant.
00178         if (e->type == Attr_container) {
00179             assert(e->attributes);
00180             e->attributes->d_parent = this;
00181         }
00182     }
00183 }
00184 
00188 AttrTable::AttrTable() :
00189     DapObj(), d_name(""), d_parent(0), attr_map(), d_is_global_attribute(true)
00190 {
00191 }
00192 
00193 AttrTable::AttrTable(const AttrTable &rhs) :
00194     DapObj()
00195 {
00196     clone(rhs);
00197 }
00198 
00199 // Private
00200 void AttrTable::delete_attr_table()
00201 {
00202     for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
00203         delete *i;
00204     }
00205     attr_map.clear();
00206 }
00207 
00208 AttrTable::~AttrTable()
00209 {
00210     delete_attr_table();
00211 }
00212 
00213 AttrTable &
00214 AttrTable::operator=(const AttrTable &rhs)
00215 {
00216     if (this != &rhs) {
00217         delete_attr_table();
00218         clone(rhs);
00219     }
00220 
00221     return *this;
00222 }
00224 
00230 unsigned int AttrTable::get_size() const
00231 {
00232     return attr_map.size();
00233 }
00234 
00237 string AttrTable::get_name() const
00238 {
00239     return d_name;
00240 }
00241 
00244 void AttrTable::set_name(const string &n)
00245 {
00246 #if WWW_ENCODING
00247     d_name = www2id(n);
00248 #else
00249     d_name = remove_space_encoding(n);
00250 #endif
00251 }
00252 
00253 #if 0
00254 // This was taken from das.y and could be used here to make the 'dods_errors'
00255 // attribute container like the parser used to. Then again, maybe this feature
00256 // was just BS. jhrg (ticket 1469)
00257 static void add_bad_attribute(AttrTable *attr, const string &type, const string &name, const string &value,
00258         const string &msg) {
00259     // First, if this bad value is already in a *_dods_errors container,
00260     // then just add it. This can happen when the server side processes a DAS
00261     // and then hands it off to a client which does the same.
00262     // Make a new container. Call it <attr's name>_errors. If that container
00263     // already exists, use it.
00264     // Add the attribute.
00265     // Add the error string to an attribute in the container called
00266     // `<name_explanation.'.
00267 
00268     if (attr->get_name().find("_dods_errors") != string::npos) {
00269         attr->append_attr(name, type, value);
00270     }
00271     else {
00272         // I think _dods_errors should be _dap_error. jhrg 11/16/11
00273         string error_cont_name = attr->get_name() + "_dods_errors";
00274         AttrTable *error_cont = attr->get_attr_table(error_cont_name);
00275         if (!error_cont)
00276         error_cont = attr->append_container(error_cont_name);
00277 
00278         error_cont->append_attr(name, type, value);
00279 
00280 #ifndef ATTR_STRING_QUOTE_FIX
00281         error_cont->append_attr(name + "_dap_explanation", "String", "\"" + msg + "\"");
00282 #else
00283         error_cont->append_attr(name + "_dap_explanation", "String", msg);
00284 #endif
00285     }
00286 }
00287 #endif
00288 
00306 unsigned int AttrTable::append_attr(const string &name, const string &type, const string &value)
00307 {
00308     DBG(cerr << "Entering AttrTable::append_attr" << endl);
00309 #if WWW_ENCODING
00310     string lname = www2id(name);
00311 #else
00312     string lname = remove_space_encoding(name);
00313 #endif
00314 
00315     Attr_iter iter = simple_find(lname);
00316 
00317     // If the types don't match OR this attribute is a container, calling
00318     // this mfunc is an error!
00319     if (iter != attr_map.end() && ((*iter)->type != String_to_AttrType(type)))
00320         throw Error(string("An attribute called `") + name + string("' already exists but is of a different type"));
00321     if (iter != attr_map.end() && (get_type(iter) == "Container"))
00322         throw Error(string("An attribute called `") + name + string("' already exists but is a container."));
00323 
00324     if (iter != attr_map.end()) { // Must be a new attribute value; add it.
00325         (*iter)->attr->push_back(value);
00326         return (*iter)->attr->size();
00327     }
00328     else { // Must be a completely new attribute; add it
00329         entry *e = new entry;
00330 
00331         e->name = lname;
00332         e->is_alias = false;
00333         e->type = String_to_AttrType(type); // Record type using standard names.
00334         e->attr = new vector<string> ;
00335         e->attr->push_back(value);
00336 
00337         attr_map.push_back(e);
00338 
00339         return e->attr->size(); // return the length of the attr vector
00340     }
00341 }
00342 
00361 unsigned int AttrTable::append_attr(const string &name, const string &type, vector<string> *values)
00362 {
00363     DBG(cerr << "Entering AttrTable::append_attr(..., vector)" << endl);
00364 #if WWW_ENCODING
00365     string lname = www2id(name);
00366 #else
00367     string lname = remove_space_encoding(name);
00368 #endif
00369     Attr_iter iter = simple_find(lname);
00370 
00371     // If the types don't match OR this attribute is a container, calling
00372     // this mfunc is an error!
00373     if (iter != attr_map.end() && ((*iter)->type != String_to_AttrType(type)))
00374         throw Error(string("An attribute called `") + name + string("' already exists but is of a different type"));
00375     if (iter != attr_map.end() && (get_type(iter) == "Container"))
00376         throw Error(string("An attribute called `") + name + string("' already exists but is a container."));
00377 
00378     if (iter != attr_map.end()) { // Must be new attribute values; add.
00379         vector<string>::iterator i = values->begin();
00380         while (i != values->end())
00381             (*iter)->attr->push_back(*i++);
00382 
00383         return (*iter)->attr->size();
00384     }
00385     else { // Must be a completely new attribute; add it
00386         entry *e = new entry;
00387 
00388         e->name = lname;
00389         e->is_alias = false;
00390         e->type = String_to_AttrType(type); // Record type using standard names.
00391         e->attr = new vector<string> (*values);
00392 
00393         attr_map.push_back(e);
00394 
00395         return e->attr->size(); // return the length of the attr vector
00396     }
00397 }
00398 
00408 AttrTable *
00409 AttrTable::append_container(const string &name)
00410 {
00411     AttrTable *new_at = new AttrTable;
00412     AttrTable *ret = NULL;
00413     try {
00414         ret = append_container(new_at, name);
00415     } catch (Error &e) {
00416         // an error occurred, attribute with that name already exists
00417         delete new_at;
00418         new_at = 0;
00419         throw;
00420     }
00421     return ret;
00422 }
00423 
00438 AttrTable *
00439 AttrTable::append_container(AttrTable *at, const string &name)
00440 {
00441 #if WWW_ENCODING
00442     string lname = www2id(name);
00443 #else
00444     string lname = remove_space_encoding(name);
00445 #endif
00446 
00447     if (simple_find(name) != attr_end())
00448         throw Error("There already exists a container called '" + name + "' in this attribute table (" + at->get_name() + "). (1)");
00449 
00450     DBG(cerr << "Setting appended attribute container name to: " << lname << endl);
00451     at->set_name(lname);
00452 
00453     entry *e = new entry;
00454     e->name = lname;
00455     e->is_alias = false;
00456     e->type = Attr_container;
00457     e->attributes = at;
00458 
00459     attr_map.push_back(e);
00460 
00461     at->d_parent = this;
00462 
00463     return e->attributes;
00464 }
00465 
00480 void AttrTable::find(const string &target, AttrTable **at, Attr_iter *iter)
00481 {
00482     string::size_type dotpos = target.rfind('.');
00483     if (dotpos != string::npos) {
00484         string container = target.substr(0, dotpos);
00485         string field = target.substr(dotpos + 1);
00486 
00487         *at = find_container(container);
00488         if (*at) {
00489             *iter = (*at)->simple_find(field);
00490         }
00491         else {
00492             *iter = attr_map.end();
00493         }
00494     }
00495     else {
00496         *at = recurrsive_find(target, iter);
00497     }
00498 }
00499 
00511 AttrTable *
00512 AttrTable::recurrsive_find(const string &target, Attr_iter *location)
00513 {
00514     Attr_iter i = attr_begin();
00515     while (i != attr_end()) {
00516         if (target == (*i)->name) {
00517             *location = i;
00518             return this;
00519         }
00520         else if ((*i)->type == Attr_container) {
00521             AttrTable *at = (*i)->attributes->recurrsive_find(target, location);
00522             if (at)
00523                 return at;
00524         }
00525 
00526         ++i;
00527     }
00528 
00529     *location = i;
00530     return 0;
00531 }
00532 
00533 // Made public for callers that want non-recursive find.  [mjohnson 6 oct 09]
00540 AttrTable::Attr_iter AttrTable::simple_find(const string &target)
00541 {
00542     Attr_iter i;
00543     for (i = attr_map.begin(); i != attr_map.end(); ++i) {
00544         if (target == (*i)->name) {
00545             break;
00546         }
00547     }
00548     return i;
00549 }
00550 
00564 AttrTable *
00565 AttrTable::find_container(const string &target)
00566 {
00567     string::size_type dotpos = target.find('.');
00568     if (dotpos != string::npos) {
00569         string container = target.substr(0, dotpos);
00570         string field = target.substr(dotpos + 1);
00571 
00572         AttrTable *at = simple_find_container(container);
00573         return (at) ? at->find_container(field) : 0;
00574     }
00575     else {
00576         return simple_find_container(target);
00577     }
00578 }
00579 
00580 // Made public for callers that want non-recursive find.  [mjohnson 6 oct 09]
00581 AttrTable *
00582 AttrTable::simple_find_container(const string &target)
00583 {
00584     if (get_name() == target)
00585         return this;
00586 
00587     for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
00588         if (is_container(i) && target == (*i)->name) {
00589             return (*i)->attributes;
00590         }
00591     }
00592 
00593     return 0;
00594 }
00595 
00603 
00605 AttrTable *
00606 AttrTable::get_attr_table(const string &name)
00607 {
00608     return find_container(name);
00609 }
00610 
00612 string AttrTable::get_type(const string &name)
00613 {
00614     Attr_iter p = simple_find(name);
00615     return (p != attr_map.end()) ? get_type(p) : (string) "";
00616 }
00617 
00620 AttrType AttrTable::get_attr_type(const string &name)
00621 {
00622     Attr_iter p = simple_find(name);
00623     return (p != attr_map.end()) ? get_attr_type(p) : Attr_unknown;
00624 }
00625 
00633 unsigned int AttrTable::get_attr_num(const string &name)
00634 {
00635     Attr_iter iter = simple_find(name);
00636     return (iter != attr_map.end()) ? get_attr_num(iter) : 0;
00637 }
00638 
00651 vector<string> *
00652 AttrTable::get_attr_vector(const string &name)
00653 {
00654     Attr_iter p = simple_find(name);
00655     return (p != attr_map.end()) ? get_attr_vector(p) : 0;
00656 }
00657 
00674 void AttrTable::del_attr(const string &name, int i)
00675 {
00676 #if WWW_ENCODING
00677     string lname = www2id(name);
00678 #else
00679     string lname = remove_space_encoding(name);
00680 #endif
00681 
00682     Attr_iter iter = simple_find(lname);
00683     if (iter != attr_map.end()) {
00684         if (i == -1) { // Delete the whole attribute
00685             entry *e = *iter;
00686             attr_map.erase(iter);
00687             delete e;
00688             e = 0;
00689         }
00690         else { // Delete one element from attribute array
00691             // Don't try to delete elements from the vector of values if the
00692             // map is a container!
00693             if ((*iter)->type == Attr_container)
00694                 return;
00695 
00696             vector<string> *sxp = (*iter)->attr;
00697 
00698             assert(i >= 0 && i < (int) sxp->size());
00699             sxp->erase(sxp->begin() + i); // rm the element
00700         }
00701     }
00702 }
00703 
00705 
00710 AttrTable::Attr_iter AttrTable::attr_begin()
00711 {
00712     return attr_map.begin();
00713 }
00714 
00718 AttrTable::Attr_iter AttrTable::attr_end()
00719 {
00720     return attr_map.end();
00721 }
00722 
00731 AttrTable::Attr_iter AttrTable::get_attr_iter(int i)
00732 {
00733     return attr_map.begin() + i;
00734 }
00735 
00737 string AttrTable::get_name(Attr_iter iter)
00738 {
00739     assert(iter != attr_map.end());
00740 
00741     return (*iter)->name;
00742 }
00743 
00745 bool AttrTable::is_container(Attr_iter i)
00746 {
00747     return (*i)->type == Attr_container;
00748 }
00749 
00755 AttrTable *
00756 AttrTable::get_attr_table(Attr_iter iter)
00757 {
00758     assert(iter != attr_map.end());
00759     return (*iter)->type == Attr_container ? (*iter)->attributes : 0;
00760 }
00761 
00780 AttrTable::Attr_iter AttrTable::del_attr_table(Attr_iter iter)
00781 {
00782     if ((*iter)->type != Attr_container)
00783         return ++iter;
00784 
00785     // the caller intends to delete/reuse the contained AttrTable,
00786     // so zero it out so it doesn't get deleted before we delete the entry
00787     // [mjohnson]
00788     struct entry *e = *iter;
00789     // container no longer has a parent.
00790     if (e->attributes) {
00791         e->attributes->d_parent = 0;
00792 
00793 #if NEW_DEL_ATTR_TABLE_BEHAVIOR
00794         delete e->attributes;
00795 #endif
00796         e->attributes = 0;
00797     }
00798 
00799     delete e;
00800 
00801     return attr_map.erase(iter);
00802 }
00803 
00807 string AttrTable::get_type(Attr_iter iter)
00808 {
00809     assert(iter != attr_map.end());
00810     return AttrType_to_String((*iter)->type);
00811 }
00812 
00816 AttrType AttrTable::get_attr_type(Attr_iter iter)
00817 {
00818     return (*iter)->type;
00819 }
00820 
00828 unsigned int AttrTable::get_attr_num(Attr_iter iter)
00829 {
00830     assert(iter != attr_map.end());
00831     return ((*iter)->type == Attr_container) ? (*iter)->attributes->get_size() : (*iter)->attr->size();
00832 }
00833 
00850 string AttrTable::get_attr(Attr_iter iter, unsigned int i)
00851 {
00852     assert(iter != attr_map.end());
00853 
00854     return (*iter)->type == Attr_container ? (string) "None" : (*(*iter)->attr)[i];
00855 }
00856 
00857 string AttrTable::get_attr(const string &name, unsigned int i)
00858 {
00859     Attr_iter p = simple_find(name);
00860     return (p != attr_map.end()) ? get_attr(p, i) : (string) "";
00861 }
00862 
00874 vector<string> *
00875 AttrTable::get_attr_vector(Attr_iter iter)
00876 {
00877     assert(iter != attr_map.end());
00878     return (*iter)->type != Attr_container ? (*iter)->attr : 0;
00879 }
00880 
00881 bool AttrTable::is_global_attribute(Attr_iter iter)
00882 {
00883     assert(iter != attr_map.end());
00884     if ((*iter)->type == Attr_container)
00885         return (*iter)->attributes->is_global_attribute();
00886     else
00887         return (*iter)->is_global;
00888 }
00889 
00890 void AttrTable::set_is_global_attribute(Attr_iter iter, bool ga)
00891 {
00892     assert(iter != attr_map.end());
00893     if ((*iter)->type == Attr_container)
00894         (*iter)->attributes->set_is_global_attribute(ga);
00895     else
00896         (*iter)->is_global = ga;
00897 }
00898 
00900 
00901 // Alias an attribute table. The alias should be added to this object.
00907 void AttrTable::add_container_alias(const string &name, AttrTable *src)
00908 {
00909 #if WWW_ENCODING
00910     string lname = www2id(name);
00911 #else
00912     string lname = remove_space_encoding(name);
00913 #endif
00914 
00915     if (simple_find(lname) != attr_end())
00916         throw Error(string("There already exists a container called `") + name + string("in this attribute table. (2)"));
00917 
00918     entry *e = new entry;
00919     e->name = lname;
00920     e->is_alias = true;
00921     e->aliased_to = src->get_name();
00922     e->type = Attr_container;
00923 
00924     e->attributes = src;
00925 
00926     attr_map.push_back(e);
00927 }
00928 
00941 void AttrTable::add_value_alias(AttrTable *das, const string &name, const string &source)
00942 {
00943 #if WWW_ENCODING
00944     string lname = www2id(name);
00945 #else
00946     string lname = remove_space_encoding(name);
00947 #endif
00948 
00949 #if WWW_ENCODING
00950     string lsource = www2id(source);
00951 #else
00952     string lsource = remove_space_encoding(source);
00953 #endif
00954 
00955     // find the container that holds source and its (sources's) iterator
00956     // within that container. Search at the uppermost level of the attribute
00957     // object to find values defined `above' the current container.
00958     AttrTable *at;
00959     Attr_iter iter;
00960     das->find(lsource, &at, &iter);
00961 
00962     // If source is not found by looking at the topmost level, look in the
00963     // current table (i.e., alias z x where x is in the current container
00964     // won't be found by looking for `x' at the top level). See test case 26
00965     // in das-testsuite.
00966     if (!at || (iter == at->attr_end()) || !*iter) {
00967         find(lsource, &at, &iter);
00968         if (!at || (iter == at->attr_end()) || !*iter)
00969             throw Error(string("Could not find the attribute `") + source + string("' in the attribute object."));
00970     }
00971 
00972     // If we've got a value to alias and it's being added at the top level of
00973     // the DAS, that's an error.
00974     if (at && !at->is_container(iter) && this == das)
00975         throw Error(
00976                 string(
00977                         "A value cannot be aliased to the top level of the DAS;\nOnly containers may be present at that level of the DAS."));
00978 
00979     if (simple_find(lname) != attr_end())
00980         throw Error(string("There already exists a container called `") + name + string("in this attribute table. (3)"));
00981 
00982     entry *e = new entry;
00983     e->name = lname;
00984     e->is_alias = true;
00985     e->aliased_to = lsource;
00986     e->type = get_attr_type(iter);
00987     if (at && e->type == Attr_container)
00988         e->attributes = at->get_attr_table(iter);
00989     else
00990         e->attr = (*iter)->attr;
00991 
00992     attr_map.push_back(e);
00993 }
00994 
00995 // Deprecated
01014 bool AttrTable::attr_alias(const string &alias, AttrTable *at, const string &name)
01015 {
01016     add_value_alias(at, alias, name);
01017     return true;
01018 }
01019 
01027 bool AttrTable::attr_alias(const string &alias, const string &name)
01028 {
01029     return attr_alias(alias, this, name);
01030 }
01031 
01035 void AttrTable::erase()
01036 {
01037     for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
01038         delete *i;
01039         *i = 0;
01040     }
01041 
01042     attr_map.erase(attr_map.begin(), attr_map.end());
01043 
01044     d_name = "";
01045 }
01046 
01047 const string double_quote = "\"";
01048 
01049 // This is here as a result of the problem described in ticket #1163 where
01050 // the data handlers are adding quotes to string attributes so the DAS will
01051 // be printed correctly. But that has the affect of adding the quotes to the
01052 // attribute's _value_ not just it's print representation. As part of the fix
01053 // I made the code here add the quotes if the handlers are fixed (but not if
01054 // handlers are still adding them). The other part of 1163 is to fix all of
01055 // the handlers... What this fix means is that attributes whose values really
01056 // do contain bracketing quotes might be misunderstood, since we're assuming
01057 // those quotes were added by the handlers as a hack to get the output
01058 // formatting correct for the DAS. jhrg 7/30/08
01059 
01060 static void write_string_attribute_for_das(ostream &out, const string &value, const string &term)
01061 {
01062     if (is_quoted(value))
01063         out << value << term;
01064     else
01065         out << double_quote << value << double_quote << term;
01066 }
01067 
01068 #if 0
01069 static void
01070 write_string_attribute_for_das(FILE *out, const string &value, const string &term)
01071 {
01072     if (is_quoted(value))
01073     fprintf(out, "%s%s", value.c_str(), term.c_str());
01074     else
01075     fprintf(out, "\"%s\"%s", value.c_str(), term.c_str());
01076 }
01077 #endif
01078 
01079 // Special treatment for XML: Make sure to escape double quotes when XML is
01080 // printed in a DAS.
01081 static void write_xml_attribute_for_das(ostream &out, const string &value, const string &term)
01082 {
01083     if (is_quoted(value))
01084         out << escape_double_quotes(value) << term;
01085     else
01086         out << double_quote << escape_double_quotes(value) << double_quote << term;
01087 }
01088 
01089 #if 0
01090 static void
01091 write_xml_attribute_for_das(FILE *out, const string &value, const string &term)
01092 {
01093     if (is_quoted(value))
01094     fprintf(out, "%s%s", escape_double_quotes(value).c_str(), term.c_str());
01095     else
01096     fprintf(out, "\"%s\"%s", escape_double_quotes(value).c_str(), term.c_str());
01097 }
01098 #endif
01099 
01102 void AttrTable::simple_print(FILE *out, string pad, Attr_iter i, bool dereference)
01103 {
01104     ostringstream oss;
01105     simple_print(oss, pad, i, dereference);
01106     fwrite(oss.str().data(), 1, oss.str().length(), out);
01107 
01108 #if 0
01109     switch ((*i)->type) {
01110         case Attr_container:
01111 #if WWW_ENCODING
01112         fprintf(out, "%s%s {\n", pad.c_str(), id2www(get_name(i)).c_str());
01113 #else
01114         fprintf(out, "%s%s {\n", pad.c_str(), get_name(i).c_str());
01115 #endif
01116         (*i)->attributes->print(out, pad + "    ", dereference);
01117 
01118         fprintf(out, "%s}\n", pad.c_str());
01119         break;
01120 
01121         case Attr_string: {
01122 #if WWW_ENCODING
01123             fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), id2www(get_name(i)).c_str());
01124 #else
01125             fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), get_name(i).c_str());
01126 #endif
01127             vector<string> *sxp = (*i)->attr;
01128             vector<string>::iterator last = sxp->end() - 1;
01129             for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
01130                 write_string_attribute_for_das(out, *i, ", ");
01131             }
01132             write_string_attribute_for_das(out, *last, ";\n");
01133         }
01134         break;
01135 
01136         case Attr_other_xml: {
01137 #if WWW_ENCODING
01138             fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), id2www(get_name(i)).c_str());
01139 #else
01140             fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), get_name(i).c_str());
01141 #endif
01142             vector<string> *sxp = (*i)->attr;
01143             vector<string>::iterator last = sxp->end() - 1;
01144             for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
01145                 write_xml_attribute_for_das(out, *i, ", ");
01146             }
01147             write_xml_attribute_for_das(out, *last, ";\n");
01148         }
01149         break;
01150 
01151         default: {
01152 #if WWW_ENCODING
01153             fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), id2www(get_name(i)).c_str());
01154 #else
01155             fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), get_name(i).c_str());
01156 #endif
01157 
01158             vector<string> *sxp = (*i)->attr;
01159             vector<string>::iterator last = sxp->end() - 1;
01160             for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
01161                 fprintf(out, "%s%s", (*i).c_str(), ", ");
01162             }
01163             fprintf(out, "%s%s", (*last).c_str(), ";\n");
01164         }
01165         break;
01166     }
01167 #endif
01168 }
01169 
01172 void AttrTable::simple_print(ostream &out, string pad, Attr_iter i, bool dereference)
01173 {
01174     switch ((*i)->type) {
01175     case Attr_container:
01176 #if WWW_ENCODING
01177         out << pad << id2www(get_name(i)) << " {\n";
01178 #else
01179         out << pad << add_space_encoding(get_name(i)) << " {\n";
01180 #endif
01181         (*i)->attributes->print(out, pad + "    ", dereference);
01182         out << pad << "}\n";
01183         break;
01184 
01185     case Attr_string: {
01186 #if WWW_ENCODING
01187         out << pad << get_type(i) << " " << id2www(get_name(i)) << " ";
01188 #else
01189         out << pad << get_type(i) << " " << add_space_encoding(get_name(i)) << " ";
01190 #endif
01191         vector<string> *sxp = (*i)->attr;
01192         vector<string>::iterator last = sxp->end() - 1;
01193         for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
01194             write_string_attribute_for_das(out, *i, ", ");
01195         }
01196         write_string_attribute_for_das(out, *last, ";\n");
01197     }
01198         break;
01199 
01200     case Attr_other_xml: {
01201 #if WWW_ENCODING
01202         out << pad << get_type(i) << " " << id2www(get_name(i)) << " ";
01203 #else
01204         out << pad << get_type(i) << " " << add_space_encoding(get_name(i)) << " ";
01205 #endif
01206         vector<string> *sxp = (*i)->attr;
01207         vector<string>::iterator last = sxp->end() - 1;
01208         for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
01209             write_xml_attribute_for_das(out, *i, ", ");
01210         }
01211         write_xml_attribute_for_das(out, *last, ";\n");
01212     }
01213         break;
01214 
01215     default: {
01216 #if WWW_ENCODING
01217         out << pad << get_type(i) << " " << id2www(get_name(i)) << " ";
01218 #else
01219         out << pad << get_type(i) << " " << add_space_encoding(get_name(i)) << " ";
01220 #endif
01221         vector<string> *sxp = (*i)->attr;
01222         vector<string>::iterator last = sxp->end() - 1;
01223         for (vector<string>::iterator i = sxp->begin(); i != last; ++i) {
01224             out << *i << ", ";
01225         }
01226         out << *last << ";\n";
01227     }
01228         break;
01229     }
01230 }
01231 
01242 void AttrTable::print(FILE *out, string pad, bool dereference)
01243 {
01244     ostringstream oss;
01245     print(oss, pad, dereference);
01246     fwrite(oss.str().data(), 1, oss.str().length(), out);
01247 
01248 #if 0
01249     for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
01250         if ((*i)->is_alias) {
01251             if (dereference) {
01252                 simple_print(out, pad, i, dereference);
01253             }
01254             else {
01255 #if WWW_ENCODING
01256                 fprintf(out, "%sAlias %s %s;\n",
01257                         pad.c_str(),
01258                         id2www(get_name(i)).c_str(),
01259                         id2www((*i)->aliased_to).c_str());
01260 #else
01261                 fprintf(out, "%sAlias %s %s;\n",
01262                         pad.c_str(), add_space_encoding(get_name(i)).c_str(), add_space_encoding((*i)->aliased_to).c_str());
01263 
01264 #endif
01265             }
01266         }
01267         else {
01268             simple_print(out, pad, i, dereference);
01269         }
01270     }
01271 #endif
01272 }
01273 
01284 void AttrTable::print(ostream &out, string pad, bool dereference)
01285 {
01286     for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) {
01287         if ((*i)->is_alias) {
01288             if (dereference) {
01289                 simple_print(out, pad, i, dereference);
01290             }
01291             else {
01292 #if WWW_ENCODING
01293                 out << pad << "Alias " << id2www(get_name(i))
01294                 << " " << id2www((*i)->aliased_to) << ";\n";
01295 #else
01296                 out << pad << "Alias " << add_space_encoding(get_name(i)) << " "
01297                         << add_space_encoding((*i)->aliased_to) << ";\n";
01298 #endif
01299             }
01300         }
01301         else {
01302             simple_print(out, pad, i, dereference);
01303         }
01304     }
01305 }
01306 
01312 void AttrTable::print_xml(FILE *out, string pad, bool /*constrained*/)
01313 {
01314     XMLWriter xml(pad);
01315     print_xml_writer(xml);
01316     fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
01317 
01318 #if OLD_XML_MOETHODS
01319     ostringstream oss;
01320     print_xml(oss, pad);
01321     fwrite(oss.str().data(), 1, oss.str().length(), out);
01322 #endif
01323 
01324 #if 0
01325     // Why this works: AttrTable is really a hacked class that used to
01326     // implement a single-level set of attributes. Containers
01327     // were added several years later by dropping in the 'entry' structure.
01328     // It's not a class in its own right; instead accessors from AttrTable
01329     // are used to access information from entry. So... the loop below
01330     // actually iterates over the entries of *this* (which is an instance of
01331     // AttrTable). A container is an entry whose sole value is an AttrTable
01332     // instance. 05/19/03 jhrg
01333     for (Attr_iter i = attr_begin(); i != attr_end(); ++i) {
01334         if ((*i)->is_alias) {
01335             fprintf(out, "%s<Alias name=\"%s\" Attribute=\"%s\"/>\n",
01336                     pad.c_str(), id2xml(get_name(i)).c_str(),
01337                     (*i)->aliased_to.c_str());
01338 
01339         }
01340         else if (is_container(i)) {
01341             fprintf(out, "%s<Attribute name=\"%s\" type=\"%s\">\n",
01342                     pad.c_str(), id2xml(get_name(i)).c_str(),
01343                     get_type(i).c_str());
01344 
01345             get_attr_table(i)->print_xml(out, pad + "    "/*, constrained*/);
01346 
01347             fprintf(out, "%s</Attribute>\n", pad.c_str());
01348         }
01349         else {
01350             fprintf(out, "%s<Attribute name=\"%s\" type=\"%s\">\n",
01351                     pad.c_str(), id2xml(get_name(i)).c_str(), get_type(i).c_str());
01352 
01353             string value_pad = pad + "    ";
01354             // Special handling for the OtherXML attribute type - don't escape
01355             // the XML and don't include the <value> element. Note that there
01356             // cannot be an vector of XML things as can be with the other types.
01357             if (get_attr_type(i) == Attr_other_xml) {
01358                 if (get_attr_num(i) != 1)
01359                 throw Error("OtherXML attributes cannot be vector-valued.");
01360                 fprintf(out, "%s%s\n", value_pad.c_str(), get_attr(i, 0).c_str());
01361             }
01362             else {
01363                 for (unsigned j = 0; j < get_attr_num(i); ++j) {
01364                     fprintf(out, "%s<value>%s</value>\n", value_pad.c_str(),
01365                             id2xml(get_attr(i, j)).c_str());
01366                 }
01367             }
01368             fprintf(out, "%s</Attribute>\n", pad.c_str());
01369         }
01370     }
01371 #endif
01372 }
01373 
01377 void AttrTable::print_xml(ostream &out, string pad, bool /*constrained*/)
01378 {
01379     XMLWriter xml(pad);
01380     print_xml_writer(xml);
01381     out << xml.get_doc();
01382 
01383 #if 0
01384     for (Attr_iter i = attr_begin(); i != attr_end(); ++i) {
01385         if ((*i)->is_alias) {
01386             out << pad << "<Alias name=\"" << id2xml(get_name(i))
01387             << "\" Attribute=\"" << (*i)->aliased_to << "\"/>\n";
01388 
01389         }
01390         else if (is_container(i)) {
01391             out << pad << "<Attribute name=\"" << id2xml(get_name(i))
01392             << "\" type=\"" << get_type(i) << "\">\n";
01393 
01394             get_attr_table(i)->print_xml(out, pad + "    "/*, constrained*/);
01395 
01396             out << pad << "</Attribute>\n";
01397         }
01398         else {
01399             out << pad << "<Attribute name=\"" << id2xml(get_name(i))
01400             << "\" type=\"" << get_type(i) << "\">\n";
01401 
01402             string value_pad = pad + "    ";
01403             if (get_attr_type(i) == Attr_other_xml) {
01404                 if (get_attr_num(i) != 1)
01405                 throw Error("OtherXML attributes cannot be vector-valued.");
01406                 out << value_pad << get_attr(i, 0) << "\n";
01407             }
01408             else {
01409                 string value_pad = pad + "    ";
01410                 for (unsigned j = 0; j < get_attr_num(i); ++j) {
01411                     out << value_pad << "<value>" << id2xml(get_attr(i, j)) << "</value>\n";
01412                 }
01413             }
01414             out << pad << "</Attribute>\n";
01415         }
01416     }
01417 #endif
01418 }
01419 
01424 void AttrTable::print_xml_writer(XMLWriter &xml)
01425 {
01426     for (Attr_iter i = attr_begin(); i != attr_end(); ++i) {
01427         if ((*i)->is_alias) {
01428             if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Alias") < 0)
01429                 throw InternalErr(__FILE__, __LINE__, "Could not write Alias element");
01430             if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name",
01431                     (const xmlChar*) get_name(i).c_str()) < 0)
01432                 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
01433             if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "Attribute",
01434                     (const xmlChar*) (*i)->aliased_to.c_str()) < 0)
01435                 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
01436             if (xmlTextWriterEndElement(xml.get_writer()) < 0)
01437                 throw InternalErr(__FILE__, __LINE__, "Could not end Alias element");
01438         }
01439         else if (is_container(i)) {
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             get_attr_table(i)->print_xml_writer(xml);
01450 
01451             if (xmlTextWriterEndElement(xml.get_writer()) < 0)
01452                 throw InternalErr(__FILE__, __LINE__, "Could not end Attribute element");
01453         }
01454         else {
01455             if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Attribute") < 0)
01456                 throw InternalErr(__FILE__, __LINE__, "Could not write Attribute element");
01457             if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name",
01458                     (const xmlChar*) get_name(i).c_str()) < 0)
01459                 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
01460             if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "type",
01461                     (const xmlChar*) get_type(i).c_str()) < 0)
01462                 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
01463 
01464             if (get_attr_type(i) == Attr_other_xml) {
01465                 if (get_attr_num(i) != 1)
01466                     throw Error("OtherXML attributes cannot be vector-valued.");
01467                 // Replaced xmltextWriterWriteString with xmlTextWriterWriteRaw to keep the
01468                 // libxml2 code from escaping the xml (which was breaking all of the inferencing
01469                 // code. jhrg
01470                 if (xmlTextWriterWriteRaw(xml.get_writer(), (const xmlChar*) get_attr(i, 0).c_str()) < 0)
01471                     throw InternalErr(__FILE__, __LINE__, "Could not write OtherXML value");
01472             }
01473             else {
01474                 for (unsigned j = 0; j < get_attr_num(i); ++j) {
01475                     if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "value") < 0)
01476                         throw InternalErr(__FILE__, __LINE__, "Could not write value element");
01477 
01478                     if (xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) get_attr(i, j).c_str()) < 0)
01479                         throw InternalErr(__FILE__, __LINE__, "Could not write attribute value");
01480 
01481                     if (xmlTextWriterEndElement(xml.get_writer()) < 0)
01482                         throw InternalErr(__FILE__, __LINE__, "Could not end value element");
01483                 }
01484             }
01485             if (xmlTextWriterEndElement(xml.get_writer()) < 0)
01486                 throw InternalErr(__FILE__, __LINE__, "Could not end Attribute element");
01487         }
01488     }
01489 }
01490 
01496 void
01497 AttrTable::print_dap4(XMLWriter &xml)
01498 {
01499     print_xml_writer(xml);
01500 }
01501 
01509 void AttrTable::dump(ostream &strm) const
01510 {
01511     strm << DapIndent::LMarg << "AttrTable::dump - (" << (void *) this << ")" << endl;
01512     DapIndent::Indent();
01513     strm << DapIndent::LMarg << "table name: " << d_name << endl;
01514     if (attr_map.size()) {
01515         strm << DapIndent::LMarg << "attributes: " << endl;
01516         DapIndent::Indent();
01517         Attr_citer i = attr_map.begin();
01518         Attr_citer ie = attr_map.end();
01519         for (; i != ie; ++i) {
01520             entry *e = (*i);
01521             string type = AttrType_to_String(e->type);
01522             if (e->is_alias) {
01523                 strm << DapIndent::LMarg << "alias: " << e->name << " aliased to: " << e->aliased_to << endl;
01524             }
01525             else if (e->type == Attr_container) {
01526                 strm << DapIndent::LMarg << "attr: " << e->name << " of type " << type << endl;
01527                 DapIndent::Indent();
01528                 e->attributes->dump(strm);
01529                 DapIndent::UnIndent();
01530             }
01531             else {
01532                 strm << DapIndent::LMarg << "attr: " << e->name << " of type " << type << endl;
01533                 DapIndent::Indent();
01534                 strm << DapIndent::LMarg;
01535                 vector<string>::const_iterator iter = e->attr->begin();
01536                 vector<string>::const_iterator last = e->attr->end() - 1;
01537                 for (; iter != last; ++iter) {
01538                     strm << (*iter) << ", ";
01539                 }
01540                 strm << (*(e->attr->end() - 1)) << endl;
01541                 DapIndent::UnIndent();
01542             }
01543         }
01544         DapIndent::UnIndent();
01545     }
01546     else {
01547         strm << DapIndent::LMarg << "attributes: empty" << endl;
01548     }
01549     if (d_parent) {
01550         strm << DapIndent::LMarg << "parent table:" << d_name << ":" << (void *) d_parent << endl;
01551     }
01552     else {
01553         strm << DapIndent::LMarg << "parent table: none" << d_name << endl;
01554     }
01555     DapIndent::UnIndent();
01556 }
01557 
01558 } // namespace libdap
01559