libdap  Updated for version 3.19.1
libdap4 is an implementation of OPeNDAP's DAP protocol.
Array.cc
1 
2 // -*- mode: c++; c-basic-offset:4 -*-
3 
4 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
5 // Access Protocol.
6 
7 // Copyright (c) 2002,2003 OPeNDAP, Inc.
8 // Author: James Gallagher <jgallagher@opendap.org>
9 //
10 // This library is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU Lesser General Public
12 // License as published by the Free Software Foundation; either
13 // version 2.1 of the License, or (at your option) any later version.
14 //
15 // This library is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public
21 // License along with this library; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 //
24 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
25 
26 // (c) COPYRIGHT URI/MIT 1994-1999
27 // Please read the full copyright statement in the file COPYRIGHT_URI.
28 //
29 // Authors:
30 // jhrg,jimg James Gallagher <jgallagher@gso.uri.edu>
31 
32 // Implementation for Array.
33 //
34 // jhrg 9/13/94
35 
36 #include "config.h"
37 
38 // #define DODS_DEBUG
39 
40 #include <algorithm>
41 #include <functional>
42 #include <sstream>
43 
44 #include "Array.h"
45 #include "Grid.h"
46 
47 #include "D4Attributes.h"
48 #include "DMR.h"
49 #include "D4Dimensions.h"
50 #include "D4Maps.h"
51 #include "D4Group.h"
52 #include "D4EnumDefs.h"
53 #include "D4Enum.h"
54 #include "XMLWriter.h"
55 
56 #include "util.h"
57 #include "debug.h"
58 #include "InternalErr.h"
59 #include "escaping.h"
60 
61 using namespace std;
62 
63 namespace libdap {
64 
65 Array::dimension::dimension(D4Dimension *d) : dim(d), use_sdim_for_slice(true)
66 {
67  size = d->size();
68  name = d->name();
69 
70  start = 0;
71  stop = size - 1;
72  stride = 1;
73  c_size = size;
74 }
75 
76 void
77 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 
103 void
105 {
106  int length = 1;
107  for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
108 #if 0
109  // If the size of any dimension is zero, then the array is not
110  // capable of storing any values. jhrg 1/28/16
111  length *= (*i).c_size > 0 ? (*i).c_size : 1;
112 #endif
113  length *= (*i).c_size;
114  }
115 
117 }
118 
119 // Construct an instance of Array. The (BaseType *) is assumed to be
120 // allocated using new - The dtor for Vector will delete this object.
121 
137 Array::Array(const string &n, BaseType *v, bool is_dap4 /* default:false */)
138  : Vector(n, 0, dods_array_c, is_dap4), d_maps(0)
139 {
140  add_var(v); // Vector::add_var() stores null if v is null
141 }
142 
156 Array::Array(const string &n, const string &d, BaseType *v, bool is_dap4 /* default:false */)
157  : Vector(n, d, 0, dods_array_c, is_dap4), d_maps(0)
158 {
159  add_var(v); // Vector::add_var() stores null if v is null
160 }
161 
163 Array::Array(const Array &rhs) : 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)
184  return *this;
185 
186  dynamic_cast<Vector &>(*this) = rhs;
187 
188  _duplicate(rhs);
189 
190  return *this;
191 }
192 
193 void
195 {
196  DBG(cerr << __func__ << "() - BEGIN (array:" << name() << ")" << endl;);
197  Array *dest = static_cast<Array*>(ptr_duplicate());
198 
199  // If it's already a DAP4 object then we can just return it!
200  if(is_dap4()){
201  DBG(cerr << __func__ << "() - Already DAP4 type: Just making a copy and adding to container. " << endl;);
202  container->add_var_nocopy(dest);
203  DBG(cerr << __func__ << "() - END (Already DAP4 type)" << endl;);
204  }
205  // Process the Array's dimensions, making D4 shared dimensions for
206  // D2 dimensions that are named. If there is just a size, don't make
207  // a D4Dimension (In DAP4 you cannot share a dimension unless it has
208  // a name). jhrg 3/18/14
209 
210  D4Dimensions *root_dims = root->dims();
211  for (Array::Dim_iter dap2_dim = dest->dim_begin(), e = dest->dim_end(); dap2_dim != e; ++dap2_dim) {
212  if (!(*dap2_dim).name.empty()) {
213  DBG(cerr << __func__ << "() - Processing the array dimension '" << (*dap2_dim).name << "'" << endl;);
214 
215  // If a D4Dimension with the name already exists, use it.
216  D4Dimension *d4_dim = root_dims->find_dim((*dap2_dim).name);
217  if (!d4_dim) {
218  d4_dim = new D4Dimension((*dap2_dim).name, (*dap2_dim).size);
219  root_dims->add_dim_nocopy(d4_dim);
220  DBG(cerr << __func__ << "() -" <<
221  " Added NEW D4Dimension '"<< d4_dim->name() << "' (" <<
222  (void *)d4_dim << ") to root->dims()"<< endl;);
223  }
224  else {
225  DBG(cerr << __func__ << "() -" <<
226  " Using Existing D4Dimension '"<< d4_dim->name() << "' (" <<
227  (void *)d4_dim << ")"<< endl;);
228 
229  if (d4_dim->size() != (unsigned long) (*dap2_dim).size) {
230  // TODO Revisit this decision. jhrg 3/18/14
231  // ...in case the name/size are different, make a unique D4Dimension
232  // but don't fiddle with the name. Not sure I like this idea, so I'm
233  // making the case explicit (could be rolled in to the block above).
234  // jhrg 3/18/14
235  //
236  // This is causing problems in the FITS handler because there are cases
237  // where two arrays have dimensions with the same name but different
238  // sizes. The deserializing code is using the first size listed, which is
239  // wrong in some cases. I'm going to try making this new D4Dimension using
240  // the dim name along with the variable name. jhrg 8/15/14
241  d4_dim = new D4Dimension((*dap2_dim).name + "_" + name(), (*dap2_dim).size);
242  DBG(cerr << __func__ << "() -" <<
243  " Utilizing Name/Size Conflict Naming Artifice. name'"<< d4_dim->name() << "' (" <<
244  (void *)d4_dim << ")"<< endl;);
245  root_dims->add_dim_nocopy(d4_dim);
246  }
247  }
248  // At this point d4_dim's name and size == those of (*d) so just set
249  // the D4Dimension pointer so it matches the one in the D4Group.
250  (*dap2_dim).dim = d4_dim;
251  }
252 
253  }
254 
255  // Copy the D2 attributes to D4 Attributes
256  dest->attributes()->transform_to_dap4(get_attr_table());
257  dest->set_is_dap4(true);
258  container->add_var_nocopy(dest);
259  DBG(cerr << __func__ << "() - END (array:" << name() << ")" << endl;);
260 }
261 
262 bool Array::is_dap2_grid(){
263  bool is_grid = false;
264  if(this->is_dap4()){
265  DBG( cerr << __func__ << "() - Array '"<< name() << "' is DAP4 object!" << endl;)
266  D4Maps *d4_maps = this->maps();
267  is_grid = d4_maps->size(); // It can't be a grid if there are no maps...
268  if(is_grid){
269  DBG( cerr << __func__ << "() - Array '"<< name() << "' has D4Maps." << endl;)
270  // hmmm this might be a DAP2 Grid...
271  D4Maps::D4MapsIter i = d4_maps->map_begin();
272  D4Maps::D4MapsIter e = d4_maps->map_end();
273  while(i!=e){
274  DBG( cerr << __func__ << "() - Map '"<< (*i)->array()->name() << " has " << (*i)->array()->_shape.size() << " dimension(s)." << endl;)
275  if((*i)->array()->_shape.size() > 1){
276  is_grid = false;
277  i = e;
278  }
279  else {
280  i++;
281  }
282  }
283  }
284  else {
285  DBG( cerr << __func__ << "() - Array '"<< name() << "' has no D4Maps." << endl;)
286  }
287  }
288  DBG( cerr << __func__ << "() - is_grid: "<< (is_grid?"true":"false") << endl;)
289  return is_grid;
290 }
291 
307 std::vector<BaseType *> *
309  DBG(cerr << __func__ << "() - BEGIN Array '"<< name() << "'" << endl;);
310 
311  BaseType *dest;
312  if(is_dap4()){ // Don't convert a DAP2 thing
313 
314  // Can Precious be represented as a DAP2 Grid
315  if(is_dap2_grid()){
316  // Oh yay! Grids are special.
317  DBG(cerr << __func__ << "() - Array '"<< name() << "' is dap2 Grid!" << endl;);
318  Grid *g = new Grid(name());
319  dest = g;
320  Array *grid_array = (Array *) this->ptr_duplicate();
321  g->set_array(grid_array);
322 
323  // Get the metadata into the Grid
324  AttrTable *grid_attrs = attributes()->get_AttrTable(name());
325  g->set_attr_table(*grid_attrs); // Copy it into the Grid object.
326  // grid_array->set_attr_table(*grid_attrs); // Copy it into the data Array.
327  delete grid_attrs;
328 
329  // Clear the Grid data Array attributes.
330  AttrTable at;
331  at.set_name(name());
332  grid_array->set_attr_table(at);
333 
334  // Process the Map Arrays.
335  D4Maps *d4_maps = this->maps();
336  vector<BaseType *> dropped_maps;
337  D4Maps::D4MapsIter miter = d4_maps->map_begin();
338  D4Maps::D4MapsIter end = d4_maps->map_end();
339  for( ; miter!=end; miter++){
340  D4Map *d4_map = (*miter);
341  Array *d4_map_array = const_cast<Array*>(d4_map->array());
342  vector<BaseType *> *d2_result = d4_map_array->transform_to_dap2(&(g->get_attr_table()));
343  if(d2_result){
344  if(d2_result->size()>1)
345  throw Error(internal_error,string(__func__)+"() - ERROR: D4Map Array conversion resulted in multiple DAP2 objects.");
346 
347  // TODO - This is probably slow and needs a better pattern. const_cast? static_cast?
348  Array *d2_map_array = dynamic_cast<Array *>((*d2_result)[0]);
349  if(d2_map_array){
350  if(d2_map_array->dimensions()!=1)
351  throw Error(internal_error,string(__func__)+"() - ERROR: DAP2 array from D4Map Array conversion has more than 1 dimension.");
352 
353  g->add_map(d2_map_array,false);
354  AttrTable at = d2_map_array->get_attr_table();
355  DBG( cerr << __func__ << "() - " <<
356  "DAS For Grid Map '" << d2_map_array->name() << "':" << endl;
357  at.print(cerr); );
358  }
359  else {
360  throw Error(internal_error,string(__func__)+"() - Unable to interpret returned DAP2 content.");
361  }
362  delete d2_result;
363  }
364  else {
365  dropped_maps.push_back(d4_map_array);
366  }
367  }
368 
369  // Did we have a transform failure?
370  if(!dropped_maps.empty()){
371  // Yup... tell the story in the attributes.
372  AttrTable *dv_table = Constructor::make_dropped_vars_attr_table(&dropped_maps);
373  dest->get_attr_table().append_container(dv_table,dv_table->get_name());
374  }
375  }
376  else {
377  // It's not a Grid so we can make a simple copy of our Precious.
378  DBG( cerr << __func__ << "() - Array '"<< name() << "' is not a Grid!" << endl);
379  BaseType *proto = this->prototype();
380  switch(proto->type()){
381  case dods_int64_c:
382  case dods_uint64_c:
383  case dods_enum_c:
384  case dods_opaque_c:
385  {
386  // For now we punt on these type as they have no easy representation in
387  // the DAP2 data model. By setting this to NULL we cause the Array to be
388  // dropped and this will be reflected in the metadata (DAS).
389  dest = NULL;
390  break;
391  }
392  default:
393  {
394  dest = this->ptr_duplicate();
395  // convert the d4 attributes to a dap2 attribute table.
396  AttrTable *attrs = this->attributes()->get_AttrTable(name());
397  dest->set_attr_table(*attrs);
398  dest->set_is_dap4(false);
399  AttrTable at = dest->get_attr_table();
400  DBG( cerr << __func__ << "() - " <<
401  "DAS for new Array '" << dest->name() << "':" << endl;
402  at.print(cerr); )
403 
404  break;
405  }
406  }
407  }
408 
409  }
410  else {
411  // If it's a DAP2 Array already then we just make a copy of our Precious.
412  dest = this->ptr_duplicate();
413  }
414  // attrs->print(cerr,"",true);
415  vector<BaseType *> *result;
416  if(dest){
417  result = new vector<BaseType *>();
418  result->push_back(dest);
419  }
420  else {
421  result = NULL;
422  }
423  DBG( cerr << __func__ << "() - END Array '"<< name() << "'" << endl;);
424  return result;
425 }
426 
438 void
439 Array::update_dimension_pointers(D4Dimensions *old_dims, D4Dimensions *new_dims)
440 {
441  std::vector<dimension>::iterator i = _shape.begin(), e = _shape.end();
442  while (i != e) {
443  D4Dimensions::D4DimensionsIter old_i = old_dims->dim_begin(), old_e = old_dims->dim_end();
444  while (old_i != old_e) {
445  if ((*i).dim == *old_i) {
446  (*i).dim = new_dims->find_dim((*old_i)->name());
447  }
448  ++old_i;
449  }
450 
451  ++i;
452  }
453 }
454 
479 void
481 {
482  // If 'v' is an Array, add the template instance to this object and
483  // then copy the dimension information. Odd semantics; I wonder if this
484  //is ever used. jhrg 6/13/12
485  if (v && v->type() == dods_array_c) {
486  Array *a = static_cast<Array*>(v);
487  Vector::add_var(a->var());
488 
489  Dim_iter i = a->dim_begin();
490  Dim_iter i_end = a->dim_end();
491  while (i != i_end) {
493  ++i;
494  }
495  }
496  else {
497  Vector::add_var(v);
498  }
499 }
500 
501 void
502 Array::add_var_nocopy(BaseType *v, Part)
503 {
504  // If 'v' is an Array, add the template instance to this object and
505  // then copy the dimension information. Odd semantics; I wonder if this
506  //is ever used. jhrg 6/13/12
507  if (v && v->type() == dods_array_c) {
508  Array &a = dynamic_cast<Array&>(*v);
509  Vector::add_var_nocopy(a.var());
510  Dim_iter i = a.dim_begin();
511  Dim_iter i_end = a.dim_end();
512  while (i != i_end) {
514  ++i;
515  }
516  }
517  else {
518  Vector::add_var_nocopy(v);
519  }
520 }
521 
533 void
534 Array::append_dim(int size, const string &name)
535 {
536  dimension d(size, www2id(name));
537  _shape.push_back(d);
538 
539  update_length();
540 }
541 
542 void
544 {
545  dimension d(/*dim->size(), www2id(dim->name()),*/ dim);
546  _shape.push_back(d);
547 
548  update_length();
549 }
550 
556 void
557 Array::prepend_dim(int size, const string& name/* = "" */)
558 {
559  dimension d(size, www2id(name));
560  // Shifts the whole array, but it's tiny in general
561  _shape.insert(_shape.begin(), d);
562 
563  update_length(); // the number is ignored...
564 }
565 
566 void
568 {
569  dimension d(/*dim->size(), www2id(dim->name()),*/ dim);
570  // Shifts the whole array, but it's tiny in general
571  _shape.insert(_shape.begin(), d);
572 
573  update_length(); // the number is ignored...
574 }
575 
579 void
581 {
582  _shape.clear();
583 }
590 void
592 {
593  set_length(-1);
594 
595  for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) {
596  (*i).start = 0;
597  (*i).stop = (*i).size - 1;
598  (*i).stride = 1;
599  (*i).c_size = (*i).size;
600 
601  update_length();
602  }
603 }
604 
605 
615 void
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
648 Array::add_constraint(Dim_iter i, int start, int stride, int stop)
649 {
650  dimension &d = *i ;
651 
652  // if stop is -1, set it to the array's max element index
653  // jhrg 12/20/12
654  if (stop == -1)
655  stop = d.size - 1;
656 
657  // Check for bad constraints.
658  // Jose Garcia
659  // Usually invalid data for a constraint is the user's mistake
660  // because they build a wrong URL in the client side.
661  if (start >= d.size || stop >= d.size || stride > d.size || stride <= 0)
662  throw Error(malformed_expr, array_sss);
663 
664  if (((stop - start) / stride + 1) > d.size)
665  throw Error(malformed_expr, array_sss);
666 
667  d.start = start;
668  d.stop = stop;
669  d.stride = stride;
670 
671  d.c_size = (stop - start) / stride + 1;
672 
673  DBG(cerr << "add_constraint: c_size = " << d.c_size << endl);
674 
675  update_length();
676 
677  d.use_sdim_for_slice = false;
678 }
679 
680 void
681 Array::add_constraint(Dim_iter i, D4Dimension *dim)
682 {
683  dimension &d = *i ;
684 
685  if (dim->constrained())
686  add_constraint(i, dim->c_start(), dim->c_stride(), dim->c_stop());
687 
688  dim->set_used_by_projected_var(true);
689 
690  // In this case the value below overrides the value for use_sdim_for_slice
691  // set in the above call. jhrg 12/20/13
692  d.use_sdim_for_slice = true;
693 }
694 
698 {
699  return _shape.begin() ;
700 }
701 
705 {
706  return _shape.end() ;
707 }
708 
709 //TODO Many of these methods take a bool parameter that serves no use; remove.
710 
719 unsigned int
720 Array::dimensions(bool /*constrained*/)
721 {
722  return _shape.size();
723 }
724 
742 int
743 Array::dimension_size(Dim_iter i, bool constrained)
744 {
745  int size = 0;
746 
747  if (!_shape.empty()) {
748  if (constrained)
749  size = (*i).c_size;
750  else
751  size = (*i).size;
752  }
753 
754  return size;
755 }
756 
775 int
776 Array::dimension_start(Dim_iter i, bool /*constrained*/)
777 {
778  return (!_shape.empty()) ? (*i).start : 0;
779 }
780 
799 int
800 Array::dimension_stop(Dim_iter i, bool /*constrained*/)
801 {
802  return (!_shape.empty()) ? (*i).stop : 0;
803 }
804 
824 int
825 Array::dimension_stride(Dim_iter i, bool /*constrained*/)
826 {
827  return (!_shape.empty()) ? (*i).stride : 0;
828 }
829 
840 string
842 {
843  // Jose Garcia
844  // Since this method is public, it is possible for a user
845  // to call it before the Array object has been properly set
846  // this will cause an exception which is the user's fault.
847  // (User in this context is the developer of the surrogate library.)
848  if (_shape.empty())
849  throw InternalErr(__FILE__, __LINE__,
850  "*This* array has no dimensions.");
851  return (*i).name;
852 }
853 
854 D4Dimension *
855 Array::dimension_D4dim(Dim_iter i)
856 {
857  return (!_shape.empty()) ? (*i).dim : 0;
858 }
859 
860 D4Maps *
861 Array::maps()
862 {
863  if (!d_maps) d_maps = new D4Maps(this); // init with this as parent
864  return d_maps;
865 }
866 
867 #if 0
868 
874 unsigned int Array::width(bool constrained) const
875 {
876 
877  if (constrained) {
878  // This preserves the original method's semantics when we ask for the
879  // size of the constrained array but no constraint has been applied.
880  // In this case, length will be -1. Wrong, I know...
881  return length() * var()->width(constrained);
882  }
883  else {
884  int length = 1;
885  for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) {
886  length *= dimension_size(i, false);
887  }
888  return length * var()->width(false);
889  }
890 }
891 #endif
892 
893 class PrintD4ArrayDimXMLWriter: public unary_function<Array::dimension&, void> {
894  XMLWriter &xml;
895  // Was this variable constrained using local/direct slicing? i.e., is d_local_constraint set?
896  // If so, don't use shared dimensions; instead emit Dim elements that are anonymous.
897  bool d_constrained;
898 public:
899 
900  PrintD4ArrayDimXMLWriter(XMLWriter &xml, bool c) : xml(xml), d_constrained(c) { }
901 
902  void operator()(Array::dimension &d)
903  {
904  // This duplicates code in D4Dimensions (where D4Dimension::print_dap4() is defined
905  // because of the need to print the constrained size of a dimension. I think that
906  // the constraint information has to be kept here and not in the dimension (since they
907  // are shared dims). Could hack print_dap4() to take the constrained size, however.
908  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Dim") < 0)
909  throw InternalErr(__FILE__, __LINE__, "Could not write Dim element");
910 
911  string name = (d.dim) ? d.dim->fully_qualified_name() : d.name;
912  // If there is a name, there must be a Dimension (named dimension) in scope
913  // so write its name but not its size.
914  if (!d_constrained && !name.empty()) {
915  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name.c_str())
916  < 0) throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
917  }
918  else if (d.use_sdim_for_slice) {
919  assert(!name.empty());
920  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name.c_str())
921  < 0) throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
922  }
923  else {
924  ostringstream size;
925  size << (d_constrained ? d.c_size : d.size);
926  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "size",
927  (const xmlChar*) size.str().c_str()) < 0)
928  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
929  }
930 
931  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
932  throw InternalErr(__FILE__, __LINE__, "Could not end Dim element");
933  }
934 };
935 
936 class PrintD4ConstructorVarXMLWriter: public unary_function<BaseType*, void> {
937  XMLWriter &xml;
938  bool d_constrained;
939 public:
940  PrintD4ConstructorVarXMLWriter(XMLWriter &xml, bool c) : xml(xml), d_constrained(c) { }
941 
942  void operator()(BaseType *btp)
943  {
944  btp->print_dap4(xml, d_constrained);
945  }
946 };
947 
948 class PrintD4MapXMLWriter: public unary_function<D4Map*, void> {
949  XMLWriter &xml;
950 
951 public:
952  PrintD4MapXMLWriter(XMLWriter &xml) : xml(xml) { }
953 
954  void operator()(D4Map *m)
955  {
956  m->print_dap4(xml);
957  }
958 };
959 
965 void
966 Array::print_dap4(XMLWriter &xml, bool constrained /* default: false*/)
967 {
968  if (constrained && !send_p()) return;
969 
970  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) var()->type_name().c_str()) < 0)
971  throw InternalErr(__FILE__, __LINE__, "Could not write " + type_name() + " element");
972 
973  if (!name().empty())
974  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0)
975  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
976 
977  // Hack job... Copied from D4Enum::print_xml_writer. jhrg 11/12/13
978  if (var()->type() == dods_enum_c) {
979  D4Enum *e = static_cast<D4Enum*>(var());
980  string path = e->enumeration()->name();
981  if (e->enumeration()->parent()) {
982  // print the FQN for the enum def; D4Group::FQN() includes the trailing '/'
983  path = static_cast<D4Group*>(e->enumeration()->parent()->parent())->FQN() + path;
984  }
985  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "enum", (const xmlChar*)path.c_str()) < 0)
986  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for enum");
987  }
988 
989  if (prototype()->is_constructor_type()) {
990  Constructor &c = static_cast<Constructor&>(*prototype());
991  for_each(c.var_begin(), c.var_end(), PrintD4ConstructorVarXMLWriter(xml, constrained));
992  // bind2nd(mem_fun_ref(&BaseType::print_dap4), xml));
993  }
994 
995  // Drop the local_constraint which is per-array and use a per-dimension on instead
996  for_each(dim_begin(), dim_end(), PrintD4ArrayDimXMLWriter(xml, constrained));
997 
998  attributes()->print_dap4(xml);
999 
1000  for_each(maps()->map_begin(), maps()->map_end(), PrintD4MapXMLWriter(xml));
1001 
1002  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
1003  throw InternalErr(__FILE__, __LINE__, "Could not end " + type_name() + " element");
1004 }
1005 
1023 void
1024 Array::print_decl(FILE *out, string space, bool print_semi,
1025  bool constraint_info, bool constrained)
1026 {
1027  ostringstream oss;
1028  print_decl(oss, space, print_semi, constraint_info, constrained);
1029  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
1030 }
1031 
1049 void Array::print_decl(ostream &out, string space, bool print_semi, bool constraint_info, bool constrained)
1050 {
1051  if (constrained && !send_p()) return;
1052 
1053  // print it, but w/o semicolon
1054  var()->print_decl(out, space, false, constraint_info, constrained);
1055 
1056  for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
1057  out << "[";
1058  if ((*i).name != "") {
1059  out << id2www((*i).name) << " = ";
1060  }
1061  if (constrained) {
1062  out << (*i).c_size << "]";
1063  }
1064  else {
1065  out << (*i).size << "]";
1066  }
1067  }
1068 
1069  if (print_semi) {
1070  out << ";\n";
1071  }
1072 }
1073 
1077 void
1078 Array::print_xml(FILE *out, string space, bool constrained)
1079 {
1080  XMLWriter xml(space);
1081  print_xml_writer_core(xml, constrained, "Array");
1082  fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
1083 }
1084 
1088 void
1089 Array::print_xml(ostream &out, string space, bool constrained)
1090 {
1091  XMLWriter xml(space);
1092  print_xml_writer_core(xml, constrained, "Array");
1093  out << xml.get_doc();
1094 }
1095 
1099 void
1100 Array::print_as_map_xml(FILE *out, string space, bool constrained)
1101 {
1102  XMLWriter xml(space);
1103  print_xml_writer_core(xml, constrained, "Map");
1104  fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
1105 }
1106 
1110 void
1111 Array::print_as_map_xml(ostream &out, string space, bool constrained)
1112 {
1113  XMLWriter xml(space);
1114  print_xml_writer_core(xml, constrained, "Map");
1115  out << xml.get_doc();
1116 }
1117 
1121 void
1122 Array::print_xml_core(FILE *out, string space, bool constrained, string tag)
1123 {
1124  XMLWriter xml(space);
1125  print_xml_writer_core(xml, constrained, tag);
1126  fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
1127 }
1128 
1132 void
1133 Array::print_xml_core(ostream &out, string space, bool constrained, string tag)
1134 {
1135  XMLWriter xml(space);
1136  print_xml_writer_core(xml, constrained, tag);
1137  out << xml.get_doc();
1138 }
1139 
1140 void
1141 Array::print_xml_writer(XMLWriter &xml, bool constrained)
1142 {
1143  print_xml_writer_core(xml, constrained, "Array");
1144 }
1145 
1146 void
1147 Array::print_as_map_xml_writer(XMLWriter &xml, bool constrained)
1148 {
1149  print_xml_writer_core(xml, constrained, "Map");
1150 }
1151 
1152 class PrintArrayDimXMLWriter : public unary_function<Array::dimension&, void>
1153 {
1154  XMLWriter &xml;
1155  bool d_constrained;
1156 public:
1157  PrintArrayDimXMLWriter(XMLWriter &xml, bool c) : xml(xml), d_constrained(c) {}
1158 
1159  void operator()(Array::dimension &d)
1160  {
1161  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)"dimension") < 0)
1162  throw InternalErr(__FILE__, __LINE__, "Could not write dimension element");
1163 
1164  if (!d.name.empty())
1165  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)d.name.c_str()) < 0)
1166  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1167 
1168  ostringstream size;
1169  size << (d_constrained ? d.c_size : d.size);
1170  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "size", (const xmlChar*)size.str().c_str()) < 0)
1171  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1172 
1173  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
1174  throw InternalErr(__FILE__, __LINE__, "Could not end dimension element");
1175  }
1176 };
1177 
1178 void
1179 Array::print_xml_writer_core(XMLWriter &xml, bool constrained, string tag)
1180 {
1181  if (constrained && !send_p())
1182  return;
1183 
1184  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)tag.c_str()) < 0)
1185  throw InternalErr(__FILE__, __LINE__, "Could not write " + tag + " element");
1186 
1187  if (!name().empty())
1188  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0)
1189  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1190 
1192 
1193  BaseType *btp = var();
1194  string tmp_name = btp->name();
1195  btp->set_name("");
1196  btp->print_xml_writer(xml, constrained);
1197  btp->set_name(tmp_name);
1198 
1199  for_each(dim_begin(), dim_end(), PrintArrayDimXMLWriter(xml, constrained));
1200 
1201  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
1202  throw InternalErr(__FILE__, __LINE__, "Could not end " + tag + " element");
1203 }
1204 
1216 unsigned int
1217 Array::print_array(FILE *out, unsigned int index, unsigned int dims,
1218  unsigned int shape[])
1219 {
1220  ostringstream oss;
1221  unsigned int i = print_array(oss, index, dims, shape);
1222  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
1223 
1224  return i;
1225 }
1226 
1238 unsigned int Array::print_array(ostream &out, unsigned int index, unsigned int dims, unsigned int shape[])
1239 {
1240  if (dims == 1) {
1241  out << "{";
1242 
1243  // Added test in case this method is passed an array with no elements. jhrg 1/27/16
1244  if (shape[0] >= 1) {
1245  for (unsigned i = 0; i < shape[0] - 1; ++i) {
1246  var(index++)->print_val(out, "", false);
1247  out << ", ";
1248  }
1249  var(index++)->print_val(out, "", false);
1250  }
1251 
1252  out << "}";
1253 
1254  return index;
1255  }
1256  else {
1257  out << "{";
1258  // Fixed an off-by-one error in the following loop. Since the array
1259  // length is shape[dims-1]-1 *and* since we want one less dimension
1260  // than that, the correct limit on this loop is shape[dims-2]-1. From
1261  // Todd Karakasian.
1262  //
1263  // The saga continues; the loop test should be `i < shape[0]-1'. jhrg
1264  // 9/12/96.
1265  //
1266  // For arrays that hold zero values but have rank > 1, the print out
1267  // may look a little odd (e.g., x[4][0] will print as { {}, {}, {}, {} })
1268  // but it's not wrong and this is really for debugging mostly. jhrg 1/28/16
1269  if (shape[0] > 0) {
1270  for (unsigned i = 0; i < shape[0] - 1; ++i) {
1271  index = print_array(out, index, dims - 1, shape + 1);
1272  out << ",";
1273  }
1274 
1275  index = print_array(out, index, dims - 1, shape + 1);
1276  }
1277 
1278  out << "}";
1279 
1280  return index;
1281  }
1282 }
1283 
1284 void
1285 Array::print_val(FILE *out, string space, bool print_decl_p)
1286 {
1287  ostringstream oss;
1288  print_val(oss, space, print_decl_p);
1289  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
1290 }
1291 
1292 void
1293 Array::print_val(ostream &out, string space, bool print_decl_p)
1294 {
1295  // print the declaration if print decl is true.
1296  // for each dimension,
1297  // for each element,
1298  // print the array given its shape, number of dimensions.
1299  // Add the `;'
1300 
1301  if (print_decl_p) {
1302  print_decl(out, space, false, false, false);
1303  out << " = " ;
1304  }
1305 
1306  unsigned int *shape = new unsigned int[dimensions(true)];
1307  unsigned int index = 0;
1308  for (Dim_iter i = _shape.begin(); i != _shape.end() && index < dimensions(true); ++i)
1309  shape[index++] = dimension_size(i, true);
1310 
1311  print_array(out, 0, dimensions(true), shape);
1312 
1313  delete [] shape; shape = 0;
1314 
1315  if (print_decl_p) {
1316  out << ";\n" ;
1317  }
1318 }
1319 
1329 bool
1330 Array::check_semantics(string &msg, bool)
1331 {
1332  bool sem = BaseType::check_semantics(msg) && !_shape.empty();
1333 
1334  if (!sem)
1335  msg = "An array variable must have dimensions";
1336 
1337  return sem;
1338 }
1339 
1348 void
1349 Array::dump(ostream &strm) const
1350 {
1351  strm << DapIndent::LMarg << "Array::dump - ("
1352  << (void *)this << ")" << endl ;
1353  DapIndent::Indent() ;
1354  Vector::dump(strm) ;
1355  strm << DapIndent::LMarg << "shape:" << endl ;
1356  DapIndent::Indent() ;
1357  Dim_citer i = _shape.begin() ;
1358  Dim_citer ie = _shape.end() ;
1359  unsigned int dim_num = 0 ;
1360  for (; i != ie; i++) {
1361  strm << DapIndent::LMarg << "dimension " << dim_num++ << ":"
1362  << endl ;
1363  DapIndent::Indent() ;
1364  strm << DapIndent::LMarg << "name: " << (*i).name << endl ;
1365  strm << DapIndent::LMarg << "size: " << (*i).size << endl ;
1366  strm << DapIndent::LMarg << "start: " << (*i).start << endl ;
1367  strm << DapIndent::LMarg << "stop: " << (*i).stop << endl ;
1368  strm << DapIndent::LMarg << "stride: " << (*i).stride << endl ;
1369  strm << DapIndent::LMarg << "constrained size: " << (*i).c_size
1370  << endl ;
1371  DapIndent::UnIndent() ;
1372  }
1373  DapIndent::UnIndent() ;
1374  DapIndent::UnIndent() ;
1375 }
1376 
1377 } // namespace libdap
1378 
virtual void print_xml_writer(XMLWriter &xml, bool constrained=false)
Definition: Array.cc:1141
virtual void reset_constraint()
Reset constraint to select entire array.
Definition: Array.cc:591
virtual void add_constraint(Dim_iter i, int start, int stride, int stop)
Adds a constraint to an Array dimension.
Definition: Array.cc:648
virtual string name() const
Returns the name of the class instance.
Definition: BaseType.cc:311
virtual bool check_semantics(string &msg, bool all=false)
Check semantic features of the Array.
Definition: Array.cc:1330
virtual void print_xml_core(FILE *out, string space, bool constrained, string tag)
Definition: Array.cc:1122
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:1349
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:994
Array(const string &n, BaseType *v, bool is_dap4=false)
Array constructor.
Definition: Array.cc:137
AttrTable * get_AttrTable(const std::string name)
copy attributes from DAP4 to DAP2
virtual unsigned int dimensions(bool constrained=false)
Return the total number of dimensions in the array.
Definition: Array.cc:720
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:480
Contains the attributes for a dataset.
Definition: AttrTable.h:142
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:543
virtual string get_name() const
Get the name of this attribute table.
Definition: AttrTable.cc:237
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:125
virtual void add_var(BaseType *v, Part p=nil)
Add the BaseType pointer to this constructor type instance.
Definition: Vector.cc:1975
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:1424
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:1242
void append_dim(int size, const string &name="")
Add a dimension of a given size.
Definition: Array.cc:534
virtual void update_length(int size=0)
Definition: Array.cc:104
D4DimensionsIter dim_begin()
Get an iterator to the start of the dimensions.
Definition: D4Dimensions.h:163
A class for software fault reporting.
Definition: InternalErr.h:64
Dim_iter dim_end()
Definition: Array.cc:704
virtual std::string FQN() const
Definition: BaseType.cc:323
virtual BaseType * var(const string &name="", bool exact_match=true, btp_stack *s=0)
Definition: Vector.cc:434
virtual void print_as_map_xml(ostream &out, string space=" ", bool constrained=false)
Definition: Array.cc:1111
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:308
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:743
virtual void print_val(FILE *out, string space="", bool print_decl_p=true)
Prints the value of the variable.
Definition: BaseType.cc:1081
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:403
void add_dim_nocopy(D4Dimension *dim)
Definition: D4Dimensions.h:160
virtual AttrTable * get_attr_table(const string &name)
Get an attribute container.
Definition: AttrTable.cc:606
virtual Type type() const
Returns the type of the class instance.
Definition: BaseType.cc:356
std::vector< dimension >::iterator Dim_iter
Definition: Array.h:206
void clear_all_dims()
Definition: Array.cc:580
virtual string dimension_name(Dim_iter i)
Returns the name of the specified dimension.
Definition: Array.cc:841
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:825
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:1049
int c_size
Size of dimension once constrained.
Definition: Array.h:151
virtual D4Attributes * attributes()
Definition: BaseType.cc:590
void prepend_dim(int size, const string &name="")
Definition: Array.cc:557
virtual void print_xml(ostream &out, string space=" ", bool constrained=false)
Definition: Array.cc:1089
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:800
virtual int dimension_start(Dim_iter i, bool constrained=false)
Return the start index of a dimension.
Definition: Array.cc:776
virtual AttrTable & get_attr_table()
Definition: BaseType.cc:573
virtual void print_dap4(XMLWriter &xml, bool constrained=false)
Print the DAP4 representation of an array.
Definition: Array.cc:966
virtual ~Array()
The Array destructor.
Definition: Array.cc:169
int size
The unconstrained dimension size.
Definition: Array.h:135
The basic data type for the DODS DAP types.
Definition: BaseType.h:117
Dim_iter dim_begin()
Definition: Array.cc:697
virtual string type_name() const
Returns the type of the class instance as a string.
Definition: BaseType.cc:370
Vars_iter var_begin()
Definition: Constructor.cc:356
virtual void set_attr_table(const AttrTable &at)
Definition: BaseType.cc:581
virtual int length() const
Definition: Vector.cc:555
Vars_iter var_end()
Definition: Constructor.cc:364
virtual void set_length(int l)
Definition: Vector.cc:562
A class for error processing.
Definition: Error.h:90
virtual void set_name(const string &n)
Set the name of this attribute table.
Definition: AttrTable.cc:244
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:1217
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:1293
virtual unsigned int width(bool constrained=false) const
How many bytes does this use Return the number of bytes of storage this variable uses. For scalar types, this is pretty simple (an int32 uses 4 bytes, etc.). For arrays and Constructors, it is a bit more complex. Note that a scalar String variable uses sizeof(String*) bytes, not the length of the string. In other words, the value returned is independent of the type. Also note width() of a String array returns the number of elements in the array times sizeof(String*). That is, each different array size is a different data type.
Definition: BaseType.cc:1292
virtual bool send_p()
Should this variable be sent?
Definition: BaseType.cc:545
virtual void transform_to_dap4(D4Group *root, Constructor *container)
DAP2 to DAP4 transform.
Definition: Array.cc:194
virtual void dump(ostream &strm) const
dumps information about this object
Definition: Vector.cc:2058
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:1200