OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
FoDapJsonTransform.cc
Go to the documentation of this file.
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 
30 #include "FoDapJsonTransform.h"
31 #include "config.h"
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 
44 #include <DDS.h>
45 #include <Structure.h>
46 #include <Constructor.h>
47 #include <Array.h>
48 #include <Grid.h>
49 #include <Sequence.h>
50 #include <Str.h>
51 #include <Url.h>
52 
53 #include <BESDebug.h>
54 #include <BESInternalError.h>
55 
56 #include "FoDapJsonTransform.h"
57 #include "fojson_utils.h"
58 
59 #define FoDapJsonTransform_debug_key "fojson"
60 
61 
66 template<typename T> unsigned int FoDapJsonTransform::json_simple_type_array_worker(ostream *strm, T *values, unsigned int indx, vector<unsigned int> *shape, unsigned int currentDim){
67 
68  *strm << "[";
69 
70  unsigned int currentDimSize = (*shape)[currentDim];
71 
72  for(unsigned int i=0; i<currentDimSize ;i++){
73  if(currentDim < shape->size()-1){
74  BESDEBUG(FoDapJsonTransform_debug_key, "json_simple_type_array_worker() - Recursing! indx: " << indx
75  << " currentDim: " << currentDim
76  << " currentDimSize: " << currentDimSize
77  << endl);
78  indx = json_simple_type_array_worker<T>(strm,values,indx,shape,currentDim+1);
79  if(i+1 != currentDimSize)
80  *strm << ", ";
81  }
82  else {
83  if(i)
84  *strm << ", ";
85  if(typeid(T) == typeid(std::string)){
86  // Strings need to be escaped to be included in a JSON object.
87  std::string val = ((std::string *) values)[indx++];
88  *strm << "\"" << fojson::escape_for_json( val )<< "\"";
89  }
90  else {
91  *strm << values[indx++];
92  }
93  }
94  }
95  *strm << "]";
96 
97  return indx;
98 }
99 
100 
101 
106 template<typename T>void FoDapJsonTransform::json_simple_type_array(ostream *strm, libdap::Array *a, string indent, bool sendData){
107 
108 
109  *strm << indent << "{" << endl;\
110 
111  string childindent = indent + _indent_increment;
112 
113  writeLeafMetadata(strm,a,childindent);
114 
115  int numDim = a->dimensions(true);
116  vector<unsigned int> shape(numDim);
117  long length = fojson::computeConstrainedShape(a, &shape);
118 
119  *strm << childindent << "\"shape\": [";
120 
121  for(std::vector<unsigned int>::size_type i=0; i<shape.size() ;i++){
122  if(i>0)
123  *strm << ",";
124  *strm << shape[i];
125  }
126  *strm << "]";
127 
128  if(sendData){
129  *strm << ","<< endl;
130 
131  // Data
132  *strm << childindent << "\"data\": ";
133  unsigned int indx;
134  T *src = new T[length];
135  a->value(src);
136  indx = json_simple_type_array_worker(strm, src, 0, &shape, 0);
137  delete src;
138 
139  if(length != indx)
140  BESDEBUG(FoDapJsonTransform_debug_key, "json_simple_type_array() - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
141  }
142 
143  *strm << endl << indent << "}";
144 
145 }
155 void FoDapJsonTransform::json_string_array(std::ostream *strm, libdap::Array *a, string indent, bool sendData)
156 {
157 
158  *strm << indent << "{" << endl;\
159 
160  string childindent = indent + _indent_increment;
161 
162  writeLeafMetadata(strm,a,childindent);
163 
164  int numDim = a->dimensions(true);
165  vector<unsigned int> shape(numDim);
166  long length = fojson::computeConstrainedShape(a, &shape);
167 
168  *strm << childindent << "\"shape\": [";
169 
170  for(std::vector<unsigned int>::size_type i=0; i<shape.size() ;i++){
171  if(i>0)
172  *strm << ",";
173  *strm << shape[i];
174  }
175  *strm << "]";
176 
177  if(sendData){
178  *strm << ","<< endl;
179 
180  // Data
181  *strm << childindent << "\"data\": ";
182  unsigned int indx;
183 
184  // The string type utilizes a specialized version of libdap:Array.value()
185  vector<std::string> sourceValues;
186  a->value(sourceValues);
187  indx = json_simple_type_array_worker(strm, (std::string *)(&sourceValues[0]), 0, &shape, 0);
188 
189  if(length != indx)
190  BESDEBUG(FoDapJsonTransform_debug_key, "json_string_array() - indx NOT equal to content length! indx: " << indx << " length: " << length << endl);
191 
192 
193  }
194 
195  *strm << endl << indent << "}";
196 
197 }
198 
199 
200 
204 void FoDapJsonTransform::writeDatasetMetadata(ostream *strm, libdap::DDS *dds, string indent){
205 
206  // Name
207  *strm << indent << "\"name\": \""<< dds->get_dataset_name() << "\"," << endl;
208 
209  //Attributes
210  transform(strm, dds->get_attr_table(), indent);
211  *strm << "," << endl;
212 
213 }
214 
219 void FoDapJsonTransform::writeNodeMetadata(ostream *strm, libdap::BaseType *bt, string indent){
220 
221  // Name
222  *strm << indent << "\"name\": \""<< bt->name() << "\"," << endl;
223 
224  //Attributes
225  transform(strm, bt->get_attr_table(), indent);
226  *strm << "," << endl;
227 
228 
229 
230 }
231 
236 void FoDapJsonTransform::writeLeafMetadata(ostream *strm, libdap::BaseType *bt, string indent){
237 
238  // Name
239  *strm << indent << "\"name\": \""<< bt->name() << "\"," << endl;
240 
241 
242 
243  // type
244  if(bt->type() == libdap::dods_array_c){
245  libdap::Array *a = (libdap::Array *)bt;
246  *strm << indent << "\"type\": \""<< a->var()->type_name() << "\"," << endl;
247  }
248  else {
249  *strm << indent << "\"type\": \""<< bt->type_name() << "\"," << endl;
250  }
251 
252 
253  //Attributes
254  transform(strm, bt->get_attr_table(), indent);
255  *strm << "," << endl;
256 
257 
258 
259 }
260 
261 
262 
263 
275 FoDapJsonTransform::FoDapJsonTransform(libdap::DDS *dds, BESDataHandlerInterface &dhi, const string &localfile) :
276  _dds(dds), _localfile(localfile), _indent_increment(" "), _ostrm(0)
277 {
278  if (!_dds)
279  throw BESInternalError("File out JSON, null DDS passed to constructor", __FILE__, __LINE__);
280 
281  if (_localfile.empty())
282  throw BESInternalError("File out JSON, empty local file name passed to constructor", __FILE__, __LINE__);
283 }
284 
285 FoDapJsonTransform::FoDapJsonTransform(libdap::DDS *dds, BESDataHandlerInterface &/*dhi*/, std::ostream *ostrm) :
286  _dds(dds), _localfile(""), _indent_increment(" "), _ostrm(ostrm)
287 {
288  if (!_dds)
289  throw BESInternalError("File out JSON, null DDS passed to constructor", __FILE__, __LINE__);
290 
291  if (!_ostrm)
292  throw BESInternalError("File out JSON, null stream pointer passed to constructor", __FILE__, __LINE__);
293 }
294 
300 {
301 }
302 
312 void FoDapJsonTransform::dump(ostream &strm) const
313 {
314  strm << BESIndent::LMarg << "FoDapJsonTransform::dump - (" << (void *) this << ")" << endl;
316  strm << BESIndent::LMarg << "temporary file = " << _localfile << endl;
317  if(_dds != 0){
318  _dds->print(strm);
319  }
321 }
322 
323 
324 
325 
334 void FoDapJsonTransform::transform(bool sendData)
335 {
336  // used to ensure the _ostrm is closed only when it's a temp file
337  bool used_temp_file = false;
338  fstream temp_file;
339 
340  if (!_ostrm) {
341  temp_file.open(_localfile.c_str(), std::fstream::out);
342  if (!temp_file)
343  throw BESInternalError("Could not open temp file: " + _localfile, __FILE__, __LINE__);
344  _ostrm = &temp_file;
345  used_temp_file = true;
346  }
347 
348  try {
349  transform(_ostrm, _dds, "", sendData);
350  if (used_temp_file)
351  temp_file.close();
352  }
353  catch (...) {
354  if (used_temp_file)
355  temp_file.close();
356  throw;
357  }
358 }
359 
360 
365 void FoDapJsonTransform::transform(ostream *strm, libdap::Constructor *cnstrctr, string indent, bool sendData){
366  vector<libdap::BaseType *> leaves;
367  vector<libdap::BaseType *> nodes;
368 
369 
370  // Sort the variables into two sets/
371  libdap::DDS::Vars_iter vi = cnstrctr->var_begin();
372  libdap::DDS::Vars_iter ve = cnstrctr->var_end();
373  for (; vi != ve; vi++) {
374  if ((*vi)->send_p()) {
375  libdap::BaseType *v = *vi;
376  v->is_constructor_type();
377  libdap::Type type = v->type();
378  if(type == libdap::dods_array_c){
379  type = v->var()->type();
380  }
381  if(v->is_constructor_type() ||
382  (v->is_vector_type() && v->var()->is_constructor_type())){
383  nodes.push_back(v);
384  }
385  else {
386  leaves.push_back(v);
387  }
388  }
389  }
390 
391  // Declare this node
392  *strm << indent << "{" << endl ;
393  string child_indent = indent + _indent_increment;
394 
395  // Write this node's metadata (name & attributes)
396  writeNodeMetadata(strm, cnstrctr, child_indent);
397 
398  transform_node_worker(strm, leaves, nodes, child_indent, sendData);
399 
400  *strm << indent << "}" << endl;
401 
402 }
403 
408 void FoDapJsonTransform::transform_node_worker(ostream *strm, vector<libdap::BaseType *> leaves, vector<libdap::BaseType *> nodes, string indent, bool sendData){
409 
410  // Write down this nodes leaves
411  *strm << indent << "\"leaves\": [";
412  if(leaves.size() > 0)
413  *strm << endl;
414  for(std::vector<libdap::BaseType *>::size_type l=0; l< leaves.size(); l++){
415  libdap::BaseType *v = leaves[l];
416  BESDEBUG(FoDapJsonTransform_debug_key, "Processing LEAF: " << v->name() << endl);
417  if( l>0 ){
418  *strm << "," ;
419  *strm << endl ;
420  }
421  transform(strm, v, indent + _indent_increment, sendData);
422  }
423  if(leaves.size()>0)
424  *strm << endl << indent;
425  *strm << "]," << endl;
426 
427 
428  // Write down this nodes child nodes
429  *strm << indent << "\"nodes\": [";
430  if(nodes.size() > 0)
431  *strm << endl;
432  for(std::vector<libdap::BaseType *>::size_type n=0; n< nodes.size(); n++){
433  libdap::BaseType *v = nodes[n];
434  transform(strm, v, indent + _indent_increment, sendData);
435  }
436  if(nodes.size()>0)
437  *strm << endl << indent;
438 
439  *strm << "]" << endl;
440 
441 
442 }
443 
444 
449 void FoDapJsonTransform::transform(ostream *strm, libdap::DDS *dds, string indent, bool sendData){
450 
451 
452 
456  vector<libdap::BaseType *> leaves;
457  vector<libdap::BaseType *> nodes;
458 
459  libdap::DDS::Vars_iter vi = dds->var_begin();
460  libdap::DDS::Vars_iter ve = dds->var_end();
461  for (; vi != ve; vi++) {
462  if ((*vi)->send_p()) {
463  libdap::BaseType *v = *vi;
464  libdap::Type type = v->type();
465  if(type == libdap::dods_array_c){
466  type = v->var()->type();
467  }
468  if(v->is_constructor_type() ||
469  (v->is_vector_type() && v->var()->is_constructor_type())){
470  nodes.push_back(v);
471  }
472  else {
473  leaves.push_back(v);
474  }
475  }
476  }
477 
478  // Declare this node
479  *strm << indent << "{" << endl ;
480  string child_indent = indent + _indent_increment;
481 
482  // Write this node's metadata (name & attributes)
483  writeDatasetMetadata(strm, dds, child_indent);
484 
485  transform_node_worker(strm, leaves, nodes, child_indent, sendData);
486 
487  *strm << indent << "}" << endl;
488 
489 }
490 
491 
496 void FoDapJsonTransform::transform(ostream *strm, libdap::BaseType *bt, string indent, bool sendData)
497 {
498  switch(bt->type()){
499  // Handle the atomic types - that's easy!
500  case libdap::dods_byte_c:
501  case libdap::dods_int16_c:
502  case libdap::dods_uint16_c:
503  case libdap::dods_int32_c:
504  case libdap::dods_uint32_c:
505  case libdap::dods_float32_c:
506  case libdap::dods_float64_c:
507  case libdap::dods_str_c:
508  case libdap::dods_url_c:
509  transformAtomic(strm, bt, indent, sendData);
510  break;
511 
512  case libdap::dods_structure_c:
513  transform(strm, (libdap::Structure *) bt, indent, sendData);
514  break;
515 
516  case libdap::dods_grid_c:
517  transform(strm, (libdap::Grid *) bt, indent, sendData);
518  break;
519 
520  case libdap::dods_sequence_c:
521  transform(strm, (libdap::Sequence *) bt, indent, sendData);
522  break;
523 
524  case libdap::dods_array_c:
525  transform(strm, (libdap::Array *) bt, indent, sendData);
526  break;
527 
528  case libdap::dods_int8_c:
529  case libdap::dods_uint8_c:
530  case libdap::dods_int64_c:
531  case libdap::dods_uint64_c:
532  // case libdap::dods_url4_c:
533  case libdap::dods_enum_c:
534  case libdap::dods_group_c:
535  {
536  string s = (string) "File out JSON, " + "DAP4 types not yet supported.";
537  throw BESInternalError(s, __FILE__, __LINE__);
538  break;
539  }
540 
541  default:
542  {
543  string s = (string) "File out JSON, " + "Unrecognized type.";
544  throw BESInternalError(s, __FILE__, __LINE__);
545  break;
546  }
547 
548  }
549 
550 }
551 
556 void FoDapJsonTransform::transformAtomic(ostream *strm, libdap::BaseType *b, string indent, bool sendData){
557 
558  *strm << indent << "{" << endl;
559 
560  string childindent = indent + _indent_increment;
561 
562  writeLeafMetadata(strm, b, childindent);
563 
564  *strm << childindent << "\"shape\": [1]," << endl;
565 
566  if(sendData){
567  // Data
568  *strm << childindent << "\"data\": [";
569 
570  if(b->type() == libdap::dods_str_c || b->type() == libdap::dods_url_c ){
571  libdap::Str *strVar = (libdap::Str *)b;
572  std::string tmpString = strVar->value();
573  *strm << "\"" << fojson::escape_for_json(tmpString) << "\"";
574  }
575  else {
576  b->print_val(*strm, "", false);
577  }
578 
579  *strm << "]";
580  }
581 
582 }
583 
584 
585 
590 void FoDapJsonTransform::transform(ostream *strm, libdap::Array *a, string indent, bool sendData){
591 
592  BESDEBUG(FoDapJsonTransform_debug_key, "FoJsonTransform::transform() - Processing Array. "
593  << " a->type(): " << a->type()
594  << " a->var()->type(): " << a->var()->type()
595  << endl);
596 
597  switch(a->var()->type()){
598  // Handle the atomic types - that's easy!
599  case libdap::dods_byte_c:
600  json_simple_type_array<libdap::dods_byte>(strm,a,indent,sendData);
601  break;
602 
603  case libdap::dods_int16_c:
604  json_simple_type_array<libdap::dods_int16>(strm,a,indent,sendData);
605  break;
606 
607  case libdap::dods_uint16_c:
608  json_simple_type_array<libdap::dods_uint16>(strm,a,indent,sendData);
609  break;
610 
611  case libdap::dods_int32_c:
612  json_simple_type_array<libdap::dods_int32>(strm,a,indent,sendData);
613  break;
614 
615  case libdap::dods_uint32_c:
616  json_simple_type_array<libdap::dods_uint32>(strm,a,indent,sendData);
617  break;
618 
619  case libdap::dods_float32_c:
620  json_simple_type_array<libdap::dods_float32>(strm,a,indent,sendData);
621  break;
622 
623  case libdap::dods_float64_c:
624  json_simple_type_array<libdap::dods_float64>(strm,a,indent,sendData);
625  break;
626 
627  case libdap::dods_str_c:
628  {
629  json_string_array(strm,a,indent,sendData);
630 
631 #if 0
632  string s = (string) "File out JSON, " + "Arrays of String objects not a supported return type.";
633  throw BESInternalError(s, __FILE__, __LINE__);
634 #endif
635  break;
636  }
637 
638  case libdap::dods_url_c:
639  {
640  json_string_array(strm,a,indent,sendData);
641 
642 #if 0
643  string s = (string) "File out JSON, " + "Arrays of URL objects not a supported return type.";
644  throw BESInternalError(s, __FILE__, __LINE__);
645 #endif
646 
647  break;
648  }
649 
650  case libdap::dods_structure_c:
651  {
652  string s = (string) "File out JSON, " + "Arrays of Structure objects not a supported return type.";
653  throw BESInternalError(s, __FILE__, __LINE__);
654  break;
655  }
656  case libdap::dods_grid_c:
657  {
658  string s = (string) "File out JSON, " + "Arrays of Grid objects not a supported return type.";
659  throw BESInternalError(s, __FILE__, __LINE__);
660  break;
661  }
662 
663  case libdap::dods_sequence_c:
664  {
665  string s = (string) "File out JSON, " + "Arrays of Sequence objects not a supported return type.";
666  throw BESInternalError(s, __FILE__, __LINE__);
667  break;
668  }
669 
670  case libdap::dods_array_c:
671  {
672  string s = (string) "File out JSON, " + "Arrays of Array objects not a supported return type.";
673  throw BESInternalError(s, __FILE__, __LINE__);
674  break;
675  }
676  case libdap::dods_int8_c:
677  case libdap::dods_uint8_c:
678  case libdap::dods_int64_c:
679  case libdap::dods_uint64_c:
680  // case libdap::dods_url4_c:
681  case libdap::dods_enum_c:
682  case libdap::dods_group_c:
683  {
684  string s = (string) "File out JSON, " + "DAP4 types not yet supported.";
685  throw BESInternalError(s, __FILE__, __LINE__);
686  break;
687  }
688 
689  default:
690  {
691  string s = (string) "File out JSON, " + "Unrecognized type.";
692  throw BESInternalError(s, __FILE__, __LINE__);
693  break;
694  }
695 
696  }
697 
698 }
699 
700 
705 void FoDapJsonTransform::transform(ostream *strm, libdap::AttrTable &attr_table, string indent){
706 
707  string child_indent = indent + _indent_increment;
708 
709  // Start the attributes block
710  *strm << indent << "\"attributes\": [";
711 
712 
713 // if(attr_table.get_name().length()>0)
714 // *strm << endl << child_indent << "{\"name\": \"name\", \"value\": \"" << attr_table.get_name() << "\"},";
715 
716 
717  // Only do more if there are actually attributes in the table
718  if(attr_table.get_size() != 0) {
719  *strm << endl;
720  libdap::AttrTable::Attr_iter begin = attr_table.attr_begin();
721  libdap::AttrTable::Attr_iter end = attr_table.attr_end();
722 
723 
724  for(libdap::AttrTable::Attr_iter at_iter=begin; at_iter !=end; at_iter++){
725 
726  switch (attr_table.get_attr_type(at_iter)){
727  case libdap::Attr_container:
728  {
729  libdap::AttrTable *atbl = attr_table.get_attr_table(at_iter);
730 
731  // not first thing? better use a comma...
732  if(at_iter != begin )
733  *strm << "," << endl;
734 
735  // Attribute Containers need to be opened and then a recursive call gets made
736  *strm << child_indent << "{" << endl;
737 
738  // If the table has a name, write it out as a json property.
739  if(atbl->get_name().length()>0)
740  *strm << child_indent + _indent_increment << "\"name\": \"" << atbl->get_name() << "\"," << endl;
741 
742 
743  // Recursive call for child attribute table.
744  transform(strm, *atbl, child_indent + _indent_increment);
745  *strm << endl << child_indent << "}";
746 
747  break;
748 
749  }
750  default:
751  {
752  // not first thing? better use a comma...
753  if(at_iter != begin)
754  *strm << "," << endl;
755 
756  // Open attribute object, write name
757  *strm << child_indent << "{\"name\": \""<< attr_table.get_name(at_iter) << "\", ";
758 
759  // Open value array
760  *strm << "\"value\": [";
761  vector<std::string> *values = attr_table.get_attr_vector(at_iter);
762  // write values
763  for(std::vector<std::string>::size_type i=0; i<values->size() ;i++){
764 
765  // not first thing? better use a comma...
766  if(i>0)
767  *strm << ",";
768 
769  // Escape the double quotes found in String and URL type attribute values.
770  if(attr_table.get_attr_type(at_iter) == libdap::Attr_string || attr_table.get_attr_type(at_iter) == libdap::Attr_url){
771  *strm << "\"";
772  // string value = (*values)[i] ;
773  *strm << fojson::escape_for_json((*values)[i]) ;
774  *strm << "\"";
775  }
776  else {
777 
778  *strm << (*values)[i] ;
779  }
780 
781  }
782  // close value array
783  *strm << "]}";
784  break;
785  }
786 
787  }
788  }
789  *strm << endl << indent;
790 
791  }
792 
793  // close AttrTable JSON
794 
795  *strm << "]";
796 
797 
798 
799 }
800 
801 
802 
803 
804 
805 
806 
807 
exception thrown if inernal error encountered
virtual void dump(std::ostream &strm) const
dumps information about this transformation object for debugging purposes
static void Indent()
Definition: BESIndent.cc:38
static ostream & LMarg(ostream &strm)
Definition: BESIndent.cc:73
virtual ~FoDapJsonTransform()
Destructor.
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 FoDapJsonTransform_debug_key
#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
FoDapJsonTransform(libdap::DDS *dds, BESDataHandlerInterface &dhi, const std::string &localfile)
Constructor that creates transformation object from the specified DataDDS object to the specified fil...