bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
FoInstanceJsonTransform.cc
1// -*- mode: c++; c-basic-offset:4 -*-
2//
3// FoInstanceJsonTransform.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
40#include <libdap/DDS.h>
41#include <libdap/Structure.h>
42#include <libdap/Constructor.h>
43#include <libdap/Array.h>
44#include <libdap/Grid.h>
45#include <libdap/Sequence.h>
46#include <libdap/Str.h>
47#include <libdap/Url.h>
48
49#include <BESDebug.h>
50#include "BESUtil.h"
51#include <BESInternalError.h>
52#include "RequestServiceTimer.h"
53
54#include "FoInstanceJsonTransform.h"
55#include "fojson_utils.h"
56
57using namespace std;
58
59#define MODULE "bes"
60#define prolog string("FoInstanceJsonTransform::").append(__func__).append("() - ")
61
62#define ATTRIBUTE_SEPARATOR "."
63#define JSON_ORIGINAL_NAME "json_original_name"
64
65#define FoInstanceJsonTransform_debug_key "fojson"
66const int int_64_precision = 15; // See also in FODapJsonTransform.cc. jhrg 9/14/15
67
71template<typename T>
72unsigned int FoInstanceJsonTransform::json_simple_type_array_worker(std::ostream *strm,
73 const std::vector<T> &values, unsigned int indx, const std::vector<unsigned int> &shape, unsigned int currentDim)
74{
75 *strm << "[";
76
77 unsigned int currentDimSize = shape.at(currentDim); // at is slower than [] but safe
78
79 for (unsigned int i = 0; i < currentDimSize; i++) {
80 if (currentDim < shape.size() - 1) {
81 BESDEBUG(FoInstanceJsonTransform_debug_key,
82 "json_simple_type_array_worker() - Recursing! indx: " << indx << " currentDim: " << currentDim << " currentDimSize: " << currentDimSize << endl);
83
84 indx = json_simple_type_array_worker<T>(strm, values, indx, shape, currentDim + 1);
85 if (i + 1 != currentDimSize) *strm << ", ";
86 }
87 else {
88 if (i) *strm << ", ";
89 *strm << values[indx++];
90 }
91 }
92
93 *strm << "]";
94
95 return indx;
96}
97
106template<typename T> void FoInstanceJsonTransform::json_simple_type_array(std::ostream *strm, libdap::Array *a,
107 std::string indent, bool sendData)
108{
109 std::string name = a->name();
110 *strm << indent << "\"" << fojson::escape_for_json(name) + "\": ";
111
112 if (sendData) { // send data
113 std::vector<unsigned int> shape(a->dimensions(true));
114 long length = fojson::computeConstrainedShape(a, &shape);
115
116 vector<T> src(length);
117 a->value(src.data());
118
119 unsigned int indx = 0;
120
121 if (typeid(T) == typeid(libdap::dods_float64)) {
122 streamsize prec = strm->precision(int_64_precision);
123 try {
124 indx = json_simple_type_array_worker(strm, src, 0, shape, 0);
125 strm->precision(prec);
126 }
127 catch (...) {
128 strm->precision(prec);
129 throw;
130 }
131 }
132 else {
133 indx = json_simple_type_array_worker(strm, src, 0, shape, 0);
134 }
135
136 // make this an assert?
137 assert(length == indx);
138#if 0
139 if (length != indx)
140 BESDEBUG(FoInstanceJsonTransform_debug_key,
141 "json_simple_type_array() - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
142#endif
143 }
144 else { // otherwise send metadata
145 *strm << "{" << endl;
146 //Attributes
147 transform(strm, a->get_attr_table(), indent + _indent_increment);
148 *strm << endl << indent << "}";
149 }
150}
151
161void FoInstanceJsonTransform::json_string_array(std::ostream *strm, libdap::Array *a, std::string indent, bool sendData)
162{
163 std::string name = a->name();
164 *strm << indent << "\"" << fojson::escape_for_json(name) + "\": ";
165
166 if (sendData) { // send data
167 std::vector<unsigned int> shape(a->dimensions(true));
168 long length = fojson::computeConstrainedShape(a, &shape);
169
170 // The string type utilizes a specialized version of libdap::Array::value()
171 std::vector<std::string> sourceValues;
172 a->value(sourceValues);
173
174 unsigned int indx = json_simple_type_array_worker(strm, sourceValues, 0, shape, 0);
175
176 // make this an assert?
177 if (length != indx)
178 BESDEBUG(FoInstanceJsonTransform_debug_key,
179 "json_string_array() - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
180 }
181 else { // otherwise send metadata
182 *strm << "{" << endl;
183 //Attributes
184 transform(strm, a->get_attr_table(), indent + _indent_increment);
185 *strm << endl << indent << "}";
186 }
187}
188
203#if 0
205 const string &localfile) :
206 _dds(dds), _localfile(localfile), _indent_increment(" "), _ostrm(0)
207{
208 // I'd make these asserts - ctors generally shoulf not throw exceptions if it can be helped
209 // jhrg 3/11/5
210 if (!_dds) throw BESInternalError("File out JSON, null DDS passed to constructor", __FILE__, __LINE__);
211 if (_localfile.empty())
212 throw BESInternalError("File out JSON, empty local file name passed to constructor", __FILE__, __LINE__);
213}
214#endif
226FoInstanceJsonTransform::FoInstanceJsonTransform(libdap::DDS *dds): _dds(dds), _indent_increment(" ")
227{
228 if (!_dds) throw BESInternalError("File out JSON, null DDS passed to constructor", __FILE__, __LINE__);
229}
230
240void FoInstanceJsonTransform::dump(std::ostream &strm) const
241{
242 strm << BESIndent::LMarg << "FoInstanceJsonTransform::dump - (" << (void *) this << ")" << endl;
243 BESIndent::Indent();
244 if (_dds != 0) {
245 _dds->print(strm);
246 }
247 BESIndent::UnIndent();
248}
249
262void FoInstanceJsonTransform::transform(ostream &ostrm, bool sendData)
263{
264 transform(&ostrm, _dds, "", sendData);
265}
266
277void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::DDS *dds, string indent, bool sendData)
278{
279 bool sentSomething = false;
280
281 // Verify the request hasn't exceeded bes_timeout.
282 RequestServiceTimer::TheTimer()->throw_if_timeout_expired( prolog + "ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
283
284 // Now that we are ready to start streaming the response data we
285 // cancel any pending timeout alarm according to the configuration.
287
288 // Open returned JSON object
289 *strm << "{" << endl;
290
291 // Name object
292 std::string name = dds->get_dataset_name();
293 *strm << indent + _indent_increment << "\"name\": \"" << fojson::escape_for_json(name) << "\"," << endl;
294
295 if (!sendData) {
296 // Send metadata if we aren't sending data
297
298 //Attributes
299 transform(strm, dds->get_attr_table(), indent);
300 if (dds->get_attr_table().get_size() > 0) *strm << ",";
301 *strm << endl;
302 }
303
304 // Process the variables in the DDS
305 if (dds->num_var() > 0) {
306
307 libdap::DDS::Vars_iter vi = dds->var_begin();
308 libdap::DDS::Vars_iter ve = dds->var_end();
309 for (; vi != ve; vi++) {
310 if ((*vi)->send_p()) {
311
312 libdap::BaseType *v = *vi;
313 BESDEBUG(FoInstanceJsonTransform_debug_key, "Processing top level variable: " << v->name() << endl);
314
315 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog + "ERROR: bes-timeout expired before transmitting " + v->name(), __FILE__, __LINE__);
316
317 if (sentSomething) {
318 *strm << ",";
319 *strm << endl;
320 }
321 transform(strm, v, indent + _indent_increment, sendData);
322
323 sentSomething = true;
324 }
325 }
326 }
327
328 // Close the JSON object
329 *strm << endl << "}" << endl;
330}
331
341void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::BaseType *bt, string indent, bool sendData)
342{
343 switch (bt->type()) {
344 // Handle the atomic types - that's easy!
345 case libdap::dods_byte_c:
346 case libdap::dods_int16_c:
347 case libdap::dods_uint16_c:
348 case libdap::dods_int32_c:
349 case libdap::dods_uint32_c:
350 case libdap::dods_float32_c:
351 case libdap::dods_float64_c:
352 case libdap::dods_str_c:
353 case libdap::dods_url_c:
354 transformAtomic(strm, bt, indent, sendData);
355 break;
356
357 case libdap::dods_structure_c:
358 transform(strm, (libdap::Structure *) bt, indent, sendData);
359 break;
360
361 case libdap::dods_grid_c:
362 transform(strm, (libdap::Grid *) bt, indent, sendData);
363 break;
364
365 case libdap::dods_sequence_c:
366 transform(strm, (libdap::Sequence *) bt, indent, sendData);
367 break;
368
369 case libdap::dods_array_c:
370 transform(strm, (libdap::Array *) bt, indent, sendData);
371 break;
372
373 case libdap::dods_int8_c:
374 case libdap::dods_uint8_c:
375 case libdap::dods_int64_c:
376 case libdap::dods_uint64_c:
377 // Removed from DAP4? jhrg 1/7/15
378 // case libdap::dods_url4_c:
379 case libdap::dods_enum_c:
380 case libdap::dods_group_c: {
381 string s = (string) "File out JSON, " + "DAP4 types not yet supported.";
382 throw BESInternalError(s, __FILE__, __LINE__);
383 break;
384 }
385
386 default: {
387 string s = (string) "File out JSON, " + "Unrecognized type.";
388 throw BESInternalError(s, __FILE__, __LINE__);
389 break;
390 }
391
392 }
393
394}
395
406void FoInstanceJsonTransform::transformAtomic(std::ostream *strm, libdap::BaseType *b, string indent, bool sendData)
407{
408 std::string name = b->name();
409 *strm << indent << "\"" << fojson::escape_for_json(name) << "\": ";
410
411 if (sendData) {
412
413 if (b->type() == libdap::dods_str_c || b->type() == libdap::dods_url_c) {
414 libdap::Str *strVar = (libdap::Str *) b;
415 std::string tmpString = strVar->value();
416 *strm << "\"" << fojson::escape_for_json(tmpString) << "\"";
417 }
418 else {
419 b->print_val(*strm, "", false);
420 }
421
422 }
423 else {
424 transform(strm, b->get_attr_table(), indent);
425 }
426}
427
437void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::Structure *b, string indent, bool sendData)
438{
439
440 // Open object with name of the structure
441 std::string name = b->name();
442 *strm << indent << "\"" << fojson::escape_for_json(name) << "\": {" << endl;
443
444 // Process the variables.
445 if (b->width(true) > 0) {
446
447 libdap::Structure::Vars_iter vi = b->var_begin();
448 libdap::Structure::Vars_iter ve = b->var_end();
449 for (; vi != ve; vi++) {
450
451 // If the variable is projected, send (transform) it.
452 if ((*vi)->send_p()) {
453 libdap::BaseType *v = *vi;
454 BESDEBUG(FoInstanceJsonTransform_debug_key,
455 "FoInstanceJsonTransform::transform() - Processing structure variable: " << v->name() << endl);
456 transform(strm, v, indent + _indent_increment, sendData);
457 if ((vi + 1) != ve) {
458 *strm << ",";
459 }
460 *strm << endl;
461 }
462 }
463 }
464 *strm << indent << "}";
465}
466
476void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::Grid *g, string indent, bool sendData)
477{
478
479 // Open JSON property object with name of the grid
480 std::string name = g->name();
481 *strm << indent << "\"" << fojson::escape_for_json(name) << "\": {" << endl;
482
483 BESDEBUG(FoInstanceJsonTransform_debug_key,
484 "FoInstanceJsonTransform::transform() - Processing Grid data Array: " << g->get_array()->name() << endl);
485
486 // Process the data array
487 transform(strm, g->get_array(), indent + _indent_increment, sendData);
488 *strm << "," << endl;
489
490 // Process the MAP arrays
491 for (libdap::Grid::Map_iter mapi = g->map_begin(); mapi < g->map_end(); mapi++) {
492 BESDEBUG(FoInstanceJsonTransform_debug_key,
493 "FoInstanceJsonTransform::transform() - Processing Grid Map Array: " << (*mapi)->name() << endl);
494 if (mapi != g->map_begin()) {
495 *strm << "," << endl;
496 }
497 transform(strm, *mapi, indent + _indent_increment, sendData);
498 }
499 // Close the JSON property object
500 *strm << endl << indent << "}";
501
502}
503
513void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::Sequence *s, string indent, bool sendData)
514{
515
516 // Open JSON property object with name of the sequence
517 std::string name = s->name();
518 *strm << indent << "\"" << fojson::escape_for_json(name) << "\": {" << endl;
519
520 string child_indent = indent + _indent_increment;
521
522#if 0
523 the erdap way
524 *strm << indent << "\"table\": {" << endl;
525
526 string child_indent = indent + _indent_increment;
527
528 *strm << child_indent << "\"name\": \"" << s->name() << "\"," << endl;
529
530#endif
531
532 *strm << child_indent << "\"columnNames\": [";
533 for (libdap::Constructor::Vars_iter v = s->var_begin(); v < s->var_end(); v++) {
534 if (v != s->var_begin()) *strm << ",";
535 std::string name = (*v)->name();
536 *strm << "\"" << fojson::escape_for_json(name) << "\"";
537 }
538 *strm << "]," << endl;
539
540 *strm << child_indent << "\"columnTypes\": [";
541 for (libdap::Constructor::Vars_iter v = s->var_begin(); v < s->var_end(); v++) {
542 if (v != s->var_begin()) *strm << ",";
543 *strm << "\"" << (*v)->type_name() << "\"";
544 }
545 *strm << "]," << endl;
546
547 bool first = true;
548 *strm << child_indent << "\"rows\": [";
549 while (s->read()) {
550 if (!first) *strm << ", ";
551 *strm << endl << child_indent << "[";
552 for (libdap::Constructor::Vars_iter v = s->var_begin(); v < s->var_end(); v++) {
553 if (v != s->var_begin()) *strm << child_indent << ",";
554 transform(strm, (*v), child_indent + _indent_increment, sendData);
555 }
556 *strm << child_indent << "]";
557 first = false;
558 }
559 *strm << endl << child_indent << "]" << endl;
560
561 // Close the JSON property object
562 *strm << indent << "}" << endl;
563}
564
574void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::Array *a, string indent, bool sendData)
575{
576
577 BESDEBUG(FoInstanceJsonTransform_debug_key,
578 "FoInstanceJsonTransform::transform() - Processing Array. " << " a->type(): " << a->type() << " a->var()->type(): " << a->var()->type() << endl);
579
580 switch (a->var()->type()) {
581 // Handle the atomic types - that's easy!
582 case libdap::dods_byte_c:
583 json_simple_type_array<libdap::dods_byte>(strm, a, indent, sendData);
584 break;
585
586 case libdap::dods_int16_c:
587 json_simple_type_array<libdap::dods_int16>(strm, a, indent, sendData);
588 break;
589
590 case libdap::dods_uint16_c:
591 json_simple_type_array<libdap::dods_uint16>(strm, a, indent, sendData);
592 break;
593
594 case libdap::dods_int32_c:
595 json_simple_type_array<libdap::dods_int32>(strm, a, indent, sendData);
596 break;
597
598 case libdap::dods_uint32_c:
599 json_simple_type_array<libdap::dods_uint32>(strm, a, indent, sendData);
600 break;
601
602 case libdap::dods_float32_c:
603 json_simple_type_array<libdap::dods_float32>(strm, a, indent, sendData);
604 break;
605
606 case libdap::dods_float64_c:
607 json_simple_type_array<libdap::dods_float64>(strm, a, indent, sendData);
608 break;
609
610 case libdap::dods_str_c: {
611 json_string_array(strm, a, indent, sendData);
612 break;
613#if 0
614 //json_simple_type_array<Str>(strm,a,indent);
615 string s = (string) "File out JSON, " + "Arrays of Strings are not yet a supported return type.";
616 throw BESInternalError(s, __FILE__, __LINE__);
617 break;
618#endif
619
620 }
621
622 case libdap::dods_url_c: {
623 json_string_array(strm, a, indent, sendData);
624 break;
625#if 0
626 //json_simple_type_array<Url>(strm,a,indent);
627 string s = (string) "File out JSON, " + "Arrays of URLs are not yet a supported return type.";
628 throw BESInternalError(s, __FILE__, __LINE__);
629 break;
630#endif
631
632 }
633
634 case libdap::dods_structure_c: {
635 string s = (string) "File out JSON, " + "Arrays of Structure objects not a supported return type.";
636 throw BESInternalError(s, __FILE__, __LINE__);
637 break;
638 }
639 case libdap::dods_grid_c: {
640 string s = (string) "File out JSON, " + "Arrays of Grid objects not a supported return type.";
641 throw BESInternalError(s, __FILE__, __LINE__);
642 break;
643 }
644
645 case libdap::dods_sequence_c: {
646 string s = (string) "File out JSON, " + "Arrays of Sequence objects not a supported return type.";
647 throw BESInternalError(s, __FILE__, __LINE__);
648 break;
649 }
650
651 case libdap::dods_array_c: {
652 string s = (string) "File out JSON, " + "Arrays of Array objects not a supported return type.";
653 throw BESInternalError(s, __FILE__, __LINE__);
654 break;
655 }
656 case libdap::dods_int8_c:
657 case libdap::dods_uint8_c:
658 case libdap::dods_int64_c:
659 case libdap::dods_uint64_c:
660 // case libdap::dods_url4_c:
661 case libdap::dods_enum_c:
662 case libdap::dods_group_c: {
663 string s = (string) "File out JSON, " + "DAP4 types not yet supported.";
664 throw BESInternalError(s, __FILE__, __LINE__);
665 break;
666 }
667
668 default: {
669 string s = (string) "File out JSON, " + "Unrecognized type.";
670 throw BESInternalError(s, __FILE__, __LINE__);
671 break;
672 }
673
674 }
675}
676
690void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::AttrTable &attr_table, string indent)
691{
692 /*
693 * Since Attributes get promoted to JSON "properties" of their parent object (either a
694 * BaseType variable or a DDS derived JSON object) this method does not open a new JSON
695 * object, but rather continues to add content to the currently open object.
696 */
697 string child_indent = indent + _indent_increment;
698
699 // Are there any attributes?
700 if (attr_table.get_size() != 0) {
701 //*strm << endl;
702 libdap::AttrTable::Attr_iter begin = attr_table.attr_begin();
703 libdap::AttrTable::Attr_iter end = attr_table.attr_end();
704
705 // Process each Attribute
706 for (libdap::AttrTable::Attr_iter at_iter = begin; at_iter != end; at_iter++) {
707
708 switch (attr_table.get_attr_type(at_iter)) {
709
710 case libdap::Attr_container: // If it's a container attribute
711 {
712 libdap::AttrTable *atbl = attr_table.get_attr_table(at_iter);
713
714 if (at_iter != begin) *strm << "," << endl;
715
716 // Open a JSON property with the name of the Attribute Table and
717 std::string name = atbl->get_name();
718 *strm << child_indent << "\"" << fojson::escape_for_json(name) << "\": {" << endl;
719
720 // Process the Attribute Table.
721 transform(strm, *atbl, child_indent + _indent_increment);
722
723 // Close JSON property object
724 *strm << endl << child_indent << "}";
725
726 break;
727
728 }
729 default: // so it's not an Attribute Table. woot. time to print
730 // First?
731 if (at_iter != begin) *strm << "," << endl;
732
733 // Name of property
734 std::string name = attr_table.get_name(at_iter);
735 *strm << child_indent << "\"" << fojson::escape_for_json(name) << "\": ";
736
737 // Open values array
738 *strm << "[";
739 // Process value(s)
740 std::vector<std::string> *values = attr_table.get_attr_vector(at_iter);
741 for (std::vector<std::string>::size_type i = 0; i < values->size(); i++) {
742 if (i > 0) *strm << ",";
743 if (attr_table.get_attr_type(at_iter) == libdap::Attr_string
744 || attr_table.get_attr_type(at_iter) == libdap::Attr_url) {
745 *strm << "\"";
746
747 string value = (*values)[i];
748 *strm << fojson::escape_for_json(value);
749 *strm << "\"";
750 }
751 else {
752 *strm << (*values)[i];
753 }
754
755 }
756 // Close values array
757 *strm << "]";
758 break;
759 }
760 }
761 }
762}
763
Structure storing information used by the BES to handle the request.
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
FoInstanceJsonTransform(libdap::DDS *dds)
Constructor that creates transformation object from the specified DataDDS object to the specified fil...
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...