blob: c086b01d12caf792a94d3abe90d56299909bd7aa [file] [log] [blame]
Alexandre Lision67916dd2014-01-24 13:33:04 -05001/* $Id$ */
2/*
3 * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#ifndef __PJSUA2_PERSISTENT_HPP__
20#define __PJSUA2_PERSISTENT_HPP__
21
22/**
23 * @file pjsua2/persistent.hpp
24 * @brief PJSUA2 Persistent Services
25 */
26#include <pjsua2/types.hpp>
27
28#include <string>
29#include <vector>
30
31/** PJSUA2 API is inside pj namespace */
32namespace pj
33{
34
35/**
36 * @defgroup PJSUA2_PERSISTENT Persistent API
37 * @ingroup PJSUA2_Ref
38 * @{
39 * The persistent API provides functionality to read/write data from/to
40 * a document (string or file). The data can be simple data types such
41 * as boolean, number, string, and string arrays, or a user defined object.
42 * Currently the implementation supports reading and writing from/to JSON
43 * document, but the framework allows application to extend the API to
44 * support other document formats.
45 */
46
47using std::string;
48using std::vector;
49
50/* Forward declaration for ContainerNode */
51class ContainerNode;
52
53/**
54 * This is the abstract base class of objects that can be serialized to/from
55 * persistent document.
56 */
57class PersistentObject
58{
59public:
60 /**
61 * Read this object from a container node.
62 *
63 * @param node Container to read values from.
64 */
65 virtual void readObject(const ContainerNode &node) throw(Error) = 0;
66
67 /**
68 * Write this object to a container node.
69 *
70 * @param node Container to write values to.
71 */
72 virtual void writeObject(ContainerNode &node) const throw(Error) = 0;
73};
74
75
76/**
77 * This a the abstract base class for a persistent document. A document
78 * is created either by loading from a string or a file, or by constructing
79 * it manually when writing data to it. The document then can be saved
80 * to either string or to a file. A document contains one root ContainerNode
81 * where all data are stored under.
82 *
83 * Document is read and written serially, hence the order of reading must be
84 * the same as the order of writing. The PersistentDocument class provides
85 * API to read and write to the root node, but for more flexible operations
86 * application can use the ContainerNode methods instead. Indeed the read
87 * and write API in PersistentDocument is just a shorthand which calls the
88 * relevant methods in the ContainerNode. As a tip, normally application only
89 * uses the readObject() and writeObject() methods declared here to read/write
90 * top level objects, and use the macros that are explained in ContainerNode
91 * documentation to read/write more detailed data.
92 */
93class PersistentDocument
94{
95public:
96 /**
97 * Virtual destructor
98 */
99 virtual ~PersistentDocument()
100 {}
101
102 /**
103 * Load this document from a file.
104 *
105 * @param filename The file name.
106 */
107 virtual void loadFile(const string &filename) throw(Error) = 0;
108
109 /**
110 * Load this document from string.
111 *
112 * @param input The string.
113 */
114 virtual void loadString(const string &input) throw(Error) = 0;
115
116 /**
117 * Write this document to a file.
118 *
119 * @param filename The file name.
120 */
121 virtual void saveFile(const string &filename) throw(Error) = 0;
122
123 /**
124 * Write this document to string.
125 *
126 * @return The string document.
127 */
128 virtual string saveString() throw(Error) = 0;
129
130 /**
131 * Get the root container node for this document
132 *
133 * @return The root node.
134 */
135 virtual ContainerNode & getRootContainer() const = 0;
136
137
138 /*
139 * Shorthand functions for reading and writing from/to the root container
140 */
141
142
143 /**
144 * Determine if there is unread element. If yes, then app can use one of
145 * the readXxx() functions to read it.
146 *
147 * @return True if there is.
148 */
149 bool hasUnread() const;
150
151 /**
152 * Get the name of the next unread element. It will throw Error if there
153 * is no more element to read.
154 *
155 * @return The name of the next element .
156 */
157 string unreadName() const throw(Error);
158
159 /**
160 * Read an integer value from the document and return the value.
161 * This will throw Error if the current element is not a number.
162 * The read position will be advanced to the next element.
163 *
164 * @param name If specified, then the function will check if the
165 * name of the next element matches the specified
166 * name and throw Error if it doesn't match.
167 *
168 * @return The value.
169 */
170 int readInt(const string &name="") const throw(Error);
171
172 /**
173 * Read a float value from the document and return the value.
174 * This will throw Error if the current element is not a number.
175 * The read position will be advanced to the next element.
176 *
177 * @param name If specified, then the function will check if the
178 * name of the next element matches the specified
179 * name and throw Error if it doesn't match.
180 *
181 * @return The value.
182 */
183 float readNumber(const string &name="") const throw(Error);
184
185 /**
186 * Read a boolean value from the container and return the value.
187 * This will throw Error if the current element is not a boolean.
188 * The read position will be advanced to the next element.
189 *
190 * @param name If specified, then the function will check if the
191 * name of the next element matches the specified
192 * name and throw Error if it doesn't match.
193 *
194 * @return The value.
195 */
196 bool readBool(const string &name="") const throw(Error);
197
198 /**
199 * Read a string value from the container and return the value.
200 * This will throw Error if the current element is not a string.
201 * The read position will be advanced to the next element.
202 *
203 * @param name If specified, then the function will check if the
204 * name of the next element matches the specified
205 * name and throw Error if it doesn't match.
206 *
207 * @return The value.
208 */
209 string readString(const string &name="") const throw(Error);
210
211 /**
212 * Read a string array from the container. This will throw Error
213 * if the current element is not a string array. The read position
214 * will be advanced to the next element.
215 *
216 * @param name If specified, then the function will check if the
217 * name of the next element matches the specified
218 * name and throw Error if it doesn't match.
219 *
220 * @return The value.
221 */
222 StringVector readStringVector(const string &name="") const
223 throw(Error);
224
225 /**
226 * Read the specified object from the container. This is equal to
227 * calling PersistentObject.readObject(ContainerNode);
228 *
229 * @param obj The object to read.
230 */
231 void readObject(PersistentObject &obj) const throw(Error);
232
233 /**
234 * Read a container from the container. This will throw Error if the
235 * current element is not an object. The read position will be advanced
236 * to the next element.
237 *
238 * @param name If specified, then the function will check if the
239 * name of the next element matches the specified
240 * name and throw Error if it doesn't match.
241 *
242 * @return Container object.
243 */
244 ContainerNode readContainer(const string &name="") const
245 throw(Error);
246
247 /**
248 * Read array container from the container. This will throw Error if the
249 * current element is not an array. The read position will be advanced
250 * to the next element.
251 *
252 * @param name If specified, then the function will check if the
253 * name of the next element matches the specified
254 * name and throw Error if it doesn't match.
255 *
256 * @return Container object.
257 */
258 ContainerNode readArray(const string &name="") const
259 throw(Error);
260
261 /**
262 * Write a number value to the container.
263 *
264 * @param name The name for the value in the container.
265 * @param num The value to be written.
266 */
267 void writeNumber(const string &name,
268 float num) throw(Error);
269
270 /**
271 * Write a number value to the container.
272 *
273 * @param name The name for the value in the container.
274 * @param num The value to be written.
275 */
276 void writeInt(const string &name,
277 int num) throw(Error);
278
279 /**
280 * Write a boolean value to the container.
281 *
282 * @param name The name for the value in the container.
283 * @param value The value to be written.
284 */
285 void writeBool(const string &name,
286 bool value) throw(Error);
287
288 /**
289 * Write a string value to the container.
290 *
291 * @param name The name for the value in the container.
292 * @param value The value to be written.
293 */
294 void writeString(const string &name,
295 const string &value) throw(Error);
296
297 /**
298 * Write string vector to the container.
299 *
300 * @param name The name for the value in the container.
301 * @param arr The vector to be written.
302 */
303 void writeStringVector(const string &name,
304 const StringVector &arr)
305 throw(Error);
306
307 /**
308 * Write an object to the container. This is equal to calling
309 * PersistentObject.writeObject(ContainerNode);
310 *
311 * @param obj The object to be written
312 */
313 void writeObject(const PersistentObject &obj) throw(Error);
314
315 /**
316 * Create and write an empty Object node that can be used as parent
317 * for subsequent write operations.
318 *
319 * @param name The name for the new container in the container.
320 *
321 * @return A sub-container.
322 */
323 ContainerNode writeNewContainer(const string &name)
324 throw(Error);
325
326 /**
327 * Create and write an empty array node that can be used as parent
328 * for subsequent write operations.
329 *
330 * @param name The name for the array.
331 *
332 * @return A sub-container.
333 */
334 ContainerNode writeNewArray(const string &name)
335 throw(Error);
336};
337
338
339/**
340 * Forward declaration of container_node_op.
341 */
342struct container_node_op;
343
344
345/**
346 * Internal data for ContainerNode. See ContainerNode implementation notes
347 * for more info.
348 */
349struct container_node_internal_data
350{
351 void *doc; /**< The document. */
352 void *data1; /**< Internal data 1 */
353 void *data2; /**< Internal data 2 */
354};
355
356/**
357 * A container node is a placeholder for storing other data elements, which
358 * could be boolean, number, string, array of strings, or another container.
359 * Each data in the container is basically a name/value pair, with a type
360 * internally associated with it so that written data can be read in the
361 * correct type. Data is read and written serially, hence the order of
362 * reading must be the same as the order of writing.
363 *
364 * Application can read data from it by using the various read methods, and
365 * write data to it using the various write methods. Alternatively, it
366 * may be more convenient to use the provided macros below to read and write
367 * the data, because these macros set the name automatically:
368 * - NODE_READ_BOOL(node,item)
369 * - NODE_READ_UNSIGNED(node,item)
370 * - NODE_READ_INT(node,item)
371 * - NODE_READ_FLOAT(node,item)
372 * - NODE_READ_NUM_T(node,type,item)
373 * - NODE_READ_STRING(node,item)
374 * - NODE_READ_STRINGV(node,item)
375 * - NODE_READ_OBJ(node,item)
376 * - NODE_WRITE_BOOL(node,item)
377 * - NODE_WRITE_UNSIGNED(node,item)
378 * - NODE_WRITE_INT(node,item)
379 * - NODE_WRITE_FLOAT(node,item)
380 * - NODE_WRITE_NUM_T(node,type,item)
381 * - NODE_WRITE_STRING(node,item)
382 * - NODE_WRITE_STRINGV(node,item)
383 * - NODE_WRITE_OBJ(node,item)
384 *
385 * Implementation notes:
386 *
387 * The ContainerNode class is subclass-able, but not in the usual C++ way.
388 * With the usual C++ inheritance, some methods will be made pure virtual
389 * and must be implemented by the actual class. However, doing so will
390 * require dynamic instantiation of the ContainerNode class, which means
391 * we will need to pass around the class as pointer, for example as the
392 * return value of readContainer() and writeNewContainer() methods. Then
393 * we will need to establish who needs or how to delete these objects, or
394 * use shared pointer mechanism, each of which is considered too inconvenient
395 * or complicated for the purpose.
396 *
397 * So hence we use C style "inheritance", where the methods are declared in
398 * container_node_op and the data in container_node_internal_data structures.
399 * An implementation of ContainerNode class will need to set up these members
400 * with values that makes sense to itself. The methods in container_node_op
401 * contains the pointer to the actual implementation of the operation, which
402 * would be specific according to the format of the document. The methods in
403 * this ContainerNode class are just thin wrappers which call the
404 * implementation in the container_node_op structure.
405 *
406 */
407class ContainerNode
408{
409public:
410 /**
411 * Determine if there is unread element. If yes, then app can use one of
412 * the readXxx() functions to read it.
413 */
414 bool hasUnread() const;
415
416 /**
417 * Get the name of the next unread element.
418 */
419 string unreadName() const throw(Error);
420
421 /**
422 * Read an integer value from the document and return the value.
423 * This will throw Error if the current element is not a number.
424 * The read position will be advanced to the next element.
425 *
426 * @param name If specified, then the function will check if the
427 * name of the next element matches the specified
428 * name and throw Error if it doesn't match.
429 *
430 * @return The value.
431 */
432 int readInt(const string &name="") const throw(Error);
433
434 /**
435 * Read a number value from the document and return the value.
436 * This will throw Error if the current element is not a number.
437 * The read position will be advanced to the next element.
438 *
439 * @param name If specified, then the function will check if the
440 * name of the next element matches the specified
441 * name and throw Error if it doesn't match.
442 *
443 * @return The value.
444 */
445 float readNumber(const string &name="") const throw(Error);
446
447 /**
448 * Read a boolean value from the container and return the value.
449 * This will throw Error if the current element is not a boolean.
450 * The read position will be advanced to the next element.
451 *
452 * @param name If specified, then the function will check if the
453 * name of the next element matches the specified
454 * name and throw Error if it doesn't match.
455 *
456 * @return The value.
457 */
458 bool readBool(const string &name="") const throw(Error);
459
460 /**
461 * Read a string value from the container and return the value.
462 * This will throw Error if the current element is not a string.
463 * The read position will be advanced to the next element.
464 *
465 * @param name If specified, then the function will check if the
466 * name of the next element matches the specified
467 * name and throw Error if it doesn't match.
468 *
469 * @return The value.
470 */
471 string readString(const string &name="") const throw(Error);
472
473 /**
474 * Read a string array from the container. This will throw Error
475 * if the current element is not a string array. The read position
476 * will be advanced to the next element.
477 *
478 * @param name If specified, then the function will check if the
479 * name of the next element matches the specified
480 * name and throw Error if it doesn't match.
481 *
482 * @return The value.
483 */
484 StringVector readStringVector(const string &name="") const
485 throw(Error);
486
487 /**
488 * Read the specified object from the container. This is equal to
489 * calling PersistentObject.readObject(ContainerNode);
490 *
491 * @param obj The object to read.
492 */
493 void readObject(PersistentObject &obj) const throw(Error);
494
495 /**
496 * Read a container from the container. This will throw Error if the
497 * current element is not a container. The read position will be advanced
498 * to the next element.
499 *
500 * @param name If specified, then the function will check if the
501 * name of the next element matches the specified
502 * name and throw Error if it doesn't match.
503 *
504 * @return Container object.
505 */
506 ContainerNode readContainer(const string &name="") const
507 throw(Error);
508
509 /**
510 * Read array container from the container. This will throw Error if the
511 * current element is not an array. The read position will be advanced
512 * to the next element.
513 *
514 * @param name If specified, then the function will check if the
515 * name of the next element matches the specified
516 * name and throw Error if it doesn't match.
517 *
518 * @return Container object.
519 */
520 ContainerNode readArray(const string &name="") const
521 throw(Error);
522
523 /**
524 * Write a number value to the container.
525 *
526 * @param name The name for the value in the container.
527 * @param num The value to be written.
528 */
529 void writeNumber(const string &name,
530 float num) throw(Error);
531
532 /**
533 * Write a number value to the container.
534 *
535 * @param name The name for the value in the container.
536 * @param num The value to be written.
537 */
538 void writeInt(const string &name,
539 int num) throw(Error);
540
541 /**
542 * Write a boolean value to the container.
543 *
544 * @param name The name for the value in the container.
545 * @param value The value to be written.
546 */
547 void writeBool(const string &name,
548 bool value) throw(Error);
549
550 /**
551 * Write a string value to the container.
552 *
553 * @param name The name for the value in the container.
554 * @param value The value to be written.
555 */
556 void writeString(const string &name,
557 const string &value) throw(Error);
558
559 /**
560 * Write string vector to the container.
561 *
562 * @param name The name for the value in the container.
563 * @param arr The vector to be written.
564 */
565 void writeStringVector(const string &name,
566 const StringVector &arr)
567 throw(Error);
568
569 /**
570 * Write an object to the container. This is equal to calling
571 * PersistentObject.writeObject(ContainerNode);
572 *
573 * @param obj The object to be written
574 */
575 void writeObject(const PersistentObject &obj) throw(Error);
576
577 /**
578 * Create and write an empty Object node that can be used as parent
579 * for subsequent write operations.
580 *
581 * @param name The name for the new container in the container.
582 *
583 * @return A sub-container.
584 */
585 ContainerNode writeNewContainer(const string &name)
586 throw(Error);
587
588 /**
589 * Create and write an empty array node that can be used as parent
590 * for subsequent write operations.
591 *
592 * @param name The name for the array.
593 *
594 * @return A sub-container.
595 */
596 ContainerNode writeNewArray(const string &name)
597 throw(Error);
598
599public:
600 /* internal data */
601 container_node_op *op; /**< Method table. */
602 container_node_internal_data data; /**< Internal data */
603};
604
605
606/**
607 * Pointer to actual ContainerNode implementation. See ContainerNode
608 * implementation notes for more info.
609 */
610//! @cond Doxygen_Suppress
611struct container_node_op
612{
613 bool (*hasUnread)(const ContainerNode*);
614 string (*unreadName)(const ContainerNode*)
615 throw(Error);
616 float (*readNumber)(const ContainerNode*,
617 const string&)
618 throw(Error);
619 bool (*readBool)(const ContainerNode*,
620 const string&)
621 throw(Error);
622 string (*readString)(const ContainerNode*,
623 const string&)
624 throw(Error);
625 StringVector (*readStringVector)(const ContainerNode*,
626 const string&)
627 throw(Error);
628 ContainerNode (*readContainer)(const ContainerNode*,
629 const string &)
630 throw(Error);
631 ContainerNode (*readArray)(const ContainerNode*,
632 const string &)
633 throw(Error);
634 void (*writeNumber)(ContainerNode*,
635 const string &name,
636 float num)
637 throw(Error);
638 void (*writeBool)(ContainerNode*,
639 const string &name,
640 bool value)
641 throw(Error);
642 void (*writeString)(ContainerNode*,
643 const string &name,
644 const string &value)
645 throw(Error);
646 void (*writeStringVector)(ContainerNode*,
647 const string &name,
648 const StringVector &value)
649 throw(Error);
650 ContainerNode (*writeNewContainer)(ContainerNode*,
651 const string &name)
652 throw(Error);
653 ContainerNode (*writeNewArray)(ContainerNode*,
654 const string &name)
655 throw(Error);
656};
657
658/*
659 * Convenient macros.
660 */
661#define NODE_READ_BOOL(node,item) item = node.readBool(#item)
662#define NODE_READ_UNSIGNED(node,item) item = (unsigned)node.readNumber(#item)
663#define NODE_READ_INT(node,item) item = (int) node.readNumber(#item)
664#define NODE_READ_FLOAT(node,item) item = node.readNumber(#item)
665#define NODE_READ_NUM_T(node,T,item) item = (T)(int)node.readNumber(#item)
666#define NODE_READ_STRING(node,item) item = node.readString(#item)
667#define NODE_READ_STRINGV(node,item) item = node.readStringVector(#item)
668#define NODE_READ_OBJ(node,item) node.readObject(item)
669
670#define NODE_WRITE_BOOL(node,item) node.writeBool(#item, item)
671#define NODE_WRITE_UNSIGNED(node,item) node.writeNumber(#item, (float)item)
672#define NODE_WRITE_INT(node,item) node.writeNumber(#item, (float)item)
673#define NODE_WRITE_NUM_T(node,T,item) node.writeNumber(#item, (float)item)
674#define NODE_WRITE_FLOAT(node,item) node.writeNumber(#item, item)
675#define NODE_WRITE_STRING(node,item) node.writeString(#item, item)
676#define NODE_WRITE_STRINGV(node,item) node.writeStringVector(#item, item)
677#define NODE_WRITE_OBJ(node,item) node.writeObject(item)
678
679//! @endcond
680
681/**
682 * @} PJSUA2
683 */
684
685} // namespace pj
686
687
688
689#endif /* __PJSUA2_PERSISTENT_HPP__ */