bes  Updated for version 3.20.13
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 
57 using 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"
66 const int int_64_precision = 15; // See also in FODapJsonTransform.cc. jhrg 9/14/15
67 
71 template<typename T>
72 unsigned 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 
106 template<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 
161 void 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
226 FoInstanceJsonTransform::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 
240 void 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 
262 void FoInstanceJsonTransform::transform(ostream &ostrm, bool sendData)
263 {
264  transform(&ostrm, _dds, "", sendData);
265 }
266 
277 void 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 
341 void 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 
406 void 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 
437 void 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 
476 void 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 
513 void 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 
574 void 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 
690 void 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:895
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...