bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
get_ascii_dap4.cc
1
2// -*- mode: c++; c-basic-offset:4 -*-
3
4// Copyright (c) 2006 OPeNDAP, Inc.
5// Author: James Gallagher <jgallagher@opendap.org>
6//
7// This is free software; you can redistribute it and/or modify it under the
8// terms of the GNU Lesser General Public License as published by the Free
9// Software Foundation; either version 2.1 of the License, or (at your
10// option) any later version.
11//
12// This is distributed in the hope that it will be useful, but WITHOUT ANY
13// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
15// more details.
16//
17// You should have received a copy of the GNU Lesser General Public
18// License along with this library; if not, write to the Free Software
19// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20//
21// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
22
23// This file holds the interface for the 'get data as ascii' function of the
24// OPeNDAP/HAO data server. This function is called by the BES when it loads
25// this as a module. The functions in the file ascii_val.cc also use this, so
26// the same basic processing software can be used both by Hyrax and tie older
27// Server3.
28
29#include <iostream>
30#include <sstream>
31#include <iomanip>
32
33#include <libdap/DMR.h>
34#include <libdap/BaseType.h>
35#include <libdap/Structure.h>
36#include <libdap/Array.h>
37#include <libdap/D4Sequence.h>
38#include <libdap/D4Enum.h>
39#include <libdap/D4Opaque.h>
40#include <libdap/D4Group.h>
41#include <libdap/crc.h>
42#include <libdap/InternalErr.h>
43
44#include "get_ascii_dap4.h"
45
46namespace dap_asciival {
47
48using namespace libdap;
49using namespace std;
50
51// most of the code here defines functions before they are used; these three
52// need to be declared.
53static void print_values_as_ascii(BaseType *btp, bool print_name, ostream &strm, Crc32 &checksum);
54static void print_sequence_header(D4Sequence *s, ostream &strm);
55static void print_val_by_rows(D4Sequence *seq, ostream &strm, Crc32 &checksum);
56
67static void print_array_vector(Array *a, ostream &strm, bool print_name)
68{
69 if (print_name)
70 strm << a->FQN() << ", " ;
71
72 // only one dimension
73 // Added to support zero-length arrays. jhrg 2/2/16
74//cerr<<"coming to print_array_vector "<<endl;
75 if (a->dimension_size_ll(a->dim_begin(), true) > 0) {
76 int64_t end = a->dimension_size_ll(a->dim_begin(), true) - 1;
77
78 for (int64_t i = 0; i < end; ++i) {
79 a->var_ll(i)->print_val(strm, "", false /*print_decl*/);
80 strm << ", ";
81 }
82 a->var(end)->print_val(strm, "", false /*print_decl*/);
83 }
84}
85
97static int print_array_row(Array *a, ostream &strm, int index, int number)
98{
99 // Added to support zero-length arrays. jhrg 2/2/16
100 if (number > 0) {
101 for (int i = 0; i < number; ++i) {
102 a->var(index++)->print_val(strm, "", false /*print_decl*/);
103 strm << ", ";
104 }
105
106 a->var(index++)->print_val(strm, "", false /*print_decl*/);
107 }
108 return index;
109}
110
111// This code implements simple modulo arithmetic. The vector shape contains
112// This code implements simple modulo arithmetic. The vector shape contains
113// the maximum count value for each dimension, state contains the current
114// state. For example, if shape holds 10, 20 then when state holds 0, 20
115// calling this method will increment state to 1, 0. For this example,
116// calling the method with state equal to 10, 20 will reset state to 0, 0 and
117// the return value will be false.
118static bool increment_state(vector<int> *state, const vector<int> &shape)
119{
120 vector < int >::reverse_iterator state_riter;
121 vector < int >::const_reverse_iterator shape_riter;
122 for (state_riter = state->rbegin(), shape_riter = shape.rbegin();
123 state_riter < state->rend(); state_riter++, shape_riter++) {
124 if (*state_riter == *shape_riter - 1) {
125 *state_riter = 0;
126 }
127 else {
128 *state_riter = *state_riter + 1;
129 return true;
130 }
131 }
132
133 return false;
134}
135
136static vector <int> get_shape_vector(Array *a, size_t n)
137{
138 if (n < 1 || n > a->dimensions(true)) {
139 ostringstream oss;
140 oss << "Attempt to get " << n << " dimensions from " << a->name() << " which has " << a->dimensions(true) << " dimensions";
141 throw InternalErr(__FILE__, __LINE__, oss.str());
142 }
143
144 vector <int>shape;
145 Array::Dim_iter p = a->dim_begin();
146 for (unsigned i = 0; i < n && p != a->dim_end(); ++i, ++p) {
147 shape.push_back(a->dimension_size(p, true));
148 }
149
150 return shape;
151}
152
156static int get_nth_dim_size(Array *a, size_t n)
157{
158 if (n > a->dimensions(true) - 1) {
159 ostringstream oss;
160 oss << "Attempt to get dimension " << n << " from " << a->name() << " which has " << a->dimensions(true) << " dimensions";
161 throw InternalErr(__FILE__, __LINE__, oss.str());
162 }
163
164 return a->dimension_size(a->dim_begin() + n, true);
165}
166
167static void print_ndim_array(Array *a, ostream &strm, bool /*print_name */ )
168{
169
170 int dims = a->dimensions(true);
171 if (dims <= 1)
172 throw InternalErr(__FILE__, __LINE__, "Dimension count is <= 1 while printing multidimensional array.");
173
174 // shape holds the maximum index value of all but the last dimension of
175 // the array (not the size; each value is one less than the size).
176 vector<int> shape = get_shape_vector(a, dims - 1);
177 int rightmost_dim_size = get_nth_dim_size(a, dims - 1);
178
179 // state holds the indexes of the current row being printed. For an N-dim
180 // array, there are N-1 dims that are iterated over when printing (the
181 // Nth dim is not printed explicitly. Instead it's the number of values
182 // on the row.
183 vector<int> state(dims - 1, 0);
184
185 bool more_indices;
186 int index = 0;
187 do {
188 // Print indices for all dimensions except the last one.
189 strm << a->FQN();
190
191 for (int i = 0; i < dims - 1; ++i) {
192 strm << "[" << state[i] << "]" ;
193 }
194 strm << ", " ;
195
196 index = print_array_row(a, strm, index, rightmost_dim_size - 1);
197 more_indices = increment_state(&state, shape);
198 if (more_indices)
199 strm << endl ;
200
201 } while (more_indices);
202}
203
204static int get_index(Array *a, vector<int> indices)
205{
206 if (indices.size() != a->dimensions(true))
207 throw InternalErr(__FILE__, __LINE__, "Index vector is the wrong size!");
208
209 // suppose shape is [3][4][5][6] for x,y,z,t. The index is
210 // t + z(6) + y(5 * 6) + x(4 * 5 *6).
211 // Assume that indices[0] holds x, indices[1] holds y, ...
212
213 vector < int >shape = get_shape_vector(a, indices.size());
214
215 // We want to work from the rightmost index to the left
216 reverse(indices.begin(), indices.end());
217 reverse(shape.begin(), shape.end());
218
219 vector<int>::iterator indices_iter = indices.begin();
220 vector<int>::iterator shape_iter = shape.begin();
221
222 int index = *indices_iter++; // in the ex. above, this adds `t'
223 int multiplier = 1;
224 while (indices_iter != indices.end()) {
225 multiplier *= *shape_iter++;
226 index += multiplier * *indices_iter++;
227 }
228
229 return index;
230}
231
241static void print_complex_array(Array *a, ostream &strm, bool print_name, Crc32 &checksum)
242{
243 int dims = a->dimensions(true);
244 if (dims < 1)
245 throw InternalErr(__FILE__, __LINE__, "Dimension count is <= 1 while printing multidimensional array.");
246
247 // shape holds the maximum index value of all but the last dimension of
248 // the array (not the size; each value is one less that the size).
249 vector<int> shape = get_shape_vector(a, dims);
250
251 vector<int> state(dims, 0);
252
253 bool more_indices;
254 do {
255 // Print indices for all dimensions except the last one.
256 strm << a->FQN();
257
258 for (int i = 0; i < dims; ++i) {
259 strm << "[" << state[i] << "]" ;
260 }
261 strm << endl;
262
263 print_values_as_ascii(a->var(get_index(a, state)), print_name, strm, checksum);
264
265 more_indices = increment_state(&state, shape);
266
267 if (more_indices)
268 strm << endl;
269
270 } while (more_indices);
271}
272
282static void print_values_as_ascii(Array *a, bool print_name, ostream &strm, Crc32 &checksum)
283{
284 if (a->var()->is_simple_type()) {
285 if (a->dimensions(true) > 1) {
286 print_ndim_array(a, strm, print_name);
287 }
288 else {
289 print_array_vector(a, strm, print_name);
290 }
291 }
292 else {
293 print_complex_array(a, strm, print_name, checksum);
294 }
295}
296
297static void print_structure_header(Structure *s, ostream &strm)
298{
299 Constructor::Vars_iter p = s->var_begin(), e = s->var_end();
300 bool needs_comma = false;
301 while (p != e) {
302 if((*p)->send_p()){
303 if ((*p)->is_simple_type())
304 strm << (needs_comma?", ":"") << (*p)->FQN();
305 else if ((*p)->type() == dods_structure_c)
306 print_structure_header(static_cast<Structure*>(*p), strm);
307 else if ((*p)->type() == dods_sequence_c)
308 print_sequence_header(static_cast<D4Sequence*>(*p), strm);
309 else
310 throw InternalErr(__FILE__, __LINE__, "Unknown or unsupported type.");
311 needs_comma = true;
312 }
313 ++p;
314 }
315}
316
317static void print_structure_ascii(Structure *s, ostream &strm, bool print_name, Crc32 &checksum)
318{
319 if (s->is_linear()) {
320 if (print_name) {
321 print_structure_header(s, strm);
322 strm << endl;
323 }
324
325 Constructor::Vars_iter p = s->var_begin(), e = s->var_end();
326 while (p !=e) {
327 // bug: print_name should be false, but will be true because it's not a param here
328 if ((*p)->send_p()) print_values_as_ascii(*p, false /*print_name*/, strm, checksum);
329
330 if (++p != e) strm << ", ";
331 }
332 }
333 else {
334 for (Constructor::Vars_iter p = s->var_begin(), e = s->var_end(); p != e; ++p) {
335 if ((*p)->send_p()) {
336 print_values_as_ascii(*p, print_name, strm, checksum);
337 // This line outputs an extra endl when print_ascii is called for
338 // nested structures because an endl is written for each member
339 // and then once for the structure itself. 9/14/2001 jhrg
340 strm << endl;
341 }
342 }
343 }
344}
345
346static void print_values_as_ascii(Structure *v, bool print_name, ostream &strm, Crc32 &checksum)
347{
348 print_structure_ascii(v, strm, print_name, checksum);
349}
350
351static void print_one_row(D4Sequence *seq, ostream &strm, Crc32 &checksum, int row)
352{
353 int elements = seq->element_count();
354 int j = 0;
355 BaseType *btp = 0;
356 bool first_val = true;
357
358 while (j < elements) {
359 btp = seq->var_value(row, j++);
360 if (btp) { // data
361 if (!first_val)
362 strm << ", ";
363 first_val = false;
364 if (btp->type() == dods_sequence_c)
365 print_val_by_rows(static_cast<D4Sequence*>(btp), strm, checksum);
366 else
367 print_values_as_ascii(btp, false, strm, checksum);
368 }
369 }
370}
371
372static void print_val_by_rows(D4Sequence *seq, ostream &strm, Crc32 &checksum)
373{
374 if (seq->length() != 0) {
375 int rows = seq->length() /*- 1*/; // -1 because the last row is treated specially
376 for (int i = 0; i < rows; ++i) {
377 print_one_row(seq, strm, checksum, i);
378 strm << endl;
379 }
380 }
381}
382
383static void print_sequence_header(D4Sequence *s, ostream &strm)
384{
385 Constructor::Vars_iter p = s->var_begin(), e = s->var_end();
386 bool needs_comma = false;
387 while (p != e) {
388 if((*p)->send_p()){
389 if((*p)->is_simple_type())
390 strm << (needs_comma?", ":"") << (*p)->FQN();
391 else if ((*p)->type() == dods_structure_c)
392 print_structure_header(static_cast<Structure*>((*p)), strm);
393 else if ((*p)->type() == dods_sequence_c)
394 print_sequence_header(static_cast<D4Sequence*>((*p)), strm);
395 else
396 throw InternalErr(__FILE__, __LINE__, "Unknown or unsupported type.");
397
398 needs_comma = true;
399 }
400 ++p;
401 }
402}
403
404
405static void print_values_as_ascii(D4Sequence *v, bool print_name, ostream &strm, Crc32 &checksum)
406{
407 if (print_name) {
408 print_sequence_header(v, strm);
409 strm << endl;
410 }
411
412 print_val_by_rows(v, strm, checksum);
413}
414
415static void print_values_as_ascii(D4Opaque *v, bool print_name, ostream &strm, Crc32 &/*checksum*/)
416{
417 if (print_name)
418 strm << v->FQN() << ", ";
419 strm << v->value().size() << " bytes" << endl;
420}
421
422static void print_values_as_ascii(D4Group *group, bool print_name, ostream &strm, Crc32 &checksum)
423{
424 for (D4Group::groupsIter g = group->grp_begin(), e = group->grp_end(); g != e; ++g)
425 print_values_as_ascii(*g, print_name, strm, checksum);
426
427 // Specialize how the top-level variables in any Group are sent; include
428 // a checksum for them. A subset operation might make an interior set of
429 // variables, but the parent structure will still be present and the checksum
430 // will be computed for that structure. In other words, DAP4 does not try
431 // to sort out which variables are the 'real' top-level variables and instead
432 // simply computes the CRC for whatever appears as a variable in the root
433 // group.
434 for (Constructor::Vars_iter i = group->var_begin(), e = group->var_end(); i != e; ++i) {
435 // Only send the stuff in the current subset.
436 if ((*i)->send_p()) {
437 (*i)->intern_data();
438 // print the data
439 print_values_as_ascii((*i), print_name, strm, checksum);
440 strm << endl;
441 }
442 }
443}
444
454static void print_values_as_ascii(BaseType *btp, bool print_name, ostream &strm, Crc32 &checksum)
455{
456 switch (btp->type()) {
457 case dods_null_c:
458 throw InternalErr(__FILE__, __LINE__, "Unknown type");
459
460 case dods_byte_c:
461 case dods_char_c:
462
463 case dods_int8_c:
464 case dods_uint8_c:
465
466 case dods_int16_c:
467 case dods_uint16_c:
468 case dods_int32_c:
469 case dods_uint32_c:
470
471 case dods_int64_c:
472 case dods_uint64_c:
473
474 case dods_float32_c:
475 case dods_float64_c:
476 case dods_str_c:
477 case dods_url_c:
478 case dods_enum_c:
479 if (print_name) strm << btp->FQN() << ", ";
480 btp->print_val(strm, "" /*leading space*/, false /*print dap2 decl*/);
481 break;
482
483 case dods_opaque_c:
484 print_values_as_ascii(static_cast<D4Opaque*>(btp), print_name, strm, checksum);
485 break;
486
487 case dods_array_c:
488 print_values_as_ascii(static_cast<Array*>(btp), print_name, strm, checksum);
489 break;
490
491 case dods_structure_c:
492 print_values_as_ascii(static_cast<Structure*>(btp), print_name, strm, checksum);
493 break;
494
495 case dods_sequence_c:
496 print_values_as_ascii(static_cast<D4Sequence*>(btp), print_name, strm, checksum);
497 break;
498
499 case dods_group_c:
500 print_values_as_ascii(static_cast<D4Group*>(btp), print_name, strm, checksum);
501 break;
502
503 case dods_grid_c:
504 default:
505 throw InternalErr(__FILE__, __LINE__, "Unsupported type");
506 }
507}
508
516void print_values_as_ascii(DMR *dmr, ostream &strm)
517{
518 Crc32 checksum;
519
520 strm << "Dataset: " << dmr->name() << endl;
521
522 print_values_as_ascii(dmr->root(), true /*print_name*/, strm, checksum);
523}
524
525} // namespace dap_asciival