bes Updated for version 3.20.13
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 if (a->dimension_size(a->dim_begin(), true) > 0) {
75 int end = a->dimension_size(a->dim_begin(), true) - 1;
76
77 for (int i = 0; i < end; ++i) {
78 a->var(i)->print_val(strm, "", false /*print_decl*/);
79 strm << ", ";
80 }
81 a->var(end)->print_val(strm, "", false /*print_decl*/);
82 }
83}
84
96static int print_array_row(Array *a, ostream &strm, int index, int number)
97{
98 // Added to support zero-length arrays. jhrg 2/2/16
99 if (number > 0) {
100 for (int i = 0; i < number; ++i) {
101 a->var(index++)->print_val(strm, "", false /*print_decl*/);
102 strm << ", ";
103 }
104
105 a->var(index++)->print_val(strm, "", false /*print_decl*/);
106 }
107 return index;
108}
109
110// This code implements simple modulo arithmetic. The vector shape contains
111// This code implements simple modulo arithmetic. The vector shape contains
112// the maximum count value for each dimension, state contains the current
113// state. For example, if shape holds 10, 20 then when state holds 0, 20
114// calling this method will increment state to 1, 0. For this example,
115// calling the method with state equal to 10, 20 will reset state to 0, 0 and
116// the return value will be false.
117static bool increment_state(vector<int> *state, const vector<int> &shape)
118{
119 vector < int >::reverse_iterator state_riter;
120 vector < int >::const_reverse_iterator shape_riter;
121 for (state_riter = state->rbegin(), shape_riter = shape.rbegin();
122 state_riter < state->rend(); state_riter++, shape_riter++) {
123 if (*state_riter == *shape_riter - 1) {
124 *state_riter = 0;
125 }
126 else {
127 *state_riter = *state_riter + 1;
128 return true;
129 }
130 }
131
132 return false;
133}
134
135static vector <int> get_shape_vector(Array *a, size_t n)
136{
137 if (n < 1 || n > a->dimensions(true)) {
138 ostringstream oss;
139 oss << "Attempt to get " << n << " dimensions from " << a->name() << " which has " << a->dimensions(true) << " dimensions";
140 throw InternalErr(__FILE__, __LINE__, oss.str());
141 }
142
143 vector <int>shape;
144 Array::Dim_iter p = a->dim_begin();
145 for (unsigned i = 0; i < n && p != a->dim_end(); ++i, ++p) {
146 shape.push_back(a->dimension_size(p, true));
147 }
148
149 return shape;
150}
151
155static int get_nth_dim_size(Array *a, size_t n)
156{
157 if (n > a->dimensions(true) - 1) {
158 ostringstream oss;
159 oss << "Attempt to get dimension " << n << " from " << a->name() << " which has " << a->dimensions(true) << " dimensions";
160 throw InternalErr(__FILE__, __LINE__, oss.str());
161 }
162
163 return a->dimension_size(a->dim_begin() + n, true);
164}
165
166static void print_ndim_array(Array *a, ostream &strm, bool /*print_name */ )
167{
168
169 int dims = a->dimensions(true);
170 if (dims <= 1)
171 throw InternalErr(__FILE__, __LINE__, "Dimension count is <= 1 while printing multidimensional array.");
172
173 // shape holds the maximum index value of all but the last dimension of
174 // the array (not the size; each value is one less than the size).
175 vector<int> shape = get_shape_vector(a, dims - 1);
176 int rightmost_dim_size = get_nth_dim_size(a, dims - 1);
177
178 // state holds the indexes of the current row being printed. For an N-dim
179 // array, there are N-1 dims that are iterated over when printing (the
180 // Nth dim is not printed explicitly. Instead it's the number of values
181 // on the row.
182 vector<int> state(dims - 1, 0);
183
184 bool more_indices;
185 int index = 0;
186 do {
187 // Print indices for all dimensions except the last one.
188 strm << a->FQN();
189
190 for (int i = 0; i < dims - 1; ++i) {
191 strm << "[" << state[i] << "]" ;
192 }
193 strm << ", " ;
194
195 index = print_array_row(a, strm, index, rightmost_dim_size - 1);
196 more_indices = increment_state(&state, shape);
197 if (more_indices)
198 strm << endl ;
199
200 } while (more_indices);
201}
202
203static int get_index(Array *a, vector<int> indices)
204{
205 if (indices.size() != a->dimensions(true))
206 throw InternalErr(__FILE__, __LINE__, "Index vector is the wrong size!");
207
208 // suppose shape is [3][4][5][6] for x,y,z,t. The index is
209 // t + z(6) + y(5 * 6) + x(4 * 5 *6).
210 // Assume that indices[0] holds x, indices[1] holds y, ...
211
212 vector < int >shape = get_shape_vector(a, indices.size());
213
214 // We want to work from the rightmost index to the left
215 reverse(indices.begin(), indices.end());
216 reverse(shape.begin(), shape.end());
217
218 vector<int>::iterator indices_iter = indices.begin();
219 vector<int>::iterator shape_iter = shape.begin();
220
221 int index = *indices_iter++; // in the ex. above, this adds `t'
222 int multiplier = 1;
223 while (indices_iter != indices.end()) {
224 multiplier *= *shape_iter++;
225 index += multiplier * *indices_iter++;
226 }
227
228 return index;
229}
230
240static void print_complex_array(Array *a, ostream &strm, bool print_name, Crc32 &checksum)
241{
242 int dims = a->dimensions(true);
243 if (dims < 1)
244 throw InternalErr(__FILE__, __LINE__, "Dimension count is <= 1 while printing multidimensional array.");
245
246 // shape holds the maximum index value of all but the last dimension of
247 // the array (not the size; each value is one less that the size).
248 vector<int> shape = get_shape_vector(a, dims);
249
250 vector<int> state(dims, 0);
251
252 bool more_indices;
253 do {
254 // Print indices for all dimensions except the last one.
255 strm << a->FQN();
256
257 for (int i = 0; i < dims; ++i) {
258 strm << "[" << state[i] << "]" ;
259 }
260 strm << endl;
261
262 print_values_as_ascii(a->var(get_index(a, state)), print_name, strm, checksum);
263
264 more_indices = increment_state(&state, shape);
265
266 if (more_indices)
267 strm << endl;
268
269 } while (more_indices);
270}
271
281static void print_values_as_ascii(Array *a, bool print_name, ostream &strm, Crc32 &checksum)
282{
283 if (a->var()->is_simple_type()) {
284 if (a->dimensions(true) > 1) {
285 print_ndim_array(a, strm, print_name);
286 }
287 else {
288 print_array_vector(a, strm, print_name);
289 }
290 }
291 else {
292 print_complex_array(a, strm, print_name, checksum);
293 }
294}
295
296static void print_structure_header(Structure *s, ostream &strm)
297{
298 Constructor::Vars_iter p = s->var_begin(), e = s->var_end();
299 bool needs_comma = false;
300 while (p != e) {
301 if((*p)->send_p()){
302 if ((*p)->is_simple_type())
303 strm << (needs_comma?", ":"") << (*p)->FQN();
304 else if ((*p)->type() == dods_structure_c)
305 print_structure_header(static_cast<Structure*>(*p), strm);
306 else if ((*p)->type() == dods_sequence_c)
307 print_sequence_header(static_cast<D4Sequence*>(*p), strm);
308 else
309 throw InternalErr(__FILE__, __LINE__, "Unknown or unsupported type.");
310 needs_comma = true;
311 }
312 ++p;
313 }
314}
315
316static void print_structure_ascii(Structure *s, ostream &strm, bool print_name, Crc32 &checksum)
317{
318 if (s->is_linear()) {
319 if (print_name) {
320 print_structure_header(s, strm);
321 strm << endl;
322 }
323
324 Constructor::Vars_iter p = s->var_begin(), e = s->var_end();
325 while (p !=e) {
326 // bug: print_name should be false, but will be true because it's not a param here
327 if ((*p)->send_p()) print_values_as_ascii(*p, false /*print_name*/, strm, checksum);
328
329 if (++p != e) strm << ", ";
330 }
331 }
332 else {
333 for (Constructor::Vars_iter p = s->var_begin(), e = s->var_end(); p != e; ++p) {
334 if ((*p)->send_p()) {
335 print_values_as_ascii(*p, print_name, strm, checksum);
336 // This line outputs an extra endl when print_ascii is called for
337 // nested structures because an endl is written for each member
338 // and then once for the structure itself. 9/14/2001 jhrg
339 strm << endl;
340 }
341 }
342 }
343}
344
345static void print_values_as_ascii(Structure *v, bool print_name, ostream &strm, Crc32 &checksum)
346{
347 print_structure_ascii(v, strm, print_name, checksum);
348}
349
350static void print_one_row(D4Sequence *seq, ostream &strm, Crc32 &checksum, int row)
351{
352 int elements = seq->element_count();
353 int j = 0;
354 BaseType *btp = 0;
355 bool first_val = true;
356
357 while (j < elements) {
358 btp = seq->var_value(row, j++);
359 if (btp) { // data
360 if (!first_val)
361 strm << ", ";
362 first_val = false;
363 if (btp->type() == dods_sequence_c)
364 print_val_by_rows(static_cast<D4Sequence*>(btp), strm, checksum);
365 else
366 print_values_as_ascii(btp, false, strm, checksum);
367 }
368 }
369}
370
371static void print_val_by_rows(D4Sequence *seq, ostream &strm, Crc32 &checksum)
372{
373 if (seq->length() != 0) {
374 int rows = seq->length() /*- 1*/; // -1 because the last row is treated specially
375 for (int i = 0; i < rows; ++i) {
376 print_one_row(seq, strm, checksum, i);
377 strm << endl;
378 }
379 }
380}
381
382static void print_sequence_header(D4Sequence *s, ostream &strm)
383{
384 Constructor::Vars_iter p = s->var_begin(), e = s->var_end();
385 bool needs_comma = false;
386 while (p != e) {
387 if((*p)->send_p()){
388 if((*p)->is_simple_type())
389 strm << (needs_comma?", ":"") << (*p)->FQN();
390 else if ((*p)->type() == dods_structure_c)
391 print_structure_header(static_cast<Structure*>((*p)), strm);
392 else if ((*p)->type() == dods_sequence_c)
393 print_sequence_header(static_cast<D4Sequence*>((*p)), strm);
394 else
395 throw InternalErr(__FILE__, __LINE__, "Unknown or unsupported type.");
396
397 needs_comma = true;
398 }
399 ++p;
400 }
401}
402
403
404static void print_values_as_ascii(D4Sequence *v, bool print_name, ostream &strm, Crc32 &checksum)
405{
406 if (print_name) {
407 print_sequence_header(v, strm);
408 strm << endl;
409 }
410
411 print_val_by_rows(v, strm, checksum);
412}
413
414static void print_values_as_ascii(D4Opaque *v, bool print_name, ostream &strm, Crc32 &/*checksum*/)
415{
416 if (print_name)
417 strm << v->FQN() << ", ";
418 strm << v->value().size() << " bytes" << endl;
419}
420
421static void print_values_as_ascii(D4Group *group, bool print_name, ostream &strm, Crc32 &checksum)
422{
423 for (D4Group::groupsIter g = group->grp_begin(), e = group->grp_end(); g != e; ++g)
424 print_values_as_ascii(*g, print_name, strm, checksum);
425
426 // Specialize how the top-level variables in any Group are sent; include
427 // a checksum for them. A subset operation might make an interior set of
428 // variables, but the parent structure will still be present and the checksum
429 // will be computed for that structure. In other words, DAP4 does not try
430 // to sort out which variables are the 'real' top-level variables and instead
431 // simply computes the CRC for whatever appears as a variable in the root
432 // group.
433 for (Constructor::Vars_iter i = group->var_begin(), e = group->var_end(); i != e; ++i) {
434 // Only send the stuff in the current subset.
435 if ((*i)->send_p()) {
436 (*i)->intern_data();
437
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