libdap  Updated for version 3.17.0
D4Group.cc
00001 // -*- mode: c++; c-basic-offset:4 -*-
00002 
00003 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
00004 // Access Protocol.
00005 
00006 // Copyright (c) 2013 OPeNDAP, Inc.
00007 // Author: James Gallagher <jgallagher@opendap.org>
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Lesser General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2.1 of the License, or (at your option) any later version.
00013 //
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 // Lesser General Public License for more details.
00018 //
00019 // You should have received a copy of the GNU Lesser General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00022 //
00023 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
00024 
00025 #include "config.h"
00026 
00027 #include <iostream>
00028 #include <sstream>
00029 #include <iomanip>
00030 
00031 #include <stdint.h>
00032 
00033 //#define DODS_DEBUG
00034 
00035 #include "crc.h"
00036 
00037 #include "BaseType.h"
00038 #include "Array.h"
00039 
00040 #include "XMLWriter.h"
00041 #include "D4Attributes.h"
00042 #include "D4Dimensions.h"
00043 #include "D4Group.h"
00044 #include "D4Enum.h"
00045 
00046 #include "D4StreamMarshaller.h"
00047 #include "D4StreamUnMarshaller.h"
00048 
00049 #include "debug.h"
00050 
00056 #undef INCLUDE_SOURCE_BYTE_ORDER
00057 
00058 namespace libdap {
00059 
00060 void D4Group::m_duplicate(const D4Group &g)
00061 {
00062         DBG(cerr << "In D4Group::m_duplicate for " << g.name() << endl);
00063 
00064         // dims; deep copy, this is the parent
00065         if (g.d_dims) {
00066                 d_dims = new D4Dimensions(*(g.d_dims));
00067                 d_dims->set_parent(this);
00068 
00069             // Update all of the D4Dimension weak pointers in the Array objects.
00070             // This is a hack - we know that Constructor::m_duplicate() has been
00071             // called at this point and any Array instances have dimension pointers
00072             // that reference the 'old' dimensions (g.d_dims) and not the 'new'
00073             // dimensions made above. Scan every array and re-wire the weak pointers.
00074             // jhrg 8/15/14
00075             Vars_citer vi = d_vars.begin();
00076             while (vi != d_vars.end()) {
00077                 if ((*vi)->type() == dods_array_c)
00078                     static_cast<Array*>(*vi)->update_dimension_pointers(g.d_dims, d_dims);
00079                 ++vi;
00080             }
00081         }
00082 
00083 #if 0
00084         // Moved this block up inside the if because g.d_dims might be false. jhrg 9/14/15
00085         Vars_citer vi = d_vars.begin();
00086         while (vi != d_vars.end()) {
00087                 if ((*vi)->type() == dods_array_c)
00088                         static_cast<Array*>(*vi)->update_dimension_pointers(g.d_dims, d_dims);
00089                 ++vi;
00090         }
00091 #endif
00092 
00093         // enums; deep copy
00094         if (g.d_enum_defs) d_enum_defs = new D4EnumDefs(*g.d_enum_defs);
00095 
00096     // groups
00097     groupsCIter i = g.d_groups.begin();
00098     while(i != g.d_groups.end()) {
00099         D4Group *g = (*i++)->ptr_duplicate();
00100         add_group_nocopy(g);
00101     }
00102 
00103     DBG(cerr << "Exiting D4Group::m_duplicate" << endl);
00104 }
00105 
00116 D4Group::D4Group(const string &name)
00117     : Constructor(name, dods_group_c, /*is_dap4*/true), d_dims(0), d_enum_defs(0)
00118 {}
00119 
00130 D4Group::D4Group(const string &name, const string &dataset)
00131     : Constructor(name, dataset, dods_group_c, /*is_dap4*/true), d_dims(0), d_enum_defs(0)
00132 {}
00133 
00135 D4Group::D4Group(const D4Group &rhs) : Constructor(rhs), d_dims(0), d_enum_defs(0)
00136 {
00137         DBG(cerr << "In D4Group::copy_ctor for " << rhs.name() << endl);
00138     m_duplicate(rhs);
00139 }
00140 
00141 D4Group::~D4Group()
00142 {
00143     delete d_dims;
00144     delete d_enum_defs;
00145 
00146     groupsIter i = d_groups.begin();
00147     while(i != d_groups.end())
00148         delete *i++;
00149 }
00150 
00151 D4Group *
00152 D4Group::ptr_duplicate()
00153 {
00154     return new D4Group(*this);
00155 }
00156 
00157 D4Group &
00158 D4Group::operator=(const D4Group &rhs)
00159 {
00160     if (this == &rhs)
00161         return *this;
00162 
00163     dynamic_cast<Constructor &>(*this) = rhs; // run Constructor=
00164 
00165     m_duplicate(rhs);
00166 
00167     return *this;
00168 }
00169 
00176 string
00177 D4Group::FQN() const
00178 {
00179         // The root group is named "/" (always)
00180         return (name() == "/") ? "/" : static_cast<D4Group*>(get_parent())->FQN() + name() + "/";
00181 }
00182 
00183 // Note that in order for this to work the second argument must not be a reference.
00184 // jhrg 8/20/13
00185 static bool
00186 name_eq(D4Group *g, const string name)
00187 {
00188         return g->name() == name;
00189 }
00190 
00191 D4Group *
00192 D4Group::find_child_grp(const string &grp_name)
00193 {
00194         groupsIter g = find_if(grp_begin(), grp_end(), bind2nd(ptr_fun(name_eq), grp_name));
00195         return (g == grp_end()) ? 0: *g;
00196 }
00197 
00198 // TODO Add constraint param? jhrg 11/17/13
00199 BaseType *
00200 D4Group::find_first_var_that_uses_dimension(D4Dimension *dim)
00201 {
00202     // for each group, starting with the root group
00203     //    for each variable in the group that is marked to send and is an array
00204     //        return the btp if it uses the D4Dimension
00205     //    if it contains child groups, search those
00206     //        return the btp if it uses the D4Dimension
00207     // return null
00208 
00209     // exhaustive breadth-first search for 'dim
00210 
00211     // root group
00212     for (Vars_iter i = var_begin(), e = var_end(); i != e; ++i) {
00213         if ((*i)->send_p() && (*i)->type() == dods_array_c) {
00214             Array *a = static_cast<Array*>(*i);
00215             for (Array::Dim_iter di = a->dim_begin(), de = a->dim_end(); di != de; ++di) {
00216                 if (a->dimension_D4dim(di) == dim)
00217                     return a;
00218             }
00219         }
00220     }
00221 
00222     for (groupsIter i = grp_begin(), e = grp_end(); i != e; ++i) {
00223         BaseType *btp = (*i)->find_first_var_that_uses_dimension(dim);
00224         if (btp) return btp;
00225     }
00226 
00227     return 0;
00228 }
00229 
00230 BaseType *
00231 D4Group::find_first_var_that_uses_enumeration(D4EnumDef *enum_def)
00232 {
00233     // for each group, starting with the root group
00234     //    for each variable in the group that is marked to send and is an array
00235     //        return the btp if it uses the D4EnumDef
00236     //    if it contains child groups, search those
00237     //        return the btp if it uses the D4EnumDef
00238     // return null
00239 
00240     // exhaustive breadth-first search for 'dim
00241 
00242     // root group
00243     for (Vars_iter i = var_begin(), e = var_end(); i != e; ++i) {
00244         if ((*i)->send_p() && (*i)->type() == dods_enum_c) {
00245             D4Enum *e = static_cast<D4Enum*>(*i);
00246             if (e->enumeration() == enum_def)
00247                 return e;
00248         }
00249     }
00250 
00251     for (groupsIter i = grp_begin(), e = grp_end(); i != e; ++i) {
00252         BaseType *btp = (*i)->find_first_var_that_uses_enumeration(enum_def);
00253         if (btp) return btp;
00254     }
00255 
00256     return 0;
00257 }
00258 
00268 D4Dimension *
00269 D4Group::find_dim(const string &path)
00270 {
00271         string lpath = path;            // get a mutable copy
00272 
00273         // special-case for the root group
00274         if (lpath[0] == '/') {
00275                 if (name() != "/")
00276                         throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
00277                 else
00278                         lpath = lpath.substr(1);
00279         }
00280 
00281         string::size_type pos = lpath.find('/');
00282         if (pos == string::npos) {
00283                 // name looks like 'bar'
00284                 return dims()->find_dim(lpath);
00285         }
00286 
00287         // name looks like foo/bar/baz where foo and bar must be groups
00288         string grp_name = lpath.substr(0, pos);
00289         lpath = lpath.substr(pos + 1);
00290 
00291         D4Group *grp = find_child_grp(grp_name);
00292         return (grp == 0) ? 0: grp->find_dim(lpath);
00293 }
00294 
00295 Array *
00296 D4Group::find_map_source(const string &path)
00297 {
00298         BaseType *map_source = m_find_map_source_helper(path);
00299 
00300         // TODO more complete semantic checking jhrg 10/16/13
00301         if (map_source && map_source->type() == dods_array_c) return static_cast<Array*>(map_source);
00302 
00303         return 0;
00304 }
00305 
00306 BaseType *
00307 D4Group::m_find_map_source_helper(const string &path)
00308 {
00309         string lpath = path;            // get a mutable copy
00310 
00311         // special-case for the root group
00312         if (lpath[0] == '/') {
00313                 if (name() != "/")
00314                         throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
00315                 else
00316                         lpath = lpath.substr(1);
00317         }
00318 
00319         string::size_type pos = lpath.find('/');
00320         if (pos == string::npos) {
00321                 // name looks like 'bar'
00322                 return var(lpath);
00323         }
00324 
00325         // name looks like foo/bar/baz where foo an bar must be groups
00326         string grp_name = lpath.substr(0, pos);
00327         lpath = lpath.substr(pos + 1);
00328 
00329         D4Group *grp = find_child_grp(grp_name);
00330         return (grp == 0) ? 0: grp->var(lpath);
00331 }
00332 
00333 D4EnumDef *
00334 D4Group::find_enum_def(const string &path)
00335 {
00336     string lpath = path;        // get a mutable copy
00337 
00338     // special-case for the root group
00339     if (lpath[0] == '/') {
00340         if (name() != "/")
00341             throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
00342         else
00343             lpath = lpath.substr(1);
00344     }
00345 
00346     string::size_type pos = lpath.find('/');
00347     if (pos == string::npos) {
00348         // name looks like 'bar'
00349         return enum_defs()->find_enum_def(lpath);
00350     }
00351 
00352     // name looks like foo/bar/baz where foo and bar must be groups
00353     string grp_name = lpath.substr(0, pos);
00354     lpath = lpath.substr(pos + 1);
00355 
00356     D4Group *grp = find_child_grp(grp_name);
00357     return (grp == 0) ? 0: grp->enum_defs()->find_enum_def(lpath);
00358 }
00359 
00367 BaseType *
00368 D4Group::find_var(const string &path)
00369 {
00370     string lpath = path;        // get a mutable copy
00371 
00372     // special-case for the root group
00373     if (lpath[0] == '/') {
00374         if (name() != "/")
00375             throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group.");
00376         else
00377             lpath = lpath.substr(1);
00378     }
00379 
00380     string::size_type pos = lpath.find('/');
00381     if (pos == string::npos) {
00382         // name looks like 'bar' or bar.baz; lookup in the Constructor that's part of the Group
00383         return var(lpath);
00384     }
00385 
00386     // name looks like foo/bar/baz where foo and bar must be groups
00387     string grp_name = lpath.substr(0, pos);
00388     lpath = lpath.substr(pos + 1);
00389 
00390     D4Group *grp = find_child_grp(grp_name);
00391     return (grp == 0) ? 0 : grp->find_var(lpath);
00392 }
00393 
00400 long
00401 D4Group::request_size(bool constrained)
00402 {
00403     long long size = 0;
00404     // variables
00405     Constructor::Vars_iter v = var_begin();
00406     while (v != var_end()) {
00407         if (constrained) {
00408             if ((*v)->send_p())
00409                 size += (*v)->width(constrained);
00410         }
00411         else {
00412             size += (*v)->width(constrained);
00413         }
00414 
00415         ++v;
00416     }
00417 
00418     // groups
00419     groupsIter g = d_groups.begin();
00420     while (g != d_groups.end())
00421         size += (*g++)->request_size(constrained);
00422 
00423     return size / 1024;
00424 }
00425 
00426 void
00427 D4Group::set_read_p(bool state)
00428 {
00429     groupsIter g = d_groups.begin();
00430     while (g != d_groups.end())
00431         (*g++)->set_read_p(state);
00432 
00433     Constructor::set_read_p(state);
00434 }
00435 
00436 void
00437 D4Group::set_send_p(bool state)
00438 {
00439     groupsIter g = d_groups.begin();
00440     while (g != d_groups.end())
00441         (*g++)->set_send_p(state);
00442 
00443     Constructor::set_send_p(state);
00444 }
00445 
00446 void
00447 D4Group::intern_data(/*Crc32 &checksum, DMR &dmr, ConstraintEvaluator &eval*/)
00448 {
00449     groupsIter g = d_groups.begin();
00450     while (g != d_groups.end())
00451         (*g++)->intern_data(/*checksum, dmr, eval*/);
00452 
00453     // Specialize how the top-level variables in any Group are sent; include
00454     // a checksum for them. A subset operation might make an interior set of
00455     // variables, but the parent structure will still be present and the checksum
00456     // will be computed for that structure. In other words, DAP4 does not try
00457     // to sort out which variables are the 'real' top-level variables and instead
00458     // simply computes the CRC for whatever appears as a variable in the root
00459     // group.
00460         for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) {
00461                 // Only send the stuff in the current subset.
00462                 if ((*i)->send_p()) {
00463 #if 0
00464                     checksum.Reset();
00465 #endif
00466                         (*i)->intern_data(/*checksum, dmr, eval*/);
00467 #if 0
00468                         D4Attribute *a = new D4Attribute("DAP4_Checksum_CRC32", attr_str_c);
00469 
00470                         ostringstream oss;
00471                     oss.setf(ios::hex, ios::basefield);
00472                     oss << setfill('0') << setw(8) << checksum.GetCrc32();
00473             a->add_value(oss.str());
00474 #if INCLUDE_SOURCE_BYTE_ORDER
00475                 if (um.is_source_big_endian())
00476                     a->add_value("source:big-endian");
00477                 else
00478                     a->add_value("source:little-endian");
00479 #endif
00480                 (*i)->attributes()->add_attribute_nocopy(a);
00481                         DBG(cerr << "CRC32: " << oss.str() << " for " << (*i)->name() << endl);
00482 #endif
00483                 }
00484         }
00485 }
00486 
00498 void
00499 D4Group::serialize(D4StreamMarshaller &m, DMR &dmr, /*ConstraintEvaluator &eval,*/ bool filter)
00500 {
00501 #if 0
00502     // This will call Constructor read which will, for everything but a Sequence,
00503     // read all of the data in one shot. However, the serialize() methods for the
00504     // Arrays, Structures, etc., also have read() calls in them and those can be
00505     // used to control how long the data are in memory, e.g., limiting the lifetime
00506     // of a large array and avoiding having overlapping arrays when they are not
00507     // needed. For a sequence read() has different semantics. It is called once
00508     // for every instance and the read_p flag is not used.
00509     if (!read_p())
00510         read();  // read() throws Error
00511 #endif
00512 
00513     groupsIter g = d_groups.begin();
00514     while (g != d_groups.end())
00515         (*g++)->serialize(m, dmr, /*eval,*/ filter);
00516 
00517     // Specialize how the top-level variables in any Group are sent; include
00518     // a checksum for them. A subset operation might make an interior set of
00519     // variables, but the parent structure will still be present and the checksum
00520     // will be computed for that structure. In other words, DAP4 does not try
00521     // to sort out which variables are the 'real' top-level variables and instead
00522     // simply computes the CRC for whatever appears as a variable in the root
00523     // group.
00524         for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) {
00525                 // Only send the stuff in the current subset.
00526                 if ((*i)->send_p()) {
00527                         m.reset_checksum();
00528 
00529                         (*i)->serialize(m, dmr, /*eval,*/ filter);
00530 
00531                         DBG(cerr << "Wrote CRC32: " << m.get_checksum() << " for " << (*i)->name() << endl);
00532                         m.put_checksum();
00533                 }
00534         }
00535 }
00536 
00537 void D4Group::deserialize(D4StreamUnMarshaller &um, DMR &dmr)
00538 {
00539         groupsIter g = d_groups.begin();
00540         while (g != d_groups.end())
00541                 (*g++)->deserialize(um, dmr);
00542 
00543         // Specialize how the top-level variables in any Group are received; read
00544         // their checksum and store the value in a magic attribute of the variable
00545         for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) {
00546                 (*i)->deserialize(um, dmr);
00547 
00548                 D4Attribute *a = new D4Attribute("DAP4_Checksum_CRC32", attr_str_c);
00549                 string crc = um.get_checksum_str();
00550                 a->add_value(crc);
00551 #if INCLUDE_SOURCE_BYTE_ORDER
00552                 if (um.is_source_big_endian())
00553                     a->add_value("source:big-endian");
00554                 else
00555                     a->add_value("source:little-endian");
00556 #endif
00557                 DBG(cerr << "Read CRC32: " << crc << " for " << (*i)->name() << endl);
00558                 (*i)->attributes()->add_attribute_nocopy(a);
00559         }
00560 }
00561 
00562 void
00563 D4Group::print_dap4(XMLWriter &xml, bool constrained)
00564 {
00565     if (!name().empty() && name() != "/") {
00566         // For named groups, if constrained is true only print if this group
00567         // has variables that are marked for transmission. For the root group
00568         // this test is not made.
00569         if (constrained && !send_p())
00570             return;
00571 
00572         if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) type_name().c_str()) < 0)
00573             throw InternalErr(__FILE__, __LINE__, "Could not write " + type_name() + " element");
00574 
00575         if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name().c_str()) < 0)
00576             throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
00577     }
00578 
00579     // dims
00580     if (!dims()->empty())
00581         dims()->print_dap4(xml, constrained);
00582 
00583     // enums
00584     if (!enum_defs()->empty())
00585         enum_defs()->print_dap4(xml, constrained);
00586 
00587     // variables
00588     Constructor::Vars_iter v = var_begin();
00589     while (v != var_end())
00590         (*v++)->print_dap4(xml, constrained);
00591 
00592     // attributes
00593     attributes()->print_dap4(xml);
00594 
00595     // groups
00596     groupsIter g = d_groups.begin();
00597     while (g != d_groups.end())
00598         (*g++)->print_dap4(xml, constrained);
00599 
00600     if (!name().empty() && name() != "/") {
00601         if (xmlTextWriterEndElement(xml.get_writer()) < 0)
00602             throw InternalErr(__FILE__, __LINE__, "Could not end " + type_name() + " element");
00603     }
00604 }
00605 
00606 } /* namespace libdap */