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