This is Unofficial EPICS BASE Doxygen Site
parseany.cpp
Go to the documentation of this file.
1 /*
2  * Copyright information and license terms for this software can be
3  * found in the file LICENSE that is included with the distribution
4  */
5 
6 #include <sstream>
7 
8 #define epicsExportSharedSymbols
9 #include <pv/pvdVersion.h>
10 #include <pv/pvData.h>
11 #include <pv/valueBuilder.h>
12 
13 #include "pv/json.h"
14 
15 namespace pvd = epics::pvData;
18 
19 namespace {
20 
21 struct context {
22 
23  unsigned depth;
24 
25  enum state_t {
26  Undefined,
27  Key,
28  Array,
29  } state;
30 
32 
33  pvd::ValueBuilder root,
34  *cur;
35 
36  std::string msg,
37  key;
38 
39  context() :depth(0u), state(Undefined), cur(&root) {}
40 };
41 
42 #define TRY context *self = (context*)ctx; try
43 
44 #define CATCH() catch(std::exception& e) { self->msg = e.what(); return 0; }
45 
46 int jtree_null(void * ctx)
47 {
48  TRY {
49  self->msg = "NULL value not permitted";
50  return 0;
51  }CATCH()
52 }
53 
54 int jtree_boolean(void * ctx, int boolVal)
55 {
56  TRY {
57  if(self->depth==0) throw std::runtime_error("Bare value not supported");
58  switch(self->state) {
59  case context::Key:
60  self->cur = &self->cur->add<pvd::pvBoolean>(self->key, boolVal);
61  self->key.clear();
62  self->state = context::Undefined;
63  break;
64  case context::Array:
65  {
66  if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvBoolean)
67  throw std::runtime_error("Mixed type array not supported");
68  pvd::shared_vector<pvd::boolean> arr(pvd::static_shared_vector_cast<pvd::boolean>(self->arr));
69  arr.push_back(boolVal);
70  self->arr = pvd::static_shared_vector_cast<void>(arr);
71  break;
72  }
73  default:
74  throw std::logic_error("boolean in bad state");
75  }
76  return 1;
77  }CATCH()
78 }
79 
80 int jtree_integer(void * ctx, integer_arg integerVal)
81 {
82  TRY {
83  if(self->depth==0) throw std::runtime_error("Bare value not supported");
84  switch(self->state) {
85  case context::Key:
86  self->cur = &self->cur->add<pvd::pvLong>(self->key, integerVal);
87  self->key.clear();
88  self->state = context::Undefined;
89  break;
90  case context::Array:
91  {
92  if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvLong)
93  throw std::runtime_error("Mixed type array not supported");
94  pvd::shared_vector<pvd::int64> arr(pvd::static_shared_vector_cast<pvd::int64>(self->arr));
95  arr.push_back(integerVal);
96  self->arr = pvd::static_shared_vector_cast<void>(arr);
97  break;
98  }
99  default:
100  throw std::logic_error("int64 in bad state");
101  }
102  return 1;
103  }CATCH()
104 }
105 
106 int jtree_double(void * ctx, double doubleVal)
107 {
108  TRY {
109  if(self->depth==0) throw std::runtime_error("Bare value not supported");
110  switch(self->state) {
111  case context::Key:
112  self->cur = &self->cur->add<pvd::pvDouble>(self->key, doubleVal);
113  self->key.clear();
114  self->state = context::Undefined;
115  break;
116  case context::Array:
117  {
118  if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvDouble)
119  throw std::runtime_error("Mixed type array not supported");
120  pvd::shared_vector<double> arr(pvd::static_shared_vector_cast<double>(self->arr));
121  arr.push_back(doubleVal);
122  self->arr = pvd::static_shared_vector_cast<void>(arr);
123  break;
124  }
125  default:
126  throw std::logic_error("double in bad state");
127  }
128  return 1;
129  }CATCH()
130 }
131 
132 int jtree_string(void * ctx, const unsigned char * stringVal,
133  size_arg stringLen)
134 {
135  TRY {
136  if(self->depth==0) throw std::runtime_error("Bare value not supported");
137  std::string sval((const char*)stringVal, stringLen);
138  switch(self->state) {
139  case context::Key:
140  self->cur = &self->cur->add<pvd::pvString>(self->key, sval);
141  self->key.clear();
142  self->state = context::Undefined;
143  break;
144  case context::Array:
145  {
146  if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvString)
147  throw std::runtime_error("Mixed type array not supported");
148  pvd::shared_vector<std::string> arr(pvd::static_shared_vector_cast<std::string>(self->arr));
149  arr.push_back(sval);
150  self->arr = pvd::static_shared_vector_cast<void>(arr);
151  break;
152  }
153  default:
154  throw std::logic_error("double in bad state");
155  }
156  return 1;
157  }CATCH()
158 }
159 
160 int jtree_start_map(void * ctx)
161 {
162  TRY {
163  if(self->depth>0) {
164  if(self->key.empty())
165  throw std::logic_error("anonymous dict not top level?");
166  self->cur = &self->cur->addNested(self->key);
167  self->key.clear();
168  }
169  self->depth++;
170  return 1;
171  }CATCH()
172 }
173 
174 int jtree_map_key(void * ctx, const unsigned char * key,
175  size_arg stringLen)
176 {
177  TRY {
178  if(!self->key.empty())
179  throw std::logic_error("double key?");
180  if(stringLen==0)
181  throw std::runtime_error("empty key not allowed");
182  self->key = std::string((const char*)key, stringLen);
183  self->state = context::Key;
184  return 1;
185  }CATCH()
186 }
187 
188 int jtree_end_map(void * ctx)
189 {
190  TRY {
191  if(self->depth>1)
192  self->cur = &self->cur->endNested();
193  else if(self->depth==0)
194  throw std::logic_error("Unbalenced dict");
195  self->depth--;
196  return 1;
197  }CATCH()
198 }
199 
200 int jtree_start_array(void * ctx)
201 {
202  TRY {
203  if(self->depth==0) throw std::runtime_error("Bare array not supported");
204  if(self->state!=context::Key)
205  throw std::logic_error("bare array not supported");
206  self->state = context::Array;
207  return 1;
208  }CATCH()
209 }
210 int jtree_end_array(void * ctx)
211 {
212  TRY {
213  if(self->state!=context::Array)
214  throw std::logic_error("Bad array parse");
215  self->cur = &self->cur->add(self->key, pvd::freeze(self->arr));
216  self->key.clear();
217  self->state = context::Undefined;
218  return 1;
219  }CATCH()
220 }
221 
222 
223 yajl_callbacks jtree_cbs = {
224  &jtree_null,
225  &jtree_boolean,
226  &jtree_integer,
227  &jtree_double,
228  NULL, // number
229  &jtree_string,
230  &jtree_start_map,
231  &jtree_map_key,
232  &jtree_end_map,
233  &jtree_start_array,
234  &jtree_end_array,
235 };
236 
237 struct handler {
238  yajl_handle handle;
239  handler(yajl_handle handle) :handle(handle)
240  {
241  if(!handle)
242  throw std::runtime_error("Failed to allocate yajl handle");
243  }
244  ~handler() {
245  yajl_free(handle);
246  }
247  operator yajl_handle() { return handle; }
248 };
249 
250 } // namespace
251 
252 namespace epics{namespace pvData{
253 
254 epics::pvData::PVStructure::shared_pointer
255 parseJSON(std::istream& strm)
256 {
257 #ifndef EPICS_YAJL_VERSION
258  yajl_parser_config conf;
259  memset(&conf, 0, sizeof(conf));
260  conf.allowComments = 1;
261  conf.checkUTF8 = 1;
262 #endif
263 
264  context ctxt;
265 
266 #ifndef EPICS_YAJL_VERSION
267  handler handle(yajl_alloc(&jtree_cbs, &conf, NULL, &ctxt));
268 #else
269  handler handle(yajl_alloc(&jtree_cbs, NULL, &ctxt));
270 
271  yajl_config(handle, yajl_allow_comments, 1);
272 #endif
273 
274  if(!yajl_parse_helper(strm, handle))
275  throw std::runtime_error(ctxt.msg);
276 
277  return ctxt.cur->buildPVStructure();
278 }
279 
280 }} // namespace epics::pvData
void yajl_free(yajl_handle handle)
Definition: yajl.c:109
A holder for a contiguous piece of memory.
Definition: sharedVector.h:27
TODO only here because of the Lockable.
Definition: ntaggregate.cpp:16
int yajl_config(yajl_handle h, yajl_option opt,...)
Definition: yajl.c:85
#define NULL
Definition: catime.c:38
#define CATCH()
Definition: parseany.cpp:44
unsigned size_arg
Definition: json.h:137
pvData
Definition: monitor.h:428
void push_back(param_type v)
Definition: sharedVector.h:602
epics::pvData::PVStructure::shared_pointer parseJSON(std::istream &strm)
Definition: parseany.cpp:255
long integer_arg
Definition: json.h:136
yajl_handle yajl_alloc(const yajl_callbacks *callbacks, yajl_alloc_funcs *afs, void *ctx)
Definition: yajl.c:46
bool yajl_parse_helper(std::istream &src, yajl_handle handle)
Definition: parsehelper.cpp:29
struct yajl_handle_t * yajl_handle
Definition: yajl_parse.h:46
#define TRY
Definition: parseany.cpp:42