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