libdap  Updated for version 3.17.0
Array.cc
00001 
00002 // -*- mode: c++; c-basic-offset:4 -*-
00003 
00004 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
00005 // Access Protocol.
00006 
00007 // Copyright (c) 2002,2003 OPeNDAP, Inc.
00008 // Author: James Gallagher <jgallagher@opendap.org>
00009 //
00010 // This library is free software; you can redistribute it and/or
00011 // modify it under the terms of the GNU Lesser General Public
00012 // License as published by the Free Software Foundation; either
00013 // version 2.1 of the License, or (at your option) any later version.
00014 //
00015 // This library is distributed in the hope that it will be useful,
00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018 // Lesser General Public License for more details.
00019 //
00020 // You should have received a copy of the GNU Lesser General Public
00021 // License along with this library; if not, write to the Free Software
00022 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00023 //
00024 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
00025 
00026 // (c) COPYRIGHT URI/MIT 1994-1999
00027 // Please read the full copyright statement in the file COPYRIGHT_URI.
00028 //
00029 // Authors:
00030 //      jhrg,jimg       James Gallagher <jgallagher@gso.uri.edu>
00031 
00032 // Implementation for Array.
00033 //
00034 // jhrg 9/13/94
00035 
00036 #include "config.h"
00037 
00038 // #define DODS_DEBUG
00039 
00040 #include <algorithm>
00041 #include <functional>
00042 #include <sstream>
00043 
00044 #include "Array.h"
00045 
00046 #include "D4Attributes.h"
00047 #include "DMR.h"
00048 #include "D4Dimensions.h"
00049 #include "D4Maps.h"
00050 #include "D4Group.h"
00051 #include "D4EnumDefs.h"
00052 #include "D4Enum.h"
00053 #include "XMLWriter.h"
00054 
00055 #include "util.h"
00056 #include "debug.h"
00057 #include "InternalErr.h"
00058 #include "escaping.h"
00059 
00060 using namespace std;
00061 
00062 namespace libdap {
00063 
00064 Array::dimension::dimension(D4Dimension *d) : dim(d), use_sdim_for_slice(true)
00065 {
00066     size = d->size();
00067     name = d->name();
00068 
00069     start = 0;
00070     stop = size - 1;
00071     stride = 1;
00072     c_size = size;
00073 }
00074 
00075 void
00076 Array::_duplicate(const Array &a)
00077 {
00078     _shape = a._shape;
00079 
00080     // Deep copy the Maps if they are being used.
00081     if (a.d_maps) {
00082         d_maps = new D4Maps(*(a.d_maps));
00083     }
00084     else {
00085         d_maps = 0;
00086     }
00087     // d_maps = a.d_maps ? new D4Maps(*(a.d_maps)) : 0;
00088 }
00089 
00090 // The first method of calculating length works when only one dimension is
00091 // constrained and you want the others to appear in total. This is important
00092 // when selecting from grids since users may not select from all dimensions
00093 // in which case that means they want the whole thing. Array projection
00094 // should probably work this way too, but it doesn't. 9/21/2001 jhrg
00095 
00102 void
00103 Array::update_length(int)
00104 {
00105     int length = 1;
00106     for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
00107 #if 0
00108         // If the size of any dimension is zero, then the array is not
00109         // capable of storing any values. jhrg 1/28/16
00110         length *= (*i).c_size > 0 ? (*i).c_size : 1;
00111 #endif
00112         length *= (*i).c_size;
00113     }
00114 
00115     set_length(length);
00116 }
00117 
00118 // Construct an instance of Array. The (BaseType *) is assumed to be
00119 // allocated using new - The dtor for Vector will delete this object.
00120 
00136 Array::Array(const string &n, BaseType *v, bool is_dap4 /* default:false */)
00137         : Vector(n, 0, dods_array_c, is_dap4), d_maps(0)
00138 {
00139     add_var(v); // Vector::add_var() stores null if v is null
00140 }
00141 
00155 Array::Array(const string &n, const string &d, BaseType *v, bool is_dap4 /* default:false */)
00156     : Vector(n, d, 0, dods_array_c, is_dap4), d_maps(0)
00157 {
00158     add_var(v); // Vector::add_var() stores null if v is null
00159 }
00160 
00162 Array::Array(const Array &rhs) : Vector(rhs)
00163 {
00164     _duplicate(rhs);
00165 }
00166 
00168 Array::~Array()
00169 {
00170         delete d_maps;
00171 }
00172 
00173 BaseType *
00174 Array::ptr_duplicate()
00175 {
00176     return new Array(*this);
00177 }
00178 
00179 Array &
00180 Array::operator=(const Array &rhs)
00181 {
00182     if (this == &rhs)
00183         return *this;
00184 
00185     dynamic_cast<Vector &>(*this) = rhs;
00186 
00187     _duplicate(rhs);
00188 
00189     return *this;
00190 }
00191 
00192 BaseType *
00193 Array::transform_to_dap4(D4Group *root, Constructor */*container*/)
00194 {
00195         Array *dest = static_cast<Array*>(ptr_duplicate());
00196 
00197         // Process the Array's dimensions, making D4 shared dimensions for
00198         // D2 dimensions that are named. If there is just a size, don't make
00199         // a D4Dimension (In DAP4 you cannot share a dimension unless it has
00200         // a name). jhrg 3/18/14
00201 
00202         D4Dimensions *dims = root->dims();
00203         for (Array::Dim_iter d = dest->dim_begin(), e = dest->dim_end(); d != e; ++d) {
00204                 if (!(*d).name.empty()) {
00205                         // If a D4Dimension with the name already exists, use it.
00206                         D4Dimension *d4_dim = dims->find_dim((*d).name);
00207                         if (!d4_dim) {
00208                                 d4_dim = new D4Dimension((*d).name, (*d).size);
00209                                 dims->add_dim_nocopy(d4_dim);
00210                         }
00211                         // TODO Revisit this decision. jhrg 3/18/14
00212                         // ...in case the name/size are different, make a unique D4Dimension
00213                         // but don't fiddle with the name. Not sure I like this idea, so I'm
00214                         // making the case explicit (could be rolled in to the block above).
00215                         // jhrg 3/18/14
00216                         //
00217                         // This is causing problems in the FITS handler because there are cases
00218                         // where two arrays have dimensions with the same name but different
00219                         // sizes. The deserializing code is using the first size listed, which is
00220                         // wrong in some cases. I'm going to try making this new D4Dimension using
00221                         // the dim name along with the variable name. jhrg 8/15/14
00222                         else if (d4_dim->size() != (unsigned long) (*d).size) {
00223                                 d4_dim = new D4Dimension((*d).name + "_" + name(), (*d).size);
00224                                 dims->add_dim_nocopy(d4_dim);
00225                         }
00226                         // At this point d4_dim's name and size == those of (*d) so just set
00227                         // the D4Dimension pointer so it matches the one in the D4Group.
00228                         (*d).dim = d4_dim;
00229                 }
00230         }
00231 
00232         // Copy the D2 attributes to D4 Attributes
00233         dest->attributes()->transform_to_dap4(get_attr_table());
00234 
00235         dest->set_is_dap4(true);
00236 
00237         return dest;
00238 }
00239 
00251 void
00252 Array::update_dimension_pointers(D4Dimensions *old_dims, D4Dimensions *new_dims)
00253 {
00254         std::vector<dimension>::iterator i = _shape.begin(), e = _shape.end();
00255         while (i != e) {
00256                 D4Dimensions::D4DimensionsIter old_i = old_dims->dim_begin(), old_e = old_dims->dim_end();
00257                 while (old_i != old_e) {
00258                         if ((*i).dim == *old_i) {
00259                                 (*i).dim =  new_dims->find_dim((*old_i)->name());
00260                         }
00261                         ++old_i;
00262                 }
00263 
00264                 ++i;
00265         }
00266 }
00267 
00292 void
00293 Array::add_var(BaseType *v, Part)
00294 {
00295     // If 'v' is an Array, add the template instance to this object and
00296     // then copy the dimension information. Odd semantics; I wonder if this
00297     //is ever used. jhrg 6/13/12
00298     if (v && v->type() == dods_array_c) {
00299         Array *a = static_cast<Array*>(v);
00300         Vector::add_var(a->var());
00301 
00302         Dim_iter i = a->dim_begin();
00303         Dim_iter i_end = a->dim_end();
00304         while (i != i_end) {
00305             append_dim(a->dimension_size(i), a->dimension_name(i));
00306             ++i;
00307         }
00308     }
00309     else {
00310         Vector::add_var(v);
00311     }
00312 }
00313 
00314 void
00315 Array::add_var_nocopy(BaseType *v, Part)
00316 {
00317     // If 'v' is an Array, add the template instance to this object and
00318     // then copy the dimension information. Odd semantics; I wonder if this
00319     //is ever used. jhrg 6/13/12
00320     if (v && v->type() == dods_array_c) {
00321         Array &a = dynamic_cast<Array&>(*v);
00322         Vector::add_var_nocopy(a.var());
00323         Dim_iter i = a.dim_begin();
00324         Dim_iter i_end = a.dim_end();
00325         while (i != i_end) {
00326             append_dim(a.dimension_size(i), a.dimension_name(i));
00327             ++i;
00328         }
00329     }
00330     else {
00331         Vector::add_var_nocopy(v);
00332     }
00333 }
00334 
00346 void
00347 Array::append_dim(int size, const string &name)
00348 {
00349     dimension d(size, www2id(name));
00350     _shape.push_back(d);
00351 
00352     update_length();
00353 }
00354 
00355 void
00356 Array::append_dim(D4Dimension *dim)
00357 {
00358         dimension d(/*dim->size(), www2id(dim->name()),*/ dim);
00359     _shape.push_back(d);
00360 
00361     update_length();
00362 }
00363 
00369 void
00370 Array::prepend_dim(int size, const string& name/* = "" */)
00371 {
00372   dimension d(size, www2id(name));
00373   // Shifts the whole array, but it's tiny in general
00374   _shape.insert(_shape.begin(), d);
00375 
00376   update_length(); // the number is ignored...
00377 }
00378 
00379 void
00380 Array::prepend_dim(D4Dimension *dim)
00381 {
00382   dimension d(/*dim->size(), www2id(dim->name()),*/ dim);
00383   // Shifts the whole array, but it's tiny in general
00384   _shape.insert(_shape.begin(), d);
00385 
00386   update_length(); // the number is ignored...
00387 }
00388 
00392 void
00393 Array::clear_all_dims()
00394 {
00395         _shape.clear();
00396 }
00403 void
00404 Array::reset_constraint()
00405 {
00406     set_length(-1);
00407 
00408     for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) {
00409         (*i).start = 0;
00410         (*i).stop = (*i).size - 1;
00411         (*i).stride = 1;
00412         (*i).c_size = (*i).size;
00413 
00414         update_length();
00415     }
00416 }
00417 
00418 
00428 void
00429 Array::clear_constraint()
00430 {
00431     reset_constraint();
00432 }
00433 
00434 // Note: MS VC++ won't tolerate embedded newlines in strings, hence the \n
00435 // is explicit.
00436 static const char *array_sss = \
00437 "Invalid constraint parameters: At least one of the start, stride or stop \n\
00438 specified do not match the array variable.";
00439 
00460 void
00461 Array::add_constraint(Dim_iter i, int start, int stride, int stop)
00462 {
00463     dimension &d = *i ;
00464 
00465     // if stop is -1, set it to the array's max element index
00466     // jhrg 12/20/12
00467     if (stop == -1)
00468         stop = d.size - 1;
00469 
00470     // Check for bad constraints.
00471     // Jose Garcia
00472     // Usually invalid data for a constraint is the user's mistake
00473     // because they build a wrong URL in the client side.
00474     if (start >= d.size || stop >= d.size || stride > d.size || stride <= 0)
00475         throw Error(malformed_expr, array_sss);
00476 
00477     if (((stop - start) / stride + 1) > d.size)
00478         throw Error(malformed_expr, array_sss);
00479 
00480     d.start = start;
00481     d.stop = stop;
00482     d.stride = stride;
00483 
00484     d.c_size = (stop - start) / stride + 1;
00485 
00486     DBG(cerr << "add_constraint: c_size = " << d.c_size << endl);
00487 
00488     update_length();
00489 
00490     d.use_sdim_for_slice = false;
00491 }
00492 
00493 void
00494 Array::add_constraint(Dim_iter i, D4Dimension *dim)
00495 {
00496     dimension &d = *i ;
00497 
00498     if (dim->constrained())
00499         add_constraint(i, dim->c_start(), dim->c_stride(), dim->c_stop());
00500 
00501     dim->set_used_by_projected_var(true);
00502 
00503         // In this case the value below overrides the value for use_sdim_for_slice
00504         // set in the above call. jhrg 12/20/13
00505         d.use_sdim_for_slice = true;
00506 }
00507 
00509 Array::Dim_iter
00510 Array::dim_begin()
00511 {
00512     return _shape.begin() ;
00513 }
00514 
00516 Array::Dim_iter
00517 Array::dim_end()
00518 {
00519     return _shape.end() ;
00520 }
00521 
00522 //TODO Many of these methods take a bool parameter that serves no use; remove.
00523 
00532 unsigned int
00533 Array::dimensions(bool /*constrained*/)
00534 {
00535     return _shape.size();
00536 }
00537 
00555 int
00556 Array::dimension_size(Dim_iter i, bool constrained)
00557 {
00558     int size = 0;
00559 
00560     if (!_shape.empty()) {
00561         if (constrained)
00562             size = (*i).c_size;
00563         else
00564             size = (*i).size;
00565     }
00566 
00567     return size;
00568 }
00569 
00588 int
00589 Array::dimension_start(Dim_iter i, bool /*constrained*/)
00590 {
00591     return (!_shape.empty()) ? (*i).start : 0;
00592 }
00593 
00612 int
00613 Array::dimension_stop(Dim_iter i, bool /*constrained*/)
00614 {
00615     return (!_shape.empty()) ? (*i).stop : 0;
00616 }
00617 
00637 int
00638 Array::dimension_stride(Dim_iter i, bool /*constrained*/)
00639 {
00640     return (!_shape.empty()) ? (*i).stride : 0;
00641 }
00642 
00653 string
00654 Array::dimension_name(Dim_iter i)
00655 {
00656     // Jose Garcia
00657     // Since this method is public, it is possible for a user
00658     // to call it before the Array object has been properly set
00659     // this will cause an exception which is the user's fault.
00660     // (User in this context is the developer of the surrogate library.)
00661     if (_shape.empty())
00662         throw  InternalErr(__FILE__, __LINE__,
00663                            "*This* array has no dimensions.");
00664     return (*i).name;
00665 }
00666 
00667 D4Dimension *
00668 Array::dimension_D4dim(Dim_iter i)
00669 {
00670         return (!_shape.empty()) ? (*i).dim : 0;
00671 }
00672 
00673 D4Maps *
00674 Array::maps()
00675 {
00676         if (!d_maps) d_maps = new D4Maps(this);         // init with this as parent
00677         return d_maps;
00678 }
00679 
00680 #if 0
00681 
00687 unsigned int Array::width(bool constrained) const
00688 {
00689 
00690         if (constrained) {
00691                 // This preserves the original method's semantics when we ask for the
00692                 // size of the constrained array but no constraint has been applied.
00693                 // In this case, length will be -1. Wrong, I know...
00694                 return length() * var()->width(constrained);
00695         }
00696         else {
00697                 int length = 1;
00698                 for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) {
00699                         length *= dimension_size(i, false);
00700                 }
00701                 return length * var()->width(false);
00702         }
00703 }
00704 #endif
00705 
00706 class PrintD4ArrayDimXMLWriter: public unary_function<Array::dimension&, void> {
00707         XMLWriter &xml;
00708         // Was this variable constrained using local/direct slicing? i.e., is d_local_constraint set?
00709         // If so, don't use shared dimensions; instead emit Dim elements that are anonymous.
00710         bool d_constrained;
00711 public:
00712 
00713         PrintD4ArrayDimXMLWriter(XMLWriter &xml, bool c) : xml(xml), d_constrained(c) { }
00714 
00715         void operator()(Array::dimension &d)
00716         {
00717                 // This duplicates code in D4Dimensions (where D4Dimension::print_dap4() is defined
00718                 // because of the need to print the constrained size of a dimension. I think that
00719                 // the constraint information has to be kept here and not in the dimension (since they
00720                 // are shared dims). Could hack print_dap4() to take the constrained size, however.
00721                 if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Dim") < 0)
00722                         throw InternalErr(__FILE__, __LINE__, "Could not write Dim element");
00723 
00724                 string name = (d.dim) ? d.dim->fully_qualified_name() : d.name;
00725                 // If there is a name, there must be a Dimension (named dimension) in scope
00726                 // so write its name but not its size.
00727                 if (!d_constrained && !name.empty()) {
00728                         if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name.c_str())
00729                                         < 0) throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
00730                 }
00731                 else if (d.use_sdim_for_slice) {
00732                         assert(!name.empty());
00733                         if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name.c_str())
00734                                         < 0) throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
00735                 }
00736                 else {
00737                         ostringstream size;
00738                         size << (d_constrained ? d.c_size : d.size);
00739                         if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "size",
00740                                         (const xmlChar*) size.str().c_str()) < 0)
00741                                 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
00742                 }
00743 
00744                 if (xmlTextWriterEndElement(xml.get_writer()) < 0)
00745                         throw InternalErr(__FILE__, __LINE__, "Could not end Dim element");
00746         }
00747 };
00748 
00749 class PrintD4ConstructorVarXMLWriter: public unary_function<BaseType*, void> {
00750         XMLWriter &xml;
00751         bool d_constrained;
00752 public:
00753         PrintD4ConstructorVarXMLWriter(XMLWriter &xml, bool c) : xml(xml), d_constrained(c) { }
00754 
00755         void operator()(BaseType *btp)
00756         {
00757                 btp->print_dap4(xml, d_constrained);
00758         }
00759 };
00760 
00761 class PrintD4MapXMLWriter: public unary_function<D4Map*, void> {
00762         XMLWriter &xml;
00763 
00764 public:
00765         PrintD4MapXMLWriter(XMLWriter &xml) : xml(xml) { }
00766 
00767         void operator()(D4Map *m)
00768         {
00769                 m->print_dap4(xml);
00770         }
00771 };
00772 
00778 void
00779 Array::print_dap4(XMLWriter &xml, bool constrained /* default: false*/)
00780 {
00781         if (constrained && !send_p()) return;
00782 
00783         if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) var()->type_name().c_str()) < 0)
00784                 throw InternalErr(__FILE__, __LINE__, "Could not write " + type_name() + " element");
00785 
00786         if (!name().empty())
00787                 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0)
00788                         throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
00789 
00790         // Hack job... Copied from D4Enum::print_xml_writer. jhrg 11/12/13
00791         if (var()->type() == dods_enum_c) {
00792                 D4Enum *e = static_cast<D4Enum*>(var());
00793                 string path = e->enumeration()->name();
00794                 if (e->enumeration()->parent()) {
00795                         // print the FQN for the enum def; D4Group::FQN() includes the trailing '/'
00796                         path = static_cast<D4Group*>(e->enumeration()->parent()->parent())->FQN() + path;
00797                 }
00798                 if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "enum", (const xmlChar*)path.c_str()) < 0)
00799                         throw InternalErr(__FILE__, __LINE__, "Could not write attribute for enum");
00800         }
00801 
00802         if (prototype()->is_constructor_type()) {
00803                 Constructor &c = static_cast<Constructor&>(*prototype());
00804                 for_each(c.var_begin(), c.var_end(), PrintD4ConstructorVarXMLWriter(xml, constrained));
00805                 // bind2nd(mem_fun_ref(&BaseType::print_dap4), xml));
00806         }
00807 
00808         // Drop the local_constraint which is per-array and use a per-dimension on instead
00809     for_each(dim_begin(), dim_end(), PrintD4ArrayDimXMLWriter(xml, constrained));
00810 
00811         attributes()->print_dap4(xml);
00812 
00813         for_each(maps()->map_begin(), maps()->map_end(), PrintD4MapXMLWriter(xml));
00814 
00815         if (xmlTextWriterEndElement(xml.get_writer()) < 0)
00816                 throw InternalErr(__FILE__, __LINE__, "Could not end " + type_name() + " element");
00817 }
00818 
00836 void
00837 Array::print_decl(FILE *out, string space, bool print_semi,
00838                   bool constraint_info, bool constrained)
00839 {
00840     ostringstream oss;
00841     print_decl(oss, space, print_semi, constraint_info, constrained);
00842     fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
00843 }
00844 
00862 void
00863 Array::print_decl(ostream &out, string space, bool print_semi,
00864                   bool constraint_info, bool constrained)
00865 {
00866     if (constrained && !send_p())
00867         return;
00868 
00869     // print it, but w/o semicolon
00870     var()->print_decl(out, space, false, constraint_info, constrained);
00871 
00872     for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
00873         out << "[" ;
00874         if ((*i).name != "") {
00875             out << id2www((*i).name) << " = " ;
00876         }
00877         if (constrained) {
00878             out << (*i).c_size << "]" ;
00879         }
00880         else {
00881             out << (*i).size << "]" ;
00882         }
00883     }
00884 
00885     if (print_semi) {
00886         out << ";\n" ;
00887     }
00888 }
00889 
00893 void
00894 Array::print_xml(FILE *out, string space, bool constrained)
00895 {
00896     XMLWriter xml(space);
00897     print_xml_writer_core(xml, constrained, "Array");
00898     fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
00899 }
00900 
00904 void
00905 Array::print_xml(ostream &out, string space, bool constrained)
00906 {
00907     XMLWriter xml(space);
00908     print_xml_writer_core(xml, constrained, "Array");
00909     out << xml.get_doc();
00910 }
00911 
00915 void
00916 Array::print_as_map_xml(FILE *out, string space, bool constrained)
00917 {
00918     XMLWriter xml(space);
00919     print_xml_writer_core(xml, constrained, "Map");
00920     fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
00921 }
00922 
00926 void
00927 Array::print_as_map_xml(ostream &out, string space, bool constrained)
00928 {
00929     XMLWriter xml(space);
00930     print_xml_writer_core(xml, constrained, "Map");
00931     out << xml.get_doc();
00932 }
00933 
00937 void
00938 Array::print_xml_core(FILE *out, string space, bool constrained, string tag)
00939 {
00940     XMLWriter xml(space);
00941     print_xml_writer_core(xml, constrained, tag);
00942     fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
00943 }
00944 
00948 void
00949 Array::print_xml_core(ostream &out, string space, bool constrained, string tag)
00950 {
00951     XMLWriter xml(space);
00952     print_xml_writer_core(xml, constrained, tag);
00953     out << xml.get_doc();
00954 }
00955 
00956 void
00957 Array::print_xml_writer(XMLWriter &xml, bool constrained)
00958 {
00959     print_xml_writer_core(xml, constrained, "Array");
00960 }
00961 
00962 void
00963 Array::print_as_map_xml_writer(XMLWriter &xml, bool constrained)
00964 {
00965     print_xml_writer_core(xml, constrained, "Map");
00966 }
00967 
00968 class PrintArrayDimXMLWriter : public unary_function<Array::dimension&, void>
00969 {
00970     XMLWriter &xml;
00971     bool d_constrained;
00972 public:
00973     PrintArrayDimXMLWriter(XMLWriter &xml, bool c) : xml(xml), d_constrained(c) {}
00974 
00975     void operator()(Array::dimension &d)
00976     {
00977         if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)"dimension") < 0)
00978             throw InternalErr(__FILE__, __LINE__, "Could not write dimension element");
00979 
00980         if (!d.name.empty())
00981             if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)d.name.c_str()) < 0)
00982                 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
00983 
00984         ostringstream size;
00985         size << (d_constrained ? d.c_size : d.size);
00986         if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "size", (const xmlChar*)size.str().c_str()) < 0)
00987             throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
00988 
00989         if (xmlTextWriterEndElement(xml.get_writer()) < 0)
00990             throw InternalErr(__FILE__, __LINE__, "Could not end dimension element");
00991     }
00992 };
00993 
00994 void
00995 Array::print_xml_writer_core(XMLWriter &xml, bool constrained, string tag)
00996 {
00997     if (constrained && !send_p())
00998         return;
00999 
01000     if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)tag.c_str()) < 0)
01001         throw InternalErr(__FILE__, __LINE__, "Could not write " + tag + " element");
01002 
01003     if (!name().empty())
01004         if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0)
01005             throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
01006 
01007     get_attr_table().print_xml_writer(xml);
01008 
01009     BaseType *btp = var();
01010     string tmp_name = btp->name();
01011     btp->set_name("");
01012     btp->print_xml_writer(xml, constrained);
01013     btp->set_name(tmp_name);
01014 
01015     for_each(dim_begin(), dim_end(), PrintArrayDimXMLWriter(xml, constrained));
01016 
01017     if (xmlTextWriterEndElement(xml.get_writer()) < 0)
01018         throw InternalErr(__FILE__, __LINE__, "Could not end " + tag + " element");
01019 }
01020 
01032 unsigned int
01033 Array::print_array(FILE *out, unsigned int index, unsigned int dims,
01034                    unsigned int shape[])
01035 {
01036     ostringstream oss;
01037     unsigned int i = print_array(oss, index, dims, shape);
01038     fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
01039 
01040     return i;
01041 }
01042 
01054 unsigned int Array::print_array(ostream &out, unsigned int index, unsigned int dims, unsigned int shape[])
01055 {
01056         if (dims == 1) {
01057                 out << "{";
01058 
01059                 // Added test in case this method is passed an array with no elements. jhrg 1/27/16
01060                 if (shape[0] >= 1) {
01061             for (unsigned i = 0; i < shape[0] - 1; ++i) {
01062                 var(index++)->print_val(out, "", false);
01063                 out << ", ";
01064             }
01065             var(index++)->print_val(out, "", false);
01066         }
01067 
01068                 out << "}";
01069 
01070                 return index;
01071         }
01072         else {
01073                 out << "{";
01074                 // Fixed an off-by-one error in the following loop. Since the array
01075                 // length is shape[dims-1]-1 *and* since we want one less dimension
01076                 // than that, the correct limit on this loop is shape[dims-2]-1. From
01077                 // Todd Karakasian.
01078                 //
01079                 // The saga continues; the loop test should be `i < shape[0]-1'. jhrg
01080                 // 9/12/96.
01081                 //
01082                 // For arrays that hold zero values but have rank > 1, the print out
01083                 // may look a little odd (e.g., x[4][0] will print as { {}, {}, {}, {} })
01084                 // but it's not wrong and this is really for debugging mostly. jhrg 1/28/16
01085         if (shape[0] > 0) {
01086             for (unsigned i = 0; i < shape[0] - 1; ++i) {
01087                 index = print_array(out, index, dims - 1, shape + 1);
01088                 out << ",";
01089             }
01090 
01091             index = print_array(out, index, dims - 1, shape + 1);
01092         }
01093 
01094                 out << "}";
01095 
01096                 return index;
01097         }
01098 }
01099 
01100 void
01101 Array::print_val(FILE *out, string space, bool print_decl_p)
01102 {
01103     ostringstream oss;
01104     print_val(oss, space, print_decl_p);
01105     fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
01106 }
01107 
01108 void
01109 Array::print_val(ostream &out, string space, bool print_decl_p)
01110 {
01111     // print the declaration if print decl is true.
01112     // for each dimension,
01113     //   for each element,
01114     //     print the array given its shape, number of dimensions.
01115     // Add the `;'
01116 
01117     if (print_decl_p) {
01118         print_decl(out, space, false, false, false);
01119             out << " = " ;
01120     }
01121 
01122     unsigned int *shape = new unsigned int[dimensions(true)];
01123     unsigned int index = 0;
01124     for (Dim_iter i = _shape.begin(); i != _shape.end() && index < dimensions(true); ++i)
01125         shape[index++] = dimension_size(i, true);
01126 
01127     print_array(out, 0, dimensions(true), shape);
01128 
01129     delete [] shape; shape = 0;
01130 
01131     if (print_decl_p) {
01132             out << ";\n" ;
01133     }
01134 }
01135 
01145 bool
01146 Array::check_semantics(string &msg, bool)
01147 {
01148     bool sem = BaseType::check_semantics(msg) && !_shape.empty();
01149 
01150     if (!sem)
01151         msg = "An array variable must have dimensions";
01152 
01153     return sem;
01154 }
01155 
01164 void
01165 Array::dump(ostream &strm) const
01166 {
01167     strm << DapIndent::LMarg << "Array::dump - ("
01168     << (void *)this << ")" << endl ;
01169     DapIndent::Indent() ;
01170     Vector::dump(strm) ;
01171     strm << DapIndent::LMarg << "shape:" << endl ;
01172     DapIndent::Indent() ;
01173     Dim_citer i = _shape.begin() ;
01174     Dim_citer ie = _shape.end() ;
01175     unsigned int dim_num = 0 ;
01176     for (; i != ie; i++) {
01177         strm << DapIndent::LMarg << "dimension " << dim_num++ << ":"
01178              << endl ;
01179         DapIndent::Indent() ;
01180         strm << DapIndent::LMarg << "name: " << (*i).name << endl ;
01181         strm << DapIndent::LMarg << "size: " << (*i).size << endl ;
01182         strm << DapIndent::LMarg << "start: " << (*i).start << endl ;
01183         strm << DapIndent::LMarg << "stop: " << (*i).stop << endl ;
01184         strm << DapIndent::LMarg << "stride: " << (*i).stride << endl ;
01185         strm << DapIndent::LMarg << "constrained size: " << (*i).c_size
01186              << endl ;
01187         DapIndent::UnIndent() ;
01188     }
01189     DapIndent::UnIndent() ;
01190     DapIndent::UnIndent() ;
01191 }
01192 
01193 } // namespace libdap
01194