libdap  Updated for version 3.20.11
libdap4 is an implementation of OPeNDAP's DAP protocol.
Array.cc
1 // -*- mode: c++; c-basic-offset:4 -*-
2 
3 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
4 // Access Protocol.
5 
6 // Copyright (c) 2002,2003 OPeNDAP, Inc.
7 // Author: James Gallagher <jgallagher@opendap.org>
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 //
23 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
24 
25 // (c) COPYRIGHT URI/MIT 1994-1999
26 // Please read the full copyright statement in the file COPYRIGHT_URI.
27 //
28 // Authors:
29 // jhrg,jimg James Gallagher <jgallagher@gso.uri.edu>
30 
31 // Implementation for Array.
32 //
33 // jhrg 9/13/94
34 
35 #include "config.h"
36 
37 //#define DODS_DEBUG
38 
39 #include <algorithm>
40 #include <functional>
41 #include <sstream>
42 
43 #include "Array.h"
44 #include "Grid.h"
45 
46 #include "D4Attributes.h"
47 #include "DMR.h"
48 #include "D4Dimensions.h"
49 #include "D4Maps.h"
50 #include "D4Group.h"
51 #include "D4EnumDefs.h"
52 #include "D4Enum.h"
53 #include "XMLWriter.h"
54 
55 #include "util.h"
56 #include "debug.h"
57 #include "InternalErr.h"
58 #include "escaping.h"
59 #include "DapIndent.h"
60 
61 using namespace std;
62 
63 namespace libdap {
64 
65 Array::dimension::dimension(D4Dimension *d) :
66  dim(d), use_sdim_for_slice(true)
67 {
68  size = d->size();
69  name = d->name();
70 
71  start = 0;
72  stop = size - 1;
73  stride = 1;
74  c_size = size;
75 }
76 
77 void Array::_duplicate(const Array &a)
78 {
79  _shape = a._shape;
80 
81  // Deep copy the Maps if they are being used.
82  if (a.d_maps) {
83  d_maps = new D4Maps(*(a.d_maps));
84  }
85  else {
86  d_maps = 0;
87  }
88 }
89 
90 // The first method of calculating length works when only one dimension is
91 // constrained and you want the others to appear in total. This is important
92 // when selecting from grids since users may not select from all dimensions
93 // in which case that means they want the whole thing. Array projection
94 // should probably work this way too, but it doesn't. 9/21/2001 jhrg
95 
102 void Array::update_length(int)
103 {
104  int length = 1;
105  for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
106  length *= (*i).c_size;
107  }
108 
109  set_length(length);
110 }
111 
112 // Construct an instance of Array. The (BaseType *) is assumed to be
113 // allocated using new - The dtor for Vector will delete this object.
114 
130 Array::Array(const string &n, BaseType *v, bool is_dap4 /* default:false */) :
131  Vector(n, 0, dods_array_c, is_dap4), d_maps(0)
132 {
133  add_var(v); // Vector::add_var() stores null if v is null
134 }
135 
149 Array::Array(const string &n, const string &d, BaseType *v, bool is_dap4 /* default:false */) :
150  Vector(n, d, 0, dods_array_c, is_dap4), d_maps(0)
151 {
152  add_var(v); // Vector::add_var() stores null if v is null
153 }
154 
156 Array::Array(const Array &rhs) :
157  Vector(rhs)
158 {
159  _duplicate(rhs);
160 }
161 
164 {
165  delete d_maps;
166 }
167 
168 BaseType *
170 {
171  return new Array(*this);
172 }
173 
174 Array &
175 Array::operator=(const Array &rhs)
176 {
177  if (this == &rhs) return *this;
178  Vector::operator=(rhs);
179  _duplicate(rhs);
180  return *this;
181 }
182 
184 {
185  Array *dest = static_cast<Array*>(ptr_duplicate());
186 
187  // If it's already a DAP4 object then we can just return it!
188  if (is_dap4()) {
189  container->add_var_nocopy(dest);
190  }
191 
192  // Process the Array's dimensions, making D4 shared dimensions for
193  // D2 dimensions that are named. If there is just a size, don't make
194  // a D4Dimension (In DAP4 you cannot share a dimension unless it has
195  // a name). jhrg 3/18/14
196 
197  D4Dimensions *root_dims = root->dims();
198  for (Array::Dim_iter dap2_dim = dest->dim_begin(), e = dest->dim_end(); dap2_dim != e; ++dap2_dim) {
199  if (!(*dap2_dim).name.empty()) {
200 
201  // If a D4Dimension with the name already exists, use it.
202  D4Dimension *d4_dim = root_dims->find_dim((*dap2_dim).name);
203  if (!d4_dim) {
204  d4_dim = new D4Dimension((*dap2_dim).name, (*dap2_dim).size);
205  root_dims->add_dim_nocopy(d4_dim);
206  }
207  else {
208  DBG(cerr << __func__ << "() -" <<
209  " Using Existing D4Dimension '"<< d4_dim->name() << "' (" <<
210  (void *)d4_dim << ")"<< endl);;
211 
212  if (d4_dim->size() != (unsigned long) (*dap2_dim).size) {
213  // TODO Revisit this decision. jhrg 3/18/14
214  // ...in case the name/size are different, make a unique D4Dimension
215  // but don't fiddle with the name. Not sure I like this idea, so I'm
216  // making the case explicit (could be rolled in to the block above).
217  // jhrg 3/18/14
218  //
219  // This is causing problems in the FITS handler because there are cases
220  // where two arrays have dimensions with the same name but different
221  // sizes. The deserializing code is using the first size listed, which is
222  // wrong in some cases. I'm going to try making this new D4Dimension using
223  // the dim name along with the variable name. jhrg 8/15/14
224  d4_dim = new D4Dimension((*dap2_dim).name + "_" + name(), (*dap2_dim).size);
225  DBG(cerr << __func__ << "() -" <<
226  " Utilizing Name/Size Conflict Naming Artifice. name'"<< d4_dim->name() << "' (" <<
227  (void *)d4_dim << ")"<< endl);;
228  root_dims->add_dim_nocopy(d4_dim);
229  }
230  }
231  // At this point d4_dim's name and size == those of (*d) so just set
232  // the D4Dimension pointer so it matches the one in the D4Group.
233  (*dap2_dim).dim = d4_dim;
234  }
235 
236  }
237 
238  // Copy the D2 attributes to D4 Attributes
240  dest->set_is_dap4(true);
241  container->add_var_nocopy(dest);
242  DBG(cerr << __func__ << "() - END (array:" << name() << ")" << endl);;
243 }
244 
245 bool Array::is_dap2_grid()
246 {
247  bool is_grid = false;
248  if (this->is_dap4()) {
249  DBG( cerr << __func__ << "() - Array '"<< name() << "' is DAP4 object!" << endl);
250  D4Maps *d4_maps = this->maps();
251  is_grid = d4_maps->size(); // It can't be a grid if there are no maps...
252  if (is_grid) {
253  DBG( cerr << __func__ << "() - Array '"<< name() << "' has D4Maps." << endl);
254  // hmmm this might be a DAP2 Grid...
255  D4Maps::D4MapsIter i = d4_maps->map_begin();
256  D4Maps::D4MapsIter e = d4_maps->map_end();
257  while (i != e) {
258  DBG( cerr << __func__ << "() - Map '"<< (*i)->array()->name() << " has " << (*i)->array()->_shape.size() << " dimension(s)." << endl);
259  if ((*i)->array()->_shape.size() > 1) {
260  is_grid = false;
261  i = e;
262  }
263  else {
264  i++;
265  }
266  }
267  }
268  else {
269  DBG( cerr << __func__ << "() - Array '"<< name() << "' has no D4Maps." << endl);
270  }
271  }
272 
273  DBG( cerr << __func__ << "() - is_grid: "<< (is_grid?"true":"false") << endl);
274  return is_grid;
275 }
276 
292 std::vector<BaseType *> *
294 {
295  DBG(cerr << __func__ << "() - BEGIN Array '"<< name() << "'" << endl);;
296 
297  BaseType *dest;
298  if (!is_dap4()) { // Don't convert a DAP2 thing
299  dest = ptr_duplicate();
300  }
301  else {
302  // At this point we have a DAP4 Array. It have D4Attributes and nothing
303  // in the DAP2 AttrTable (which is held as a reference, defined in BaseType).
304  // This test determines in the D4 Array qualifies as a D2 Grid.
305  if (is_dap2_grid()) {
306  // Oh yay! Grids are special.
307  DBG(cerr << __func__ << "() - Array '"<< name() << "' is dap2 Grid!" << endl);;
308  Grid *g = new Grid(name());
309  dest = g;
310  Array *grid_array = static_cast<Array *>(ptr_duplicate());
311  g->set_array(grid_array);
312 
313  // Fix for HK-403. jhrg 6/17/19
315 
316  // Process the Map Arrays.
317  D4Maps *d4_maps = this->maps();
318  vector<BaseType *> dropped_maps;
319  D4Maps::D4MapsIter miter = d4_maps->map_begin();
320  D4Maps::D4MapsIter end = d4_maps->map_end();
321  for (; miter != end; miter++) {
322  D4Map *d4_map = (*miter);
323  Array *d4_map_array = const_cast<Array*>(d4_map->array());
324  vector<BaseType *> *d2_result = d4_map_array->transform_to_dap2(&(g->get_attr_table()));
325  if (d2_result) {
326  if (d2_result->size() > 1)
327  throw Error(internal_error, "D4Map Array conversion resulted in multiple DAP2 objects.");
328 
329  // TODO - This is probably slow and needs a better pattern. const_cast? static_cast?
330  Array *d2_map_array = dynamic_cast<Array *>((*d2_result)[0]);
331  if (d2_map_array) {
332  if (d2_map_array->dimensions() != 1)
333  throw Error(internal_error, "DAP2 array from D4Map Array conversion has more than 1 dimension.");
334 
335  g->add_map(d2_map_array, false);
336  AttrTable at = d2_map_array->get_attr_table();
337  DBG( cerr << __func__ << "() - " <<
338  "DAS For Grid Map '" << d2_map_array->name() << "':" << endl;
339  at.print(cerr); );
340  }
341  else {
342  throw Error(internal_error, "Unable to interpret returned DAP2 content.");
343  }
344  delete d2_result;
345  }
346  else {
347  dropped_maps.push_back(d4_map_array);
348  }
349  }
350 
351  // Did we have a transform failure?
352  if (!dropped_maps.empty()) {
353  // Yup... tell the story in the attributes.
354  AttrTable *dv_table = Constructor::make_dropped_vars_attr_table(&dropped_maps);
355  dest->get_attr_table().append_container(dv_table, dv_table->get_name());
356  }
357  }
358  else {
359  DBG( cerr << __func__ << "() - Array '"<< name() << "' is not a Grid!" << endl);
360 
361  BaseType *proto = prototype();
362  switch (proto->type()) {
363  case dods_int64_c:
364  case dods_uint64_c:
365  case dods_enum_c:
366  case dods_opaque_c:
367  // For now we punt on these types as they have no easy representation in
368  // the DAP2 data model. By setting this to NULL we cause the Array to be
369  // dropped and this will be reflected in the metadata (DAS).
370  dest = NULL;
371  break;
372 
373  default:
374  // ptr_duplicate() does the Attributes too.
375  dest = ptr_duplicate();
376 
377  // Fix for HK-403. jhrg 6/17/19
378  // Only transform the DAP4 attributes to DAP2 ones if the DAP2 object lacks
379  // attributes. If the new DAP2 variable already has attributes, they were
380  // added by this process (driven by D4Group::transform_to_dap2() and calling
381  // attributes()->transform_to_dap2() will put a second copy of each attribute's
382  // value in the DAP2 AttrTable. This attribute transform code (here and elsewhere)
383  // depends on the AttrTable for a DAP4 variable initially being empty. Once it
384  // contains attributes, the code assumes they were put there by this transform
385  // process. jhrg 6/18/19
386  if (dest->get_attr_table().get_size() == 0) {
388  dest->get_attr_table().set_name(name());
389  }
390 
391  dest->set_is_dap4(false);
392  break;
393  }
394  }
395  }
396 
397  vector<BaseType *> *result;
398  if (dest) {
399  result = new vector<BaseType *>();
400  result->push_back(dest);
401  }
402  else {
403  result = NULL;
404  }
405 
406  DBG( cerr << __func__ << "() - END Array '"<< name() << "'" << endl);;
407  return result;
408 }
409 
421 void Array::update_dimension_pointers(D4Dimensions *old_dims, D4Dimensions *new_dims)
422 {
423  std::vector<dimension>::iterator i = _shape.begin(), e = _shape.end();
424  while (i != e) {
425  D4Dimensions::D4DimensionsIter old_i = old_dims->dim_begin(), old_e = old_dims->dim_end();
426  while (old_i != old_e) {
427  if ((*i).dim == *old_i) {
428  (*i).dim = new_dims->find_dim((*old_i)->name());
429  }
430  ++old_i;
431  }
432 
433  ++i;
434  }
435 }
436 
462 {
463 // If 'v' is an Array, add the template instance to this object and
464 // then copy the dimension information. Odd semantics; I wonder if this
465 //is ever used. jhrg 6/13/12
466  if (v && v->type() == dods_array_c) {
467  Array *a = static_cast<Array*>(v);
468  Vector::add_var(a->var());
469 
470  Dim_iter i = a->dim_begin();
471  Dim_iter i_end = a->dim_end();
472  while (i != i_end) {
474  ++i;
475  }
476  }
477  else {
478  Vector::add_var(v);
479  }
480 }
481 
482 void Array::add_var_nocopy(BaseType *v, Part)
483 {
484 // If 'v' is an Array, add the template instance to this object and
485 // then copy the dimension information. Odd semantics; I wonder if this
486 //is ever used. jhrg 6/13/12
487  if (v && v->type() == dods_array_c) {
488  Array &a = dynamic_cast<Array&>(*v);
489  Vector::add_var_nocopy(a.var());
490  Dim_iter i = a.dim_begin();
491  Dim_iter i_end = a.dim_end();
492  while (i != i_end) {
494  ++i;
495  }
496  }
497  else {
498  Vector::add_var_nocopy(v);
499  }
500 }
501 
513 void Array::append_dim(int size, const string &name)
514 {
515  dimension d(size, www2id(name));
516  _shape.push_back(d);
517 
518  update_length();
519 }
520 
522 {
523  dimension d(/*dim->size(), www2id(dim->name()),*/dim);
524  _shape.push_back(d);
525 
526  update_length();
527 }
528 
534 void Array::prepend_dim(int size, const string& name/* = "" */)
535 {
536  dimension d(size, www2id(name));
537 // Shifts the whole array, but it's tiny in general
538  _shape.insert(_shape.begin(), d);
539 
540  update_length(); // the number is ignored...
541 }
542 
544 {
545  dimension d(/*dim->size(), www2id(dim->name()),*/dim);
546 // Shifts the whole array, but it's tiny in general
547  _shape.insert(_shape.begin(), d);
548 
549  update_length(); // the number is ignored...
550 }
551 
556 {
557  _shape.clear();
558 }
559 
565 void Array::rename_dim(const string &oldName, const string &newName)
566 {
567  std::vector<dimension>::iterator i = _shape.begin(), e = _shape.end();
568  while (i != e) {
569  dimension &d = *i;
570  if (d.name == oldName) {
571  DBG(cerr << "Old name = " << d.name << " newName = " << newName << endl);
572  d.name = newName;
573  }
574 
575  ++i;
576  }
577 }
578 
585 {
586  set_length(-1);
587 
588  for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) {
589  (*i).start = 0;
590  (*i).stop = (*i).size - 1;
591  (*i).stride = 1;
592  (*i).c_size = (*i).size;
593 
594  update_length();
595  }
596 }
597 
608 {
610 }
611 
612 // Note: MS VC++ won't tolerate embedded newlines in strings, hence the \n
613 // is explicit.
614 static const char *array_sss =
615  "Invalid constraint parameters: At least one of the start, stride or stop \n\
616 specified do not match the array variable.";
617 
638 void Array::add_constraint(Dim_iter i, int start, int stride, int stop)
639 {
640  dimension &d = *i;
641 
642 // if stop is -1, set it to the array's max element index
643 // jhrg 12/20/12
644  if (stop == -1) stop = d.size - 1;
645 
646 // Check for bad constraints.
647 // Jose Garcia
648 // Usually invalid data for a constraint is the user's mistake
649 // because they build a wrong URL in the client side.
650  if (start >= d.size || stop >= d.size || stride > d.size || stride <= 0) throw Error(malformed_expr, array_sss);
651 
652  if (((stop - start) / stride + 1) > d.size) throw Error(malformed_expr, array_sss);
653 
654  d.start = start;
655  d.stop = stop;
656  d.stride = stride;
657 
658  d.c_size = (stop - start) / stride + 1;
659 
660  DBG(cerr << "add_constraint: c_size = " << d.c_size << endl);
661 
662  update_length();
663 
664  d.use_sdim_for_slice = false;
665 }
666 
667 void Array::add_constraint(Dim_iter i, D4Dimension *dim)
668 {
669  dimension &d = *i;
670 
671  if (dim->constrained()) add_constraint(i, dim->c_start(), dim->c_stride(), dim->c_stop());
672 
673  dim->set_used_by_projected_var(true);
674 
675 // In this case the value below overrides the value for use_sdim_for_slice
676 // set in the above call. jhrg 12/20/13
677  d.use_sdim_for_slice = true;
678 }
679 
682 {
683  return _shape.begin();
684 }
685 
688 {
689  return _shape.end();
690 }
691 
692 //TODO Many of these methods take a bool parameter that serves no use; remove.
693 
702 unsigned int Array::dimensions(bool /*constrained*/)
703 {
704  return _shape.size();
705 }
706 
724 int Array::dimension_size(Dim_iter i, bool constrained)
725 {
726  int size = 0;
727 
728  if (!_shape.empty()) {
729  if (constrained)
730  size = (*i).c_size;
731  else
732  size = (*i).size;
733  }
734 
735  return size;
736 }
737 
756 int Array::dimension_start(Dim_iter i, bool /*constrained*/)
757 {
758  return (!_shape.empty()) ? (*i).start : 0;
759 }
760 
779 int Array::dimension_stop(Dim_iter i, bool /*constrained*/)
780 {
781  return (!_shape.empty()) ? (*i).stop : 0;
782 }
783 
803 int Array::dimension_stride(Dim_iter i, bool /*constrained*/)
804 {
805  return (!_shape.empty()) ? (*i).stride : 0;
806 }
807 
819 {
820 // Jose Garcia
821 // Since this method is public, it is possible for a user
822 // to call it before the Array object has been properly set
823 // this will cause an exception which is the user's fault.
824 // (User in this context is the developer of the surrogate library.)
825  if (_shape.empty()) throw InternalErr(__FILE__, __LINE__, "*This* array has no dimensions.");
826  return (*i).name;
827 }
828 
829 D4Dimension *
830 Array::dimension_D4dim(Dim_iter i)
831 {
832  return (!_shape.empty()) ? (*i).dim : 0;
833 }
834 
835 D4Maps *
836 Array::maps()
837 {
838  if (!d_maps) d_maps = new D4Maps(this); // init with this as parent
839  return d_maps;
840 }
841 
842 #if 0
849 unsigned int Array::width(bool constrained) const
850 {
851 
852  if (constrained) {
853  // This preserves the original method's semantics when we ask for the
854  // size of the constrained array but no constraint has been applied.
855  // In this case, length will be -1. Wrong, I know...
856  return length() * var()->width(constrained);
857  }
858  else {
859  int length = 1;
860  for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) {
861  length *= dimension_size(i, false);
862  }
863  return length * var()->width(false);
864  }
865 }
866 #endif
867 
868 class PrintD4ArrayDimXMLWriter: public unary_function<Array::dimension&, void> {
869  XMLWriter &xml;
870 // Was this variable constrained using local/direct slicing? i.e., is d_local_constraint set?
871 // If so, don't use shared dimensions; instead emit Dim elements that are anonymous.
872  bool d_constrained;
873 public:
874 
875  PrintD4ArrayDimXMLWriter(XMLWriter &xml, bool c) :
876  xml(xml), d_constrained(c)
877  {
878  }
879 
880  void operator()(Array::dimension &d)
881  {
882  // This duplicates code in D4Dimensions (where D4Dimension::print_dap4() is defined
883  // because of the need to print the constrained size of a dimension. I think that
884  // the constraint information has to be kept here and not in the dimension (since they
885  // are shared dims). Could hack print_dap4() to take the constrained size, however.
886  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Dim") < 0)
887  throw InternalErr(__FILE__, __LINE__, "Could not write Dim element");
888 
889  string name = (d.dim) ? d.dim->fully_qualified_name() : d.name;
890  // If there is a name, there must be a Dimension (named dimension) in scope
891  // so write its name but not its size.
892  if (!d_constrained && !name.empty()) {
893  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name.c_str())
894  < 0) throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
895  }
896  else if (d.use_sdim_for_slice) {
897  assert(!name.empty());
898  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name.c_str())
899  < 0) throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
900  }
901  else {
902  ostringstream size;
903  size << (d_constrained ? d.c_size : d.size);
904  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "size",
905  (const xmlChar*) size.str().c_str()) < 0)
906  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
907  }
908 
909  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
910  throw InternalErr(__FILE__, __LINE__, "Could not end Dim element");
911  }
912 };
913 
914 class PrintD4ConstructorVarXMLWriter: public unary_function<BaseType*, void> {
915  XMLWriter &xml;
916  bool d_constrained;
917 public:
918  PrintD4ConstructorVarXMLWriter(XMLWriter &xml, bool c) :
919  xml(xml), d_constrained(c)
920  {
921  }
922 
923  void operator()(BaseType *btp)
924  {
925  btp->print_dap4(xml, d_constrained);
926  }
927 };
928 
929 class PrintD4MapXMLWriter: public unary_function<D4Map*, void> {
930  XMLWriter &xml;
931 
932 public:
933  PrintD4MapXMLWriter(XMLWriter &xml) :
934  xml(xml)
935  {
936  }
937 
938  void operator()(D4Map *m)
939  {
940  m->print_dap4(xml);
941  }
942 };
943 
949 void Array::print_dap4(XMLWriter &xml, bool constrained /* default: false*/)
950 {
951  if (constrained && !send_p()) return;
952 
953  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) var()->type_name().c_str()) < 0)
954  throw InternalErr(__FILE__, __LINE__, "Could not write " + type_name() + " element");
955 
956  if (!name().empty())
957  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name().c_str()) < 0)
958  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
959 
960 // Hack job... Copied from D4Enum::print_xml_writer. jhrg 11/12/13
961  if (var()->type() == dods_enum_c) {
962  D4Enum *e = static_cast<D4Enum*>(var());
963  string path = e->enumeration()->name();
964  if (e->enumeration()->parent()) {
965  // print the FQN for the enum def; D4Group::FQN() includes the trailing '/'
966  path = static_cast<D4Group*>(e->enumeration()->parent()->parent())->FQN() + path;
967  }
968  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "enum", (const xmlChar*) path.c_str()) < 0)
969  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for enum");
970  }
971 
972  if (prototype()->is_constructor_type()) {
973  Constructor &c = static_cast<Constructor&>(*prototype());
974  for_each(c.var_begin(), c.var_end(), PrintD4ConstructorVarXMLWriter(xml, constrained));
975  // bind2nd(mem_fun_ref(&BaseType::print_dap4), xml));
976  }
977 
978 // Drop the local_constraint which is per-array and use a per-dimension on instead
979  for_each(dim_begin(), dim_end(), PrintD4ArrayDimXMLWriter(xml, constrained));
980 
981  attributes()->print_dap4(xml);
982 
983  for_each(maps()->map_begin(), maps()->map_end(), PrintD4MapXMLWriter(xml));
984 
985  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
986  throw InternalErr(__FILE__, __LINE__, "Could not end " + type_name() + " element");
987 }
988 
1006 void Array::print_decl(FILE *out, string space, bool print_semi, bool constraint_info, bool constrained)
1007 {
1008  ostringstream oss;
1009  print_decl(oss, space, print_semi, constraint_info, constrained);
1010  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
1011 }
1012 
1030 void Array::print_decl(ostream &out, string space, bool print_semi, bool constraint_info, bool constrained)
1031 {
1032  if (constrained && !send_p()) return;
1033 
1034 // print it, but w/o semicolon
1035  var()->print_decl(out, space, false, constraint_info, constrained);
1036 
1037  for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
1038  out << "[";
1039  if ((*i).name != "") {
1040  out << id2www((*i).name) << " = ";
1041  }
1042  if (constrained) {
1043  out << (*i).c_size << "]";
1044  }
1045  else {
1046  out << (*i).size << "]";
1047  }
1048  }
1049 
1050  if (print_semi) {
1051  out << ";\n";
1052  }
1053 }
1054 
1058 void Array::print_xml(FILE *out, string space, bool constrained)
1059 {
1060  XMLWriter xml(space);
1061  print_xml_writer_core(xml, constrained, "Array");
1062  fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
1063 }
1064 
1068 void Array::print_xml(ostream &out, string space, bool constrained)
1069 {
1070  XMLWriter xml(space);
1071  print_xml_writer_core(xml, constrained, "Array");
1072  out << xml.get_doc();
1073 }
1074 
1078 void Array::print_as_map_xml(FILE *out, string space, bool constrained)
1079 {
1080  XMLWriter xml(space);
1081  print_xml_writer_core(xml, constrained, "Map");
1082  fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
1083 }
1084 
1088 void Array::print_as_map_xml(ostream &out, string space, bool constrained)
1089 {
1090  XMLWriter xml(space);
1091  print_xml_writer_core(xml, constrained, "Map");
1092  out << xml.get_doc();
1093 }
1094 
1098 void Array::print_xml_core(FILE *out, string space, bool constrained, string tag)
1099 {
1100  XMLWriter xml(space);
1101  print_xml_writer_core(xml, constrained, tag);
1102  fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
1103 }
1104 
1108 void Array::print_xml_core(ostream &out, string space, bool constrained, string tag)
1109 {
1110  XMLWriter xml(space);
1111  print_xml_writer_core(xml, constrained, tag);
1112  out << xml.get_doc();
1113 }
1114 
1115 void Array::print_xml_writer(XMLWriter &xml, bool constrained)
1116 {
1117  print_xml_writer_core(xml, constrained, "Array");
1118 }
1119 
1120 void Array::print_as_map_xml_writer(XMLWriter &xml, bool constrained)
1121 {
1122  print_xml_writer_core(xml, constrained, "Map");
1123 }
1124 
1125 class PrintArrayDimXMLWriter: public unary_function<Array::dimension&, void> {
1126  XMLWriter &xml;
1127  bool d_constrained;
1128 public:
1129  PrintArrayDimXMLWriter(XMLWriter &xml, bool c) :
1130  xml(xml), d_constrained(c)
1131  {
1132  }
1133 
1134  void operator()(Array::dimension &d)
1135  {
1136  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "dimension") < 0)
1137  throw InternalErr(__FILE__, __LINE__, "Could not write dimension element");
1138 
1139  if (!d.name.empty())
1140  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) d.name.c_str())
1141  < 0) throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1142 
1143  ostringstream size;
1144  size << (d_constrained ? d.c_size : d.size);
1145  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "size", (const xmlChar*) size.str().c_str())
1146  < 0) throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1147 
1148  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
1149  throw InternalErr(__FILE__, __LINE__, "Could not end dimension element");
1150  }
1151 };
1152 
1153 void Array::print_xml_writer_core(XMLWriter &xml, bool constrained, string tag)
1154 {
1155  if (constrained && !send_p()) return;
1156 
1157  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) tag.c_str()) < 0)
1158  throw InternalErr(__FILE__, __LINE__, "Could not write " + tag + " element");
1159 
1160  if (!name().empty())
1161  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name().c_str()) < 0)
1162  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1163 
1165 
1166  BaseType *btp = var();
1167  string tmp_name = btp->name();
1168  btp->set_name("");
1169  btp->print_xml_writer(xml, constrained);
1170  btp->set_name(tmp_name);
1171 
1172  for_each(dim_begin(), dim_end(), PrintArrayDimXMLWriter(xml, constrained));
1173 
1174  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
1175  throw InternalErr(__FILE__, __LINE__, "Could not end " + tag + " element");
1176 }
1177 
1189 unsigned int Array::print_array(FILE *out, unsigned int index, unsigned int dims, unsigned int shape[])
1190 {
1191  ostringstream oss;
1192  unsigned int i = print_array(oss, index, dims, shape);
1193  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
1194 
1195  return i;
1196 }
1197 
1209 unsigned int Array::print_array(ostream &out, unsigned int index, unsigned int dims, unsigned int shape[])
1210 {
1211  if (dims == 1) {
1212  out << "{";
1213 
1214  // Added test in case this method is passed an array with no elements. jhrg 1/27/16
1215  if (shape[0] >= 1) {
1216  for (unsigned i = 0; i < shape[0] - 1; ++i) {
1217  var(index++)->print_val(out, "", false);
1218  out << ", ";
1219  }
1220  var(index++)->print_val(out, "", false);
1221  }
1222 
1223  out << "}";
1224 
1225  return index;
1226  }
1227  else {
1228  out << "{";
1229  // Fixed an off-by-one error in the following loop. Since the array
1230  // length is shape[dims-1]-1 *and* since we want one less dimension
1231  // than that, the correct limit on this loop is shape[dims-2]-1. From
1232  // Todd Karakasian.
1233  //
1234  // The saga continues; the loop test should be `i < shape[0]-1'. jhrg
1235  // 9/12/96.
1236  //
1237  // For arrays that hold zero values but have rank > 1, the print out
1238  // may look a little odd (e.g., x[4][0] will print as { {}, {}, {}, {} })
1239  // but it's not wrong and this is really for debugging mostly. jhrg 1/28/16
1240  if (shape[0] > 0) {
1241  for (unsigned i = 0; i < shape[0] - 1; ++i) {
1242  index = print_array(out, index, dims - 1, shape + 1);
1243  out << ",";
1244  }
1245 
1246  index = print_array(out, index, dims - 1, shape + 1);
1247  }
1248 
1249  out << "}";
1250 
1251  return index;
1252  }
1253 }
1254 
1255 void Array::print_val(FILE *out, string space, bool print_decl_p)
1256 {
1257  ostringstream oss;
1258  print_val(oss, space, print_decl_p);
1259  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
1260 }
1261 
1262 void Array::print_val(ostream &out, string space, bool print_decl_p)
1263 {
1264 // print the declaration if print decl is true.
1265 // for each dimension,
1266 // for each element,
1267 // print the array given its shape, number of dimensions.
1268 // Add the `;'
1269 
1270  if (print_decl_p) {
1271  print_decl(out, space, false, false, false);
1272  out << " = ";
1273  }
1274 
1275  unsigned int *shape = new unsigned int[dimensions(true)];
1276  unsigned int index = 0;
1277  for (Dim_iter i = _shape.begin(); i != _shape.end() && index < dimensions(true); ++i)
1278  shape[index++] = dimension_size(i, true);
1279 
1280  print_array(out, 0, dimensions(true), shape);
1281 
1282  delete[] shape;
1283  shape = 0;
1284 
1285  if (print_decl_p) {
1286  out << ";\n";
1287  }
1288 }
1289 
1299 bool Array::check_semantics(string &msg, bool)
1300 {
1301  bool sem = BaseType::check_semantics(msg) && !_shape.empty();
1302 
1303  if (!sem) msg = "An array variable must have dimensions";
1304 
1305  return sem;
1306 }
1307 
1316 void Array::dump(ostream &strm) const
1317 {
1318  strm << DapIndent::LMarg << "Array::dump - (" << (void *) this << ")" << endl;
1319  DapIndent::Indent();
1320  Vector::dump(strm);
1321  strm << DapIndent::LMarg << "shape:" << endl;
1322  DapIndent::Indent();
1323  Dim_citer i = _shape.begin();
1324  Dim_citer ie = _shape.end();
1325  unsigned int dim_num = 0;
1326  for (; i != ie; i++) {
1327  strm << DapIndent::LMarg << "dimension " << dim_num++ << ":" << endl;
1328  DapIndent::Indent();
1329  strm << DapIndent::LMarg << "name: " << (*i).name << endl;
1330  strm << DapIndent::LMarg << "size: " << (*i).size << endl;
1331  strm << DapIndent::LMarg << "start: " << (*i).start << endl;
1332  strm << DapIndent::LMarg << "stop: " << (*i).stop << endl;
1333  strm << DapIndent::LMarg << "stride: " << (*i).stride << endl;
1334  strm << DapIndent::LMarg << "constrained size: " << (*i).c_size << endl;
1335  DapIndent::UnIndent();
1336  }
1337  DapIndent::UnIndent();
1338  DapIndent::UnIndent();
1339 }
1340 
1341 } // namespace libdap
1342 
A multidimensional array of identical data types.
Definition: Array.h:113
virtual int dimension_start(Dim_iter i, bool constrained=false)
Return the start index of a dimension.
Definition: Array.cc:756
virtual void clear_constraint()
Clears the projection; add each projected dimension explicitly using add_constraint.
Definition: Array.cc:607
virtual void dump(ostream &strm) const
dumps information about this object
Definition: Array.cc:1316
virtual std::vector< BaseType * > * transform_to_dap2(AttrTable *parent_attr_table)
Transforms this instance of a D4Array into the corresponding DAP2 object.
Definition: Array.cc:293
Dim_iter dim_end()
Definition: Array.cc:687
virtual void transform_to_dap4(D4Group *root, Constructor *container)
DAP2 to DAP4 transform.
Definition: Array.cc:183
virtual BaseType * ptr_duplicate()
Definition: Array.cc:169
virtual void print_xml(ostream &out, string space=" ", bool constrained=false)
Definition: Array.cc:1068
unsigned int print_array(FILE *out, unsigned int index, unsigned int dims, unsigned int shape[])
Print the value given the current constraint.
Definition: Array.cc:1189
virtual int dimension_stop(Dim_iter i, bool constrained=false)
Return the stop index of the constraint.
Definition: Array.cc:779
virtual void update_length(int size=0)
Definition: Array.cc:102
virtual void add_constraint(Dim_iter i, int start, int stride, int stop)
Adds a constraint to an Array dimension.
Definition: Array.cc:638
virtual string dimension_name(Dim_iter i)
Returns the name of the specified dimension.
Definition: Array.cc:818
virtual void print_decl(ostream &out, string space=" ", bool print_semi=true, bool constraint_info=false, bool constrained=false)
Prints a DDS entry for the Array.
Definition: Array.cc:1030
void append_dim(int size, const string &name="")
Add a dimension of a given size.
Definition: Array.cc:513
std::vector< dimension >::iterator Dim_iter
Definition: Array.h:206
void rename_dim(const string &oldName="", const string &newName="")
Renames dimension.
Definition: Array.cc:565
virtual int dimension_size(Dim_iter i, bool constrained=false)
Returns the size of the dimension.
Definition: Array.cc:724
void clear_all_dims()
Definition: Array.cc:555
virtual void print_dap4(XMLWriter &xml, bool constrained=false)
Print the DAP4 representation of an array.
Definition: Array.cc:949
virtual bool check_semantics(string &msg, bool all=false)
Check semantic features of the Array.
Definition: Array.cc:1299
std::vector< dimension >::const_iterator Dim_citer
Definition: Array.h:198
virtual void print_as_map_xml(ostream &out, string space=" ", bool constrained=false)
Definition: Array.cc:1088
virtual void reset_constraint()
Reset constraint to select entire array.
Definition: Array.cc:584
void add_var(BaseType *v, Part p=nil)
Add the BaseType pointer to this constructor type instance.
Definition: Array.cc:461
virtual ~Array()
The Array destructor.
Definition: Array.cc:163
virtual void print_xml_core(FILE *out, string space, bool constrained, string tag)
Definition: Array.cc:1098
void prepend_dim(int size, const string &name="")
Definition: Array.cc:534
Dim_iter dim_begin()
Definition: Array.cc:681
Array(const string &n, BaseType *v, bool is_dap4=false)
Array constructor.
Definition: Array.cc:130
virtual void print_xml_writer(XMLWriter &xml, bool constrained=false)
Definition: Array.cc:1115
virtual void print_val(ostream &out, string space="", bool print_decl_p=true)
Prints the value of the variable.
Definition: Array.cc:1262
virtual unsigned int dimensions(bool constrained=false)
Return the total number of dimensions in the array.
Definition: Array.cc:702
virtual int dimension_stride(Dim_iter i, bool constrained=false)
Returns the stride value of the constraint.
Definition: Array.cc:803
Contains the attributes for a dataset.
Definition: AttrTable.h:143
virtual AttrTable * append_container(const string &name)
Add a container to the attribute table.
Definition: AttrTable.cc:410
virtual void set_name(const string &n)
Set the name of this attribute table.
Definition: AttrTable.cc:245
virtual string get_name() const
Get the name of this attribute table.
Definition: AttrTable.cc:238
void print_xml_writer(XMLWriter &xml)
Definition: AttrTable.cc:1425
virtual void print(FILE *out, string pad=" ", bool dereference=false)
Prints the attribute table.
Definition: AttrTable.cc:1243
virtual unsigned int get_size() const
Get the number of entries in this attribute table.
Definition: AttrTable.cc:231
The basic data type for the DODS DAP types.
Definition: BaseType.h:118
virtual string type_name() const
Returns the type of the class instance as a string.
Definition: BaseType.cc:375
virtual AttrTable & get_attr_table()
Definition: BaseType.cc:578
virtual string name() const
Returns the name of the class instance.
Definition: BaseType.cc:316
virtual void print_decl(FILE *out, string space=" ", bool print_semi=true, bool constraint_info=false, bool constrained=false)
Print an ASCII representation of the variable structure.
Definition: BaseType.cc:999
virtual unsigned int width(bool constrained=false) const
How many bytes does this variable use Return the number of bytes of storage this variable uses....
Definition: BaseType.cc:1295
virtual bool is_constructor_type() const
Returns true if the instance is a constructor (i.e., Structure, Sequence or Grid) type variable.
Definition: BaseType.cc:408
virtual D4Attributes * attributes()
Definition: BaseType.cc:595
virtual std::string FQN() const
Definition: BaseType.cc:328
virtual bool send_p()
Should this variable be sent?
Definition: BaseType.cc:550
virtual bool check_semantics(string &msg, bool all=false)
Compare an object's current state with the semantics of its type.
Definition: BaseType.cc:1205
BaseType(const string &n, const Type &t, bool is_dap4=false)
The BaseType constructor.
Definition: BaseType.cc:126
virtual Type type() const
Returns the type of the class instance.
Definition: BaseType.cc:361
virtual void print_val(FILE *out, string space="", bool print_decl_p=true)
Prints the value of the variable.
Definition: BaseType.cc:1086
Vars_iter var_end()
Definition: Constructor.cc:280
void add_var_nocopy(BaseType *bt, Part part=nil) override
Definition: Constructor.cc:345
Vars_iter var_begin()
Definition: Constructor.cc:272
void transform_to_dap4(AttrTable &at)
copy attributes from DAP2 to DAP4
void transform_attrs_to_dap2(AttrTable *d2_attr_table)
Copy the attributes from this D4Attributes object to a DAP2 AttrTable.
vector< D4Dimension * >::iterator D4DimensionsIter
Iterator used for D4Dimensions.
Definition: D4Dimensions.h:122
void add_dim_nocopy(D4Dimension *dim)
Definition: D4Dimensions.h:160
D4DimensionsIter dim_end()
Get an iterator to the end of the dimensions.
Definition: D4Dimensions.h:166
D4DimensionsIter dim_begin()
Get an iterator to the start of the dimensions.
Definition: D4Dimensions.h:163
Holds a DAP4 enumeration.
Definition: D4Enum.h:56
D4Dimensions * dims()
Get the dimensions defined for this Group.
Definition: D4Group.h:83
A class for error processing.
Definition: Error.h:94
Holds the Grid data type.
Definition: Grid.h:123
virtual void set_array(Array *p_new_arr)
Definition: Grid.cc:368
virtual Array * add_map(Array *p_new_map, bool add_copy)
Definition: Grid.cc:434
A class for software fault reporting.
Definition: InternalErr.h:65
Holds a one-dimensional collection of DAP2 data types.
Definition: Vector.h:81
virtual void add_var(BaseType *v, Part p=nil)
Add the BaseType pointer to this constructor type instance.
Definition: Vector.cc:1961
virtual void set_length(int l)
Definition: Vector.cc:555
virtual int length() const
Definition: Vector.cc:548
virtual unsigned int width(bool constrained=false) const
Returns the width of the data, in bytes.
Definition: Vector.cc:536
virtual void dump(ostream &strm) const
dumps information about this object
Definition: Vector.cc:2044
virtual BaseType * var(const string &name="", bool exact_match=true, btp_stack *s=0)
Definition: Vector.cc:433
top level DAP object to house generic methods
Definition: AlarmHandler.h:36
string www2id(const string &in, const string &escape, const string &except)
Definition: escaping.cc:220
Part
Names the parts of multi-section constructor data types.
Definition: Type.h:48
string id2www(string in, const string &allowable)
Definition: escaping.cc:153
int stride
The constraint stride.
Definition: Array.h:150
string name
The name of this dimension.
Definition: Array.h:136
bool use_sdim_for_slice
Used to control printing the DMR in data responses.
Definition: Array.h:146
int start
The constraint start index.
Definition: Array.h:148
int size
The unconstrained dimension size.
Definition: Array.h:135
int stop
The constraint end index.
Definition: Array.h:149
int c_size
Size of dimension once constrained.
Definition: Array.h:151