Flow123d
json_to_storage.hh
Go to the documentation of this file.
1 /*
2  * json_to_storage.hh
3  *
4  * Created on: May 7, 2012
5  * Author: jb
6  *
7  * TODO:
8  * EI_InputType - passing pointer is not save. The object can be deleted during stack unfolding.
9  * Error_info can not be used for abstract types (of course), the only save way is to
10  * use boost::shared_ptr, so make the copy our self by make_shared and then only pass the pointer.
11  *
12  * These problems can be eliminated, when all Type instances are static.
13  *
14  * TODO:
15  * - check cyclic references, drop const for json_spirit pointers and modify REF keys
16  * when dereferenced and modify it back when we return.
17  * (e.g. add a new entry into map
18  * TODO:
19  * Find deleting of a null pointer in json_spirit library. Possibly implement
20  * output of true stack for GNU.
21  */
22 
23 #ifndef JSON_TO_STORAGE_HH_
24 #define JSON_TO_STORAGE_HH_
25 
26 
27 #include <sstream>
28 
30 #include "input/input_type.hh"
31 
32 #include "input/accessors.hh"
33 #include "input/storage.hh"
34 
35 
36 namespace Input {
37 
38 
39 
40 /**
41  * @brief Class used by JSONToStorage class to iterate over the JSON tree provided by json_spirit library.
42  *
43  * This class keeps whole path from the root of the JSON tree to the current node. We store nodes along path in \p nodes_
44  * and address of the node in \p path_.
45  *
46  * The class also contains methods for processing of special keys 'REF' and 'TYPE'. The reference is record with only one key
47  * 'REF' with a string value that contains address of the reference. The string with the address is extracted by \p JSONToStorage::get_ref_from_head
48  * then the JSONPath corresponding to the address is provided by method \p JSONtoStorage::find_ref_node.
49  */
50 class JSONPath {
51 public:
52  /**
53  * Thrown if a reference in the input file
54  */
55 
56  TYPEDEF_ERR_INFO(EI_ErrorAddress, JSONPath);
57  TYPEDEF_ERR_INFO(EI_RefAddress, JSONPath);
58  TYPEDEF_ERR_INFO(EI_JsonFile, const string);
59  TYPEDEF_ERR_INFO(EI_RefStr, const string);
60  TYPEDEF_ERR_INFO(EI_Specification, const string);
61  DECLARE_INPUT_EXCEPTION(ExcRefOfWrongType,
62  << "Reference at address "
63  << EI_ErrorAddress::qval << " has wrong type, should by string.");
64  DECLARE_INPUT_EXCEPTION(ExcReferenceNotFound,
65  << "Error in input file: " << EI_JsonFile::qval << "\nReference {REF=\"" << EI_RefStr::val << "\"} at address " << EI_RefAddress::qval << " not found.\n"
66  << "failed to follow at address: " << EI_ErrorAddress::qval << " because " << EI_Specification::val);
67 
68 
69 
71 
72  JSONPath(const Node& root_node);
73 
74  /**
75  * Dive into json_spirit hierarchy. Store current path and returns pointer to new json_spirit node.
76  * If the json_spirit type do not match returns NULL.
77  */
78  const Node * down(unsigned int index);
79  const Node * down(const string& key);
80 
81  /**
82  * Return one level up in the hierrarchy.
83  */
84  void up();
85 
86  /**
87  * Move to root node.
88  */
89  void go_to_root();
90 
91  /**
92  * Pointer to JSON Value object at current path.
93  */
94  inline const Node * head() const
95  { return nodes_.back(); }
96 
97  /**
98  * Returns level of actual path. Root has level == 0.
99  */
100  inline int level() const
101  { return nodes_.size() - 1; }
102 
103  /**
104  * Check if current head node is a JSON Object containing one key REF of type string.
105  * If yes, returns the string through reference @p ref_address.
106  */
107  bool get_ref_from_head(string & ref_address);
108 
109  /**
110  * Creates a new JSONPath object given by address string possibly relative to the current
111  * path.
112  */
113  JSONPath find_ref_node(const string& ref_address);
114 
115  /**
116  * Output to the given stream.
117  */
118  void output(ostream &stream) const;
119 
120  /**
121  * Returns string address of current position.
122  */
123  string str();
124 
125  /**
126  * Put actual address to previous_references_ set
127  */
128  void put_address();
129 
130 private:
131  /**
132  * One level of the @p path_ is either index (nonnegative int) in array or string key in a json object.
133  * For the first type we save index into first part of the pair and empty string to the second.
134  * For the later type of level, we save -1 for index and the key into the secodn part of the pair.
135  */
138  std::set<string> previous_references_;
139 
140 
141 };
142 
143 /**
144  * Output operator for JSONPath. Mainly for debugging purposes and error messages.
145  */
146 std::ostream& operator<<(std::ostream& stream, const JSONPath& path);
147 
148 
149 /**
150  * @brief Reader for (slightly) modified JSON files.
151  *
152  * This class implements a reader of modified JSON file format. The modifications include
153  * shell-like comments (using hash '#' character), this is implemented in comment_filter.hh, optional quoting of
154  * keys in JSON objects that do not contain spaces, and possibility to use '=' instead of ':'. So you can write:
155  * @code
156  * { key1="text", key2=2, "key 3"=4 }
157  * @endcode
158  * Note, however, that our input interface allows only C identifiers for keys. The reader use json_spirit library
159  * (based on Spirit parser from Boost) with slightly modified grammar.
160  *
161  * The input file is at first read and parsed by json_spirit. Then JSONToStorage pass through tree with parsed data along
162  * with passing through declaration tree. The input data are check against declaration and stored in the Storage tree.
163  *
164  * Accessor to the root record is provided by JSONToStorage::get_root_interface<T> method template.
165  *
166  * @ingroup input
167  */
169 public:
170  /*
171  * Exceptions.
172  */
173  // unfortunately following is not safe:
174  // TYPEDEF_ERR_INFO(EI_InputType, Type::TypeBase const *);
175 
176  /// General exception during conversion from JSON tree to storage.
177  TYPEDEF_ERR_INFO(EI_InputType, string );
178  TYPEDEF_ERR_INFO(EI_File, const string);
179  TYPEDEF_ERR_INFO(EI_Specification, const string);
180  TYPEDEF_ERR_INFO(EI_JSON_Type, const string);
181  TYPEDEF_ERR_INFO( EI_ErrorAddress, JSONPath);
182  DECLARE_INPUT_EXCEPTION( ExcInputError, << "Error in input file: " << EI_File::qval << " at address: " << EI_ErrorAddress::qval <<"\n"
183  << EI_Specification::val << "\n"
184  << "JSON type: " << EI_JSON_Type::qval << "\n"
185  << "Expected type:\n" << EI_InputType::val );
186 
187 
188  TYPEDEF_ERR_INFO( EI_JSONLine, unsigned int);
189  TYPEDEF_ERR_INFO( EI_JSONColumn, unsigned int);
190  TYPEDEF_ERR_INFO( EI_JSONReason, string);
191  DECLARE_INPUT_EXCEPTION( ExcNotJSONFormat, << "Not valid JSON file " << EI_File::qval << ". Error at line "
192  << EI_JSONLine::val << " : col " << EI_JSONColumn::val
193  << " ; reason: " << EI_JSONReason::val << "\n" );
194 
195 
196  /**
197  * Read a storage from input stream. Parameter @p root_type
198  * provides input type tree declaration. See @p read_from_stream for details.
199  */
200  JSONToStorage(istream &in, const Type::TypeBase &root_type);
201 
202  /**
203  * Read a storage from string (e.g. complex default value).
204  */
205  JSONToStorage( const string &default_str, const Type::TypeBase &root_type);
206 
207  /**
208  * Returns the root accessor. The template type \p T should correspond
209  * to the kind of the input type at root of the declaration tree.
210  */
211  template <class T>
212  T get_root_interface() const;
213 
214 
215 protected:
216 
217  /**
218  * Default constructor.
219  * Provides common initialization for public constructors.
220  */
221  JSONToStorage();
222 
223  /**
224  * This method actually reads the given stream \p in, checks the data just read against the declaration tree given by \p root_type, and
225  * store the data into private storage tree using \p StorageBase classes.
226  */
227  void read_stream(istream &in, const Type::TypeBase &root_type);
228 
229  /**
230  * Getter for root of the storage tree.
231  */
233  { return storage_;}
234 
235 
236  /**
237  * Check correctness of the input given by json_spirit node at head() of JSONPath @p p
238  * against type specification @p type. Die on input error (and return NULL).
239  * For correct input, creates the storage tree and returns pointer to its root node.
240  */
241  StorageBase * make_storage(JSONPath &p, const Type::TypeBase *type);
242 
243  StorageBase * make_storage(JSONPath &p, const Type::Record *record);
244  StorageBase * make_storage(JSONPath &p, const Type::AbstractRecord *abstr_rec);
245  StorageBase * make_storage(JSONPath &p, const Type::Array *array);
246 
248  StorageBase * make_storage(JSONPath &p, const Type::Selection *selection);
251  StorageBase * make_storage(JSONPath &p, const Type::Double *double_type);
252  StorageBase * make_storage(JSONPath &p, const Type::String *string_type);
253 
254  /**
255  * Dispatch according to @p type and create corresponding storage from the given string.
256  */
257  StorageBase * make_storage_from_default( const string &dflt_str, const Type::TypeBase *type);
258 
259 
260 
261  /// helper envelope for get_root_interface
262  //StorageArray *envelope;
263 
264  /// Storage of the read and checked input data
266 
267  /// Root of the declaration tree of the data in the storage.
269 
270  /**
271  * Names of all possible node types in parsed JSON tree provided by JSON Spirit library.
272  * Initialized in constructor.
273  *
274  */
276 
277  /**
278  * List of paths specifications of the keys that wasn't read by the JSON reader.
279  */
280  //vector<string> ignored_keys;
281 
282 };
283 
284 
285 
286 
287 
288 
289 
290 /********************************************88
291  * Implementation
292  */
293 
294 template <class T>
296 {
297  ASSERT(storage_, "NULL pointer to storage !!! \n");
298 
299  Address addr(storage_, root_type_);
300  // try to create an iterator just to check type
301  Iterator<T>( *root_type_, addr, 0);
302 
303  auto tmp_root_type = static_cast<const typename T::InputType &>(*root_type_);
304  return T( addr, tmp_root_type );
305 }
306 
307 
308 
309 
310 } // namespace Input
311 
312 
313 
314 #endif /* JSON_TO_STORAGE_HH_ */