libdap  Updated for version 3.20.11
libdap4 is an implementation of OPeNDAP's DAP protocol.
DMR.cc
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) 2013 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 //
23 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
24 
25 #include "config.h"
26 
27 #ifdef WIN32
28 #include <io.h>
29 #include <process.h>
30 #include <fstream>
31 #else
32 #include <unistd.h> // for alarm and dup
33 #include <sys/wait.h>
34 #endif
35 
36 #include <cassert>
37 
38 #include <iostream>
39 #include <sstream>
40 #include <memory>
41 
42 //#define DODS_DEBUG
43 //#define DODS_DEBUG2
44 
45 #include "D4Group.h"
46 #include "BaseType.h"
47 #include "Array.h"
48 #include "Grid.h"
49 #include "DMR.h"
50 #include "XMLWriter.h"
51 #include "D4BaseTypeFactory.h"
52 #include "D4Attributes.h"
53 
54 #include "DDS.h" // Included so DMRs can be built using a DDS for 'legacy' handlers
55 
56 #include "debug.h"
57 #include "DapIndent.h"
58 
64 const string c_xml_xsi = "http://www.w3.org/2001/XMLSchema-instance";
65 const string c_xml_namespace = "http://www.w3.org/XML/1998/namespace";
66 
67 const string c_default_dap40_schema_location = "http://xml.opendap.org/dap/dap4.0.xsd";
68 
69 const string c_dap40_namespace = "http://xml.opendap.org/ns/DAP/4.0#";
70 
71 const string c_dap_40_n_sl = c_dap40_namespace + " " + c_default_dap40_schema_location;
72 
73 using namespace std;
74 
75 namespace libdap {
76 
77 void
78 DMR::m_duplicate(const DMR &dmr)
79 {
80  // This is needed because we use the factory to make a new instance of the root group
81  assert(dmr.OK());
82 
83  d_factory = dmr.d_factory; // Shallow copy here
84 
85  d_name = dmr.d_name;
86  d_filename = dmr.d_filename;
87 
88  d_dap_major = dmr.d_dap_major;
89  d_dap_minor = dmr.d_dap_minor;
90  d_dap_version = dmr.d_dap_version; // String version of the protocol
91 
92  d_dmr_version = dmr.d_dmr_version;
93 
94  d_request_xml_base = dmr.d_request_xml_base;
95 
96  d_namespace = dmr.d_namespace;
97 
98  d_max_response_size_kb = dmr.d_max_response_size_kb;
99 
100  d_ce_empty = dmr.d_ce_empty;
101 
102  // Deep copy, using ptr_duplicate()
103  // d_root can only be a D4Group, so the thing returned by ptr_duplicate() must be a D4Group.
104  d_root = static_cast<D4Group*>(dmr.d_root->ptr_duplicate());
105  DBG(cerr << "dmr.d_root: " << dmr.d_root << endl);
106  DBG(cerr << "d_root (from ptr_dup(): " << d_root << endl);
107 
108  //d_root = static_cast<D4Group*>(dmr.d_factory->NewVariable(dods_group_c, dmr.d_root->name()));
109 }
110 
123 DMR::DMR(D4BaseTypeFactory *factory, const string &name)
124  : d_factory(factory), d_name(name), d_filename(""),
125  d_dap_major(4), d_dap_minor(0),
126  d_dmr_version("1.0"), d_request_xml_base(""),
127  d_namespace(c_dap40_namespace), d_max_response_size_kb(0),
128  d_ce_empty(false),d_root(0)
129 {
130  // sets d_dap_version string and the two integer fields too
131  set_dap_version("4.0");
132 }
133 
155  : d_factory(factory), d_name(dds.get_dataset_name()),
156  d_filename(dds.filename()), d_dap_major(4), d_dap_minor(0),
157  d_dmr_version("1.0"), d_request_xml_base(""),
158  d_namespace(c_dap40_namespace), d_max_response_size_kb(0),d_ce_empty(false), d_root(0)
159 {
160  // sets d_dap_version string and the two integer fields too
161  set_dap_version("4.0");
162 
163  build_using_dds(dds);
164 }
165 
173  : d_factory(0), d_name(""), d_filename(""), d_dap_major(4), d_dap_minor(0),
174  d_dap_version("4.0"), d_dmr_version("1.0"), d_request_xml_base(""),
175  d_namespace(c_dap40_namespace), d_max_response_size_kb(0), d_ce_empty(false),d_root(0)
176 {
177  // sets d_dap_version string and the two integer fields too
178  set_dap_version("4.0");
179 }
180 
182 DMR::DMR(const DMR &rhs) : DapObj()
183 {
184  m_duplicate(rhs);
185 }
186 
191 {
192  delete d_root;
193 }
194 
195 DMR &
196 DMR::operator=(const DMR &rhs)
197 {
198  if (this == &rhs)
199  return *this;
200  m_duplicate(rhs);
201  return *this;
202 }
203 
213 {
214  set_name(dds.get_dataset_name());
215  set_filename(dds.filename());
216 
217  D4Group *root_grp = root();
218  for (DDS::Vars_iter i = dds.var_begin(), e = dds.var_end(); i != e; ++i) {
219  BaseType *d4_var = root()->var((*i)->name());
220  // Don't add duplicate variables. We have to make this check
221  // because some of the child variables may add arrays
222  // to the root object. For example, this happens in
223  // Grid with the Map Arrays - ndp - 05/08/17
224  if(!d4_var){
225  // no variable of this name is in the root group at this point. Add it.
226  DBG(cerr << __func__ << "() - Transforming top level variable: " <<
227  " (" << (*i)->type_name() << ":'" << (*i)->name() << "':"<<(void *)(*i) <<
228  ") (root:"<< root_grp << ")"<< endl; );
229  (*i)->transform_to_dap4(root_grp, root_grp);
230  DBG(cerr << __func__ << "() - top level variable: '" <<
231  (*i)->name() << "' (type:" << (*i)->type_name() << ") Transformed"<< endl; );
232  }
233  else {
234  DBG(cerr << __func__ << "() - Skipping variable: " <<
235  d4_var->type_name() << " " << d4_var->name() << " because a variable with" <<
236  " this name already exists in the root group." << endl; );
237  }
238  }
239 
240  // Now copy the global attributes
242 }
243 
259 DDS *
261 {
262  DBG( cerr << __func__ << "() - BEGIN" << endl);
263 
264  BaseTypeFactory btf;
265  DDS *dds = new DDS(&btf, name());
266  dds->filename(filename());
267 
268  // Now copy the global attributes
269  // TODO Make this a unique_ptr<> and let the compiler delete it. jhrg 6/17/19
270  // change made jhrg 2/4/22
271 #if 1
272  unique_ptr< vector<BaseType *>> top_vars(root()->transform_to_dap2(&(dds->get_attr_table())/*, true*/));
273  for (vector<BaseType *>::iterator i = top_vars->begin(), e = top_vars->end(); i != e; i++) {
274  dds->add_var_nocopy(*i);
275  }
276 #else
277  vector<BaseType *> *top_vars = root()->transform_to_dap2(&(dds->get_attr_table())/*, true*/);
278  for (vector<BaseType *>::iterator i = top_vars->begin(), e = top_vars->end(); i != e; i++) {
279  dds->add_var_nocopy(*i);
280  }
281  delete top_vars;
282 #endif
283  DBG( cerr << __func__ << "() - END" << endl);
284 
285  dds->set_factory(0);
286  return dds;
287 }
288 
295 D4Group *
297 {
298  if (!d_root) d_root = static_cast<D4Group*>(d_factory->NewVariable(dods_group_c, "/"));
299  return d_root;
300 }
301 
307 void
308 DMR::set_dap_version(const string &v)
309 {
310  istringstream iss(v);
311 
312  int major = -1, minor = -1;
313  char dot;
314  if (!iss.eof() && !iss.fail())
315  iss >> major;
316  if (!iss.eof() && !iss.fail())
317  iss >> dot;
318  if (!iss.eof() && !iss.fail())
319  iss >> minor;
320 
321  if (major == -1 || minor == -1 or dot != '.')
322  throw InternalErr(__FILE__, __LINE__, "Could not parse dap version. Value given: " + v);
323 
324  d_dap_version = v;
325 
326  d_dap_major = major;
327  d_dap_minor = minor;
328 
329  // Now set the related XML constants. These might be overwritten if
330  // the DMR instance is being built from a document parse, but if it's
331  // being constructed by a server the code to generate the XML document
332  // needs these values to match the DAP version information.
333  switch (d_dap_major) {
334  case 4:
335  d_namespace = c_dap40_namespace;
336  break;
337  default:
338  d_namespace = "";
339  break;
340  }
341 }
342 
354 //[[deprecated("Use DMR::request_size_kb()")]]
355 long DMR::request_size(bool constrained)
356 {
357  return d_root->request_size(constrained);
358 }
359 
370 uint64_t DMR::request_size_kb(bool constrained)
371 {
372  return d_root->request_size_kb(constrained);
373 }
374 
375 
376 
377 
385 void
386 DMR::print_dap4(XMLWriter &xml, bool constrained)
387 {
388  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Dataset") < 0)
389  throw InternalErr(__FILE__, __LINE__, "Could not write Dataset element");
390 
391 #if 0
392  // Reintroduce these if they are really useful. jhrg 4/15/13
393  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xmlns:xml",
394  (const xmlChar*) c_xml_namespace.c_str()) < 0)
395  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:xml");
396 
397  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xmlns:xsi", (const xmlChar*) c_xml_xsi.c_str())
398  < 0)
399  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:xsi");
400 
401  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xsi:schemaLocation",
402  (const xmlChar*) c_dap_40_n_sl.c_str()) < 0)
403  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:schemaLocation");
404 #endif
405 
406  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xmlns", (const xmlChar*) get_namespace().c_str()) < 0)
407  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns");
408 
409  if (!request_xml_base().empty()) {
410  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xml:base",
411  (const xmlChar*)request_xml_base().c_str()) < 0)
412  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xml:base");
413  }
414 
415  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "dapVersion", (const xmlChar*)dap_version().c_str()) < 0)
416  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for dapVersion");
417 
418  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "dmrVersion", (const xmlChar*)dmr_version().c_str()) < 0)
419  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for dapVersion");
420 
421  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0)
422  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
423 
424  root()->print_dap4(xml, constrained);
425 
426  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
427  throw InternalErr(__FILE__, __LINE__, "Could not end the top-level Group element");
428 }
429 
430 
438 void
439 DMR::dump(ostream &strm) const
440 {
441  strm << DapIndent::LMarg << "DMR::dump - ("
442  << (void *)this << ")" << endl ;
443  DapIndent::Indent() ;
444  strm << DapIndent::LMarg << "factory: " << (void *)d_factory << endl ;
445  strm << DapIndent::LMarg << "name: " << d_name << endl ;
446  strm << DapIndent::LMarg << "filename: " << d_filename << endl ;
447  strm << DapIndent::LMarg << "protocol major: " << d_dap_major << endl;
448  strm << DapIndent::LMarg << "protocol minor: " << d_dap_minor << endl;
449 
450  DapIndent::UnIndent() ;
451 }
452 
453 } // namespace libdap
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:375
virtual string name() const
Returns the name of the class instance.
Definition: BaseType.cc:316
virtual D4Attributes * attributes()
Definition: BaseType.cc:595
BaseType * var(const string &name, bool exact_match=true, btp_stack *s=nullptr) override
btp_stack no longer needed; use back pointers (BaseType::get_parent())
Definition: Constructor.cc:185
void transform_to_dap4(AttrTable &at)
copy attributes from DAP2 to DAP4
virtual BaseType * NewVariable(Type t, const string &name) const
uint64_t request_size_kb(bool constrained)
Get the estimated size of a response in kilobytes. This method looks at the variables in the DDS and ...
Definition: D4Group.cc:449
virtual std::vector< BaseType * > * transform_to_dap2(AttrTable *parent_attr_table)
Transform the D4Group's variables to DAP2 variables.
Definition: D4Group.cc:707
long request_size(bool constrained)
Definition: D4Group.cc:409
void print_dap4(XMLWriter &xml, bool constrained=false)
Definition: D4Group.cc:618
void add_var_nocopy(BaseType *bt)
Adds the variable to the DDS.
Definition: DDS.cc:567
string filename() const
Definition: DDS.cc:316
virtual AttrTable & get_attr_table()
Definition: DDS.cc:301
string get_dataset_name() const
Definition: DDS.cc:285
Vars_iter var_begin()
Definition: DDS.h:344
BaseTypeFactory * set_factory(BaseTypeFactory *factory)
Definition: DDS.h:244
Vars_iter var_end()
Return an iterator.
Definition: DDS.h:349
void dump(std::ostream &strm) const override
dumps information about this object
Definition: DMR.cc:439
virtual DDS * getDDS()
Build a DDS from a DMR.
Definition: DMR.cc:260
~DMR() override
Definition: DMR.cc:190
std::string name() const
Definition: DMR.h:119
void set_dap_version(const std::string &version_string)
Definition: DMR.cc:308
std::string get_namespace() const
Get the namespace associated with the DMR.
Definition: DMR.h:155
std::string request_xml_base() const
Get the URL that will return this DMR.
Definition: DMR.h:149
virtual void build_using_dds(DDS &dds)
Definition: DMR.cc:212
D4Group * root()
Definition: DMR.cc:296
long request_size(bool constrained)
Get the estimated response size, in kilobytes.
Definition: DMR.cc:355
uint64_t request_size_kb(bool constrained)
Compute the estimated response size, in kilobytes.
Definition: DMR.cc:370
std::string filename() const
Definition: DMR.h:136
void print_dap4(XMLWriter &xml, bool constrained=false)
Definition: DMR.cc:386
libdap base object for common functionality of libdap objects
Definition: DapObj.h:51
A class for software fault reporting.
Definition: InternalErr.h:65
top level DAP object to house generic methods
Definition: AlarmHandler.h:36