libdap Updated for version 3.21.1
libdap4 is an implementation of OPeNDAP's DAP protocol.
DDS.cc
Go to the documentation of this file.
1// -*- mode: c++; c-basic-offset:4 -*-
2
3// This file is part of libdap, A C++ implementation of the OPeNDAP Data
4// Access Protocol.
5
6// Copyright (c) 2002,2003 OPeNDAP, Inc.
7// Author: James Gallagher <jgallagher@opendap.org>
8//
9// This library is free software; you can redistribute it and/or
10// modify it under the terms of the GNU Lesser General Public
11// License as published by the Free Software Foundation; either
12// version 2.1 of the License, or (at your option) any later version.
13//
14// This library is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17// Lesser General Public License for more details.
18//
19// You should have received a copy of the GNU Lesser General Public
20// License along with this library; if not, write to the Free Software
21// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22//
23// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
24
25// (c) COPYRIGHT URI/MIT 1994-1999
26// Please read the full copyright statement in the file COPYRIGHT_URI.
27//
28// Authors:
29// jhrg,jimg James Gallagher <jgallagher@gso.uri.edu>
30
31//
32// jhrg 9/7/94
33
34#include "config.h"
35
36#include <climits>
37#include <cmath>
38#include <cstdio>
39
40#ifdef WIN32
41#include <fstream>
42#include <io.h>
43#include <process.h>
44#else
45#include <sys/wait.h>
46#include <unistd.h> // for alarm and dup
47#endif
48
49#include <algorithm>
50#include <functional>
51#include <iostream>
52#include <memory>
53#include <sstream>
54
55#include "Array.h"
56#include "Byte.h"
57#include "Float32.h"
58#include "Float64.h"
59#include "Grid.h"
60#include "Int16.h"
61#include "Int32.h"
62#include "Sequence.h"
63#include "Str.h"
64#include "Structure.h"
65#include "UInt16.h"
66#include "UInt32.h"
67#include "Url.h"
68
69#include "Clause.h"
70#include "DAS.h"
71#include "DapIndent.h"
72#include "Error.h"
73#include "InternalErr.h"
74#include "debug.h"
75#include "escaping.h"
76#include "parser.h"
77#include "util.h"
78
84const string c_xml_xsi = "http://www.w3.org/2001/XMLSchema-instance";
85const string c_xml_namespace = "http://www.w3.org/XML/1998/namespace";
86
87const string grddl_transformation_dap32 = "http://xml.opendap.org/transforms/ddxToRdfTriples.xsl";
88
89const string c_default_dap20_schema_location = "http://xml.opendap.org/dap/dap2.xsd";
90const string c_default_dap32_schema_location = "http://xml.opendap.org/dap/dap3.2.xsd";
91const string c_default_dap40_schema_location = "http://xml.opendap.org/dap/dap4.0.xsd";
92
93const string c_dap20_namespace = "http://xml.opendap.org/ns/DAP2";
94const string c_dap32_namespace = "http://xml.opendap.org/ns/DAP/3.2#";
95const string c_dap40_namespace = "http://xml.opendap.org/ns/DAP/4.0#";
96
100
104const string TOP_LEVEL_ATTRS_CONTAINER_NAME = "DAP4_GLOBAL";
105
106using namespace std;
107
109
110// Glue for the DDS parser defined in dds.lex
111void dds_switch_to_buffer(void *new_buffer);
112void dds_delete_buffer(void *buffer);
113void *dds_buffer(FILE *fp);
114
115namespace libdap {
116
117void DDS::duplicate(const DDS &dds) {
118 DBG(cerr << "Entering DDS::duplicate... " << endl);
119
120 d_factory = dds.d_factory;
121
122 d_name = dds.d_name;
123 d_filename = dds.d_filename;
124 d_container_name = dds.d_container_name;
125 d_container = dds.d_container;
126
127 d_dap_major = dds.d_dap_major;
128 d_dap_minor = dds.d_dap_minor;
129
130 d_dap_version = dds.d_dap_version; // String version of the protocol
131 d_request_xml_base = dds.d_request_xml_base;
132 d_namespace = dds.d_namespace;
133
134 d_attr = dds.d_attr;
135
136 DDS &dds_tmp = const_cast<DDS &>(dds);
137
138 // copy the things pointed to by the list, not just the pointers
139 for (Vars_iter i = dds_tmp.var_begin(); i != dds_tmp.var_end(); i++) {
140 add_var(*i); // add_var() dups the BaseType.
141 }
142
143 d_timeout = dds.d_timeout;
144
145 d_max_response_size_kb = dds.d_max_response_size_kb;
146}
147
160DDS::DDS(BaseTypeFactory *factory, const string &name)
161 : d_factory(factory), d_name(name), d_container_name(""), d_container(0), d_request_xml_base(""), d_timeout(0),
162 /*d_keywords(),*/ d_max_response_size_kb(0) {
163 DBG(cerr << "Building a DDS for the default version (2.0)" << endl);
164
165 // This method sets a number of values, including those returned by
166 // get_protocol_major(), ..., get_namespace().
167 set_dap_version("2.0");
168}
169
185DDS::DDS(BaseTypeFactory *factory, const string &name, const string &version)
186 : d_factory(factory), d_name(name), d_container_name(""), d_container(0), d_request_xml_base(""), d_timeout(0),
187 /*d_keywords(),*/ d_max_response_size_kb(0) {
188 DBG(cerr << "Building a DDS for version: " << version << endl);
189
190 // This method sets a number of values, including those returned by
191 // get_protocol_major(), ..., get_namespace().
193}
194
196DDS::DDS(const DDS &rhs) : DapObj() {
197 DBG(cerr << "Entering DDS(const DDS &rhs) ..." << endl);
198 duplicate(rhs);
199 DBG(cerr << " bye." << endl);
200}
201
203 // delete all the variables in this DDS
204 for (Vars_iter i = vars.begin(); i != vars.end(); i++) {
205 BaseType *btp = *i;
206 delete btp;
207 btp = 0;
208 }
209}
210
211DDS &DDS::operator=(const DDS &rhs) {
212 if (this == &rhs)
213 return *this;
214 duplicate(rhs);
215 return *this;
216}
217
232 // If there is a container set in the DDS then check the container from
233 // the DAS. If they are not the same container, then throw an exception
234 // (should be working on the same container). If the container does not
235 // exist in the DAS, then throw an exception
236 if (d_container && das->container_name() != d_container_name)
237 throw InternalErr(__FILE__, __LINE__,
238 "Error transferring attributes: working on a container in dds, but not das");
239
240 // Give each variable a chance to claim its attributes.
242
243 for (DDS::Vars_iter i = var_begin(), e = var_end(); i != e; i++) {
244 (*i)->transfer_attributes(top);
245 }
246
247 // Now we transfer all the attributes still marked as global to the
248 // global container in the DDS.
249 for (AttrTable::Attr_iter i = top->attr_begin(), e = top->attr_end(); i != e; ++i) {
250 if ((*i)->type == Attr_container && (*i)->attributes->is_global_attribute()) {
251 // copy the source container so that the DAS passed in can be
252 // deleted after calling this method.
253 AttrTable *at = new AttrTable(*(*i)->attributes);
254 d_attr.append_container(at, at->get_name());
255 }
256 }
257}
258
264
266
268string DDS::get_dataset_name() const { return d_name; }
269
271void DDS::set_dataset_name(const string &n) { d_name = n; }
272
274
276AttrTable &DDS::get_attr_table() { return d_attr; }
277
284
286
287string DDS::filename() const { return d_filename; }
288
290void DDS::filename(const string &fn) { d_filename = fn; }
292
297 d_dap_major = p;
298
299 // This works because regardless of the order set_dap_major and set_dap_minor
300 // are called, once they both are called, the value in the string is
301 // correct. I protect against negative numbers because that would be
302 // nonsensical.
303 if (d_dap_minor >= 0) {
304 ostringstream oss;
305 oss << d_dap_major << "." << d_dap_minor;
306 d_dap_version = oss.str();
307 }
308}
309
314 d_dap_minor = p;
315
316 if (d_dap_major >= 0) {
317 ostringstream oss;
318 oss << d_dap_major << "." << d_dap_minor;
319 d_dap_version = oss.str();
320 }
321}
322
328void DDS::set_dap_version(const string &v /* = "2.0" */) {
329 istringstream iss(v);
330
331 int major = -1, minor = -1;
332 char dot;
333 if (!iss.eof() && !iss.fail())
334 iss >> major;
335 if (!iss.eof() && !iss.fail())
336 iss >> dot;
337 if (!iss.eof() && !iss.fail())
338 iss >> minor;
339
340 if (major == -1 || minor == -1 or dot != '.')
341 throw InternalErr(__FILE__, __LINE__, "Could not parse dap version. Value given: " + v);
342
343 d_dap_version = v;
344
345 d_dap_major = major;
346 d_dap_minor = minor;
347
348 // Now set the related XML constants. These might be overwritten if
349 // the DDS instance is being built from a document parse, but if it's
350 // being constructed by a server the code to generate the XML document
351 // needs these values to match the DAP version information.
352 switch (d_dap_major) {
353 case 2:
354 d_namespace = c_dap20_namespace;
355 break;
356 case 3:
357 d_namespace = c_dap32_namespace;
358 break;
359 case 4:
360 d_namespace = c_dap40_namespace;
361 break;
362 default:
363 throw InternalErr(__FILE__, __LINE__, "Unknown DAP version.");
364 }
365}
366
374void DDS::set_dap_version(double d) {
375 int major = floor(d);
376 int minor = (d - major) * 10;
377
378 DBG(cerr << "Major: " << major << ", Minor: " << minor << endl);
379
380 ostringstream oss;
381 oss << major << "." << minor;
382
383 set_dap_version(oss.str());
384}
385
392
394
395string DDS::container_name() { return d_container_name; }
396
399void DDS::container_name(const string &cn) {
400 // we want to search the DDS for the top level structure with the given
401 // d_name. Set the container to null so that we don't search some previous
402 // container.
403 d_container = 0;
404 if (!cn.empty()) {
405 d_container = dynamic_cast<Structure *>(var(cn));
406 if (!d_container) {
407 // create a structure for this container. Calling add_var
408 // while_container is null will add the new structure to DDS and
409 // not some sub structure. Adding the new structure makes a copy
410 // of it. So after adding it, go get it and set d_container.
411 Structure *s = new Structure(cn);
412 add_var(s);
413 delete s;
414 s = 0;
415 d_container = dynamic_cast<Structure *>(var(cn));
416 }
417 }
418 d_container_name = cn;
419}
420
422Structure *DDS::container() { return d_container; }
423
425
437//[[deprecated("Use DDS::get_request_size_kb()")]]
438int DDS::get_request_size(bool constrained) {
439 int w = 0;
440 for (Vars_iter i = vars.begin(); i != vars.end(); i++) {
441 if (constrained) {
442 if ((*i)->send_p())
443 w += (*i)->width(constrained);
444 } else {
445 w += (*i)->width(constrained);
446 }
447 }
448 return w;
449}
450
463uint64_t DDS::get_request_size_kb(bool constrained) {
464 uint64_t req_size = 0;
465 for (auto &btp : vars) {
466 if (constrained) {
467 if (btp->send_p())
468 req_size += btp->width_ll(constrained);
469 } else {
470 req_size += btp->width_ll(constrained);
471 }
472 }
473
474 return req_size / 1024;
475}
476
483 if (!bt)
484 throw InternalErr(__FILE__, __LINE__, "Trying to add a BaseType object with a NULL pointer.");
485
486 DBG2(cerr << "In DDS::add_var(), bt's address is: " << bt << endl);
487
488 BaseType *btp = bt->ptr_duplicate();
489 DBG2(cerr << "In DDS::add_var(), btp's address is: " << btp << endl);
490 if (d_container) {
491 // Mem leak fix [mjohnson nov 2009]
492 // Structure::add_var() creates ANOTHER copy.
493 d_container->add_var(bt);
494 // So we need to delete btp or else it leaks
495 delete btp;
496 btp = 0;
497 } else {
498 vars.push_back(btp);
499 }
500}
501
505 if (!bt)
506 throw InternalErr(__FILE__, __LINE__, "Trying to add a BaseType object with a NULL pointer.");
507
508 DBG2(cerr << "In DDS::add_var(), bt's address is: " << bt << endl);
509
510 if (d_container) {
511 d_container->add_var_nocopy(bt);
512 } else {
513 vars.push_back(bt);
514 }
515}
516
523void DDS::del_var(const string &n) {
524 if (d_container) {
525 d_container->del_var(n);
526 return;
527 }
528
529 for (Vars_iter i = vars.begin(); i != vars.end(); i++) {
530 if ((*i)->name() == n) {
531 BaseType *bt = *i;
532 vars.erase(i);
533 delete bt;
534 bt = 0;
535 return;
536 }
537 }
538}
539
545 if (i != vars.end()) {
546 BaseType *bt = *i;
547 vars.erase(i);
548 delete bt;
549 bt = 0;
550 }
551}
552
560 for (Vars_iter i_tmp = i1; i_tmp != i2; i_tmp++) {
561 BaseType *bt = *i_tmp;
562 delete bt;
563 bt = 0;
564 }
565 vars.erase(i1, i2);
566}
567
575BaseType *DDS::var(const string &n, BaseType::btp_stack &s) { return var(n, &s); }
576
596BaseType *DDS::var(const string &n, BaseType::btp_stack *s) {
597 string name = www2id(n);
598 if (d_container)
599 return d_container->var(name, false, s);
600
601 BaseType *v = exact_match(name, s);
602 if (v)
603 return v;
604
605 return leaf_match(name, s);
606}
607
609 DBG(cerr << "DDS::leaf_match: Looking for " << n << endl);
610
611 for (Vars_iter i = vars.begin(); i != vars.end(); i++) {
612 BaseType *btp = *i;
613 DBG(cerr << "DDS::leaf_match: Looking for " << n << " in: " << btp->name() << endl);
614 // Look for the d_name in the dataset's top-level
615 if (btp->name() == n) {
616 DBG(cerr << "Found " << n << " in: " << btp->name() << endl);
617 return btp;
618 }
619
620 if (btp->is_constructor_type()) {
621 BaseType *found = btp->var(n, false, s);
622 if (found) {
623 DBG(cerr << "Found " << n << " in: " << btp->name() << endl);
624 return found;
625 }
626 }
627#if STRUCTURE_ARRAY_SYNTAX_OLD
628 if (btp->is_vector_type() && btp->var()->is_constructor_type()) {
629 s->push(btp);
630 BaseType *found = btp->var()->var(n, false, s);
631 if (found) {
632 DBG(cerr << "Found " << n << " in: " << btp->var()->d_name() << endl);
633 return found;
634 }
635 }
636#endif
637 }
638
639 return 0; // It is not here.
640}
641
643 for (Vars_iter i = vars.begin(); i != vars.end(); i++) {
644 BaseType *btp = *i;
645 DBG2(cerr << "Looking for " << d_name << " in: " << btp << endl);
646 // Look for the d_name in the current ctor type or the top level
647 if (btp->name() == name) {
648 DBG2(cerr << "Found " << d_name << " in: " << btp << endl);
649 return btp;
650 }
651 }
652
653 string::size_type dot_pos = name.find(".");
654 if (dot_pos != string::npos) {
655 string aggregate = name.substr(0, dot_pos);
656 string field = name.substr(dot_pos + 1);
657
658 BaseType *agg_ptr = var(aggregate, s);
659 if (agg_ptr) {
660 DBG2(cerr << "Descending into " << agg_ptr->name() << endl);
661 return agg_ptr->var(field, true, s);
662 } else
663 return 0; // qualified names must be *fully* qualified
664 }
665
666 return 0; // It is not here.
667}
668
673void DDS::insert_var(Vars_iter i, BaseType *ptr) { vars.insert(i, ptr->ptr_duplicate()); }
674
682void DDS::insert_var_nocopy(Vars_iter i, BaseType *ptr) { vars.insert(i, ptr); }
683
685int DDS::num_var() { return vars.size(); }
686
688#if USE_LOCAL_TIMEOUT_SCHEME
689#ifndef WIN32
690 alarm(d_timeout);
691#endif
692#endif
693}
694
696#if USE_LOCAL_TIMEOUT_SCHEME
697#ifndef WIN32
698 // Old behavior commented out. I think it is an error to change the value
699 // of d_timeout. The way this will likely be used is to set the timeout
700 // value once and then 'turn on' or turn off' that timeout as the situation
701 // dictates. The initeded use for the DDS timeout is so that timeouts for
702 // data responses will include the CPU resources needed to build the response
703 // but not the time spent transmitting the response. This may change when
704 // more parallelism is added to the server... These methods are called from
705 // BESDapResponseBuilder in bes/dap. jhrg 12/22/15
706
707 // d_timeout = alarm(0);
708
709 alarm(0);
710#endif
711#endif
712}
713
715#if USE_LOCAL_TIMEOUT_SCHEME
716 // Has no effect under win32
717 d_timeout = t;
718#endif
719}
720
722#if USE_LOCAL_TIMEOUT_SCHEME
723 // Has to effect under win32
724 return d_timeout;
725#endif
726 return 0;
727}
728
731 for (Vars_iter i = vars.begin(); i != vars.end(); i++) {
732 if ((*i)->type() == dods_sequence_c)
733 dynamic_cast<Sequence &>(**i).set_leaf_sequence();
734 else if ((*i)->type() == dods_structure_c)
735 dynamic_cast<Structure &>(**i).set_leaf_sequence();
736 }
737}
738
740void DDS::parse(string fname) {
741 FILE *in = fopen(fname.c_str(), "r");
742
743 if (!in) {
744 throw Error(cannot_read_file, "Could not open: " + fname);
745 }
746
747 try {
748 parse(in);
749 fclose(in);
750 } catch (Error &e) {
751 fclose(in);
752 throw;
753 }
754}
755
757void DDS::parse(int fd) {
758#ifdef WIN32
759 int new_fd = _dup(fd);
760#else
761 int new_fd = dup(fd);
762#endif
763
764 if (new_fd < 0)
765 throw InternalErr(__FILE__, __LINE__, "Could not access file.");
766 FILE *in = fdopen(new_fd, "r");
767
768 if (!in) {
769 throw InternalErr(__FILE__, __LINE__, "Could not access file.");
770 }
771
772 try {
773 parse(in);
774 fclose(in);
775 } catch (Error &e) {
776 fclose(in);
777 throw;
778 }
779}
780
787void DDS::parse(FILE *in) {
788 if (!in) {
789 throw InternalErr(__FILE__, __LINE__, "Null input stream.");
790 }
791
792 void *buffer = dds_buffer(in);
793 dds_switch_to_buffer(buffer);
794
795 parser_arg arg(this);
796
797 bool status = ddsparse(&arg) == 0;
798
799 dds_delete_buffer(buffer);
800
801 DBG2(cout << "Status from parser: " << status << endl);
802
803 // STATUS is the result of the parser function; if a recoverable error
804 // was found it will be true but arg.status() will be false.
805 if (!status || !arg.status()) { // Check parse result
806 if (arg.error())
807 throw *arg.error();
808 }
809}
810
812void DDS::print(FILE *out) {
813 ostringstream oss;
814 print(oss);
815 fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
816}
817
819void DDS::print(ostream &out) {
820 out << "Dataset {\n";
821
822 for (Vars_citer i = vars.begin(); i != vars.end(); i++) {
823 (*i)->print_decl(out);
824 }
825
826 out << "} " << id2www(d_name) << ";\n";
827
828 return;
829}
830
839 for (AttrTable::Attr_iter i = a.attr_begin(), e = a.attr_end(); i != e; ++i) {
840 if (a.get_attr_type(i) != Attr_container) {
841 return true;
842 } else if (has_dap2_attributes(*a.get_attr_table(i))) {
843 return true;
844 }
845 }
846
847 return false;
848}
849
859 return true;
860 }
861
862 Constructor *cons = dynamic_cast<Constructor *>(btp);
863 if (cons) {
864 Grid *grid = dynamic_cast<Grid *>(btp);
865 if (grid) {
866 return has_dap2_attributes(grid->get_array());
867 } else {
868 for (Constructor::Vars_iter i = cons->var_begin(), e = cons->var_end(); i != e; i++) {
869 if (has_dap2_attributes(*i))
870 return true;
871 }
872 }
873 }
874 return false;
875}
876
886static string four_spaces = " ";
887void print_var_das(ostream &out, BaseType *bt, string indent = "") {
888
889 if (!has_dap2_attributes(bt))
890 return;
891
892 AttrTable attr_table = bt->get_attr_table();
893 out << indent << add_space_encoding(bt->name()) << " {" << endl;
894
895 Constructor *cnstrctr = dynamic_cast<Constructor *>(bt);
896 if (cnstrctr) {
897 Grid *grid = dynamic_cast<Grid *>(bt);
898 if (grid) {
899 Array *gridArray = grid->get_array();
900 AttrTable arrayAT = gridArray->get_attr_table();
901
902 if (has_dap2_attributes(gridArray))
903 gridArray->get_attr_table().print(out, indent + four_spaces);
904#if 0
905 // I dropped this because we don't want the MAP vectors showing up in the DAS
906 // as children of a Grid (aka flatten the Grid bro) - ndp 5/25/18
907 for (Grid::Map_iter mIter = grid->map_begin();
908 mIter != grid->map_end(); ++mIter) {
909 BaseType *currentMap = *mIter;
910 if (has_dap2_attributes(currentMap))
911 print_var_das(out, currentMap, indent + four_spaces);
912 }
913#endif
914 } else {
915 attr_table.print(out, indent + four_spaces);
916 Constructor::Vars_iter i = cnstrctr->var_begin();
917 Constructor::Vars_iter e = cnstrctr->var_end();
918 for (; i != e; i++) {
919 // Only call print_var_das() if there really are attributes.
920 // This is made complicated because while there might be none
921 // for a particular var (*i), that var might be a ctor and its
922 // descendant might have an attribute. jhrg 3/18/18
923 if (has_dap2_attributes(*i))
924 print_var_das(out, *i, indent + four_spaces);
925 }
926 }
927 } else {
928 attr_table.print(out, indent + four_spaces);
929 }
930
931 out << indent << "}" << endl;
932}
933
942void DDS::print_das(ostream &out) {
943 unique_ptr<DAS> das(get_das());
944
945 das->print(out);
946}
947
958 DAS *das = new DAS();
959 get_das(das);
960 return das;
961}
962
968static string get_unique_top_level_global_container_name(DAS *das) {
969 // It's virtually certain that the TOP_LEVE... name will be unique. If so,
970 // return the name. The code tests for a table to see if the name _should not_ be used.
972 if (!table)
974
975 // ... but the default name might already be used
976 unsigned int i = 0;
977 string name;
978 ostringstream oss;
979 while (table) {
980 oss.str(""); // reset to empty for the next suffix
981 oss << "_" << ++i;
982 if (!(i < UINT_MAX))
983 throw InternalErr(__FILE__, __LINE__, "Cannot add top-level attributes to the DAS");
984 name = TOP_LEVEL_ATTRS_CONTAINER_NAME + oss.str();
985 table = das->get_table(name);
986 }
987
988 return name;
989}
990
999 Constructor *cons = dynamic_cast<Constructor *>(bt);
1000 if (cons) {
1001 Grid *grid = dynamic_cast<Grid *>(bt);
1002 if (grid) {
1003 Array *gridArray = grid->get_array();
1004 AttrTable arrayAT = gridArray->get_attr_table();
1005
1006 for (AttrTable::Attr_iter atIter = arrayAT.attr_begin(); atIter != arrayAT.attr_end(); ++atIter) {
1007 AttrType type = arrayAT.get_attr_type(atIter);
1008 string childName = arrayAT.get_name(atIter);
1009 if (type == Attr_container) {
1010 at->append_container(new AttrTable(*arrayAT.get_attr_table(atIter)), childName);
1011 } else {
1012 vector<string> *pAttrTokens = arrayAT.get_attr_vector(atIter);
1013 // append_attr makes a copy of the vector, so we don't have to do so here.
1014 at->append_attr(childName, AttrType_to_String(type), pAttrTokens, (*atIter)->is_utf8_str);
1015 }
1016 }
1017
1018 } else {
1019 for (Constructor::Vars_iter i = cons->var_begin(), e = cons->var_end(); i != e; i++) {
1020 if (has_dap2_attributes(*i)) {
1021 AttrTable *childAttrT = new AttrTable((*i)->get_attr_table());
1022 fillConstructorAttrTable(childAttrT, *i);
1023 at->append_container(childAttrT, (*i)->name());
1024 }
1025 }
1026 }
1027 }
1028}
1029
1030void DDS::get_das(DAS *das) {
1031 for (Vars_citer i = vars.begin(); i != vars.end(); i++) {
1032 if (has_dap2_attributes(*i)) {
1033 AttrTable *childAttrT = new AttrTable((*i)->get_attr_table());
1034 fillConstructorAttrTable(childAttrT, *i);
1035 das->add_table((*i)->name(), childAttrT);
1036 }
1037 }
1038
1039 // Used in the rare case we have global attributes not in a table.
1040 unique_ptr<AttrTable> global(new AttrTable);
1041
1042 for (AttrTable::Attr_iter i = d_attr.attr_begin(); i != d_attr.attr_end(); ++i) {
1043 // It's possible, given the API and if the DDS was built from a DMR, that a
1044 // global attribute might not be a container; check for that.
1045 if (d_attr.get_attr_table(i)) {
1046 das->add_table(d_attr.get_name(i), new AttrTable(*(d_attr.get_attr_table(i))));
1047 } else {
1048 // This must be a top level attribute outside a container. jhrg 4/6/18
1049 global->append_attr(d_attr.get_name(i), d_attr.get_type(i), d_attr.get_attr_vector(i), (*i)->is_utf8_str);
1050 }
1051 }
1052
1053 // if any attributes were added to 'global,' add it to the DAS and take control of the pointer.
1054 if (global->get_size() > 0) {
1055 das->add_table(get_unique_top_level_global_container_name(das),
1056 global.get()); // What if this name is not unique?
1057 global.release();
1058 }
1059}
1060
1071void DDS::print_constrained(FILE *out) {
1072 ostringstream oss;
1073 print_constrained(oss);
1074 fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
1075}
1076
1087void DDS::print_constrained(ostream &out) {
1088 out << "Dataset {\n";
1089
1090 for (Vars_citer i = vars.begin(); i != vars.end(); i++) {
1091 // for each variable, indent with four spaces, print a trailing
1092 // semicolon, do not print debugging information, print only
1093 // variables in the current projection.
1094 (*i)->print_decl(out, " ", true, false, true);
1095 }
1096
1097 out << "} " << id2www(d_name) << ";\n";
1098
1099 return;
1100}
1101
1113void DDS::print_xml(FILE *out, bool constrained, const string &blob) {
1114 ostringstream oss;
1115 print_xml_writer(oss, constrained, blob);
1116 fwrite(oss.str().data(), 1, oss.str().length(), out);
1117}
1118
1130void DDS::print_xml(ostream &out, bool constrained, const string &blob) { print_xml_writer(out, constrained, blob); }
1131
1148void DDS::print_xml_writer(ostream &out, bool constrained, const string &blob) {
1149 XMLWriter xml(" ");
1150
1151 // This is the 'DAP 3.2' DDX response - now the only response libdap will return.
1152 // jhrg 9/10/18
1153 if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar *)"Dataset") < 0)
1154 throw InternalErr(__FILE__, __LINE__, "Could not write Dataset element");
1155 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"name", (const xmlChar *)d_name.c_str()) < 0)
1156 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1157 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"xmlns:xsi",
1158 (const xmlChar *)"http://www.w3.org/2001/XMLSchema-instance") < 0)
1159 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:xsi");
1160
1161 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"xsi:schemaLocation",
1162 (const xmlChar *)c_dap_32_n_sl.c_str()) < 0)
1163 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:schemaLocation");
1164
1165 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"xmlns:grddl",
1166 (const xmlChar *)"http://www.w3.org/2003/g/data-view#") < 0)
1167 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:grddl");
1168
1169 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"grddl:transformation",
1170 (const xmlChar *)grddl_transformation_dap32.c_str()) < 0)
1171 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:transformation");
1172
1173 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"xmlns",
1174 (const xmlChar *)c_dap32_namespace.c_str()) < 0)
1175 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns");
1176 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"xmlns:dap",
1177 (const xmlChar *)c_dap32_namespace.c_str()) < 0)
1178 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:dap");
1179
1180 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"dapVersion", (const xmlChar *)"3.2") < 0)
1181 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for dapVersion");
1182
1183 if (!get_request_xml_base().empty()) {
1184 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"xmlns:xml",
1185 (const xmlChar *)c_xml_namespace.c_str()) < 0)
1186 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:xml");
1187
1188 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"xml:base",
1189 (const xmlChar *)get_request_xml_base().c_str()) < 0)
1190 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xml:base");
1191 }
1192
1193 // Print the global attributes
1194 d_attr.print_xml_writer(xml);
1195
1196 // Print each variable
1197 for (auto const &var : variables()) {
1198 var->print_xml_writer(xml, constrained);
1199 }
1200
1201 if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar *)"blob") < 0)
1202 throw InternalErr(__FILE__, __LINE__, "Could not write blob element");
1203 string cid = "cid:" + blob;
1204 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"href", (const xmlChar *)cid.c_str()) < 0)
1205 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for d_name");
1206 if (xmlTextWriterEndElement(xml.get_writer()) < 0)
1207 throw InternalErr(__FILE__, __LINE__, "Could not end blob element");
1208
1209 if (xmlTextWriterEndElement(xml.get_writer()) < 0)
1210 throw InternalErr(__FILE__, __LINE__, "Could not end Dataset element");
1211
1212 out << xml.get_doc(); // << ends;// << endl;
1213}
1214
1228void DDS::print_dmr(ostream &out, bool constrained) {
1229 if (get_dap_major() < 4)
1230 throw InternalErr(__FILE__, __LINE__, "Tried to print a DMR with DAP major version less than 4");
1231
1232 XMLWriter xml(" ");
1233
1234 // DAP4 wraps a dataset in a top-level Group element.
1235 if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar *)"Group") < 0)
1236 throw InternalErr(__FILE__, __LINE__, "Could not write Group element");
1237
1238 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"xmlns:xml",
1239 (const xmlChar *)c_xml_namespace.c_str()) < 0)
1240 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:xml");
1241
1242 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"xmlns:xsi",
1243 (const xmlChar *)c_xml_xsi.c_str()) < 0)
1244 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:xsi");
1245
1246 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"xsi:schemaLocation",
1247 (const xmlChar *)c_dap_40_n_sl.c_str()) < 0)
1248 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:schemaLocation");
1249
1250 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"xmlns",
1251 (const xmlChar *)get_namespace().c_str()) < 0)
1252 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns");
1253
1254 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"dapVersion",
1255 (const xmlChar *)get_dap_version().c_str()) < 0)
1256 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for dapVersion");
1257
1258 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"dmrVersion",
1259 (const xmlChar *)get_dmr_version().c_str()) < 0)
1260 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for dapVersion");
1261
1262 if (!get_request_xml_base().empty()) {
1263 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"xml:base",
1264 (const xmlChar *)get_request_xml_base().c_str()) < 0)
1265 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xml:base");
1266 }
1267
1268 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar *)"name", (const xmlChar *)d_name.c_str()) < 0)
1269 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1270
1271 // Print the global attributes
1272 d_attr.print_xml_writer(xml);
1273
1274 // Print each variable
1275 for (auto const &var : variables()) {
1276 var->print_xml_writer(xml, constrained);
1277 }
1278
1279 if (xmlTextWriterEndElement(xml.get_writer()) < 0)
1280 throw InternalErr(__FILE__, __LINE__, "Could not end the top-level Group element");
1281
1282 out << xml.get_doc();
1283}
1284
1285// Used by DDS::send() when returning data from a function call.
1300bool DDS::check_semantics(bool all) {
1301 // The dataset must have a d_name
1302 if (d_name == "") {
1303 cerr << "A dataset must have a d_name" << endl;
1304 return false;
1305 }
1306
1307 string msg;
1308 if (!unique_names(vars, d_name, "Dataset", msg))
1309 return false;
1310
1311 if (all)
1312 for (Vars_iter i = vars.begin(); i != vars.end(); i++)
1313 if (!(*i)->check_semantics(msg, true))
1314 return false;
1315
1316 return true;
1317}
1318
1342bool DDS::mark(const string &n, bool state) {
1343 unique_ptr<BaseType::btp_stack> s(new BaseType::btp_stack);
1344
1345 DBG2(cerr << "DDS::mark: Looking for " << n << endl);
1346
1347 BaseType *variable = var(n, s.get());
1348 if (!variable) {
1349 throw Error(malformed_expr, "Could not find variable " + n);
1350 }
1351
1352 variable->set_send_p(state);
1353
1354 DBG2(cerr << "DDS::mark: Set variable " << variable->d_name() << " (a " << variable->type_name() << ")" << endl);
1355
1356 // Now check the btp_stack and run BaseType::set_send_p for every
1357 // BaseType pointer on the stack. Using BaseType::set_send_p() will
1358 // set the property for a Constructor but not its contained variables
1359 // which preserves the semantics of projecting just one field.
1360 while (!s->empty()) {
1361 s->top()->BaseType::set_send_p(state);
1362
1363 DBG2(cerr << "DDS::mark: Set variable " << s->top()->d_name() << " (a " << s->top()->type_name() << ")"
1364 << endl);
1365
1366 string parent_name = (s->top()->get_parent()) ? s->top()->get_parent()->name() : "none";
1367 string parent_type = (s->top()->get_parent()) ? s->top()->get_parent()->type_name() : "none";
1368 DBG2(cerr << "DDS::mark: Parent variable " << parent_name << " (a " << parent_type << ")" << endl);
1369
1370 s->pop();
1371 }
1372
1373 return true;
1374}
1375
1381void DDS::mark_all(bool state) {
1382 for (Vars_iter i = vars.begin(); i != vars.end(); i++)
1383 (*i)->set_send_p(state);
1384}
1385
1391bool DDS::is_dap4_projected(std::vector<string> &inventory) {
1392 bool has_dap4 = d_attr.has_dap4_types("/", inventory);
1393 for (const auto var : variables()) {
1394 has_dap4 |= var->is_dap4_projected(inventory);
1395 }
1396 return has_dap4;
1397}
1398
1406void DDS::dump(ostream &strm) const {
1407 strm << DapIndent::LMarg << "DDS::dump - (" << (void *)this << ")" << endl;
1409 strm << DapIndent::LMarg << "d_name: " << d_name << endl;
1410 strm << DapIndent::LMarg << "filename: " << d_filename << endl;
1411 strm << DapIndent::LMarg << "protocol major: " << d_dap_major << endl;
1412 strm << DapIndent::LMarg << "protocol minor: " << d_dap_minor << endl;
1413 strm << DapIndent::LMarg << "factory: " << (void *)d_factory << endl;
1414
1415 strm << DapIndent::LMarg << "global attributes:" << endl;
1417 d_attr.dump(strm);
1419
1420 if (vars.size()) {
1421 strm << DapIndent::LMarg << "vars:" << endl;
1423 Vars_citer i = vars.begin();
1424 Vars_citer ie = vars.end();
1425 for (; i != ie; i++) {
1426 (*i)->dump(strm);
1427 }
1429 } else {
1430 strm << DapIndent::LMarg << "vars: none" << endl;
1431 }
1432
1434}
1435
1436} // namespace libdap
const string c_default_dap20_schema_location
Definition DDS.cc:89
void * dds_buffer(FILE *fp)
const string TOP_LEVEL_ATTRS_CONTAINER_NAME
Definition DDS.cc:104
const string c_dap_20_n_sl
Definition DDS.cc:97
const string c_default_dap32_schema_location
Definition DDS.cc:90
const string grddl_transformation_dap32
Definition DDS.cc:87
int ddsparse(libdap::parser_arg *arg)
const string c_dap_32_n_sl
Definition DDS.cc:98
const string c_xml_xsi
Definition DDS.cc:84
const string c_dap_40_n_sl
Definition DDS.cc:99
const string c_dap32_namespace
Definition DDS.cc:94
const string c_dap20_namespace
Definition DDS.cc:93
const string c_default_dap40_schema_location
Definition DDS.cc:91
void dds_switch_to_buffer(void *new_buffer)
void dds_delete_buffer(void *buffer)
const string c_xml_namespace
Definition DDS.cc:85
const string c_dap40_namespace
Definition DDS.cc:95
#define malformed_expr
(400)
Definition Error.h:66
#define cannot_read_file
(400)
Definition Error.h:68
A multidimensional array of identical data types.
Definition Array.h:121
Contains the attributes for a dataset.
Definition AttrTable.h:150
virtual AttrTable * append_container(const string &name)
Add a container to the attribute table.
Definition AttrTable.cc:517
virtual AttrTable * get_attr_table(const string &name)
Get an attribute container.
Definition AttrTable.cc:699
virtual Attr_iter attr_end()
Definition AttrTable.cc:798
virtual vector< string > * get_attr_vector(const string &name)
Get a vector-valued attribute.
Definition AttrTable.cc:738
virtual unsigned int append_attr(const string &name, const string &type, const string &value)
Add an attribute to the table.
Definition AttrTable.cc:308
virtual Attr_iter attr_begin()
Definition AttrTable.cc:793
virtual string get_name() const
Get the name of this attribute table.
Definition AttrTable.cc:243
virtual void print(FILE *out, string pad=" ", bool dereference=false)
Prints the attribute table.
virtual unsigned int get_size() const
Get the number of entries in this attribute table.
Definition AttrTable.cc:239
std::vector< entry * >::iterator Attr_iter
Definition AttrTable.h:258
virtual AttrType get_attr_type(const string &name)
Get the type of an attribute.
Definition AttrTable.cc:709
The basic data type for the DODS DAP types.
Definition BaseType.h:118
virtual string type_name() const
Returns the type of the class instance as a string.
Definition BaseType.cc:335
virtual AttrTable & get_attr_table()
Definition BaseType.cc:498
virtual string name() const
Returns the name of the class instance.
Definition BaseType.cc:296
virtual bool is_vector_type() const
Returns true if the instance is a vector (i.e., array) type variable.
Definition BaseType.cc:352
virtual bool is_constructor_type() const
Returns true if the instance is a constructor (i.e., Structure, Sequence or Grid) type variable.
Definition BaseType.cc:358
virtual void set_send_p(bool state)
Definition BaseType.cc:488
virtual BaseType * ptr_duplicate()=0
stack< BaseType * > btp_stack
Definition BaseType.h:149
virtual BaseType * var(const string &name="", bool exact_match=true, btp_stack *s=nullptr)
Returns a pointer to a member of a constructor class.
Definition BaseType.cc:646
std::vector< BaseType * >::iterator Vars_iter
Definition Constructor.h:60
Vars_iter var_begin()
Hold attribute data for a DAP2 dataset.
Definition DAS.h:119
virtual AttrTable * get_top_level_attributes()
Returns the top most set of attributes.
Definition DAS.h:163
virtual AttrTable * add_table(const string &name, AttrTable *at)
Adds a variable attribute table to the DAS or the current dataset container attribute table.
Definition DAS.cc:193
AttrTable * get_table(AttrTable::Attr_iter &i)
Returns the referenced variable attribute table.
Definition DAS.cc:167
virtual string container_name() const
Returns the name of the current attribute container when multiple files used to build this DAS.
Definition DAS.h:146
void set_dataset_name(const string &n)
Definition DDS.cc:271
void set_dap_major(int p)
Definition DDS.cc:296
void mark_all(bool state)
Definition DDS.cc:1381
void print_dmr(ostream &out, bool constrained)
Print the DAP4 DMR object using a DDS.
Definition DDS.cc:1228
virtual ~DDS()
Definition DDS.cc:202
void add_var_nocopy(BaseType *bt)
Adds the variable to the DDS.
Definition DDS.cc:504
bool check_semantics(bool all=false)
Check the semantics of each of the variables represented in the DDS.
Definition DDS.cc:1300
string filename() const
Definition DDS.cc:287
virtual AttrTable & get_attr_table()
Definition DDS.cc:276
std::vector< BaseType * >::const_iterator Vars_citer
Definition DDS.h:210
string get_dap_version() const
Definition DDS.h:256
uint64_t get_request_size_kb(bool constrained)
Get the estimated response size in kilobytes.
Definition DDS.cc:463
int get_timeout()
Definition DDS.cc:721
virtual void transfer_attributes(DAS *das)
Definition DDS.cc:231
BaseType * leaf_match(const string &name, BaseType::btp_stack *s=0)
Definition DDS.cc:608
void set_dap_minor(int p)
Definition DDS.cc:313
string get_namespace() const
Get the namespace associated with the DDS - likely set only by DDX responses.
Definition DDS.h:273
int num_var()
Returns the number of variables in the DDS.
Definition DDS.cc:685
void print(FILE *out)
Print the entire DDS to the specified file.
Definition DDS.cc:812
int get_request_size(bool constrained)
Get the estimated response size in bytes.
Definition DDS.cc:438
bool is_dap4_projected(std::vector< string > &inventory)
Definition DDS.cc:1391
string get_dataset_name() const
Definition DDS.cc:268
void timeout_off()
Definition DDS.cc:695
BaseType * exact_match(const string &name, BaseType::btp_stack *s=0)
Definition DDS.cc:642
void del_var(const string &n)
Removes a variable from the DDS.
Definition DDS.cc:523
void parse(string fname)
Parse a DDS from a file with the given d_name.
Definition DDS.cc:740
BaseType * var(const string &n, BaseType::btp_stack &s)
Definition DDS.cc:575
void print_xml(FILE *out, bool constrained, const string &blob="")
Definition DDS.cc:1113
void insert_var(Vars_iter i, BaseType *ptr)
Insert a variable before the referenced element.
Definition DDS.cc:673
void duplicate(const DDS &dds)
Definition DDS.cc:117
bool mark(const string &name, bool state)
Mark the send_p flag of the named variable to state.
Definition DDS.cc:1342
void timeout_on()
Definition DDS.cc:687
DDS(BaseTypeFactory *factory, const string &name="")
Definition DDS.cc:160
void tag_nested_sequences()
Traverse DDS, set Sequence leaf nodes.
Definition DDS.cc:730
DAS * get_das()
Get a DAS object.
Definition DDS.cc:957
Vars_iter var_begin()
Definition DDS.h:336
void print_constrained(FILE *out)
Print a constrained DDS to the specified file.
Definition DDS.cc:1071
string container_name()
Definition DDS.cc:395
string get_dmr_version() const
Definition DDS.h:257
void insert_var_nocopy(Vars_iter i, BaseType *ptr)
Definition DDS.cc:682
string get_request_xml_base() const
Get the URL that will return this DDS/DDX/DataThing.
Definition DDS.h:267
void set_timeout(int t)
Definition DDS.cc:714
Vars_iter var_end()
Return an iterator.
Definition DDS.h:341
int get_dap_major() const
Get the DAP major version as sent by the client.
Definition DDS.h:251
const vector< BaseType * > & variables() const
Definition DDS.h:331
void set_dap_version(const string &version_string="2.0")
Definition DDS.cc:328
Structure * container()
Definition DDS.cc:422
void add_var(BaseType *bt)
Adds a copy of the variable to the DDS. Using the ptr_duplicate() method, perform a deep copy on the ...
Definition DDS.cc:482
std::vector< BaseType * >::iterator Vars_iter
Definition DDS.h:211
void print_xml_writer(ostream &out, bool constrained, const string &blob="")
Definition DDS.cc:1148
DDS & operator=(const DDS &rhs)
Definition DDS.cc:211
void print_das(ostream &out)
write the DAS response given the attribute information in the DDS
Definition DDS.cc:942
virtual void dump(ostream &strm) const
dumps information about this object
Definition DDS.cc:1406
static ostream & LMarg(ostream &strm)
Definition DapIndent.cc:61
static void Indent()
Definition DapIndent.cc:44
static void UnIndent()
Definition DapIndent.cc:46
libdap base object for common functionality of libdap objects
Definition DapObj.h:49
A class for error processing.
Definition Error.h:92
Holds the Grid data type.
Definition Grid.h:121
Map_iter map_begin()
Returns an iterator referencing the first Map vector.
Definition Grid.cc:448
std::vector< BaseType * >::iterator Map_iter
Definition Grid.h:136
Array * get_array()
Returns the Grid Array. This method returns the array using an Array*, so no cast is required.
Definition Grid.cc:445
Map_iter map_end()
Definition Grid.cc:458
A class for software fault reporting.
Definition InternalErr.h:61
Holds a sequence.
Definition Sequence.h:162
virtual void set_leaf_sequence(int lvl=1)
Mark the Sequence which holds the leaf elements.
Definition Sequence.cc:1148
Holds a structure (aggregate) type.
Definition Structure.h:82
virtual void set_leaf_sequence(int level=1)
Traverse Structure, set Sequence leaf nodes.
Definition Structure.cc:285
const char * get_doc()
Definition XMLWriter.cc:104
xmlTextWriterPtr get_writer() const
Definition XMLWriter.h:55
#define DBG(x)
Definition debug.h:58
#define DBG2(x)
Definition debug.h:74
const char * version
Definition getdap.cc:65
top level DAP object to house generic methods
Definition AISConnect.cc:30
@ dods_sequence_c
Definition Type.h:108
@ dods_structure_c
Definition Type.h:106
bool has_dap2_attributes(AttrTable &a)
Definition DDS.cc:838
string www2id(const string &in, const string &escape, const string &except)
Definition escaping.cc:202
const string c_dap40_namespace
Definition DMR.h:40
string add_space_encoding(const string &s)
Definition AttrTable.cc:76
string AttrType_to_String(const AttrType at)
Definition AttrTable.cc:93
void print_var_das(ostream &out, BaseType *bt, string indent="")
Definition DDS.cc:887
@ Attr_container
Definition AttrTable.h:80
void fillConstructorAttrTable(AttrTable *at, BaseType *bt)
Recursive helper function for Building DAS entries for Constructor types.
Definition DDS.cc:998
bool unique_names(vector< BaseType * > l, const string &var_name, const string &type_name, string &msg)
Definition util.cc:472
string id2www(string in, const string &allowable)
Definition escaping.cc:143
Pass parameters by reference to a parser.
Definition parser.h:67
Error * error()
Definition parser.h:83