OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
NCMLUtil.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 "NCMLUtil.h"
30 
31 #include "Array.h"
32 #include "BESDapResponse.h"
33 #include "BESDataDDSResponse.h"
34 #include "BESDDSResponse.h"
35 #include "BESDebug.h"
36 #include "BESInternalError.h"
37 #include <ctype.h>
38 #include "Constructor.h"
39 #include "DAS.h"
40 #include "DDS.h"
41 #include <AttrTable.h>
42 
43 #include "NCMLDebug.h"
44 
45 using namespace libdap;
46 using namespace std;
47 
48 namespace ncml_module
49 {
50 
51  const std::string NCMLUtil::WHITESPACE = " \t\n";
52 
53  int
54  NCMLUtil::tokenize(const string& str,
55  vector<string>& tokens,
56  const string& delimiters)
57  {
58  BESDEBUG("ncml", "NCMLUtil::tokenize value of str:" << str << endl);
59 
60  // start empty
61  tokens.resize(0);
62  // Skip delimiters at beginning.
63  string::size_type lastPos = str.find_first_not_of(delimiters, 0);
64  // Find first "non-delimiter".
65  string::size_type pos = str.find_first_of(delimiters, lastPos);
66 
67  int count=0; // how many we added.
68  while (string::npos != pos || string::npos != lastPos)
69  {
70  // Found a token, add it to the vector.
71  tokens.push_back(str.substr(lastPos, pos - lastPos));
72  count++;
73  // Skip delimiters. Note the "not_of"
74  lastPos = str.find_first_not_of(delimiters, pos);
75  // Find next "non-delimiter"
76  pos = str.find_first_of(delimiters, lastPos);
77  }
78  return count;
79  }
80 
81  int
82  NCMLUtil::tokenizeChars(const string& str, vector<string>& tokens)
83  {
84  tokens.resize(0);
85  // push each char as a token
86  for (unsigned int i=0; i<str.size(); ++i)
87  {
88  string val = "";
89  val += str[i];
90  tokens.push_back(val);
91  }
92  return str.size();
93  }
94 
95  bool
96  NCMLUtil::isAscii(const string& str)
97  {
98  string::const_iterator endIt = str.end();
99  for (string::const_iterator it = str.begin(); it != endIt; ++it)
100  {
101  if (!isascii(*it))
102  {
103  return false;
104  }
105  }
106  return true;
107  }
108 
109  bool
110  NCMLUtil::isAllWhitespace(const string& str)
111  {
112  return (str.find_first_not_of(" \t\n") == string::npos);
113  }
114 
115  void
116  NCMLUtil::trimLeft(std::string& input, const std::string& trimChars /* = WHITESPACE */)
117  {
118  size_t firstValid = input.find_first_not_of(trimChars);
119  input.erase(0, firstValid);
120  }
121 
125  void
126  NCMLUtil::trimRight(std::string& input, const std::string& trimChars /* = WHITESPACE */)
127  {
128  size_t lastValid = input.find_last_not_of(trimChars);
129  if (lastValid != string::npos)
130  {
131  input.erase(lastValid+1, string::npos);
132  }
133  }
134 
135  void
136  NCMLUtil::trimAll(std::vector<std::string>& tokens, const std::string& trimChars /* = WHITESPACE */)
137  {
138  unsigned int num = tokens.size();
139  for (unsigned int i=0; i<num; ++i)
140  {
141  trim(tokens[i], trimChars);
142  }
143  }
144 
145  bool
146  NCMLUtil::toUnsignedInt(const std::string& stringVal, unsigned int& oVal)
147  {
148  bool success = true;
149  oVal = 0;
150  istringstream iss(stringVal);
151  iss >> oVal;
152  if (iss.fail() ||
153  (stringVal[0] == '-') // parsing negatives is locale-dependent, but we DO NOT want them allowed.
154  )
155  {
156  success = false;
157  }
158  return success;
159  }
160 
165  static void populateAttrTableForContainerVariableRecursive(AttrTable* dasTable, Constructor* consVar)
166  {
167  VALID_PTR(dasTable);
168  VALID_PTR(consVar);
169 
170  BESDEBUG("ncml", "Recursively adding attribute tables for children of composite variable " << consVar->name() << "..." << endl);
171  Constructor::Vars_iter endIt = consVar->var_end();
172  for (Constructor::Vars_iter it = consVar->var_begin(); it != endIt; ++it)
173  {
174  BaseType* var = *it;
175  VALID_PTR(var);
176  BESDEBUG("ncml", "Adding attribute table for var: " << var->name() << endl);
177  // Make a new table for the child variable
178  AttrTable* newTable = new AttrTable(var->get_attr_table());
179  // Add it to the DAS's attribute table for the consVar scope.
180  dasTable->append_container(newTable, var->name());
181 
182  // If it's a container type, we need to recurse.
183  if (var->is_constructor_type())
184  {
185  Constructor* child = dynamic_cast<Constructor*>(var);
186  if (!child)
187  {
188  throw BESInternalError("Type cast error", __FILE__, __LINE__);
189  }
190 
191  BESDEBUG("ncml", "Var " << child->name() << " is composite, so recursively adding attribute tables" << endl);
192  populateAttrTableForContainerVariableRecursive(newTable, child);
193  }
194  }
195  }
196 
197 
198  // This is basically the opposite of transfer_attributes.
199  void
200  NCMLUtil::populateDASFromDDS(DAS* das, const DDS& dds_const)
201  {
202  BESDEBUG("ncml", "Populating a DAS from a DDS...." << endl);
203 
204  VALID_PTR(das);
205 
206  // Make sure the DAS is empty to start.
207  das->erase();
208 
209  // dds is semantically const in this function, but the calls to it aren't...
210  DDS& dds = const_cast<DDS&>(dds_const);
211 
212  // First, make sure we don't have a container at top level since we're assuming for now
213  // that we only have one dataset per call (right?)
214  if (dds.container())
215  {
216  BESDEBUG("ncml", "populateDASFromDDS got unexpected container " << dds.container_name() << " and is failing." << endl);
217  throw BESInternalError("Unexpected Container Error creating DAS from DDS in NCMLHandler", __FILE__, __LINE__);
218  }
219 
220  // Copy over the global attributes table
221  //BESDEBUG("ncml", "Coping global attribute tables from DDS to DAS..." << endl);
222  *(das->get_top_level_attributes()) = dds.get_attr_table();
223 
224  // For each variable in the DDS, make a table in the DAS.
225  // If the variable in composite, then recurse
226  // BESDEBUG("ncml", "Adding attribute tables for all DDS variables into DAS recursively..." << endl);
227  DDS::Vars_iter endIt = dds.var_end();
228  for (DDS::Vars_iter it = dds.var_begin(); it != endIt; ++it)
229  {
230  // For each BaseType*, copy its table and add to DAS under its name.
231  BaseType* var = *it;
232  VALID_PTR(var);
233 
234  // BESDEBUG("ncml", "Adding attribute table for variable: " << var->name() << endl);
235  AttrTable* clonedVarTable = new AttrTable(var->get_attr_table());
236  VALID_PTR(clonedVarTable);
237  das->add_table(var->name(), clonedVarTable);
238 
239  // If it's a container type, we need to recurse.
240  if (var->is_constructor_type())
241  {
242  Constructor* consVar = dynamic_cast<Constructor*>(var);
243  if (!consVar)
244  {
245  throw BESInternalError("Type cast error", __FILE__, __LINE__);
246  }
247 
248  populateAttrTableForContainerVariableRecursive(clonedVarTable, consVar);
249  }
250  }
251  }
252 
253  // This function was added since DDS::operator= had some bugs we need to fix.
254  // At that point, we can just use that function, probably.
255  void
256  NCMLUtil::copyVariablesAndAttributesInto(DDS* dds_out, const DDS& dds_in)
257  {
258  VALID_PTR(dds_out);
259 
260  // Avoid obvious bugs
261  if (dds_out == &dds_in)
262  {
263  return;
264  }
265 
266  // handle semantic constness
267  DDS& dds = const_cast<DDS&>(dds_in);
268 
269  // Copy the global attribute table
270  dds_out->get_attr_table() = dds.get_attr_table();
271 
272  // copy the things pointed to by the variable list, not just the pointers
273  // add_var is designed to deepcopy *i, so this should get all the children
274  // as well.
275  for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); ++i)
276  {
277  dds_out->add_var(*i); // add_var() dups the BaseType.
278  }
279  }
280 
281  libdap::DDS*
282  NCMLUtil::getDDSFromEitherResponse(BESDapResponse* response)
283  {
284  DDS* pDDS = 0;
285  BESDDSResponse* pDDXResponse = dynamic_cast<BESDDSResponse*>(response);
286  BESDataDDSResponse* pDataDDSResponse = dynamic_cast<BESDataDDSResponse*>(response);
287 
288  if (pDDXResponse)
289  {
290  pDDS = pDDXResponse->get_dds();
291  }
292  else if (pDataDDSResponse)
293  {
294  pDDS = pDataDDSResponse->get_dds(); // return as superclass ptr
295  }
296  else
297  {
298  pDDS = 0; // return null on error
299  }
300 
301  BESDEBUG("ncml_attr", "DDS' global table contains " << pDDS->get_attr_table().get_size() << " attributes." << endl);
302 
303  return pDDS;
304  }
305 
306  // This little gem takes attributes that have been added to the top level
307  // attribute table (which is allowed in DAP4) and moves them all to a single
308  // container. In DAP2, only containers are allowed at the top level of the
309  // DAS. By _convention_ the name of the global attributes is NC_GLOBAL although
310  // other names are equally valid...
311  //
312  // How this works: The top-level attribute table is filled with various global
313  // attributes. To follow the spec for DAP2 that top-level container must contain
314  // _only_ other containers, each of which must be named. There are four cases...
315  //
316  // jhrg 12/15/11
317  void
318  NCMLUtil::hackGlobalAttributesForDAP2(libdap::AttrTable &global_attributes, const std::string &global_container_name)
319  {
320  if (global_container_name.empty())
321  return;
322 
323  // Cases: 1. only containers at the top --> return
324  // 2. only simple attrs at the top --> move them into one container
325  // 3. mixture of simple and containers --> move the simples into a new container
326  // 4. mixture ... and global_container_name exists --> move simples into that container
327 
328  // Look at the top-level container and see if it has any simple attributes.
329  // If it is empty or has only containers, do nothing.
330  bool simple_attribute_found = false;
331  AttrTable::Attr_iter i = global_attributes.attr_begin();
332  while (!simple_attribute_found && i != global_attributes.attr_end()) {
333  if (!global_attributes.is_container(i))
334  simple_attribute_found = true;
335  ++i;
336  }
337 
338  // Case 1
339  if (!simple_attribute_found)
340  return;
341 #if 0
342  // Now determine if there are _only_ simple attributes
343  bool only_simple_attributes = true;
344  i = global_attributes.attr_begin();
345  while (only_simple_attributes && i != global_attributes.attr_end()) {
346  if (global_attributes.is_container(i))
347  only_simple_attributes = false;
348  ++i;
349  }
350 
351  // Case 2
352  // Note that the assignment operator first clears the destination and
353  // then performs a deep copy, so the 'new_global_attr_container' will completely
354  // replace the existing collection of attributes at teh top-level.
355  if (only_simple_attributes)
356  {
357  AttrTable *new_global_attr_container = new AttrTable();
358  AttrTable *new_attr_container = new_global_attr_container->append_container(global_container_name);
359  *new_attr_container = global_attributes;
360  global_attributes = *new_global_attr_container;
361 
362  return;
363  }
364 #endif
365  // Cases 2, 3 & 4
366  AttrTable *new_attr_container = global_attributes.find_container(global_container_name);
367  if (!new_attr_container)
368  new_attr_container = global_attributes.append_container(global_container_name);
369 
370  // Now we have a destination for all the simple attributes
371  i = global_attributes.attr_begin();
372  while (i != global_attributes.attr_end()) {
373  if (!global_attributes.is_container(i)) {
374  new_attr_container->append_attr(global_attributes.get_name(i),
375  global_attributes.get_type(i), global_attributes.get_attr_vector(i));
376  }
377  ++i;
378  }
379 
380  // Now delete the simple attributes we just moved; they are not deleted in the
381  // above loop because deleting things in a container invalidates iterators
382  i = global_attributes.attr_begin();
383  while (i != global_attributes.attr_end()) {
384  if (!global_attributes.is_container(i)) {
385  global_attributes.del_attr(global_attributes.get_name(i));
386  // delete invalidates iterators; must restart the loop
387  i = global_attributes.attr_begin();
388  }
389  else {
390  ++i;
391  }
392  }
393 
394  return;
395  }
396 
397  void
398  NCMLUtil::setVariableNameProperly(libdap::BaseType* pVar, const std::string& name)
399  {
400  VALID_PTR(pVar);
401  pVar->set_name(name);
402  // if template, set it too since it's used to print dds...
403  BaseType* pTemplate = pVar->var();
404  if (pTemplate)
405  {
406  pTemplate->set_name(name);
407  }
408  }
409 } // namespace ncml_module
exception thrown if inernal error encountered
Represents an OPeNDAP DDS DAP2 data object within the BES.
An abstract superclass for NCMLArray that handles the non-parameterized functionality and allows u...
STL namespace.
static class NCMLUtil overview
Represents an OPeNDAP DataDDS DAP2 data object within the BES.
Represents an OPeNDAP DAP response object within the BES.
#define WHITESPACE
Definition: os_utils.h:97
#define VALID_PTR(ptr)
Definition: NCMLDebug.h:88
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64