00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #include "config.h"
00035
00036 static char rcsid[] not_used = "$Id: AttrTable.cc 18315 2008-03-03 20:14:44Z jimg $";
00037
00038 #include <cassert>
00039
00040 #include "AttrTable.h"
00041
00042 #include "util.h"
00043 #include "escaping.h"
00044
00045 #include "debug.h"
00046
00047 using std::cerr;
00048 using std::string;
00049 using std::endl;
00050 using std::vector;
00051
00052 namespace libdap {
00053
00057 string
00058 AttrType_to_String(const AttrType at)
00059 {
00060 switch (at) {
00061 case Attr_container: return "Container";
00062 case Attr_byte: return "Byte";
00063 case Attr_int16: return "Int16";
00064 case Attr_uint16: return "UInt16";
00065 case Attr_int32: return "Int32";
00066 case Attr_uint32: return "UInt32";
00067 case Attr_float32: return "Float32";
00068 case Attr_float64: return "Float64";
00069 case Attr_string: return "String";
00070 case Attr_url: return "Url";
00071 default: return "";
00072 }
00073 }
00074
00075 AttrType
00076 String_to_AttrType(const string &s)
00077 {
00078 string s2 = s;
00079 downcase(s2);
00080
00081 if (s2 == "container")
00082 return Attr_container;
00083 else if (s2 == "byte")
00084 return Attr_byte;
00085 else if (s2 == "int16")
00086 return Attr_int16;
00087 else if (s2 == "uint16")
00088 return Attr_uint16;
00089 else if (s2 == "int32")
00090 return Attr_int32;
00091 else if (s2 == "uint32")
00092 return Attr_uint32;
00093 else if (s2 == "float32")
00094 return Attr_float32;
00095 else if (s2 == "float64")
00096 return Attr_float64;
00097 else if (s2 == "string")
00098 return Attr_string;
00099 else if (s2 == "url")
00100 return Attr_url;
00101 else
00102 return Attr_unknown;
00103 }
00104
00107 void
00108 AttrTable::clone(const AttrTable &at)
00109 {
00110 d_name = at.d_name;
00111
00112 Attr_citer i = at.attr_map.begin() ;
00113 Attr_citer ie = at.attr_map.end() ;
00114 for (; i != ie; i++) {
00115 entry *e = new entry(*(*i)) ;
00116 attr_map.push_back(e) ;
00117 }
00118
00119 d_parent = at.d_parent;
00120 }
00121
00125 AttrTable::AttrTable() : d_name(""), d_parent(0)
00126 {}
00127
00128 AttrTable::AttrTable(const AttrTable &rhs) : DapObj()
00129 {
00130 clone(rhs);
00131 }
00132
00133
00134 void
00135 AttrTable::delete_attr_table()
00136 {
00137 for (Attr_iter i = attr_map.begin(); i != attr_map.end(); i++) {
00138 delete *i; *i = 0;
00139 }
00140 }
00141
00142 AttrTable::~AttrTable()
00143 {
00144 DBG(cerr << "Entering ~AttrTable (" << this << ")" << endl);
00145 delete_attr_table();
00146 DBG(cerr << "Exiting ~AttrTable" << endl);
00147 }
00148
00149 AttrTable &
00150 AttrTable::operator=(const AttrTable &rhs)
00151 {
00152 if (this != &rhs) {
00153 delete_attr_table();
00154 clone(rhs);
00155 }
00156
00157 return *this;
00158 }
00160
00166 unsigned int
00167 AttrTable::get_size() const
00168 {
00169 return attr_map.size();
00170 }
00171
00174 string
00175 AttrTable::get_name() const
00176 {
00177 return d_name;
00178 }
00179
00182 void
00183 AttrTable::set_name(const string &n)
00184 {
00185 d_name = www2id(n);
00186 }
00187
00205 unsigned int
00206 AttrTable::append_attr(const string &name, const string &type,
00207 const string &attribute)
00208 {
00209 string lname = www2id(name);
00210
00211 Attr_iter iter = simple_find(lname) ;
00212
00213
00214
00215 if (iter != attr_map.end() && ((*iter)->type != String_to_AttrType(type)))
00216 throw Error(string("An attribute called `") + name
00217 + string("' already exists but is of a different type"));
00218 if (iter != attr_map.end() && (get_type(iter) == "Container"))
00219 throw Error(string("An attribute called `") + name
00220 + string("' already exists but is a container."));
00221
00222 if (iter != attr_map.end()) {
00223 (*iter)->attr->push_back(attribute);
00224 return (*iter)->attr->size();
00225 }
00226 else {
00227 entry *e = new entry;
00228
00229 e->name = lname;
00230 e->is_alias = false;
00231 e->type = String_to_AttrType(type);
00232 e->attr = new vector<string>;
00233 e->attr->push_back(attribute);
00234
00235 attr_map.push_back(e);
00236
00237 return e->attr->size();
00238 }
00239 }
00240
00258 unsigned int
00259 AttrTable::append_attr(const string &name, const string &type,
00260 vector<string> *values)
00261 {
00262 string lname = www2id(name);
00263
00264 Attr_iter iter = simple_find(lname) ;
00265
00266
00267
00268 if (iter != attr_map.end() && ((*iter)->type != String_to_AttrType(type)))
00269 throw Error(string("An attribute called `") + name
00270 + string("' already exists but is of a different type"));
00271 if (iter != attr_map.end() && (get_type(iter) == "Container"))
00272 throw Error(string("An attribute called `") + name
00273 + string("' already exists but is a container."));
00274
00275 if (iter != attr_map.end()) {
00276 vector<string>::iterator i = values->begin();
00277 while (i != values->end())
00278 (*iter)->attr->push_back(*i++);
00279
00280 return (*iter)->attr->size();
00281 }
00282 else {
00283 entry *e = new entry;
00284
00285 e->name = lname;
00286 e->is_alias = false;
00287 e->type = String_to_AttrType(type);
00288 e->attr = new vector<string>(*values);
00289
00290 attr_map.push_back(e);
00291
00292 return e->attr->size();
00293 }
00294 }
00295
00304 AttrTable *
00305 AttrTable::append_container(const string &name)
00306 {
00307 AttrTable *new_at = new AttrTable ;
00308 AttrTable *ret = NULL ;
00309 try {
00310 ret = append_container(new_at, name) ;
00311 }
00312 catch (Error &e) {
00313
00314 delete new_at; new_at = 0;
00315 throw e;
00316 }
00317 return ret;
00318 }
00319
00332 AttrTable *
00333 AttrTable::append_container(AttrTable *at, const string &name)
00334 {
00335 string lname = www2id(name);
00336
00337 if (simple_find(name) != attr_end())
00338 throw Error(string("There already exists a container called `")
00339 + name + string("' in this attribute table."));
00340 DBG(cerr << "Setting appended attribute container name to: "
00341 << lname << endl);
00342 at->set_name(lname);
00343
00344 entry *e = new entry;
00345 e->name = lname;
00346 e->is_alias = false;
00347 e->type = Attr_container;
00348 e->attributes = at;
00349
00350 attr_map.push_back(e);
00351
00352 at->d_parent = this;
00353
00354 return e->attributes;
00355 }
00356
00371 void
00372 AttrTable::find(const string &target, AttrTable **at, Attr_iter *iter)
00373 {
00374 string::size_type dotpos = target.rfind('.');
00375 if (dotpos != string::npos) {
00376 string container = target.substr(0, dotpos);
00377 string field = target.substr(dotpos + 1);
00378
00379 *at = find_container(container) ;
00380 if (*at) {
00381 *iter = (*at)->simple_find(field) ;
00382 }
00383 else {
00384 *iter = attr_map.end() ;
00385 }
00386 }
00387 else {
00388 #if 0
00389
00390
00391
00392 *at = this;
00393 *iter = simple_find(target);
00394 #endif
00395 *at = recurrsive_find(target, iter);
00396 }
00397 }
00398
00410 AttrTable *
00411 AttrTable::recurrsive_find(const string &target, Attr_iter *location)
00412 {
00413
00414 Attr_iter i = attr_begin();
00415 while (i != attr_end()) {
00416 if (target == (*i)->name) {
00417 *location = i;
00418 return this;
00419 }
00420 else if ((*i)->type == Attr_container) {
00421 AttrTable *at = (*i)->attributes->recurrsive_find(target, location);
00422 if (at)
00423 return at;
00424 }
00425
00426 ++i;
00427 }
00428
00429 *location = i;
00430 return 0;
00431 }
00432
00433
00440 AttrTable::Attr_iter
00441 AttrTable::simple_find(const string &target)
00442 {
00443 Attr_iter i ;
00444 for (i = attr_map.begin(); i != attr_map.end(); i++) {
00445 if (target == (*i)->name) {
00446 break ;
00447 }
00448 }
00449 return i ;
00450 }
00451
00465 AttrTable *
00466 AttrTable::find_container(const string &target)
00467 {
00468 string::size_type dotpos = target.find('.');
00469 if (dotpos != string::npos) {
00470 string container = target.substr(0, dotpos);
00471 string field = target.substr(dotpos + 1);
00472
00473 AttrTable *at = simple_find_container(container);
00474 return (at) ? at->find_container(field) : 0;
00475 }
00476 else {
00477 return simple_find_container(target);
00478 }
00479 }
00480
00481
00482 AttrTable *
00483 AttrTable::simple_find_container(const string &target)
00484 {
00485 if (get_name() == target)
00486 return this;
00487
00488 for (Attr_iter i = attr_map.begin(); i != attr_map.end(); i++) {
00489 if (is_container(i) && target == (*i)->name) {
00490 return (*i)->attributes;
00491 }
00492 }
00493
00494 return 0;
00495 }
00496
00504
00506 AttrTable *
00507 AttrTable::get_attr_table(const string &name)
00508 {
00509 return find_container(name);
00510 }
00511
00513 string
00514 AttrTable::get_type(const string &name)
00515 {
00516 Attr_iter p = simple_find(name);
00517 return (p != attr_map.end()) ? get_type(p) : (string)"";
00518 }
00519
00522 AttrType
00523 AttrTable::get_attr_type(const string &name)
00524 {
00525 Attr_iter p = simple_find(name);
00526 return (p != attr_map.end()) ? get_attr_type(p) : Attr_unknown;
00527 }
00528
00536 unsigned int
00537 AttrTable::get_attr_num(const string &name)
00538 {
00539 Attr_iter iter = simple_find(name);
00540 return (iter != attr_map.end()) ? get_attr_num(iter) : 0;
00541 }
00542
00555 vector<string> *
00556 AttrTable::get_attr_vector(const string &name)
00557 {
00558 Attr_iter p = simple_find(name);
00559 return (p != attr_map.end()) ? get_attr_vector(p) : 0;
00560 }
00561
00578 void
00579 AttrTable::del_attr(const string &name, int i)
00580 {
00581 string lname = www2id(name);
00582
00583 Attr_iter iter = simple_find(lname) ;
00584 if (iter != attr_map.end()) {
00585 if (i == -1) {
00586 entry *e = *iter ;
00587 attr_map.erase(iter) ;
00588 delete e ; e = 0;
00589 }
00590 else {
00591
00592
00593 if ((*iter)->type == Attr_container)
00594 return;
00595
00596 vector<string> *sxp = (*iter)->attr;
00597
00598 assert(i >= 0 && i < (int)sxp->size());
00599 sxp->erase(sxp->begin() + i);
00600 }
00601 }
00602 }
00603
00605
00610 AttrTable::Attr_iter
00611 AttrTable::attr_begin()
00612 {
00613 return attr_map.begin() ;
00614 }
00615
00619 AttrTable::Attr_iter
00620 AttrTable::attr_end()
00621 {
00622 return attr_map.end() ;
00623 }
00624
00633 AttrTable::Attr_iter
00634 AttrTable::get_attr_iter(int i)
00635 {
00636 return attr_map.begin() + i;
00637 }
00638
00640 string
00641 AttrTable::get_name(Attr_iter iter)
00642 {
00643 assert(iter != attr_map.end()) ;
00644
00645 return (*iter)->name ;
00646 }
00647
00649 bool
00650 AttrTable::is_container(Attr_iter i)
00651 {
00652 return (*i)->type == Attr_container ;
00653 }
00654
00660 AttrTable *
00661 AttrTable::get_attr_table(Attr_iter iter)
00662 {
00663 assert(iter != attr_map.end()) ;
00664 return (*iter)->type == Attr_container ? (*iter)->attributes : 0 ;
00665 }
00666
00674 AttrTable::Attr_iter
00675 AttrTable::del_attr_table(Attr_iter iter)
00676 {
00677 if ((*iter)->type != Attr_container)
00678 return ++iter;
00679
00680 return attr_map.erase(iter);
00681 }
00682
00686 string
00687 AttrTable::get_type(Attr_iter iter)
00688 {
00689 assert(iter != attr_map.end()) ;
00690 return AttrType_to_String((*iter)->type) ;
00691 }
00692
00696 AttrType
00697 AttrTable::get_attr_type(Attr_iter iter)
00698 {
00699 return (*iter)->type ;
00700 }
00701
00709 unsigned int
00710 AttrTable::get_attr_num(Attr_iter iter)
00711 {
00712 assert(iter != attr_map.end()) ;
00713 return ((*iter)->type == Attr_container)
00714 ? (*iter)->attributes->get_size()
00715 : (*iter)->attr->size() ;
00716 }
00717
00734 string
00735 AttrTable::get_attr(Attr_iter iter, unsigned int i)
00736 {
00737 assert(iter != attr_map.end());
00738 #if 1
00739 return (*iter)->type == Attr_container ? (string)"None" : (*(*iter)->attr)[i];
00740 #else
00741 if ((*iter)->type == Attr_container) {
00742 return "None";
00743 }
00744 else {
00745 cerr << "(*iter)->attr: " << (*iter)->attr << endl;
00746 cerr << "(*iter)->name: " << (*iter)->name << endl;
00747 cerr << "(*iter)->type: " << (*iter)->type << endl;
00748
00749 if ((*iter)->name == "SIS_ID")
00750 return "SIS_ID_value";
00751 else
00752 return (*(*iter)->attr)[i];
00753 }
00754 #endif
00755 }
00756
00757 string
00758 AttrTable::get_attr(const string &name, unsigned int i)
00759 {
00760 Attr_iter p = simple_find(name);
00761 return (p != attr_map.end()) ? get_attr(p, i) : (string)"";
00762 }
00763
00775 vector<string> *
00776 AttrTable::get_attr_vector(Attr_iter iter)
00777 {
00778 assert(iter != attr_map.end());
00779 return (*iter)->type != Attr_container ? (*iter)->attr : 0;
00780 }
00781
00783
00784
00790 void
00791 AttrTable::add_container_alias(const string &name, AttrTable *src)
00792 {
00793 string lname = www2id(name);
00794
00795 if (simple_find(lname) != attr_end())
00796 throw Error(string("There already exists a container called `")
00797 + name + string("in this attribute table."));
00798
00799 entry *e = new entry;
00800 e->name = lname;
00801 e->is_alias = true;
00802 e->aliased_to = src->get_name();
00803 e->type = Attr_container;
00804
00805 e->attributes = src;
00806
00807 attr_map.push_back(e);
00808 }
00809
00822 void
00823 AttrTable::add_value_alias(AttrTable *das, const string &name,
00824 const string &source)
00825 {
00826 string lname = www2id(name);
00827 string lsource = www2id(source);
00828
00829
00830
00831
00832 AttrTable *at;
00833 Attr_iter iter;
00834 das->find(lsource, &at, &iter);
00835
00836
00837
00838
00839
00840 if (!at || (iter == at->attr_end()) || !*iter) {
00841 find(lsource, &at, &iter);
00842 if (!at || (iter == at->attr_end()) || !*iter)
00843 throw Error(string("Could not find the attribute `")
00844 + source + string("' in the attribute object."));
00845 }
00846
00847
00848
00849 if (at && !at->is_container(iter) && this == das)
00850 throw Error(string("A value cannot be aliased to the top level of the DAS;\nOnly containers may be present at that level of the DAS."));
00851
00852 if (simple_find(lname) != attr_end())
00853 throw Error(string("There already exists a container called `")
00854 + name + string("in this attribute table."));
00855
00856 entry *e = new entry;
00857 e->name = lname;
00858 e->is_alias = true;
00859 e->aliased_to = lsource;
00860 e->type = get_attr_type(iter);
00861 if (at && e->type == Attr_container)
00862 e->attributes = at->get_attr_table(iter);
00863 else
00864 e->attr = (*iter)->attr;
00865
00866 attr_map.push_back(e);
00867 }
00868
00869
00888 bool
00889 AttrTable::attr_alias(const string &alias, AttrTable *at, const string &name)
00890 {
00891 add_value_alias(at, alias, name);
00892 return true;
00893 }
00894
00902 bool
00903 AttrTable::attr_alias(const string &alias, const string &name)
00904 {
00905 return attr_alias(alias, this, name);
00906 }
00907
00911 void
00912 AttrTable::erase()
00913 {
00914 for (Attr_iter i = attr_map.begin(); i != attr_map.end(); i++) {
00915 delete *i; *i = 0;
00916 }
00917
00918 attr_map.erase(attr_map.begin(), attr_map.end());
00919
00920 d_name = "";
00921 }
00922
00923
00926 void
00927 AttrTable::simple_print(FILE *out, string pad, Attr_iter i,
00928 bool dereference)
00929 {
00930 switch ((*i)->type) {
00931 case Attr_container:
00932 fprintf(out, "%s%s {\n", pad.c_str(), id2www(get_name(i)).c_str()) ;
00933
00934 (*i)->attributes->print(out, pad + " ", dereference);
00935
00936 fprintf(out, "%s}\n", pad.c_str()) ;
00937 break;
00938
00939 default: {
00940 fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(),
00941 id2www(get_name(i)).c_str()) ;
00942
00943 vector<string> *sxp = (*i)->attr;
00944
00945 vector<string>::iterator last = sxp->end() - 1;
00946 for (vector<string>::iterator i = sxp->begin(); i != last; ++i)
00947 fprintf(out, "%s, ", (*i).c_str()) ;
00948
00949 fprintf(out, "%s;\n", (*(sxp->end() - 1)).c_str()) ;
00950 }
00951 break;
00952 }
00953 }
00954
00957 void
00958 AttrTable::simple_print(ostream &out, string pad, Attr_iter i,
00959 bool dereference)
00960 {
00961 switch ((*i)->type) {
00962 case Attr_container:
00963 out << pad << id2www(get_name(i)) << " {\n" ;
00964
00965 (*i)->attributes->print(out, pad + " ", dereference);
00966
00967 out << pad << "}\n" ;
00968 break;
00969
00970 default: {
00971 out << pad << get_type(i) << " " << id2www(get_name(i)) << " " ;
00972
00973 vector<string> *sxp = (*i)->attr;
00974
00975 vector<string>::iterator last = sxp->end() - 1;
00976 for (vector<string>::iterator i = sxp->begin(); i != last; ++i)
00977 out << (*i) << ", " ;
00978
00979 out << (*(sxp->end() - 1)) << ";\n" ;
00980 }
00981 break;
00982 }
00983 }
00984
00995 void
00996 AttrTable::print(FILE *out, string pad, bool dereference)
00997 {
00998 for (Attr_iter i = attr_map.begin(); i != attr_map.end(); i++) {
00999 if ((*i)->is_alias) {
01000 if (dereference) {
01001 simple_print(out, pad, i, dereference);
01002 }
01003 else {
01004 fprintf(out, "%sAlias %s %s;\n",
01005 pad.c_str(),
01006 id2www(get_name(i)).c_str(),
01007 id2www((*i)->aliased_to).c_str()) ;
01008 }
01009 }
01010 else {
01011 simple_print(out, pad, i, dereference);
01012 }
01013 }
01014 }
01015
01026 void
01027 AttrTable::print(ostream &out, string pad, bool dereference)
01028 {
01029 for (Attr_iter i = attr_map.begin(); i != attr_map.end(); i++) {
01030 if ((*i)->is_alias) {
01031 if (dereference) {
01032 simple_print(out, pad, i, dereference);
01033 }
01034 else {
01035 out << pad << "Alias " << id2www(get_name(i))
01036 << " " << id2www((*i)->aliased_to) << ";\n" ;
01037 }
01038 }
01039 else {
01040 simple_print(out, pad, i, dereference);
01041 }
01042 }
01043 }
01044
01050 void
01051 AttrTable::print_xml(FILE *out, string pad, bool constrained)
01052 {
01053
01054
01055
01056
01057
01058
01059
01060
01061 for (Attr_iter i = attr_begin(); i != attr_end(); ++i) {
01062
01063
01064
01065
01066
01067 if ((*i)->is_alias) {
01068 fprintf(out, "%s<Alias name=\"%s\" Attribute=\"%s\">\n",
01069 pad.c_str(), id2xml(get_name(i)).c_str(),
01070 (*i)->aliased_to.c_str());
01071
01072 }
01073 else if (is_container(i)) {
01074 fprintf(out, "%s<Attribute name=\"%s\" type=\"%s\">\n",
01075 pad.c_str(), id2xml(get_name(i)).c_str(),
01076 get_type(i).c_str());
01077
01078 get_attr_table(i)->print_xml(out, pad + " ", constrained);
01079
01080 fprintf(out, "%s</Attribute>\n", pad.c_str());
01081 }
01082 else {
01083 fprintf(out, "%s<Attribute name=\"%s\" type=\"%s\">\n",
01084 pad.c_str(), id2xml(get_name(i)).c_str(), get_type(i).c_str());
01085
01086 string value_pad = pad + " ";
01087 for (unsigned j = 0; j < get_attr_num(i); ++j) {
01088 fprintf(out, "%s<value>%s</value>\n", value_pad.c_str(),
01089 id2xml(get_attr(i, j)).c_str());
01090 }
01091
01092 fprintf(out, "%s</Attribute>\n", pad.c_str());
01093 }
01094 }
01095 }
01096
01102 void
01103 AttrTable::print_xml(ostream &out, string pad, bool constrained)
01104 {
01105
01106
01107
01108
01109
01110
01111
01112
01113 for (Attr_iter i = attr_begin(); i != attr_end(); ++i) {
01114
01115
01116
01117
01118
01119 if ((*i)->is_alias) {
01120 out << pad << "<Alias name=\"" << id2xml(get_name(i))
01121 << "\" Attribute=\"" << (*i)->aliased_to << "\">\n" ;
01122
01123 }
01124 else if (is_container(i)) {
01125 out << pad << "<Attribute name=\"" << id2xml(get_name(i))
01126 << "\" type=\"" << get_type(i) << "\">\n" ;
01127
01128 get_attr_table(i)->print_xml(out, pad + " ", constrained);
01129
01130 out << pad << "</Attribute>\n" ;
01131 }
01132 else {
01133 out << pad << "<Attribute name=\"" << id2xml(get_name(i))
01134 << "\" type=\"" << get_type(i) << "\">\n" ;
01135
01136 string value_pad = pad + " ";
01137 for (unsigned j = 0; j < get_attr_num(i); ++j) {
01138 out << value_pad << "<value>" << id2xml(get_attr(i, j)) << "</value>\n" ;
01139 }
01140
01141 out << pad << "</Attribute>\n" ;
01142 }
01143 }
01144 }
01145
01153 void
01154 AttrTable::dump(ostream &strm) const
01155 {
01156 strm << DapIndent::LMarg << "AttrTable::dump - ("
01157 << (void *)this << ")" << endl ;
01158 DapIndent::Indent() ;
01159 strm << DapIndent::LMarg << "table name: " << d_name << endl ;
01160 if (attr_map.size()) {
01161 strm << DapIndent::LMarg << "attributes: " << endl ;
01162 DapIndent::Indent() ;
01163 Attr_citer i = attr_map.begin() ;
01164 Attr_citer ie = attr_map.end() ;
01165 for (; i != ie; i++) {
01166 entry *e = (*i) ;
01167 string type = AttrType_to_String(e->type) ;
01168 if (e->is_alias) {
01169 strm << DapIndent::LMarg << "alias: " << e->name
01170 << " aliased to: " << e->aliased_to
01171 << endl ;
01172 }
01173 else if (e->type == Attr_container) {
01174 strm << DapIndent::LMarg << "attr: " << e->name
01175 << " of type " << type
01176 << endl ;
01177 DapIndent::Indent() ;
01178 e->attributes->dump(strm) ;
01179 DapIndent::UnIndent() ;
01180 }
01181 else {
01182 strm << DapIndent::LMarg << "attr: " << e->name
01183 << " of type " << type
01184 << endl ;
01185 DapIndent::Indent() ;
01186 strm << DapIndent::LMarg ;
01187 vector<string>::const_iterator iter = e->attr->begin() ;
01188 vector<string>::const_iterator last = e->attr->end() - 1 ;
01189 for (; iter != last; iter++) {
01190 strm << (*iter) << ", " ;
01191 }
01192 strm << (*(e->attr->end() - 1)) << endl ;
01193 DapIndent::UnIndent() ;
01194 }
01195 }
01196 DapIndent::UnIndent() ;
01197 }
01198 else {
01199 strm << DapIndent::LMarg << "attributes: empty" << endl ;
01200 }
01201 if (d_parent) {
01202 strm << DapIndent::LMarg << "parent table:"
01203 << d_name << ":" << (void *)d_parent << endl ;
01204 }
01205 else {
01206 strm << DapIndent::LMarg << "parent table: none" << d_name << endl ;
01207 }
01208 DapIndent::UnIndent() ;
01209 }
01210
01211 }
01212