bes  Updated for version 3.20.13
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 
40 using std::ostringstream;
41 using 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 
69 const int int_64_precision = 15; // 15 digits to the right of the decimal point. jhrg 9/14/15
70 
75 template<typename T>
76 unsigned 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 
111 template<typename T>
112 void 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 
175 void 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 
218 void 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 
234 void 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 
250 void 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 
287 FoDapJsonTransform::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 
301 void FoDapJsonTransform::dump(ostream &strm) const
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 
325 void FoDapJsonTransform::transform(ostream &ostrm, bool sendData)
326 {
327  transform(&ostrm, _dds, "", sendData);
328 }
329 
334 void 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 
383 void 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 
423 void 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 
465 void 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 
522 void 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 
555 void 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 
643 void 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().length()>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().length() > 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:895
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...
Type
Type of JSON value.
Definition: rapidjson.h:664