bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
FoDapJsonTransform.cc
1// -*- mode: c++; c-basic-offset:4 -*-
2//
3// FoDapJsonTransform.cc
4//
5// This file is part of BES JSON File Out Module
6//
7// Copyright (c) 2014 OPeNDAP, Inc.
8// Author: Nathan Potter <ndp@opendap.org>
9//
10// This library is free software; you can redistribute it and/or
11// modify it under the terms of the GNU Lesser General Public
12// License as published by the Free Software Foundation; either
13// version 2.1 of the License, or (at your option) any later version.
14//
15// This library is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18// Lesser General Public License for more details.
19//
20// You should have received a copy of the GNU Lesser General Public
21// License along with this library; if not, write to the Free Software
22// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23//
24// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
25// (c) COPYRIGHT URI/MIT 1995-1999
26// Please read the full copyright statement in the file COPYRIGHT_URI.
27//
28
29#include "config.h"
30
31#include <cassert>
32
33#include <sstream>
34#include <iostream>
35#include <fstream>
36#include <stddef.h>
37#include <string>
38#include <typeinfo>
39
40using std::ostringstream;
41using std::istringstream;
42
43#define MODULE "bes"
44#define prolog string("FoDapJsonTransform::").append(__func__).append("() - ")
45
46#include <libdap/DDS.h>
47#include <libdap/Structure.h>
48#include <libdap/Constructor.h>
49#include <libdap/Array.h>
50#include <libdap/Grid.h>
51#include <libdap/Sequence.h>
52#include <libdap/Float64.h>
53#include <libdap/Str.h>
54#include <libdap/Url.h>
55
56#include <BESDebug.h>
57#include <BESInternalError.h>
58
59#include "BESUtil.h"
60#include "RequestServiceTimer.h"
61
62#include <DapFunctionUtils.h>
63
64#include "FoDapJsonTransform.h"
65#include "fojson_utils.h"
66
67#define FoDapJsonTransform_debug_key "fojson"
68
69const int int_64_precision = 15; // 15 digits to the right of the decimal point. jhrg 9/14/15
70
75template<typename T>
76unsigned int FoDapJsonTransform::json_simple_type_array_worker(ostream *strm, T *values, unsigned int indx,
77 vector<unsigned int> *shape, unsigned int currentDim)
78{
79 *strm << "[";
80
81 unsigned int currentDimSize = (*shape)[currentDim];
82
83 for (unsigned int i = 0; i < currentDimSize; i++) {
84 if (currentDim < shape->size() - 1) {
85// BESDEBUG(FoDapJsonTransform_debug_key,
86// "json_simple_type_array_worker() - Recursing! indx: " << indx << " currentDim: " << currentDim << " currentDimSize: " << currentDimSize << endl);
87 indx = json_simple_type_array_worker<T>(strm, values, indx, shape, currentDim + 1);
88 if (i + 1 != currentDimSize) *strm << ", ";
89 }
90 else {
91 if (i) *strm << ", ";
92 if (typeid(T) == typeid(std::string)) {
93 // Strings need to be escaped to be included in a JSON object.
94 string val = reinterpret_cast<string*>(values)[indx++]; // ((string *) values)[indx++];
95 *strm << "\"" << fojson::escape_for_json(val) << "\"";
96 }
97 else {
98 *strm << values[indx++];
99 }
100 }
101 }
102 *strm << "]";
103
104 return indx;
105}
106
111template<typename T>
112void FoDapJsonTransform::json_simple_type_array(ostream *strm, libdap::Array *a, string indent, bool sendData)
113{
114 *strm << indent << "{" << endl;\
115 string childindent = indent + _indent_increment;
116
117 writeLeafMetadata(strm, a, childindent);
118
119 int numDim = a->dimensions(true);
120 vector<unsigned int> shape(numDim);
121 long length = fojson::computeConstrainedShape(a, &shape);
122
123 *strm << childindent << "\"shape\": [";
124
125 for (std::vector<unsigned int>::size_type i = 0; i < shape.size(); i++) {
126 if (i > 0) *strm << ",";
127 *strm << shape[i];
128 }
129 *strm << "]";
130
131 if (sendData) {
132 *strm << "," << endl;
133
134 // Data
135 *strm << childindent << "\"data\": ";
136 unsigned int indx = 0;
137 vector<T> src(length);
138 a->value(src.data());
139
140 // I added this, and a corresponding block in FoInstance... because I fixed
141 // an issue in libdap::Float64 where the precision was not properly reset
142 // in it's print_val() method. Because of that error, precision was (left at)
143 // 15 when this code was called until I fixed that method. Then this code
144 // was not printing at the required precision. jhrg 9/14/15
145 if (typeid(T) == typeid(libdap::dods_float64)) {
146 streamsize prec = strm->precision(int_64_precision);
147 try {
148 indx = json_simple_type_array_worker(strm, src.data(), 0, &shape, 0);
149 strm->precision(prec);
150 }
151 catch(...) {
152 strm->precision(prec);
153 throw;
154 }
155 }
156 else {
157 indx = json_simple_type_array_worker(strm, src.data(), 0, &shape, 0);
158 }
159
160 assert(length == indx);
161 }
162
163 *strm << endl << indent << "}";
164}
165
175void FoDapJsonTransform::json_string_array(std::ostream *strm, libdap::Array *a, string indent, bool sendData)
176{
177 *strm << indent << "{" << endl;\
178 string childindent = indent + _indent_increment;
179
180 writeLeafMetadata(strm, a, childindent);
181
182 int numDim = a->dimensions(true);
183 vector<unsigned int> shape(numDim);
184 long length = fojson::computeConstrainedShape(a, &shape);
185
186 *strm << childindent << "\"shape\": [";
187
188 for (std::vector<unsigned int>::size_type i = 0; i < shape.size(); i++) {
189 if (i > 0) *strm << ",";
190 *strm << shape[i];
191 }
192 *strm << "]";
193
194 if (sendData) {
195 *strm << "," << endl;
196
197 // Data
198 *strm << childindent << "\"data\": ";
199 unsigned int indx;
200
201 // The string type utilizes a specialized version of libdap:Array.value()
202 vector<std::string> sourceValues;
203 a->value(sourceValues);
204 indx = json_simple_type_array_worker(strm, (std::string *) (sourceValues.data()), 0, &shape, 0);
205
206 if (length != indx)
207 BESDEBUG(FoDapJsonTransform_debug_key,
208 "json_string_array() - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
209
210 }
211
212 *strm << endl << indent << "}";
213}
214
218void FoDapJsonTransform::writeDatasetMetadata(ostream *strm, libdap::DDS *dds, string indent)
219{
220
221 // Name
222 *strm << indent << "\"name\": \"" << dds->get_dataset_name() << "\"," << endl;
223
224 //Attributes
225 transform(strm, dds->get_attr_table(), indent);
226 *strm << "," << endl;
227
228}
229
234void FoDapJsonTransform::writeNodeMetadata(ostream *strm, libdap::BaseType *bt, string indent)
235{
236
237 // Name
238 *strm << indent << "\"name\": \"" << bt->name() << "\"," << endl;
239
240 //Attributes
241 transform(strm, bt->get_attr_table(), indent);
242 *strm << "," << endl;
243
244}
245
250void FoDapJsonTransform::writeLeafMetadata(ostream *strm, libdap::BaseType *bt, string indent)
251{
252
253 // Name
254 *strm << indent << "\"name\": \"" << bt->name() << "\"," << endl;
255
256 // type
257 if (bt->type() == libdap::dods_array_c) {
258 libdap::Array *a = (libdap::Array *) bt;
259 *strm << indent << "\"type\": \"" << a->var()->type_name() << "\"," << endl;
260 }
261 else {
262 *strm << indent << "\"type\": \"" << bt->type_name() << "\"," << endl;
263 }
264
265 //Attributes
266 transform(strm, bt->get_attr_table(), indent);
267 *strm << "," << endl;
268
269}
270
287FoDapJsonTransform::FoDapJsonTransform(libdap::DDS *dds) : _dds(dds), _indent_increment(" ")
288{
289 if (!_dds) throw BESInternalError("File out JSON, null DDS passed to constructor", __FILE__, __LINE__);
290}
291
302{
303 strm << BESIndent::LMarg << "FoDapJsonTransform::dump - (" << (void *) this << ")" << endl;
304 BESIndent::Indent();
305 if (_dds != 0) {
306 _dds->print(strm);
307 }
308 BESIndent::UnIndent();
309}
310
325void FoDapJsonTransform::transform(ostream &ostrm, bool sendData)
326{
327 transform(&ostrm, _dds, "", sendData);
328}
329
334void FoDapJsonTransform::transform(ostream *strm, libdap::Constructor *cnstrctr, string indent, bool sendData)
335{
336 vector<libdap::BaseType *> leaves;
337 vector<libdap::BaseType *> nodes;
338
339 // Sort the variables into two sets/
340 libdap::DDS::Vars_iter vi = cnstrctr->var_begin();
341 libdap::DDS::Vars_iter ve = cnstrctr->var_end();
342 for (; vi != ve; vi++) {
343 if ((*vi)->send_p()) {
344 libdap::BaseType *v = *vi;
345
346 libdap::Type type = v->type();
347 if (type == libdap::dods_array_c) {
348 type = v->var()->type();
349 }
350 if (v->is_constructor_type() || (v->is_vector_type() && v->var()->is_constructor_type())) {
351 nodes.push_back(v);
352 }
353 else {
354 leaves.push_back(v);
355 }
356 }
357 }
358
359 // Verify the request hasn't exceeded bes_timeout.
360 RequestServiceTimer::TheTimer()->throw_if_timeout_expired( prolog + "ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
361
362 // Now that we are ready to start streaming the response data we
363 // cancel any pending timeout alarm according to the configuration.
365
366 // Declare this node
367 *strm << indent << "{" << endl;
368 string child_indent = indent + _indent_increment;
369
370 // Write this node's metadata (name & attributes)
371 writeNodeMetadata(strm, cnstrctr, child_indent);
372
373 transform_node_worker(strm, leaves, nodes, child_indent, sendData);
374
375 *strm << indent << "}" << endl;
376
377}
378
383void FoDapJsonTransform::transform_node_worker(ostream *strm, vector<libdap::BaseType *> leaves,
384 vector<libdap::BaseType *> nodes, string indent, bool sendData)
385{
386 // Write down this nodes leaves
387 *strm << indent << "\"leaves\": [";
388 if (leaves.size() > 0) *strm << endl;
389 for (std::vector<libdap::BaseType *>::size_type l = 0; l < leaves.size(); l++) {
390 libdap::BaseType *v = leaves[l];
391 BESDEBUG(FoDapJsonTransform_debug_key, "Processing LEAF: " << v->name() << endl);
392
393 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog + "ERROR: bes-timeout expired before transmit " + v->name(), __FILE__, __LINE__);
394
395 if (l > 0) {
396 *strm << ",";
397 *strm << endl;
398 }
399 transform(strm, v, indent + _indent_increment, sendData);
400 }
401 if (leaves.size() > 0) *strm << endl << indent;
402 *strm << "]," << endl;
403
404 // Write down this nodes child nodes
405 *strm << indent << "\"nodes\": [";
406 if (nodes.size() > 0) *strm << endl;
407 for (std::vector<libdap::BaseType *>::size_type n = 0; n < nodes.size(); n++) {
408 libdap::BaseType *v = nodes[n];
409
410 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog + "ERROR: bes-timeout expired before transmit " + v->name(), __FILE__, __LINE__);
411
412 transform(strm, v, indent + _indent_increment, sendData);
413 }
414 if (nodes.size() > 0) *strm << endl << indent;
415
416 *strm << "]" << endl;
417}
418
423void FoDapJsonTransform::transform(ostream *strm, libdap::DDS *dds, string indent, bool sendData)
424{
428 vector<libdap::BaseType *> leaves;
429 vector<libdap::BaseType *> nodes;
430
431 libdap::DDS::Vars_iter vi = dds->var_begin();
432 libdap::DDS::Vars_iter ve = dds->var_end();
433 for (; vi != ve; vi++) {
434 if ((*vi)->send_p()) {
435 libdap::BaseType *v = *vi;
436 libdap::Type type = v->type();
437 if (type == libdap::dods_array_c) {
438 type = v->var()->type();
439 }
440 if (v->is_constructor_type() || (v->is_vector_type() && v->var()->is_constructor_type())) {
441 nodes.push_back(v);
442 }
443 else {
444 leaves.push_back(v);
445 }
446 }
447 }
448
449 // Declare this node
450 *strm << indent << "{" << endl;
451 string child_indent = indent + _indent_increment;
452
453 // Write this node's metadata (name & attributes)
454 writeDatasetMetadata(strm, dds, child_indent);
455
456 transform_node_worker(strm, leaves, nodes, child_indent, sendData);
457
458 *strm << indent << "}" << endl;
459}
460
465void FoDapJsonTransform::transform(ostream *strm, libdap::BaseType *bt, string indent, bool sendData)
466{
467 switch (bt->type()) {
468 // Handle the atomic types - that's easy!
469 case libdap::dods_byte_c:
470 case libdap::dods_int16_c:
471 case libdap::dods_uint16_c:
472 case libdap::dods_int32_c:
473 case libdap::dods_uint32_c:
474 case libdap::dods_float32_c:
475 case libdap::dods_float64_c:
476 case libdap::dods_str_c:
477 case libdap::dods_url_c:
478 transformAtomic(strm, bt, indent, sendData);
479 break;
480
481 case libdap::dods_structure_c:
482 transform(strm, (libdap::Structure *) bt, indent, sendData);
483 break;
484
485 case libdap::dods_grid_c:
486 transform(strm, (libdap::Grid *) bt, indent, sendData);
487 break;
488
489 case libdap::dods_sequence_c:
490 transform(strm, (libdap::Sequence *) bt, indent, sendData);
491 break;
492
493 case libdap::dods_array_c:
494 transform(strm, (libdap::Array *) bt, indent, sendData);
495 break;
496
497 case libdap::dods_int8_c:
498 case libdap::dods_uint8_c:
499 case libdap::dods_int64_c:
500 case libdap::dods_uint64_c:
501 // case libdap::dods_url4_c:
502 case libdap::dods_enum_c:
503 case libdap::dods_group_c: {
504 string s = (string) "File out JSON, " + "DAP4 types not yet supported.";
505 throw BESInternalError(s, __FILE__, __LINE__);
506 break;
507 }
508
509 default: {
510 string s = (string) "File out JSON, " + "Unrecognized type.";
511 throw BESInternalError(s, __FILE__, __LINE__);
512 break;
513 }
514
515 }
516}
517
522void FoDapJsonTransform::transformAtomic(ostream *strm, libdap::BaseType *b, string indent, bool sendData)
523{
524
525 *strm << indent << "{" << endl;
526
527 string childindent = indent + _indent_increment;
528
529 writeLeafMetadata(strm, b, childindent);
530
531 *strm << childindent << "\"shape\": [1]," << endl;
532
533 if (sendData) {
534 // Data
535 *strm << childindent << "\"data\": [";
536
537 if (b->type() == libdap::dods_str_c || b->type() == libdap::dods_url_c) {
538 libdap::Str *strVar = (libdap::Str *) b;
539 std::string tmpString = strVar->value();
540 *strm << "\"" << fojson::escape_for_json(tmpString) << "\"";
541 }
542 else {
543 b->print_val(*strm, "", false);
544 }
545
546 *strm << "]";
547 }
548
549}
550
555void FoDapJsonTransform::transform(ostream *strm, libdap::Array *a, string indent, bool sendData)
556{
557
558 BESDEBUG(FoDapJsonTransform_debug_key,
559 "FoJsonTransform::transform() - Processing Array. " << " a->type(): " << a->type() << " a->var()->type(): " << a->var()->type() << endl);
560
561 switch (a->var()->type()) {
562 // Handle the atomic types - that's easy!
563 case libdap::dods_byte_c:
564 json_simple_type_array<libdap::dods_byte>(strm, a, indent, sendData);
565 break;
566
567 case libdap::dods_int16_c:
568 json_simple_type_array<libdap::dods_int16>(strm, a, indent, sendData);
569 break;
570
571 case libdap::dods_uint16_c:
572 json_simple_type_array<libdap::dods_uint16>(strm, a, indent, sendData);
573 break;
574
575 case libdap::dods_int32_c:
576 json_simple_type_array<libdap::dods_int32>(strm, a, indent, sendData);
577 break;
578
579 case libdap::dods_uint32_c:
580 json_simple_type_array<libdap::dods_uint32>(strm, a, indent, sendData);
581 break;
582
583 case libdap::dods_float32_c:
584 json_simple_type_array<libdap::dods_float32>(strm, a, indent, sendData);
585 break;
586
587 case libdap::dods_float64_c:
588 json_simple_type_array<libdap::dods_float64>(strm, a, indent, sendData);
589 break;
590
591 case libdap::dods_str_c: {
592 json_string_array(strm, a, indent, sendData);
593 break;
594 }
595
596 case libdap::dods_url_c: {
597 json_string_array(strm, a, indent, sendData);
598 break;
599 }
600
601 case libdap::dods_structure_c: {
602 throw BESInternalError("File out JSON, Arrays of Structure objects not a supported return type.", __FILE__, __LINE__);
603 break;
604 }
605 case libdap::dods_grid_c: {
606 throw BESInternalError("File out JSON, Arrays of Grid objects not a supported return type.", __FILE__, __LINE__);
607 break;
608 }
609
610 case libdap::dods_sequence_c: {
611 throw BESInternalError("File out JSON, Arrays of Sequence objects not a supported return type.", __FILE__, __LINE__);
612 break;
613 }
614
615 case libdap::dods_array_c: {
616 throw BESInternalError("File out JSON, Arrays of Array objects not a supported return type.", __FILE__, __LINE__);
617 break;
618 }
619 case libdap::dods_int8_c:
620 case libdap::dods_uint8_c:
621 case libdap::dods_int64_c:
622 case libdap::dods_uint64_c:
623 // case libdap::dods_url4_c:
624 case libdap::dods_enum_c:
625 case libdap::dods_group_c: {
626 throw BESInternalError("File out JSON, DAP4 types not yet supported.", __FILE__, __LINE__);
627 break;
628 }
629
630 default: {
631 throw BESInternalError("File out JSON, Unrecognized type.", __FILE__, __LINE__);
632 break;
633 }
634
635 }
636
637}
638
643void FoDapJsonTransform::transform(ostream *strm, libdap::AttrTable &attr_table, string indent)
644{
645
646 string child_indent = indent + _indent_increment;
647
648 // Start the attributes block
649 *strm << indent << "\"attributes\": [";
650
651// if(attr_table.get_name().size()>0)
652// *strm << endl << child_indent << "{\"name\": \"name\", \"value\": \"" << attr_table.get_name() << "\"},";
653
654// Only do more if there are actually attributes in the table
655 if (attr_table.get_size() != 0) {
656 *strm << endl;
657 libdap::AttrTable::Attr_iter begin = attr_table.attr_begin();
658 libdap::AttrTable::Attr_iter end = attr_table.attr_end();
659
660 for (libdap::AttrTable::Attr_iter at_iter = begin; at_iter != end; at_iter++) {
661
662 switch (attr_table.get_attr_type(at_iter)) {
663 case libdap::Attr_container: {
664 libdap::AttrTable *atbl = attr_table.get_attr_table(at_iter);
665
666 // not first thing? better use a comma...
667 if (at_iter != begin) *strm << "," << endl;
668
669 // Attribute Containers need to be opened and then a recursive call gets made
670 *strm << child_indent << "{" << endl;
671
672 // If the table has a name, write it out as a json property.
673 if (atbl->get_name().size() > 0)
674 *strm << child_indent + _indent_increment << "\"name\": \"" << atbl->get_name() << "\"," << endl;
675
676 // Recursive call for child attribute table.
677 transform(strm, *atbl, child_indent + _indent_increment);
678 *strm << endl << child_indent << "}";
679
680 break;
681
682 }
683 default: {
684 // not first thing? better use a comma...
685 if (at_iter != begin) *strm << "," << endl;
686
687 // Open attribute object, write name
688 *strm << child_indent << "{\"name\": \"" << attr_table.get_name(at_iter) << "\", ";
689
690 // Open value array
691 *strm << "\"value\": [";
692 vector<std::string> *values = attr_table.get_attr_vector(at_iter);
693 // write values
694 for (std::vector<std::string>::size_type i = 0; i < values->size(); i++) {
695
696 // not first thing? better use a comma...
697 if (i > 0) *strm << ",";
698
699 // Escape the double quotes found in String and URL type attribute values.
700 if (attr_table.get_attr_type(at_iter) == libdap::Attr_string
701 || attr_table.get_attr_type(at_iter) == libdap::Attr_url) {
702 *strm << "\"";
703 // string value = (*values)[i] ;
704 *strm << fojson::escape_for_json((*values)[i]);
705 *strm << "\"";
706 }
707 else {
708
709 *strm << (*values)[i];
710 }
711
712 }
713 // close value array
714 *strm << "]}";
715 break;
716 }
717
718 }
719 }
720
721 *strm << endl << indent;
722 }
723
724 // close AttrTable JSON
725
726 *strm << "]";
727}
728
exception thrown if internal error encountered
static void conditional_timeout_cancel()
Checks if the timeout alarm should be canceled based on the value of the BES key BES....
Definition BESUtil.cc:898
virtual void dump(std::ostream &strm) const
dumps information about this transformation object for debugging purposes
FoDapJsonTransform(libdap::DDS *dds)
Get the JSON encoding for a DDS.
static RequestServiceTimer * TheTimer()
Return a pointer to a singleton timer instance. If an instance does not exist it will create and init...
void throw_if_timeout_expired(const std::string &message, const std::string &file, const int line)
Checks the RequestServiceTimer to determine if the time spent servicing the request at this point has...
STL class.
STL class.