bes  Updated for version 3.20.6
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 #include <DDS.h>
44 #include <Structure.h>
45 #include <Constructor.h>
46 #include <Array.h>
47 #include <Grid.h>
48 #include <Sequence.h>
49 #include <Float64.h>
50 #include <Str.h>
51 #include <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 
63 const int int_64_precision = 15; // 15 digits to the right of the decimal point. jhrg 9/14/15
64 
69 template<typename T>
70 unsigned 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 
105 template<typename T>
106 void 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 
169 void 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 
212 void 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 
228 void 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 
244 void 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 
281 FoDapJsonTransform::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 
295 void 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 
319 void FoDapJsonTransform::transform(ostream &ostrm, bool sendData)
320 {
321  transform(&ostrm, _dds, "", sendData);
322 }
323 
328 void 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 
370 void 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 
404 void 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 
446 void 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 
503 void 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 
536 void 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 
624 void 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 
FoDapJsonTransform::dump
virtual void dump(std::ostream &strm) const
dumps information about this transformation object for debugging purposes
Definition: FoDapJsonTransform.cc:295
FoDapJsonTransform::FoDapJsonTransform
FoDapJsonTransform(libdap::DDS *dds)
Get the JSON encoding for a DDS.
Definition: FoDapJsonTransform.cc:281
Type
Type
Type of JSON value.
Definition: cmr_module/rapidjson/rapidjson.h:603
BESInternalError
exception thrown if internal error encountered
Definition: BESInternalError.h:43