diff options
Diffstat (limited to 'pjsip/include/pjsua2/persistent.hpp')
-rw-r--r-- | pjsip/include/pjsua2/persistent.hpp | 689 |
1 files changed, 689 insertions, 0 deletions
diff --git a/pjsip/include/pjsua2/persistent.hpp b/pjsip/include/pjsua2/persistent.hpp new file mode 100644 index 00000000..c086b01d --- /dev/null +++ b/pjsip/include/pjsua2/persistent.hpp @@ -0,0 +1,689 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJSUA2_PERSISTENT_HPP__ +#define __PJSUA2_PERSISTENT_HPP__ + +/** + * @file pjsua2/persistent.hpp + * @brief PJSUA2 Persistent Services + */ +#include <pjsua2/types.hpp> + +#include <string> +#include <vector> + +/** PJSUA2 API is inside pj namespace */ +namespace pj +{ + +/** + * @defgroup PJSUA2_PERSISTENT Persistent API + * @ingroup PJSUA2_Ref + * @{ + * The persistent API provides functionality to read/write data from/to + * a document (string or file). The data can be simple data types such + * as boolean, number, string, and string arrays, or a user defined object. + * Currently the implementation supports reading and writing from/to JSON + * document, but the framework allows application to extend the API to + * support other document formats. + */ + +using std::string; +using std::vector; + +/* Forward declaration for ContainerNode */ +class ContainerNode; + +/** + * This is the abstract base class of objects that can be serialized to/from + * persistent document. + */ +class PersistentObject +{ +public: + /** + * Read this object from a container node. + * + * @param node Container to read values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error) = 0; + + /** + * Write this object to a container node. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error) = 0; +}; + + +/** + * This a the abstract base class for a persistent document. A document + * is created either by loading from a string or a file, or by constructing + * it manually when writing data to it. The document then can be saved + * to either string or to a file. A document contains one root ContainerNode + * where all data are stored under. + * + * Document is read and written serially, hence the order of reading must be + * the same as the order of writing. The PersistentDocument class provides + * API to read and write to the root node, but for more flexible operations + * application can use the ContainerNode methods instead. Indeed the read + * and write API in PersistentDocument is just a shorthand which calls the + * relevant methods in the ContainerNode. As a tip, normally application only + * uses the readObject() and writeObject() methods declared here to read/write + * top level objects, and use the macros that are explained in ContainerNode + * documentation to read/write more detailed data. + */ +class PersistentDocument +{ +public: + /** + * Virtual destructor + */ + virtual ~PersistentDocument() + {} + + /** + * Load this document from a file. + * + * @param filename The file name. + */ + virtual void loadFile(const string &filename) throw(Error) = 0; + + /** + * Load this document from string. + * + * @param input The string. + */ + virtual void loadString(const string &input) throw(Error) = 0; + + /** + * Write this document to a file. + * + * @param filename The file name. + */ + virtual void saveFile(const string &filename) throw(Error) = 0; + + /** + * Write this document to string. + * + * @return The string document. + */ + virtual string saveString() throw(Error) = 0; + + /** + * Get the root container node for this document + * + * @return The root node. + */ + virtual ContainerNode & getRootContainer() const = 0; + + + /* + * Shorthand functions for reading and writing from/to the root container + */ + + + /** + * Determine if there is unread element. If yes, then app can use one of + * the readXxx() functions to read it. + * + * @return True if there is. + */ + bool hasUnread() const; + + /** + * Get the name of the next unread element. It will throw Error if there + * is no more element to read. + * + * @return The name of the next element . + */ + string unreadName() const throw(Error); + + /** + * Read an integer value from the document and return the value. + * This will throw Error if the current element is not a number. + * The read position will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + int readInt(const string &name="") const throw(Error); + + /** + * Read a float value from the document and return the value. + * This will throw Error if the current element is not a number. + * The read position will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + float readNumber(const string &name="") const throw(Error); + + /** + * Read a boolean value from the container and return the value. + * This will throw Error if the current element is not a boolean. + * The read position will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + bool readBool(const string &name="") const throw(Error); + + /** + * Read a string value from the container and return the value. + * This will throw Error if the current element is not a string. + * The read position will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + string readString(const string &name="") const throw(Error); + + /** + * Read a string array from the container. This will throw Error + * if the current element is not a string array. The read position + * will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + StringVector readStringVector(const string &name="") const + throw(Error); + + /** + * Read the specified object from the container. This is equal to + * calling PersistentObject.readObject(ContainerNode); + * + * @param obj The object to read. + */ + void readObject(PersistentObject &obj) const throw(Error); + + /** + * Read a container from the container. This will throw Error if the + * current element is not an object. The read position will be advanced + * to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return Container object. + */ + ContainerNode readContainer(const string &name="") const + throw(Error); + + /** + * Read array container from the container. This will throw Error if the + * current element is not an array. The read position will be advanced + * to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return Container object. + */ + ContainerNode readArray(const string &name="") const + throw(Error); + + /** + * Write a number value to the container. + * + * @param name The name for the value in the container. + * @param num The value to be written. + */ + void writeNumber(const string &name, + float num) throw(Error); + + /** + * Write a number value to the container. + * + * @param name The name for the value in the container. + * @param num The value to be written. + */ + void writeInt(const string &name, + int num) throw(Error); + + /** + * Write a boolean value to the container. + * + * @param name The name for the value in the container. + * @param value The value to be written. + */ + void writeBool(const string &name, + bool value) throw(Error); + + /** + * Write a string value to the container. + * + * @param name The name for the value in the container. + * @param value The value to be written. + */ + void writeString(const string &name, + const string &value) throw(Error); + + /** + * Write string vector to the container. + * + * @param name The name for the value in the container. + * @param arr The vector to be written. + */ + void writeStringVector(const string &name, + const StringVector &arr) + throw(Error); + + /** + * Write an object to the container. This is equal to calling + * PersistentObject.writeObject(ContainerNode); + * + * @param obj The object to be written + */ + void writeObject(const PersistentObject &obj) throw(Error); + + /** + * Create and write an empty Object node that can be used as parent + * for subsequent write operations. + * + * @param name The name for the new container in the container. + * + * @return A sub-container. + */ + ContainerNode writeNewContainer(const string &name) + throw(Error); + + /** + * Create and write an empty array node that can be used as parent + * for subsequent write operations. + * + * @param name The name for the array. + * + * @return A sub-container. + */ + ContainerNode writeNewArray(const string &name) + throw(Error); +}; + + +/** + * Forward declaration of container_node_op. + */ +struct container_node_op; + + +/** + * Internal data for ContainerNode. See ContainerNode implementation notes + * for more info. + */ +struct container_node_internal_data +{ + void *doc; /**< The document. */ + void *data1; /**< Internal data 1 */ + void *data2; /**< Internal data 2 */ +}; + +/** + * A container node is a placeholder for storing other data elements, which + * could be boolean, number, string, array of strings, or another container. + * Each data in the container is basically a name/value pair, with a type + * internally associated with it so that written data can be read in the + * correct type. Data is read and written serially, hence the order of + * reading must be the same as the order of writing. + * + * Application can read data from it by using the various read methods, and + * write data to it using the various write methods. Alternatively, it + * may be more convenient to use the provided macros below to read and write + * the data, because these macros set the name automatically: + * - NODE_READ_BOOL(node,item) + * - NODE_READ_UNSIGNED(node,item) + * - NODE_READ_INT(node,item) + * - NODE_READ_FLOAT(node,item) + * - NODE_READ_NUM_T(node,type,item) + * - NODE_READ_STRING(node,item) + * - NODE_READ_STRINGV(node,item) + * - NODE_READ_OBJ(node,item) + * - NODE_WRITE_BOOL(node,item) + * - NODE_WRITE_UNSIGNED(node,item) + * - NODE_WRITE_INT(node,item) + * - NODE_WRITE_FLOAT(node,item) + * - NODE_WRITE_NUM_T(node,type,item) + * - NODE_WRITE_STRING(node,item) + * - NODE_WRITE_STRINGV(node,item) + * - NODE_WRITE_OBJ(node,item) + * + * Implementation notes: + * + * The ContainerNode class is subclass-able, but not in the usual C++ way. + * With the usual C++ inheritance, some methods will be made pure virtual + * and must be implemented by the actual class. However, doing so will + * require dynamic instantiation of the ContainerNode class, which means + * we will need to pass around the class as pointer, for example as the + * return value of readContainer() and writeNewContainer() methods. Then + * we will need to establish who needs or how to delete these objects, or + * use shared pointer mechanism, each of which is considered too inconvenient + * or complicated for the purpose. + * + * So hence we use C style "inheritance", where the methods are declared in + * container_node_op and the data in container_node_internal_data structures. + * An implementation of ContainerNode class will need to set up these members + * with values that makes sense to itself. The methods in container_node_op + * contains the pointer to the actual implementation of the operation, which + * would be specific according to the format of the document. The methods in + * this ContainerNode class are just thin wrappers which call the + * implementation in the container_node_op structure. + * + */ +class ContainerNode +{ +public: + /** + * Determine if there is unread element. If yes, then app can use one of + * the readXxx() functions to read it. + */ + bool hasUnread() const; + + /** + * Get the name of the next unread element. + */ + string unreadName() const throw(Error); + + /** + * Read an integer value from the document and return the value. + * This will throw Error if the current element is not a number. + * The read position will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + int readInt(const string &name="") const throw(Error); + + /** + * Read a number value from the document and return the value. + * This will throw Error if the current element is not a number. + * The read position will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + float readNumber(const string &name="") const throw(Error); + + /** + * Read a boolean value from the container and return the value. + * This will throw Error if the current element is not a boolean. + * The read position will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + bool readBool(const string &name="") const throw(Error); + + /** + * Read a string value from the container and return the value. + * This will throw Error if the current element is not a string. + * The read position will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + string readString(const string &name="") const throw(Error); + + /** + * Read a string array from the container. This will throw Error + * if the current element is not a string array. The read position + * will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + StringVector readStringVector(const string &name="") const + throw(Error); + + /** + * Read the specified object from the container. This is equal to + * calling PersistentObject.readObject(ContainerNode); + * + * @param obj The object to read. + */ + void readObject(PersistentObject &obj) const throw(Error); + + /** + * Read a container from the container. This will throw Error if the + * current element is not a container. The read position will be advanced + * to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return Container object. + */ + ContainerNode readContainer(const string &name="") const + throw(Error); + + /** + * Read array container from the container. This will throw Error if the + * current element is not an array. The read position will be advanced + * to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return Container object. + */ + ContainerNode readArray(const string &name="") const + throw(Error); + + /** + * Write a number value to the container. + * + * @param name The name for the value in the container. + * @param num The value to be written. + */ + void writeNumber(const string &name, + float num) throw(Error); + + /** + * Write a number value to the container. + * + * @param name The name for the value in the container. + * @param num The value to be written. + */ + void writeInt(const string &name, + int num) throw(Error); + + /** + * Write a boolean value to the container. + * + * @param name The name for the value in the container. + * @param value The value to be written. + */ + void writeBool(const string &name, + bool value) throw(Error); + + /** + * Write a string value to the container. + * + * @param name The name for the value in the container. + * @param value The value to be written. + */ + void writeString(const string &name, + const string &value) throw(Error); + + /** + * Write string vector to the container. + * + * @param name The name for the value in the container. + * @param arr The vector to be written. + */ + void writeStringVector(const string &name, + const StringVector &arr) + throw(Error); + + /** + * Write an object to the container. This is equal to calling + * PersistentObject.writeObject(ContainerNode); + * + * @param obj The object to be written + */ + void writeObject(const PersistentObject &obj) throw(Error); + + /** + * Create and write an empty Object node that can be used as parent + * for subsequent write operations. + * + * @param name The name for the new container in the container. + * + * @return A sub-container. + */ + ContainerNode writeNewContainer(const string &name) + throw(Error); + + /** + * Create and write an empty array node that can be used as parent + * for subsequent write operations. + * + * @param name The name for the array. + * + * @return A sub-container. + */ + ContainerNode writeNewArray(const string &name) + throw(Error); + +public: + /* internal data */ + container_node_op *op; /**< Method table. */ + container_node_internal_data data; /**< Internal data */ +}; + + +/** + * Pointer to actual ContainerNode implementation. See ContainerNode + * implementation notes for more info. + */ +//! @cond Doxygen_Suppress +struct container_node_op +{ + bool (*hasUnread)(const ContainerNode*); + string (*unreadName)(const ContainerNode*) + throw(Error); + float (*readNumber)(const ContainerNode*, + const string&) + throw(Error); + bool (*readBool)(const ContainerNode*, + const string&) + throw(Error); + string (*readString)(const ContainerNode*, + const string&) + throw(Error); + StringVector (*readStringVector)(const ContainerNode*, + const string&) + throw(Error); + ContainerNode (*readContainer)(const ContainerNode*, + const string &) + throw(Error); + ContainerNode (*readArray)(const ContainerNode*, + const string &) + throw(Error); + void (*writeNumber)(ContainerNode*, + const string &name, + float num) + throw(Error); + void (*writeBool)(ContainerNode*, + const string &name, + bool value) + throw(Error); + void (*writeString)(ContainerNode*, + const string &name, + const string &value) + throw(Error); + void (*writeStringVector)(ContainerNode*, + const string &name, + const StringVector &value) + throw(Error); + ContainerNode (*writeNewContainer)(ContainerNode*, + const string &name) + throw(Error); + ContainerNode (*writeNewArray)(ContainerNode*, + const string &name) + throw(Error); +}; + +/* + * Convenient macros. + */ +#define NODE_READ_BOOL(node,item) item = node.readBool(#item) +#define NODE_READ_UNSIGNED(node,item) item = (unsigned)node.readNumber(#item) +#define NODE_READ_INT(node,item) item = (int) node.readNumber(#item) +#define NODE_READ_FLOAT(node,item) item = node.readNumber(#item) +#define NODE_READ_NUM_T(node,T,item) item = (T)(int)node.readNumber(#item) +#define NODE_READ_STRING(node,item) item = node.readString(#item) +#define NODE_READ_STRINGV(node,item) item = node.readStringVector(#item) +#define NODE_READ_OBJ(node,item) node.readObject(item) + +#define NODE_WRITE_BOOL(node,item) node.writeBool(#item, item) +#define NODE_WRITE_UNSIGNED(node,item) node.writeNumber(#item, (float)item) +#define NODE_WRITE_INT(node,item) node.writeNumber(#item, (float)item) +#define NODE_WRITE_NUM_T(node,T,item) node.writeNumber(#item, (float)item) +#define NODE_WRITE_FLOAT(node,item) node.writeNumber(#item, item) +#define NODE_WRITE_STRING(node,item) node.writeString(#item, item) +#define NODE_WRITE_STRINGV(node,item) node.writeStringVector(#item, item) +#define NODE_WRITE_OBJ(node,item) node.writeObject(item) + +//! @endcond + +/** + * @} PJSUA2 + */ + +} // namespace pj + + + +#endif /* __PJSUA2_PERSISTENT_HPP__ */ |