libdap++  Updated for version 3.14.0
D4Group.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) 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 
51 namespace libdap {
52 
54 {
55  DBG(cerr << "In D4Group::m_duplicate for " << g.name() << endl);
56 
57  // dims; deep copy, this is the parent
58  if (g.d_dims) {
59  d_dims = new D4Dimensions(*(g.d_dims));
60  d_dims->set_parent(this);
61  }
62 
63  // Update all of the D4Dimension weak pointers in the Array objects.
64  // This is a hack - we know that Constructor::m_duplicate() has been
65  // called at this point and any Array instances have dimension pointers
66  // that reference the 'old' dimensions (g.d_dims) and not the 'new'
67  // dimensions made above. Scan every array and re-wire the weak pointers.
68  // jhrg 8/15/14
69  Vars_citer vi = d_vars.begin();
70  while (vi != d_vars.end()) {
71  if ((*vi)->type() == dods_array_c)
72  static_cast<Array*>(*vi)->update_dimension_pointers(g.d_dims, d_dims);
73  ++vi;
74  }
75 
76  // enums; deep copy
77  if (g.d_enum_defs) d_enum_defs = new D4EnumDefs(*g.d_enum_defs);
78 
79  // groups
80  groupsCIter i = g.d_groups.begin();
81  while(i != g.d_groups.end()) {
82  D4Group *g = (*i++)->ptr_duplicate();
84  }
85 
86  DBG(cerr << "Exiting D4Group::m_duplicate" << endl);
87 }
88 
95 D4Group::D4Group(const string &name)
96  : Constructor(name, dods_group_c, /*is_dap4*/true), d_dims(0), d_enum_defs(0)
97 {}
98 
106 D4Group::D4Group(const string &name, const string &dataset)
107  : Constructor(name, dataset, dods_group_c, /*is_dap4*/true), d_dims(0), d_enum_defs(0)
108 {}
109 
111 D4Group::D4Group(const D4Group &rhs) : Constructor(rhs), d_dims(0), d_enum_defs(0)
112 {
113  DBG(cerr << "In D4Group::copy_ctor for " << rhs.name() << endl);
114  m_duplicate(rhs);
115 }
116 
118 {
119  delete d_dims;
120  delete d_enum_defs;
121 
122  groupsIter i = d_groups.begin();
123  while(i != d_groups.end())
124  delete *i++;
125 }
126 
127 D4Group *
129 {
130  return new D4Group(*this);
131 }
132 
133 D4Group &
135 {
136  if (this == &rhs)
137  return *this;
138 
139  dynamic_cast<Constructor &>(*this) = rhs; // run Constructor=
140 
141  m_duplicate(rhs);
142 
143  return *this;
144 }
145 
152 string
154 {
155  // The root group is named "/" (always)
156  return (name() == "/") ? "/" : static_cast<D4Group*>(get_parent())->FQN() + name() + "/";
157 }
158 
159 // Note that in order for this to work the second argument must not be a reference.
160 // jhrg 8/20/13
161 static bool
162 name_eq(D4Group *g, const string name)
163 {
164  return g->name() == name;
165 }
166 
167 D4Group *
168 D4Group::find_child_grp(const string &grp_name)
169 {
170  groupsIter g = find_if(grp_begin(), grp_end(), bind2nd(ptr_fun(name_eq), grp_name));
171  return (g == grp_end()) ? 0: *g;
172 }
173 
174 // TODO Add constraint param? jhrg 11/17/13
175 BaseType *
177 {
178  // for each group, starting with the root group
179  // for each variable in the group that is marked to send and is an array
180  // return the btp if it uses the D4Dimension
181  // if it contains child groups, search those
182  // return the btp if it uses the D4Dimension
183  // return null
184 
185  // exhaustive breadth-first search for 'dim
186 
187  // root group
188  for (Vars_iter i = var_begin(), e = var_end(); i != e; ++i) {
189  if ((*i)->send_p() && (*i)->type() == dods_array_c) {
190  Array *a = static_cast<Array*>(*i);
191  for (Array::Dim_iter di = a->dim_begin(), de = a->dim_end(); di != de; ++di) {
192  if (a->dimension_D4dim(di) == dim)
193  return a;
194  }
195  }
196  }
197 
198  for (groupsIter i = grp_begin(), e = grp_end(); i != e; ++i) {
199  BaseType *btp = (*i)->find_first_var_that_uses_dimension(dim);
200  if (btp) return btp;
201  }
202 
203  return 0;
204 }
205 
206 BaseType *
208 {
209  // for each group, starting with the root group
210  // for each variable in the group that is marked to send and is an array
211  // return the btp if it uses the D4EnumDef
212  // if it contains child groups, search those
213  // return the btp if it uses the D4EnumDef
214  // return null
215 
216  // exhaustive breadth-first search for 'dim
217 
218  // root group
219  for (Vars_iter i = var_begin(), e = var_end(); i != e; ++i) {
220  if ((*i)->send_p() && (*i)->type() == dods_enum_c) {
221  D4Enum *e = static_cast<D4Enum*>(*i);
222  if (e->enumeration() == enum_def)
223  return e;
224  }
225  }
226 
227  for (groupsIter i = grp_begin(), e = grp_end(); i != e; ++i) {
228  BaseType *btp = (*i)->find_first_var_that_uses_enumeration(enum_def);
229  if (btp) return btp;
230  }
231 
232  return 0;
233 }
234 
244 D4Dimension *
245 D4Group::find_dim(const string &path)
246 {
247  string lpath = path; // get a mutable copy
248 
249  // special-case for the root group
250  if (lpath[0] == '/') {
251  if (name() != "/")
252  throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
253  else
254  lpath = lpath.substr(1);
255  }
256 
257  string::size_type pos = lpath.find('/');
258  if (pos == string::npos) {
259  // name looks like 'bar'
260  return dims()->find_dim(lpath);
261  }
262 
263  // name looks like foo/bar/baz where foo and bar must be groups
264  string grp_name = lpath.substr(0, pos);
265  lpath = lpath.substr(pos + 1);
266 
267  D4Group *grp = find_child_grp(grp_name);
268  return (grp == 0) ? 0: grp->find_dim(lpath);
269 }
270 
271 Array *
272 D4Group::find_map_source(const string &path)
273 {
274  BaseType *map_source = m_find_map_source_helper(path);
275 
276  // TODO more complete semantic checking jhrg 10/16/13
277  if (map_source && map_source->type() == dods_array_c) return static_cast<Array*>(map_source);
278 
279  return 0;
280 }
281 
282 BaseType *
283 D4Group::m_find_map_source_helper(const string &path)
284 {
285  string lpath = path; // get a mutable copy
286 
287  // special-case for the root group
288  if (lpath[0] == '/') {
289  if (name() != "/")
290  throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
291  else
292  lpath = lpath.substr(1);
293  }
294 
295  string::size_type pos = lpath.find('/');
296  if (pos == string::npos) {
297  // name looks like 'bar'
298  return var(lpath);
299  }
300 
301  // name looks like foo/bar/baz where foo an bar must be groups
302  string grp_name = lpath.substr(0, pos);
303  lpath = lpath.substr(pos + 1);
304 
305  D4Group *grp = find_child_grp(grp_name);
306  return (grp == 0) ? 0: grp->var(lpath);
307 }
308 
309 D4EnumDef *
310 D4Group::find_enum_def(const string &path)
311 {
312  string lpath = path; // get a mutable copy
313 
314  // special-case for the root group
315  if (lpath[0] == '/') {
316  if (name() != "/")
317  throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
318  else
319  lpath = lpath.substr(1);
320  }
321 
322  string::size_type pos = lpath.find('/');
323  if (pos == string::npos) {
324  // name looks like 'bar'
325  return enum_defs()->find_enum_def(lpath);
326  }
327 
328  // name looks like foo/bar/baz where foo and bar must be groups
329  string grp_name = lpath.substr(0, pos);
330  lpath = lpath.substr(pos + 1);
331 
332  D4Group *grp = find_child_grp(grp_name);
333  return (grp == 0) ? 0: grp->enum_defs()->find_enum_def(lpath);
334 }
335 
343 BaseType *
344 D4Group::find_var(const string &path)
345 {
346  string lpath = path; // get a mutable copy
347 
348  // special-case for the root group
349  if (lpath[0] == '/') {
350  if (name() != "/")
351  throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
352  else
353  lpath = lpath.substr(1);
354  }
355 
356  string::size_type pos = lpath.find('/');
357  if (pos == string::npos) {
358  // name looks like 'bar' or bar.baz; lookup in the Constructor that's part of the Group
359  return var(lpath);
360  }
361 
362  // name looks like foo/bar/baz where foo and bar must be groups
363  string grp_name = lpath.substr(0, pos);
364  lpath = lpath.substr(pos + 1);
365 
366  D4Group *grp = find_child_grp(grp_name);
367  return (grp == 0) ? 0 : grp->find_var(lpath);
368 }
369 
376 long
377 D4Group::request_size(bool constrained)
378 {
379  long long size = 0;
380  // variables
382  while (v != var_end()) {
383  if (constrained) {
384  if ((*v)->send_p())
385  size += (*v)->width(constrained);
386  }
387  else {
388  size += (*v)->width(constrained);
389  }
390 
391  ++v;
392  }
393 
394  // groups
395  groupsIter g = d_groups.begin();
396  while (g != d_groups.end())
397  size += (*g++)->request_size(constrained);
398 
399  return size / 1024;
400 }
401 
402 void
404 {
405  groupsIter g = d_groups.begin();
406  while (g != d_groups.end())
407  (*g++)->set_read_p(state);
408 
410 }
411 
412 void
414 {
415  groupsIter g = d_groups.begin();
416  while (g != d_groups.end())
417  (*g++)->set_send_p(state);
418 
420 }
421 
422 void
423 D4Group::intern_data(Crc32 &checksum/*, DMR &dmr, ConstraintEvaluator &eval*/)
424 {
425  groupsIter g = d_groups.begin();
426  while (g != d_groups.end())
427  (*g++)->intern_data(checksum/*, dmr, eval*/);
428 
429  // Specialize how the top-level variables in any Group are sent; include
430  // a checksum for them. A subset operation might make an interior set of
431  // variables, but the parent structure will still be present and the checksum
432  // will be computed for that structure. In other words, DAP4 does not try
433  // to sort out which variables are the 'real' top-level variables and instead
434  // simply computes the CRC for whatever appears as a variable in the root
435  // group.
436  for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) {
437  // Only send the stuff in the current subset.
438  if ((*i)->send_p()) {
439  checksum.Reset();
440 
441  (*i)->intern_data(checksum/*, dmr, eval*/);
442 
443  D4Attribute *a = new D4Attribute("DAP4_Checksum_CRC32", attr_str_c);
444  ostringstream oss;
445  oss.setf(ios::hex, ios::basefield);
446  oss << setfill('0') << setw(8) << checksum.GetCrc32();
447  a->add_value(oss.str());
448  (*i)->attributes()->add_attribute_nocopy(a);
449  DBG(cerr << "CRC32: " << oss.str() << " for " << (*i)->name() << endl);
450  }
451  }
452 }
453 
465 void
466 D4Group::serialize(D4StreamMarshaller &m, DMR &dmr, /*ConstraintEvaluator &eval,*/ bool filter)
467 {
468 #if 0
469  // This will call Constructor read which will, for everything but a Sequence,
470  // read all of the data in one shot. However, the serialize() methods for the
471  // Arrays, Structures, etc., also have read() calls in them and those can be
472  // used to control how long the data are in memory, e.g., limiting the lifetime
473  // of a large array and avoiding having overlapping arrays when they are not
474  // needed. For a sequence read() has different semantics. It is called once
475  // for every instance and the read_p flag is not used.
476  if (!read_p())
477  read(); // read() throws Error
478 #endif
479 
480  groupsIter g = d_groups.begin();
481  while (g != d_groups.end())
482  (*g++)->serialize(m, dmr, /*eval,*/ filter);
483 
484  // Specialize how the top-level variables in any Group are sent; include
485  // a checksum for them. A subset operation might make an interior set of
486  // variables, but the parent structure will still be present and the checksum
487  // will be computed for that structure. In other words, DAP4 does not try
488  // to sort out which variables are the 'real' top-level variables and instead
489  // simply computes the CRC for whatever appears as a variable in the root
490  // group.
491  for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) {
492  // Only send the stuff in the current subset.
493  if ((*i)->send_p()) {
494  m.reset_checksum();
495 
496  (*i)->serialize(m, dmr, /*eval,*/ filter);
497 
498  DBG(cerr << "Wrote CRC32: " << m.get_checksum() << " for " << (*i)->name() << endl);
499  m.put_checksum();
500  }
501  }
502 }
503 
505 {
506  groupsIter g = d_groups.begin();
507  while (g != d_groups.end())
508  (*g++)->deserialize(um, dmr);
509 
510  // Specialize how the top-level variables in any Group are received; read
511  // their checksum and store the value in a magic attribute of the variable
512  for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) {
513  (*i)->deserialize(um, dmr);
514 
515  D4Attribute *a = new D4Attribute("DAP4_Checksum_CRC32", attr_str_c);
516  string crc = um.get_checksum_str();
517  a->add_value(crc);
518  DBG(cerr << "Read CRC32: " << crc << " for " << (*i)->name() << endl);
519  (*i)->attributes()->add_attribute_nocopy(a);
520  }
521 }
522 
523 void
524 D4Group::print_dap4(XMLWriter &xml, bool constrained)
525 {
526  if (!name().empty() && name() != "/") {
527  // For named groups, if constrained is true only print if this group
528  // has variables that are marked for transmission. For the root group
529  // this test is not made.
530  if (constrained && !send_p())
531  return;
532 
533  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) type_name().c_str()) < 0)
534  throw InternalErr(__FILE__, __LINE__, "Could not write " + type_name() + " element");
535 
536  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name().c_str()) < 0)
537  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
538  }
539 
540  // dims
541  if (!dims()->empty())
542  dims()->print_dap4(xml, constrained);
543 
544  // enums
545  if (!enum_defs()->empty())
546  enum_defs()->print_dap4(xml, constrained);
547 
548  // variables
550  while (v != var_end())
551  (*v++)->print_dap4(xml, constrained);
552 
553  // attributes
554  attributes()->print_dap4(xml);
555 
556  // groups
557  groupsIter g = d_groups.begin();
558  while (g != d_groups.end())
559  (*g++)->print_dap4(xml, constrained);
560 
561  if (!name().empty() && name() != "/") {
562  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
563  throw InternalErr(__FILE__, __LINE__, "Could not end " + type_name() + " element");
564  }
565 }
566 
567 } /* namespace libdap */
virtual bool read_p()
Has this variable been read?
Definition: BaseType.cc:421
checksum GetCrc32() const
Definition: crc.h:92
D4Group * find_child_grp(const string &grp_name)
Definition: D4Group.cc:168
xmlTextWriterPtr get_writer()
Definition: XMLWriter.h:56
void set_parent(D4Group *g)
Definition: D4Dimensions.h:145
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
std::vector< BaseType * > d_vars
Definition: Constructor.h:49
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:245
void print_dap4(XMLWriter &xml, bool constrained=false) const
void m_duplicate(const D4Group &g)
Definition: D4Group.cc:53
Read data from the stream made by D4StreamMarshaller.
std::vector< BaseType * >::iterator Vars_iter
Definition: Constructor.h:62
D4Group(const string &name)
Definition: D4Group.cc:95
Definition: crc.h:76
BaseType * find_first_var_that_uses_enumeration(D4EnumDef *enum_def)
Definition: D4Group.cc:207
virtual void serialize(D4StreamMarshaller &m, DMR &dmr, bool filter=false)
Serialize a Group.
Definition: D4Group.cc:466
Array * find_map_source(const string &path)
Definition: D4Group.cc:272
Type type() const
Returns the type of the class instance.
Definition: BaseType.cc:306
D4EnumDef * find_enum_def(const string &name)
Definition: D4EnumDefs.cc:77
virtual D4Dimension * dimension_D4dim(Dim_iter i)
Definition: Array.cc:662
A class for software fault reporting.
Definition: InternalErr.h:64
Dim_iter dim_end()
Definition: Array.cc:511
BaseType * find_first_var_that_uses_dimension(D4Dimension *dim)
Definition: D4Group.cc:176
#define DBG(x)
Definition: debug.h:58
string type_name() const
Returns the type of the class instance as a string.
Definition: BaseType.cc:320
Holds a DAP4 enumeration.
Definition: D4Enum.h:55
Marshaller that knows how to marshal/serialize dap data objects to a C++ iostream using DAP4's receiv...
long request_size(bool constrained)
Definition: D4Group.cc:377
vector< D4Group * >::iterator groupsIter
Definition: D4Group.h:67
D4EnumDef * find_enum_def(const string &path)
Definition: D4Group.cc:310
std::vector< dimension >::iterator Dim_iter
Definition: Array.h:214
virtual BaseType * get_parent() const
Definition: BaseType.cc:657
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:529
virtual void deserialize(D4StreamUnMarshaller &um, DMR &dmr)
Definition: D4Group.cc:504
string name() const
Returns the name of the class instance.
Definition: BaseType.cc:261
std::vector< BaseType * >::const_iterator Vars_citer
Definition: Constructor.h:61
void add_group_nocopy(D4Group *g)
Definition: D4Group.h:118
groupsIter grp_begin()
Get an iterator to the start of the values.
Definition: D4Group.h:109
void print_dap4(XMLWriter &xml, bool constrained=false) const
Definition: D4EnumDefs.cc:126
D4Group & operator=(const D4Group &rhs)
Definition: D4Group.cc:134
virtual ~D4Group()
Definition: D4Group.cc:117
virtual void intern_data(Crc32 &checksum)
Read data into this variable.
Definition: D4Group.cc:423
The basic data type for the DODS DAP types.
Definition: BaseType.h:117
Dim_iter dim_begin()
Definition: Array.cc:504
Vars_iter var_begin()
Definition: Constructor.cc:331
void print_dap4(XMLWriter &xml, bool constrained=false)
Definition: D4Group.cc:524
Vars_iter var_end()
Definition: Constructor.cc:339
virtual D4EnumDef * enumeration() const
Definition: D4Enum.h:164
virtual D4Group * ptr_duplicate()
Definition: D4Group.cc:128
void add_value(const string &value)
Definition: D4Attributes.h:77
virtual void put_checksum()
Write the checksum Write the checksum for the data sent since the last call to reset_checksum() to th...
vector< D4Group * >::const_iterator groupsCIter
Definition: D4Group.h:68
virtual std::string FQN() const
Definition: D4Group.cc:153
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
void Reset()
Definition: crc.h:83
A multidimensional array of identical data types.
Definition: Array.h:112
virtual bool send_p()
Should this variable be sent?
Definition: BaseType.cc:484
D4Dimension * find_dim(const string &name)
virtual void set_read_p(bool state)
Sets the value of the read_p property.
Definition: D4Group.cc:403
virtual void set_send_p(bool state)
Definition: D4Group.cc:413
void print_dap4(XMLWriter &xml) const
BaseType * find_var(const string &name)
Definition: D4Group.cc:344
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