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 //#define DODS_DEBUG
28
29 #include <cassert>
30
31 #include <iostream>
32 #include <sstream>
33 #include <iomanip>
34
35 #include <stdint.h>
36
37 #include "crc.h"
38
39 #include "BaseType.h"
40 #include "Array.h"
41
42 #include "XMLWriter.h"
43 #include "D4Attributes.h"
44 #include "D4Dimensions.h"
45 #include "D4Group.h"
46 #include "D4Enum.h"
47
48 #include "D4StreamMarshaller.h"
49 #include "D4StreamUnMarshaller.h"
50
51 #include "debug.h"
52
53 /**
54 * Define this symbol iff we decide to include information about the
55 * byte order of the response (as sent from the server) so that the
56 * client can determine the correct CRC32 hash code. jhrg 1/4/16
57 */
58 #undef INCLUDE_SOURCE_BYTE_ORDER
59
60 namespace libdap {
61
m_duplicate(const D4Group & g)62 void D4Group::m_duplicate(const D4Group &g)
63 {
64 DBG(cerr << "In D4Group::m_duplicate for " << g.name() << endl);
65
66 // dims; deep copy, this is the parent
67 if (g.d_dims) {
68 d_dims = new D4Dimensions(*(g.d_dims));
69 d_dims->set_parent(this);
70
71 // Update all of the D4Dimension weak pointers in the Array objects.
72 // This is a hack - we know that Constructor::m_duplicate() has been
73 // called at this point and any Array instances have dimension pointers
74 // that reference the 'old' dimensions (g.d_dims) and not the 'new'
75 // dimensions made above. Scan every array and re-wire the weak pointers.
76 // jhrg 8/15/14
77 Vars_citer vi = d_vars.begin();
78 while (vi != d_vars.end()) {
79 if ((*vi)->type() == dods_array_c)
80 static_cast<Array*>(*vi)->update_dimension_pointers(g.d_dims, d_dims);
81 ++vi;
82 }
83 }
84
85 #if 0
86 // Moved this block up inside the if because g.d_dims might be false. jhrg 9/14/15
87 Vars_citer vi = d_vars.begin();
88 while (vi != d_vars.end()) {
89 if ((*vi)->type() == dods_array_c)
90 static_cast<Array*>(*vi)->update_dimension_pointers(g.d_dims, d_dims);
91 ++vi;
92 }
93 #endif
94
95 // enums; deep copy
96 if (g.d_enum_defs) d_enum_defs = new D4EnumDefs(*g.d_enum_defs);
97
98 // groups
99 groupsCIter i = g.d_groups.begin();
100 while(i != g.d_groups.end()) {
101 // Only D4Groups are in the d_groups container.
102 D4Group *g = static_cast<D4Group*>((*i++)->ptr_duplicate());
103 add_group_nocopy(g);
104 }
105
106 DBG(cerr << "Exiting D4Group::m_duplicate" << endl);
107 }
108
109 /** The D4Group constructor requires only the name of the variable
110 to be created. The name may be omitted, which will create a
111 nameless variable. This may be adequate for some applications.
112
113 @note This type is available in DAP4 only.
114 See http://docs.opendap.org/index.php/DAP4:_Specification_Volume_1#Groups
115
116
117 @param n A string containing the name of the variable.
118 */
D4Group(const string & name)119 D4Group::D4Group(const string &name)
120 : Constructor(name, dods_group_c, /*is_dap4*/true), d_dims(0), d_enum_defs(0)
121 {}
122
123 /** The D4Group server-side constructor requires the name of the variable
124 to be created and the dataset name from which this variable is being
125 created. Used on server-side handlers.
126
127 @note This type is available in DAP4 only.
128 See http://docs.opendap.org/index.php/DAP4:_Specification_Volume_1#Groups
129
130 @param n A string containing the name of the variable.
131 @param d A string containing the name of the dataset.
132 */
D4Group(const string & name,const string & dataset)133 D4Group::D4Group(const string &name, const string &dataset)
134 : Constructor(name, dataset, dods_group_c, /*is_dap4*/true), d_dims(0), d_enum_defs(0)
135 {}
136
137 /** The D4Group copy constructor. */
D4Group(const D4Group & rhs)138 D4Group::D4Group(const D4Group &rhs) : Constructor(rhs), d_dims(0), d_enum_defs(0)
139 {
140 DBG(cerr << "In D4Group::copy_ctor for " << rhs.name() << endl);
141 m_duplicate(rhs);
142 }
143
~D4Group()144 D4Group::~D4Group()
145 {
146 delete d_dims;
147 delete d_enum_defs;
148
149 groupsIter i = d_groups.begin();
150 while(i != d_groups.end())
151 delete *i++;
152 }
153
154 #if 0
155 D4Group *
156
157 // I think this was a mistake. jhrg 11/17/16
158 #endif
159 BaseType *
ptr_duplicate()160 D4Group::ptr_duplicate()
161 {
162 return new D4Group(*this);
163 }
164
165 D4Group &
operator =(const D4Group & rhs)166 D4Group::operator=(const D4Group &rhs)
167 {
168 if (this == &rhs)
169 return *this;
170
171 dynamic_cast<Constructor &>(*this) = rhs; // run Constructor=
172
173 m_duplicate(rhs);
174
175 return *this;
176 }
177
178 /**
179 * Get the Fully Qualified Name for this Group, including the Group. This
180 * uses the name representation described in the DAP4 specification.
181 *
182 * @return The FQN in a string
183 */
184 string
FQN() const185 D4Group::FQN() const
186 {
187 // The root group is named "/" (always)
188 return (name() == "/") ? "/" : static_cast<D4Group*>(get_parent())->FQN() + name() + "/";
189 }
190
191 // Note that in order for this to work the second argument must not be a reference.
192 // jhrg 8/20/13
193 static bool
name_eq(D4Group * g,const string name)194 name_eq(D4Group *g, const string name)
195 {
196 return g->name() == name;
197 }
198
199 D4Group *
find_child_grp(const string & grp_name)200 D4Group::find_child_grp(const string &grp_name)
201 {
202 groupsIter g = find_if(grp_begin(), grp_end(), bind2nd(ptr_fun(name_eq), grp_name));
203 return (g == grp_end()) ? 0: *g;
204 }
205
206 // TODO Add constraint param? jhrg 11/17/13
207 BaseType *
find_first_var_that_uses_dimension(D4Dimension * dim)208 D4Group::find_first_var_that_uses_dimension(D4Dimension *dim)
209 {
210 // for each group, starting with the root group
211 // for each variable in the group that is marked to send and is an array
212 // return the btp if it uses the D4Dimension
213 // if it contains child groups, search those
214 // return the btp if it uses the D4Dimension
215 // return null
216
217 // exhaustive breadth-first search for 'dim
218
219 // root group
220 for (Vars_iter i = var_begin(), e = var_end(); i != e; ++i) {
221 if ((*i)->send_p() && (*i)->type() == dods_array_c) {
222 Array *a = static_cast<Array*>(*i);
223 for (Array::Dim_iter di = a->dim_begin(), de = a->dim_end(); di != de; ++di) {
224 if (a->dimension_D4dim(di) == dim)
225 return a;
226 }
227 }
228 }
229
230 for (groupsIter i = grp_begin(), e = grp_end(); i != e; ++i) {
231 BaseType *btp = (*i)->find_first_var_that_uses_dimension(dim);
232 if (btp) return btp;
233 }
234
235 return 0;
236 }
237
238 BaseType *
find_first_var_that_uses_enumeration(D4EnumDef * enum_def)239 D4Group::find_first_var_that_uses_enumeration(D4EnumDef *enum_def)
240 {
241 // for each group, starting with the root group
242 // for each variable in the group that is marked to send and is an array
243 // return the btp if it uses the D4EnumDef
244 // if it contains child groups, search those
245 // return the btp if it uses the D4EnumDef
246 // return null
247
248 // exhaustive breadth-first search for 'dim
249
250 // root group
251 for (Vars_iter i = var_begin(), e = var_end(); i != e; ++i) {
252 if ((*i)->send_p() && (*i)->type() == dods_enum_c) {
253 D4Enum *e = static_cast<D4Enum*>(*i);
254 if (e->enumeration() == enum_def)
255 return e;
256 }
257 }
258
259 for (groupsIter i = grp_begin(), e = grp_end(); i != e; ++i) {
260 BaseType *btp = (*i)->find_first_var_that_uses_enumeration(enum_def);
261 if (btp) return btp;
262 }
263
264 return 0;
265 }
266
267 /**
268 * @brief Find the dimension using a path.
269 * Using the DAP4 name syntax, lookup a dimension. The dimension must
270 * be defined before it is used. The \c path argument may be either an
271 * absolute path or a relative path. Note that the name syntax does not
272 * provide for paths to contain an 'up one level' symbol.
273 * @param path The path to the dimension
274 * @return A pointer to the D4Dimension object.
275 */
276 D4Dimension *
find_dim(const string & path)277 D4Group::find_dim(const string &path)
278 {
279 string lpath = path; // get a mutable copy
280
281 // special-case for the root group
282 if (lpath[0] == '/') {
283 if (name() != "/")
284 throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
285 else
286 lpath = lpath.substr(1);
287 }
288
289 string::size_type pos = lpath.find('/');
290 if (pos == string::npos) {
291 // name looks like 'bar'
292 return dims()->find_dim(lpath);
293 }
294
295 // name looks like foo/bar/baz where foo and bar must be groups
296 string grp_name = lpath.substr(0, pos);
297 lpath = lpath.substr(pos + 1);
298
299 D4Group *grp = find_child_grp(grp_name);
300 return (grp == 0) ? 0: grp->find_dim(lpath);
301 }
302
303 Array *
find_map_source(const string & path)304 D4Group::find_map_source(const string &path)
305 {
306 BaseType *map_source = m_find_map_source_helper(path);
307
308 // TODO more complete semantic checking jhrg 10/16/13
309 if (map_source && map_source->type() == dods_array_c) return static_cast<Array*>(map_source);
310
311 return 0;
312 }
313
314 BaseType *
m_find_map_source_helper(const string & path)315 D4Group::m_find_map_source_helper(const string &path)
316 {
317 string lpath = path; // get a mutable copy
318
319 // special-case for the root group
320 if (lpath[0] == '/') {
321 if (name() != "/")
322 throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
323 else
324 lpath = lpath.substr(1);
325 }
326
327 string::size_type pos = lpath.find('/');
328 if (pos == string::npos) {
329 // name looks like 'bar'
330 return var(lpath);
331 }
332
333 // name looks like foo/bar/baz where foo an bar must be groups
334 string grp_name = lpath.substr(0, pos);
335 lpath = lpath.substr(pos + 1);
336
337 D4Group *grp = find_child_grp(grp_name);
338 return (grp == 0) ? 0: grp->var(lpath);
339 }
340
341 D4EnumDef *
find_enum_def(const string & path)342 D4Group::find_enum_def(const string &path)
343 {
344 string lpath = path; // get a mutable copy
345
346 // special-case for the root group
347 if (lpath[0] == '/') {
348 if (name() != "/")
349 throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
350 else
351 lpath = lpath.substr(1);
352 }
353
354 string::size_type pos = lpath.find('/');
355 if (pos == string::npos) {
356 // name looks like 'bar'
357 return enum_defs()->find_enum_def(lpath);
358 }
359
360 // name looks like foo/bar/baz where foo and bar must be groups
361 string grp_name = lpath.substr(0, pos);
362 lpath = lpath.substr(pos + 1);
363
364 D4Group *grp = find_child_grp(grp_name);
365 return (grp == 0) ? 0: grp->enum_defs()->find_enum_def(lpath);
366 }
367
368 /**
369 * Find a variable using it's FUlly Qualified Name (FQN). The leading '/' is optional.
370 *
371 * @param path The FQN to the variable
372 * @return A BaseType* to the variable of null if it was not found
373 * @see BaseType::FQN()
374 */
375 BaseType *
find_var(const string & path)376 D4Group::find_var(const string &path)
377 {
378 string lpath = path; // get a mutable copy
379
380 // special-case for the root group
381 if (lpath[0] == '/') {
382 if (name() != "/")
383 throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
384 else
385 lpath = lpath.substr(1);
386 }
387
388 string::size_type pos = lpath.find('/');
389 if (pos == string::npos) {
390 // name looks like 'bar' or bar.baz; lookup in the Constructor that's part of the Group
391 return var(lpath);
392 }
393
394 // name looks like foo/bar/baz where foo and bar must be groups
395 string grp_name = lpath.substr(0, pos);
396 lpath = lpath.substr(pos + 1);
397
398 D4Group *grp = find_child_grp(grp_name);
399 return (grp == 0) ? 0 : grp->find_var(lpath);
400 }
401
402 /** Compute the size of all of the variables in this group and it's children,
403 * in kilobytes
404 *
405 * @param constrained Should the current constraint be taken into account?
406 * @return The size in kilobytes
407 */
408 long
request_size(bool constrained)409 D4Group::request_size(bool constrained)
410 {
411 long long size = 0;
412 // variables
413 Constructor::Vars_iter v = var_begin();
414 while (v != var_end()) {
415 if (constrained) {
416 if ((*v)->send_p())
417 size += (*v)->width(constrained);
418 }
419 else {
420 size += (*v)->width(constrained);
421 }
422
423 ++v;
424 }
425
426 // groups
427 groupsIter g = d_groups.begin();
428 while (g != d_groups.end())
429 size += (*g++)->request_size(constrained);
430
431 return size / 1024;
432 }
433
434 /**
435 * @brief Get the estimated size of a response in kilobytes.
436 * This method looks at the variables in the DDS and computes
437 * the number of bytes in the response.
438 *
439 * @note This version of the method does a poor job with Sequences. A better
440 * implementation would look at row-constraint-based limitations and use them
441 * for size computations. If a row-constraint is missing, return an error.
442 *
443 * @param constrained Should the size of the whole DDS be used or should the
444 * current constraint be taken into account?
445 */
request_size_kb(bool constrained)446 uint64_t D4Group::request_size_kb(bool constrained)
447 {
448 uint64_t size = 0;
449 // variables
450 Constructor::Vars_iter v = var_begin();
451 while (v != var_end()) {
452 if (constrained) {
453 if ((*v)->send_p())
454 size += (*v)->width(constrained);
455 }
456 else {
457 size += (*v)->width(constrained);
458 }
459 ++v;
460 }
461 // groups
462 groupsIter g = d_groups.begin();
463 while (g != d_groups.end())
464 size += (*g++)->request_size(constrained);
465
466 return size / 1024;
467 }
468
469
470 void
set_read_p(bool state)471 D4Group::set_read_p(bool state)
472 {
473 groupsIter g = d_groups.begin();
474 while (g != d_groups.end())
475 (*g++)->set_read_p(state);
476
477 Constructor::set_read_p(state);
478 }
479
480 void
set_send_p(bool state)481 D4Group::set_send_p(bool state)
482 {
483 groupsIter g = d_groups.begin();
484 while (g != d_groups.end())
485 (*g++)->set_send_p(state);
486
487 Constructor::set_send_p(state);
488 }
489
490 void
intern_data()491 D4Group::intern_data(/*Crc32 &checksum, DMR &dmr, ConstraintEvaluator &eval*/)
492 {
493 groupsIter g = d_groups.begin();
494 while (g != d_groups.end())
495 (*g++)->intern_data(/*checksum, dmr, eval*/);
496
497 // Specialize how the top-level variables in any Group are sent; include
498 // a checksum for them. A subset operation might make an interior set of
499 // variables, but the parent structure will still be present and the checksum
500 // will be computed for that structure. In other words, DAP4 does not try
501 // to sort out which variables are the 'real' top-level variables and instead
502 // simply computes the CRC for whatever appears as a variable in the root
503 // group.
504 for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) {
505 // Only send the stuff in the current subset.
506 if ((*i)->send_p()) {
507 #if 0
508 checksum.Reset();
509 #endif
510 (*i)->intern_data(/*checksum, dmr, eval*/);
511 #if 0
512 D4Attribute *a = new D4Attribute("DAP4_Checksum_CRC32", attr_str_c);
513
514 ostringstream oss;
515 oss.setf(ios::hex, ios::basefield);
516 oss << setfill('0') << setw(8) << checksum.GetCrc32();
517 a->add_value(oss.str());
518 #if INCLUDE_SOURCE_BYTE_ORDER
519 if (um.is_source_big_endian())
520 a->add_value("source:big-endian");
521 else
522 a->add_value("source:little-endian");
523 #endif
524 (*i)->attributes()->add_attribute_nocopy(a);
525 DBG(cerr << "CRC32: " << oss.str() << " for " << (*i)->name() << endl);
526 #endif
527 }
528 }
529 }
530
531 /**
532 * @brief Serialize a Group
533 * @param m The DAP4 Stream Marshaller. This object serializes the data values and
534 * writes checksums (using CRC32) for the top level variables in every Group for which
535 * one or more variables are sent. The DAP4 Marshaller object can be made so that only
536 * the checksums are written.
537 * @param dmr Unused
538 * @param eval Unused
539 * @param filter Unused
540 * @exception Error is thrown if the value needs to be read and that operation fails.
541 */
542 void
serialize(D4StreamMarshaller & m,DMR & dmr,bool filter)543 D4Group::serialize(D4StreamMarshaller &m, DMR &dmr, /*ConstraintEvaluator &eval,*/ bool filter)
544 {
545 #if 0
546 // This will call Constructor read which will, for everything but a Sequence,
547 // read all of the data in one shot. However, the serialize() methods for the
548 // Arrays, Structures, etc., also have read() calls in them and those can be
549 // used to control how long the data are in memory, e.g., limiting the lifetime
550 // of a large array and avoiding having overlapping arrays when they are not
551 // needed. For a sequence read() has different semantics. It is called once
552 // for every instance and the read_p flag is not used.
553 if (!read_p())
554 read(); // read() throws Error
555 #endif
556
557 groupsIter g = d_groups.begin();
558 while (g != d_groups.end())
559 (*g++)->serialize(m, dmr, filter);
560
561 // Specialize how the top-level variables in any Group are sent; include
562 // a checksum for them. A subset operation might make an interior set of
563 // variables, but the parent structure will still be present and the checksum
564 // will be computed for that structure. In other words, DAP4 does not try
565 // to sort out which variables are the 'real' top-level variables and instead
566 // simply computes the CRC for whatever appears as a variable in the root
567 // group.
568 for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) {
569 // Only send the stuff in the current subset.
570 if ((*i)->send_p()) {
571 m.reset_checksum();
572
573 DBG(cerr << "Serializing variable " << (*i)->type_name() << " " << (*i)->name() << endl);
574 (*i)->serialize(m, dmr, filter);
575
576 DBG(cerr << "Wrote CRC32: " << m.get_checksum() << " for " << (*i)->name() << endl);
577 m.put_checksum();
578 }
579 }
580 }
581
deserialize(D4StreamUnMarshaller & um,DMR & dmr)582 void D4Group::deserialize(D4StreamUnMarshaller &um, DMR &dmr)
583 {
584 groupsIter g = d_groups.begin();
585 while (g != d_groups.end()) {
586 DBG(cerr << "Deserializing group " << (*g)->name() << endl);
587 (*g++)->deserialize(um, dmr);
588 }
589 // Specialize how the top-level variables in any Group are received; read
590 // their checksum and store the value in a magic attribute of the variable
591 for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) {
592 DBG(cerr << "Deserializing variable " << (*i)->type_name() << " " << (*i)->name() << endl);
593 (*i)->deserialize(um, dmr);
594
595 D4Attribute *a = new D4Attribute("DAP4_Checksum_CRC32", attr_str_c);
596 string crc = um.get_checksum_str();
597 a->add_value(crc);
598 #if INCLUDE_SOURCE_BYTE_ORDER
599 if (um.is_source_big_endian())
600 a->add_value("source:big-endian");
601 else
602 a->add_value("source:little-endian");
603 #endif
604 DBG(cerr << "Read CRC32: " << crc << " for " << (*i)->name() << endl);
605 (*i)->attributes()->add_attribute_nocopy(a);
606 }
607 }
608
609 void
print_dap4(XMLWriter & xml,bool constrained)610 D4Group::print_dap4(XMLWriter &xml, bool constrained)
611 {
612 if (!name().empty() && name() != "/") {
613 // For named groups, if constrained is true only print if this group
614 // has variables that are marked for transmission. For the root group
615 // this test is not made.
616 if (constrained && !send_p())
617 return;
618
619 if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) type_name().c_str()) < 0)
620 throw InternalErr(__FILE__, __LINE__, "Could not write " + type_name() + " element");
621
622 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name().c_str()) < 0)
623 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
624 }
625
626 // dims
627 if (!dims()->empty())
628 dims()->print_dap4(xml, constrained);
629
630 // enums
631 if (!enum_defs()->empty())
632 enum_defs()->print_dap4(xml, constrained);
633
634 // variables
635 Constructor::Vars_iter v = var_begin();
636 while (v != var_end())
637 (*v++)->print_dap4(xml, constrained);
638
639 // attributes
640 attributes()->print_dap4(xml);
641
642 // groups
643 groupsIter g = d_groups.begin();
644 while (g != d_groups.end())
645 (*g++)->print_dap4(xml, constrained);
646
647 if (!name().empty() && name() != "/") {
648 if (xmlTextWriterEndElement(xml.get_writer()) < 0)
649 throw InternalErr(__FILE__, __LINE__, "Could not end " + type_name() + " element");
650 }
651 }
652 #if 0
653 /** @brief DAP4 to DAP2 transform
654 *
655 * D4Group objects, with the exception of the root group, "disappear"
656 * into the names of their member variables. Specifically the Group
657 * name is add as a prefix followed by a "/" separator to the names
658 * of all of the Group's member groups variables. The Group attributes
659 * (metadata) are transfered to the parent_attr_table. The Group
660 * members are collected returned in vector.
661 *
662 * @param The AttrTable pointer parent_attr_table is used by Groups, which disappear
663 * from the DAP2 representation. Their children are returned in the the BaseType vector
664 * their attributes are added to parent_attr_table;
665 * @return A pointer to a vector of BaseType pointers (right?). In this D4Group case the
666 * vector will contain DAP2 versions of all of the member variables of the D4Group instance.
667 * (ex: UInt64) the will return a NULL pointer and so this must be tested!
668 */
669 vector<BaseType *> *
670 D4Group::transform_to_dap2(AttrTable *parent_attr_table)
671 {
672 return transform_to_dap2(parent_attr_table, false);
673 }
674 #endif
675 /** @brief Transform the D4Group's variables to DAP2 variables
676 *
677 * For all of the variables in a D4Group, build a vector of DAP2 variables
678 * that can be directly added to DDS object. Extract the DAP4 variables'
679 * attributes and transfer them the the AttrTable object passed as the first
680 * argument.
681 *
682 * For all variables in the D4Group that are members of child groups (i.e,
683 * not the root group), the name of the group (or names of the groups) will
684 * be prepended to the name of the variable. Group names are separated using
685 * a '/' character.
686 *
687 * The Group attributes are transfered to the parent_attr_table.
688 *
689 * @todo Fix the comment.
690 *
691 * @param parent_attr_table The AttrTable pointer parent_attr_table is used by Groups, which disappear
692 * from the DAP2 representation. Their children are returned in the the BaseType vector
693 * their attributes are added to parent_attr_table;
694 * @return A pointer to a vector of BaseType pointers (right?). In this D4Group case the
695 * vector will contain DAP2 versions of all of the member variables of the D4Group instance.
696 * (ex: UInt64) the will return a NULL pointer and so this must be tested!
697 */
698 vector<BaseType *> *
transform_to_dap2(AttrTable * parent_attr_table)699 D4Group::transform_to_dap2(AttrTable *parent_attr_table)
700 {
701 DBG( cerr << __func__ << "() - BEGIN ("<< name() << ")" << endl);
702
703 vector<BaseType *> *results = new vector<BaseType *>(); // LEAK
704
705 // Get the D4Group's attributes
706 #if 0
707 AttrTable *group_attrs = attributes()->get_AttrTable(name());
708 #else
709 AttrTable *group_attrs = new AttrTable();
710 attributes()->transform_attrs_to_dap2(group_attrs);
711 group_attrs->set_name(name());
712 #endif
713
714 // If this is the root group then copy all of its attributes into the parent_attr_table.
715 // The group_attrs AttrTable* above will be replaced by the parent_attr_table.
716 bool is_root = (name() == "/");
717
718 if (is_root) {
719 assert(name() == "/");
720 for (AttrTable::Attr_iter i = group_attrs->attr_begin(), e = group_attrs->attr_end(); i != e; ++i) {
721 if ((*i)->type == Attr_container) {
722 // copy the source container so that the DAS passed in can be
723 // deleted after calling this method.
724 AttrTable *at = new AttrTable(*(*i)->attributes);
725 parent_attr_table->append_container(at, at->get_name());
726 }
727 else {
728 parent_attr_table->append_attr((*i)->name, AttrType_to_String((*i)->type), (*i)->attr);
729 }
730 }
731 delete group_attrs;
732 group_attrs = parent_attr_table;
733 }
734
735 // Now we process the child variables of this group
736
737 vector<BaseType *> dropped_vars;
738 for (D4Group::Vars_citer i = var_begin(), e = var_end(); i != e; ++i) {
739
740 DBG( cerr << __func__ << "() - Processing member variable '" << (*i)->name() <<
741 "' root: " << (is_root?"true":"false") << endl);
742
743 vector<BaseType *> *new_vars = (*i)->transform_to_dap2(group_attrs);
744 if (new_vars) { // Might be un-mappable
745 // It's not so game on..
746 for (vector<BaseType*>::iterator vi = new_vars->begin(), ve = new_vars->end(); vi != ve; vi++) {
747 string new_name = (is_root ? "" : FQN()) + (*vi)->name();
748 (*vi)->set_name(new_name);
749 (*vi)->set_parent(NULL);
750 results->push_back((*vi));
751 #if 0
752 (*vi) = NULL;
753 #endif
754 DBG( cerr << __func__ << "() - Added member variable '" << (*i)->name() << "' " <<
755 "to results vector. root: "<< (is_root?"true":"false") << endl);
756 }
757
758 delete new_vars;
759 }
760 else {
761 DBG( cerr << __func__ << "() - Dropping member variable " << (*i)->name() <<
762 " root: " << (is_root?"true":"false") << endl);
763 // Got back a NULL, so we are dropping this var.
764 dropped_vars.push_back(*i);
765 }
766 }
767
768 // Process dropped DAP4 vars
769 DBG( cerr << __func__ << "() - Processing " << dropped_vars.size() << " Dropped Variable(s)" << endl);
770
771 AttrTable *dv_attr_table = make_dropped_vars_attr_table(&dropped_vars);
772 if (dv_attr_table) {
773 group_attrs->append_container(dv_attr_table, dv_attr_table->get_name());
774 }
775
776 // Get all the child groups.
777 for (D4Group::groupsIter gi = grp_begin(), ge = grp_end(); gi != ge; ++gi) {
778 vector<BaseType *> *d2_vars = (*gi)->transform_to_dap2(group_attrs);
779 if (d2_vars) {
780 for (vector<BaseType *>::iterator i = d2_vars->begin(), e = d2_vars->end(); i != e; ++i) {
781 results->push_back(*i);
782 }
783 }
784 delete d2_vars;
785 }
786
787 if (!is_root) {
788 group_attrs->set_name(name());
789 parent_attr_table->append_container(group_attrs, group_attrs->get_name());
790 }
791
792 return results;
793 }
794
795
796 } /* namespace libdap */
797