libdap Updated for version 3.18.1
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
57namespace libdap {
58
59void 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
115D4Group::D4Group(const string &name)
116 : Constructor(name, dods_group_c, /*is_dap4*/true), d_dims(0), d_enum_defs(0)
117{}
118
129D4Group::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
134D4Group::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
140D4Group::~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
150D4Group *
152{
153 return new D4Group(*this);
154}
155
156D4Group &
157D4Group::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
175string
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
184static bool
185name_eq(D4Group *g, const string name)
186{
187 return g->name() == name;
188}
189
190D4Group *
191D4Group::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
198BaseType *
199D4Group::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
229BaseType *
230D4Group::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
267D4Dimension *
268D4Group::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
294Array *
295D4Group::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
305BaseType *
306D4Group::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
332D4EnumDef *
333D4Group::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
366BaseType *
367D4Group::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
399long
400D4Group::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
425void
427{
428 groupsIter g = d_groups.begin();
429 while (g != d_groups.end())
430 (*g++)->set_read_p(state);
431
433}
434
435void
437{
438 groupsIter g = d_groups.begin();
439 while (g != d_groups.end())
440 (*g++)->set_send_p(state);
441
443}
444
445void
446D4Group::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
497void
498D4Group::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
564void
565D4Group::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 */
A multidimensional array of identical data types.
Definition: Array.h:113
std::vector< dimension >::iterator Dim_iter
Definition: Array.h:204
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:324
virtual string name() const
Returns the name of the class instance.
Definition: BaseType.cc:265
virtual BaseType * get_parent() const
Definition: BaseType.cc:672
virtual bool read_p()
Has this variable been read?
Definition: BaseType.cc:425
virtual D4Attributes * attributes()
Definition: BaseType.cc:544
virtual bool send_p()
Should this variable be sent?
Definition: BaseType.cc:499
BaseType(const string &n, const Type &t, bool is_dap4=false)
The BaseType constructor.
Definition: BaseType.cc:125
virtual Type type() const
Returns the type of the class instance.
Definition: BaseType.cc:310
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
Vars_iter var_end()
Definition: Constructor.cc:339
virtual void set_send_p(bool state)
Definition: Constructor.cc:183
virtual bool read()
simple implementation of read that iterates through vars and calls read on them
Definition: Constructor.cc:451
Vars_iter var_begin()
Definition: Constructor.cc:331
virtual void set_read_p(bool state)
Sets the value of the read_p property.
Definition: Constructor.cc:193
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
virtual void intern_data()
Read data into this variable.
Definition: D4Group.cc:446
virtual void set_read_p(bool state)
Sets the value of the read_p property.
Definition: D4Group.cc:426
BaseType * find_var(const string &name)
Definition: D4Group.cc:367
virtual std::string FQN() const
Definition: D4Group.cc:176
groupsIter grp_begin()
Get an iterator to the start of the values.
Definition: D4Group.h:109
virtual void deserialize(D4StreamUnMarshaller &um, DMR &dmr)
Definition: D4Group.cc:537
virtual void set_send_p(bool state)
Definition: D4Group.cc:436
groupsIter grp_end()
Get an iterator to the end of the values.
Definition: D4Group.h:112
D4Dimensions * dims()
Get the dimensions defined for this Group.
Definition: D4Group.h:80
D4EnumDefs * enum_defs()
Get the enumerations defined for this Group.
Definition: D4Group.h:95
long request_size(bool constrained)
Definition: D4Group.cc:400
D4Group(const string &name)
Definition: D4Group.cc:115
void print_dap4(XMLWriter &xml, bool constrained=false)
Definition: D4Group.cc:565
virtual void serialize(D4StreamMarshaller &m, DMR &dmr, bool filter=false)
Serialize a Group.
Definition: D4Group.cc:498
virtual D4Group * ptr_duplicate()
Definition: D4Group.cc:151
Marshaller that knows how to marshal/serialize dap data objects to a C++ iostream using DAP4's receiv...
virtual void put_checksum()
Write the checksum Write the checksum for the data sent since the last call to reset_checksum() to th...
Read data from the stream made by D4StreamMarshaller.
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...
A class for software fault reporting.
Definition: InternalErr.h:65