bes Updated for version 3.20.10
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 <BESInternalError.h>
51
52#include "FoInstanceJsonTransform.h"
53#include "fojson_utils.h"
54
55using namespace std;
56
57#define ATTRIBUTE_SEPARATOR "."
58#define JSON_ORIGINAL_NAME "json_original_name"
59
60#define FoInstanceJsonTransform_debug_key "fojson"
61const int int_64_precision = 15; // See also in FODapJsonTransform.cc. jhrg 9/14/15
62
66template<typename T>
67unsigned int FoInstanceJsonTransform::json_simple_type_array_worker(std::ostream *strm,
68 const std::vector<T> &values, unsigned int indx, const std::vector<unsigned int> &shape, unsigned int currentDim)
69{
70 *strm << "[";
71
72 unsigned int currentDimSize = shape.at(currentDim); // at is slower than [] but safe
73
74 for (unsigned int i = 0; i < currentDimSize; i++) {
75 if (currentDim < shape.size() - 1) {
76 BESDEBUG(FoInstanceJsonTransform_debug_key,
77 "json_simple_type_array_worker() - Recursing! indx: " << indx << " currentDim: " << currentDim << " currentDimSize: " << currentDimSize << endl);
78
79 indx = json_simple_type_array_worker<T>(strm, values, indx, shape, currentDim + 1);
80 if (i + 1 != currentDimSize) *strm << ", ";
81 }
82 else {
83 if (i) *strm << ", ";
84 *strm << values[indx++];
85 }
86 }
87
88 *strm << "]";
89
90 return indx;
91}
92
101template<typename T> void FoInstanceJsonTransform::json_simple_type_array(std::ostream *strm, libdap::Array *a,
102 std::string indent, bool sendData)
103{
104 std::string name = a->name();
105 *strm << indent << "\"" << fojson::escape_for_json(name) + "\": ";
106
107 if (sendData) { // send data
108 std::vector<unsigned int> shape(a->dimensions(true));
109 long length = fojson::computeConstrainedShape(a, &shape);
110
111 vector<T> src(length);
112 a->value(&src[0]);
113
114 unsigned int indx = 0;
115
116 if (typeid(T) == typeid(libdap::dods_float64)) {
117 streamsize prec = strm->precision(int_64_precision);
118 try {
119 indx = json_simple_type_array_worker(strm, src, 0, shape, 0);
120 strm->precision(prec);
121 }
122 catch (...) {
123 strm->precision(prec);
124 throw;
125 }
126 }
127 else {
128 indx = json_simple_type_array_worker(strm, src, 0, shape, 0);
129 }
130
131 // make this an assert?
132 assert(length == indx);
133#if 0
134 if (length != indx)
135 BESDEBUG(FoInstanceJsonTransform_debug_key,
136 "json_simple_type_array() - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
137#endif
138 }
139 else { // otherwise send metadata
140 *strm << "{" << endl;
141 //Attributes
142 transform(strm, a->get_attr_table(), indent + _indent_increment);
143 *strm << endl << indent << "}";
144 }
145}
146
156void FoInstanceJsonTransform::json_string_array(std::ostream *strm, libdap::Array *a, std::string indent, bool sendData)
157{
158 std::string name = a->name();
159 *strm << indent << "\"" << fojson::escape_for_json(name) + "\": ";
160
161 if (sendData) { // send data
162 std::vector<unsigned int> shape(a->dimensions(true));
163 long length = fojson::computeConstrainedShape(a, &shape);
164
165 // The string type utilizes a specialized version of libdap::Array::value()
166 std::vector<std::string> sourceValues;
167 a->value(sourceValues);
168
169 unsigned int indx = json_simple_type_array_worker(strm, sourceValues, 0, shape, 0);
170
171 // make this an assert?
172 if (length != indx)
173 BESDEBUG(FoInstanceJsonTransform_debug_key,
174 "json_string_array() - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
175 }
176 else { // otherwise send metadata
177 *strm << "{" << endl;
178 //Attributes
179 transform(strm, a->get_attr_table(), indent + _indent_increment);
180 *strm << endl << indent << "}";
181 }
182}
183
198#if 0
200 const string &localfile) :
201 _dds(dds), _localfile(localfile), _indent_increment(" "), _ostrm(0)
202{
203 // I'd make these asserts - ctors generally shoulf not throw exceptions if it can be helped
204 // jhrg 3/11/5
205 if (!_dds) throw BESInternalError("File out JSON, null DDS passed to constructor", __FILE__, __LINE__);
206 if (_localfile.empty())
207 throw BESInternalError("File out JSON, empty local file name passed to constructor", __FILE__, __LINE__);
208}
209#endif
221FoInstanceJsonTransform::FoInstanceJsonTransform(libdap::DDS *dds): _dds(dds), _indent_increment(" ")
222{
223 if (!_dds) throw BESInternalError("File out JSON, null DDS passed to constructor", __FILE__, __LINE__);
224}
225
235void FoInstanceJsonTransform::dump(std::ostream &strm) const
236{
237 strm << BESIndent::LMarg << "FoInstanceJsonTransform::dump - (" << (void *) this << ")" << endl;
238 BESIndent::Indent();
239 if (_dds != 0) {
240 _dds->print(strm);
241 }
242 BESIndent::UnIndent();
243}
244
257void FoInstanceJsonTransform::transform(ostream &ostrm, bool sendData)
258{
259 transform(&ostrm, _dds, "", sendData);
260}
261
272void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::DDS *dds, string indent, bool sendData)
273{
274 bool sentSomething = false;
275
276 // Open returned JSON object
277 *strm << "{" << endl;
278
279 // Name object
280 std::string name = dds->get_dataset_name();
281 *strm << indent + _indent_increment << "\"name\": \"" << fojson::escape_for_json(name) << "\"," << endl;
282
283 if (!sendData) {
284 // Send metadata if we aren't sending data
285
286 //Attributes
287 transform(strm, dds->get_attr_table(), indent);
288 if (dds->get_attr_table().get_size() > 0) *strm << ",";
289 *strm << endl;
290 }
291
292 // Process the variables in the DDS
293 if (dds->num_var() > 0) {
294
295 libdap::DDS::Vars_iter vi = dds->var_begin();
296 libdap::DDS::Vars_iter ve = dds->var_end();
297 for (; vi != ve; vi++) {
298 if ((*vi)->send_p()) {
299
300 libdap::BaseType *v = *vi;
301 BESDEBUG(FoInstanceJsonTransform_debug_key, "Processing top level variable: " << v->name() << endl);
302
303 if (sentSomething) {
304 *strm << ",";
305 *strm << endl;
306 }
307 transform(strm, v, indent + _indent_increment, sendData);
308
309 sentSomething = true;
310 }
311 }
312 }
313
314 // Close the JSON object
315 *strm << endl << "}" << endl;
316}
317
327void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::BaseType *bt, string indent, bool sendData)
328{
329 switch (bt->type()) {
330 // Handle the atomic types - that's easy!
331 case libdap::dods_byte_c:
332 case libdap::dods_int16_c:
333 case libdap::dods_uint16_c:
334 case libdap::dods_int32_c:
335 case libdap::dods_uint32_c:
336 case libdap::dods_float32_c:
337 case libdap::dods_float64_c:
338 case libdap::dods_str_c:
339 case libdap::dods_url_c:
340 transformAtomic(strm, bt, indent, sendData);
341 break;
342
343 case libdap::dods_structure_c:
344 transform(strm, (libdap::Structure *) bt, indent, sendData);
345 break;
346
347 case libdap::dods_grid_c:
348 transform(strm, (libdap::Grid *) bt, indent, sendData);
349 break;
350
351 case libdap::dods_sequence_c:
352 transform(strm, (libdap::Sequence *) bt, indent, sendData);
353 break;
354
355 case libdap::dods_array_c:
356 transform(strm, (libdap::Array *) bt, indent, sendData);
357 break;
358
359 case libdap::dods_int8_c:
360 case libdap::dods_uint8_c:
361 case libdap::dods_int64_c:
362 case libdap::dods_uint64_c:
363 // Removed from DAP4? jhrg 1/7/15
364 // case libdap::dods_url4_c:
365 case libdap::dods_enum_c:
366 case libdap::dods_group_c: {
367 string s = (string) "File out JSON, " + "DAP4 types not yet supported.";
368 throw BESInternalError(s, __FILE__, __LINE__);
369 break;
370 }
371
372 default: {
373 string s = (string) "File out JSON, " + "Unrecognized type.";
374 throw BESInternalError(s, __FILE__, __LINE__);
375 break;
376 }
377
378 }
379
380}
381
392void FoInstanceJsonTransform::transformAtomic(std::ostream *strm, libdap::BaseType *b, string indent, bool sendData)
393{
394 std::string name = b->name();
395 *strm << indent << "\"" << fojson::escape_for_json(name) << "\": ";
396
397 if (sendData) {
398
399 if (b->type() == libdap::dods_str_c || b->type() == libdap::dods_url_c) {
400 libdap::Str *strVar = (libdap::Str *) b;
401 std::string tmpString = strVar->value();
402 *strm << "\"" << fojson::escape_for_json(tmpString) << "\"";
403 }
404 else {
405 b->print_val(*strm, "", false);
406 }
407
408 }
409 else {
410 transform(strm, b->get_attr_table(), indent);
411 }
412}
413
423void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::Structure *b, string indent, bool sendData)
424{
425
426 // Open object with name of the structure
427 std::string name = b->name();
428 *strm << indent << "\"" << fojson::escape_for_json(name) << "\": {" << endl;
429
430 // Process the variables.
431 if (b->width(true) > 0) {
432
433 libdap::Structure::Vars_iter vi = b->var_begin();
434 libdap::Structure::Vars_iter ve = b->var_end();
435 for (; vi != ve; vi++) {
436
437 // If the variable is projected, send (transform) it.
438 if ((*vi)->send_p()) {
439 libdap::BaseType *v = *vi;
440 BESDEBUG(FoInstanceJsonTransform_debug_key,
441 "FoInstanceJsonTransform::transform() - Processing structure variable: " << v->name() << endl);
442 transform(strm, v, indent + _indent_increment, sendData);
443 if ((vi + 1) != ve) {
444 *strm << ",";
445 }
446 *strm << endl;
447 }
448 }
449 }
450 *strm << indent << "}";
451}
452
462void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::Grid *g, string indent, bool sendData)
463{
464
465 // Open JSON property object with name of the grid
466 std::string name = g->name();
467 *strm << indent << "\"" << fojson::escape_for_json(name) << "\": {" << endl;
468
469 BESDEBUG(FoInstanceJsonTransform_debug_key,
470 "FoInstanceJsonTransform::transform() - Processing Grid data Array: " << g->get_array()->name() << endl);
471
472 // Process the data array
473 transform(strm, g->get_array(), indent + _indent_increment, sendData);
474 *strm << "," << endl;
475
476 // Process the MAP arrays
477 for (libdap::Grid::Map_iter mapi = g->map_begin(); mapi < g->map_end(); mapi++) {
478 BESDEBUG(FoInstanceJsonTransform_debug_key,
479 "FoInstanceJsonTransform::transform() - Processing Grid Map Array: " << (*mapi)->name() << endl);
480 if (mapi != g->map_begin()) {
481 *strm << "," << endl;
482 }
483 transform(strm, *mapi, indent + _indent_increment, sendData);
484 }
485 // Close the JSON property object
486 *strm << endl << indent << "}";
487
488}
489
499void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::Sequence *s, string indent, bool sendData)
500{
501
502 // Open JSON property object with name of the sequence
503 std::string name = s->name();
504 *strm << indent << "\"" << fojson::escape_for_json(name) << "\": {" << endl;
505
506 string child_indent = indent + _indent_increment;
507
508#if 0
509 the erdap way
510 *strm << indent << "\"table\": {" << endl;
511
512 string child_indent = indent + _indent_increment;
513
514 *strm << child_indent << "\"name\": \"" << s->name() << "\"," << endl;
515
516#endif
517
518 *strm << child_indent << "\"columnNames\": [";
519 for (libdap::Constructor::Vars_iter v = s->var_begin(); v < s->var_end(); v++) {
520 if (v != s->var_begin()) *strm << ",";
521 std::string name = (*v)->name();
522 *strm << "\"" << fojson::escape_for_json(name) << "\"";
523 }
524 *strm << "]," << endl;
525
526 *strm << child_indent << "\"columnTypes\": [";
527 for (libdap::Constructor::Vars_iter v = s->var_begin(); v < s->var_end(); v++) {
528 if (v != s->var_begin()) *strm << ",";
529 *strm << "\"" << (*v)->type_name() << "\"";
530 }
531 *strm << "]," << endl;
532
533 bool first = true;
534 *strm << child_indent << "\"rows\": [";
535 while (s->read()) {
536 if (!first) *strm << ", ";
537 *strm << endl << child_indent << "[";
538 for (libdap::Constructor::Vars_iter v = s->var_begin(); v < s->var_end(); v++) {
539 if (v != s->var_begin()) *strm << child_indent << ",";
540 transform(strm, (*v), child_indent + _indent_increment, sendData);
541 }
542 *strm << child_indent << "]";
543 first = false;
544 }
545 *strm << endl << child_indent << "]" << endl;
546
547 // Close the JSON property object
548 *strm << indent << "}" << endl;
549}
550
560void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::Array *a, string indent, bool sendData)
561{
562
563 BESDEBUG(FoInstanceJsonTransform_debug_key,
564 "FoInstanceJsonTransform::transform() - Processing Array. " << " a->type(): " << a->type() << " a->var()->type(): " << a->var()->type() << endl);
565
566 switch (a->var()->type()) {
567 // Handle the atomic types - that's easy!
568 case libdap::dods_byte_c:
569 json_simple_type_array<libdap::dods_byte>(strm, a, indent, sendData);
570 break;
571
572 case libdap::dods_int16_c:
573 json_simple_type_array<libdap::dods_int16>(strm, a, indent, sendData);
574 break;
575
576 case libdap::dods_uint16_c:
577 json_simple_type_array<libdap::dods_uint16>(strm, a, indent, sendData);
578 break;
579
580 case libdap::dods_int32_c:
581 json_simple_type_array<libdap::dods_int32>(strm, a, indent, sendData);
582 break;
583
584 case libdap::dods_uint32_c:
585 json_simple_type_array<libdap::dods_uint32>(strm, a, indent, sendData);
586 break;
587
588 case libdap::dods_float32_c:
589 json_simple_type_array<libdap::dods_float32>(strm, a, indent, sendData);
590 break;
591
592 case libdap::dods_float64_c:
593 json_simple_type_array<libdap::dods_float64>(strm, a, indent, sendData);
594 break;
595
596 case libdap::dods_str_c: {
597 json_string_array(strm, a, indent, sendData);
598 break;
599#if 0
600 //json_simple_type_array<Str>(strm,a,indent);
601 string s = (string) "File out JSON, " + "Arrays of Strings are not yet a supported return type.";
602 throw BESInternalError(s, __FILE__, __LINE__);
603 break;
604#endif
605
606 }
607
608 case libdap::dods_url_c: {
609 json_string_array(strm, a, indent, sendData);
610 break;
611#if 0
612 //json_simple_type_array<Url>(strm,a,indent);
613 string s = (string) "File out JSON, " + "Arrays of URLs are not yet a supported return type.";
614 throw BESInternalError(s, __FILE__, __LINE__);
615 break;
616#endif
617
618 }
619
620 case libdap::dods_structure_c: {
621 string s = (string) "File out JSON, " + "Arrays of Structure objects not a supported return type.";
622 throw BESInternalError(s, __FILE__, __LINE__);
623 break;
624 }
625 case libdap::dods_grid_c: {
626 string s = (string) "File out JSON, " + "Arrays of Grid objects not a supported return type.";
627 throw BESInternalError(s, __FILE__, __LINE__);
628 break;
629 }
630
631 case libdap::dods_sequence_c: {
632 string s = (string) "File out JSON, " + "Arrays of Sequence objects not a supported return type.";
633 throw BESInternalError(s, __FILE__, __LINE__);
634 break;
635 }
636
637 case libdap::dods_array_c: {
638 string s = (string) "File out JSON, " + "Arrays of Array objects not a supported return type.";
639 throw BESInternalError(s, __FILE__, __LINE__);
640 break;
641 }
642 case libdap::dods_int8_c:
643 case libdap::dods_uint8_c:
644 case libdap::dods_int64_c:
645 case libdap::dods_uint64_c:
646 // case libdap::dods_url4_c:
647 case libdap::dods_enum_c:
648 case libdap::dods_group_c: {
649 string s = (string) "File out JSON, " + "DAP4 types not yet supported.";
650 throw BESInternalError(s, __FILE__, __LINE__);
651 break;
652 }
653
654 default: {
655 string s = (string) "File out JSON, " + "Unrecognized type.";
656 throw BESInternalError(s, __FILE__, __LINE__);
657 break;
658 }
659
660 }
661}
662
676void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::AttrTable &attr_table, string indent)
677{
678 /*
679 * Since Attributes get promoted to JSON "properties" of their parent object (either a
680 * BaseType variable or a DDS derived JSON object) this method does not open a new JSON
681 * object, but rather continues to add content to the currently open object.
682 */
683 string child_indent = indent + _indent_increment;
684
685 // Are there any attributes?
686 if (attr_table.get_size() != 0) {
687 //*strm << endl;
688 libdap::AttrTable::Attr_iter begin = attr_table.attr_begin();
689 libdap::AttrTable::Attr_iter end = attr_table.attr_end();
690
691 // Process each Attribute
692 for (libdap::AttrTable::Attr_iter at_iter = begin; at_iter != end; at_iter++) {
693
694 switch (attr_table.get_attr_type(at_iter)) {
695
696 case libdap::Attr_container: // If it's a container attribute
697 {
698 libdap::AttrTable *atbl = attr_table.get_attr_table(at_iter);
699
700 if (at_iter != begin) *strm << "," << endl;
701
702 // Open a JSON property with the name of the Attribute Table and
703 std::string name = atbl->get_name();
704 *strm << child_indent << "\"" << fojson::escape_for_json(name) << "\": {" << endl;
705
706 // Process the Attribute Table.
707 transform(strm, *atbl, child_indent + _indent_increment);
708
709 // Close JSON property object
710 *strm << endl << child_indent << "}";
711
712 break;
713
714 }
715 default: // so it's not an Attribute Table. woot. time to print
716 // First?
717 if (at_iter != begin) *strm << "," << endl;
718
719 // Name of property
720 std::string name = attr_table.get_name(at_iter);
721 *strm << child_indent << "\"" << fojson::escape_for_json(name) << "\": ";
722
723 // Open values array
724 *strm << "[";
725 // Process value(s)
726 std::vector<std::string> *values = attr_table.get_attr_vector(at_iter);
727 for (std::vector<std::string>::size_type i = 0; i < values->size(); i++) {
728 if (i > 0) *strm << ",";
729 if (attr_table.get_attr_type(at_iter) == libdap::Attr_string
730 || attr_table.get_attr_type(at_iter) == libdap::Attr_url) {
731 *strm << "\"";
732
733 string value = (*values)[i];
734 *strm << fojson::escape_for_json(value);
735 *strm << "\"";
736 }
737 else {
738 *strm << (*values)[i];
739 }
740
741 }
742 // Close values array
743 *strm << "]";
744 break;
745 }
746 }
747 }
748}
749
Structure storing information used by the BES to handle the request.
exception thrown if internal error encountered
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...