OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
FoInstanceJsonTransform.cc
Go to the documentation of this file.
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 <sstream>
32 #include <iostream>
33 #include <fstream>
34 #include <stddef.h>
35 #include <string>
36 #include <typeinfo>
37 
38 #include <DDS.h>
39 #include <Structure.h>
40 #include <Constructor.h>
41 #include <Array.h>
42 #include <Grid.h>
43 #include <Sequence.h>
44 #include <Str.h>
45 #include <Url.h>
46 
47 #include <BESDebug.h>
48 #include <BESInternalError.h>
49 
51 #include "fojson_utils.h"
52 
53 using namespace std;
54 
55 #define ATTRIBUTE_SEPARATOR "."
56 #define JSON_ORIGINAL_NAME "json_original_name"
57 
58 #define FoInstanceJsonTransform_debug_key "fojson"
59 
63 template<typename T> unsigned int FoInstanceJsonTransform::json_simple_type_array_worker(std::ostream *strm, const std::vector<T> &values,
64  unsigned int indx, const std::vector<unsigned int> &shape, unsigned int currentDim)
65 {
66  *strm << "[";
67 
68  unsigned int currentDimSize = shape.at(currentDim); // at is slower than [] but safe
69 
70  for (unsigned int i = 0; i < currentDimSize; i++) {
71  if (currentDim < shape.size() - 1) {
73  "json_simple_type_array_worker() - Recursing! indx: " << indx << " currentDim: " << currentDim
74  << " currentDimSize: " << currentDimSize << endl);
75 
76  indx = json_simple_type_array_worker<T>(strm, values, indx, shape, currentDim + 1);
77  if (i + 1 != currentDimSize) *strm << ", ";
78  }
79  else {
80  if (i) *strm << ", ";
81  *strm << values[indx++];
82  }
83  }
84 
85  *strm << "]";
86 
87  return indx;
88 }
89 
98 template<typename T> void FoInstanceJsonTransform::json_simple_type_array(std::ostream *strm, libdap::Array *a, std::string indent,
99  bool sendData)
100 {
101  std::string name = a->name();
102  *strm << indent << "\"" << fojson::escape_for_json(name) + "\": ";
103 
104 
105  if (sendData) { // send data
106  std::vector<unsigned int> shape(a->dimensions(true));
107  long length = fojson::computeConstrainedShape(a, &shape);
108 
109  vector<T> src(length);
110  a->value(&src[0]);
111  unsigned int indx = json_simple_type_array_worker(strm, src, 0, shape, 0);
112 
113  // make this an assert?
114  if (length != indx)
116  "json_simple_type_array() - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
117  }
118  else { // otherwise send metadata
119  *strm << "{" << endl;
120  //Attributes
121  transform(strm, a->get_attr_table(), indent + _indent_increment);
122  *strm << endl << indent << "}";
123  }
124 }
125 
135 void FoInstanceJsonTransform::json_string_array(std::ostream *strm, libdap::Array *a, std::string indent, bool sendData)
136 {
137  std::string name = a->name();
138  *strm << indent << "\"" << fojson::escape_for_json(name) + "\": ";
139 
140  if (sendData) { // send data
141  std::vector<unsigned int> shape(a->dimensions(true));
142  long length = fojson::computeConstrainedShape(a, &shape);
143 
144  // The string type utilizes a specialized version of libdap::Array::value()
145  std::vector<std::string> sourceValues;
146  a->value(sourceValues);
147 
148  unsigned int indx = json_simple_type_array_worker(strm, sourceValues, 0, shape, 0);
149 
150  // make this an assert?
151  if (length != indx)
152  BESDEBUG(FoInstanceJsonTransform_debug_key, "json_string_array() - indx NOT equal to content length! indx: "
153  << indx << " length: " << length << endl);
154  }
155  else { // otherwise send metadata
156  *strm << "{" << endl;
157  //Attributes
158  transform(strm, a->get_attr_table(), indent + _indent_increment);
159  *strm << endl << indent << "}";
160  }
161 }
162 
177 FoInstanceJsonTransform::FoInstanceJsonTransform(libdap::DDS *dds, BESDataHandlerInterface &/*dhi*/, const string &localfile) :
178  _dds(dds), _localfile(localfile), _indent_increment(" "), _ostrm(0)
179 {
180  // I'd make these asserts - ctors generally shoulf not throw exceptions if it can be helped
181  // jhrg 3/11/5
182  if (!_dds) throw BESInternalError("File out JSON, null DDS passed to constructor", __FILE__, __LINE__);
183  if (_localfile.empty())
184  throw BESInternalError("File out JSON, empty local file name passed to constructor", __FILE__, __LINE__);
185 }
186 
198 FoInstanceJsonTransform::FoInstanceJsonTransform(libdap::DDS *dds, BESDataHandlerInterface &/*dhi*/, std::ostream *ostrm) :
199  _dds(dds), _localfile(""), _indent_increment(" "), _ostrm(ostrm)
200 {
201  // assert
202  if (!_dds) throw BESInternalError("File out JSON, null DDS passed to constructor", __FILE__, __LINE__);
203 }
204 
210 {
211 }
212 
222 void FoInstanceJsonTransform::dump(std::ostream &strm) const
223 {
224  strm << BESIndent::LMarg << "FoInstanceJsonTransform::dump - (" << (void *) this << ")" << endl;
226  strm << BESIndent::LMarg << "temporary file = " << _localfile << endl;
227  if (_dds != 0) {
228  _dds->print(strm);
229  }
231 }
232 
245 void FoInstanceJsonTransform::transform(bool sendData)
246 {
247  // used to ensure the _ostrm is closed only when it's a temp file
248  bool used_temp_file = false;
249  fstream temp_file;
250 
251  // We could factor out the ostream* parameters from all of the methods
252  // if we wanted... jhrg 7/31/14
253  if (!_ostrm) {
254  temp_file.open(_localfile.c_str(), std::fstream::out);
255  if (!temp_file) throw BESInternalError("Could not open temp file: " + _localfile, __FILE__, __LINE__);
256  _ostrm = &temp_file;
257  used_temp_file = true;
258  }
259 
260  try {
261  transform(_ostrm, _dds, "", sendData);
262  if (used_temp_file) temp_file.close();
263  }
264  catch (...) {
265  if (used_temp_file) temp_file.close();
266  throw;
267  }
268 }
269 
280 void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::DDS *dds, string indent, bool sendData)
281 {
282 
283  bool sentSomething = false;
284 
285  // Open returned JSON object
286  *strm << "{" << endl;
287 
288  // Name object
289  std::string name = dds->get_dataset_name();
290  *strm << indent + _indent_increment << "\"name\": \"" << fojson::escape_for_json(name) << "\"," << endl;
291 
292  if (!sendData) {
293  // Send metadata if we aren't sending data
294 
295  //Attributes
296  transform(strm, dds->get_attr_table(), indent);
297  if (dds->get_attr_table().get_size() > 0) *strm << ",";
298  *strm << endl;
299  }
300 
301  // Process the variables in the DDS
302  if (dds->num_var() > 0) {
303 
304  libdap::DDS::Vars_iter vi = dds->var_begin();
305  libdap::DDS::Vars_iter ve = dds->var_end();
306  for (; vi != ve; vi++) {
307  if ((*vi)->send_p()) {
308 
309  libdap::BaseType *v = *vi;
310  BESDEBUG(FoInstanceJsonTransform_debug_key, "Processing top level variable: " << v->name() << endl);
311 
312  if (sentSomething) {
313  *strm << ",";
314  *strm << endl;
315  }
316  transform(strm, v, indent + _indent_increment, sendData);
317 
318  sentSomething = true;
319  }
320 
321  }
322  }
323 
324  // Close the JSON object
325  *strm << endl << "}" << endl;
326 
327 }
328 
338 void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::BaseType *bt, string indent, bool sendData)
339 {
340  switch (bt->type()) {
341  // Handle the atomic types - that's easy!
342  case libdap::dods_byte_c:
343  case libdap::dods_int16_c:
344  case libdap::dods_uint16_c:
345  case libdap::dods_int32_c:
346  case libdap::dods_uint32_c:
347  case libdap::dods_float32_c:
348  case libdap::dods_float64_c:
349  case libdap::dods_str_c:
350  case libdap::dods_url_c:
351  transformAtomic(strm, bt, indent, sendData);
352  break;
353 
354  case libdap::dods_structure_c:
355  transform(strm, (libdap::Structure *) bt, indent, sendData);
356  break;
357 
358  case libdap::dods_grid_c:
359  transform(strm, (libdap::Grid *) bt, indent, sendData);
360  break;
361 
362  case libdap::dods_sequence_c:
363  transform(strm, (libdap::Sequence *) bt, indent, sendData);
364  break;
365 
366  case libdap::dods_array_c:
367  transform(strm, (libdap::Array *) bt, indent, sendData);
368  break;
369 
370  case libdap::dods_int8_c:
371  case libdap::dods_uint8_c:
372  case libdap::dods_int64_c:
373  case libdap::dods_uint64_c:
374  // Removed from DAP4? jhrg 1/7/15
375  // case libdap::dods_url4_c:
376  case libdap::dods_enum_c:
377  case libdap::dods_group_c: {
378  string s = (string) "File out JSON, " + "DAP4 types not yet supported.";
379  throw BESInternalError(s, __FILE__, __LINE__);
380  break;
381  }
382 
383  default: {
384  string s = (string) "File out JSON, " + "Unrecognized type.";
385  throw BESInternalError(s, __FILE__, __LINE__);
386  break;
387  }
388 
389  }
390 
391 }
392 
403 void FoInstanceJsonTransform::transformAtomic(std::ostream *strm, libdap::BaseType *b, string indent, bool sendData)
404 {
405  std::string name = b->name();
406  *strm << indent << "\"" << fojson::escape_for_json(name) << "\": ";
407 
408  if (sendData) {
409 
410  if (b->type() == libdap::dods_str_c || b->type() == libdap::dods_url_c) {
411  libdap::Str *strVar = (libdap::Str *) b;
412  std::string tmpString = strVar->value();
413  *strm << "\"" << fojson::escape_for_json(tmpString) << "\"";
414  }
415  else {
416  b->print_val(*strm, "", false);
417  }
418 
419  }
420  else {
421  transform(strm, b->get_attr_table(), indent);
422  }
423 }
424 
434 void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::Structure *b, string indent, bool sendData)
435 {
436 
437  // Open object with name of the structure
438  std::string name = b->name();
439  *strm << indent << "\"" << fojson::escape_for_json(name) << "\": {" << endl;
440 
441  // Process the variables.
442  if (b->width(true) > 0) {
443 
444  libdap::Structure::Vars_iter vi = b->var_begin();
445  libdap::Structure::Vars_iter ve = b->var_end();
446  for (; vi != ve; vi++) {
447 
448  // If the variable is projected, send (transform) it.
449  if ((*vi)->send_p()) {
450  libdap::BaseType *v = *vi;
452  "FoInstanceJsonTransform::transform() - Processing structure variable: " << v->name() << endl);
453  transform(strm, v, indent + _indent_increment, sendData);
454  if ((vi + 1) != ve) {
455  *strm << ",";
456  }
457  *strm << endl;
458  }
459  }
460  }
461  *strm << indent << "}";
462 }
463 
473 void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::Grid *g, string indent, bool sendData)
474 {
475 
476  // Open JSON property object with name of the grid
477  std::string name = g->name();
478  *strm << indent << "\"" << fojson::escape_for_json(name) << "\": {" << endl;
479 
481  "FoInstanceJsonTransform::transform() - Processing Grid data Array: " << g->get_array()->name() << endl);
482 
483  // Process the data array
484  transform(strm, g->get_array(), indent + _indent_increment, sendData);
485  *strm << "," << endl;
486 
487  // Process the MAP arrays
488  for (libdap::Grid::Map_iter mapi = g->map_begin(); mapi < g->map_end(); mapi++) {
490  "FoInstanceJsonTransform::transform() - Processing Grid Map Array: " << (*mapi)->name() << endl);
491  if (mapi != g->map_begin()) {
492  *strm << "," << endl;
493  }
494  transform(strm, *mapi, indent + _indent_increment, sendData);
495  }
496  // Close the JSON property object
497  *strm << endl << indent << "}";
498 
499 }
500 
510 void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::Sequence *s, string indent, bool sendData)
511 {
512 
513  // Open JSON property object with name of the sequence
514  std::string name = s->name();
515  *strm << indent << "\"" << fojson::escape_for_json(name) << "\": {" << endl;
516 
517  string child_indent = indent + _indent_increment;
518 
519 #if 0
520  the erdap way
521  *strm << indent << "\"table\": {" << endl;
522 
523  string child_indent = indent + _indent_increment;
524 
525  *strm << child_indent << "\"name\": \"" << s->name() << "\"," << endl;
526 
527 #endif
528 
529  *strm << child_indent << "\"columnNames\": [";
530  for (libdap::Constructor::Vars_iter v = s->var_begin(); v < s->var_end(); v++) {
531  if (v != s->var_begin())
532  *strm << ",";
533  std::string name = (*v)->name();
534  *strm << "\"" << fojson::escape_for_json(name) << "\"";
535  }
536  *strm << "]," << endl;
537 
538  *strm << child_indent << "\"columnTypes\": [";
539  for (libdap::Constructor::Vars_iter v = s->var_begin(); v < s->var_end(); v++) {
540  if (v != s->var_begin()) *strm << ",";
541  *strm << "\"" << (*v)->type_name() << "\"";
542  }
543  *strm << "]," << endl;
544 
545  bool first = true;
546  *strm << child_indent << "\"rows\": [";
547  while (s->read()) {
548  if (!first) *strm << ", ";
549  *strm << endl << child_indent << "[";
550  for (libdap::Constructor::Vars_iter v = s->var_begin(); v < s->var_end(); v++) {
551  if (v != s->var_begin()) *strm << child_indent << ",";
552  transform(strm, (*v), child_indent + _indent_increment, sendData);
553  }
554  *strm << child_indent << "]";
555  first = false;
556  }
557  *strm << endl << child_indent << "]" << endl;
558 
559  // Close the JSON property object
560  *strm << indent << "}" << endl;
561 }
562 
572 void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::Array *a, string indent, bool sendData)
573 {
574 
576  "FoInstanceJsonTransform::transform() - Processing Array. " << " a->type(): " << a->type() << " a->var()->type(): " << a->var()->type() << endl);
577 
578  switch (a->var()->type()) {
579  // Handle the atomic types - that's easy!
580  case libdap::dods_byte_c:
581  json_simple_type_array<libdap::dods_byte>(strm, a, indent, sendData);
582  break;
583 
584  case libdap::dods_int16_c:
585  json_simple_type_array<libdap::dods_int16>(strm, a, indent, sendData);
586  break;
587 
588  case libdap::dods_uint16_c:
589  json_simple_type_array<libdap::dods_uint16>(strm, a, indent, sendData);
590  break;
591 
592  case libdap::dods_int32_c:
593  json_simple_type_array<libdap::dods_int32>(strm, a, indent, sendData);
594  break;
595 
596  case libdap::dods_uint32_c:
597  json_simple_type_array<libdap::dods_uint32>(strm, a, indent, sendData);
598  break;
599 
600  case libdap::dods_float32_c:
601  json_simple_type_array<libdap::dods_float32>(strm, a, indent, sendData);
602  break;
603 
604  case libdap::dods_float64_c:
605  json_simple_type_array<libdap::dods_float64>(strm, a, indent, sendData);
606  break;
607 
608  case libdap::dods_str_c: {
609  json_string_array(strm, a, indent, sendData);
610  break;
611 #if 0
612  //json_simple_type_array<Str>(strm,a,indent);
613  string s = (string) "File out JSON, " + "Arrays of Strings are not yet a supported return type.";
614  throw BESInternalError(s, __FILE__, __LINE__);
615  break;
616 #endif
617 
618  }
619 
620  case libdap::dods_url_c: {
621  json_string_array(strm, a, indent, sendData);
622  break;
623 #if 0
624  //json_simple_type_array<Url>(strm,a,indent);
625  string s = (string) "File out JSON, " + "Arrays of URLs are not yet a supported return type.";
626  throw BESInternalError(s, __FILE__, __LINE__);
627  break;
628 #endif
629 
630  }
631 
632  case libdap::dods_structure_c: {
633  string s = (string) "File out JSON, " + "Arrays of Structure objects not a supported return type.";
634  throw BESInternalError(s, __FILE__, __LINE__);
635  break;
636  }
637  case libdap::dods_grid_c: {
638  string s = (string) "File out JSON, " + "Arrays of Grid objects not a supported return type.";
639  throw BESInternalError(s, __FILE__, __LINE__);
640  break;
641  }
642 
643  case libdap::dods_sequence_c: {
644  string s = (string) "File out JSON, " + "Arrays of Sequence objects not a supported return type.";
645  throw BESInternalError(s, __FILE__, __LINE__);
646  break;
647  }
648 
649  case libdap::dods_array_c: {
650  string s = (string) "File out JSON, " + "Arrays of Array objects not a supported return type.";
651  throw BESInternalError(s, __FILE__, __LINE__);
652  break;
653  }
654  case libdap::dods_int8_c:
655  case libdap::dods_uint8_c:
656  case libdap::dods_int64_c:
657  case libdap::dods_uint64_c:
658  // case libdap::dods_url4_c:
659  case libdap::dods_enum_c:
660  case libdap::dods_group_c: {
661  string s = (string) "File out JSON, " + "DAP4 types not yet supported.";
662  throw BESInternalError(s, __FILE__, __LINE__);
663  break;
664  }
665 
666  default: {
667  string s = (string) "File out JSON, " + "Unrecognized type.";
668  throw BESInternalError(s, __FILE__, __LINE__);
669  break;
670  }
671 
672  }
673 
674 }
675 
688 void FoInstanceJsonTransform::transform(std::ostream *strm, libdap::AttrTable &attr_table, string indent)
689 {
690 
691  /*
692  * Since Attributes get promoted to JSON "properties" of their parent object (either a
693  * BaseType variable or a DDS derived JSON object) this method does not open a new JSON
694  * object, but rather continues to add content to the currently open object.
695  */
696  string child_indent = indent + _indent_increment;
697 
698  // Are there any attributes?
699  if (attr_table.get_size() != 0) {
700  //*strm << endl;
701  libdap::AttrTable::Attr_iter begin = attr_table.attr_begin();
702  libdap::AttrTable::Attr_iter end = attr_table.attr_end();
703 
704  // Process each Attribute
705  for (libdap::AttrTable::Attr_iter at_iter = begin; at_iter != end; at_iter++) {
706 
707  switch (attr_table.get_attr_type(at_iter)) {
708 
709  case libdap::Attr_container: // If it's a container attribute
710  {
711  libdap::AttrTable *atbl = attr_table.get_attr_table(at_iter);
712 
713  if (at_iter != begin) *strm << "," << endl;
714 
715  // Open a JSON property with the name of the Attribute Table and
716  std::string name = atbl->get_name();
717  *strm << child_indent << "\"" << fojson::escape_for_json(name) << "\": {" << endl;
718 
719  // Process the Attribute Table.
720  transform(strm, *atbl, child_indent + _indent_increment);
721 
722  // Close JSON property object
723  *strm << endl << child_indent << "}";
724 
725  break;
726 
727  }
728  default: // so it's not an Attribute Table. woot. time to print
729  // First?
730  if (at_iter != begin) *strm << "," << endl;
731 
732  // Name of property
733  std::string name = attr_table.get_name(at_iter);
734  *strm << child_indent << "\"" << fojson::escape_for_json(name) << "\": ";
735 
736  // Open values array
737  *strm << "[";
738  // Process value(s)
739  std::vector<std::string> *values = attr_table.get_attr_vector(at_iter);
740  for (std::vector<std::string>::size_type i = 0; i < values->size(); i++) {
741  if (i > 0) *strm << ",";
742  if (attr_table.get_attr_type(at_iter) == libdap::Attr_string
743  || attr_table.get_attr_type(at_iter) == libdap::Attr_url) {
744  *strm << "\"";
745 
746  string value = (*values)[i];
747  *strm << fojson::escape_for_json(value);
748  *strm << "\"";
749  }
750  else {
751  *strm << (*values)[i];
752  }
753 
754  }
755  // Close values array
756  *strm << "]";
757  break;
758  }
759  }
760  }
761 }
762 
exception thrown if inernal error encountered
FoInstanceJsonTransform(libdap::DDS *dds, BESDataHandlerInterface &dhi, const std::string &localfile)
Constructor that creates transformation object from the specified DataDDS object to the specified fil...
#define FoInstanceJsonTransform_debug_key
STL namespace.
virtual ~FoInstanceJsonTransform()
Destructor.
static void Indent()
Definition: BESIndent.cc:38
static ostream & LMarg(ostream &strm)
Definition: BESIndent.cc:73
long computeConstrainedShape(libdap::Array *a, std::vector< unsigned int > *shape)
Compute the constrained shape of the Array and return it in a vector.
Definition: fojson_utils.cc:61
std::string escape_for_json(const std::string &input)
Definition: fojson_utils.cc:41
Structure storing information used by the BES to handle the request.
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
static void UnIndent()
Definition: BESIndent.cc:44
virtual void dump(std::ostream &strm) const
dumps information about this transformation object for debugging purposes