libdap  Updated for version 3.20.6
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  // d_maps = a.d_maps ? new D4Maps(*(a.d_maps)) : 0;
89 }
90 
91 // The first method of calculating length works when only one dimension is
92 // constrained and you want the others to appear in total. This is important
93 // when selecting from grids since users may not select from all dimensions
94 // in which case that means they want the whole thing. Array projection
95 // should probably work this way too, but it doesn't. 9/21/2001 jhrg
96 
104 {
105  int length = 1;
106  for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
107 #if 0
108  // If the size of any dimension is zero, then the array is not
109  // capable of storing any values. jhrg 1/28/16
110  length *= (*i).c_size > 0 ? (*i).c_size : 1;
111 #endif
112  length *= (*i).c_size;
113  }
114 
115  set_length(length);
116 }
117 
118 // Construct an instance of Array. The (BaseType *) is assumed to be
119 // allocated using new - The dtor for Vector will delete this object.
120 
136 Array::Array(const string &n, BaseType *v, bool is_dap4 /* default:false */) :
137  Vector(n, 0, dods_array_c, is_dap4), d_maps(0)
138 {
139  add_var(v); // Vector::add_var() stores null if v is null
140 }
141 
155 Array::Array(const string &n, const string &d, BaseType *v, bool is_dap4 /* default:false */) :
156  Vector(n, d, 0, dods_array_c, is_dap4), d_maps(0)
157 {
158  add_var(v); // Vector::add_var() stores null if v is null
159 }
160 
162 Array::Array(const Array &rhs) :
163  Vector(rhs)
164 {
165  _duplicate(rhs);
166 }
167 
170 {
171  delete d_maps;
172 }
173 
174 BaseType *
176 {
177  return new Array(*this);
178 }
179 
180 Array &
181 Array::operator=(const Array &rhs)
182 {
183  if (this == &rhs) return *this;
184 
185  dynamic_cast<Vector &>(*this) = rhs;
186 
187  _duplicate(rhs);
188 
189  return *this;
190 }
191 
193 {
194  Array *dest = static_cast<Array*>(ptr_duplicate());
195 
196  // If it's already a DAP4 object then we can just return it!
197  if (is_dap4()) {
198  container->add_var_nocopy(dest);
199  }
200 
201  // Process the Array's dimensions, making D4 shared dimensions for
202  // D2 dimensions that are named. If there is just a size, don't make
203  // a D4Dimension (In DAP4 you cannot share a dimension unless it has
204  // a name). jhrg 3/18/14
205 
206  D4Dimensions *root_dims = root->dims();
207  for (Array::Dim_iter dap2_dim = dest->dim_begin(), e = dest->dim_end(); dap2_dim != e; ++dap2_dim) {
208  if (!(*dap2_dim).name.empty()) {
209 
210  // If a D4Dimension with the name already exists, use it.
211  D4Dimension *d4_dim = root_dims->find_dim((*dap2_dim).name);
212  if (!d4_dim) {
213  d4_dim = new D4Dimension((*dap2_dim).name, (*dap2_dim).size);
214  root_dims->add_dim_nocopy(d4_dim);
215  }
216  else {
217  DBG(cerr << __func__ << "() -" <<
218  " Using Existing D4Dimension '"<< d4_dim->name() << "' (" <<
219  (void *)d4_dim << ")"<< endl);;
220 
221  if (d4_dim->size() != (unsigned long) (*dap2_dim).size) {
222  // TODO Revisit this decision. jhrg 3/18/14
223  // ...in case the name/size are different, make a unique D4Dimension
224  // but don't fiddle with the name. Not sure I like this idea, so I'm
225  // making the case explicit (could be rolled in to the block above).
226  // jhrg 3/18/14
227  //
228  // This is causing problems in the FITS handler because there are cases
229  // where two arrays have dimensions with the same name but different
230  // sizes. The deserializing code is using the first size listed, which is
231  // wrong in some cases. I'm going to try making this new D4Dimension using
232  // the dim name along with the variable name. jhrg 8/15/14
233  d4_dim = new D4Dimension((*dap2_dim).name + "_" + name(), (*dap2_dim).size);
234  DBG(cerr << __func__ << "() -" <<
235  " Utilizing Name/Size Conflict Naming Artifice. name'"<< d4_dim->name() << "' (" <<
236  (void *)d4_dim << ")"<< endl);;
237  root_dims->add_dim_nocopy(d4_dim);
238  }
239  }
240  // At this point d4_dim's name and size == those of (*d) so just set
241  // the D4Dimension pointer so it matches the one in the D4Group.
242  (*dap2_dim).dim = d4_dim;
243  }
244 
245  }
246 
247  // Copy the D2 attributes to D4 Attributes
249  dest->set_is_dap4(true);
250  container->add_var_nocopy(dest);
251  DBG(cerr << __func__ << "() - END (array:" << name() << ")" << endl);;
252 }
253 
254 bool Array::is_dap2_grid()
255 {
256  bool is_grid = false;
257  if (this->is_dap4()) {
258  DBG( cerr << __func__ << "() - Array '"<< name() << "' is DAP4 object!" << endl);
259  D4Maps *d4_maps = this->maps();
260  is_grid = d4_maps->size(); // It can't be a grid if there are no maps...
261  if (is_grid) {
262  DBG( cerr << __func__ << "() - Array '"<< name() << "' has D4Maps." << endl);
263  // hmmm this might be a DAP2 Grid...
264  D4Maps::D4MapsIter i = d4_maps->map_begin();
265  D4Maps::D4MapsIter e = d4_maps->map_end();
266  while (i != e) {
267  DBG( cerr << __func__ << "() - Map '"<< (*i)->array()->name() << " has " << (*i)->array()->_shape.size() << " dimension(s)." << endl);
268  if ((*i)->array()->_shape.size() > 1) {
269  is_grid = false;
270  i = e;
271  }
272  else {
273  i++;
274  }
275  }
276  }
277  else {
278  DBG( cerr << __func__ << "() - Array '"<< name() << "' has no D4Maps." << endl);
279  }
280  }
281 
282  DBG( cerr << __func__ << "() - is_grid: "<< (is_grid?"true":"false") << endl);
283  return is_grid;
284 }
285 
301 std::vector<BaseType *> *
303 {
304  DBG(cerr << __func__ << "() - BEGIN Array '"<< name() << "'" << endl);;
305 
306  BaseType *dest;
307  if (!is_dap4()) { // Don't convert a DAP2 thing
308  dest = ptr_duplicate();
309  }
310  else {
311  // At this point we have a DAP4 Array. It have D4Attributes and nothing
312  // in the DAP2 AttrTable (which is held as a reference, defined in BaseType).
313  // This test determines in the D4 Array qualifies as a D2 Grid.
314  if (is_dap2_grid()) {
315  // Oh yay! Grids are special.
316  DBG(cerr << __func__ << "() - Array '"<< name() << "' is dap2 Grid!" << endl);;
317  Grid *g = new Grid(name());
318  dest = g;
319  Array *grid_array = static_cast<Array *>(ptr_duplicate());
320  g->set_array(grid_array);
321 
322  // Fix for HK-403. jhrg 6/17/19
324 
325  // Process the Map Arrays.
326  D4Maps *d4_maps = this->maps();
327  vector<BaseType *> dropped_maps;
328  D4Maps::D4MapsIter miter = d4_maps->map_begin();
329  D4Maps::D4MapsIter end = d4_maps->map_end();
330  for (; miter != end; miter++) {
331  D4Map *d4_map = (*miter);
332  Array *d4_map_array = const_cast<Array*>(d4_map->array());
333  vector<BaseType *> *d2_result = d4_map_array->transform_to_dap2(&(g->get_attr_table()));
334  if (d2_result) {
335  if (d2_result->size() > 1)
336  throw Error(internal_error, "D4Map Array conversion resulted in multiple DAP2 objects.");
337 
338  // TODO - This is probably slow and needs a better pattern. const_cast? static_cast?
339  Array *d2_map_array = dynamic_cast<Array *>((*d2_result)[0]);
340  if (d2_map_array) {
341  if (d2_map_array->dimensions() != 1)
342  throw Error(internal_error, "DAP2 array from D4Map Array conversion has more than 1 dimension.");
343 
344  g->add_map(d2_map_array, false);
345  AttrTable at = d2_map_array->get_attr_table();
346  DBG( cerr << __func__ << "() - " <<
347  "DAS For Grid Map '" << d2_map_array->name() << "':" << endl;
348  at.print(cerr); );
349  }
350  else {
351  throw Error(internal_error, "Unable to interpret returned DAP2 content.");
352  }
353  delete d2_result;
354  }
355  else {
356  dropped_maps.push_back(d4_map_array);
357  }
358  }
359 
360  // Did we have a transform failure?
361  if (!dropped_maps.empty()) {
362  // Yup... tell the story in the attributes.
363  AttrTable *dv_table = Constructor::make_dropped_vars_attr_table(&dropped_maps);
364  dest->get_attr_table().append_container(dv_table, dv_table->get_name());
365  }
366  }
367  else {
368  DBG( cerr << __func__ << "() - Array '"<< name() << "' is not a Grid!" << endl);
369 
370  BaseType *proto = prototype();
371  switch (proto->type()) {
372  case dods_int64_c:
373  case dods_uint64_c:
374  case dods_enum_c:
375  case dods_opaque_c:
376  // For now we punt on these types as they have no easy representation in
377  // the DAP2 data model. By setting this to NULL we cause the Array to be
378  // dropped and this will be reflected in the metadata (DAS).
379  dest = NULL;
380  break;
381 
382  default:
383  // ptr_duplicate() does the Attributes too.
384  dest = ptr_duplicate();
385 
386  // Fix for HK-403. jhrg 6/17/19
387  // Only transform the DAP4 attributes to DAP2 ones if the DAP2 object lacks
388  // attributes. If the new DAP2 variable already has attributes, they were
389  // added by this process (driven by D4Group::transform_to_dap2() and calling
390  // attributes()->transform_to_dap2() will put a second copy of each attribute's
391  // value in the DAP2 AttrTable. This attribute transform code (here and elsewhere)
392  // depends on the AttrTable for a DAP4 variable initially being empty. Once it
393  // contains attributes, the code assumes they were put there by this transform
394  // process. jhrg 6/18/19
395  if (dest->get_attr_table().get_size() == 0) {
397  dest->get_attr_table().set_name(name());
398  }
399 
400  dest->set_is_dap4(false);
401  break;
402  }
403  }
404  }
405 
406  vector<BaseType *> *result;
407  if (dest) {
408  result = new vector<BaseType *>();
409  result->push_back(dest);
410  }
411  else {
412  result = NULL;
413  }
414 
415  DBG( cerr << __func__ << "() - END Array '"<< name() << "'" << endl);;
416  return result;
417 }
418 
430 void Array::update_dimension_pointers(D4Dimensions *old_dims, D4Dimensions *new_dims)
431 {
432  std::vector<dimension>::iterator i = _shape.begin(), e = _shape.end();
433  while (i != e) {
434  D4Dimensions::D4DimensionsIter old_i = old_dims->dim_begin(), old_e = old_dims->dim_end();
435  while (old_i != old_e) {
436  if ((*i).dim == *old_i) {
437  (*i).dim = new_dims->find_dim((*old_i)->name());
438  }
439  ++old_i;
440  }
441 
442  ++i;
443  }
444 }
445 
471 {
472 // If 'v' is an Array, add the template instance to this object and
473 // then copy the dimension information. Odd semantics; I wonder if this
474 //is ever used. jhrg 6/13/12
475  if (v && v->type() == dods_array_c) {
476  Array *a = static_cast<Array*>(v);
477  Vector::add_var(a->var());
478 
479  Dim_iter i = a->dim_begin();
480  Dim_iter i_end = a->dim_end();
481  while (i != i_end) {
483  ++i;
484  }
485  }
486  else {
487  Vector::add_var(v);
488  }
489 }
490 
491 void Array::add_var_nocopy(BaseType *v, Part)
492 {
493 // If 'v' is an Array, add the template instance to this object and
494 // then copy the dimension information. Odd semantics; I wonder if this
495 //is ever used. jhrg 6/13/12
496  if (v && v->type() == dods_array_c) {
497  Array &a = dynamic_cast<Array&>(*v);
498  Vector::add_var_nocopy(a.var());
499  Dim_iter i = a.dim_begin();
500  Dim_iter i_end = a.dim_end();
501  while (i != i_end) {
503  ++i;
504  }
505  }
506  else {
507  Vector::add_var_nocopy(v);
508  }
509 }
510 
522 void Array::append_dim(int size, const string &name)
523 {
524  dimension d(size, www2id(name));
525  _shape.push_back(d);
526 
527  update_length();
528 }
529 
531 {
532  dimension d(/*dim->size(), www2id(dim->name()),*/dim);
533  _shape.push_back(d);
534 
535  update_length();
536 }
537 
543 void Array::prepend_dim(int size, const string& name/* = "" */)
544 {
545  dimension d(size, www2id(name));
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 
553 {
554  dimension d(/*dim->size(), www2id(dim->name()),*/dim);
555 // Shifts the whole array, but it's tiny in general
556  _shape.insert(_shape.begin(), d);
557 
558  update_length(); // the number is ignored...
559 }
560 
565 {
566  _shape.clear();
567 }
568 
574 void Array::rename_dim(const string &oldName, const string &newName)
575 {
576  std::vector<dimension>::iterator i = _shape.begin(), e = _shape.end();
577  while (i != e) {
578  dimension &d = *i;
579  if (d.name == oldName) {
580  DBG(cerr << "Old name = " << d.name << " newName = " << newName << endl);
581  d.name = newName;
582  }
583 
584  ++i;
585  }
586 }
587 
594 {
595  set_length(-1);
596 
597  for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) {
598  (*i).start = 0;
599  (*i).stop = (*i).size - 1;
600  (*i).stride = 1;
601  (*i).c_size = (*i).size;
602 
603  update_length();
604  }
605 }
606 
617 {
619 }
620 
621 // Note: MS VC++ won't tolerate embedded newlines in strings, hence the \n
622 // is explicit.
623 static const char *array_sss =
624  "Invalid constraint parameters: At least one of the start, stride or stop \n\
625 specified do not match the array variable.";
626 
647 void Array::add_constraint(Dim_iter i, int start, int stride, int stop)
648 {
649  dimension &d = *i;
650 
651 // if stop is -1, set it to the array's max element index
652 // jhrg 12/20/12
653  if (stop == -1) stop = d.size - 1;
654 
655 // Check for bad constraints.
656 // Jose Garcia
657 // Usually invalid data for a constraint is the user's mistake
658 // because they build a wrong URL in the client side.
659  if (start >= d.size || stop >= d.size || stride > d.size || stride <= 0) throw Error(malformed_expr, array_sss);
660 
661  if (((stop - start) / stride + 1) > d.size) throw Error(malformed_expr, array_sss);
662 
663  d.start = start;
664  d.stop = stop;
665  d.stride = stride;
666 
667  d.c_size = (stop - start) / stride + 1;
668 
669  DBG(cerr << "add_constraint: c_size = " << d.c_size << endl);
670 
671  update_length();
672 
673  d.use_sdim_for_slice = false;
674 }
675 
677 {
678  dimension &d = *i;
679 
680  if (dim->constrained()) add_constraint(i, dim->c_start(), dim->c_stride(), dim->c_stop());
681 
682  dim->set_used_by_projected_var(true);
683 
684 // In this case the value below overrides the value for use_sdim_for_slice
685 // set in the above call. jhrg 12/20/13
686  d.use_sdim_for_slice = true;
687 }
688 
691 {
692  return _shape.begin();
693 }
694 
697 {
698  return _shape.end();
699 }
700 
701 //TODO Many of these methods take a bool parameter that serves no use; remove.
702 
711 unsigned int Array::dimensions(bool /*constrained*/)
712 {
713  return _shape.size();
714 }
715 
733 int Array::dimension_size(Dim_iter i, bool constrained)
734 {
735  int size = 0;
736 
737  if (!_shape.empty()) {
738  if (constrained)
739  size = (*i).c_size;
740  else
741  size = (*i).size;
742  }
743 
744  return size;
745 }
746 
765 int Array::dimension_start(Dim_iter i, bool /*constrained*/)
766 {
767  return (!_shape.empty()) ? (*i).start : 0;
768 }
769 
788 int Array::dimension_stop(Dim_iter i, bool /*constrained*/)
789 {
790  return (!_shape.empty()) ? (*i).stop : 0;
791 }
792 
812 int Array::dimension_stride(Dim_iter i, bool /*constrained*/)
813 {
814  return (!_shape.empty()) ? (*i).stride : 0;
815 }
816 
828 {
829 // Jose Garcia
830 // Since this method is public, it is possible for a user
831 // to call it before the Array object has been properly set
832 // this will cause an exception which is the user's fault.
833 // (User in this context is the developer of the surrogate library.)
834  if (_shape.empty()) throw InternalErr(__FILE__, __LINE__, "*This* array has no dimensions.");
835  return (*i).name;
836 }
837 
838 D4Dimension *
839 Array::dimension_D4dim(Dim_iter i)
840 {
841  return (!_shape.empty()) ? (*i).dim : 0;
842 }
843 
844 D4Maps *
845 Array::maps()
846 {
847  if (!d_maps) d_maps = new D4Maps(this); // init with this as parent
848  return d_maps;
849 }
850 
851 #if 0
852 
858 unsigned int Array::width(bool constrained) const
859 {
860 
861  if (constrained) {
862  // This preserves the original method's semantics when we ask for the
863  // size of the constrained array but no constraint has been applied.
864  // In this case, length will be -1. Wrong, I know...
865  return length() * var()->width(constrained);
866  }
867  else {
868  int length = 1;
869  for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) {
870  length *= dimension_size(i, false);
871  }
872  return length * var()->width(false);
873  }
874 }
875 #endif
876 
877 class PrintD4ArrayDimXMLWriter: public unary_function<Array::dimension&, void> {
878  XMLWriter &xml;
879 // Was this variable constrained using local/direct slicing? i.e., is d_local_constraint set?
880 // If so, don't use shared dimensions; instead emit Dim elements that are anonymous.
881  bool d_constrained;
882 public:
883 
884  PrintD4ArrayDimXMLWriter(XMLWriter &xml, bool c) :
885  xml(xml), d_constrained(c)
886  {
887  }
888 
889  void operator()(Array::dimension &d)
890  {
891  // This duplicates code in D4Dimensions (where D4Dimension::print_dap4() is defined
892  // because of the need to print the constrained size of a dimension. I think that
893  // the constraint information has to be kept here and not in the dimension (since they
894  // are shared dims). Could hack print_dap4() to take the constrained size, however.
895  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Dim") < 0)
896  throw InternalErr(__FILE__, __LINE__, "Could not write Dim element");
897 
898  string name = (d.dim) ? d.dim->fully_qualified_name() : d.name;
899  // If there is a name, there must be a Dimension (named dimension) in scope
900  // so write its name but not its size.
901  if (!d_constrained && !name.empty()) {
902  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name.c_str())
903  < 0) throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
904  }
905  else if (d.use_sdim_for_slice) {
906  assert(!name.empty());
907  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name.c_str())
908  < 0) throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
909  }
910  else {
911  ostringstream size;
912  size << (d_constrained ? d.c_size : d.size);
913  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "size",
914  (const xmlChar*) size.str().c_str()) < 0)
915  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
916  }
917 
918  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
919  throw InternalErr(__FILE__, __LINE__, "Could not end Dim element");
920  }
921 };
922 
923 class PrintD4ConstructorVarXMLWriter: public unary_function<BaseType*, void> {
924  XMLWriter &xml;
925  bool d_constrained;
926 public:
927  PrintD4ConstructorVarXMLWriter(XMLWriter &xml, bool c) :
928  xml(xml), d_constrained(c)
929  {
930  }
931 
932  void operator()(BaseType *btp)
933  {
934  btp->print_dap4(xml, d_constrained);
935  }
936 };
937 
938 class PrintD4MapXMLWriter: public unary_function<D4Map*, void> {
939  XMLWriter &xml;
940 
941 public:
942  PrintD4MapXMLWriter(XMLWriter &xml) :
943  xml(xml)
944  {
945  }
946 
947  void operator()(D4Map *m)
948  {
949  m->print_dap4(xml);
950  }
951 };
952 
958 void Array::print_dap4(XMLWriter &xml, bool constrained /* default: false*/)
959 {
960  if (constrained && !send_p()) return;
961 
962  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) var()->type_name().c_str()) < 0)
963  throw InternalErr(__FILE__, __LINE__, "Could not write " + type_name() + " element");
964 
965  if (!name().empty())
966  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name().c_str()) < 0)
967  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
968 
969 // Hack job... Copied from D4Enum::print_xml_writer. jhrg 11/12/13
970  if (var()->type() == dods_enum_c) {
971  D4Enum *e = static_cast<D4Enum*>(var());
972  string path = e->enumeration()->name();
973  if (e->enumeration()->parent()) {
974  // print the FQN for the enum def; D4Group::FQN() includes the trailing '/'
975  path = static_cast<D4Group*>(e->enumeration()->parent()->parent())->FQN() + path;
976  }
977  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "enum", (const xmlChar*) path.c_str()) < 0)
978  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for enum");
979  }
980 
981  if (prototype()->is_constructor_type()) {
982  Constructor &c = static_cast<Constructor&>(*prototype());
983  for_each(c.var_begin(), c.var_end(), PrintD4ConstructorVarXMLWriter(xml, constrained));
984  // bind2nd(mem_fun_ref(&BaseType::print_dap4), xml));
985  }
986 
987 // Drop the local_constraint which is per-array and use a per-dimension on instead
988  for_each(dim_begin(), dim_end(), PrintD4ArrayDimXMLWriter(xml, constrained));
989 
990  attributes()->print_dap4(xml);
991 
992  for_each(maps()->map_begin(), maps()->map_end(), PrintD4MapXMLWriter(xml));
993 
994  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
995  throw InternalErr(__FILE__, __LINE__, "Could not end " + type_name() + " element");
996 }
997 
1015 void Array::print_decl(FILE *out, string space, bool print_semi, bool constraint_info, bool constrained)
1016 {
1017  ostringstream oss;
1018  print_decl(oss, space, print_semi, constraint_info, constrained);
1019  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
1020 }
1021 
1039 void Array::print_decl(ostream &out, string space, bool print_semi, bool constraint_info, bool constrained)
1040 {
1041  if (constrained && !send_p()) return;
1042 
1043 // print it, but w/o semicolon
1044  var()->print_decl(out, space, false, constraint_info, constrained);
1045 
1046  for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
1047  out << "[";
1048  if ((*i).name != "") {
1049  out << id2www((*i).name) << " = ";
1050  }
1051  if (constrained) {
1052  out << (*i).c_size << "]";
1053  }
1054  else {
1055  out << (*i).size << "]";
1056  }
1057  }
1058 
1059  if (print_semi) {
1060  out << ";\n";
1061  }
1062 }
1063 
1067 void Array::print_xml(FILE *out, string space, bool constrained)
1068 {
1069  XMLWriter xml(space);
1070  print_xml_writer_core(xml, constrained, "Array");
1071  fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
1072 }
1073 
1077 void Array::print_xml(ostream &out, string space, bool constrained)
1078 {
1079  XMLWriter xml(space);
1080  print_xml_writer_core(xml, constrained, "Array");
1081  out << xml.get_doc();
1082 }
1083 
1087 void Array::print_as_map_xml(FILE *out, string space, bool constrained)
1088 {
1089  XMLWriter xml(space);
1090  print_xml_writer_core(xml, constrained, "Map");
1091  fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
1092 }
1093 
1097 void Array::print_as_map_xml(ostream &out, string space, bool constrained)
1098 {
1099  XMLWriter xml(space);
1100  print_xml_writer_core(xml, constrained, "Map");
1101  out << xml.get_doc();
1102 }
1103 
1107 void Array::print_xml_core(FILE *out, string space, bool constrained, string tag)
1108 {
1109  XMLWriter xml(space);
1110  print_xml_writer_core(xml, constrained, tag);
1111  fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
1112 }
1113 
1117 void Array::print_xml_core(ostream &out, string space, bool constrained, string tag)
1118 {
1119  XMLWriter xml(space);
1120  print_xml_writer_core(xml, constrained, tag);
1121  out << xml.get_doc();
1122 }
1123 
1124 void Array::print_xml_writer(XMLWriter &xml, bool constrained)
1125 {
1126  print_xml_writer_core(xml, constrained, "Array");
1127 }
1128 
1129 void Array::print_as_map_xml_writer(XMLWriter &xml, bool constrained)
1130 {
1131  print_xml_writer_core(xml, constrained, "Map");
1132 }
1133 
1134 class PrintArrayDimXMLWriter: public unary_function<Array::dimension&, void> {
1135  XMLWriter &xml;
1136  bool d_constrained;
1137 public:
1138  PrintArrayDimXMLWriter(XMLWriter &xml, bool c) :
1139  xml(xml), d_constrained(c)
1140  {
1141  }
1142 
1143  void operator()(Array::dimension &d)
1144  {
1145  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "dimension") < 0)
1146  throw InternalErr(__FILE__, __LINE__, "Could not write dimension element");
1147 
1148  if (!d.name.empty())
1149  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) d.name.c_str())
1150  < 0) throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1151 
1152  ostringstream size;
1153  size << (d_constrained ? d.c_size : d.size);
1154  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "size", (const xmlChar*) size.str().c_str())
1155  < 0) throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1156 
1157  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
1158  throw InternalErr(__FILE__, __LINE__, "Could not end dimension element");
1159  }
1160 };
1161 
1162 void Array::print_xml_writer_core(XMLWriter &xml, bool constrained, string tag)
1163 {
1164  if (constrained && !send_p()) return;
1165 
1166  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) tag.c_str()) < 0)
1167  throw InternalErr(__FILE__, __LINE__, "Could not write " + tag + " element");
1168 
1169  if (!name().empty())
1170  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name().c_str()) < 0)
1171  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1172 
1174 
1175  BaseType *btp = var();
1176  string tmp_name = btp->name();
1177  btp->set_name("");
1178  btp->print_xml_writer(xml, constrained);
1179  btp->set_name(tmp_name);
1180 
1181  for_each(dim_begin(), dim_end(), PrintArrayDimXMLWriter(xml, constrained));
1182 
1183  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
1184  throw InternalErr(__FILE__, __LINE__, "Could not end " + tag + " element");
1185 }
1186 
1198 unsigned int Array::print_array(FILE *out, unsigned int index, unsigned int dims, unsigned int shape[])
1199 {
1200  ostringstream oss;
1201  unsigned int i = print_array(oss, index, dims, shape);
1202  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
1203 
1204  return i;
1205 }
1206 
1218 unsigned int Array::print_array(ostream &out, unsigned int index, unsigned int dims, unsigned int shape[])
1219 {
1220  if (dims == 1) {
1221  out << "{";
1222 
1223  // Added test in case this method is passed an array with no elements. jhrg 1/27/16
1224  if (shape[0] >= 1) {
1225  for (unsigned i = 0; i < shape[0] - 1; ++i) {
1226  var(index++)->print_val(out, "", false);
1227  out << ", ";
1228  }
1229  var(index++)->print_val(out, "", false);
1230  }
1231 
1232  out << "}";
1233 
1234  return index;
1235  }
1236  else {
1237  out << "{";
1238  // Fixed an off-by-one error in the following loop. Since the array
1239  // length is shape[dims-1]-1 *and* since we want one less dimension
1240  // than that, the correct limit on this loop is shape[dims-2]-1. From
1241  // Todd Karakasian.
1242  //
1243  // The saga continues; the loop test should be `i < shape[0]-1'. jhrg
1244  // 9/12/96.
1245  //
1246  // For arrays that hold zero values but have rank > 1, the print out
1247  // may look a little odd (e.g., x[4][0] will print as { {}, {}, {}, {} })
1248  // but it's not wrong and this is really for debugging mostly. jhrg 1/28/16
1249  if (shape[0] > 0) {
1250  for (unsigned i = 0; i < shape[0] - 1; ++i) {
1251  index = print_array(out, index, dims - 1, shape + 1);
1252  out << ",";
1253  }
1254 
1255  index = print_array(out, index, dims - 1, shape + 1);
1256  }
1257 
1258  out << "}";
1259 
1260  return index;
1261  }
1262 }
1263 
1264 void Array::print_val(FILE *out, string space, bool print_decl_p)
1265 {
1266  ostringstream oss;
1267  print_val(oss, space, print_decl_p);
1268  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
1269 }
1270 
1271 void Array::print_val(ostream &out, string space, bool print_decl_p)
1272 {
1273 // print the declaration if print decl is true.
1274 // for each dimension,
1275 // for each element,
1276 // print the array given its shape, number of dimensions.
1277 // Add the `;'
1278 
1279  if (print_decl_p) {
1280  print_decl(out, space, false, false, false);
1281  out << " = ";
1282  }
1283 
1284  unsigned int *shape = new unsigned int[dimensions(true)];
1285  unsigned int index = 0;
1286  for (Dim_iter i = _shape.begin(); i != _shape.end() && index < dimensions(true); ++i)
1287  shape[index++] = dimension_size(i, true);
1288 
1289  print_array(out, 0, dimensions(true), shape);
1290 
1291  delete[] shape;
1292  shape = 0;
1293 
1294  if (print_decl_p) {
1295  out << ";\n";
1296  }
1297 }
1298 
1308 bool Array::check_semantics(string &msg, bool)
1309 {
1310  bool sem = BaseType::check_semantics(msg) && !_shape.empty();
1311 
1312  if (!sem) msg = "An array variable must have dimensions";
1313 
1314  return sem;
1315 }
1316 
1325 void Array::dump(ostream &strm) const
1326 {
1327  strm << DapIndent::LMarg << "Array::dump - (" << (void *) this << ")" << endl;
1328  DapIndent::Indent();
1329  Vector::dump(strm);
1330  strm << DapIndent::LMarg << "shape:" << endl;
1331  DapIndent::Indent();
1332  Dim_citer i = _shape.begin();
1333  Dim_citer ie = _shape.end();
1334  unsigned int dim_num = 0;
1335  for (; i != ie; i++) {
1336  strm << DapIndent::LMarg << "dimension " << dim_num++ << ":" << endl;
1337  DapIndent::Indent();
1338  strm << DapIndent::LMarg << "name: " << (*i).name << endl;
1339  strm << DapIndent::LMarg << "size: " << (*i).size << endl;
1340  strm << DapIndent::LMarg << "start: " << (*i).start << endl;
1341  strm << DapIndent::LMarg << "stop: " << (*i).stop << endl;
1342  strm << DapIndent::LMarg << "stride: " << (*i).stride << endl;
1343  strm << DapIndent::LMarg << "constrained size: " << (*i).c_size << endl;
1344  DapIndent::UnIndent();
1345  }
1346  DapIndent::UnIndent();
1347  DapIndent::UnIndent();
1348 }
1349 
1350 } // namespace libdap
1351 
virtual void print_xml_writer(XMLWriter &xml, bool constrained=false)
Definition: Array.cc:1124
virtual void reset_constraint()
Reset constraint to select entire array.
Definition: Array.cc:593
virtual void add_constraint(Dim_iter i, int start, int stride, int stop)
Adds a constraint to an Array dimension.
Definition: Array.cc:647
virtual string name() const
Returns the name of the class instance.
Definition: BaseType.cc:320
virtual bool check_semantics(string &msg, bool all=false)
Check semantic features of the Array.
Definition: Array.cc:1308
virtual void print_xml_core(FILE *out, string space, bool constrained, string tag)
Definition: Array.cc:1107
vector< D4Dimension * >::iterator D4DimensionsIter
Iterator used for D4Dimensions.
Definition: D4Dimensions.h:122
virtual void dump(ostream &strm) const
dumps information about this object
Definition: Array.cc:1325
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:1003
Array(const string &n, BaseType *v, bool is_dap4=false)
Array constructor.
Definition: Array.cc:136
virtual unsigned int dimensions(bool constrained=false)
Return the total number of dimensions in the array.
Definition: Array.cc:711
Part
Names the parts of multi-section constructor data types.
Definition: Type.h:48
void add_var(BaseType *v, Part p=nil)
Add the BaseType pointer to this constructor type instance.
Definition: Array.cc:470
Contains the attributes for a dataset.
Definition: AttrTable.h:142
virtual void set_name(const string &n)
Sets the name of the class instance.
Definition: BaseType.cc:344
int stop
The constraint end index.
Definition: Array.h:149
Holds a one-dimensional collection of DAP2 data types.
Definition: Vector.h:80
virtual unsigned int width(bool constrained=false) const
Returns the width of the data, in bytes.
Definition: Vector.cc:536
virtual void print_xml_writer(XMLWriter &xml, bool constrained=false)
Definition: BaseType.cc:1134
virtual string get_name() const
Get the name of this attribute table.
Definition: AttrTable.cc:238
std::vector< dimension >::const_iterator Dim_citer
Definition: Array.h:198
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
BaseType(const string &n, const Type &t, bool is_dap4=false)
The BaseType constructor.
Definition: BaseType.cc:126
virtual void add_var(BaseType *v, Part p=nil)
Add the BaseType pointer to this constructor type instance.
Definition: Vector.cc:1960
D4DimensionsIter dim_end()
Get an iterator to the end of the dimensions.
Definition: D4Dimensions.h:166
STL namespace.
void print_xml_writer(XMLWriter &xml)
Definition: AttrTable.cc:1425
virtual void add_var_nocopy(BaseType *bt, Part part=nil)
Definition: Constructor.cc:432
virtual void print(FILE *out, string pad=" ", bool dereference=false)
Prints the attribute table.
Definition: AttrTable.cc:1243
void append_dim(int size, const string &name="")
Add a dimension of a given size.
Definition: Array.cc:522
virtual void update_length(int size=0)
Definition: Array.cc:103
D4DimensionsIter dim_begin()
Get an iterator to the start of the dimensions.
Definition: D4Dimensions.h:163
top level DAP object to house generic methods
Definition: AISConnect.cc:30
A class for software fault reporting.
Definition: InternalErr.h:64
Dim_iter dim_end()
Definition: Array.cc:696
virtual std::string FQN() const
Definition: BaseType.cc:332
void transform_attrs_to_dap2(AttrTable *d2_attr_table)
Copy the attributes from this D4Attributes object to a DAP2 AttrTable.
virtual void set_array(Array *p_new_arr)
Definition: Grid.cc:389
virtual BaseType * var(const string &name="", bool exact_match=true, btp_stack *s=0)
Definition: Vector.cc:433
virtual void print_as_map_xml(ostream &out, string space=" ", bool constrained=false)
Definition: Array.cc:1097
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:302
Holds a DAP4 enumeration.
Definition: D4Enum.h:61
virtual int dimension_size(Dim_iter i, bool constrained=false)
Returns the size of the dimension.
Definition: Array.cc:733
virtual void print_val(FILE *out, string space="", bool print_decl_p=true)
Prints the value of the variable.
Definition: BaseType.cc:1090
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:412
void add_dim_nocopy(D4Dimension *dim)
Definition: D4Dimensions.h:160
virtual AttrTable * append_container(const string &name)
Add a container to the attribute table.
Definition: AttrTable.cc:410
virtual Type type() const
Returns the type of the class instance.
Definition: BaseType.cc:365
std::vector< dimension >::iterator Dim_iter
Definition: Array.h:206
void clear_all_dims()
Definition: Array.cc:564
virtual string dimension_name(Dim_iter i)
Returns the name of the specified dimension.
Definition: Array.cc:827
virtual BaseType * ptr_duplicate()
Definition: Array.cc:175
virtual int dimension_stride(Dim_iter i, bool constrained=false)
Returns the stride value of the constraint.
Definition: Array.cc:812
Holds the Grid data type.
Definition: Grid.h:122
int stride
The constraint stride.
Definition: Array.h:150
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:1039
int c_size
Size of dimension once constrained.
Definition: Array.h:151
virtual D4Attributes * attributes()
Definition: BaseType.cc:599
virtual Array * add_map(Array *p_new_map, bool add_copy)
Definition: Grid.cc:455
void prepend_dim(int size, const string &name="")
Definition: Array.cc:543
virtual void print_xml(ostream &out, string space=" ", bool constrained=false)
Definition: Array.cc:1077
string www2id(const string &in, const string &escape, const string &except)
Definition: escaping.cc:220
virtual int dimension_stop(Dim_iter i, bool constrained=false)
Return the stop index of the constraint.
Definition: Array.cc:788
virtual int dimension_start(Dim_iter i, bool constrained=false)
Return the start index of a dimension.
Definition: Array.cc:765
virtual AttrTable & get_attr_table()
Definition: BaseType.cc:582
virtual void print_dap4(XMLWriter &xml, bool constrained=false)
Print the DAP4 representation of an array.
Definition: Array.cc:958
virtual ~Array()
The Array destructor.
Definition: Array.cc:169
int size
The unconstrained dimension size.
Definition: Array.h:135
string name
The name of this dimension.
Definition: Array.h:136
The basic data type for the DODS DAP types.
Definition: BaseType.h:117
Dim_iter dim_begin()
Definition: Array.cc:690
virtual string type_name() const
Returns the type of the class instance as a string.
Definition: BaseType.cc:379
Vars_iter var_begin()
Definition: Constructor.cc:356
virtual int length() const
Definition: Vector.cc:548
void rename_dim(const string &oldName="", const string &newName="")
Renames dimension.
Definition: Array.cc:574
Vars_iter var_end()
Definition: Constructor.cc:364
virtual void set_length(int l)
Definition: Vector.cc:555
A class for error processing.
Definition: Error.h:92
virtual unsigned int get_size() const
Get the number of entries in this attribute table.
Definition: AttrTable.cc:231
virtual void set_name(const string &n)
Set the name of this attribute table.
Definition: AttrTable.cc:245
void transform_to_dap4(AttrTable &at)
copy attributes from DAP2 to DAP4
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:1198
A multidimensional array of identical data types.
Definition: Array.h:112
virtual void print_val(ostream &out, string space="", bool print_decl_p=true)
Prints the value of the variable.
Definition: Array.cc:1271
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:1299
virtual bool send_p()
Should this variable be sent?
Definition: BaseType.cc:554
virtual void transform_to_dap4(D4Group *root, Constructor *container)
DAP2 to DAP4 transform.
Definition: Array.cc:192
virtual void dump(ostream &strm) const
dumps information about this object
Definition: Vector.cc:2043
string id2www(string in, const string &allowable)
Definition: escaping.cc:153
D4Dimensions * dims()
Get the dimensions defined for this Group.
Definition: D4Group.h:82
virtual void clear_constraint()
Clears the projection; add each projected dimension explicitly using add_constraint.
Definition: Array.cc:616
virtual bool check_semantics(string &msg, bool all=false)
Compare an object&#39;s current state with the semantics of its type.
Definition: BaseType.cc:1209