OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
AggregationUtil.cc
Go to the documentation of this file.
1 // This file is part of the "NcML Module" project, a BES module designed
3 // to allow NcML files to be used to be used as a wrapper to add
4 // AIS to existing datasets of any format.
5 //
6 // Copyright (c) 2009 OPeNDAP, Inc.
7 // Author: Michael Johnson <m.johnson@opendap.org>
8 //
9 // For more information, please also see the main website: http://opendap.org/
10 //
11 // This library is free software; you can redistribute it and/or
12 // modify it under the terms of the GNU Lesser General Public
13 // License as published by the Free Software Foundation; either
14 // version 2.1 of the License, or (at your option) any later version.
15 //
16 // This library is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 // Lesser General Public License for more details.
20 //
21 // You should have received a copy of the GNU Lesser General Public
22 // License along with this library; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 //
25 // Please see the files COPYING and COPYRIGHT for more information on the GLPL.
26 //
27 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
29 #include "AggregationUtil.h"
30 
31 // agg_util includes
32 #include "AggMemberDataset.h"
33 #include "AggregationException.h"
34 #include "Dimension.h"
35 
36 // libdap includes
37 #include <Array.h> // libdap
38 #include <AttrTable.h>
39 #include <BaseType.h>
40 #include <DataDDS.h>
41 #include <DDS.h>
42 #include <Grid.h>
43 
44 // Outside includes (MINIMIZE THESE!)
45 #include "NCMLDebug.h" // This the ONLY dependency on NCML Module I want in this class since the macros there are general it's ok...
46 
47 using libdap::Array;
48 using libdap::AttrTable;
49 using libdap::BaseType;
50 using libdap::Constructor;
51 using libdap::DataDDS;
52 using libdap::DDS;
53 using libdap::Grid;
54 using libdap::Vector;
55 using std::string;
56 using std::vector;
57 
58 namespace agg_util
59 {
60  // Static class member used to track the position of the last CVs insertion
61  // when building a JoinExisting aggregation.
62  int AggregationUtil::d_last_added_cv_position = 0;
63 
65  // ArrayGetterInterface impls
66 
67  /* virtual */
69  {
70  }
71 
73  // TopLevelArrayGetter impl
74 
77  {
78  }
79 
80  /* virtual */
82  {
83  }
84 
85  /* virtual */
88  {
89  return new TopLevelArrayGetter(*this);
90  }
91 
92  /* virtual */
93  libdap::Array*
95  const std::string& name,
96  const libdap::DataDDS& dds,
97  const libdap::Array* const pConstraintTemplate,
98  const std::string& debugChannel
99  ) const
100  {
101  // First, look up the BaseType
102  BaseType* pBT = AggregationUtil::getVariableNoRecurse(dds, name);
103 
104  // Next, if it's not there, throw exception.
105  if (!pBT)
106  {
107  throw AggregationException("TopLevelArrayGetter: "
108  "Did not find a variable named \"" +
109  name + "\" at the top-level of the DDS!");
110  }
111 
112  // Next, make sure it's an Array before we cast it
113  // Prefer using the enum type for speed rather than RTTI
114  if (pBT->type() != libdap::dods_array_c)
115  {
116  throw AggregationException("TopLevelArrayGetter: "
117  "The top-level DDS variable named \"" +
118  name +
119  "\" was not of the expected type!"
120  " Expected:Array Found:" + pBT->type_name());
121  }
122 
123  libdap::Array* pDatasetArray = static_cast<libdap::Array*>(pBT);
124 
125  // If given, copy the constraints over to the found Array
126  if (pConstraintTemplate)
127  {
129  (
130  pDatasetArray, // into this dataset array to be read
131  *pConstraintTemplate, // from this template
132  false, // same rank Array's in template and loaded, don't skip first dim
133  false, // or the to dimension. copy whole thing.
134  !(debugChannel.empty()), // printDebug
135  debugChannel
136  );
137  }
138 
139  // Force a read() perhaps with constraints
140  pDatasetArray->set_send_p(true);
141  pDatasetArray->set_in_selection(true);
142  pDatasetArray->read();
143 
144  return pDatasetArray;
145  }
146 
148  // TopLevelGridDataArrayGetter impl
149 
152  {
153  }
154 
155  /* virtual */
157  {
158  }
159 
160  /* virtual */
163  {
164  return new TopLevelGridDataArrayGetter(*this);
165  }
166 
167  /* virtual */
168  libdap::Array*
170  const std::string& name,
171  const libdap::DataDDS& dds,
172  const libdap::Array* const pConstraintTemplate,
173  const std::string& debugChannel
174  ) const
175  {
176  // First, look up the BaseType
177  BaseType* pBT = AggregationUtil::getVariableNoRecurse(dds, name);
178 
179  // Next, if it's not there, throw exception.
180  if (!pBT)
181  {
182  throw AggregationException("TopLevelGridArrayGetter: "
183  "Did not find a variable named \"" +
184  name + "\" at the top-level of the DDS!");
185  }
186 
187  // Next, make sure it's a Grid before we cast it
188  // Prefer using the enum type for speed rather than RTTI
189  if (pBT->type() != libdap::dods_grid_c)
190  {
191  throw AggregationException("TopLevelGridArrayGetter: "
192  "The top-level DDS variable named \"" +
193  name +
194  "\" was not of the expected type!"
195  " Expected:Grid Found:" + pBT->type_name());
196  }
197 
198  // Grab the array and return it.
199  Grid* pDataGrid = static_cast<Grid*>(pBT);
200  Array* pDataArray =
201  static_cast<Array*>(
202  pDataGrid->array_var()
203  );
204  if (!pDataArray)
205  {
206  throw AggregationException("TopLevelGridArrayGetter: "
207  "The data Array var for variable name=\"" +
208  name +
209  "\" was unexpectedly null!");
210  }
211 
212  // If given, copy the constraints over to the found Array
213  if (pConstraintTemplate)
214  {
216  (
217  pDataArray, // into this data array to be read
218  *pConstraintTemplate, // from this template
219  false, // same rank Array's in template and loaded, don't skip first dim
220  false, // also don't skip in the to array
221  !(debugChannel.empty()), // printDebug
222  debugChannel
223  );
224  }
225 
226  // Force the read() on the Grid level since some handlers
227  // cannot handle a read on a subobject unless read() is called
228  // on the parent object. We have given the constraints to the
229  // data Array already.
230  // TODO make an option on whether to load the Grid's map
231  // vectors or not! I think for these cases we do not want them ever!
232  pDataGrid->set_send_p(true);
233  pDataGrid->set_in_selection(true);
234  pDataGrid->read();
235 
236  // Also make sure the Array was read and if not call it as well.
237  if (!pDataArray->read_p())
238  {
239  pDataArray->set_send_p(true);
240  pDataArray->set_in_selection(true);
241  pDataArray->read();
242  }
243 
244  return pDataArray;
245  }
246 
248  // TopLevelGridMapArrayGetter impl
249 
252  , _gridName(gridName)
253  {
254  }
255 
256  /* virtual */
258  {
259  }
260 
261  /* virtual */
264  {
265  return new TopLevelGridMapArrayGetter(*this);
266  }
267 
268  /* virtual */
269  libdap::Array*
271  const std::string& arrayName,
272  const libdap::DataDDS& dds,
273  const libdap::Array* const pConstraintTemplate,
274  const std::string& debugChannel
275  ) const
276  {
277  // First, look up the Grid the map is in
278  BaseType* pBT = AggregationUtil::getVariableNoRecurse(dds, _gridName);
279 
280  // Next, if it's not there, throw exception.
281  if (!pBT)
282  {
283  throw AggregationException(
284  "Did not find a variable named \"" +
285  _gridName + "\" at the top-level of the DDS!");
286  }
287 
288  // Next, make sure it's a Grid before we cast it
289  // Prefer using the enum type for speed rather than RTTI
290  if (pBT->type() != libdap::dods_grid_c)
291  {
292  throw AggregationException(
293  "The top-level DDS variable named \"" +
294  _gridName +
295  "\" was not of the expected type!"
296  " Expected:Grid Found:" + pBT->type_name());
297  }
298 
299  // Find the correct map
300  Grid* pDataGrid = static_cast<Grid*>(pBT);
301  Array* pMap = const_cast<Array*>(AggregationUtil::findMapByName(*pDataGrid, arrayName));
302  NCML_ASSERT_MSG(pMap,
303  "Expected to find the map with name " + arrayName + " within the Grid "
304  + _gridName + " but failed to find it!");
305 
306  // Prepare it to be read in so we can get the data
307  pMap->set_send_p(true);
308  pMap->set_in_selection(true);
309 
310  // If given, copy the constraints over to the found Array
311  if (pConstraintTemplate)
312  {
314  (
315  pMap, // into this data array to be read
316  *pConstraintTemplate, // from this template
317  false, // same rank Array's in template and loaded, don't skip first dim
318  false, // also don't skip in the to array
319  !(debugChannel.empty()), // printDebug
320  debugChannel
321  );
322  }
323 
324  // Do the read
325  pMap->read();
326 
327  return pMap;
328  }
329 
330 
331  /*********************************************************************************************************
332  * AggregationUtil Impl
333  */
334  void
335  AggregationUtil::performUnionAggregation(DDS* pOutputUnion, const ConstDDSList& datasetsInOrder)
336  {
337  VALID_PTR(pOutputUnion);
338 
339  // Union any non-aggregated variables from the template dataset into the aggregated dataset
340  // Because we want the joinExistingaggregation to build up the Coordinate Variables (CVs)
341  // in the order they are declared in the NCML file, we need to track the current position
342  // where the last one was inserted. We can do that with a field in the AggregationUtil
343  // class. Here we reset that field so that it starts at position 0. 12.13.11 jhrg
345 
346  vector<const DDS*>::const_iterator endIt = datasetsInOrder.end();
347  vector<const DDS*>::const_iterator it;
348  for (it = datasetsInOrder.begin(); it != endIt; ++it)
349  {
350  const DDS* pDDS = *it;
351  VALID_PTR(pDDS);
352 
353  // Merge in the global attribute tables
354  unionAttrsInto(&(pOutputUnion->get_attr_table()),
355  // TODO there really should be const version of this in libdap::DDS
356  const_cast<DDS*>(pDDS)->get_attr_table());
357 
358  // Merge in the variables, which carry their tables along with them since the AttrTable is
359  // within the variable's "namespace", or lexical scope.
360  unionAllVariablesInto(pOutputUnion, *pDDS);
361  }
362  }
363 
364  void
365  AggregationUtil::unionAttrsInto(AttrTable* pOut, const AttrTable& fromTableIn)
366  {
367  // semantically const
368  AttrTable& fromTable = const_cast<AttrTable&>(fromTableIn);
369  AttrTable::Attr_iter endIt = fromTable.attr_end();
370  AttrTable::Attr_iter it;
371  for (it = fromTable.attr_begin(); it != endIt; ++it)
372  {
373  const string& name = fromTable.get_name(it);
374  AttrTable::Attr_iter attrInOut;
375  bool foundIt = findAttribute(*pOut, name, attrInOut);
376  // If it's already in the output, then skip it
377  if (foundIt)
378  {
379  BESDEBUG("ncml", "Union of AttrTable: an attribute named " << name << " already exist in output, skipping it..." << endl);
380  continue;
381  }
382  else // put a copy of it into the output
383  {
384  // containers need deep copy
385  if (fromTable.is_container(it))
386  {
387  AttrTable* pOrigAttrContainer = fromTable.get_attr_table(it);
388  NCML_ASSERT_MSG(pOrigAttrContainer,
389  "AggregationUtil::mergeAttrTables(): expected non-null AttrTable for the attribute container: " + name);
390  AttrTable* pClonedAttrContainer = new AttrTable(*pOrigAttrContainer);
391  VALID_PTR(pClonedAttrContainer);
392  pOut->append_container(pClonedAttrContainer, name);
393  BESDEBUG("ncml", "Union of AttrTable: adding a deep copy of attribute=" << name << " to the merged output." << endl);
394  }
395  else // for a simple type
396  {
397  string type = fromTable.get_type(it);
398  vector<string>* pAttrTokens = fromTable.get_attr_vector(it);
399  VALID_PTR(pAttrTokens);
400  // append_attr makes a copy of the vector, so we don't have to do so here.
401  pOut->append_attr(name, type, pAttrTokens);
402  }
403  }
404  }
405  }
406 
407  bool
408  AggregationUtil::findAttribute(const AttrTable& inTable, const string& name, AttrTable::Attr_iter& attr)
409  {
410  AttrTable& inTableSemanticConst = const_cast<AttrTable&>(inTable);
411  attr = inTableSemanticConst.simple_find(name);
412  return (attr != inTableSemanticConst.attr_end());
413  }
414 
415  void
416  AggregationUtil::unionAllVariablesInto(libdap::DDS* pOutputUnion, const ConstDDSList& datasetsInOrder)
417  {
418  ConstDDSList::const_iterator endIt = datasetsInOrder.end();
419  ConstDDSList::const_iterator it;
420  for (it = datasetsInOrder.begin(); it != endIt; ++it)
421  {
422  unionAllVariablesInto(pOutputUnion, *(*it) );
423  }
424  }
425 
426  void
427  AggregationUtil::unionAllVariablesInto(libdap::DDS* pOutputUnion, const libdap::DDS& fromDDS, bool add_at_top)
428  {
429  DDS& dds = const_cast<DDS&>(fromDDS); // semantically const
430  DDS::Vars_iter endIt = dds.var_end();
431  DDS::Vars_iter it;
432  for (it = dds.var_begin(); it != endIt; ++it)
433  {
434  BaseType* var = *it;
435  if (var)
436  {
437  bool addedVar = addCopyOfVariableIfNameIsAvailable(pOutputUnion, *var, add_at_top);
438  if (addedVar)
439  {
440  BESDEBUG("ncml", "Variable name=" << var->name() << " wasn't in the union yet and was added." << endl);
441  }
442  else
443  {
444  BESDEBUG("ncml", "Variable name=" << var->name() << " was already in the union and was skipped." << endl);
445  }
446  }
447  }
448  }
449 
450  // This method is used to 'initialize' a new JoinExisting aggregation so that
451  // A set of Coordinate Variables (CVs) will be inserted _in the order they are
452  // listed_ in the .ncml file.
453  void
455  {
456  //cerr << "Called resetCVInsertionPosition" << endl;
457  d_last_added_cv_position = 0;
458  }
459 
460  bool
461  AggregationUtil::addCopyOfVariableIfNameIsAvailable(libdap::DDS* pOutDDS, const libdap::BaseType& varProto, bool add_at_top)
462  {
463  bool ret = false;
464  BaseType* existingVar = findVariableAtDDSTopLevel(*pOutDDS, varProto.name());
465  if (!existingVar)
466  {
467  // Add the var. add_var does a clone, so we don't need to.
468  BESDEBUG("ncml2", "AggregationUtil::addCopyOfVariableIfNameIsAvailable: " << varProto.name() << endl);
469  if (add_at_top)
470  {
471  // This provides a way to remember where the last CV was inserted and adds
472  // this one after it. That provides the behavior that all of the CVs are
473  // added at the beginning of the DDS but in the order they appear in the NCML.
474  // That will translate into a greater chance of success for users, I think ...
475  //
476  // See also similar code in AggregationElement::createAndAddCoordinateVariableForNewDimensio
477  // jhrg 10/17/11
478  //cerr << "d_last_added_cv_position: " << d_last_added_cv_position << endl;
479  DDS::Vars_iter pos = pOutDDS->var_begin() + d_last_added_cv_position;
480 
481  pOutDDS->insert_var(pos, const_cast<BaseType*>(&varProto));
482 
483  ++d_last_added_cv_position;
484  }
485  else
486  {
487  pOutDDS->add_var(const_cast<BaseType*>(&varProto));
488  }
489 
490  ret = true;
491  }
492  return ret;
493  }
494 
495  void
496  AggregationUtil::addOrReplaceVariableForName(libdap::DDS* pOutDDS, const libdap::BaseType& varProto)
497  {
498  BaseType* existingVar = findVariableAtDDSTopLevel(*pOutDDS, varProto.name());
499 
500  // If exists, nuke it.
501  if (existingVar)
502  {
503  pOutDDS->del_var(varProto.name());
504  }
505 
506  // Add the var. add_var does a clone, so we don't need to clone it here.
507  pOutDDS->add_var(const_cast<BaseType*>(&varProto));
508  }
509 
510  libdap::BaseType*
511  AggregationUtil::findVariableAtDDSTopLevel(const libdap::DDS& dds_const, const string& name)
512  {
513  BaseType* ret = 0;
514  DDS& dds = const_cast<DDS&>(dds_const); // semantically const
515  DDS::Vars_iter endIt = dds.var_end();
516  DDS::Vars_iter it;
517  for (it = dds.var_begin(); it != endIt; ++it)
518  {
519  BaseType* var = *it;
520  if (var && var->name() == name)
521  {
522  ret = var;
523  break;
524  }
525  }
526  return ret;
527  }
528 
529  template <class LibdapType>
530  LibdapType*
531  AggregationUtil::findTypedVariableAtDDSTopLevel(const libdap::DDS& dds, const string& name)
532  {
533  BaseType* pBT = findVariableAtDDSTopLevel(dds, name);
534  if (pBT)
535  {
536  return dynamic_cast<LibdapType*>(pBT);
537  }
538  else
539  {
540  return 0;
541  }
542  }
543 
544  void
546  Array* pJoinedArray,
547  const std::string& joinedArrayName,
548  const std::string& newOuterDimName,
549  const std::vector<libdap::Array*>& fromVars,
550  bool copyData)
551  {
552  string funcName = "AggregationUtil::createOuterDimensionJoinedArray:";
553 
554  NCML_ASSERT_MSG(fromVars.size() > 0,
555  funcName + "Must be at least one Array in input!");
556 
557  // uses the first one as template for type and shape
558  if (!validateArrayTypesAndShapesMatch(fromVars, true))
559  {
560  throw AggregationException(funcName +
561  " The input arrays must all have the same data type and dimensions but do not!");
562  }
563 
564  // The first will be used to "set up" the pJoinedArray
565  Array* templateArray = fromVars[0];
566  VALID_PTR(templateArray);
567  BaseType* templateVar = templateArray->var();
568  NCML_ASSERT_MSG(templateVar,
569  funcName + "Expected a non-NULL prototype BaseType in the first Array!");
570 
571  // Set the template var for the type.
572  pJoinedArray->add_var(templateVar);
573  // and force the name to be the desired one, not the prototype's
574  pJoinedArray->set_name(joinedArrayName);
575 
576  // Copy the attribute table from the template over... We're not merging or anything.
577  pJoinedArray->set_attr_table(templateArray->get_attr_table());
578 
579  // Create a new outer dimension based on the number of inputs we have.
580  // These append_dim calls go left to right, so we need to add the new dim FIRST.
581  pJoinedArray->append_dim(fromVars.size(), newOuterDimName);
582 
583  // Use the template to add inner dimensions to the new array
584  for (Array::Dim_iter it = templateArray->dim_begin(); it != templateArray->dim_end(); ++it)
585  {
586  int dimSize = templateArray->dimension_size(it);
587  string dimName = templateArray->dimension_name(it);
588  pJoinedArray->append_dim(dimSize, dimName);
589  }
590 
591  if (copyData)
592  {
593  // Make sure we have capacity for the full length of the up-ranked shape.
594  pJoinedArray->reserve_value_capacity(pJoinedArray->length());
595  // Glom the data together in
596  joinArrayData(pJoinedArray, fromVars,
597  false, // we already reserved the space
598  true); // but please clear the Vector buffers after you use each Array in fromVars to help on memory.
599  }
600  }
601 
602  bool
604  const std::vector<libdap::Array*>& arrays,
605  bool enforceMatchingDimNames)
606  {
607  NCML_ASSERT(arrays.size() > 0);
608  bool valid = true;
609  Array* pTemplate = 0;
610  for (vector<Array*>::const_iterator it = arrays.begin(); it != arrays.end(); ++it)
611  {
612  // Set the template from the first one.
613  if (!pTemplate)
614  {
615  pTemplate = *it;
616  VALID_PTR(pTemplate);
617  continue;
618  }
619 
620  valid = ( valid &&
621  doTypesMatch(*pTemplate, **it) &&
622  doShapesMatch(*pTemplate, **it, enforceMatchingDimNames)
623  );
624  // No use continuing
625  if (!valid)
626  {
627  break;
628  }
629  }
630  return valid;
631  }
632 
633  bool
634  AggregationUtil::doTypesMatch(const libdap::Array& lhsC, const libdap::Array& rhsC)
635  {
636  // semantically const
637  Array& lhs = const_cast<Array&>(lhsC);
638  Array& rhs = const_cast<Array&>(rhsC);
639  return (lhs.var() && rhs.var() && lhs.var()->type() == rhs.var()->type());
640  }
641 
642  bool
643  AggregationUtil::doShapesMatch(const libdap::Array& lhsC, const libdap::Array& rhsC, bool checkDimNames)
644  {
645  // semantically const
646  Array& lhs = const_cast<Array&>(lhsC);
647  Array& rhs = const_cast<Array&>(rhsC);
648 
649  // Check the number of dims matches first.
650  bool valid = true;
651  if (lhs.dimensions() != rhs.dimensions())
652  {
653  valid = false;
654  }
655  else
656  {
657  // Then iterate on both in sync and compare.
658  Array::Dim_iter rhsIt = rhs.dim_begin();
659  for (Array::Dim_iter lhsIt = lhs.dim_begin(); lhsIt != lhs.dim_end(); (++lhsIt, ++rhsIt) )
660  {
661  valid = (valid && ( lhs.dimension_size(lhsIt) == rhs.dimension_size(rhsIt) ));
662 
663  if (checkDimNames)
664  {
665  valid = (valid && ( lhs.dimension_name(lhsIt) == rhs.dimension_name(rhsIt) ));
666  }
667  }
668  }
669  return valid;
670  }
671 
672  unsigned int
674  std::vector<Array*>& varArrays,
675  const std::string& collectVarName,
676  const ConstDDSList& datasetsInOrder)
677  {
678  unsigned int count = 0;
679  ConstDDSList::const_iterator endIt = datasetsInOrder.end();
680  ConstDDSList::const_iterator it;
681  for (it = datasetsInOrder.begin(); it != endIt; ++it)
682  {
683  DDS* pDDS = const_cast<DDS*>(*it);
684  VALID_PTR(pDDS);
685  Array* pVar = dynamic_cast<Array*>(findVariableAtDDSTopLevel(*pDDS, collectVarName));
686  if (pVar)
687  {
688  varArrays.push_back(pVar);
689  count++;
690  }
691  }
692  return count;
693  }
694 
695  bool
697  {
698  Array* pArr = dynamic_cast<Array*>(pBT);
699  if (pArr &&
700  (pArr->dimensions() == 1))
701  {
702  // only one dimension, so grab the first and make sure we only got one.
703  Array::Dim_iter it = pArr->dim_begin();
704  bool matches = (pArr->dimension_name(it) == pArr->name());
705  NCML_ASSERT_MSG( (++it == pArr->dim_end()),
706  "Logic error: NCMLUtil::isCoordinateVariable(): expected one dimension from Array, but got more!");
707  return matches;
708  }
709  else
710  {
711  return false;
712  }
713  }
714 
715  void
717  const std::vector<Array*>& varArrays,
718  bool reserveStorage/*=true*/,
719  bool clearDataAfterUse/*=false*/)
720  {
721  // Make sure we get a pAggArray with a type var we can deal with.
722  VALID_PTR(pAggArray->var());
723  NCML_ASSERT_MSG(pAggArray->var()->is_simple_type(),
724  "AggregationUtil::joinArrayData: the output Array is not of a simple type! Can't aggregate!");
725 
726  // If the caller wants us to do it, sum up length() and reserve that much.
727  if (reserveStorage)
728  {
729  // Figure it how much we need...
730  unsigned int totalLength = 0;
731  {
732  vector<Array*>::const_iterator it;
733  vector<Array*>::const_iterator endIt = varArrays.end();
734  for (it = varArrays.begin(); it != endIt; ++it)
735  {
736  Array* pArr = *it;
737  if (pArr)
738  {
739  totalLength += pArr->length();
740  }
741  }
742  }
743  pAggArray->reserve_value_capacity(totalLength);
744  }
745 
746  // For each Array, make sure it's read in and copy its data into the output.
747  unsigned int nextElt = 0; // keeps track of where we are to write next in the output
748  vector<Array*>::const_iterator it;
749  vector<Array*>::const_iterator endIt = varArrays.end();
750  for (it = varArrays.begin(); it != endIt; ++it)
751  {
752  Array* pArr = *it;
753  VALID_PTR(pArr);
754  NCML_ASSERT_MSG(pArr->var() && (pArr->var()->type() == pAggArray->var()->type()),
755  "AggregationUtil::joinArrayData: one of the arrays to join has different type than output! Can't aggregate!");
756 
757  // Make sure it was read in...
758  if (!pArr->read_p())
759  {
760  pArr->read();
761  }
762 
763  // Copy it in with the Vector call and update our location
764  nextElt += pAggArray->set_value_slice_from_row_major_vector(*pArr, nextElt);
765 
766  if (clearDataAfterUse)
767  {
768  pArr->clear_local_data();
769  }
770  }
771 
772  // That's all folks!
773  }
774 
776  // struct dimension
777  // {
778  // int size; ///< The unconstrained dimension size.
779  // string name; ///< The name of this dimension.
780  // int start; ///< The constraint start index
781  // int stop; ///< The constraint end index
782  // int stride; ///< The constraint stride
783  // int c_size; ///< Size of dimension once constrained
784  // };
785 
787  void
788  AggregationUtil::printDimensions(std::ostream& os, const libdap::Array& fromArray)
789  {
790  os << "Array dimensions: " << endl;
791  Array& theArray = const_cast<Array&>(fromArray);
792  Array::Dim_iter it;
793  Array::Dim_iter endIt = theArray.dim_end();
794  for (it = theArray.dim_begin(); it != endIt; ++it)
795  {
796  Array::dimension d = *it;
797  os << "Dim = {" << endl;
798  os << "name=" << d.name << endl;
799  os << "size=" << d.size << endl;
800  os << " }" << endl;
801  }
802  os << "End Array dimensions." << endl;
803  }
804 
805  void
806  AggregationUtil::printConstraints(std::ostream& os, const Array& rcArray)
807  {
808  os << "Array constraints: " << endl;
809  Array& theArray = const_cast<Array&>(rcArray);
810  Array::Dim_iter it;
811  Array::Dim_iter endIt = theArray.dim_end();
812  for (it = theArray.dim_begin(); it != endIt; ++it)
813  {
814  Array::dimension d = *it;
815  os << "Dim = {" << endl;
816  os << "name=" << d.name << endl;
817  os << "start=" << d.start << endl;
818  os << "stride=" << d.stride << endl;
819  os << "stop=" << d.stop << endl;
820  os << " }" << endl;
821  }
822  os << "End Array constraints" << endl;
823  }
824 
825  void
827  const std::string& debugChannel,
828  const libdap::Array& fromArray)
829  {
830  ostringstream oss;
831  BESDEBUG(debugChannel, "Printing constraints for Array: " << fromArray.name() << ": " << oss.str() << endl);
832  AggregationUtil::printConstraints(oss, fromArray);
833  BESDEBUG(debugChannel, oss.str() << endl);
834  }
835 
836  void
838  const Array& fromArrayConst,
839  bool skipFirstFromDim,
840  bool skipFirstToDim,
841  bool printDebug /* = false */,
842  const std::string& debugChannel /* = "agg_util" */)
843  {
844  VALID_PTR(pToArray);
845  Array& fromArray = const_cast<Array&>(fromArrayConst);
846 
847  // Make sure there's no constraints on output. Shouldn't be, but...
848  pToArray->reset_constraint();
849 
850  // Ensure the dimensionalities will work out.
851  int skipDelta = ((skipFirstFromDim)?(1):(0));
852  // If skipping output as well, subtract out the delta.
853  // If we go negative, also an error.
854  if (skipFirstToDim)
855  {
856  skipDelta -= 1;
857  }
858  if (skipDelta < 0 ||
859  (pToArray->dimensions() + skipDelta !=
860  const_cast<Array&>(fromArrayConst).dimensions()) )
861  {
862  throw AggregationException("AggregationUtil::transferArrayConstraints: "
863  "Mismatched dimensionalities!");
864  }
865 
866  if (printDebug)
867  {
868  BESDEBUG(debugChannel, "Printing constraints on fromArray name= " <<
869  fromArray.name() <<
870  " before transfer..." <<
871  endl);
872  printConstraintsToDebugChannel(debugChannel, fromArray);
873  }
874 
875  // Only real way to the constraints is with the iterator,
876  // so we'll iterator on the fromArray and move
877  // to toarray iterator in sync.
878  Array::Dim_iter fromArrIt = fromArray.dim_begin();
879  Array::Dim_iter fromArrEndIt = fromArray.dim_end();
880  Array::Dim_iter toArrIt = pToArray->dim_begin();
881  for (;
882  fromArrIt != fromArrEndIt;
883  ++fromArrIt)
884  {
885  if (skipFirstFromDim && (fromArrIt == fromArray.dim_begin()) )
886  {
887  // If we skip first to array as well, increment
888  // before the next call.
889  if (skipFirstToDim)
890  {
891  ++toArrIt;
892  }
893  continue;
894  }
895 
896  NCML_ASSERT_MSG(fromArrIt->name == toArrIt->name,
897  "GAggregationUtil::transferArrayConstraints: "
898  "Expected the dimensions to have the same name but they did not.");
899  pToArray->add_constraint(
900  toArrIt,
901  fromArrIt->start,
902  fromArrIt->stride,
903  fromArrIt->stop );
904  ++toArrIt;
905  }
906 
907  if (printDebug)
908  {
909  BESDEBUG(debugChannel, "Printing constrains on pToArray after transfer..." << endl);
910  printConstraintsToDebugChannel(debugChannel, *pToArray);
911  }
912  }
913 
914  BaseType*
915  AggregationUtil::getVariableNoRecurse(const libdap::DDS& ddsConst, const std::string& name)
916  {
917  BaseType* ret = 0;
918  DDS& dds = const_cast<DDS&>(ddsConst); // semantically const
919  DDS::Vars_iter endIt = dds.var_end();
920  DDS::Vars_iter it;
921  for (it = dds.var_begin(); it != endIt; ++it)
922  {
923  BaseType* var = *it;
924  if (var && var->name() == name)
925  {
926  ret = var;
927  break;
928  }
929  }
930  return ret;
931  }
932 
933  // Ugh cut and pasted from the other...
934  // TODO REFACTOR DDS and Constructor really need a common abstract interface,
935  // like IVariableContainer that declares the iterators and associated methods.
936  BaseType*
937  AggregationUtil::getVariableNoRecurse(const libdap::Constructor& varContainerConst, const std::string& name)
938  {
939  BaseType* ret = 0;
940  Constructor& varContainer = const_cast<Constructor&>(varContainerConst); // semantically const
941  Constructor::Vars_iter endIt = varContainer.var_end();
942  Constructor::Vars_iter it;
943  for (it = varContainer.var_begin(); it != endIt; ++it)
944  {
945  BaseType* var = *it;
946  if (var && var->name() == name)
947  {
948  ret = var;
949  break;
950  }
951  }
952  return ret;
953  }
954 
955  /*static*/
956  Array*
958  {
959  if (!pBT)
960  {
961  return 0;
962  }
963 
964  // After switch():
965  // if Array, will be cast to Array.
966  // if Grid, will be cast data Array member of Grid.
967  // Other types, will be null.
968  libdap::Array* pArray(0);
969  switch (pBT->type())
970  {
971  case libdap::dods_array_c:
972  pArray = static_cast<libdap::Array*>(pBT);
973  break;
974 
975  case libdap::dods_grid_c:
976  pArray = static_cast<Grid*>(pBT)->get_array();
977  break;
978 
979  default:
980  pArray = 0;
981  break;
982  }
983  return pArray;
984  }
985 
986  const Array*
987  AggregationUtil::findMapByName(const libdap::Grid& inGrid, const string& findName)
988  {
989  Grid& grid = const_cast<Grid&>(inGrid);
990  Array* pRet = 0;
991  Grid::Map_iter it;
992  Grid::Map_iter endIt = grid.map_end();
993  for (it = grid.map_begin(); it != endIt; ++it)
994  {
995  if ( (*it)->name() == findName )
996  {
997  pRet = static_cast<Array*>(*it);
998  break;
999  }
1000  }
1001  return pRet;
1002  }
1003 
1004  void
1006  libdap::Array& oOutputArray,
1007  unsigned int atIndex,
1008  const Array& constrainedTemplateArray,
1009  const std::string& varName,
1010  AggMemberDataset& dataset,
1011  const ArrayGetterInterface& arrayGetter,
1012  const std::string& debugChannel
1013  )
1014  {
1015  const libdap::DataDDS* pDataDDS = dataset.getDataDDS();
1016  NCML_ASSERT_MSG(pDataDDS, "GridAggregateOnOuterDimension::read(): Got a null DataDDS "
1017  "while loading dataset = " + dataset.getLocation() );
1018 
1019  // Grab the Array from the DataDDS with the getter
1020  Array* pDatasetArray = arrayGetter.readAndGetArray(
1021  varName,
1022  *pDataDDS,
1023  &constrainedTemplateArray,
1024  debugChannel);
1025  NCML_ASSERT_MSG(pDatasetArray, "In aggregation member dataset, failed to get the array! "
1026  "Dataset location = " + dataset.getLocation());
1027 
1028  // Make sure that the data was read in or I dunno what went on.
1029  if (!pDatasetArray->read_p())
1030  {
1031  NCML_ASSERT_MSG(pDatasetArray->read_p(),
1032  "AggregationUtil::addDatasetArrayDataToAggregationOutputArray: pDatasetArray was not read_p()!");
1033  }
1034 
1035  // Make sure it matches the prototype or somthing went wrong
1036  if (!AggregationUtil::doTypesMatch(constrainedTemplateArray, *pDatasetArray))
1037  {
1038  throw AggregationException(
1039  "Invalid aggregation! "
1040  "AggregationUtil::addDatasetArrayDataToAggregationOutputArray: "
1041  "We found the aggregation variable name=" + varName +
1042  " but it was not of the same type as the prototype variable!");
1043  }
1044 
1045  // Make sure the subshapes match! (true means check dimension names too... debate this)
1046  if (!AggregationUtil::doShapesMatch(constrainedTemplateArray, *pDatasetArray, true))
1047  {
1048  throw AggregationException(
1049  "Invalid aggregation! "
1050  "AggregationUtil::addDatasetArrayDataToAggregationOutputArray: "
1051  "We found the aggregation variable name=" + varName +
1052  " but it was not of the same shape as the prototype!");
1053  }
1054 
1055  // Make sure the length of the data array also matches the proto
1056  if (constrainedTemplateArray.length() != pDatasetArray->length())
1057  {
1058  NCML_ASSERT_MSG(constrainedTemplateArray.length() == pDatasetArray->length(),
1059  "AggregationUtil::addDatasetArrayDataToAggregationOutputArray: "
1060  "The prototype array and the loaded dataset array length()'s were not equal, even "
1061  "though their shapes matched. Logic problem.");
1062  }
1063 
1064  // FINALLY, we get to stream the data!
1065  oOutputArray.set_value_slice_from_row_major_vector(*pDatasetArray, atIndex);
1066  }
1067 
1068  void
1069  AggregationUtil::gatherMetadataChangesFrom(BaseType* pIntoVar, const BaseType& fromVarC)
1070  {
1071  BaseType& fromVar = const_cast<BaseType&>(fromVarC); //semantic const
1072  // The output will end up here.
1073  AttrTable finalAT;
1074 
1075  // First, seed it with the changes in the fromVar.
1076  unionAttrsInto(&finalAT, fromVar.get_attr_table());
1077 
1078  // Then union in the items from the to var
1079  unionAttrsInto(&finalAT, pIntoVar->get_attr_table());
1080 
1081  // HACK BUG In the set_attr_table call through AttrTable operator=
1082  // means we keep bad memory around. Workaround until fixed!
1083  pIntoVar->get_attr_table().erase();
1084 
1085  // Finally, replace the output var's table with the constructed one!
1086  pIntoVar->set_attr_table(finalAT);
1087  }
1088 
1089 } // namespace agg_util
static void gatherMetadataChangesFrom(libdap::BaseType *pIntoVar, const libdap::BaseType &fromVar)
Union fromVar's AttrTable (initially) with pIntoVar's AttrTable and replace pIntoVar's AttrTable with...
virtual libdap::Array * readAndGetArray(const std::string &name, const libdap::DataDDS &dds, const libdap::Array *const pConstraintTemplate, const std::string &debugChannel) const
Lookup name within the top level of DDS ONLY and return it as an Array* if it is a subclass of Array...
static LibdapType * findTypedVariableAtDDSTopLevel(const libdap::DDS &dds, const string &name)
Template wrapper for findVariableAtDDSTopLevel() which does the find but only return non-NULL if the ...
Abstract helper superclass for allowing lazy access to the DataDDS for an aggregation.
TopLevelGridMapArrayGetter(const std::string &gridName)
virtual libdap::Array * readAndGetArray(const std::string &name, const libdap::DataDDS &dds, const libdap::Array *const pConstraintTemplate, const std::string &debugChannel) const
Find's the Array using name as the name of a map to find within a top level Grid names in the constru...
virtual TopLevelGridDataArrayGetter * clone() const
Virtual constructor idiom.
#define NCML_ASSERT(cond)
Definition: NCMLDebug.h:80
static libdap::BaseType * findVariableAtDDSTopLevel(const libdap::DDS &dds, const string &name)
Find a variable with name at the top level of the DDS.
std::vector< const libdap::DDS * > ConstDDSList
static const libdap::Array * findMapByName(const libdap::Grid &inGrid, const std::string &findName)
Find the given map name in the given Grid and return it if found, else NULL.
Helper class for temporarily hijacking an existing dhi to load a DDX response for one particular file...
static bool couldBeCoordinateVariable(libdap::BaseType *pBT)
#define NCML_ASSERT_MSG(cond, msg)
Definition: NCMLDebug.h:83
static void unionAttrsInto(libdap::AttrTable *pOut, const libdap::AttrTable &fromTable)
Merge any attributes in tableToMerge whose names do not already exist within *pOut into pOut...
static bool validateArrayTypesAndShapesMatch(const std::vector< libdap::Array * > &arrays, bool enforceMatchingDimNames)
Scan all the arrays in arrays using the first as a template and make sure that they all have the same...
static void joinArrayData(libdap::Array *pAggArray, const std::vector< libdap::Array * > &varArrays, bool reserveStorage=true, bool clearDataAfterUse=false)
Copy the simple type data Vector for each Array in varArrays into pAggArray, sequentially, effectively appending all the row major data in each entry in varArray into the row major order of pAggArray.
static void performUnionAggregation(libdap::DDS *pOutputUnion, const ConstDDSList &datasetsInOrder)
Perform an NCML-type union aggregation on the datasets in datasetsInOrder into pOutputUnion.
static void addOrReplaceVariableForName(libdap::DDS *pOutDDS, const libdap::BaseType &varProto)
If a variable with the name varProto.name() doesn't exist, add a copy of varProto to pOutDDS...
Helper class hierarchy for acquiring variable of a certain type from a DDS.
const std::string & getLocation() const
The location to which the AggMemberDataset refers Note: this could be "" for some subclasses if they ...
Concrete impl that simply finds the Array by looking for a variable of the given name at the top leve...
virtual libdap::Array * readAndGetArray(const std::string &name, const libdap::DataDDS &dds, const libdap::Array *const pConstraintTemplate, const std::string &debugChannel) const =0
Find, constrain, read, and return an Array inside the DDS using the given name information (somehow...
static unsigned int collectVariableArraysInOrder(std::vector< libdap::Array * > &varArrays, const std::string &collectVarName, const ConstDDSList &datasetsInOrder)
Fill in varArrays with Array's named collectVarName from each DDS in datasetsInOrder, in that order.
virtual const libdap::DataDDS * getDataDDS()=0
Return the DataDDS for the location, loading it in if it hasn't yet been loaded.
static libdap::BaseType * getVariableNoRecurse(const libdap::DDS &dds, const std::string &name)
Return the variable in dds top level (no recursing, no fully qualified name dot notation) if it exist...
static void produceOuterDimensionJoinedArray(libdap::Array *pJoinedArray, const std::string &joinedArrayName, const std::string &newOuterDimName, const std::vector< libdap::Array * > &fromVars, bool copyData)
Basic joinNew aggregation into pJoinedArray on the array of inputs fromVars.
static void resetCVInsertionPosition()
Used to reset the class field that tracks where Coordinate Variables (CVs) have been inserted into th...
static bool doTypesMatch(const libdap::Array &lhs, const libdap::Array &rhs)
Do the lhs and rhs have the same data type?
virtual TopLevelGridMapArrayGetter * clone() const
Virtual constructor idiom.
static bool doShapesMatch(const libdap::Array &lhs, const libdap::Array &rhs, bool checkDimNames)
Do the lhs and rhs have the same shapes? Only use size for dimension compares unless checkDimNames...
static void unionAllVariablesInto(libdap::DDS *pOutputUnion, const ConstDDSList &datasetsInOrder)
For each variable within the top level of each dataset in datasetsInOrder (forward iteration) add a c...
static bool findAttribute(const libdap::AttrTable &inTable, const string &name, libdap::AttrTable::Attr_iter &attr)
Lookup the attribute with given name in inTable and place a reference in attr.
virtual TopLevelArrayGetter * clone() const
Virtual constructor idiom.
static void addDatasetArrayDataToAggregationOutputArray(libdap::Array &oOutputArray, unsigned int atIndex, const libdap::Array &constrainedTemplateArray, const string &varName, AggMemberDataset &dataset, const ArrayGetterInterface &arrayGetter, const string &debugChannel)
Load the given dataset's DataDDS.
virtual libdap::Array * readAndGetArray(const std::string &name, const libdap::DataDDS &dds, const libdap::Array *const pConstraintTemplate, const std::string &debugChannel) const
Find the array as the data Array of a TOP-LEVEL Grid with name.
static void transferArrayConstraints(libdap::Array *pToArray, const libdap::Array &fromArray, bool skipFirstFromDim, bool skipFirstToDim, bool printDebug=false, const std::string &debugChannel="agg_util")
Copy the constraints from the from Array into the pToArray in Dim_iter order.
#define VALID_PTR(ptr)
Definition: NCMLDebug.h:88
static void printDimensions(std::ostream &os, const libdap::Array &fromArray)
Print out the dimensions name and size for the given Array into os.
static bool addCopyOfVariableIfNameIsAvailable(libdap::DDS *pOutDDS, const libdap::BaseType &varProto, bool add_at_top=false)
If a variable does not exist within pOutDDS (top level) with the same name as varProto, then place a clone of varProto (using virtual ctor ptr_duplicate) into pOutDDS.
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
Exception class used by AggregationUtil and other agg_util classes to pass generic exceptions upward ...
static libdap::Array * getAsArrayIfPossible(libdap::BaseType *pBT)
If pBT is an Array type, cast and return it as the Array.
static void printConstraintsToDebugChannel(const std::string &debugChannel, const libdap::Array &fromArray)
Output using BESDEBUG to the debugChannel channel.
static void printConstraints(std::ostream &os, const libdap::Array &fromArray)
Stream out the current constraints for all the dimensions in the Array.