libdap
Updated for version 3.17.0
|
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 */