libdap  Updated for version 3.18.3
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  // Only D4Groups are in the d_groups container.
99  D4Group *g = static_cast<D4Group*>((*i++)->ptr_duplicate());
100  add_group_nocopy(g);
101  }
102 
103  DBG(cerr << "Exiting D4Group::m_duplicate" << endl);
104 }
105 
116 D4Group::D4Group(const string &name)
117  : Constructor(name, dods_group_c, /*is_dap4*/true), d_dims(0), d_enum_defs(0)
118 {}
119 
130 D4Group::D4Group(const string &name, const string &dataset)
131  : Constructor(name, dataset, dods_group_c, /*is_dap4*/true), d_dims(0), d_enum_defs(0)
132 {}
133 
135 D4Group::D4Group(const D4Group &rhs) : Constructor(rhs), d_dims(0), d_enum_defs(0)
136 {
137  DBG(cerr << "In D4Group::copy_ctor for " << rhs.name() << endl);
138  m_duplicate(rhs);
139 }
140 
141 D4Group::~D4Group()
142 {
143  delete d_dims;
144  delete d_enum_defs;
145 
146  groupsIter i = d_groups.begin();
147  while(i != d_groups.end())
148  delete *i++;
149 }
150 
151 #if 0
152 D4Group *
153 
154 // I think this was a mistake. jhrg 11/17/16
155 #endif
156 BaseType *
158 {
159  return new D4Group(*this);
160 }
161 
162 D4Group &
163 D4Group::operator=(const D4Group &rhs)
164 {
165  if (this == &rhs)
166  return *this;
167 
168  dynamic_cast<Constructor &>(*this) = rhs; // run Constructor=
169 
170  m_duplicate(rhs);
171 
172  return *this;
173 }
174 
181 string
183 {
184  // The root group is named "/" (always)
185  return (name() == "/") ? "/" : static_cast<D4Group*>(get_parent())->FQN() + name() + "/";
186 }
187 
188 // Note that in order for this to work the second argument must not be a reference.
189 // jhrg 8/20/13
190 static bool
191 name_eq(D4Group *g, const string name)
192 {
193  return g->name() == name;
194 }
195 
196 D4Group *
197 D4Group::find_child_grp(const string &grp_name)
198 {
199  groupsIter g = find_if(grp_begin(), grp_end(), bind2nd(ptr_fun(name_eq), grp_name));
200  return (g == grp_end()) ? 0: *g;
201 }
202 
203 // TODO Add constraint param? jhrg 11/17/13
204 BaseType *
205 D4Group::find_first_var_that_uses_dimension(D4Dimension *dim)
206 {
207  // for each group, starting with the root group
208  // for each variable in the group that is marked to send and is an array
209  // return the btp if it uses the D4Dimension
210  // if it contains child groups, search those
211  // return the btp if it uses the D4Dimension
212  // return null
213 
214  // exhaustive breadth-first search for 'dim
215 
216  // root group
217  for (Vars_iter i = var_begin(), e = var_end(); i != e; ++i) {
218  if ((*i)->send_p() && (*i)->type() == dods_array_c) {
219  Array *a = static_cast<Array*>(*i);
220  for (Array::Dim_iter di = a->dim_begin(), de = a->dim_end(); di != de; ++di) {
221  if (a->dimension_D4dim(di) == dim)
222  return a;
223  }
224  }
225  }
226 
227  for (groupsIter i = grp_begin(), e = grp_end(); i != e; ++i) {
228  BaseType *btp = (*i)->find_first_var_that_uses_dimension(dim);
229  if (btp) return btp;
230  }
231 
232  return 0;
233 }
234 
235 BaseType *
236 D4Group::find_first_var_that_uses_enumeration(D4EnumDef *enum_def)
237 {
238  // for each group, starting with the root group
239  // for each variable in the group that is marked to send and is an array
240  // return the btp if it uses the D4EnumDef
241  // if it contains child groups, search those
242  // return the btp if it uses the D4EnumDef
243  // return null
244 
245  // exhaustive breadth-first search for 'dim
246 
247  // root group
248  for (Vars_iter i = var_begin(), e = var_end(); i != e; ++i) {
249  if ((*i)->send_p() && (*i)->type() == dods_enum_c) {
250  D4Enum *e = static_cast<D4Enum*>(*i);
251  if (e->enumeration() == enum_def)
252  return e;
253  }
254  }
255 
256  for (groupsIter i = grp_begin(), e = grp_end(); i != e; ++i) {
257  BaseType *btp = (*i)->find_first_var_that_uses_enumeration(enum_def);
258  if (btp) return btp;
259  }
260 
261  return 0;
262 }
263 
273 D4Dimension *
274 D4Group::find_dim(const string &path)
275 {
276  string lpath = path; // get a mutable copy
277 
278  // special-case for the root group
279  if (lpath[0] == '/') {
280  if (name() != "/")
281  throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
282  else
283  lpath = lpath.substr(1);
284  }
285 
286  string::size_type pos = lpath.find('/');
287  if (pos == string::npos) {
288  // name looks like 'bar'
289  return dims()->find_dim(lpath);
290  }
291 
292  // name looks like foo/bar/baz where foo and bar must be groups
293  string grp_name = lpath.substr(0, pos);
294  lpath = lpath.substr(pos + 1);
295 
296  D4Group *grp = find_child_grp(grp_name);
297  return (grp == 0) ? 0: grp->find_dim(lpath);
298 }
299 
300 Array *
301 D4Group::find_map_source(const string &path)
302 {
303  BaseType *map_source = m_find_map_source_helper(path);
304 
305  // TODO more complete semantic checking jhrg 10/16/13
306  if (map_source && map_source->type() == dods_array_c) return static_cast<Array*>(map_source);
307 
308  return 0;
309 }
310 
311 BaseType *
312 D4Group::m_find_map_source_helper(const string &path)
313 {
314  string lpath = path; // get a mutable copy
315 
316  // special-case for the root group
317  if (lpath[0] == '/') {
318  if (name() != "/")
319  throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
320  else
321  lpath = lpath.substr(1);
322  }
323 
324  string::size_type pos = lpath.find('/');
325  if (pos == string::npos) {
326  // name looks like 'bar'
327  return var(lpath);
328  }
329 
330  // name looks like foo/bar/baz where foo an bar must be groups
331  string grp_name = lpath.substr(0, pos);
332  lpath = lpath.substr(pos + 1);
333 
334  D4Group *grp = find_child_grp(grp_name);
335  return (grp == 0) ? 0: grp->var(lpath);
336 }
337 
338 D4EnumDef *
339 D4Group::find_enum_def(const string &path)
340 {
341  string lpath = path; // get a mutable copy
342 
343  // special-case for the root group
344  if (lpath[0] == '/') {
345  if (name() != "/")
346  throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
347  else
348  lpath = lpath.substr(1);
349  }
350 
351  string::size_type pos = lpath.find('/');
352  if (pos == string::npos) {
353  // name looks like 'bar'
354  return enum_defs()->find_enum_def(lpath);
355  }
356 
357  // name looks like foo/bar/baz where foo and bar must be groups
358  string grp_name = lpath.substr(0, pos);
359  lpath = lpath.substr(pos + 1);
360 
361  D4Group *grp = find_child_grp(grp_name);
362  return (grp == 0) ? 0: grp->enum_defs()->find_enum_def(lpath);
363 }
364 
372 BaseType *
373 D4Group::find_var(const string &path)
374 {
375  string lpath = path; // get a mutable copy
376 
377  // special-case for the root group
378  if (lpath[0] == '/') {
379  if (name() != "/")
380  throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
381  else
382  lpath = lpath.substr(1);
383  }
384 
385  string::size_type pos = lpath.find('/');
386  if (pos == string::npos) {
387  // name looks like 'bar' or bar.baz; lookup in the Constructor that's part of the Group
388  return var(lpath);
389  }
390 
391  // name looks like foo/bar/baz where foo and bar must be groups
392  string grp_name = lpath.substr(0, pos);
393  lpath = lpath.substr(pos + 1);
394 
395  D4Group *grp = find_child_grp(grp_name);
396  return (grp == 0) ? 0 : grp->find_var(lpath);
397 }
398 
405 long
406 D4Group::request_size(bool constrained)
407 {
408  long long size = 0;
409  // variables
410  Constructor::Vars_iter v = var_begin();
411  while (v != var_end()) {
412  if (constrained) {
413  if ((*v)->send_p())
414  size += (*v)->width(constrained);
415  }
416  else {
417  size += (*v)->width(constrained);
418  }
419 
420  ++v;
421  }
422 
423  // groups
424  groupsIter g = d_groups.begin();
425  while (g != d_groups.end())
426  size += (*g++)->request_size(constrained);
427 
428  return size / 1024;
429 }
430 
431 void
433 {
434  groupsIter g = d_groups.begin();
435  while (g != d_groups.end())
436  (*g++)->set_read_p(state);
437 
439 }
440 
441 void
443 {
444  groupsIter g = d_groups.begin();
445  while (g != d_groups.end())
446  (*g++)->set_send_p(state);
447 
449 }
450 
451 void
452 D4Group::intern_data(/*Crc32 &checksum, DMR &dmr, ConstraintEvaluator &eval*/)
453 {
454  groupsIter g = d_groups.begin();
455  while (g != d_groups.end())
456  (*g++)->intern_data(/*checksum, dmr, eval*/);
457 
458  // Specialize how the top-level variables in any Group are sent; include
459  // a checksum for them. A subset operation might make an interior set of
460  // variables, but the parent structure will still be present and the checksum
461  // will be computed for that structure. In other words, DAP4 does not try
462  // to sort out which variables are the 'real' top-level variables and instead
463  // simply computes the CRC for whatever appears as a variable in the root
464  // group.
465  for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) {
466  // Only send the stuff in the current subset.
467  if ((*i)->send_p()) {
468 #if 0
469  checksum.Reset();
470 #endif
471  (*i)->intern_data(/*checksum, dmr, eval*/);
472 #if 0
473  D4Attribute *a = new D4Attribute("DAP4_Checksum_CRC32", attr_str_c);
474 
475  ostringstream oss;
476  oss.setf(ios::hex, ios::basefield);
477  oss << setfill('0') << setw(8) << checksum.GetCrc32();
478  a->add_value(oss.str());
479 #if INCLUDE_SOURCE_BYTE_ORDER
480  if (um.is_source_big_endian())
481  a->add_value("source:big-endian");
482  else
483  a->add_value("source:little-endian");
484 #endif
485  (*i)->attributes()->add_attribute_nocopy(a);
486  DBG(cerr << "CRC32: " << oss.str() << " for " << (*i)->name() << endl);
487 #endif
488  }
489  }
490 }
491 
503 void
504 D4Group::serialize(D4StreamMarshaller &m, DMR &dmr, /*ConstraintEvaluator &eval,*/ bool filter)
505 {
506 #if 0
507  // This will call Constructor read which will, for everything but a Sequence,
508  // read all of the data in one shot. However, the serialize() methods for the
509  // Arrays, Structures, etc., also have read() calls in them and those can be
510  // used to control how long the data are in memory, e.g., limiting the lifetime
511  // of a large array and avoiding having overlapping arrays when they are not
512  // needed. For a sequence read() has different semantics. It is called once
513  // for every instance and the read_p flag is not used.
514  if (!read_p())
515  read(); // read() throws Error
516 #endif
517 
518  groupsIter g = d_groups.begin();
519  while (g != d_groups.end())
520  (*g++)->serialize(m, dmr, /*eval,*/ filter);
521 
522  // Specialize how the top-level variables in any Group are sent; include
523  // a checksum for them. A subset operation might make an interior set of
524  // variables, but the parent structure will still be present and the checksum
525  // will be computed for that structure. In other words, DAP4 does not try
526  // to sort out which variables are the 'real' top-level variables and instead
527  // simply computes the CRC for whatever appears as a variable in the root
528  // group.
529  for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) {
530  // Only send the stuff in the current subset.
531  if ((*i)->send_p()) {
532  m.reset_checksum();
533 
534  DBG(cerr << "Serializing variable " << (*i)->type_name() << " " << (*i)->name() << endl);
535  (*i)->serialize(m, dmr, /*eval,*/ filter);
536 
537  DBG(cerr << "Wrote CRC32: " << m.get_checksum() << " for " << (*i)->name() << endl);
538  m.put_checksum();
539  }
540  }
541 }
542 
544 {
545  groupsIter g = d_groups.begin();
546  while (g != d_groups.end()) {
547  DBG(cerr << "Deserializing group " << (*g)->name() << endl);
548  (*g++)->deserialize(um, dmr);
549  }
550  // Specialize how the top-level variables in any Group are received; read
551  // their checksum and store the value in a magic attribute of the variable
552  for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) {
553  DBG(cerr << "Deserializing variable " << (*i)->type_name() << " " << (*i)->name() << endl);
554  (*i)->deserialize(um, dmr);
555 
556  D4Attribute *a = new D4Attribute("DAP4_Checksum_CRC32", attr_str_c);
557  string crc = um.get_checksum_str();
558  a->add_value(crc);
559 #if INCLUDE_SOURCE_BYTE_ORDER
560  if (um.is_source_big_endian())
561  a->add_value("source:big-endian");
562  else
563  a->add_value("source:little-endian");
564 #endif
565  DBG(cerr << "Read CRC32: " << crc << " for " << (*i)->name() << endl);
566  (*i)->attributes()->add_attribute_nocopy(a);
567  }
568 }
569 
570 void
571 D4Group::print_dap4(XMLWriter &xml, bool constrained)
572 {
573  if (!name().empty() && name() != "/") {
574  // For named groups, if constrained is true only print if this group
575  // has variables that are marked for transmission. For the root group
576  // this test is not made.
577  if (constrained && !send_p())
578  return;
579 
580  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) type_name().c_str()) < 0)
581  throw InternalErr(__FILE__, __LINE__, "Could not write " + type_name() + " element");
582 
583  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name().c_str()) < 0)
584  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
585  }
586 
587  // dims
588  if (!dims()->empty())
589  dims()->print_dap4(xml, constrained);
590 
591  // enums
592  if (!enum_defs()->empty())
593  enum_defs()->print_dap4(xml, constrained);
594 
595  // variables
596  Constructor::Vars_iter v = var_begin();
597  while (v != var_end())
598  (*v++)->print_dap4(xml, constrained);
599 
600  // attributes
601  attributes()->print_dap4(xml);
602 
603  // groups
604  groupsIter g = d_groups.begin();
605  while (g != d_groups.end())
606  (*g++)->print_dap4(xml, constrained);
607 
608  if (!name().empty() && name() != "/") {
609  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
610  throw InternalErr(__FILE__, __LINE__, "Could not end " + type_name() + " element");
611  }
612 }
613 
614 } /* 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:274
Read data from the stream made by D4StreamMarshaller.
D4Group(const string &name)
Definition: D4Group.cc:116
BaseType(const string &n, const Type &t, bool is_dap4=false)
The BaseType constructor.
Definition: BaseType.cc:125
virtual void serialize(D4StreamMarshaller &m, DMR &dmr, bool filter=false)
Serialize a Group.
Definition: D4Group.cc:504
A class for software fault reporting.
Definition: InternalErr.h:64
virtual BaseType * ptr_duplicate()
Definition: D4Group.cc:157
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:406
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:114
virtual D4Attributes * attributes()
Definition: BaseType.cc:544
virtual void deserialize(D4StreamUnMarshaller &um, DMR &dmr)
Definition: D4Group.cc:543
virtual void intern_data()
Read data into this variable.
Definition: D4Group.cc:452
groupsIter grp_begin()
Get an iterator to the start of the values.
Definition: D4Group.h:111
virtual BaseType * get_parent() const
Definition: BaseType.cc:672
The basic data type for the DODS DAP types.
Definition: BaseType.h:117
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:571
virtual std::string FQN() const
Definition: D4Group.cc:182
Vars_iter var_end()
Definition: Constructor.cc:339
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:97
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:432
virtual void set_send_p(bool state)
Definition: D4Group.cc:442
BaseType * find_var(const string &name)
Definition: D4Group.cc:373
D4Dimensions * dims()
Get the dimensions defined for this Group.
Definition: D4Group.h:82
virtual void set_read_p(bool state)
Sets the value of the read_p property.
Definition: Constructor.cc:193