libdap++  Updated for version 3.12.0
Array.cc
Go to the documentation of this file.
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 
37 #include "config.h"
38 
39 #include <algorithm>
40 #include <functional>
41 #include <sstream>
42 
43 #include "Array.h"
44 #include "util.h"
45 #include "debug.h"
46 #include "InternalErr.h"
47 #include "escaping.h"
48 
49 using namespace std;
50 
51 namespace libdap {
52 
53 void
54 Array::_duplicate(const Array &a)
55 {
56  _shape = a._shape;
57 }
58 
59 // The first method of calculating length works when only one dimension is
60 // constrained and you want the others to appear in total. This is important
61 // when selecting from grids since users may not select from all dimensions
62 // in which case that means they want the whole thing. Array projection
63 // should probably work this way too, but it doesn't. 9/21/2001 jhrg
64 
71 void
72 Array::update_length(int)
73 {
74  int length = 1;
75  for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
76  length *= (*i).c_size > 0 ? (*i).c_size : 1;
77  }
78 
79  set_length(length);
80 }
81 
82 // Construct an instance of Array. The (BaseType *) is assumed to be
83 // allocated using new - The dtor for Vector will delete this object.
84 
100 Array::Array(const string &n, BaseType *v) : Vector(n, 0, dods_array_c)
101 {
102  add_var(v); // Vector::add_var() stores null if v is null
103 }
104 
118 Array::Array(const string &n, const string &d, BaseType *v)
119  : Vector(n, d, 0, dods_array_c)
120 {
121  add_var(v); // Vector::add_var() stores null is v is null
122 }
123 
125 Array::Array(const Array &rhs) : Vector(rhs)
126 {
127  _duplicate(rhs);
128 }
129 
132 {
133  DBG(cerr << "Entering ~Array (" << this << ")" << endl);
134  DBG(cerr << "Exiting ~Array" << endl);
135 }
136 
137 BaseType *
139 {
140  return new Array(*this);
141 }
142 
143 Array &
145 {
146  if (this == &rhs)
147  return *this;
148 
149  dynamic_cast<Vector &>(*this) = rhs;
150 
151  _duplicate(rhs);
152 
153  return *this;
154 }
155 
180 void
182 {
183 #if 0
184  if (v && v->is_dap4_only_type())
185  throw InternalErr(__FILE__, __LINE__, "Attempt to add a DAP4 type to a DAP2 Array.");
186 #endif
187  // If 'v' is an Array, add the template instance to this object and
188  // then copy the dimension information. Odd semantics; I wonder if this
189  //is ever used. jhrg 6/13/12
190  if (v && v->type() == dods_array_c) {
191  Array *a = static_cast<Array*>(v);
192  Vector::add_var(a->var());
193 
194  Dim_iter i = a->dim_begin();
195  Dim_iter i_end = a->dim_end();
196  while (i != i_end) {
198  ++i;
199  }
200  }
201  else {
202  Vector::add_var(v);
203  }
204 }
205 
206 void
208 {
209 #if 0
210  if (v && v->is_dap4_only_type())
211  throw InternalErr(__FILE__, __LINE__, "Attempt to add a DAP4 type to a DAP2 Array.");
212 #endif
213  // If 'v' is an Array, add the template instance to this object and
214  // then copy the dimension information. Odd semantics; I wonder if this
215  //is ever used. jhrg 6/13/12
216  if (v && v->type() == dods_array_c) {
217  Array &a = dynamic_cast<Array&>(*v);
219  Dim_iter i = a.dim_begin();
220  Dim_iter i_end = a.dim_end();
221  while (i != i_end) {
223  ++i;
224  }
225  }
226  else {
228  }
229 }
230 
242 void
243 Array::append_dim(int size, string name)
244 {
245  dimension d;
246 
247  // This is invariant
248  d.size = size;
249  d.name = www2id(name);
250 
251  // this information changes with each constraint expression
252  d.start = 0;
253  d.stop = size - 1;
254  d.stride = 1;
255  d.c_size = size;
256 
257  _shape.push_back(d);
258 
259  update_length(size);
260 }
261 
267 void
268 Array::prepend_dim(int size, const string& name/* = "" */)
269 {
270  dimension d;
271 
272  // This is invariant
273  d.size = size;
274  d.name = www2id(name);
275 
276  // this information changes with each constraint expression
277  d.start = 0;
278  d.stop = size - 1;
279  d.stride = 1;
280  d.c_size = size;
281 
282  // Shifts the whole array, but it's tiny in general
283  _shape.insert(_shape.begin(), d);
284 
285  update_length(size); // the number is ignored...
286 }
287 
291 void
293 {
294  _shape.clear();
295 }
302 void
304 {
305  set_length(-1);
306 
307  for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) {
308  (*i).start = 0;
309  (*i).stop = (*i).size - 1;
310  (*i).stride = 1;
311  (*i).c_size = (*i).size;
312 
313  update_length((*i).size);
314  }
315 }
316 
317 
327 void
329 {
331 }
332 
333 // Note: MS VC++ won't tolerate embedded newlines in strings, hence the \n
334 // is explicit.
335 static const char *array_sss = \
336 "Invalid constraint parameters: At least one of the start, stride or stop \n\
337 specified do not match the array variable.";
338 
358 void
359 Array::add_constraint(Dim_iter i, int start, int stride, int stop)
360 {
361  dimension &d = *i ;
362 
363  // if stop is -1, set it to the array's max element index
364  // jhrg 12/20/12
365  if (stop == -1)
366  stop = d.size - 1;
367 
368  // Check for bad constraints.
369  // Jose Garcia
370  // Usually invalid data for a constraint is the user's mistake
371  // because they build a wrong URL in the client side.
372  if (start >= d.size || stop >= d.size || stride > d.size || stride <= 0)
373  throw Error(malformed_expr, array_sss);
374 
375  if (((stop - start) / stride + 1) > d.size)
376  throw Error(malformed_expr, array_sss);
377 
378  d.start = start;
379  d.stop = stop;
380  d.stride = stride;
381 
382  d.c_size = (stop - start) / stride + 1;
383 
384  DBG(cerr << "add_constraint: c_size = " << d.c_size << endl);
385 
387 }
388 
392 {
393  return _shape.begin() ;
394 }
395 
399 {
400  return _shape.end() ;
401 }
402 
412 unsigned int
413 Array::dimensions(bool /*constrained*/)
414 {
415 #if 0
416  // TODO This could be _shape.end() - _shape.begin()
417  unsigned int dim = 0;
418  for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
419  dim++;
420  }
421 
422  return dim;
423 #endif
424 
425  return _shape.end() - _shape.begin();
426 }
427 
445 int
446 Array::dimension_size(Dim_iter i, bool constrained)
447 {
448  int size = 0;
449 
450  if (!_shape.empty()) {
451  if (constrained)
452  size = (*i).c_size;
453  else
454  size = (*i).size;
455  }
456 
457  return size;
458 }
459 
478 int
479 Array::dimension_start(Dim_iter i, bool /*constrained*/)
480 {
481  return (!_shape.empty()) ? (*i).start : 0;
482 }
483 
502 int
503 Array::dimension_stop(Dim_iter i, bool /*constrained*/)
504 {
505  return (!_shape.empty()) ? (*i).stop : 0;
506 }
507 
527 int
528 Array::dimension_stride(Dim_iter i, bool /*constrained*/)
529 {
530  return (!_shape.empty()) ? (*i).stride : 0;
531 }
532 
543 string
545 {
546  // Jose Garcia
547  // Since this method is public, it is possible for a user
548  // to call it before the Array object has been properly set
549  // this will cause an exception which is the user's fault.
550  // (User in this context is the developer of the surrogate library.)
551  if (_shape.empty())
552  throw InternalErr(__FILE__, __LINE__,
553  "*This* array has no dimensions.");
554  return (*i).name;
555 }
556 
563 unsigned int Array::width(bool constrained)
564 {
565 
566  if (constrained) {
567  // This preserves the original method's semantics when we ask for the
568  // size of the constrained array but no constraint has been applied.
569  // In this case, length will be -1. Wrong, I know...
570  return length() * var()->width(constrained);
571  }
572  else {
573  int length = 1;
574  for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) {
575  length *= dimension_size(i, false);
576  }
577  return length * var()->width(false);
578  }
579 }
580 
598 void
599 Array::print_decl(FILE *out, string space, bool print_semi,
600  bool constraint_info, bool constrained)
601 {
602  ostringstream oss;
603  print_decl(oss, space, print_semi, constraint_info, constrained);
604  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
605 }
606 
624 void
625 Array::print_decl(ostream &out, string space, bool print_semi,
626  bool constraint_info, bool constrained)
627 {
628  if (constrained && !send_p())
629  return;
630 
631  // print it, but w/o semicolon
632  var()->print_decl(out, space, false, constraint_info, constrained);
633 
634  for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
635  out << "[" ;
636  if ((*i).name != "") {
637  out << id2www((*i).name) << " = " ;
638  }
639  if (constrained) {
640  out << (*i).c_size << "]" ;
641  }
642  else {
643  out << (*i).size << "]" ;
644  }
645  }
646 
647  if (print_semi) {
648  out << ";\n" ;
649  }
650 }
651 
655 void
656 Array::print_xml(FILE *out, string space, bool constrained)
657 {
658  XMLWriter xml(space);
659  print_xml_writer_core(xml, constrained, "Array");
660  fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
661 }
662 
666 void
667 Array::print_xml(ostream &out, string space, bool constrained)
668 {
669  XMLWriter xml(space);
670  print_xml_writer_core(xml, constrained, "Array");
671  out << xml.get_doc();
672 }
673 
677 void
678 Array::print_as_map_xml(FILE *out, string space, bool constrained)
679 {
680  XMLWriter xml(space);
681  print_xml_writer_core(xml, constrained, "Map");
682  fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
683 }
684 
688 void
689 Array::print_as_map_xml(ostream &out, string space, bool constrained)
690 {
691  XMLWriter xml(space);
692  print_xml_writer_core(xml, constrained, "Map");
693  out << xml.get_doc();
694 }
695 
699 void
700 Array::print_xml_core(FILE *out, string space, bool constrained, string tag)
701 {
702  XMLWriter xml(space);
703  print_xml_writer_core(xml, constrained, tag);
704  fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
705 }
706 
710 void
711 Array::print_xml_core(ostream &out, string space, bool constrained, string tag)
712 {
713  XMLWriter xml(space);
714  print_xml_writer_core(xml, constrained, tag);
715  out << xml.get_doc();
716 }
717 
718 void
719 Array::print_xml_writer(XMLWriter &xml, bool constrained)
720 {
721  print_xml_writer_core(xml, constrained, "Array");
722 }
723 
724 void
726 {
727  print_xml_writer_core(xml, constrained, "Map");
728 }
729 
730 class PrintArrayDimXMLWriter : public unary_function<Array::dimension&, void>
731 {
732  XMLWriter &xml;
733  bool d_constrained;
734 public:
735  PrintArrayDimXMLWriter(XMLWriter &xml, bool c) : xml(xml), d_constrained(c) {}
736 
737  void operator()(Array::dimension &d)
738  {
739  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)"dimension") < 0)
740  throw InternalErr(__FILE__, __LINE__, "Could not write dimension element");
741 
742  if (!d.name.empty())
743  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)d.name.c_str()) < 0)
744  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
745 
746  ostringstream size;
747  size << (d_constrained ? d.c_size : d.size);
748  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "size", (const xmlChar*)size.str().c_str()) < 0)
749  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
750 
751  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
752  throw InternalErr(__FILE__, __LINE__, "Could not end dimension element");
753  }
754 };
755 
756 void
757 Array::print_xml_writer_core(XMLWriter &xml, bool constrained, string tag)
758 {
759  if (constrained && !send_p())
760  return;
761 
762  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)tag.c_str()) < 0)
763  throw InternalErr(__FILE__, __LINE__, "Could not write " + tag + " element");
764 
765  if (!name().empty())
766  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0)
767  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
768 
770 
771  BaseType *btp = var();
772  string tmp_name = btp->name();
773  btp->set_name("");
774  btp->print_xml_writer(xml, constrained);
775  btp->set_name(tmp_name);
776 
777  for_each(dim_begin(), dim_end(), PrintArrayDimXMLWriter(xml, constrained));
778 
779  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
780  throw InternalErr(__FILE__, __LINE__, "Could not end " + tag + " element");
781 }
782 
794 unsigned int
795 Array::print_array(FILE *out, unsigned int index, unsigned int dims,
796  unsigned int shape[])
797 {
798  ostringstream oss;
799  unsigned int i = print_array(oss, index, dims, shape);
800  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
801 
802  return i;
803 }
804 
816 unsigned int
817 Array::print_array(ostream &out, unsigned int index, unsigned int dims,
818  unsigned int shape[])
819 {
820  if (dims == 1) {
821  out << "{" ;
822  for (unsigned i = 0; i < shape[0] - 1; ++i) {
823  var(index++)->print_val(out, "", false);
824  out << ", " ;
825  }
826  var(index++)->print_val(out, "", false);
827  out << "}" ;
828 
829  return index;
830  }
831  else {
832  out << "{" ;
833  // Fixed an off-by-one error in the following loop. Since the array
834  // length is shape[dims-1]-1 *and* since we want one less dimension
835  // than that, the correct limit on this loop is shape[dims-2]-1. From
836  // Todd Karakasian.
837  // The saga continues; the loop test should be `i < shape[0]-1'. jhrg
838  // 9/12/96.
839  for (unsigned i = 0; i < shape[0] - 1; ++i) {
840  index = print_array(out, index, dims - 1, shape + 1);
841  out << "," ;
842  }
843  index = print_array(out, index, dims - 1, shape + 1);
844  out << "}" ;
845 
846  return index;
847  }
848 }
849 
850 void
851 Array::print_val(FILE *out, string space, bool print_decl_p)
852 {
853  ostringstream oss;
854  print_val(oss, space, print_decl_p);
855  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
856 }
857 
858 void
859 Array::print_val(ostream &out, string space, bool print_decl_p)
860 {
861  // print the declaration if print decl is true.
862  // for each dimension,
863  // for each element,
864  // print the array given its shape, number of dimensions.
865  // Add the `;'
866 
867  if (print_decl_p) {
868  print_decl(out, space, false, false, false);
869  out << " = " ;
870  }
871 
872  unsigned int *shape = new unsigned int[dimensions(true)];
873  unsigned int index = 0;
874  for (Dim_iter i = _shape.begin(); i != _shape.end() && index < dimensions(true); ++i)
875  shape[index++] = dimension_size(i, true);
876 
877  print_array(out, 0, dimensions(true), shape);
878 
879  delete [] shape; shape = 0;
880 
881  if (print_decl_p) {
882  out << ";\n" ;
883  }
884 }
885 
895 bool
896 Array::check_semantics(string &msg, bool)
897 {
898  bool sem = BaseType::check_semantics(msg) && !_shape.empty();
899 
900  if (!sem)
901  msg = "An array variable must have dimensions";
902 
903  return sem;
904 }
905 
914 void
915 Array::dump(ostream &strm) const
916 {
917  strm << DapIndent::LMarg << "Array::dump - ("
918  << (void *)this << ")" << endl ;
920  Vector::dump(strm) ;
921  strm << DapIndent::LMarg << "shape:" << endl ;
923  Dim_citer i = _shape.begin() ;
924  Dim_citer ie = _shape.end() ;
925  unsigned int dim_num = 0 ;
926  for (; i != ie; i++) {
927  strm << DapIndent::LMarg << "dimension " << dim_num++ << ":"
928  << endl ;
930  strm << DapIndent::LMarg << "name: " << (*i).name << endl ;
931  strm << DapIndent::LMarg << "size: " << (*i).size << endl ;
932  strm << DapIndent::LMarg << "start: " << (*i).start << endl ;
933  strm << DapIndent::LMarg << "stop: " << (*i).stop << endl ;
934  strm << DapIndent::LMarg << "stride: " << (*i).stride << endl ;
935  strm << DapIndent::LMarg << "constrained size: " << (*i).c_size
936  << endl ;
938  }
941 }
942 
943 } // namespace libdap
944