libdap  Updated for version 3.18.2
D4Group.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., 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 #include "config.h"
26 // #define DODS_DEBUG 1
27 
28 #include <iostream>
29 #include <sstream>
30 #include <iomanip>
31 
32 #include <stdint.h>
33 
34 #include "crc.h"
35 
36 #include "BaseType.h"
37 #include "Array.h"
38 
39 #include "XMLWriter.h"
40 #include "D4Attributes.h"
41 #include "D4Dimensions.h"
42 #include "D4Group.h"
43 #include "D4Enum.h"
44 
45 #include "D4StreamMarshaller.h"
46 #include "D4StreamUnMarshaller.h"
47 
48 #include "debug.h"
49 
55 #undef INCLUDE_SOURCE_BYTE_ORDER
56 
57 namespace libdap {
58 
59 void D4Group::m_duplicate(const D4Group &g)
60 {
61  DBG(cerr << "In D4Group::m_duplicate for " << g.name() << endl);
62 
63  // dims; deep copy, this is the parent
64  if (g.d_dims) {
65  d_dims = new D4Dimensions(*(g.d_dims));
66  d_dims->set_parent(this);
67 
68  // Update all of the D4Dimension weak pointers in the Array objects.
69  // This is a hack - we know that Constructor::m_duplicate() has been
70  // called at this point and any Array instances have dimension pointers
71  // that reference the 'old' dimensions (g.d_dims) and not the 'new'
72  // dimensions made above. Scan every array and re-wire the weak pointers.
73  // jhrg 8/15/14
74  Vars_citer vi = d_vars.begin();
75  while (vi != d_vars.end()) {
76  if ((*vi)->type() == dods_array_c)
77  static_cast<Array*>(*vi)->update_dimension_pointers(g.d_dims, d_dims);
78  ++vi;
79  }
80  }
81 
82 #if 0
83  // Moved this block up inside the if because g.d_dims might be false. jhrg 9/14/15
84  Vars_citer vi = d_vars.begin();
85  while (vi != d_vars.end()) {
86  if ((*vi)->type() == dods_array_c)
87  static_cast<Array*>(*vi)->update_dimension_pointers(g.d_dims, d_dims);
88  ++vi;
89  }
90 #endif
91 
92  // enums; deep copy
93  if (g.d_enum_defs) d_enum_defs = new D4EnumDefs(*g.d_enum_defs);
94 
95  // groups
96  groupsCIter i = g.d_groups.begin();
97  while(i != g.d_groups.end()) {
98  D4Group *g = (*i++)->ptr_duplicate();
99  add_group_nocopy(g);
100  }
101 
102  DBG(cerr << "Exiting D4Group::m_duplicate" << endl);
103 }
104 
115 D4Group::D4Group(const string &name)
116  : Constructor(name, dods_group_c, /*is_dap4*/true), d_dims(0), d_enum_defs(0)
117 {}
118 
129 D4Group::D4Group(const string &name, const string &dataset)
130  : Constructor(name, dataset, dods_group_c, /*is_dap4*/true), d_dims(0), d_enum_defs(0)
131 {}
132 
134 D4Group::D4Group(const D4Group &rhs) : Constructor(rhs), d_dims(0), d_enum_defs(0)
135 {
136  DBG(cerr << "In D4Group::copy_ctor for " << rhs.name() << endl);
137  m_duplicate(rhs);
138 }
139 
140 D4Group::~D4Group()
141 {
142  delete d_dims;
143  delete d_enum_defs;
144 
145  groupsIter i = d_groups.begin();
146  while(i != d_groups.end())
147  delete *i++;
148 }
149 
150 D4Group *
152 {
153  return new D4Group(*this);
154 }
155 
156 D4Group &
157 D4Group::operator=(const D4Group &rhs)
158 {
159  if (this == &rhs)
160  return *this;
161 
162  dynamic_cast<Constructor &>(*this) = rhs; // run Constructor=
163 
164  m_duplicate(rhs);
165 
166  return *this;
167 }
168 
175 string
177 {
178  // The root group is named "/" (always)
179  return (name() == "/") ? "/" : static_cast<D4Group*>(get_parent())->FQN() + name() + "/";
180 }
181 
182 // Note that in order for this to work the second argument must not be a reference.
183 // jhrg 8/20/13
184 static bool
185 name_eq(D4Group *g, const string name)
186 {
187  return g->name() == name;
188 }
189 
190 D4Group *
191 D4Group::find_child_grp(const string &grp_name)
192 {
193  groupsIter g = find_if(grp_begin(), grp_end(), bind2nd(ptr_fun(name_eq), grp_name));
194  return (g == grp_end()) ? 0: *g;
195 }
196 
197 // TODO Add constraint param? jhrg 11/17/13
198 BaseType *
199 D4Group::find_first_var_that_uses_dimension(D4Dimension *dim)
200 {
201  // for each group, starting with the root group
202  // for each variable in the group that is marked to send and is an array
203  // return the btp if it uses the D4Dimension
204  // if it contains child groups, search those
205  // return the btp if it uses the D4Dimension
206  // return null
207 
208  // exhaustive breadth-first search for 'dim
209 
210  // root group
211  for (Vars_iter i = var_begin(), e = var_end(); i != e; ++i) {
212  if ((*i)->send_p() && (*i)->type() == dods_array_c) {
213  Array *a = static_cast<Array*>(*i);
214  for (Array::Dim_iter di = a->dim_begin(), de = a->dim_end(); di != de; ++di) {
215  if (a->dimension_D4dim(di) == dim)
216  return a;
217  }
218  }
219  }
220 
221  for (groupsIter i = grp_begin(), e = grp_end(); i != e; ++i) {
222  BaseType *btp = (*i)->find_first_var_that_uses_dimension(dim);
223  if (btp) return btp;
224  }
225 
226  return 0;
227 }
228 
229 BaseType *
230 D4Group::find_first_var_that_uses_enumeration(D4EnumDef *enum_def)
231 {
232  // for each group, starting with the root group
233  // for each variable in the group that is marked to send and is an array
234  // return the btp if it uses the D4EnumDef
235  // if it contains child groups, search those
236  // return the btp if it uses the D4EnumDef
237  // return null
238 
239  // exhaustive breadth-first search for 'dim
240 
241  // root group
242  for (Vars_iter i = var_begin(), e = var_end(); i != e; ++i) {
243  if ((*i)->send_p() && (*i)->type() == dods_enum_c) {
244  D4Enum *e = static_cast<D4Enum*>(*i);
245  if (e->enumeration() == enum_def)
246  return e;
247  }
248  }
249 
250  for (groupsIter i = grp_begin(), e = grp_end(); i != e; ++i) {
251  BaseType *btp = (*i)->find_first_var_that_uses_enumeration(enum_def);
252  if (btp) return btp;
253  }
254 
255  return 0;
256 }
257 
267 D4Dimension *
268 D4Group::find_dim(const string &path)
269 {
270  string lpath = path; // get a mutable copy
271 
272  // special-case for the root group
273  if (lpath[0] == '/') {
274  if (name() != "/")
275  throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
276  else
277  lpath = lpath.substr(1);
278  }
279 
280  string::size_type pos = lpath.find('/');
281  if (pos == string::npos) {
282  // name looks like 'bar'
283  return dims()->find_dim(lpath);
284  }
285 
286  // name looks like foo/bar/baz where foo and bar must be groups
287  string grp_name = lpath.substr(0, pos);
288  lpath = lpath.substr(pos + 1);
289 
290  D4Group *grp = find_child_grp(grp_name);
291  return (grp == 0) ? 0: grp->find_dim(lpath);
292 }
293 
294 Array *
295 D4Group::find_map_source(const string &path)
296 {
297  BaseType *map_source = m_find_map_source_helper(path);
298 
299  // TODO more complete semantic checking jhrg 10/16/13
300  if (map_source && map_source->type() == dods_array_c) return static_cast<Array*>(map_source);
301 
302  return 0;
303 }
304 
305 BaseType *
306 D4Group::m_find_map_source_helper(const string &path)
307 {
308  string lpath = path; // get a mutable copy
309 
310  // special-case for the root group
311  if (lpath[0] == '/') {
312  if (name() != "/")
313  throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
314  else
315  lpath = lpath.substr(1);
316  }
317 
318  string::size_type pos = lpath.find('/');
319  if (pos == string::npos) {
320  // name looks like 'bar'
321  return var(lpath);
322  }
323 
324  // name looks like foo/bar/baz where foo an bar must be groups
325  string grp_name = lpath.substr(0, pos);
326  lpath = lpath.substr(pos + 1);
327 
328  D4Group *grp = find_child_grp(grp_name);
329  return (grp == 0) ? 0: grp->var(lpath);
330 }
331 
332 D4EnumDef *
333 D4Group::find_enum_def(const string &path)
334 {
335  string lpath = path; // get a mutable copy
336 
337  // special-case for the root group
338  if (lpath[0] == '/') {
339  if (name() != "/")
340  throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
341  else
342  lpath = lpath.substr(1);
343  }
344 
345  string::size_type pos = lpath.find('/');
346  if (pos == string::npos) {
347  // name looks like 'bar'
348  return enum_defs()->find_enum_def(lpath);
349  }
350 
351  // name looks like foo/bar/baz where foo and bar must be groups
352  string grp_name = lpath.substr(0, pos);
353  lpath = lpath.substr(pos + 1);
354 
355  D4Group *grp = find_child_grp(grp_name);
356  return (grp == 0) ? 0: grp->enum_defs()->find_enum_def(lpath);
357 }
358 
366 BaseType *
367 D4Group::find_var(const string &path)
368 {
369  string lpath = path; // get a mutable copy
370 
371  // special-case for the root group
372  if (lpath[0] == '/') {
373  if (name() != "/")
374  throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
375  else
376  lpath = lpath.substr(1);
377  }
378 
379  string::size_type pos = lpath.find('/');
380  if (pos == string::npos) {
381  // name looks like 'bar' or bar.baz; lookup in the Constructor that's part of the Group
382  return var(lpath);
383  }
384 
385  // name looks like foo/bar/baz where foo and bar must be groups
386  string grp_name = lpath.substr(0, pos);
387  lpath = lpath.substr(pos + 1);
388 
389  D4Group *grp = find_child_grp(grp_name);
390  return (grp == 0) ? 0 : grp->find_var(lpath);
391 }
392 
399 long
400 D4Group::request_size(bool constrained)
401 {
402  long long size = 0;
403  // variables
404  Constructor::Vars_iter v = var_begin();
405  while (v != var_end()) {
406  if (constrained) {
407  if ((*v)->send_p())
408  size += (*v)->width(constrained);
409  }
410  else {
411  size += (*v)->width(constrained);
412  }
413 
414  ++v;
415  }
416 
417  // groups
418  groupsIter g = d_groups.begin();
419  while (g != d_groups.end())
420  size += (*g++)->request_size(constrained);
421 
422  return size / 1024;
423 }
424 
425 void
427 {
428  groupsIter g = d_groups.begin();
429  while (g != d_groups.end())
430  (*g++)->set_read_p(state);
431 
433 }
434 
435 void
437 {
438  groupsIter g = d_groups.begin();
439  while (g != d_groups.end())
440  (*g++)->set_send_p(state);
441 
443 }
444 
445 void
446 D4Group::intern_data(/*Crc32 &checksum, DMR &dmr, ConstraintEvaluator &eval*/)
447 {
448  groupsIter g = d_groups.begin();
449  while (g != d_groups.end())
450  (*g++)->intern_data(/*checksum, dmr, eval*/);
451 
452  // Specialize how the top-level variables in any Group are sent; include
453  // a checksum for them. A subset operation might make an interior set of
454  // variables, but the parent structure will still be present and the checksum
455  // will be computed for that structure. In other words, DAP4 does not try
456  // to sort out which variables are the 'real' top-level variables and instead
457  // simply computes the CRC for whatever appears as a variable in the root
458  // group.
459  for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) {
460  // Only send the stuff in the current subset.
461  if ((*i)->send_p()) {
462 #if 0
463  checksum.Reset();
464 #endif
465  (*i)->intern_data(/*checksum, dmr, eval*/);
466 #if 0
467  D4Attribute *a = new D4Attribute("DAP4_Checksum_CRC32", attr_str_c);
468 
469  ostringstream oss;
470  oss.setf(ios::hex, ios::basefield);
471  oss << setfill('0') << setw(8) << checksum.GetCrc32();
472  a->add_value(oss.str());
473 #if INCLUDE_SOURCE_BYTE_ORDER
474  if (um.is_source_big_endian())
475  a->add_value("source:big-endian");
476  else
477  a->add_value("source:little-endian");
478 #endif
479  (*i)->attributes()->add_attribute_nocopy(a);
480  DBG(cerr << "CRC32: " << oss.str() << " for " << (*i)->name() << endl);
481 #endif
482  }
483  }
484 }
485 
497 void
498 D4Group::serialize(D4StreamMarshaller &m, DMR &dmr, /*ConstraintEvaluator &eval,*/ bool filter)
499 {
500 #if 0
501  // This will call Constructor read which will, for everything but a Sequence,
502  // read all of the data in one shot. However, the serialize() methods for the
503  // Arrays, Structures, etc., also have read() calls in them and those can be
504  // used to control how long the data are in memory, e.g., limiting the lifetime
505  // of a large array and avoiding having overlapping arrays when they are not
506  // needed. For a sequence read() has different semantics. It is called once
507  // for every instance and the read_p flag is not used.
508  if (!read_p())
509  read(); // read() throws Error
510 #endif
511 
512  groupsIter g = d_groups.begin();
513  while (g != d_groups.end())
514  (*g++)->serialize(m, dmr, /*eval,*/ filter);
515 
516  // Specialize how the top-level variables in any Group are sent; include
517  // a checksum for them. A subset operation might make an interior set of
518  // variables, but the parent structure will still be present and the checksum
519  // will be computed for that structure. In other words, DAP4 does not try
520  // to sort out which variables are the 'real' top-level variables and instead
521  // simply computes the CRC for whatever appears as a variable in the root
522  // group.
523  for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) {
524  // Only send the stuff in the current subset.
525  if ((*i)->send_p()) {
526  m.reset_checksum();
527 
528  DBG(cerr << "Serializing variable " << (*i)->type_name() << " " << (*i)->name() << endl);
529  (*i)->serialize(m, dmr, /*eval,*/ filter);
530 
531  DBG(cerr << "Wrote CRC32: " << m.get_checksum() << " for " << (*i)->name() << endl);
532  m.put_checksum();
533  }
534  }
535 }
536 
538 {
539  groupsIter g = d_groups.begin();
540  while (g != d_groups.end()) {
541  DBG(cerr << "Deserializing group " << (*g)->name() << endl);
542  (*g++)->deserialize(um, dmr);
543  }
544  // Specialize how the top-level variables in any Group are received; read
545  // their checksum and store the value in a magic attribute of the variable
546  for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) {
547  DBG(cerr << "Deserializing variable " << (*i)->type_name() << " " << (*i)->name() << endl);
548  (*i)->deserialize(um, dmr);
549 
550  D4Attribute *a = new D4Attribute("DAP4_Checksum_CRC32", attr_str_c);
551  string crc = um.get_checksum_str();
552  a->add_value(crc);
553 #if INCLUDE_SOURCE_BYTE_ORDER
554  if (um.is_source_big_endian())
555  a->add_value("source:big-endian");
556  else
557  a->add_value("source:little-endian");
558 #endif
559  DBG(cerr << "Read CRC32: " << crc << " for " << (*i)->name() << endl);
560  (*i)->attributes()->add_attribute_nocopy(a);
561  }
562 }
563 
564 void
565 D4Group::print_dap4(XMLWriter &xml, bool constrained)
566 {
567  if (!name().empty() && name() != "/") {
568  // For named groups, if constrained is true only print if this group
569  // has variables that are marked for transmission. For the root group
570  // this test is not made.
571  if (constrained && !send_p())
572  return;
573 
574  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) type_name().c_str()) < 0)
575  throw InternalErr(__FILE__, __LINE__, "Could not write " + type_name() + " element");
576 
577  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name().c_str()) < 0)
578  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
579  }
580 
581  // dims
582  if (!dims()->empty())
583  dims()->print_dap4(xml, constrained);
584 
585  // enums
586  if (!enum_defs()->empty())
587  enum_defs()->print_dap4(xml, constrained);
588 
589  // variables
590  Constructor::Vars_iter v = var_begin();
591  while (v != var_end())
592  (*v++)->print_dap4(xml, constrained);
593 
594  // attributes
595  attributes()->print_dap4(xml);
596 
597  // groups
598  groupsIter g = d_groups.begin();
599  while (g != d_groups.end())
600  (*g++)->print_dap4(xml, constrained);
601 
602  if (!name().empty() && name() != "/") {
603  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
604  throw InternalErr(__FILE__, __LINE__, "Could not end " + type_name() + " element");
605  }
606 }
607 
608 } /* namespace libdap */
virtual bool read_p()
Has this variable been read?
Definition: BaseType.cc:425
virtual string name() const
Returns the name of the class instance.
Definition: BaseType.cc:265
bool is_source_big_endian() const
Is the data source we are reading from a big-endian machine? We need this because the value of the CR...
virtual BaseType * var(const string &name, bool exact_match=true, btp_stack *s=0)
btp_stack no longer needed; use back pointers (BaseType::get_parent())
Definition: Constructor.cc:242
D4Dimension * find_dim(const string &path)
Find the dimension using a path. Using the DAP4 name syntax, lookup a dimension. The dimension must b...
Definition: D4Group.cc:268
Read data from the stream made by D4StreamMarshaller.
D4Group(const string &name)
Definition: D4Group.cc:115
virtual void serialize(D4StreamMarshaller &m, DMR &dmr, bool filter=false)
Serialize a Group.
Definition: D4Group.cc:498
A class for software fault reporting.
Definition: InternalErr.h:64
Dim_iter dim_end()
Definition: Array.cc:517
Holds a DAP4 enumeration.
Definition: D4Enum.h:61
Marshaller that knows how to marshal/serialize dap data objects to a C++ iostream using DAP4&#39;s receiv...
long request_size(bool constrained)
Definition: D4Group.cc:400
virtual Type type() const
Returns the type of the class instance.
Definition: BaseType.cc:310
std::vector< dimension >::iterator Dim_iter
Definition: Array.h:204
virtual bool read()
simple implementation of read that iterates through vars and calls read on them
Definition: Constructor.cc:451
groupsIter grp_end()
Get an iterator to the end of the values.
Definition: D4Group.h:112
virtual D4Attributes * attributes()
Definition: BaseType.cc:544
virtual void deserialize(D4StreamUnMarshaller &um, DMR &dmr)
Definition: D4Group.cc:537
virtual void intern_data()
Read data into this variable.
Definition: D4Group.cc:446
groupsIter grp_begin()
Get an iterator to the start of the values.
Definition: D4Group.h:109
virtual BaseType * get_parent() const
Definition: BaseType.cc:672
The basic data type for the DODS DAP types.
Definition: BaseType.h:117
Dim_iter dim_begin()
Definition: Array.cc:510
virtual string type_name() const
Returns the type of the class instance as a string.
Definition: BaseType.cc:324
Vars_iter var_begin()
Definition: Constructor.cc:331
void print_dap4(XMLWriter &xml, bool constrained=false)
Definition: D4Group.cc:565
virtual std::string FQN() const
Definition: D4Group.cc:176
Vars_iter var_end()
Definition: Constructor.cc:339
virtual D4Group * ptr_duplicate()
Definition: D4Group.cc:151
virtual void put_checksum()
Write the checksum Write the checksum for the data sent since the last call to reset_checksum() to th...
D4EnumDefs * enum_defs()
Get the enumerations defined for this Group.
Definition: D4Group.h:95
virtual void set_send_p(bool state)
Definition: Constructor.cc:183
A multidimensional array of identical data types.
Definition: Array.h:112
virtual bool send_p()
Should this variable be sent?
Definition: BaseType.cc:499
virtual void set_read_p(bool state)
Sets the value of the read_p property.
Definition: D4Group.cc:426
virtual void set_send_p(bool state)
Definition: D4Group.cc:436
virtual string dataset() const
Returns the name of the dataset used to create this instance.
Definition: BaseType.cc:303
BaseType * find_var(const string &name)
Definition: D4Group.cc:367
D4Dimensions * dims()
Get the dimensions defined for this Group.
Definition: D4Group.h:80
virtual void set_read_p(bool state)
Sets the value of the read_p property.
Definition: Constructor.cc:193