bes  Updated for version 3.20.6
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 <DDS.h>
41 #include <Structure.h>
42 #include <Constructor.h>
43 #include <Array.h>
44 #include <Grid.h>
45 #include <Sequence.h>
46 #include <Str.h>
47 #include <Url.h>
48 
49 #include <BESDebug.h>
50 #include <BESInternalError.h>
51 
52 #include "FoInstanceJsonTransform.h"
53 #include "fojson_utils.h"
54 
55 using namespace std;
56 
57 #define ATTRIBUTE_SEPARATOR "."
58 #define JSON_ORIGINAL_NAME "json_original_name"
59 
60 #define FoInstanceJsonTransform_debug_key "fojson"
61 const int int_64_precision = 15; // See also in FODapJsonTransform.cc. jhrg 9/14/15
62 
66 template<typename T>
67 unsigned 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 
101 template<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 
156 void 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
210 
221 FoInstanceJsonTransform::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 
235 void 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 
257 void FoInstanceJsonTransform::transform(ostream &ostrm, bool sendData)
258 {
259  transform(&ostrm, _dds, "", sendData);
260 }
261 
272 void 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 
327 void 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 
392 void 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 
423 void 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 
462 void 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 
499 void 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 
560 void 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 
676 void 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 
FoInstanceJsonTransform::dump
virtual void dump(std::ostream &strm) const
dumps information about this transformation object for debugging purposes
Definition: FoInstanceJsonTransform.cc:235
FoInstanceJsonTransform::FoInstanceJsonTransform
FoInstanceJsonTransform(libdap::DDS *dds)
Constructor that creates transformation object from the specified DataDDS object to the specified fil...
Definition: FoInstanceJsonTransform.cc:221
BESInternalError
exception thrown if internal error encountered
Definition: BESInternalError.h:43
BESDataHandlerInterface
Structure storing information used by the BES to handle the request.
Definition: BESDataHandlerInterface.h:56