libdap++  Updated for version 3.8.2
Array.cc
Go to the documentation of this file.
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 
00037 #include "config.h"
00038 
00039 #include <algorithm>
00040 #include <functional>
00041 #include <sstream>
00042 
00043 #include "Array.h"
00044 #include "util.h"
00045 #include "debug.h"
00046 #include "InternalErr.h"
00047 #include "escaping.h"
00048 
00049 using namespace std;
00050 
00051 namespace libdap {
00052 
00053 void
00054 Array::_duplicate(const Array &a)
00055 {
00056     _shape = a._shape;
00057 }
00058 
00059 // The first method of calculating length works when only one dimension is
00060 // constrained and you want the others to appear in total. This is important
00061 // when selecting from grids since users may not select from all dimensions
00062 // in which case that means they want the whole thing. Array projection
00063 // should probably work this way too, but it doesn't. 9/21/2001 jhrg
00064 
00071 void
00072 Array::update_length(int)
00073 {
00074     int length = 1;
00075     for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
00076         length *= (*i).c_size > 0 ? (*i).c_size : 1;
00077     }
00078 
00079     set_length(length);
00080 }
00081 
00082 // Construct an instance of Array. The (BaseType *) is assumed to be
00083 // allocated using new - The dtor for Vector will delete this object.
00084 
00100 Array::Array(const string &n, BaseType *v) : Vector(n, 0, dods_array_c)
00101 {
00102     add_var(v); // Vector::add_var() stores null is v is null
00103 }
00104 
00118 Array::Array(const string &n, const string &d, BaseType *v)
00119     : Vector(n, d, 0, dods_array_c)
00120 {
00121     add_var(v); // Vector::add_var() stores null is v is null
00122 }
00123 
00125 Array::Array(const Array &rhs) : Vector(rhs)
00126 {
00127     _duplicate(rhs);
00128 }
00129 
00131 Array::~Array()
00132 {
00133     DBG(cerr << "Entering ~Array (" << this << ")" << endl);
00134     DBG(cerr << "Exiting ~Array" << endl);
00135 }
00136 
00137 BaseType *
00138 Array::ptr_duplicate()
00139 {
00140     return new Array(*this);
00141 }
00142 
00143 Array &
00144 Array::operator=(const Array &rhs)
00145 {
00146     if (this == &rhs)
00147         return *this;
00148 
00149     dynamic_cast<Vector &>(*this) = rhs;
00150 
00151     _duplicate(rhs);
00152 
00153     return *this;
00154 }
00155 
00175 void
00176 Array::add_var(BaseType *v, Part)
00177 {
00178     if (v && v->type() == dods_array_c) {
00179         Array &a = dynamic_cast<Array&>(*v);
00180         Vector::add_var(a.var());
00181         Dim_iter i = a.dim_begin();
00182         Dim_iter i_end = a.dim_end();
00183         while (i != i_end) {
00184             append_dim(a.dimension_size(i), a.dimension_name(i));
00185             ++i;
00186         }
00187     }
00188     else {
00189         Vector::add_var(v);
00190     }
00191 }
00192 
00204 void
00205 Array::append_dim(int size, string name)
00206 {
00207     dimension d;
00208 
00209     // This is invariant
00210     d.size = size;
00211     d.name = www2id(name);
00212 
00213     // this information changes with each constraint expression
00214     d.start = 0;
00215     d.stop = size - 1;
00216     d.stride = 1;
00217     d.c_size = size;
00218 
00219     _shape.push_back(d);
00220 
00221     update_length(size);
00222 }
00223 
00229 void
00230 Array::prepend_dim(int size, const string& name/* = "" */)
00231 {
00232   dimension d;
00233 
00234   // This is invariant
00235   d.size = size;
00236   d.name = www2id(name);
00237 
00238   // this information changes with each constraint expression
00239   d.start = 0;
00240   d.stop = size - 1;
00241   d.stride = 1;
00242   d.c_size = size;
00243 
00244   // Shifts the whole array, but it's tiny in general
00245   _shape.insert(_shape.begin(), d);
00246 
00247   update_length(size); // the number is ignored...
00248 }
00249 
00256 void
00257 Array::reset_constraint()
00258 {
00259     set_length(-1);
00260 
00261     for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) {
00262         (*i).start = 0;
00263         (*i).stop = (*i).size - 1;
00264         (*i).stride = 1;
00265         (*i).c_size = (*i).size;
00266 
00267         update_length((*i).size);
00268     }
00269 }
00270 
00271 
00281 void
00282 Array::clear_constraint()
00283 {
00284     reset_constraint();
00285 }
00286 
00287 // Note: MS VC++ won't tolerate embedded newlines in strings, hence the \n
00288 // is explicit.
00289 static const char *array_sss = \
00290 "Invalid constraint parameters: At least one of the start, stride or stop \n\
00291 specified do not match the array variable.";
00292 
00312 void
00313 Array::add_constraint(Dim_iter i, int start, int stride, int stop)
00314 {
00315     dimension &d = *i ;
00316 
00317     // Check for bad constraints.
00318     // Jose Garcia
00319     // Usually invalid data for a constraint is the user's mistake
00320     // because they build a wrong URL in the client side.
00321     if (start >= d.size || stop >= d.size || stride > d.size || stride <= 0)
00322         throw Error(malformed_expr, array_sss);
00323 
00324     if (((stop - start) / stride + 1) > d.size)
00325         throw Error(malformed_expr, array_sss);
00326 
00327     d.start = start;
00328     d.stop = stop;
00329     d.stride = stride;
00330 
00331     d.c_size = (stop - start) / stride + 1;
00332 
00333     DBG(cerr << "add_constraint: c_size = " << d.c_size << endl);
00334 
00335     update_length(d.c_size);
00336 }
00337 
00339 Array::Dim_iter
00340 Array::dim_begin()
00341 {
00342     return _shape.begin() ;
00343 }
00344 
00346 Array::Dim_iter
00347 Array::dim_end()
00348 {
00349     return _shape.end() ;
00350 }
00351 
00361 unsigned int
00362 Array::dimensions(bool /*constrained*/)
00363 {
00364     unsigned int dim = 0;
00365     for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
00366         dim++;
00367     }
00368 
00369     return dim;
00370 }
00371 
00389 int
00390 Array::dimension_size(Dim_iter i, bool constrained)
00391 {
00392     int size = 0;
00393 
00394     if (!_shape.empty()) {
00395         if (constrained)
00396             size = (*i).c_size;
00397         else
00398             size = (*i).size;
00399     }
00400 
00401     return size;
00402 }
00403 
00422 int
00423 Array::dimension_start(Dim_iter i, bool /*constrained*/)
00424 {
00425     return (!_shape.empty()) ? (*i).start : 0;
00426 }
00427 
00446 int
00447 Array::dimension_stop(Dim_iter i, bool /*constrained*/)
00448 {
00449     return (!_shape.empty()) ? (*i).stop : 0;
00450 }
00451 
00471 int
00472 Array::dimension_stride(Dim_iter i, bool /*constrained*/)
00473 {
00474     return (!_shape.empty()) ? (*i).stride : 0;
00475 }
00476 
00487 string
00488 Array::dimension_name(Dim_iter i)
00489 {
00490     // Jose Garcia
00491     // Since this method is public, it is possible for a user
00492     // to call it before the Array object has been properly set
00493     // this will cause an exception which is the user's fault.
00494     // (User in this context is the developer of the surrogate library.)
00495     if (_shape.empty())
00496         throw  InternalErr(__FILE__, __LINE__,
00497                            "*This* array has no dimensions.");
00498     return (*i).name;
00499 }
00500 
00504 unsigned int Array::width(bool constrained)
00505 {
00506 
00507         if (constrained) {
00508                 // This preserves the original method's semantics when we ask for the
00509                 // size of the constrained array but no constraint has been applied.
00510                 // In this case, length will be -1. Wrong, I know...
00511                 return length() * var()->width(constrained);
00512         }
00513         else {
00514                 int length = 1;
00515                 for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) {
00516                         length *= dimension_size(i, false);
00517                 }
00518                 return length * var()->width(false);
00519         }
00520 }
00521 
00522 
00523 #if FILE_METHODS
00524 
00541 void
00542 Array::print_decl(FILE *out, string space, bool print_semi,
00543                   bool constraint_info, bool constrained)
00544 {
00545     if (constrained && !send_p())
00546         return;
00547 
00548     // print it, but w/o semicolon
00549     var()->print_decl(out, space, false, constraint_info, constrained);
00550 
00551     for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
00552         fprintf(out, "[") ;
00553         if ((*i).name != "") {
00554             fprintf(out, "%s = ", id2www((*i).name).c_str()) ;
00555         }
00556         if (constrained) {
00557             fprintf(out, "%d]", (*i).c_size) ;
00558         }
00559         else {
00560             fprintf(out, "%d]", (*i).size) ;
00561         }
00562     }
00563 
00564     if (print_semi) {
00565         fprintf(out, ";\n") ;
00566     }
00567 }
00568 #endif
00569 
00587 void
00588 Array::print_decl(ostream &out, string space, bool print_semi,
00589                   bool constraint_info, bool constrained)
00590 {
00591     if (constrained && !send_p())
00592         return;
00593 
00594     // print it, but w/o semicolon
00595     var()->print_decl(out, space, false, constraint_info, constrained);
00596 
00597     for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
00598         out << "[" ;
00599         if ((*i).name != "") {
00600             out << id2www((*i).name) << " = " ;
00601         }
00602         if (constrained) {
00603             out << (*i).c_size << "]" ;
00604         }
00605         else {
00606             out << (*i).size << "]" ;
00607         }
00608     }
00609 
00610     if (print_semi) {
00611         out << ";\n" ;
00612     }
00613 }
00614 #if FILE_METHODS
00615 
00618 void
00619 Array::print_xml(FILE *out, string space, bool constrained)
00620 {
00621     print_xml_core(out, space, constrained, "Array");
00622 }
00623 #endif
00624 
00628 void
00629 Array::print_xml(ostream &out, string space, bool constrained)
00630 {
00631     print_xml_core(out, space, constrained, "Array");
00632 }
00633 
00634 #if FILE_METHODS
00635 
00638 void
00639 Array::print_as_map_xml(FILE *out, string space, bool constrained)
00640 {
00641     print_xml_core(out, space, constrained, "Map");
00642 }
00643 #endif
00644 
00648 void
00649 Array::print_as_map_xml(ostream &out, string space, bool constrained)
00650 {
00651     print_xml_core(out, space, constrained, "Map");
00652 }
00653 
00654 #if FILE_METHODS
00655 class PrintArrayDim : public unary_function<Array::dimension&, void>
00656 {
00657     FILE *d_out;
00658     string d_space;
00659     bool d_constrained;
00660 public:
00661     PrintArrayDim(FILE *o, string s, bool c)
00662             : d_out(o), d_space(s), d_constrained(c)
00663     {}
00664 
00665     void operator()(Array::dimension &d)
00666     {
00667         int size = d_constrained ? d.c_size : d.size;
00668         if (d.name.empty())
00669             fprintf(d_out, "%s<dimension size=\"%d\"/>\n", d_space.c_str(),
00670                     size);
00671         else
00672             fprintf(d_out, "%s<dimension name=\"%s\" size=\"%d\"/>\n",
00673                     d_space.c_str(), id2xml(d.name).c_str(), size);
00674     }
00675 };
00676 
00680 void
00681 Array::print_xml_core(FILE *out, string space, bool constrained, string tag)
00682 {
00683     if (constrained && !send_p())
00684         return;
00685 
00686     fprintf(out, "%s<%s", space.c_str(), tag.c_str());
00687     if (!name().empty())
00688         fprintf(out, " name=\"%s\"", id2xml(name()).c_str());
00689     fprintf(out , ">\n");
00690 
00691     get_attr_table().print_xml(out, space + "    ", constrained);
00692 
00693     BaseType *btp = var();
00694     string tmp_name = btp->name();
00695     btp->set_name("");
00696     btp->print_xml(out, space + "    ", constrained);
00697     btp->set_name(tmp_name);
00698 
00699     for_each(dim_begin(), dim_end(),
00700              PrintArrayDim(out, space + "    ", constrained));
00701 
00702     fprintf(out, "%s</%s>\n", space.c_str(), tag.c_str());
00703 }
00704 #endif
00705 
00706 class PrintArrayDimStrm : public unary_function<Array::dimension&, void>
00707 {
00708     ostream &d_out;
00709     string d_space;
00710     bool d_constrained;
00711 public:
00712     PrintArrayDimStrm(ostream &o, string s, bool c)
00713             : d_out(o), d_space(s), d_constrained(c)
00714     {}
00715 
00716     void operator()(Array::dimension &d)
00717     {
00718         int size = d_constrained ? d.c_size : d.size;
00719         if (d.name.empty())
00720         d_out << d_space << "<dimension size=\"" << size << "\"/>\n" ;
00721         else
00722         d_out << d_space << "<dimension name=\"" << id2xml(d.name)
00723               << "\" size=\"" << size << "\"/>\n" ;
00724     }
00725 };
00726 
00730 void
00731 Array::print_xml_core(ostream &out, string space, bool constrained, string tag)
00732 {
00733     if (constrained && !send_p())
00734         return;
00735 
00736     out << space << "<" << tag;
00737     if (!name().empty())
00738         out << " name=\"" << id2xml(name()) << "\"";
00739     out << ">\n";
00740 
00741     get_attr_table().print_xml(out, space + "    ", constrained);
00742 
00743     BaseType *btp = var();
00744     string tmp_name = btp->name();
00745     btp->set_name("");
00746     btp->print_xml(out, space + "    ", constrained);
00747     btp->set_name(tmp_name);
00748 
00749     for_each(dim_begin(), dim_end(), PrintArrayDimStrm(out, space + "    ", constrained));
00750 
00751     out << space << "</" << tag << ">\n";
00752 }
00753 
00754 void
00755 Array::print_xml_writer(XMLWriter &xml, bool constrained)
00756 {
00757     print_xml_writer_core(xml, constrained, "Array");
00758 }
00759 
00760 void
00761 Array::print_as_map_xml_writer(XMLWriter &xml, bool constrained)
00762 {
00763     print_xml_writer_core(xml, constrained, "Map");
00764 }
00765 
00766 class PrintArrayDimXMLWriter : public unary_function<Array::dimension&, void>
00767 {
00768     XMLWriter &xml;
00769     bool d_constrained;
00770 public:
00771     PrintArrayDimXMLWriter(XMLWriter &xml, bool c) : xml(xml), d_constrained(c) {}
00772 
00773     void operator()(Array::dimension &d)
00774     {
00775         if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)"dimension") < 0)
00776             throw InternalErr(__FILE__, __LINE__, "Could not write dimension element");
00777 
00778         if (!d.name.empty())
00779             if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)d.name.c_str()) < 0)
00780                 throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
00781 
00782         ostringstream size;
00783         size << (d_constrained ? d.c_size : d.size);
00784         if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "size", (const xmlChar*)size.str().c_str()) < 0)
00785             throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
00786 
00787         if (xmlTextWriterEndElement(xml.get_writer()) < 0)
00788             throw InternalErr(__FILE__, __LINE__, "Could not end dimension element");
00789     }
00790 };
00791 
00792 void
00793 Array::print_xml_writer_core(XMLWriter &xml, bool constrained, string tag)
00794 {
00795     if (constrained && !send_p())
00796         return;
00797 
00798     if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)tag.c_str()) < 0)
00799         throw InternalErr(__FILE__, __LINE__, "Could not write " + tag + " element");
00800 
00801     if (!name().empty())
00802         if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0)
00803             throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
00804 
00805     get_attr_table().print_xml_writer(xml);
00806 
00807     BaseType *btp = var();
00808     string tmp_name = btp->name();
00809     btp->set_name("");
00810     btp->print_xml_writer(xml, constrained);
00811     btp->set_name(tmp_name);
00812 
00813     for_each(dim_begin(), dim_end(), PrintArrayDimXMLWriter(xml, constrained));
00814 
00815     if (xmlTextWriterEndElement(xml.get_writer()) < 0)
00816         throw InternalErr(__FILE__, __LINE__, "Could not end " + tag + " element");
00817 }
00818 
00819 #if FILE_METHODS
00820 
00831 unsigned int
00832 Array::print_array(FILE *out, unsigned int index, unsigned int dims,
00833                    unsigned int shape[])
00834 {
00835     if (dims == 1) {
00836         fprintf(out, "{") ;
00837         for (unsigned i = 0; i < shape[0] - 1; ++i) {
00838             var(index++)->print_val(out, "", false);
00839             fprintf(out, ", ") ;
00840         }
00841         var(index++)->print_val(out, "", false);
00842         fprintf(out, "}") ;
00843 
00844         return index;
00845     }
00846     else {
00847         fprintf(out, "{") ;
00848         // Fixed an off-by-one error in the following loop. Since the array
00849         // length is shape[dims-1]-1 *and* since we want one less dimension
00850         // than that, the correct limit on this loop is shape[dims-2]-1. From
00851         // Todd Karakasian.
00852         // The saga continues; the loop test should be `i < shape[0]-1'. jhrg
00853         // 9/12/96.
00854         for (unsigned i = 0; i < shape[0] - 1; ++i) {
00855             index = print_array(out, index, dims - 1, shape + 1);
00856             fprintf(out, ",") ;   // Removed the extra `}'. Also from Todd
00857         }
00858         index = print_array(out, index, dims - 1, shape + 1);
00859         fprintf(out, "}") ;
00860 
00861         return index;
00862     }
00863 }
00864 #endif
00865 
00877 unsigned int
00878 Array::print_array(ostream &out, unsigned int index, unsigned int dims,
00879                    unsigned int shape[])
00880 {
00881     if (dims == 1) {
00882         out << "{" ;
00883         for (unsigned i = 0; i < shape[0] - 1; ++i) {
00884             var(index++)->print_val(out, "", false);
00885             out << ", " ;
00886         }
00887         var(index++)->print_val(out, "", false);
00888         out << "}" ;
00889 
00890         return index;
00891     }
00892     else {
00893         out << "{" ;
00894         // Fixed an off-by-one error in the following loop. Since the array
00895         // length is shape[dims-1]-1 *and* since we want one less dimension
00896         // than that, the correct limit on this loop is shape[dims-2]-1. From
00897         // Todd Karakasian.
00898         // The saga continues; the loop test should be `i < shape[0]-1'. jhrg
00899         // 9/12/96.
00900         for (unsigned i = 0; i < shape[0] - 1; ++i) {
00901             index = print_array(out, index, dims - 1, shape + 1);
00902             out << "," ;
00903         }
00904         index = print_array(out, index, dims - 1, shape + 1);
00905         out << "}" ;
00906 
00907         return index;
00908     }
00909 }
00910 
00911 #if FILE_METHODS
00912 void
00913 Array::print_val(FILE *out, string space, bool print_decl_p)
00914 {
00915     // print the declaration if print decl is true.
00916     // for each dimension,
00917     //   for each element,
00918     //     print the array given its shape, number of dimensions.
00919     // Add the `;'
00920 
00921     if (print_decl_p) {
00922         print_decl(out, space, false, false, false);
00923         fprintf(out, " = ") ;
00924     }
00925 
00926     unsigned int *shape = new unsigned int[_shape.size()];
00927     unsigned int index = 0;
00928     for (Dim_iter i = _shape.begin(); i != _shape.end() && index < _shape.size(); i++)
00929         shape[index++] = dimension_size(i, true);
00930 
00931     print_array(out, 0, _shape.size(), shape);
00932 
00933     delete [] shape; shape = 0;
00934 
00935     if (print_decl_p) {
00936         fprintf(out, ";\n") ;
00937     }
00938 }
00939 #endif
00940 
00941 void
00942 Array::print_val(ostream &out, string space, bool print_decl_p)
00943 {
00944     // print the declaration if print decl is true.
00945     // for each dimension,
00946     //   for each element,
00947     //     print the array given its shape, number of dimensions.
00948     // Add the `;'
00949 
00950     if (print_decl_p) {
00951         print_decl(out, space, false, false, false);
00952             out << " = " ;
00953     }
00954 
00955     unsigned int *shape = new unsigned int[dimensions(true)];
00956     unsigned int index = 0;
00957     for (Dim_iter i = _shape.begin(); i != _shape.end() && index < dimensions(true); ++i)
00958         shape[index++] = dimension_size(i, true);
00959 
00960     print_array(out, 0, dimensions(true), shape);
00961 
00962     delete [] shape; shape = 0;
00963 
00964     if (print_decl_p) {
00965             out << ";\n" ;
00966     }
00967 }
00968 
00978 bool
00979 Array::check_semantics(string &msg, bool)
00980 {
00981     bool sem = BaseType::check_semantics(msg) && !_shape.empty();
00982 
00983     if (!sem)
00984         msg = "An array variable must have dimensions";
00985 
00986     return sem;
00987 }
00988 
00997 void
00998 Array::dump(ostream &strm) const
00999 {
01000     strm << DapIndent::LMarg << "Array::dump - ("
01001     << (void *)this << ")" << endl ;
01002     DapIndent::Indent() ;
01003     Vector::dump(strm) ;
01004     strm << DapIndent::LMarg << "shape:" << endl ;
01005     DapIndent::Indent() ;
01006     Dim_citer i = _shape.begin() ;
01007     Dim_citer ie = _shape.end() ;
01008     unsigned int dim_num = 0 ;
01009     for (; i != ie; i++) {
01010         strm << DapIndent::LMarg << "dimension " << dim_num++ << ":"
01011              << endl ;
01012         DapIndent::Indent() ;
01013         strm << DapIndent::LMarg << "name: " << (*i).name << endl ;
01014         strm << DapIndent::LMarg << "size: " << (*i).size << endl ;
01015         strm << DapIndent::LMarg << "start: " << (*i).start << endl ;
01016         strm << DapIndent::LMarg << "stop: " << (*i).stop << endl ;
01017         strm << DapIndent::LMarg << "stride: " << (*i).stride << endl ;
01018         strm << DapIndent::LMarg << "constrained size: " << (*i).c_size
01019              << endl ;
01020         DapIndent::UnIndent() ;
01021     }
01022     DapIndent::UnIndent() ;
01023     DapIndent::UnIndent() ;
01024 }
01025 
01026 } // namespace libdap
01027