libdap  Updated for version 3.20.3
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 #include "DapIndent.h"
61 
62 using namespace std;
63 
64 namespace libdap {
65 
66 Array::dimension::dimension(D4Dimension *d) : 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
78 Array::_duplicate(const Array &a)
79 {
80  _shape = a._shape;
81 
82  // Deep copy the Maps if they are being used.
83  if (a.d_maps) {
84  d_maps = new D4Maps(*(a.d_maps));
85  }
86  else {
87  d_maps = 0;
88  }
89  // d_maps = a.d_maps ? new D4Maps(*(a.d_maps)) : 0;
90 }
91 
92 // The first method of calculating length works when only one dimension is
93 // constrained and you want the others to appear in total. This is important
94 // when selecting from grids since users may not select from all dimensions
95 // in which case that means they want the whole thing. Array projection
96 // should probably work this way too, but it doesn't. 9/21/2001 jhrg
97 
104 void
106 {
107  int length = 1;
108  for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
109 #if 0
110  // If the size of any dimension is zero, then the array is not
111  // capable of storing any values. jhrg 1/28/16
112  length *= (*i).c_size > 0 ? (*i).c_size : 1;
113 #endif
114  length *= (*i).c_size;
115  }
116 
117  set_length(length);
118 }
119 
120 // Construct an instance of Array. The (BaseType *) is assumed to be
121 // allocated using new - The dtor for Vector will delete this object.
122 
138 Array::Array(const string &n, BaseType *v, bool is_dap4 /* default:false */)
139  : Vector(n, 0, dods_array_c, is_dap4), d_maps(0)
140 {
141  add_var(v); // Vector::add_var() stores null if v is null
142 }
143 
157 Array::Array(const string &n, const string &d, BaseType *v, bool is_dap4 /* default:false */)
158  : Vector(n, d, 0, dods_array_c, is_dap4), d_maps(0)
159 {
160  add_var(v); // Vector::add_var() stores null if v is null
161 }
162 
164 Array::Array(const Array &rhs) : Vector(rhs)
165 {
166  _duplicate(rhs);
167 }
168 
171 {
172  delete d_maps;
173 }
174 
175 BaseType *
177 {
178  return new Array(*this);
179 }
180 
181 Array &
182 Array::operator=(const Array &rhs)
183 {
184  if (this == &rhs)
185  return *this;
186 
187  dynamic_cast<Vector &>(*this) = rhs;
188 
189  _duplicate(rhs);
190 
191  return *this;
192 }
193 
194 void
196 {
197  DBG(cerr << __func__ << "() - BEGIN (array:" << name() << ")" << endl;);
198  Array *dest = static_cast<Array*>(ptr_duplicate());
199 
200  // If it's already a DAP4 object then we can just return it!
201  if(is_dap4()){
202  DBG(cerr << __func__ << "() - Already DAP4 type: Just making a copy and adding to container. " << endl;);
203  container->add_var_nocopy(dest);
204  DBG(cerr << __func__ << "() - END (Already DAP4 type)" << endl;);
205  }
206  // Process the Array's dimensions, making D4 shared dimensions for
207  // D2 dimensions that are named. If there is just a size, don't make
208  // a D4Dimension (In DAP4 you cannot share a dimension unless it has
209  // a name). jhrg 3/18/14
210 
211  D4Dimensions *root_dims = root->dims();
212  for (Array::Dim_iter dap2_dim = dest->dim_begin(), e = dest->dim_end(); dap2_dim != e; ++dap2_dim) {
213  if (!(*dap2_dim).name.empty()) {
214  DBG(cerr << __func__ << "() - Processing the array dimension '" << (*dap2_dim).name << "'" << endl;);
215 
216  // If a D4Dimension with the name already exists, use it.
217  D4Dimension *d4_dim = root_dims->find_dim((*dap2_dim).name);
218  if (!d4_dim) {
219  d4_dim = new D4Dimension((*dap2_dim).name, (*dap2_dim).size);
220  root_dims->add_dim_nocopy(d4_dim);
221  DBG(cerr << __func__ << "() -" <<
222  " Added NEW D4Dimension '"<< d4_dim->name() << "' (" <<
223  (void *)d4_dim << ") to root->dims()"<< endl;);
224  }
225  else {
226  DBG(cerr << __func__ << "() -" <<
227  " Using Existing D4Dimension '"<< d4_dim->name() << "' (" <<
228  (void *)d4_dim << ")"<< endl;);
229 
230  if (d4_dim->size() != (unsigned long) (*dap2_dim).size) {
231  // TODO Revisit this decision. jhrg 3/18/14
232  // ...in case the name/size are different, make a unique D4Dimension
233  // but don't fiddle with the name. Not sure I like this idea, so I'm
234  // making the case explicit (could be rolled in to the block above).
235  // jhrg 3/18/14
236  //
237  // This is causing problems in the FITS handler because there are cases
238  // where two arrays have dimensions with the same name but different
239  // sizes. The deserializing code is using the first size listed, which is
240  // wrong in some cases. I'm going to try making this new D4Dimension using
241  // the dim name along with the variable name. jhrg 8/15/14
242  d4_dim = new D4Dimension((*dap2_dim).name + "_" + name(), (*dap2_dim).size);
243  DBG(cerr << __func__ << "() -" <<
244  " Utilizing Name/Size Conflict Naming Artifice. name'"<< d4_dim->name() << "' (" <<
245  (void *)d4_dim << ")"<< endl;);
246  root_dims->add_dim_nocopy(d4_dim);
247  }
248  }
249  // At this point d4_dim's name and size == those of (*d) so just set
250  // the D4Dimension pointer so it matches the one in the D4Group.
251  (*dap2_dim).dim = d4_dim;
252  }
253 
254  }
255 
256  // Copy the D2 attributes to D4 Attributes
257  dest->attributes()->transform_to_dap4(get_attr_table());
258  dest->set_is_dap4(true);
259  container->add_var_nocopy(dest);
260  DBG(cerr << __func__ << "() - END (array:" << name() << ")" << endl;);
261 }
262 
263 bool Array::is_dap2_grid(){
264  bool is_grid = false;
265  if(this->is_dap4()){
266  DBG( cerr << __func__ << "() - Array '"<< name() << "' is DAP4 object!" << endl;)
267  D4Maps *d4_maps = this->maps();
268  is_grid = d4_maps->size(); // It can't be a grid if there are no maps...
269  if(is_grid){
270  DBG( cerr << __func__ << "() - Array '"<< name() << "' has D4Maps." << endl;)
271  // hmmm this might be a DAP2 Grid...
272  D4Maps::D4MapsIter i = d4_maps->map_begin();
273  D4Maps::D4MapsIter e = d4_maps->map_end();
274  while(i!=e){
275  DBG( cerr << __func__ << "() - Map '"<< (*i)->array()->name() << " has " << (*i)->array()->_shape.size() << " dimension(s)." << endl;)
276  if((*i)->array()->_shape.size() > 1){
277  is_grid = false;
278  i = e;
279  }
280  else {
281  i++;
282  }
283  }
284  }
285  else {
286  DBG( cerr << __func__ << "() - Array '"<< name() << "' has no D4Maps." << endl;)
287  }
288  }
289  DBG( cerr << __func__ << "() - is_grid: "<< (is_grid?"true":"false") << endl;)
290  return is_grid;
291 }
292 
308 std::vector<BaseType *> *
310  DBG(cerr << __func__ << "() - BEGIN Array '"<< name() << "'" << endl;);
311 
312  BaseType *dest;
313  if(is_dap4()){ // Don't convert a DAP2 thing
314 
315  // Can Precious be represented as a DAP2 Grid
316  if(is_dap2_grid()){
317  // Oh yay! Grids are special.
318  DBG(cerr << __func__ << "() - Array '"<< name() << "' is dap2 Grid!" << endl;);
319  Grid *g = new Grid(name());
320  dest = g;
321  Array *grid_array = (Array *) this->ptr_duplicate();
322  g->set_array(grid_array);
323 
324  // Get the metadata into the Grid Array
325  AttrTable *grid_attrs = attributes()->get_AttrTable(name());
326  grid_array->set_attr_table(*grid_attrs); // Copy it into the Grid object.
327  // grid_array->set_attr_table(*grid_attrs); // Copy it into the data Array.
328  delete grid_attrs;
329 
330  // Clear the Grid attributes.
331  AttrTable at;
332  at.set_name(name());
333  g->set_attr_table(at);
334 
335  // Process the Map Arrays.
336  D4Maps *d4_maps = this->maps();
337  vector<BaseType *> dropped_maps;
338  D4Maps::D4MapsIter miter = d4_maps->map_begin();
339  D4Maps::D4MapsIter end = d4_maps->map_end();
340  for( ; miter!=end; miter++){
341  D4Map *d4_map = (*miter);
342  Array *d4_map_array = const_cast<Array*>(d4_map->array());
343  vector<BaseType *> *d2_result = d4_map_array->transform_to_dap2(&(g->get_attr_table()));
344  if(d2_result){
345  if(d2_result->size()>1)
346  throw Error(internal_error,string(__func__)+"() - ERROR: D4Map Array conversion resulted in multiple DAP2 objects.");
347 
348  // TODO - This is probably slow and needs a better pattern. const_cast? static_cast?
349  Array *d2_map_array = dynamic_cast<Array *>((*d2_result)[0]);
350  if(d2_map_array){
351  if(d2_map_array->dimensions()!=1)
352  throw Error(internal_error,string(__func__)+"() - ERROR: DAP2 array from D4Map Array conversion has more than 1 dimension.");
353 
354  g->add_map(d2_map_array,false);
355  AttrTable at = d2_map_array->get_attr_table();
356  DBG( cerr << __func__ << "() - " <<
357  "DAS For Grid Map '" << d2_map_array->name() << "':" << endl;
358  at.print(cerr); );
359  }
360  else {
361  throw Error(internal_error,string(__func__)+"() - Unable to interpret returned DAP2 content.");
362  }
363  delete d2_result;
364  }
365  else {
366  dropped_maps.push_back(d4_map_array);
367  }
368  }
369 
370  // Did we have a transform failure?
371  if(!dropped_maps.empty()){
372  // Yup... tell the story in the attributes.
373  AttrTable *dv_table = Constructor::make_dropped_vars_attr_table(&dropped_maps);
374  dest->get_attr_table().append_container(dv_table,dv_table->get_name());
375  }
376  }
377  else {
378  // It's not a Grid so we can make a simple copy of our Precious.
379  DBG( cerr << __func__ << "() - Array '"<< name() << "' is not a Grid!" << endl);
380  BaseType *proto = this->prototype();
381  switch(proto->type()){
382  case dods_int64_c:
383  case dods_uint64_c:
384  case dods_enum_c:
385  case dods_opaque_c:
386  {
387  // For now we punt on these type as they have no easy representation in
388  // the DAP2 data model. By setting this to NULL we cause the Array to be
389  // dropped and this will be reflected in the metadata (DAS).
390  dest = NULL;
391  break;
392  }
393  default:
394  {
395  dest = this->ptr_duplicate();
396  // convert the d4 attributes to a dap2 attribute table.
397  AttrTable *attrs = this->attributes()->get_AttrTable(name());
398  dest->set_attr_table(*attrs);
399  dest->set_is_dap4(false);
400  AttrTable at = dest->get_attr_table();
401  DBG( cerr << __func__ << "() - " <<
402  "DAS for new Array '" << dest->name() << "':" << endl;
403  at.print(cerr); )
404 
405  break;
406  }
407  }
408  }
409 
410  }
411  else {
412  // If it's a DAP2 Array already then we just make a copy of our Precious.
413  dest = this->ptr_duplicate();
414  }
415  // attrs->print(cerr,"",true);
416  vector<BaseType *> *result;
417  if(dest){
418  result = new vector<BaseType *>();
419  result->push_back(dest);
420  }
421  else {
422  result = NULL;
423  }
424  DBG( cerr << __func__ << "() - END Array '"<< name() << "'" << endl;);
425  return result;
426 }
427 
439 void
440 Array::update_dimension_pointers(D4Dimensions *old_dims, D4Dimensions *new_dims)
441 {
442  std::vector<dimension>::iterator i = _shape.begin(), e = _shape.end();
443  while (i != e) {
444  D4Dimensions::D4DimensionsIter old_i = old_dims->dim_begin(), old_e = old_dims->dim_end();
445  while (old_i != old_e) {
446  if ((*i).dim == *old_i) {
447  (*i).dim = new_dims->find_dim((*old_i)->name());
448  }
449  ++old_i;
450  }
451 
452  ++i;
453  }
454 }
455 
480 void
482 {
483  // If 'v' is an Array, add the template instance to this object and
484  // then copy the dimension information. Odd semantics; I wonder if this
485  //is ever used. jhrg 6/13/12
486  if (v && v->type() == dods_array_c) {
487  Array *a = static_cast<Array*>(v);
488  Vector::add_var(a->var());
489 
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(v);
499  }
500 }
501 
502 void
503 Array::add_var_nocopy(BaseType *v, Part)
504 {
505  // If 'v' is an Array, add the template instance to this object and
506  // then copy the dimension information. Odd semantics; I wonder if this
507  //is ever used. jhrg 6/13/12
508  if (v && v->type() == dods_array_c) {
509  Array &a = dynamic_cast<Array&>(*v);
510  Vector::add_var_nocopy(a.var());
511  Dim_iter i = a.dim_begin();
512  Dim_iter i_end = a.dim_end();
513  while (i != i_end) {
515  ++i;
516  }
517  }
518  else {
519  Vector::add_var_nocopy(v);
520  }
521 }
522 
534 void
535 Array::append_dim(int size, const string &name)
536 {
537  dimension d(size, www2id(name));
538  _shape.push_back(d);
539 
540  update_length();
541 }
542 
543 void
545 {
546  dimension d(/*dim->size(), www2id(dim->name()),*/ dim);
547  _shape.push_back(d);
548 
549  update_length();
550 }
551 
557 void
558 Array::prepend_dim(int size, const string& name/* = "" */)
559 {
560  dimension d(size, www2id(name));
561  // Shifts the whole array, but it's tiny in general
562  _shape.insert(_shape.begin(), d);
563 
564  update_length(); // the number is ignored...
565 }
566 
567 void
569 {
570  dimension d(/*dim->size(), www2id(dim->name()),*/ dim);
571  // Shifts the whole array, but it's tiny in general
572  _shape.insert(_shape.begin(), d);
573 
574  update_length(); // the number is ignored...
575 }
576 
580 void
582 {
583  _shape.clear();
584 }
585 
591 void
592 Array::rename_dim(const string &oldName, const string &newName)
593 {
594  std::vector<dimension>::iterator i = _shape.begin(), e = _shape.end();
595  while (i != e) {
596  dimension &d = *i ;
597  if(d.name == oldName){
598  DBG(cerr << "Old name = " << d.name << " newName = " << newName << endl);
599  d.name = newName;
600  }
601 
602  ++i;
603  }
604 }
605 
611 void
613 {
614  set_length(-1);
615 
616  for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) {
617  (*i).start = 0;
618  (*i).stop = (*i).size - 1;
619  (*i).stride = 1;
620  (*i).c_size = (*i).size;
621 
622  update_length();
623  }
624 }
625 
626 
636 void
638 {
640 }
641 
642 // Note: MS VC++ won't tolerate embedded newlines in strings, hence the \n
643 // is explicit.
644 static const char *array_sss = \
645 "Invalid constraint parameters: At least one of the start, stride or stop \n\
646 specified do not match the array variable.";
647 
668 void
669 Array::add_constraint(Dim_iter i, int start, int stride, int stop)
670 {
671  dimension &d = *i ;
672 
673  // if stop is -1, set it to the array's max element index
674  // jhrg 12/20/12
675  if (stop == -1)
676  stop = d.size - 1;
677 
678  // Check for bad constraints.
679  // Jose Garcia
680  // Usually invalid data for a constraint is the user's mistake
681  // because they build a wrong URL in the client side.
682  if (start >= d.size || stop >= d.size || stride > d.size || stride <= 0)
683  throw Error(malformed_expr, array_sss);
684 
685  if (((stop - start) / stride + 1) > d.size)
686  throw Error(malformed_expr, array_sss);
687 
688  d.start = start;
689  d.stop = stop;
690  d.stride = stride;
691 
692  d.c_size = (stop - start) / stride + 1;
693 
694  DBG(cerr << "add_constraint: c_size = " << d.c_size << endl);
695 
696  update_length();
697 
698  d.use_sdim_for_slice = false;
699 }
700 
701 void
703 {
704  dimension &d = *i ;
705 
706  if (dim->constrained())
707  add_constraint(i, dim->c_start(), dim->c_stride(), dim->c_stop());
708 
709  dim->set_used_by_projected_var(true);
710 
711  // In this case the value below overrides the value for use_sdim_for_slice
712  // set in the above call. jhrg 12/20/13
713  d.use_sdim_for_slice = true;
714 }
715 
719 {
720  return _shape.begin() ;
721 }
722 
726 {
727  return _shape.end() ;
728 }
729 
730 //TODO Many of these methods take a bool parameter that serves no use; remove.
731 
740 unsigned int
741 Array::dimensions(bool /*constrained*/)
742 {
743  return _shape.size();
744 }
745 
763 int
764 Array::dimension_size(Dim_iter i, bool constrained)
765 {
766  int size = 0;
767 
768  if (!_shape.empty()) {
769  if (constrained)
770  size = (*i).c_size;
771  else
772  size = (*i).size;
773  }
774 
775  return size;
776 }
777 
796 int
797 Array::dimension_start(Dim_iter i, bool /*constrained*/)
798 {
799  return (!_shape.empty()) ? (*i).start : 0;
800 }
801 
820 int
821 Array::dimension_stop(Dim_iter i, bool /*constrained*/)
822 {
823  return (!_shape.empty()) ? (*i).stop : 0;
824 }
825 
845 int
846 Array::dimension_stride(Dim_iter i, bool /*constrained*/)
847 {
848  return (!_shape.empty()) ? (*i).stride : 0;
849 }
850 
861 string
863 {
864  // Jose Garcia
865  // Since this method is public, it is possible for a user
866  // to call it before the Array object has been properly set
867  // this will cause an exception which is the user's fault.
868  // (User in this context is the developer of the surrogate library.)
869  if (_shape.empty())
870  throw InternalErr(__FILE__, __LINE__,
871  "*This* array has no dimensions.");
872  return (*i).name;
873 }
874 
875 D4Dimension *
876 Array::dimension_D4dim(Dim_iter i)
877 {
878  return (!_shape.empty()) ? (*i).dim : 0;
879 }
880 
881 D4Maps *
882 Array::maps()
883 {
884  if (!d_maps) d_maps = new D4Maps(this); // init with this as parent
885  return d_maps;
886 }
887 
888 #if 0
889 
895 unsigned int Array::width(bool constrained) const
896 {
897 
898  if (constrained) {
899  // This preserves the original method's semantics when we ask for the
900  // size of the constrained array but no constraint has been applied.
901  // In this case, length will be -1. Wrong, I know...
902  return length() * var()->width(constrained);
903  }
904  else {
905  int length = 1;
906  for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) {
907  length *= dimension_size(i, false);
908  }
909  return length * var()->width(false);
910  }
911 }
912 #endif
913 
914 class PrintD4ArrayDimXMLWriter: public unary_function<Array::dimension&, void> {
915  XMLWriter &xml;
916  // Was this variable constrained using local/direct slicing? i.e., is d_local_constraint set?
917  // If so, don't use shared dimensions; instead emit Dim elements that are anonymous.
918  bool d_constrained;
919 public:
920 
921  PrintD4ArrayDimXMLWriter(XMLWriter &xml, bool c) : xml(xml), d_constrained(c) { }
922 
923  void operator()(Array::dimension &d)
924  {
925  // This duplicates code in D4Dimensions (where D4Dimension::print_dap4() is defined
926  // because of the need to print the constrained size of a dimension. I think that
927  // the constraint information has to be kept here and not in the dimension (since they
928  // are shared dims). Could hack print_dap4() to take the constrained size, however.
929  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Dim") < 0)
930  throw InternalErr(__FILE__, __LINE__, "Could not write Dim element");
931 
932  string name = (d.dim) ? d.dim->fully_qualified_name() : d.name;
933  // If there is a name, there must be a Dimension (named dimension) in scope
934  // so write its name but not its size.
935  if (!d_constrained && !name.empty()) {
936  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name.c_str())
937  < 0) throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
938  }
939  else if (d.use_sdim_for_slice) {
940  assert(!name.empty());
941  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name.c_str())
942  < 0) throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
943  }
944  else {
945  ostringstream size;
946  size << (d_constrained ? d.c_size : d.size);
947  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "size",
948  (const xmlChar*) size.str().c_str()) < 0)
949  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
950  }
951 
952  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
953  throw InternalErr(__FILE__, __LINE__, "Could not end Dim element");
954  }
955 };
956 
957 class PrintD4ConstructorVarXMLWriter: public unary_function<BaseType*, void> {
958  XMLWriter &xml;
959  bool d_constrained;
960 public:
961  PrintD4ConstructorVarXMLWriter(XMLWriter &xml, bool c) : xml(xml), d_constrained(c) { }
962 
963  void operator()(BaseType *btp)
964  {
965  btp->print_dap4(xml, d_constrained);
966  }
967 };
968 
969 class PrintD4MapXMLWriter: public unary_function<D4Map*, void> {
970  XMLWriter &xml;
971 
972 public:
973  PrintD4MapXMLWriter(XMLWriter &xml) : xml(xml) { }
974 
975  void operator()(D4Map *m)
976  {
977  m->print_dap4(xml);
978  }
979 };
980 
986 void
987 Array::print_dap4(XMLWriter &xml, bool constrained /* default: false*/)
988 {
989  if (constrained && !send_p()) return;
990 
991  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) var()->type_name().c_str()) < 0)
992  throw InternalErr(__FILE__, __LINE__, "Could not write " + type_name() + " element");
993 
994  if (!name().empty())
995  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0)
996  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
997 
998  // Hack job... Copied from D4Enum::print_xml_writer. jhrg 11/12/13
999  if (var()->type() == dods_enum_c) {
1000  D4Enum *e = static_cast<D4Enum*>(var());
1001  string path = e->enumeration()->name();
1002  if (e->enumeration()->parent()) {
1003  // print the FQN for the enum def; D4Group::FQN() includes the trailing '/'
1004  path = static_cast<D4Group*>(e->enumeration()->parent()->parent())->FQN() + path;
1005  }
1006  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "enum", (const xmlChar*)path.c_str()) < 0)
1007  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for enum");
1008  }
1009 
1010  if (prototype()->is_constructor_type()) {
1011  Constructor &c = static_cast<Constructor&>(*prototype());
1012  for_each(c.var_begin(), c.var_end(), PrintD4ConstructorVarXMLWriter(xml, constrained));
1013  // bind2nd(mem_fun_ref(&BaseType::print_dap4), xml));
1014  }
1015 
1016  // Drop the local_constraint which is per-array and use a per-dimension on instead
1017  for_each(dim_begin(), dim_end(), PrintD4ArrayDimXMLWriter(xml, constrained));
1018 
1019  attributes()->print_dap4(xml);
1020 
1021  for_each(maps()->map_begin(), maps()->map_end(), PrintD4MapXMLWriter(xml));
1022 
1023  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
1024  throw InternalErr(__FILE__, __LINE__, "Could not end " + type_name() + " element");
1025 }
1026 
1044 void
1045 Array::print_decl(FILE *out, string space, bool print_semi,
1046  bool constraint_info, bool constrained)
1047 {
1048  ostringstream oss;
1049  print_decl(oss, space, print_semi, constraint_info, constrained);
1050  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
1051 }
1052 
1070 void Array::print_decl(ostream &out, string space, bool print_semi, bool constraint_info, bool constrained)
1071 {
1072  if (constrained && !send_p()) return;
1073 
1074  // print it, but w/o semicolon
1075  var()->print_decl(out, space, false, constraint_info, constrained);
1076 
1077  for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
1078  out << "[";
1079  if ((*i).name != "") {
1080  out << id2www((*i).name) << " = ";
1081  }
1082  if (constrained) {
1083  out << (*i).c_size << "]";
1084  }
1085  else {
1086  out << (*i).size << "]";
1087  }
1088  }
1089 
1090  if (print_semi) {
1091  out << ";\n";
1092  }
1093 }
1094 
1098 void
1099 Array::print_xml(FILE *out, string space, bool constrained)
1100 {
1101  XMLWriter xml(space);
1102  print_xml_writer_core(xml, constrained, "Array");
1103  fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
1104 }
1105 
1109 void
1110 Array::print_xml(ostream &out, string space, bool constrained)
1111 {
1112  XMLWriter xml(space);
1113  print_xml_writer_core(xml, constrained, "Array");
1114  out << xml.get_doc();
1115 }
1116 
1120 void
1121 Array::print_as_map_xml(FILE *out, string space, bool constrained)
1122 {
1123  XMLWriter xml(space);
1124  print_xml_writer_core(xml, constrained, "Map");
1125  fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
1126 }
1127 
1131 void
1132 Array::print_as_map_xml(ostream &out, string space, bool constrained)
1133 {
1134  XMLWriter xml(space);
1135  print_xml_writer_core(xml, constrained, "Map");
1136  out << xml.get_doc();
1137 }
1138 
1142 void
1143 Array::print_xml_core(FILE *out, string space, bool constrained, string tag)
1144 {
1145  XMLWriter xml(space);
1146  print_xml_writer_core(xml, constrained, tag);
1147  fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
1148 }
1149 
1153 void
1154 Array::print_xml_core(ostream &out, string space, bool constrained, string tag)
1155 {
1156  XMLWriter xml(space);
1157  print_xml_writer_core(xml, constrained, tag);
1158  out << xml.get_doc();
1159 }
1160 
1161 void
1162 Array::print_xml_writer(XMLWriter &xml, bool constrained)
1163 {
1164  print_xml_writer_core(xml, constrained, "Array");
1165 }
1166 
1167 void
1168 Array::print_as_map_xml_writer(XMLWriter &xml, bool constrained)
1169 {
1170  print_xml_writer_core(xml, constrained, "Map");
1171 }
1172 
1173 class PrintArrayDimXMLWriter : public unary_function<Array::dimension&, void>
1174 {
1175  XMLWriter &xml;
1176  bool d_constrained;
1177 public:
1178  PrintArrayDimXMLWriter(XMLWriter &xml, bool c) : xml(xml), d_constrained(c) {}
1179 
1180  void operator()(Array::dimension &d)
1181  {
1182  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)"dimension") < 0)
1183  throw InternalErr(__FILE__, __LINE__, "Could not write dimension element");
1184 
1185  if (!d.name.empty())
1186  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)d.name.c_str()) < 0)
1187  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1188 
1189  ostringstream size;
1190  size << (d_constrained ? d.c_size : d.size);
1191  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "size", (const xmlChar*)size.str().c_str()) < 0)
1192  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1193 
1194  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
1195  throw InternalErr(__FILE__, __LINE__, "Could not end dimension element");
1196  }
1197 };
1198 
1199 void
1200 Array::print_xml_writer_core(XMLWriter &xml, bool constrained, string tag)
1201 {
1202  if (constrained && !send_p())
1203  return;
1204 
1205  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)tag.c_str()) < 0)
1206  throw InternalErr(__FILE__, __LINE__, "Could not write " + tag + " element");
1207 
1208  if (!name().empty())
1209  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0)
1210  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
1211 
1213 
1214  BaseType *btp = var();
1215  string tmp_name = btp->name();
1216  btp->set_name("");
1217  btp->print_xml_writer(xml, constrained);
1218  btp->set_name(tmp_name);
1219 
1220  for_each(dim_begin(), dim_end(), PrintArrayDimXMLWriter(xml, constrained));
1221 
1222  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
1223  throw InternalErr(__FILE__, __LINE__, "Could not end " + tag + " element");
1224 }
1225 
1237 unsigned int
1238 Array::print_array(FILE *out, unsigned int index, unsigned int dims,
1239  unsigned int shape[])
1240 {
1241  ostringstream oss;
1242  unsigned int i = print_array(oss, index, dims, shape);
1243  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
1244 
1245  return i;
1246 }
1247 
1259 unsigned int Array::print_array(ostream &out, unsigned int index, unsigned int dims, unsigned int shape[])
1260 {
1261  if (dims == 1) {
1262  out << "{";
1263 
1264  // Added test in case this method is passed an array with no elements. jhrg 1/27/16
1265  if (shape[0] >= 1) {
1266  for (unsigned i = 0; i < shape[0] - 1; ++i) {
1267  var(index++)->print_val(out, "", false);
1268  out << ", ";
1269  }
1270  var(index++)->print_val(out, "", false);
1271  }
1272 
1273  out << "}";
1274 
1275  return index;
1276  }
1277  else {
1278  out << "{";
1279  // Fixed an off-by-one error in the following loop. Since the array
1280  // length is shape[dims-1]-1 *and* since we want one less dimension
1281  // than that, the correct limit on this loop is shape[dims-2]-1. From
1282  // Todd Karakasian.
1283  //
1284  // The saga continues; the loop test should be `i < shape[0]-1'. jhrg
1285  // 9/12/96.
1286  //
1287  // For arrays that hold zero values but have rank > 1, the print out
1288  // may look a little odd (e.g., x[4][0] will print as { {}, {}, {}, {} })
1289  // but it's not wrong and this is really for debugging mostly. jhrg 1/28/16
1290  if (shape[0] > 0) {
1291  for (unsigned i = 0; i < shape[0] - 1; ++i) {
1292  index = print_array(out, index, dims - 1, shape + 1);
1293  out << ",";
1294  }
1295 
1296  index = print_array(out, index, dims - 1, shape + 1);
1297  }
1298 
1299  out << "}";
1300 
1301  return index;
1302  }
1303 }
1304 
1305 void
1306 Array::print_val(FILE *out, string space, bool print_decl_p)
1307 {
1308  ostringstream oss;
1309  print_val(oss, space, print_decl_p);
1310  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
1311 }
1312 
1313 void
1314 Array::print_val(ostream &out, string space, bool print_decl_p)
1315 {
1316  // print the declaration if print decl is true.
1317  // for each dimension,
1318  // for each element,
1319  // print the array given its shape, number of dimensions.
1320  // Add the `;'
1321 
1322  if (print_decl_p) {
1323  print_decl(out, space, false, false, false);
1324  out << " = " ;
1325  }
1326 
1327  unsigned int *shape = new unsigned int[dimensions(true)];
1328  unsigned int index = 0;
1329  for (Dim_iter i = _shape.begin(); i != _shape.end() && index < dimensions(true); ++i)
1330  shape[index++] = dimension_size(i, true);
1331 
1332  print_array(out, 0, dimensions(true), shape);
1333 
1334  delete [] shape; shape = 0;
1335 
1336  if (print_decl_p) {
1337  out << ";\n" ;
1338  }
1339 }
1340 
1350 bool
1351 Array::check_semantics(string &msg, bool)
1352 {
1353  bool sem = BaseType::check_semantics(msg) && !_shape.empty();
1354 
1355  if (!sem)
1356  msg = "An array variable must have dimensions";
1357 
1358  return sem;
1359 }
1360 
1369 void
1370 Array::dump(ostream &strm) const
1371 {
1372  strm << DapIndent::LMarg << "Array::dump - ("
1373  << (void *)this << ")" << endl ;
1374  DapIndent::Indent() ;
1375  Vector::dump(strm) ;
1376  strm << DapIndent::LMarg << "shape:" << endl ;
1377  DapIndent::Indent() ;
1378  Dim_citer i = _shape.begin() ;
1379  Dim_citer ie = _shape.end() ;
1380  unsigned int dim_num = 0 ;
1381  for (; i != ie; i++) {
1382  strm << DapIndent::LMarg << "dimension " << dim_num++ << ":"
1383  << endl ;
1384  DapIndent::Indent() ;
1385  strm << DapIndent::LMarg << "name: " << (*i).name << endl ;
1386  strm << DapIndent::LMarg << "size: " << (*i).size << endl ;
1387  strm << DapIndent::LMarg << "start: " << (*i).start << endl ;
1388  strm << DapIndent::LMarg << "stop: " << (*i).stop << endl ;
1389  strm << DapIndent::LMarg << "stride: " << (*i).stride << endl ;
1390  strm << DapIndent::LMarg << "constrained size: " << (*i).c_size
1391  << endl ;
1392  DapIndent::UnIndent() ;
1393  }
1394  DapIndent::UnIndent() ;
1395  DapIndent::UnIndent() ;
1396 }
1397 
1398 } // namespace libdap
1399 
virtual void print_xml_writer(XMLWriter &xml, bool constrained=false)
Definition: Array.cc:1162
virtual void reset_constraint()
Reset constraint to select entire array.
Definition: Array.cc:612
virtual void add_constraint(Dim_iter i, int start, int stride, int stop)
Adds a constraint to an Array dimension.
Definition: Array.cc:669
virtual string name() const
Returns the name of the class instance.
Definition: BaseType.cc:312
virtual bool check_semantics(string &msg, bool all=false)
Check semantic features of the Array.
Definition: Array.cc:1351
virtual void print_xml_core(FILE *out, string space, bool constrained, string tag)
Definition: Array.cc:1143
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:1370
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:995
Array(const string &n, BaseType *v, bool is_dap4=false)
Array constructor.
Definition: Array.cc:138
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:741
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:481
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:336
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:1126
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:1946
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:535
virtual void update_length(int size=0)
Definition: Array.cc:105
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: AlarmHandler.h:35
A class for software fault reporting.
Definition: InternalErr.h:64
Dim_iter dim_end()
Definition: Array.cc:725
virtual std::string FQN() const
Definition: BaseType.cc:324
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:1132
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:309
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:764
virtual void print_val(FILE *out, string space="", bool print_decl_p=true)
Prints the value of the variable.
Definition: BaseType.cc:1082
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:404
virtual AttrTable * get_attr_table(const string &name)
Get an attribute container.
Definition: AttrTable.cc:607
virtual Type type() const
Returns the type of the class instance.
Definition: BaseType.cc:357
std::vector< dimension >::iterator Dim_iter
Definition: Array.h:206
void clear_all_dims()
Definition: Array.cc:581
virtual string dimension_name(Dim_iter i)
Returns the name of the specified dimension.
Definition: Array.cc:862
virtual BaseType * ptr_duplicate()
Definition: Array.cc:176
virtual int dimension_stride(Dim_iter i, bool constrained=false)
Returns the stride value of the constraint.
Definition: Array.cc:846
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:1070
int c_size
Size of dimension once constrained.
Definition: Array.h:151
virtual D4Attributes * attributes()
Definition: BaseType.cc:591
void prepend_dim(int size, const string &name="")
Definition: Array.cc:558
virtual void print_xml(ostream &out, string space=" ", bool constrained=false)
Definition: Array.cc:1110
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:821
virtual int dimension_start(Dim_iter i, bool constrained=false)
Return the start index of a dimension.
Definition: Array.cc:797
virtual AttrTable & get_attr_table()
Definition: BaseType.cc:574
virtual void print_dap4(XMLWriter &xml, bool constrained=false)
Print the DAP4 representation of an array.
Definition: Array.cc:987
virtual ~Array()
The Array destructor.
Definition: Array.cc:170
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:718
virtual string type_name() const
Returns the type of the class instance as a string.
Definition: BaseType.cc:371
Vars_iter var_begin()
Definition: Constructor.cc:356
virtual void set_attr_table(const AttrTable &at)
Definition: BaseType.cc:582
virtual int length() const
Definition: Vector.cc:548
void rename_dim(const string &oldName="", const string &newName="")
Renames dimension.
Definition: Array.cc:592
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 void set_name(const string &n)
Set the name of this attribute table.
Definition: AttrTable.cc:245
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:1238
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:1314
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:1293
virtual bool send_p()
Should this variable be sent?
Definition: BaseType.cc:546
virtual void transform_to_dap4(D4Group *root, Constructor *container)
DAP2 to DAP4 transform.
Definition: Array.cc:195
virtual void dump(ostream &strm) const
dumps information about this object
Definition: Vector.cc:2029
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:637
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:1201