bes Updated for version 3.20.10
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#include <libdap/DDS.h>
44#include <libdap/Structure.h>
45#include <libdap/Constructor.h>
46#include <libdap/Array.h>
47#include <libdap/Grid.h>
48#include <libdap/Sequence.h>
49#include <libdap/Float64.h>
50#include <libdap/Str.h>
51#include <libdap/Url.h>
52
53#include <BESDebug.h>
54#include <BESInternalError.h>
55
56#include <DapFunctionUtils.h>
57
58#include "FoDapJsonTransform.h"
59#include "fojson_utils.h"
60
61#define FoDapJsonTransform_debug_key "fojson"
62
63const int int_64_precision = 15; // 15 digits to the right of the decimal point. jhrg 9/14/15
64
69template<typename T>
70unsigned int FoDapJsonTransform::json_simple_type_array_worker(ostream *strm, T *values, unsigned int indx,
71 vector<unsigned int> *shape, unsigned int currentDim)
72{
73 *strm << "[";
74
75 unsigned int currentDimSize = (*shape)[currentDim];
76
77 for (unsigned int i = 0; i < currentDimSize; i++) {
78 if (currentDim < shape->size() - 1) {
79// BESDEBUG(FoDapJsonTransform_debug_key,
80// "json_simple_type_array_worker() - Recursing! indx: " << indx << " currentDim: " << currentDim << " currentDimSize: " << currentDimSize << endl);
81 indx = json_simple_type_array_worker<T>(strm, values, indx, shape, currentDim + 1);
82 if (i + 1 != currentDimSize) *strm << ", ";
83 }
84 else {
85 if (i) *strm << ", ";
86 if (typeid(T) == typeid(std::string)) {
87 // Strings need to be escaped to be included in a JSON object.
88 string val = reinterpret_cast<string*>(values)[indx++]; // ((string *) values)[indx++];
89 *strm << "\"" << fojson::escape_for_json(val) << "\"";
90 }
91 else {
92 *strm << values[indx++];
93 }
94 }
95 }
96 *strm << "]";
97
98 return indx;
99}
100
105template<typename T>
106void FoDapJsonTransform::json_simple_type_array(ostream *strm, libdap::Array *a, string indent, bool sendData)
107{
108 *strm << indent << "{" << endl;\
109 string childindent = indent + _indent_increment;
110
111 writeLeafMetadata(strm, a, childindent);
112
113 int numDim = a->dimensions(true);
114 vector<unsigned int> shape(numDim);
115 long length = fojson::computeConstrainedShape(a, &shape);
116
117 *strm << childindent << "\"shape\": [";
118
119 for (std::vector<unsigned int>::size_type i = 0; i < shape.size(); i++) {
120 if (i > 0) *strm << ",";
121 *strm << shape[i];
122 }
123 *strm << "]";
124
125 if (sendData) {
126 *strm << "," << endl;
127
128 // Data
129 *strm << childindent << "\"data\": ";
130 unsigned int indx = 0;
131 vector<T> src(length);
132 a->value(&src[0]);
133
134 // I added this, and a corresponding block in FoInstance... because I fixed
135 // an issue in libdap::Float64 where the precision was not properly reset
136 // in it's print_val() method. Because of that error, precision was (left at)
137 // 15 when this code was called until I fixed that method. Then this code
138 // was not printing at the required precision. jhrg 9/14/15
139 if (typeid(T) == typeid(libdap::dods_float64)) {
140 streamsize prec = strm->precision(int_64_precision);
141 try {
142 indx = json_simple_type_array_worker(strm, &src[0], 0, &shape, 0);
143 strm->precision(prec);
144 }
145 catch(...) {
146 strm->precision(prec);
147 throw;
148 }
149 }
150 else {
151 indx = json_simple_type_array_worker(strm, &src[0], 0, &shape, 0);
152 }
153
154 assert(length == indx);
155 }
156
157 *strm << endl << indent << "}";
158}
159
169void FoDapJsonTransform::json_string_array(std::ostream *strm, libdap::Array *a, string indent, bool sendData)
170{
171 *strm << indent << "{" << endl;\
172 string childindent = indent + _indent_increment;
173
174 writeLeafMetadata(strm, a, childindent);
175
176 int numDim = a->dimensions(true);
177 vector<unsigned int> shape(numDim);
178 long length = fojson::computeConstrainedShape(a, &shape);
179
180 *strm << childindent << "\"shape\": [";
181
182 for (std::vector<unsigned int>::size_type i = 0; i < shape.size(); i++) {
183 if (i > 0) *strm << ",";
184 *strm << shape[i];
185 }
186 *strm << "]";
187
188 if (sendData) {
189 *strm << "," << endl;
190
191 // Data
192 *strm << childindent << "\"data\": ";
193 unsigned int indx;
194
195 // The string type utilizes a specialized version of libdap:Array.value()
196 vector<std::string> sourceValues;
197 a->value(sourceValues);
198 indx = json_simple_type_array_worker(strm, (std::string *) (&sourceValues[0]), 0, &shape, 0);
199
200 if (length != indx)
201 BESDEBUG(FoDapJsonTransform_debug_key,
202 "json_string_array() - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
203
204 }
205
206 *strm << endl << indent << "}";
207}
208
212void FoDapJsonTransform::writeDatasetMetadata(ostream *strm, libdap::DDS *dds, string indent)
213{
214
215 // Name
216 *strm << indent << "\"name\": \"" << dds->get_dataset_name() << "\"," << endl;
217
218 //Attributes
219 transform(strm, dds->get_attr_table(), indent);
220 *strm << "," << endl;
221
222}
223
228void FoDapJsonTransform::writeNodeMetadata(ostream *strm, libdap::BaseType *bt, string indent)
229{
230
231 // Name
232 *strm << indent << "\"name\": \"" << bt->name() << "\"," << endl;
233
234 //Attributes
235 transform(strm, bt->get_attr_table(), indent);
236 *strm << "," << endl;
237
238}
239
244void FoDapJsonTransform::writeLeafMetadata(ostream *strm, libdap::BaseType *bt, string indent)
245{
246
247 // Name
248 *strm << indent << "\"name\": \"" << bt->name() << "\"," << endl;
249
250 // type
251 if (bt->type() == libdap::dods_array_c) {
252 libdap::Array *a = (libdap::Array *) bt;
253 *strm << indent << "\"type\": \"" << a->var()->type_name() << "\"," << endl;
254 }
255 else {
256 *strm << indent << "\"type\": \"" << bt->type_name() << "\"," << endl;
257 }
258
259 //Attributes
260 transform(strm, bt->get_attr_table(), indent);
261 *strm << "," << endl;
262
263}
264
281FoDapJsonTransform::FoDapJsonTransform(libdap::DDS *dds) : _dds(dds), _indent_increment(" ")
282{
283 if (!_dds) throw BESInternalError("File out JSON, null DDS passed to constructor", __FILE__, __LINE__);
284}
285
295void FoDapJsonTransform::dump(ostream &strm) const
296{
297 strm << BESIndent::LMarg << "FoDapJsonTransform::dump - (" << (void *) this << ")" << endl;
298 BESIndent::Indent();
299 if (_dds != 0) {
300 _dds->print(strm);
301 }
302 BESIndent::UnIndent();
303}
304
319void FoDapJsonTransform::transform(ostream &ostrm, bool sendData)
320{
321 transform(&ostrm, _dds, "", sendData);
322}
323
328void FoDapJsonTransform::transform(ostream *strm, libdap::Constructor *cnstrctr, string indent, bool sendData)
329{
330 vector<libdap::BaseType *> leaves;
331 vector<libdap::BaseType *> nodes;
332
333 // Sort the variables into two sets/
334 libdap::DDS::Vars_iter vi = cnstrctr->var_begin();
335 libdap::DDS::Vars_iter ve = cnstrctr->var_end();
336 for (; vi != ve; vi++) {
337 if ((*vi)->send_p()) {
338 libdap::BaseType *v = *vi;
339
340 libdap::Type type = v->type();
341 if (type == libdap::dods_array_c) {
342 type = v->var()->type();
343 }
344 if (v->is_constructor_type() || (v->is_vector_type() && v->var()->is_constructor_type())) {
345 nodes.push_back(v);
346 }
347 else {
348 leaves.push_back(v);
349 }
350 }
351 }
352
353 // Declare this node
354 *strm << indent << "{" << endl;
355 string child_indent = indent + _indent_increment;
356
357 // Write this node's metadata (name & attributes)
358 writeNodeMetadata(strm, cnstrctr, child_indent);
359
360 transform_node_worker(strm, leaves, nodes, child_indent, sendData);
361
362 *strm << indent << "}" << endl;
363
364}
365
370void FoDapJsonTransform::transform_node_worker(ostream *strm, vector<libdap::BaseType *> leaves,
371 vector<libdap::BaseType *> nodes, string indent, bool sendData)
372{
373 // Write down this nodes leaves
374 *strm << indent << "\"leaves\": [";
375 if (leaves.size() > 0) *strm << endl;
376 for (std::vector<libdap::BaseType *>::size_type l = 0; l < leaves.size(); l++) {
377 libdap::BaseType *v = leaves[l];
378 BESDEBUG(FoDapJsonTransform_debug_key, "Processing LEAF: " << v->name() << endl);
379 if (l > 0) {
380 *strm << ",";
381 *strm << endl;
382 }
383 transform(strm, v, indent + _indent_increment, sendData);
384 }
385 if (leaves.size() > 0) *strm << endl << indent;
386 *strm << "]," << endl;
387
388 // Write down this nodes child nodes
389 *strm << indent << "\"nodes\": [";
390 if (nodes.size() > 0) *strm << endl;
391 for (std::vector<libdap::BaseType *>::size_type n = 0; n < nodes.size(); n++) {
392 libdap::BaseType *v = nodes[n];
393 transform(strm, v, indent + _indent_increment, sendData);
394 }
395 if (nodes.size() > 0) *strm << endl << indent;
396
397 *strm << "]" << endl;
398}
399
404void FoDapJsonTransform::transform(ostream *strm, libdap::DDS *dds, string indent, bool sendData)
405{
409 vector<libdap::BaseType *> leaves;
410 vector<libdap::BaseType *> nodes;
411
412 libdap::DDS::Vars_iter vi = dds->var_begin();
413 libdap::DDS::Vars_iter ve = dds->var_end();
414 for (; vi != ve; vi++) {
415 if ((*vi)->send_p()) {
416 libdap::BaseType *v = *vi;
417 libdap::Type type = v->type();
418 if (type == libdap::dods_array_c) {
419 type = v->var()->type();
420 }
421 if (v->is_constructor_type() || (v->is_vector_type() && v->var()->is_constructor_type())) {
422 nodes.push_back(v);
423 }
424 else {
425 leaves.push_back(v);
426 }
427 }
428 }
429
430 // Declare this node
431 *strm << indent << "{" << endl;
432 string child_indent = indent + _indent_increment;
433
434 // Write this node's metadata (name & attributes)
435 writeDatasetMetadata(strm, dds, child_indent);
436
437 transform_node_worker(strm, leaves, nodes, child_indent, sendData);
438
439 *strm << indent << "}" << endl;
440}
441
446void FoDapJsonTransform::transform(ostream *strm, libdap::BaseType *bt, string indent, bool sendData)
447{
448 switch (bt->type()) {
449 // Handle the atomic types - that's easy!
450 case libdap::dods_byte_c:
451 case libdap::dods_int16_c:
452 case libdap::dods_uint16_c:
453 case libdap::dods_int32_c:
454 case libdap::dods_uint32_c:
455 case libdap::dods_float32_c:
456 case libdap::dods_float64_c:
457 case libdap::dods_str_c:
458 case libdap::dods_url_c:
459 transformAtomic(strm, bt, indent, sendData);
460 break;
461
462 case libdap::dods_structure_c:
463 transform(strm, (libdap::Structure *) bt, indent, sendData);
464 break;
465
466 case libdap::dods_grid_c:
467 transform(strm, (libdap::Grid *) bt, indent, sendData);
468 break;
469
470 case libdap::dods_sequence_c:
471 transform(strm, (libdap::Sequence *) bt, indent, sendData);
472 break;
473
474 case libdap::dods_array_c:
475 transform(strm, (libdap::Array *) bt, indent, sendData);
476 break;
477
478 case libdap::dods_int8_c:
479 case libdap::dods_uint8_c:
480 case libdap::dods_int64_c:
481 case libdap::dods_uint64_c:
482 // case libdap::dods_url4_c:
483 case libdap::dods_enum_c:
484 case libdap::dods_group_c: {
485 string s = (string) "File out JSON, " + "DAP4 types not yet supported.";
486 throw BESInternalError(s, __FILE__, __LINE__);
487 break;
488 }
489
490 default: {
491 string s = (string) "File out JSON, " + "Unrecognized type.";
492 throw BESInternalError(s, __FILE__, __LINE__);
493 break;
494 }
495
496 }
497}
498
503void FoDapJsonTransform::transformAtomic(ostream *strm, libdap::BaseType *b, string indent, bool sendData)
504{
505
506 *strm << indent << "{" << endl;
507
508 string childindent = indent + _indent_increment;
509
510 writeLeafMetadata(strm, b, childindent);
511
512 *strm << childindent << "\"shape\": [1]," << endl;
513
514 if (sendData) {
515 // Data
516 *strm << childindent << "\"data\": [";
517
518 if (b->type() == libdap::dods_str_c || b->type() == libdap::dods_url_c) {
519 libdap::Str *strVar = (libdap::Str *) b;
520 std::string tmpString = strVar->value();
521 *strm << "\"" << fojson::escape_for_json(tmpString) << "\"";
522 }
523 else {
524 b->print_val(*strm, "", false);
525 }
526
527 *strm << "]";
528 }
529
530}
531
536void FoDapJsonTransform::transform(ostream *strm, libdap::Array *a, string indent, bool sendData)
537{
538
539 BESDEBUG(FoDapJsonTransform_debug_key,
540 "FoJsonTransform::transform() - Processing Array. " << " a->type(): " << a->type() << " a->var()->type(): " << a->var()->type() << endl);
541
542 switch (a->var()->type()) {
543 // Handle the atomic types - that's easy!
544 case libdap::dods_byte_c:
545 json_simple_type_array<libdap::dods_byte>(strm, a, indent, sendData);
546 break;
547
548 case libdap::dods_int16_c:
549 json_simple_type_array<libdap::dods_int16>(strm, a, indent, sendData);
550 break;
551
552 case libdap::dods_uint16_c:
553 json_simple_type_array<libdap::dods_uint16>(strm, a, indent, sendData);
554 break;
555
556 case libdap::dods_int32_c:
557 json_simple_type_array<libdap::dods_int32>(strm, a, indent, sendData);
558 break;
559
560 case libdap::dods_uint32_c:
561 json_simple_type_array<libdap::dods_uint32>(strm, a, indent, sendData);
562 break;
563
564 case libdap::dods_float32_c:
565 json_simple_type_array<libdap::dods_float32>(strm, a, indent, sendData);
566 break;
567
568 case libdap::dods_float64_c:
569 json_simple_type_array<libdap::dods_float64>(strm, a, indent, sendData);
570 break;
571
572 case libdap::dods_str_c: {
573 json_string_array(strm, a, indent, sendData);
574 break;
575 }
576
577 case libdap::dods_url_c: {
578 json_string_array(strm, a, indent, sendData);
579 break;
580 }
581
582 case libdap::dods_structure_c: {
583 throw BESInternalError("File out JSON, Arrays of Structure objects not a supported return type.", __FILE__, __LINE__);
584 break;
585 }
586 case libdap::dods_grid_c: {
587 throw BESInternalError("File out JSON, Arrays of Grid objects not a supported return type.", __FILE__, __LINE__);
588 break;
589 }
590
591 case libdap::dods_sequence_c: {
592 throw BESInternalError("File out JSON, Arrays of Sequence objects not a supported return type.", __FILE__, __LINE__);
593 break;
594 }
595
596 case libdap::dods_array_c: {
597 throw BESInternalError("File out JSON, Arrays of Array objects not a supported return type.", __FILE__, __LINE__);
598 break;
599 }
600 case libdap::dods_int8_c:
601 case libdap::dods_uint8_c:
602 case libdap::dods_int64_c:
603 case libdap::dods_uint64_c:
604 // case libdap::dods_url4_c:
605 case libdap::dods_enum_c:
606 case libdap::dods_group_c: {
607 throw BESInternalError("File out JSON, DAP4 types not yet supported.", __FILE__, __LINE__);
608 break;
609 }
610
611 default: {
612 throw BESInternalError("File out JSON, Unrecognized type.", __FILE__, __LINE__);
613 break;
614 }
615
616 }
617
618}
619
624void FoDapJsonTransform::transform(ostream *strm, libdap::AttrTable &attr_table, string indent)
625{
626
627 string child_indent = indent + _indent_increment;
628
629 // Start the attributes block
630 *strm << indent << "\"attributes\": [";
631
632// if(attr_table.get_name().length()>0)
633// *strm << endl << child_indent << "{\"name\": \"name\", \"value\": \"" << attr_table.get_name() << "\"},";
634
635// Only do more if there are actually attributes in the table
636 if (attr_table.get_size() != 0) {
637 *strm << endl;
638 libdap::AttrTable::Attr_iter begin = attr_table.attr_begin();
639 libdap::AttrTable::Attr_iter end = attr_table.attr_end();
640
641 for (libdap::AttrTable::Attr_iter at_iter = begin; at_iter != end; at_iter++) {
642
643 switch (attr_table.get_attr_type(at_iter)) {
644 case libdap::Attr_container: {
645 libdap::AttrTable *atbl = attr_table.get_attr_table(at_iter);
646
647 // not first thing? better use a comma...
648 if (at_iter != begin) *strm << "," << endl;
649
650 // Attribute Containers need to be opened and then a recursive call gets made
651 *strm << child_indent << "{" << endl;
652
653 // If the table has a name, write it out as a json property.
654 if (atbl->get_name().length() > 0)
655 *strm << child_indent + _indent_increment << "\"name\": \"" << atbl->get_name() << "\"," << endl;
656
657 // Recursive call for child attribute table.
658 transform(strm, *atbl, child_indent + _indent_increment);
659 *strm << endl << child_indent << "}";
660
661 break;
662
663 }
664 default: {
665 // not first thing? better use a comma...
666 if (at_iter != begin) *strm << "," << endl;
667
668 // Open attribute object, write name
669 *strm << child_indent << "{\"name\": \"" << attr_table.get_name(at_iter) << "\", ";
670
671 // Open value array
672 *strm << "\"value\": [";
673 vector<std::string> *values = attr_table.get_attr_vector(at_iter);
674 // write values
675 for (std::vector<std::string>::size_type i = 0; i < values->size(); i++) {
676
677 // not first thing? better use a comma...
678 if (i > 0) *strm << ",";
679
680 // Escape the double quotes found in String and URL type attribute values.
681 if (attr_table.get_attr_type(at_iter) == libdap::Attr_string
682 || attr_table.get_attr_type(at_iter) == libdap::Attr_url) {
683 *strm << "\"";
684 // string value = (*values)[i] ;
685 *strm << fojson::escape_for_json((*values)[i]);
686 *strm << "\"";
687 }
688 else {
689
690 *strm << (*values)[i];
691 }
692
693 }
694 // close value array
695 *strm << "]}";
696 break;
697 }
698
699 }
700 }
701
702 *strm << endl << indent;
703 }
704
705 // close AttrTable JSON
706
707 *strm << "]";
708}
709
exception thrown if internal error encountered
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.
Type
Type of JSON value.
Definition: rapidjson.h:664