libdap++  Updated for version 3.8.2
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 is 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 
175 void
177 {
178  if (v && v->type() == dods_array_c) {
179  Array &a = dynamic_cast<Array&>(*v);
180  Vector::add_var(a.var());
181  Dim_iter i = a.dim_begin();
182  Dim_iter i_end = a.dim_end();
183  while (i != i_end) {
185  ++i;
186  }
187  }
188  else {
189  Vector::add_var(v);
190  }
191 }
192 
204 void
205 Array::append_dim(int size, string name)
206 {
207  dimension d;
208 
209  // This is invariant
210  d.size = size;
211  d.name = www2id(name);
212 
213  // this information changes with each constraint expression
214  d.start = 0;
215  d.stop = size - 1;
216  d.stride = 1;
217  d.c_size = size;
218 
219  _shape.push_back(d);
220 
221  update_length(size);
222 }
223 
229 void
230 Array::prepend_dim(int size, const string& name/* = "" */)
231 {
232  dimension d;
233 
234  // This is invariant
235  d.size = size;
236  d.name = www2id(name);
237 
238  // this information changes with each constraint expression
239  d.start = 0;
240  d.stop = size - 1;
241  d.stride = 1;
242  d.c_size = size;
243 
244  // Shifts the whole array, but it's tiny in general
245  _shape.insert(_shape.begin(), d);
246 
247  update_length(size); // the number is ignored...
248 }
249 
256 void
258 {
259  set_length(-1);
260 
261  for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) {
262  (*i).start = 0;
263  (*i).stop = (*i).size - 1;
264  (*i).stride = 1;
265  (*i).c_size = (*i).size;
266 
267  update_length((*i).size);
268  }
269 }
270 
271 
281 void
283 {
285 }
286 
287 // Note: MS VC++ won't tolerate embedded newlines in strings, hence the \n
288 // is explicit.
289 static const char *array_sss = \
290 "Invalid constraint parameters: At least one of the start, stride or stop \n\
291 specified do not match the array variable.";
292 
312 void
313 Array::add_constraint(Dim_iter i, int start, int stride, int stop)
314 {
315  dimension &d = *i ;
316 
317  // Check for bad constraints.
318  // Jose Garcia
319  // Usually invalid data for a constraint is the user's mistake
320  // because they build a wrong URL in the client side.
321  if (start >= d.size || stop >= d.size || stride > d.size || stride <= 0)
322  throw Error(malformed_expr, array_sss);
323 
324  if (((stop - start) / stride + 1) > d.size)
325  throw Error(malformed_expr, array_sss);
326 
327  d.start = start;
328  d.stop = stop;
329  d.stride = stride;
330 
331  d.c_size = (stop - start) / stride + 1;
332 
333  DBG(cerr << "add_constraint: c_size = " << d.c_size << endl);
334 
336 }
337 
341 {
342  return _shape.begin() ;
343 }
344 
348 {
349  return _shape.end() ;
350 }
351 
361 unsigned int
362 Array::dimensions(bool /*constrained*/)
363 {
364  unsigned int dim = 0;
365  for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
366  dim++;
367  }
368 
369  return dim;
370 }
371 
389 int
390 Array::dimension_size(Dim_iter i, bool constrained)
391 {
392  int size = 0;
393 
394  if (!_shape.empty()) {
395  if (constrained)
396  size = (*i).c_size;
397  else
398  size = (*i).size;
399  }
400 
401  return size;
402 }
403 
422 int
423 Array::dimension_start(Dim_iter i, bool /*constrained*/)
424 {
425  return (!_shape.empty()) ? (*i).start : 0;
426 }
427 
446 int
447 Array::dimension_stop(Dim_iter i, bool /*constrained*/)
448 {
449  return (!_shape.empty()) ? (*i).stop : 0;
450 }
451 
471 int
472 Array::dimension_stride(Dim_iter i, bool /*constrained*/)
473 {
474  return (!_shape.empty()) ? (*i).stride : 0;
475 }
476 
487 string
489 {
490  // Jose Garcia
491  // Since this method is public, it is possible for a user
492  // to call it before the Array object has been properly set
493  // this will cause an exception which is the user's fault.
494  // (User in this context is the developer of the surrogate library.)
495  if (_shape.empty())
496  throw InternalErr(__FILE__, __LINE__,
497  "*This* array has no dimensions.");
498  return (*i).name;
499 }
500 
504 unsigned int Array::width(bool constrained)
505 {
506 
507  if (constrained) {
508  // This preserves the original method's semantics when we ask for the
509  // size of the constrained array but no constraint has been applied.
510  // In this case, length will be -1. Wrong, I know...
511  return length() * var()->width(constrained);
512  }
513  else {
514  int length = 1;
515  for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) {
516  length *= dimension_size(i, false);
517  }
518  return length * var()->width(false);
519  }
520 }
521 
522 
523 #if FILE_METHODS
524 
541 void
542 Array::print_decl(FILE *out, string space, bool print_semi,
543  bool constraint_info, bool constrained)
544 {
545  if (constrained && !send_p())
546  return;
547 
548  // print it, but w/o semicolon
549  var()->print_decl(out, space, false, constraint_info, constrained);
550 
551  for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
552  fprintf(out, "[") ;
553  if ((*i).name != "") {
554  fprintf(out, "%s = ", id2www((*i).name).c_str()) ;
555  }
556  if (constrained) {
557  fprintf(out, "%d]", (*i).c_size) ;
558  }
559  else {
560  fprintf(out, "%d]", (*i).size) ;
561  }
562  }
563 
564  if (print_semi) {
565  fprintf(out, ";\n") ;
566  }
567 }
568 #endif
569 
587 void
588 Array::print_decl(ostream &out, string space, bool print_semi,
589  bool constraint_info, bool constrained)
590 {
591  if (constrained && !send_p())
592  return;
593 
594  // print it, but w/o semicolon
595  var()->print_decl(out, space, false, constraint_info, constrained);
596 
597  for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
598  out << "[" ;
599  if ((*i).name != "") {
600  out << id2www((*i).name) << " = " ;
601  }
602  if (constrained) {
603  out << (*i).c_size << "]" ;
604  }
605  else {
606  out << (*i).size << "]" ;
607  }
608  }
609 
610  if (print_semi) {
611  out << ";\n" ;
612  }
613 }
614 #if FILE_METHODS
615 
618 void
619 Array::print_xml(FILE *out, string space, bool constrained)
620 {
621  print_xml_core(out, space, constrained, "Array");
622 }
623 #endif
624 
628 void
629 Array::print_xml(ostream &out, string space, bool constrained)
630 {
631  print_xml_core(out, space, constrained, "Array");
632 }
633 
634 #if FILE_METHODS
635 
638 void
639 Array::print_as_map_xml(FILE *out, string space, bool constrained)
640 {
641  print_xml_core(out, space, constrained, "Map");
642 }
643 #endif
644 
648 void
649 Array::print_as_map_xml(ostream &out, string space, bool constrained)
650 {
651  print_xml_core(out, space, constrained, "Map");
652 }
653 
654 #if FILE_METHODS
655 class PrintArrayDim : public unary_function<Array::dimension&, void>
656 {
657  FILE *d_out;
658  string d_space;
659  bool d_constrained;
660 public:
661  PrintArrayDim(FILE *o, string s, bool c)
662  : d_out(o), d_space(s), d_constrained(c)
663  {}
664 
665  void operator()(Array::dimension &d)
666  {
667  int size = d_constrained ? d.c_size : d.size;
668  if (d.name.empty())
669  fprintf(d_out, "%s<dimension size=\"%d\"/>\n", d_space.c_str(),
670  size);
671  else
672  fprintf(d_out, "%s<dimension name=\"%s\" size=\"%d\"/>\n",
673  d_space.c_str(), id2xml(d.name).c_str(), size);
674  }
675 };
676 
680 void
681 Array::print_xml_core(FILE *out, string space, bool constrained, string tag)
682 {
683  if (constrained && !send_p())
684  return;
685 
686  fprintf(out, "%s<%s", space.c_str(), tag.c_str());
687  if (!name().empty())
688  fprintf(out, " name=\"%s\"", id2xml(name()).c_str());
689  fprintf(out , ">\n");
690 
691  get_attr_table().print_xml(out, space + " ", constrained);
692 
693  BaseType *btp = var();
694  string tmp_name = btp->name();
695  btp->set_name("");
696  btp->print_xml(out, space + " ", constrained);
697  btp->set_name(tmp_name);
698 
699  for_each(dim_begin(), dim_end(),
700  PrintArrayDim(out, space + " ", constrained));
701 
702  fprintf(out, "%s</%s>\n", space.c_str(), tag.c_str());
703 }
704 #endif
705 
706 class PrintArrayDimStrm : public unary_function<Array::dimension&, void>
707 {
708  ostream &d_out;
709  string d_space;
710  bool d_constrained;
711 public:
712  PrintArrayDimStrm(ostream &o, string s, bool c)
713  : d_out(o), d_space(s), d_constrained(c)
714  {}
715 
716  void operator()(Array::dimension &d)
717  {
718  int size = d_constrained ? d.c_size : d.size;
719  if (d.name.empty())
720  d_out << d_space << "<dimension size=\"" << size << "\"/>\n" ;
721  else
722  d_out << d_space << "<dimension name=\"" << id2xml(d.name)
723  << "\" size=\"" << size << "\"/>\n" ;
724  }
725 };
726 
730 void
731 Array::print_xml_core(ostream &out, string space, bool constrained, string tag)
732 {
733  if (constrained && !send_p())
734  return;
735 
736  out << space << "<" << tag;
737  if (!name().empty())
738  out << " name=\"" << id2xml(name()) << "\"";
739  out << ">\n";
740 
741  get_attr_table().print_xml(out, space + " ", constrained);
742 
743  BaseType *btp = var();
744  string tmp_name = btp->name();
745  btp->set_name("");
746  btp->print_xml(out, space + " ", constrained);
747  btp->set_name(tmp_name);
748 
749  for_each(dim_begin(), dim_end(), PrintArrayDimStrm(out, space + " ", constrained));
750 
751  out << space << "</" << tag << ">\n";
752 }
753 
754 void
755 Array::print_xml_writer(XMLWriter &xml, bool constrained)
756 {
757  print_xml_writer_core(xml, constrained, "Array");
758 }
759 
760 void
762 {
763  print_xml_writer_core(xml, constrained, "Map");
764 }
765 
766 class PrintArrayDimXMLWriter : public unary_function<Array::dimension&, void>
767 {
768  XMLWriter &xml;
769  bool d_constrained;
770 public:
771  PrintArrayDimXMLWriter(XMLWriter &xml, bool c) : xml(xml), d_constrained(c) {}
772 
773  void operator()(Array::dimension &d)
774  {
775  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)"dimension") < 0)
776  throw InternalErr(__FILE__, __LINE__, "Could not write dimension element");
777 
778  if (!d.name.empty())
779  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)d.name.c_str()) < 0)
780  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
781 
782  ostringstream size;
783  size << (d_constrained ? d.c_size : d.size);
784  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "size", (const xmlChar*)size.str().c_str()) < 0)
785  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
786 
787  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
788  throw InternalErr(__FILE__, __LINE__, "Could not end dimension element");
789  }
790 };
791 
792 void
793 Array::print_xml_writer_core(XMLWriter &xml, bool constrained, string tag)
794 {
795  if (constrained && !send_p())
796  return;
797 
798  if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)tag.c_str()) < 0)
799  throw InternalErr(__FILE__, __LINE__, "Could not write " + tag + " element");
800 
801  if (!name().empty())
802  if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0)
803  throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
804 
806 
807  BaseType *btp = var();
808  string tmp_name = btp->name();
809  btp->set_name("");
810  btp->print_xml_writer(xml, constrained);
811  btp->set_name(tmp_name);
812 
813  for_each(dim_begin(), dim_end(), PrintArrayDimXMLWriter(xml, constrained));
814 
815  if (xmlTextWriterEndElement(xml.get_writer()) < 0)
816  throw InternalErr(__FILE__, __LINE__, "Could not end " + tag + " element");
817 }
818 
819 #if FILE_METHODS
820 
831 unsigned int
832 Array::print_array(FILE *out, unsigned int index, unsigned int dims,
833  unsigned int shape[])
834 {
835  if (dims == 1) {
836  fprintf(out, "{") ;
837  for (unsigned i = 0; i < shape[0] - 1; ++i) {
838  var(index++)->print_val(out, "", false);
839  fprintf(out, ", ") ;
840  }
841  var(index++)->print_val(out, "", false);
842  fprintf(out, "}") ;
843 
844  return index;
845  }
846  else {
847  fprintf(out, "{") ;
848  // Fixed an off-by-one error in the following loop. Since the array
849  // length is shape[dims-1]-1 *and* since we want one less dimension
850  // than that, the correct limit on this loop is shape[dims-2]-1. From
851  // Todd Karakasian.
852  // The saga continues; the loop test should be `i < shape[0]-1'. jhrg
853  // 9/12/96.
854  for (unsigned i = 0; i < shape[0] - 1; ++i) {
855  index = print_array(out, index, dims - 1, shape + 1);
856  fprintf(out, ",") ; // Removed the extra `}'. Also from Todd
857  }
858  index = print_array(out, index, dims - 1, shape + 1);
859  fprintf(out, "}") ;
860 
861  return index;
862  }
863 }
864 #endif
865 
877 unsigned int
878 Array::print_array(ostream &out, unsigned int index, unsigned int dims,
879  unsigned int shape[])
880 {
881  if (dims == 1) {
882  out << "{" ;
883  for (unsigned i = 0; i < shape[0] - 1; ++i) {
884  var(index++)->print_val(out, "", false);
885  out << ", " ;
886  }
887  var(index++)->print_val(out, "", false);
888  out << "}" ;
889 
890  return index;
891  }
892  else {
893  out << "{" ;
894  // Fixed an off-by-one error in the following loop. Since the array
895  // length is shape[dims-1]-1 *and* since we want one less dimension
896  // than that, the correct limit on this loop is shape[dims-2]-1. From
897  // Todd Karakasian.
898  // The saga continues; the loop test should be `i < shape[0]-1'. jhrg
899  // 9/12/96.
900  for (unsigned i = 0; i < shape[0] - 1; ++i) {
901  index = print_array(out, index, dims - 1, shape + 1);
902  out << "," ;
903  }
904  index = print_array(out, index, dims - 1, shape + 1);
905  out << "}" ;
906 
907  return index;
908  }
909 }
910 
911 #if FILE_METHODS
912 void
913 Array::print_val(FILE *out, string space, bool print_decl_p)
914 {
915  // print the declaration if print decl is true.
916  // for each dimension,
917  // for each element,
918  // print the array given its shape, number of dimensions.
919  // Add the `;'
920 
921  if (print_decl_p) {
922  print_decl(out, space, false, false, false);
923  fprintf(out, " = ") ;
924  }
925 
926  unsigned int *shape = new unsigned int[_shape.size()];
927  unsigned int index = 0;
928  for (Dim_iter i = _shape.begin(); i != _shape.end() && index < _shape.size(); i++)
929  shape[index++] = dimension_size(i, true);
930 
931  print_array(out, 0, _shape.size(), shape);
932 
933  delete [] shape; shape = 0;
934 
935  if (print_decl_p) {
936  fprintf(out, ";\n") ;
937  }
938 }
939 #endif
940 
941 void
942 Array::print_val(ostream &out, string space, bool print_decl_p)
943 {
944  // print the declaration if print decl is true.
945  // for each dimension,
946  // for each element,
947  // print the array given its shape, number of dimensions.
948  // Add the `;'
949 
950  if (print_decl_p) {
951  print_decl(out, space, false, false, false);
952  out << " = " ;
953  }
954 
955  unsigned int *shape = new unsigned int[dimensions(true)];
956  unsigned int index = 0;
957  for (Dim_iter i = _shape.begin(); i != _shape.end() && index < dimensions(true); ++i)
958  shape[index++] = dimension_size(i, true);
959 
960  print_array(out, 0, dimensions(true), shape);
961 
962  delete [] shape; shape = 0;
963 
964  if (print_decl_p) {
965  out << ";\n" ;
966  }
967 }
968 
978 bool
979 Array::check_semantics(string &msg, bool)
980 {
981  bool sem = BaseType::check_semantics(msg) && !_shape.empty();
982 
983  if (!sem)
984  msg = "An array variable must have dimensions";
985 
986  return sem;
987 }
988 
997 void
998 Array::dump(ostream &strm) const
999 {
1000  strm << DapIndent::LMarg << "Array::dump - ("
1001  << (void *)this << ")" << endl ;
1002  DapIndent::Indent() ;
1003  Vector::dump(strm) ;
1004  strm << DapIndent::LMarg << "shape:" << endl ;
1005  DapIndent::Indent() ;
1006  Dim_citer i = _shape.begin() ;
1007  Dim_citer ie = _shape.end() ;
1008  unsigned int dim_num = 0 ;
1009  for (; i != ie; i++) {
1010  strm << DapIndent::LMarg << "dimension " << dim_num++ << ":"
1011  << endl ;
1012  DapIndent::Indent() ;
1013  strm << DapIndent::LMarg << "name: " << (*i).name << endl ;
1014  strm << DapIndent::LMarg << "size: " << (*i).size << endl ;
1015  strm << DapIndent::LMarg << "start: " << (*i).start << endl ;
1016  strm << DapIndent::LMarg << "stop: " << (*i).stop << endl ;
1017  strm << DapIndent::LMarg << "stride: " << (*i).stride << endl ;
1018  strm << DapIndent::LMarg << "constrained size: " << (*i).c_size
1019  << endl ;
1021  }
1024 }
1025 
1026 } // namespace libdap
1027