1 // -*- mode: c++; c-basic-offset:4 -*-
2 
3 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
4 // Access Protocol.
5 
6 // Copyright (c) 2002,2003 OPeNDAP, Inc.
7 // Author: James Gallagher <jgallagher@opendap.org>
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 //
23 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
24 
25 // (c) COPYRIGHT URI/MIT 1995-1996,1999
26 // Please read the full copyright statement in the file COPYRIGHT_URI.
27 //
28 // Authors:
29 //      jhrg,jimg       James Gallagher <jgallagher@gso.uri.edu>
30 
31 // Implementation for TestArray. See TestByte.cc
32 //
33 // jhrg 1/12/95
34 
35 #include "config.h"
36 
37 #include <cstring>
38 
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42 
43 #ifndef WIN32
44 #else
45 #include <io.h>
46 #include <fcntl.h>
47 #include <process.h>
48 #endif
49 
50 //#define DODS_DEBUG
51 
52 #include "util.h"
53 #include "debug.h"
54 
55 #include "TestInt8.h"
56 
57 #include "TestByte.h"
58 #include "TestInt16.h"
59 #include "TestUInt16.h"
60 #include "TestInt32.h"
61 #include "TestUInt32.h"
62 
63 #include "TestInt64.h"
64 #include "TestUInt64.h"
65 
66 #include "TestD4Enum.h"
67 
68 #include "TestFloat32.h"
69 #include "TestFloat64.h"
70 
71 #include "TestStr.h"
72 
73 #include "TestArray.h"
74 #include "TestCommon.h"
75 
76 using std::cerr;
77 using std::endl;
78 
79 extern int test_variable_sleep_interval;
80 
_duplicate(const TestArray & ts)81 void TestArray::_duplicate(const TestArray &ts)
82 {
83     d_series_values = ts.d_series_values;
84 }
85 
86 BaseType *
ptr_duplicate()87 TestArray::ptr_duplicate()
88 {
89     return new TestArray(*this);
90 }
91 
TestArray(const string & n,BaseType * v,bool is_dap4)92 TestArray::TestArray(const string &n, BaseType *v, bool is_dap4) :
93         Array(n, v, is_dap4), d_series_values(false)
94 {
95 }
96 
TestArray(const string & n,const string & d,BaseType * v,bool is_dap4)97 TestArray::TestArray(const string &n, const string &d, BaseType *v, bool is_dap4) :
98         Array(n, d, v, is_dap4), d_series_values(false)
99 {
100 }
101 
TestArray(const TestArray & rhs)102 TestArray::TestArray(const TestArray &rhs) :
103         Array(rhs), TestCommon(rhs)
104 {
105     _duplicate(rhs);
106 }
107 
~TestArray()108 TestArray::~TestArray()
109 {
110 }
111 
112 TestArray &
operator =(const TestArray & rhs)113 TestArray::operator=(const TestArray &rhs)
114 {
115     if (this == &rhs) return *this;
116 
117     dynamic_cast<Array &>(*this) = rhs;
118 
119     _duplicate(rhs);
120 
121     return *this;
122 }
123 
124 // This code calls 'output_values()' because print_val() does not test
125 // the value of send_p(). We need to wrap a method around the calls to
126 // print_val() to ensure that only values for variables with send_p() set
127 // are called. In the serialize/deserialize case, the 'client' DDS only
128 // has variables sent by the 'server' but in the intern_data() case, the
129 // whole DDS is still present but only variables selected in the CE have
130 // values.
131 
m_print_array(ostream & out,unsigned int index,unsigned int dims,unsigned int shape[])132 unsigned int TestArray::m_print_array(ostream &out, unsigned int index, unsigned int dims, unsigned int shape[])
133 {
134     if (dims == 1) {
135         out << "{";
136 
137         // Added this test for zero-length arrays. jhrg 1/28/16
138         if (shape[0] >= 1) {
139             for (unsigned i = 0; i < shape[0] - 1; ++i) {
140                 dynamic_cast<TestCommon&>(*var(index++)).output_values(out);
141                 out << ", ";
142             }
143 
144             dynamic_cast<TestCommon&>(*var(index++)).output_values(out);
145         }
146 
147         out << "}";
148 
149         return index;
150     }
151     else {
152         out << "{";
153         // Fixed an off-by-one error in the following loop. Since the array
154         // length is shape[dims-1]-1 *and* since we want one less dimension
155         // than that, the correct limit on this loop is shape[dims-2]-1. From
156         // Todd Karakasian.
157         //
158         // The saga continues; the loop test should be `i < shape[0]-1'. jhrg
159         // 9/12/96.
160         //
161         // Added this (primitive) guard against errors when a zero-length array
162         // is declared with a shape like [0][4]. jhrg 1/28/16
163         if (shape[0] > 0) {
164             for (unsigned i = 0; i < shape[0] - 1; ++i) {
165                 index = m_print_array(out, index, dims - 1, shape + 1);
166                 out << ",";
167             }
168 
169             index = m_print_array(out, index, dims - 1, shape + 1);
170         }
171 
172         out << "}";
173 
174         return index;
175     }
176 }
177 
output_values(std::ostream & out)178 void TestArray::output_values(std::ostream &out)
179 {
180     //unsigned int *shape = new unsigned int[dimensions(true)];
181 
182     vector<unsigned int> shape(dimensions(true));
183     unsigned int index = 0;
184     for (Dim_iter i = dim_begin(); i != dim_end() && index < dimensions(true); ++i)
185         shape[index++] = dimension_size(i, true);
186 
187     m_print_array(out, 0, dimensions(true), &shape[0]);
188 
189     //delete[] shape;
190     //shape = 0;
191 }
192 
193 /** Special names are ones that start with 'lat' or 'lon'. These indicate
194  that the vector (this is only for vectors) is a vector of latitude or
195  longitude values. */
m_name_is_special()196 bool TestArray::m_name_is_special()
197 {
198     return (name().find("lat") != string::npos || name().find("lon") != string::npos);
199 }
200 
m_build_special_values()201 void TestArray::m_build_special_values()
202 {
203     if (name().find("lat_reversed") != string::npos) {
204         int array_len = length();
205         //double *lat_data = new double[array_len];
206         vector<double> lat_data(array_len);
207         for (int i = 0; i < array_len; ++i) {
208             lat_data[i] = -89 + (180 / array_len) * (i + 1);
209         }
210         libdap::set_array_using_double(this, &lat_data[0], array_len);
211     }
212     else if (name().find("lat") != string::npos) {
213         int array_len = length();
214         // double *lat_data = new double[array_len];
215         vector<double> lat_data(array_len);
216         for (int i = 0; i < array_len; ++i) {
217             lat_data[i] = 90 - (180 / array_len) * (i + 1);
218         }
219         libdap::set_array_using_double(this, &lat_data[0], array_len);
220     }
221     else if (name().find("lon") != string::npos) {
222         int array_len = length();
223         //double *lon_data = new double[array_len];
224         vector<double> lon_data(array_len);
225         for (int i = 0; i < array_len; ++i) {
226             lon_data[i] = (360 / array_len) * (i + 1);
227         }
228         libdap::set_array_using_double(this, &lon_data[0], array_len);
229     }
230     else {
231         throw InternalErr(__FILE__, __LINE__, "Unrecognized name");
232     }
233 }
234 
m_offset(int y,Dim_iter X,int x)235 int TestArray::m_offset(int y, Dim_iter X, int x)
236 {
237     return y * dimension_size(X, false) + x;
238 }
239 
240 /**
241  * @brief Load an 2D array with values.
242  * Use the read() function for the prototype element of the array to
243  * get values and load them into an array, then constrain the array.
244  * Thus if 'series values' are used and the array is constrained, the
245  * result will 'make sense'
246  *
247  * @param constrained_array
248  */
249 template<typename T, class C>
m_constrained_matrix(vector<T> & constrained_array)250 void TestArray::m_constrained_matrix(vector<T>&constrained_array)
251 {
252     int unconstrained_size = 1;
253     Dim_iter d = dim_begin();
254     while (d != dim_end())
255         unconstrained_size *= dimension_size(d++, false);
256 
257     vector<T> whole_array(unconstrained_size);
258     for (int i = 0; i < unconstrained_size; ++i) {
259         T v;
260         var()->read();
261 #if 0
262         if (var()->type() == dods_enum_c)
263         static_cast<C*>(var())->value(&v);
264         else
265 #endif
266         v = static_cast<C*>(var())->value();
267 
268         whole_array[i] = v;
269         var()->set_read_p(false); // pick up the next value
270     }
271 
272     DBG(cerr << "whole_array: "; copy(whole_array.begin(), whole_array.end(), ostream_iterator<T>(cerr, ", ")); cerr << endl);
273 
274     Dim_iter Y = dim_begin();
275     Dim_iter X = Y + 1;
276 
277     DBG(cerr << "dimension_start(Y): " << dimension_start(Y) << endl); DBG(cerr << "dimension_stop(Y): " << dimension_stop(Y) << endl); DBG(cerr << "dimension_start(X): " << dimension_start(X) << endl); DBG(cerr << "dimension_stop(X): " << dimension_stop(X) << endl);
278 
279     int constrained_size = 0;
280     int y = dimension_start(Y);
281     while (y < dimension_stop(Y) + 1) {
282         int x = dimension_start(X);
283 
284         while (x < dimension_stop(X) + 1) {
285             constrained_array[constrained_size++] = whole_array[m_offset(y, X, x)];
286             x += dimension_stride(X);
287         }
288 
289         y += dimension_stride(Y);
290     }
291 }
292 
293 template<typename T>
m_enum_constrained_matrix(vector<T> & constrained_array)294 void TestArray::m_enum_constrained_matrix(vector<T>&constrained_array)
295 {
296     int unconstrained_size = 1;
297     Dim_iter d = dim_begin();
298     while (d != dim_end())
299         unconstrained_size *= dimension_size(d++, false);
300 
301     vector<T> whole_array(unconstrained_size);
302     for (int i = 0; i < unconstrained_size; ++i) {
303         T v;
304         var()->read();
305         static_cast<D4Enum*>(var())->value(&v);
306         whole_array[i] = v;
307         var()->set_read_p(false); // pick up the next value
308     }
309 
310     DBG(cerr << "whole_array: "; copy(whole_array.begin(), whole_array.end(), ostream_iterator<T>(cerr, ", ")); cerr << endl);
311 
312     Dim_iter Y = dim_begin();
313     Dim_iter X = Y + 1;
314 
315     DBG(cerr << "dimension_start(Y): " << dimension_start(Y) << endl); DBG(cerr << "dimension_stop(Y): " << dimension_stop(Y) << endl); DBG(cerr << "dimension_start(X): " << dimension_start(X) << endl); DBG(cerr << "dimension_stop(X): " << dimension_stop(X) << endl);
316 
317     int constrained_size = 0;
318     int y = dimension_start(Y);
319     while (y < dimension_stop(Y) + 1) {
320         int x = dimension_start(X);
321 
322         while (x < dimension_stop(X) + 1) {
323             constrained_array[constrained_size++] = whole_array[m_offset(y, X, x)];
324             x += dimension_stride(X);
325         }
326 
327         y += dimension_stride(Y);
328     }
329 }
330 
331 /**
332  * Load the variable's internal data buffer with values, simulating a read()
333  * call to some data store. A private method.
334  */
335 template<typename T, class C>
m_cardinal_type_read_helper()336 void TestArray::m_cardinal_type_read_helper()
337 {
338     if (get_series_values()) {
339         // Special case code for vectors that have specific names.
340         // This is used to test code that works with lat/lon data.
341         if (dimensions() == 1 && m_name_is_special()) {
342             m_build_special_values();
343         }
344         else if (dimensions() == 2) {
345             vector<T> tmp(length());
346             m_constrained_matrix<T, C>(tmp);
347             set_value(tmp, length());
348         }
349         else {
350             vector<T> tmp(length());
351             for (int64_t i = 0, end = length(); i < end; ++i) {
352                 var()->read();
353                 tmp[i] = static_cast<C*>(var())->value();
354                 var()->set_read_p(false); // pick up the next value
355             }
356             set_value(tmp, length());
357         }
358     }
359     else {
360         // read a value into the Array's prototype element
361         var()->read();
362         T value = static_cast<C*>(var())->value();
363         vector<T> tmp(length());
364         for (int64_t i = 0, end = length(); i < end; ++i) {
365             tmp[i] = value;
366         }
367 
368         set_value(tmp, length());
369     }
370 }
371 
372 /**
373  * Load the variable's internal data buffer with values, simulating a read()
374  * call to some data store. A private method.
375  */
376 template<typename T>
m_enum_type_read_helper()377 void TestArray::m_enum_type_read_helper()
378 {
379     if (get_series_values()) {
380         if (dimensions() == 2) {
381             vector<T> tmp(length());
382             m_enum_constrained_matrix<T>(tmp);
383             set_value(tmp, length());
384         }
385         else {
386             vector<T> tmp(length());
387             for (int64_t i = 0, end = length(); i < end; ++i) {
388                 var()->read();
389                 T v;
390                 static_cast<D4Enum*>(var())->value(&v);
391 
392                 tmp[i] = v;
393                 var()->set_read_p(false); // pick up the next value
394             }
395             set_value(tmp, length());
396         }
397     }
398     else {
399         // read a value into the Array's prototype element
400         var()->read();
401         T value;
402         static_cast<D4Enum*>(var())->value(&value);
403 
404         vector<T> tmp(length());
405         for (int64_t i = 0, end = length(); i < end; ++i) {
406             tmp[i] = value;
407         }
408 
409         set_value(tmp, length());
410     }
411 }
412 
read()413 bool TestArray::read()
414 {
415     if (read_p()) return true;
416 
417     if (test_variable_sleep_interval > 0) sleep(test_variable_sleep_interval);
418 
419     int64_t array_len = length(); // elements in the array
420 
421     switch (var()->type()) {
422     // These are the DAP2 types and the classes that implement them all define
423     // the old buf2val() and val2buf() methods. For the new DAP4 types see below.
424     //case dods_byte_c:
425     //case dods_uint8_c:
426     case dods_int16_c:
427         m_cardinal_type_read_helper<dods_int16, Int16>();
428         set_read_p(true);
429         break;
430 
431     case dods_uint16_c:
432         m_cardinal_type_read_helper<dods_uint16, UInt16>();
433         set_read_p(true);
434         break;
435 
436     case dods_int32_c:
437         m_cardinal_type_read_helper<dods_int32, Int32>();
438         set_read_p(true);
439         break;
440 
441     case dods_uint32_c:
442         m_cardinal_type_read_helper<dods_uint32, UInt32>();
443         set_read_p(true);
444         break;
445 
446     case dods_float32_c:
447         m_cardinal_type_read_helper<dods_float32, Float32>();
448         set_read_p(true);
449         break;
450 
451     case dods_float64_c:
452         m_cardinal_type_read_helper<dods_float64, Float64>();
453         set_read_p(true);
454         break;
455 
456     case dods_int8_c:
457         m_cardinal_type_read_helper<dods_int8, Int8>();
458         set_read_p(true);
459         break;
460 
461     case dods_byte_c:
462     case dods_char_c:
463     case dods_uint8_c:
464         m_cardinal_type_read_helper<dods_byte, Byte>();
465         set_read_p(true);
466         break;
467 
468     case dods_int64_c:
469         m_cardinal_type_read_helper<dods_int64, Int64>();
470         set_read_p(true);
471         break;
472 
473     case dods_uint64_c:
474         m_cardinal_type_read_helper<dods_uint64, UInt64>();
475         set_read_p(true);
476         break;
477 
478     case dods_enum_c:
479         switch (static_cast<D4Enum*>(var())->element_type()) {
480         case dods_byte_c:
481         case dods_char_c:
482         case dods_uint8_c:
483             m_enum_type_read_helper<dods_byte>();
484             break;
485         case dods_int8_c:
486             m_enum_type_read_helper<dods_int8>();
487             break;
488         case dods_int16_c:
489             m_enum_type_read_helper<dods_int16>();
490             break;
491         case dods_uint16_c:
492             m_enum_type_read_helper<dods_uint16>();
493             break;
494         case dods_int32_c:
495             m_enum_type_read_helper<dods_int32>();
496             break;
497         case dods_uint32_c:
498             m_enum_type_read_helper<dods_uint32>();
499             break;
500         case dods_int64_c:
501             m_enum_type_read_helper<dods_int64>();
502             break;
503         case dods_uint64_c:
504             m_enum_type_read_helper<dods_uint64>();
505             break;
506         default:
507             throw InternalErr(__FILE__, __LINE__, "Enum with undefined type.");
508         }
509         set_read_p(true);
510         break;
511 
512     case dods_str_c:
513     case dods_url_c: {
514         vector<string> tmp(array_len);
515 
516         if (get_series_values()) {
517             for (int64_t i = 0; i < array_len; ++i) {
518                 var()->read();
519                 // URL isa Str
520                 tmp[i] = static_cast<Str*>(var())->value();
521                 var()->set_read_p(false); // pick up the next value
522             }
523         }
524         else {
525             var()->read();
526             string value = static_cast<Str*>(var())->value();
527 
528             for (unsigned i = 0; i < array_len; ++i)
529                 tmp[i] = value;
530         }
531 
532         set_value(tmp, array_len);
533         set_read_p(true);
534         break;
535     }
536 
537     case dods_opaque_c:
538     case dods_structure_c:
539         vec_resize(array_len);
540         for (unsigned i = 0; i < array_len; ++i) {
541             // Copy the prototype and read a value into it
542             BaseType *elem = var()->ptr_duplicate();
543             elem->read();
544             // Load the new value into this object's array
545             set_vec_nocopy(i, elem);   // Use set_vec_nocopy() TODO (and below)
546         }
547         set_read_p(true);
548         break;
549 
550     case dods_sequence_c:
551         // No sequence arrays in DAP2
552         if (!is_dap4()) throw InternalErr(__FILE__, __LINE__, "Bad data type");
553 
554         vec_resize(array_len);
555         for (unsigned i = 0; i < array_len; ++i) {
556             // Load the new BaseType (a D4Sequence) into the array element
557             set_vec_nocopy(i, var()->ptr_duplicate());
558         }
559 
560         break;
561 
562         // No Grids in DAP4; No arrays of arrays and no null-typed vars in DAP2 or 4
563     case dods_grid_c:
564     case dods_array_c:
565     case dods_null_c:
566     default:
567         throw InternalErr(__FILE__, __LINE__, "Bad data type");
568     }
569 
570     // set_read_p(true);
571 
572     return true;
573 }
574 
set_series_values(bool sv)575 void TestArray::set_series_values(bool sv)
576 {
577     dynamic_cast<TestCommon&>(*var()).set_series_values(sv);
578     d_series_values = sv;
579 }
580