diff --git a/.gitignore b/.gitignore index 998b2b701fc21b88115143c8a36aca2f34ebd35d..e1a5fc4c1d536089f9d655ec80dadcbc218dcd56 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ .DS_Store generatedsrc build -docs +docs/doxygen_generated lib test/lib report diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..6359fd891778d27722ff758ccedbcccc9ea0f3c8 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,3 @@ +doxygen_generated +.*.sw? + diff --git a/docs/ipaacaCpp.Doxyfile b/docs/ipaacaCpp.Doxyfile new file mode 100644 index 0000000000000000000000000000000000000000..5d3a150a1c15d1962e4895bca852a8afe770173b --- /dev/null +++ b/docs/ipaacaCpp.Doxyfile @@ -0,0 +1,2362 @@ +# Doxyfile 1.8.9.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "IPAACA-C++" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = "Revision 12 (Protocol 2.0)" + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "IPAACA - Incremental Processing Architecture for Artificial Conversational Agents (C++ version)" + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = doxygen_generated + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = ../ipaacalib/cpp/ + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = YES # user-set by ryaghoub from NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if <section_label> ... \endif and \cond <section_label> +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = ../ipaacalib/cpp/include ../ipaacalib/cpp/src # user-set by ryaghoub from empty + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES # user-set by ryaghoub from NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = ../ipaacalib/cpp/include/rapidjson # user-set by ryaghoub from empty + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# <filter> <input-file> +# +# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = YES # user-set by ryaghoub from NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = YES # user-set by ryaghoub from NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES # user-set by ryaghoub from NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use <access key> + S +# (what the <access key> is depends on the OS and browser, but it is typically +# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down +# key> to jump into the search results window, the results can be navigated +# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel +# the search. The filter options can be selected when the cursor is inside the +# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys> +# to select a filter and <Enter> or <escape> to activate or cancel the filter +# option. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. There +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain the +# search results. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: http://xapian.org/). +# +# See the section "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will return the search results when EXTERNAL_SEARCH is enabled. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: http://xapian.org/). See the section "External Indexing and +# Searching" for details. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. +# The default file is: searchdata.xml. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of +# to a relative location where the documentation can be found. The format is: +# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. +# The default value is: YES. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. +# +# Note that when enabling USE_PDFLATEX this option is only used for generating +# bitmaps for formulas in the HTML output, but not in the Makefile that is +# written to the output directory. +# The default file is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate +# index for LaTeX. +# The default file is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used by the +# printer. +# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x +# 14 inches) and executive (7.25 x 10.5 inches). +# The default value is: a4. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names +# that should be included in the LaTeX output. To get the times font for +# instance you can specify +# EXTRA_PACKAGES=times +# If left blank no extra packages will be included. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the +# generated LaTeX document. The header should contain everything until the first +# chapter. If it is left blank doxygen will generate a standard header. See +# section "Doxygen usage" for information on how to let doxygen write the +# default header to a separate file. +# +# Note: Only use a user-defined header if you know what you are doing! The +# following commands have a special meaning inside the header: $title, +# $datetime, $date, $doxygenversion, $projectname, $projectnumber, +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty +# string, for the replacement values of the other commands the user is referred +# to HTML_HEADER. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the +# generated LaTeX document. The footer should contain everything after the last +# chapter. If it is left blank doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. +# +# Note: Only use a user-defined footer if you know what you are doing! +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_FOOTER = + +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the LATEX_OUTPUT output +# directory. Note that the files will be copied as-is; there are no commands or +# markers available. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_FILES = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is +# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will +# contain links (just like the HTML output) instead of page references. This +# makes the output suitable for online browsing using a PDF viewer. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate +# the PDF file directly from the LaTeX files. Set this option to YES, to get a +# higher quality PDF documentation. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode +# command to the generated LaTeX files. This will instruct LaTeX to keep running +# if errors occur, instead of asking the user for help. This option is also used +# when generating formulas in HTML. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BATCHMODE = NO + +# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# index chapters (such as File Index, Compound Index, etc.) in the output. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HIDE_INDICES = NO + +# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source +# code with syntax highlighting in the LaTeX output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. See +# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# The default value is: plain. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The +# RTF output is optimized for Word 97 and may not look too pretty with other RTF +# readers/editors. +# The default value is: NO. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: rtf. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will +# contain hyperlink fields. The RTF file will contain links (just like the HTML +# output) instead of page references. This makes the output suitable for online +# browsing using Word or some other Word compatible readers that support those +# fields. +# +# Note: WordPad (write) and others do not support links. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's config +# file, i.e. a series of assignments. You only have to provide replacements, +# missing definitions are set to their default value. +# +# See also section "Doxygen usage" for information on how to generate the +# default style sheet that doxygen normally uses. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an RTF document. Syntax is +# similar to doxygen's config file. A template extensions file can be generated +# using doxygen -e rtf extensionFile. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_EXTENSIONS_FILE = + +# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code +# with syntax highlighting in the RTF output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for +# classes and files. +# The default value is: NO. + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. A directory man3 will be created inside the directory specified by +# MAN_OUTPUT. +# The default directory is: man. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to the generated +# man pages. In case the manual section does not start with a number, the number +# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is +# optional. +# The default value is: .3. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_EXTENSION = .3 + +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + +# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# will generate one additional man file for each entity documented in the real +# man page(s). These additional files only source the real man page, but without +# them the man command would be unable to find the correct page. +# The default value is: NO. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that +# captures the structure of the code including all documentation. +# The default value is: NO. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: xml. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_OUTPUT = xml + +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program +# listings (including syntax highlighting and cross-referencing information) to +# the XML output. Note that enabling this will significantly increase the size +# of the XML output. +# The default value is: YES. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- + +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files +# that can be used to generate PDF. +# The default value is: NO. + +GENERATE_DOCBOOK = NO + +# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. +# The default directory is: docbook. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_OUTPUT = docbook + +# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the +# program listings (including syntax highlighting and cross-referencing +# information) to the DOCBOOK output. Note that enabling this will significantly +# increase the size of the DOCBOOK output. +# The default value is: NO. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_PROGRAMLISTING = NO + +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see http://autogen.sf.net) file that captures the +# structure of the code including all documentation. Note that this feature is +# still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module +# file that captures the structure of the code including all documentation. +# +# Note that this feature is still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary +# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI +# output from the Perl module output. +# The default value is: NO. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely +# formatted so it can be parsed by a human reader. This is useful if you want to +# understand what is going on. On the other hand, if this tag is set to NO, the +# size of the Perl module output will be much smaller and Perl will parse it +# just the same. +# The default value is: YES. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file are +# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful +# so different doxyrules.make files included by the same Makefile don't +# overwrite each other's variables. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all +# C-preprocessor directives found in the sources and include files. +# The default value is: YES. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be +# performed. Macro expansion can be done in a controlled way by setting +# EXPAND_ONLY_PREDEF to YES. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +MACRO_EXPANSION = YES # user-set by ryaghoub from NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then +# the macro expansion is limited to the macros specified with the PREDEFINED and +# EXPAND_AS_DEFINED tags. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_ONLY_PREDEF = YES # user-set by ryaghoub from NO + +# If the SEARCH_INCLUDES tag is set to YES, the include files in the +# INCLUDE_PATH will be searched if a #include is found. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by the +# preprocessor. +# This tag requires that the tag SEARCH_INCLUDES is set to YES. + +INCLUDE_PATH = ../ipaacalib/cpp/include # user-set by ryaghoub from empty + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will be +# used. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that are +# defined before the preprocessor is started (similar to the -D option of e.g. +# gcc). The argument of the tag is a list of macros of the form: name or +# name=definition (no spaces). If the definition and the "=" are omitted, "=1" +# is assumed. To prevent a macro definition from being undefined via #undef or +# recursively expanded use the := operator instead of the = operator. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +PREDEFINED = IPAACA_EXPORT="" IPAACA_HEADER_EXPORT="" IPAACA_MEMBER_VAR_EXPORT="" _IPAACA_ABSTRACT_="" _IPAACA_OVERRIDE_="override" + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this +# tag can be used to specify a list of macro names that should be expanded. The +# macro definition that is found in the sources will be used. Use the PREDEFINED +# tag if you want to use a different macro definition that overrules the +# definition found in the source code. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not +# removed. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tag files. For each tag +# file the location of the external documentation should be added. The format of +# a tag file without this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where loc1 and loc2 can be relative or absolute paths or URLs. See the +# section "Linking to external documentation" for more information about the use +# of tag files. +# Note: Each tag file must have a unique name (where the name does NOT include +# the path). If a tag file is not located in the directory in which doxygen is +# run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# tag file that is based on the input files it reads. See section "Linking to +# external documentation" for more information about the usage of tag files. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES, all external class will be listed in +# the class index. If set to NO, only the inherited external classes will be +# listed. +# The default value is: NO. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will be +# listed. +# The default value is: YES. + +EXTERNAL_GROUPS = YES + +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in +# the related pages index. If set to NO, only the current project's pages will +# be listed. +# The default value is: YES. + +EXTERNAL_PAGES = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of 'which perl'). +# The default file (with absolute path) is: /usr/bin/perl. + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram +# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to +# NO turns the diagrams off. Note that this option also works with HAVE_DOT +# disabled, but it is recommended to install and use dot, since it yields more +# powerful graphs. +# The default value is: YES. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see: +# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. + +DIA_PATH = + +# If set to YES the inheritance and collaboration graphs will hide inheritance +# and usage relations if the target is undocumented or is not a class. +# The default value is: YES. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz (see: +# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# Bell Labs. The other options in this section have no effect if this option is +# set to NO +# The default value is: NO. + +HAVE_DOT = YES # user-set by ryaghoub from NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed +# to run in parallel. When set to 0 doxygen will base this on the number of +# processors available in the system. You can set it explicitly to a value +# larger than 0 to get control over the balance between CPU load and processing +# speed. +# Minimum value: 0, maximum value: 32, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NUM_THREADS = 0 + +# When you want a differently looking font in the dot files that doxygen +# generates you can specify the font name using DOT_FONTNAME. You need to make +# sure dot is able to find the font, which can be done by putting it in a +# standard location or by setting the DOTFONTPATH environment variable or by +# setting DOT_FONTPATH to the directory containing the font. +# The default value is: Helvetica. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of +# dot graphs. +# Minimum value: 4, maximum value: 24, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the default font as specified with +# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set +# the path where dot can find it using this tag. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTPATH = + +# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for +# each documented class showing the direct and indirect inheritance relations. +# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# graph for each documented class showing the direct and indirect implementation +# dependencies (inheritance, containment, and class references variables) of the +# class with other documented classes. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for +# groups, showing the direct groups dependencies. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside the +# class node. If there are many fields or methods and many nodes the graph may +# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the +# number of items for each type to make the size more manageable. Set this to 0 +# for no limit. Note that the threshold may be exceeded by 50% before the limit +# is enforced. So when you set the threshold to 10, up to 15 fields may appear, +# but if the number exceeds 15, the total amount of fields shown is limited to +# 10. +# Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LIMIT_NUM_FIELDS = 10 + +# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and +# collaboration graphs will show the relations between templates and their +# instances. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +TEMPLATE_RELATIONS = NO + +# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to +# YES then doxygen will generate a graph for each documented file showing the +# direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDE_GRAPH = YES + +# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are +# set to YES then doxygen will generate a graph for each documented file showing +# the direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# hierarchy of all classes instead of a textual one. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# dependencies a directory has on other directories in a graphical way. The +# dependency relations are determined by the #include relations between the +# files in the directories. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. +# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order +# to make the SVG files visible in IE 9+ (other browsers do not have this +# requirement). +# Possible values are: png, jpg, gif and svg. +# The default value is: png. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# +# Note that this requires a modern browser other than Internet Explorer. Tested +# and working are Firefox, Chrome, Safari, and Opera. +# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make +# the SVG files visible. Older versions of IE do not have SVG support. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +INTERACTIVE_SVG = NO + +# The DOT_PATH tag can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the \dotfile +# command). +# This tag requires that the tag HAVE_DOT is set to YES. + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = + +# The DIAFILE_DIRS tag can be used to specify one or more directories that +# contain dia files that are included in the documentation (see the \diafile +# command). + +DIAFILE_DIRS = + +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file. If left blank, it is assumed +# PlantUML is not used or called during a preprocessing step. Doxygen will +# generate a warning when it encounters a \startuml command in this case and +# will not generate output for the diagram. + +PLANTUML_JAR_PATH = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes +# that will be shown in the graph. If the number of nodes in a graph becomes +# larger than this value, doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that doxygen if the number of direct +# children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that +# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. +# Minimum value: 0, maximum value: 10000, default value: 50. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs +# generated by dot. A depth value of 3 means that only nodes reachable from the +# root by following a path via at most 3 edges will be shown. Nodes that lay +# further from the root node will be omitted. Note that setting this option to 1 +# or 2 may greatly reduce the computation time needed for large code bases. Also +# note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. +# Minimum value: 0, maximum value: 1000, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not seem +# to support this out of the box. +# +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) support +# this, this feature is disabled by default. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# explaining the meaning of the various boxes and arrows in the dot generated +# graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# files that are used to generate the various graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_CLEANUP = YES diff --git a/docs/Photo 12.12.11 14 46 22.jpg b/docs/notes/Photo 12.12.11 14 46 22.jpg similarity index 100% rename from docs/Photo 12.12.11 14 46 22.jpg rename to docs/notes/Photo 12.12.11 14 46 22.jpg diff --git a/docs/Photo 14.11.11 14 14 03.jpg b/docs/notes/Photo 14.11.11 14 14 03.jpg similarity index 100% rename from docs/Photo 14.11.11 14 14 03.jpg rename to docs/notes/Photo 14.11.11 14 14 03.jpg diff --git a/docs/Photo 14.11.11 14 14 27.jpg b/docs/notes/Photo 14.11.11 14 14 27.jpg similarity index 100% rename from docs/Photo 14.11.11 14 14 27.jpg rename to docs/notes/Photo 14.11.11 14 14 27.jpg diff --git a/docs/Photo 18.11.11 13 29 58.jpg b/docs/notes/Photo 18.11.11 13 29 58.jpg similarity index 100% rename from docs/Photo 18.11.11 13 29 58.jpg rename to docs/notes/Photo 18.11.11 13 29 58.jpg diff --git a/docs/Photo 21.11.11 13 09 54.jpg b/docs/notes/Photo 21.11.11 13 09 54.jpg similarity index 100% rename from docs/Photo 21.11.11 13 09 54.jpg rename to docs/notes/Photo 21.11.11 13 09 54.jpg diff --git a/docs/Tafelbild-2011-07-25.JPG b/docs/notes/Tafelbild-2011-07-25.JPG similarity index 100% rename from docs/Tafelbild-2011-07-25.JPG rename to docs/notes/Tafelbild-2011-07-25.JPG diff --git a/docs/Tafelbild-2011-07-27-Ramin.jpeg b/docs/notes/Tafelbild-2011-07-27-Ramin.jpeg similarity index 100% rename from docs/Tafelbild-2011-07-27-Ramin.jpeg rename to docs/notes/Tafelbild-2011-07-27-Ramin.jpeg diff --git a/docs/Tafelbild-2011-07-27-Stefans-Impuls.jpeg b/docs/notes/Tafelbild-2011-07-27-Stefans-Impuls.jpeg similarity index 100% rename from docs/Tafelbild-2011-07-27-Stefans-Impuls.jpeg rename to docs/notes/Tafelbild-2011-07-27-Stefans-Impuls.jpeg diff --git a/docs/Tafelbild-2011-7-27-Hendrik.jpeg b/docs/notes/Tafelbild-2011-7-27-Hendrik.jpeg similarity index 100% rename from docs/Tafelbild-2011-7-27-Hendrik.jpeg rename to docs/notes/Tafelbild-2011-7-27-Hendrik.jpeg diff --git a/docs/Tech-ComponentNotify-1.pdf b/docs/notes/Tech-ComponentNotify-1.pdf similarity index 100% rename from docs/Tech-ComponentNotify-1.pdf rename to docs/notes/Tech-ComponentNotify-1.pdf diff --git a/docs/skizze-hendrik.pdf b/docs/notes/skizze-hendrik.pdf similarity index 100% rename from docs/skizze-hendrik.pdf rename to docs/notes/skizze-hendrik.pdf diff --git a/ipaacalib/cpp/CMakeLists.txt b/ipaacalib/cpp/CMakeLists.txt index 037c76e684e20cbdc2b4f682aaf054f61a52dd11..688725b7a861ba801a9b2fd3b1f73c67f78b87cb 100644 --- a/ipaacalib/cpp/CMakeLists.txt +++ b/ipaacalib/cpp/CMakeLists.txt @@ -3,63 +3,127 @@ cmake_minimum_required (VERSION 2.6) # project name project (ipaaca_cpp) +# use C++11 (starting with proto v2 / ipaaca-c++ release 12) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + ## use the following line to enable console debug messages in ipaaca set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DIPAACA_DEBUG_MESSAGES") # expose the full RSB api in the headers (set only in ipaaca itself) +# !! NOTE: at the moment required in any ipaaca cpp project in Windows !! set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DIPAACA_EXPOSE_FULL_RSB_API") # find cmake modules locally too -set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules) - -find_package(Boost COMPONENTS system filesystem thread regex REQUIRED) -link_directories(${Boost_LIBRARY_DIRS}) -include_directories(${Boost_INCLUDE_DIRS}) -#set(BOOSTLIBS boost_regex-mt boost_date_time-mt boost_program_options-mt boost_thread-mt boost_filesystem-mt boost_signals-mt boost_system-mt) - -find_package(Protobuf REQUIRED) -link_directories(${PROTOBUF_LIBRARY_DIRS}) -include_directories(${PROTOBUF_INCLUDE_DIRS}) +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules ) + +if(WIN32) # Check if we are on Windows + if(MSVC) # Check if we are using the Visual Studio compiler + #set_target_properties(TestProject PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") + # + # Setup section for Windows build (using precompiled rsb + deps) + # + # You need the rsx precompiled archive, even if you build rsb yourself, + # for the dependencies. Make sure to grab the right version (bitness + # and Visual Studio version). Tested with the rsx-0.10 branch. + # Please unpack the rsx archive into the repo dir (where ipaaca also is). + # Then set these environment variables before building rsb or ipaaca: + # + # set BOOST_ROOT=%SOA_REPO_DIR%\rsx\boost + # set PROTOBUF_ROOT=%SOA_REPO_DIR%\rsx\protobuf + # set SPREAD_ROOT=%SOA_REPO_DIR%\rsx\spread + # + # + + # + # If you want to compile rsb locally, check out the soa project 'rsb', build it + # and use resolve.sh to pull its libraries into this project. + # On the other hand, if you simply want to use the precompiled rsb from inside + # "rsx" (it works but has no debug info), uncomment the following four lines. + # + include_directories( ${PROJECT_SOURCE_DIR}/../../../rsx/RSC-0.11.0-win32/include/rsc0.11 ) + include_directories( ${PROJECT_SOURCE_DIR}/../../../rsx/RSB-0.11.2-win32/include/rsb0.11 ) + link_directories( ${PROJECT_SOURCE_DIR}/../../../rsx/RSC-0.11.0-win32/lib ) + link_directories( ${PROJECT_SOURCE_DIR}/../../../rsx/RSB-0.11.2-win32/lib ) + + set(RSBLIBS rsc0.11 rsb0.11) + set(LIBS ${LIBS} rpcrt4) + + # Using custom Protobuf script (from rsc) because it honors PROTOBUF_ROOT + find_package(ProtocolBuffers REQUIRED) + link_directories(${PROTOBUF_LIBRARY_DIRS}) + include_directories(${PROTOBUF_INCLUDE_DIRS}) + + find_package(Boost COMPONENTS date_time program_options system filesystem thread signals regex REQUIRED) + link_directories(${Boost_LIBRARY_DIRS}) + include_directories(${Boost_INCLUDE_DIRS}) + + # Windows linkage hack: overriding the determined libs to remove boost_thread (causes multiple-definition issues) + set(CORRECT_BOOST_LIBS "") + foreach(BLIB ${Boost_LIBRARIES}) + #message(STATUS "Boost lib: ${BLIB}") + string(REGEX MATCH "boost_thread[^/]+$" drop_item ${BLIB}) + if(drop_item) + message(STATUS "(Windows hack:) Removing boost_thread library from the linkage list.") + else(drop_item) + list(APPEND CORRECT_BOOST_LIBS ${BLIB}) + endif(drop_item) + endforeach(BLIB ${Boost_LIBRARIES}) + set(Boost_LIBRARIES ${CORRECT_BOOST_LIBS}) + + else() + message(SEND_ERROR "Unsupported compiler! Please build with MSVC (2010).") + endif() +else() + # + # + # Setup section for Linux or OS X (using 'rsb' soa project) + # + # + find_package(Boost COMPONENTS system filesystem thread regex signals REQUIRED) + link_directories(${Boost_LIBRARY_DIRS}) + include_directories(${Boost_INCLUDE_DIRS}) + #set(BOOSTLIBS boost_regex-mt boost_date_time-mt boost_program_options-mt boost_thread-mt boost_filesystem-mt boost_signals-mt boost_system-mt) + + find_package(ProtocolBuffers REQUIRED) + link_directories(${PROTOBUF_LIBRARY_DIRS}) + include_directories(${PROTOBUF_INCLUDE_DIRS}) + + # change for each new rsb version + if (DEFINED APPLE) + set(RSBLIBS rsc0.11 rsb0.11) + #set(RSBLIBS rsc0.10 rsb.0.10) + else(DEFINED APPLE) + set(RSBLIBS ${PROJECT_SOURCE_DIR}/../../deps/lib/librsc0.11.so ${PROJECT_SOURCE_DIR}/../../deps/lib/librsb0.11.so ) + set(LIBS ${LIBS} uuid) + endif(DEFINED APPLE) + # enhance the default search paths (headers, libs ...) + set(CMAKE_PREFIX_PATH ${PROJECT_SOURCE_DIR}:/opt/local:${CMAKE_PREFIX_PATH}) + # MacPorts compatibility + if (DEFINED APPLE) + message(STATUS "Adding extra options for building on Mac OS X") + set(CXX_DEFINES "${CXX_DEFINES} -D__MACOSX__") + link_directories( /opt/local/lib ) + include_directories( /opt/local/include ) + endif(DEFINED APPLE) +endif(WIN32) -##set(PROTOLIBS protobuf) -#set(RSBLIBS rsc rsbcore) -# add for for each new rsb version -include_directories( ${PROJECT_SOURCE_DIR}/../../deps/include/rsc0.9 ) -include_directories( ${PROJECT_SOURCE_DIR}/../../deps/include/rsb0.9 ) -# change for each new rsb version -if (DEFINED APPLE) -set(RSBLIBS rsc0.9 rsb.0.9) -else(DEFINED APPLE) -set(RSBLIBS ${PROJECT_SOURCE_DIR}/../../deps/lib/librsc0.9.so ${PROJECT_SOURCE_DIR}/../../deps/lib/librsb.so.0.9 ) -endif(DEFINED APPLE) - -#set(LIBS ${LIBS} ${BOOSTLIBS} ${PROTOLIBS} ${RSBLIBS}) -#set(LIBS ${LIBS} ${PROTOLIBS} ${RSBLIBS}) set(LIBS ${LIBS} ${PROTOBUF_LIBRARY} ${Boost_LIBRARIES} ${RSBLIBS}) -if (NOT DEFINED APPLE) - set(LIBS ${LIBS} uuid) -endif(NOT DEFINED APPLE) - -# enhance the default search paths (headers, libs ...) -set(CMAKE_PREFIX_PATH ${PROJECT_SOURCE_DIR}:/opt/local:${CMAKE_PREFIX_PATH}) - -## Ace2 uses deprecated style (non-template friend functions and non-const char*s) -## We just hide the warnings here -#set(CXX_OLD_CODE_CONVENIENCE_FLAGS "-Wno-non-template-friend -Wno-write-strings") +# Hide the rsb-induced boost-signals warning (FOR NOW) +set(IPAACA_CXX_DEFINES "${IPAACA_CXX_DEFINES} -DBOOST_SIGNALS_NO_DEPRECATION_WARNING") # Compiler defines copied from the old build system -set(CXX_DEFINES "-D_BSD_SOURCE -DUSE_AV -DMGC_USE_DOUBLE -DLEDA_PREFIX -D__NO_CAST_TO_LOCAL_TYPE__ -DDBGLVL=0") -if (DEFINED APPLE) - message(STATUS "Adding extra options for building on Mac OS X") - set(CXX_DEFINES "${CXX_DEFINES} -D__MACOSX__") - link_directories( /opt/local/lib ) - include_directories( /opt/local/include ) -endif(DEFINED APPLE) +set(IPAACA_CXX_DEFINES "${IPAACA_CXX_DEFINES} -D_BSD_SOURCE -DUSE_AV -DMGC_USE_DOUBLE -DLEDA_PREFIX -D__NO_CAST_TO_LOCAL_TYPE__ -DDBGLVL=0") # Combine the extra compiler flags -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_OLD_CODE_CONVENIENCE_FLAGS} ${CXX_DEFINES}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_OLD_CODE_CONVENIENCE_FLAGS} ${IPAACA_CXX_DEFINES}") + +# add for for each new rsb version +include_directories( ${PROJECT_SOURCE_DIR}/../../deps/include/rsc0.11 ) +include_directories( ${PROJECT_SOURCE_DIR}/../../deps/include/rsb0.11 ) +#include_directories( ${PROJECT_SOURCE_DIR}/../../deps/include/rsc0.10 ) +#include_directories( ${PROJECT_SOURCE_DIR}/../../deps/include/rsb0.10 ) # add include dir for auto-generated headers placed in build/ include_directories( ${PROJECT_SOURCE_DIR}/build ) @@ -74,17 +138,47 @@ link_directories( ${PROJECT_SOURCE_DIR}/../../deps/lib ) # specify source files for ipaaca (auto-generated ones are in build/ ) set (SOURCE src/ipaaca.cc + src/ipaaca-buffers.cc + src/ipaaca-internal.cc + src/ipaaca-iuinterface.cc + src/ipaaca-ius.cc + src/ipaaca-links.cc + src/ipaaca-locking.cc + src/ipaaca-payload.cc src/ipaaca-cmdline-parser.cc src/ipaaca-string-utils.cc src/util/notifier.cc build/ipaaca/ipaaca.pb.cc ) +set (JSON_TEST_SOURCE + src/ipaaca.cc + src/ipaaca-buffers.cc + src/ipaaca-fake.cc + src/ipaaca-internal.cc + src/ipaaca-iuinterface.cc + src/ipaaca-json.cc # main + src/ipaaca-locking.cc + src/ipaaca-links.cc + src/ipaaca-payload.cc + src/ipaaca-cmdline-parser.cc + src/ipaaca-string-utils.cc + # more stuff going beyond the fake test case + src/ipaaca-ius.cc + build/ipaaca/ipaaca.pb.cc + ) + + + + # compile all files to "ipaaca" shared library add_library(ipaaca SHARED ${SOURCE}) # and link all the required external libs (found above using find_package etc.) target_link_libraries(ipaaca ${LIBS}) +add_executable (ipaaca-test-json ${JSON_TEST_SOURCE}) +target_link_libraries (ipaaca-test-json ${LIBS}) + set(DEFAULT_BIN_SUBDIR bin) set(DEFAULT_LIB_SUBDIR lib) set(DEFAULT_DATA_SUBDIR share/data) @@ -98,12 +192,19 @@ install ( ) install( DIRECTORY include - DESTINATION / + DESTINATION . FILES_MATCHING PATTERN "*.h" PATTERN "*.hh" PATTERN "*.hpp" PATTERN "*.inl" ) install( FILES build/ipaaca/ipaaca.pb.h - DESTINATION /include/ipaaca/ + DESTINATION include/ipaaca/ + ) + +install ( + TARGETS ipaaca-test-json + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib ) diff --git a/ipaacalib/cpp/CMakeModules/FindProtocolBuffers.cmake b/ipaacalib/cpp/CMakeModules/FindProtocolBuffers.cmake new file mode 100644 index 0000000000000000000000000000000000000000..997c78f4cb6d2d32f4cb1055b359f1d1e4d4ac52 --- /dev/null +++ b/ipaacalib/cpp/CMakeModules/FindProtocolBuffers.cmake @@ -0,0 +1,484 @@ +# Locate and configure the Google Protocol Buffers library. +# A modified version of the original macro from CMake 2.8. +# Defines the following variables: +# +# PROTOBUF_FOUND - Found the Google Protocol Buffers library +# PROTOBUF_INCLUDE_DIRS - Include directories for Google Protocol Buffers +# PROTOBUF_LIBRARIES - The protobuf library +# +# The following cache variables are also defined: +# PROTOBUF_LIBRARY - The protobuf library +# PROTOBUF_PROTOC_LIBRARY - The protoc library +# PROTOBUF_INCLUDE_DIR - The include directory for protocol buffers +# PROTOBUF_PROTOC_EXECUTABLE - The protoc compiler +# +# These variables are read for additional hints: +# PROTOBUF_ROOT - Root directory of the protobuf installation if not found +# automatically +# +# ==================================================================== +# Example: +# +# find_package(ProtocolBuffers REQUIRED) +# include_directories(${PROTOBUF_INCLUDE_DIRS}) +# +# include_directories(${CMAKE_CURRENT_BINARY_DIR}) +# PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS foo.proto) +# add_executable(bar bar.cc ${PROTO_SRCS} ${PROTO_HDRS}) +# target_link_libraries(bar ${PROTOBUF_LIBRARY}) +# +# NOTE: You may need to link against pthreads, depending +# on the platform. +# ==================================================================== +# +# PROTOBUF_GENERATE_CPP ([CPP srcs hdrs] [JAVA files] [PYTHON files] PROTOFILES files... [PROTOROOT root] [OUTPATH path] [EXPORT_MACRO macroName] [DEBUG]) +# srcs = Variable to define with autogenerated +# source files +# hdrs = Variable to define with autogenerated +# header files +# PROTOROOT = Root under which the proto files are located. Paths starting +# from this root are used under OUTPATH as directory structure +# for the generated files. Defaults to CMAKE_CURRENT_SOURCE_DIR. +# OUTPATH = Path to store generated files under. Default is +# CMAKE_CURRENT_BINARY_DIR. +# EXPORT_MACRO = Tells protoc to generate DLL export definitions using the +# specified macro name +# DEBUG = if set, debug messages will be generated +# +# ==================================================================== + + +#============================================================================= +# Copyright 2009 Kitware, Inc. +# Copyright 2009 Philip Lowman <philip@yhbt.com> +# Copyright 2008 Esben Mose Hansen, Ange Optimization ApS +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distributed this file outside of CMake, substitute the full +# License text for the above reference.) + +INCLUDE(ParseArguments) + +FUNCTION(PROTOBUF_GENERATE) + + # argument parsing + PARSE_ARGUMENTS(ARG "PROTOROOT;PROTOFILES;OUTPATH;INCLUDES;EXPORT_MACRO;CPP;JAVA;PYTHON;MATLAB" "DEBUG" ${ARGN}) + + IF(NOT ARG_PROTOFILES) + MESSAGE(SEND_ERROR "Error: PROTOBUF_GENERATE() called without any proto files") + RETURN() + ENDIF(NOT ARG_PROTOFILES) + LIST(LENGTH ARG_PROTOROOT PROTOROOT_LENGTH) + #IF(PROTOROOT_LENGTH GREATER 1) + # MESSAGE(SEND_ERROR "Error: PROTOBUF_GENERATE() called with too many protoroots, only one is allowed") + # RETURN() + #ENDIF() + LIST(LENGTH ARG_OUTPATH OUTPATH_LENGTH) + IF(OUTPATH_LENGTH GREATER 1) + MESSAGE(SEND_ERROR "Error: PROTOBUF_GENERATE() called with too many outpaths, only one is allowed") + RETURN() + ENDIF() + LIST(LENGTH ARG_EXPORT_MACRO EXPORT_MACRO_LENGTH) + IF(EXPORT_MACRO_LENGTH GREATER 1) + MESSAGE(SEND_ERROR "Error: PROTOBUF_GENERATE() called with too many export macro names, only one is allowed") + RETURN() + ENDIF() + + # decide whether to build CPP + LIST(LENGTH ARG_CPP CPP_LENGTH) + IF(CPP_LENGTH EQUAL 0) + SET(BUILD_CPP FALSE) + ELSE() + IF(NOT CPP_LENGTH EQUAL 2) + MESSAGE(SEND_ERROR "Error: PROTOBUF_GENERATE() CPP argument expects two parameters SRC_VAR and HDR_VAR") + RETURN() + ENDIF() + SET(BUILD_CPP TRUE) + LIST(GET ARG_CPP 0 RESULT_CPP_SRCS) + LIST(GET ARG_CPP 1 RESULT_CPP_HDRS) + ENDIF() + + # decide whether to build java + LIST(LENGTH ARG_JAVA JAVA_LENGTH) + IF(JAVA_LENGTH EQUAL 0) + SET(BUILD_JAVA FALSE) + ELSE() + IF(NOT JAVA_LENGTH EQUAL 1) + MESSAGE(SEND_ERROR "Error: PROTOBUF_GENERATE() JAVA argument expects one parameter JAVA_VAR") + RETURN() + ENDIF() + SET(BUILD_JAVA TRUE) + LIST(GET ARG_JAVA 0 RESULT_JAVA) + ENDIF() + + # decide whether to build PYTHON + LIST(LENGTH ARG_PYTHON PYTHON_LENGTH) + IF(PYTHON_LENGTH EQUAL 0) + SET(BUILD_PYTHON FALSE) + ELSE() + IF(NOT PYTHON_LENGTH EQUAL 1) + MESSAGE(SEND_ERROR "Error: PROTOBUF_GENERATE() PYTHON argument expects one parameter PYTHON_VAR") + RETURN() + ENDIF() + SET(BUILD_PYTHON TRUE) + LIST(GET ARG_PYTHON 0 RESULT_PYTHON) + ENDIF() + + # decide whether to build MATLAB + LIST(LENGTH ARG_MATLAB MATLAB_LENGTH) + IF(MATLAB_LENGTH EQUAL 0) + SET(BUILD_MATLAB FALSE) + ELSE() + IF(NOT MATLAB_LENGTH EQUAL 1) + MESSAGE(SEND_ERROR "Error: PROTOBUF_GENERATE() MATLAB argument expects one parameter MATLAB_VAR") + RETURN() + ENDIF() + SET(BUILD_MATLAB TRUE) + LIST(GET ARG_MATLAB 0 RESULT_MATLAB) + ENDIF() + + # create proper export macro for CPP if desired + IF(EXPORT_MACRO_LENGTH EQUAL 1) + SET(ARG_EXPORT "dllexport_decl=${ARG_EXPORT_MACRO}:") + MESSAGE(STATUS "Enabling export macro ${ARG_EXPORT_MACRO} for CPP") + ENDIF() + + SET(OUTPATH ${CMAKE_CURRENT_BINARY_DIR}) + IF(OUTPATH_LENGTH EQUAL 1) + SET(OUTPATH ${ARG_OUTPATH}) + ENDIF() + SET(PROTOROOTS ${CMAKE_CURRENT_SOURCE_DIR}) + IF(PROTOROOT_LENGTH GREATER 0) + SET(PROTOROOTS ${ARG_PROTOROOT}) + ENDIF() + + SET(ARG_EXPORT "") + IF(EXPORT_MACRO_LENGTH EQUAL 1) + SET(ARG_EXPORT "dllexport_decl=${ARG_EXPORT_MACRO}:") + ENDIF() + + # build command line for additional includes paths + SET(INCLUDE_CMD_LINE) + FOREACH(P ${ARG_INCLUDES}) + LIST(APPEND INCLUDE_CMD_LINE "--proto_path" ${P}) + ENDFOREACH() + + IF(ARG_DEBUG) + MESSAGE("OUTPATH: ${OUTPATH}") + MESSAGE("PROTOROOTS: ${PROTOROOTS}") + MESSAGE("INCLUDE_CMD_LINE: ${INCLUDE_CMD_LINE}") + ENDIF() + + SET(MATCHED_FILE_PATHS) + FOREACH(PROTOFILE ${ARG_PROTOFILES}) + + FILE(TO_CMAKE_PATH ${PROTOFILE} PROTOFILE) + + # ensure that the file ends with .proto + STRING(REGEX MATCH "\\.proto$$" PROTOEND ${PROTOFILE}) + IF(NOT PROTOEND) + MESSAGE(SEND_ERROR "Proto file '${PROTOFILE}' does not end with .proto") + ENDIF() + + GET_FILENAME_COMPONENT(PROTO_PATH ${PROTOFILE} PATH) + GET_FILENAME_COMPONENT(ABS_FILE ${PROTOFILE} ABSOLUTE) + GET_FILENAME_COMPONENT(FILE_WE ${PROTOFILE} NAME_WE) + + STRING(LENGTH ${ABS_FILE} ABS_FILE_LENGTH) + + IF(ARG_DEBUG) + MESSAGE("file ${PROTOFILE}:") + MESSAGE(" PATH=${PROTO_PATH}") + MESSAGE(" ABS_FILE=${ABS_FILE}") + MESSAGE(" FILE_WE=${FILE_WE}") + MESSAGE(" PROTOROOTS=${PROTOROOTS}") + ENDIF() + + # find out if the file is in one of the specified proto root + # we mimic the protoc logic here by taking the first matching proto_path + SET(MATCH_PATH) + FOREACH(ROOT ${PROTOROOTS}) + + IF(ARG_DEBUG) + MESSAGE(" ROOT=${ROOT}") + ENDIF() + + FILE(RELATIVE_PATH REL_ABS ${ROOT} ${ABS_FILE}) + STRING(LENGTH ${REL_ABS} REL_LENGTH) + + IF(ARG_DEBUG) + MESSAGE(" REL_ABS=${REL_ABS}") + MESSAGE(" REL_LENGTH=${REL_LENGTH}") + ENDIF() + + IF(${REL_LENGTH} GREATER 0 AND ${REL_LENGTH} LESS ${ABS_FILE_LENGTH}) + # we did not need to go directories up, hence the path is shorter + # and this is a match... bad assumption but works + SET(MATCH_PATH ${REL_ABS}) + SET(MATCH_ROOT ${ROOT}) + IF(ARG_DEBUG) + MESSAGE(" MATCH_ROOT=${MATCH_ROOT}") + ENDIF() + BREAK() + ENDIF() + + ENDFOREACH() + + IF(ARG_DEBUG) + MESSAGE(" MATCH_PATH=${MATCH_PATH}") + ENDIF() + + IF(NOT MATCH_PATH) + MESSAGE(SEND_ERROR "Proto file '${PROTOFILE}' is not in protoroots '${PROTOROOTS}'") + ENDIF() + LIST(APPEND MATCHED_FILE_PATHS ${ABS_FILE}) + + # build the result file name + FILE(RELATIVE_PATH ROOT_CLEANED_FILE ${MATCH_ROOT} ${ABS_FILE}) + IF(ARG_DEBUG) + MESSAGE(" ROOT_CLEANED_FILE=${ROOT_CLEANED_FILE}") + ENDIF() + STRING(REGEX REPLACE "\\.proto$$" "" EXT_CLEANED_FILE ${ROOT_CLEANED_FILE}) + IF(ARG_DEBUG) + MESSAGE(" EXT_CLEANED_FILE=${EXT_CLEANED_FILE}") + ENDIF() + + SET(CPP_FILE "${OUTPATH}/${EXT_CLEANED_FILE}.pb.cc") + SET(HDR_FILE "${OUTPATH}/${EXT_CLEANED_FILE}.pb.h") + SET(PYTHON_FILE "${OUTPATH}/${EXT_CLEANED_FILE}_pb2.py") + + # determine the java file name + FILE(READ ${PROTOFILE} PROTO_CONTENT) + + # first the package + # TODO jwienke: ignore comments... see below TODO + SET(PACKAGE_REGEX "package[\t ]+([^;\n\r]+);") + STRING(REGEX MATCH ${PACKAGE_REGEX} PACKAGE_LINE ${PROTO_CONTENT}) + IF(ARG_DEBUG) + MESSAGE(" PACKAGE_LINE=${PACKAGE_LINE}") + ENDIF() + SET(PACKAGE "") + IF(PACKAGE_LINE) + STRING(REGEX REPLACE ${PACKAGE_REGEX} "\\1" PACKAGE ${PACKAGE_LINE}) + ENDIF() + STRING(REPLACE "." "/" JAVA_PACKAGE_PATH "${PACKAGE}") + IF(ARG_DEBUG) + MESSAGE(" PACKAGE=${PACKAGE}") + MESSAGE(" JAVA_PACKAGE_PATH=${JAVA_PACKAGE_PATH}") + ENDIF() + + # then the java class name + + # this is the default + # TODO jwienke: how to integrate that this line must not start with //? + # cmake regex are strange, because ^ and $ match beginning + # and end of file and not of each line + SET(JAVA_CLASS_REGEX "option[\t ]+java_outer_classname[\t ]+=[\t ]+\"([^\"]+)\"") + STRING(REGEX MATCH ${JAVA_CLASS_REGEX} JAVA_CLASS_LINE ${PROTO_CONTENT}) + IF(ARG_DEBUG) + MESSAGE(" JAVA_CLASS_LINE=${JAVA_CLASS_LINE}") + ENDIF() + SET(JAVA_CLASS ${FILE_WE}) + IF(JAVA_CLASS_LINE) + STRING(REGEX REPLACE ${JAVA_CLASS_REGEX} "\\1" JAVA_CLASS ${JAVA_CLASS_LINE}) + # Now that we have the real java class name, this must be replaced + # in the original file name proposal. However, the subpath in the + # file system is also part of EXT_CLEANED_FILE. Hence, we need to + # do some replace logic again... + ENDIF() + IF(ARG_DEBUG) + MESSAGE(" JAVA_CLASS=${JAVA_CLASS}") + ENDIF() + + # finally deduce the real java name + SET(JAVA_FILE "${OUTPATH}/${JAVA_PACKAGE_PATH}/${JAVA_CLASS}.java") + + IF(ARG_DEBUG) + MESSAGE(" CPP_FILE=${CPP_FILE}") + MESSAGE(" HDR_FILE=${HDR_FILE}") + MESSAGE(" JAVA_FILE=${JAVA_FILE}") + MESSAGE(" PYTHON_FILE=${PYTHON_FILE}") + ENDIF() + + # generate and use a list of protoroot arguments to pass to protoc + SET(ROOT_ARGS) + FOREACH(ROOT ${PROTOROOTS}) + LIST(APPEND ROOT_ARGS "--proto_path" ${ROOT}) + ENDFOREACH() + + IF(BUILD_CPP) + LIST(APPEND CPP_SRCS "${CPP_FILE}") + LIST(APPEND CPP_HDRS "${HDR_FILE}") + + ADD_CUSTOM_COMMAND( + OUTPUT "${CPP_FILE}" + "${HDR_FILE}" + COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTPATH} + COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} + ARGS "--cpp_out=${ARG_EXPORT}${OUTPATH}" ${ROOT_ARGS} ${INCLUDE_CMD_LINE} "${ABS_FILE}" + DEPENDS ${ABS_FILE} + COMMENT "Running C++ protocol buffer compiler on ${ABS_FILE} with root ${MATCH_ROOT}, generating: ${CPP_FILE}" + VERBATIM) + ENDIF() + + IF(BUILD_JAVA) + LIST(APPEND JAVA_FILES "${JAVA_FILE}") + + ADD_CUSTOM_COMMAND( + OUTPUT "${JAVA_FILE}" + COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTPATH} + COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} + ARGS "--java_out=${OUTPATH}" ${ROOT_ARGS} ${INCLUDE_CMD_LINE} "${ABS_FILE}" + DEPENDS ${ABS_FILE} + COMMENT "Running Java protocol buffer compiler on ${ABS_FILE} with root ${MATCH_ROOT}, generating: ${JAVA_FILE}" + VERBATIM) + ENDIF() + + IF(BUILD_PYTHON) + LIST(APPEND PYTHON_FILES "${PYTHON_FILE}") + + ADD_CUSTOM_COMMAND( + OUTPUT "${PYTHON_FILE}" + COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTPATH} + COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} + ARGS "--python_out=${OUTPATH}" ${ROOT_ARGS} ${INCLUDE_CMD_LINE} "${ABS_FILE}" + DEPENDS ${ABS_FILE} + COMMENT "Running Python protocol buffer compiler on ${ABS_FILE} with root ${MATCH_ROOT}, generating: ${PYTHON_FILE}" + VERBATIM) + ENDIF() + + ENDFOREACH() + + IF(BUILD_CPP) + SET_SOURCE_FILES_PROPERTIES(${${CPP_SRCS}} ${${CPP_HDRS}} PROPERTIES GENERATED TRUE) + SET(${RESULT_CPP_SRCS} ${CPP_SRCS} PARENT_SCOPE) + SET(${RESULT_CPP_HDRS} ${CPP_HDRS} PARENT_SCOPE) + ENDIF() + + IF(BUILD_JAVA) + SET_SOURCE_FILES_PROPERTIES(${${JAVA_FILES}} PROPERTIES GENERATED TRUE) + SET(${RESULT_JAVA} ${JAVA_FILES} PARENT_SCOPE) + ENDIF() + + IF(BUILD_PYTHON) + SET_SOURCE_FILES_PROPERTIES(${${PYTHON_FILES}} PROPERTIES GENERATED TRUE) + SET(${RESULT_PYTHON} ${PYTHON_FILES} PARENT_SCOPE) + ENDIF() + + IF(BUILD_MATLAB) + ADD_CUSTOM_COMMAND( + OUTPUT ${OUTPATH} + COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTPATH} + COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} + ARGS "--matlab_out=${OUTPATH}" ${ROOT_ARGS} ${INCLUDE_CMD_LINE} ${MATCHED_FILE_PATHS} + DEPENDS ${MATCHED_FILE_PATHS} + COMMENT "Running Matlab protocol buffer compiler to generate files in ${OUTPATH}" + VERBATIM) + ENDIF() + + IF(BUILD_MATLAB) + SET(${RESULT_MATLAB} ${OUTPATH} PARENT_SCOPE) + ENDIF() + +ENDFUNCTION() + +MACRO(PROTOBUF_GENERATE_CPP SRCS HDRS) + PROTOBUF_GENERATE(${ARGN} CPP ${SRCS} ${HDRS}) +ENDMACRO() + +FIND_PATH(PROTOBUF_INCLUDE_DIR NAMES google/protobuf/service.h + HINTS "${PROTOBUF_ROOT}/include" + DOC "The Google Protocol Buffers Headers") + +# Google's provided vcproj files generate libraries with a "lib" +# prefix on Windows +IF(WIN32) + SET(PROTOBUF_ORIG_FIND_LIBRARY_PREFIXES "${CMAKE_FIND_LIBRARY_PREFIXES}") + SET(CMAKE_FIND_LIBRARY_PREFIXES "lib" "") +ENDIF() + +FIND_LIBRARY(PROTOBUF_LIBRARY NAMES protobuf + HINTS "${PROTOBUF_ROOT}/bin" + "${PROTOBUF_ROOT}/lib" + DOC "The Google Protocol Buffers Library" +) +FIND_LIBRARY(PROTOBUF_PROTOC_LIBRARY NAMES protoc + HINTS "${PROTOBUF_ROOT}/bin" + "${PROTOBUF_ROOT}/lib" + DOC "The Google Protocol Buffers Compiler Library" +) +FIND_PROGRAM(PROTOBUF_PROTOC_EXECUTABLE NAMES protoc + HINTS "${PROTOBUF_ROOT}/bin" + DOC "The Google Protocol Buffers Compiler" +) +IF(PROTOBUF_PROTOC_EXECUTABLE AND NOT PROTOBUF_PROTOC_MATLAB) + # check whether this protoc version supports matlab + EXECUTE_PROCESS(COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} "-h" + ERROR_VARIABLE PROTOC_HELP_TEXT + OUTPUT_QUIET) + STRING(REGEX MATCH "--matlab_out" PROTOC_MATLAB_OUT "${PROTOC_HELP_TEXT}") + IF(PROTOC_MATLAB_OUT) + SET(PROTOBUF_PROTOC_MATLAB TRUE CACHE BOOL "Whether protoc is able to generate matlab output.") + MESSAGE(STATUS "protoc supports matlab") + ELSE() + SET(PROTOBUF_PROTOC_MATLAB FALSE CACHE BOOL "Whether protoc is able to generate matlab output.") + MESSAGE(STATUS "protoc does not support matlab") + ENDIF() +ENDIF() +SET(PROTOBUF_PROTOC_VERSION "PROTOBUF_PROTOC_VERSION-NOTFOUND") +IF(PROTOBUF_PROTOC_EXECUTABLE) + EXECUTE_PROCESS(COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} --version + OUTPUT_VARIABLE PROTOBUF_PROTOC_VERSION_TEMP + RESULT_VARIABLE PROTOBUF_PROTOC_VERSION_RESULT) + STRING(REGEX REPLACE ".*([0-9]\\.[0-9]\\.[0-9]).*" "\\1" + PROTOBUF_PROTOC_VERSION_TEMP "${PROTOBUF_PROTOC_VERSION_TEMP}") + IF(PROTOBUF_PROTOC_VERSION_TEMP) + SET(PROTOBUF_PROTOC_VERSION "${PROTOBUF_PROTOC_VERSION_TEMP}") + ENDIF() +ENDIF() + +SET(KNOWN_VERSIONS 2.6.0 2.5.1 2.5.0 2.4.1 2.4.0 2.3.0) +# if we know the compiler version, we should favor it +IF(PROTOBUF_PROTOC_VERSION) + LIST(INSERT KNOWN_VERSIONS 0 ${PROTOBUF_PROTOC_VERSION}) +ENDIF() +SET(JAVA_NAMES ${PROTOBUF_JAVA_NAME} protobuf.jar protobuf-java.jar) +FOREACH(VERSION ${KNOWN_VERSIONS}) + LIST(APPEND JAVA_NAMES "protobuf-java-${VERSION}.jar") +ENDFOREACH() + +FIND_FILE(PROTOBUF_JAVA_LIBRARY + NAMES ${JAVA_NAMES} + HINTS ${PROTOBUF_JAVA_ROOT} + "${PROTOBUF_JAVA_ROOT}/share/java" + "/usr/share/java" + "/usr/share/java/protobuf-java" + "/usr/share/protobuf/lib" + "${CMAKE_INSTALL_PREFIX}/lib/java" + "${CMAKE_INSTALL_PREFIX}/share/java") + +MARK_AS_ADVANCED(PROTOBUF_INCLUDE_DIR + PROTOBUF_LIBRARY + PROTOBUF_PROTOC_LIBRARY + PROTOBUF_PROTOC_EXECUTABLE + PROTOBUF_JAVA_LIBRARY + PROTOBUF_PROTOC_MATLAB) + +# Restore original find library prefixes +IF(WIN32) + SET(CMAKE_FIND_LIBRARY_PREFIXES "${PROTOBUF_ORIG_FIND_LIBRARY_PREFIXES}") +ENDIF() + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(PROTOBUF DEFAULT_MSG PROTOBUF_LIBRARY PROTOBUF_INCLUDE_DIR) + +IF(PROTOBUF_FOUND) + SET(PROTOBUF_INCLUDE_DIRS ${PROTOBUF_INCLUDE_DIR}) + SET(PROTOBUF_LIBRARIES ${PROTOBUF_LIBRARY}) +ENDIF() diff --git a/ipaacalib/cpp/CMakeModules/ParseArguments.cmake b/ipaacalib/cpp/CMakeModules/ParseArguments.cmake new file mode 100644 index 0000000000000000000000000000000000000000..e13f671a73c1aa4d686e79cafa3762a9219eadbd --- /dev/null +++ b/ipaacalib/cpp/CMakeModules/ParseArguments.cmake @@ -0,0 +1,52 @@ +# Parse arguments passed to a function into several lists separated by +# upper-case identifiers and options that do not have an associated list e.g.: +# +# SET(arguments +# hello OPTION3 world +# LIST3 foo bar +# OPTION2 +# LIST1 fuz baz +# ) +# PARSE_ARGUMENTS(ARG "LIST1;LIST2;LIST3" "OPTION1;OPTION2;OPTION3" ${arguments}) +# +# results in 7 distinct variables: +# * ARG_DEFAULT_ARGS: hello;world +# * ARG_LIST1: fuz;baz +# * ARG_LIST2: +# * ARG_LIST3: foo;bar +# * ARG_OPTION1: FALSE +# * ARG_OPTION2: TRUE +# * ARG_OPTION3: TRUE +# +# taken from http://www.cmake.org/Wiki/CMakeMacroParseArguments + +MACRO(PARSE_ARGUMENTS prefix arg_names option_names) + SET(DEFAULT_ARGS) + FOREACH(arg_name ${arg_names}) + SET(${prefix}_${arg_name}) + ENDFOREACH(arg_name) + FOREACH(option ${option_names}) + SET(${prefix}_${option} FALSE) + ENDFOREACH(option) + + SET(current_arg_name DEFAULT_ARGS) + SET(current_arg_list) + FOREACH(arg ${ARGN}) + SET(larg_names ${arg_names}) + LIST(FIND larg_names "${arg}" is_arg_name) + IF (is_arg_name GREATER -1) + SET(${prefix}_${current_arg_name} ${current_arg_list}) + SET(current_arg_name ${arg}) + SET(current_arg_list) + ELSE (is_arg_name GREATER -1) + SET(loption_names ${option_names}) + LIST(FIND loption_names "${arg}" is_option) + IF (is_option GREATER -1) + SET(${prefix}_${arg} TRUE) + ELSE (is_option GREATER -1) + SET(current_arg_list ${current_arg_list} ${arg}) + ENDIF (is_option GREATER -1) + ENDIF (is_arg_name GREATER -1) + ENDFOREACH(arg) + SET(${prefix}_${current_arg_name} ${current_arg_list}) +ENDMACRO(PARSE_ARGUMENTS) diff --git a/ipaacalib/cpp/build.xml b/ipaacalib/cpp/build.xml index f205ed3674f49dd18e475bf98c64235bd6ff622a..c8f0ffb7a86f9826e94336379e30563c4c073684 100644 --- a/ipaacalib/cpp/build.xml +++ b/ipaacalib/cpp/build.xml @@ -18,5 +18,21 @@ </exec> </target> <target name="-pre-compilation" depends="-proto-yes,-proto-no" /> + <target name="-compilation" depends="-build-setup, -pre-compilation"> + <!-- echo message="Using temporary RSBProtocol_DIR=${cmake.base.dir}/rsc_tmp/share/rsbprotocol0.10" / --> + <cmake srcdir="${cmake.base.dir}" + bindir="${build.dir}" + buildtype="Debug"> + <generator name="Visual Studio 10" platform="windows" buildargs="ALL_BUILD.vcxproj"> + <variable name="CMAKE_INSTALL_PREFIX" type="PATH" value="../${zip.tmp.dir}" /> + </generator> + <generator name="Unix Makefiles"> + <variable name="CMAKE_INSTALL_PREFIX" type="PATH" value="" /> + </generator> + <variable name="SPREAD_ROOT" type="FILEPATH" value="${env.SPREAD_ROOT}" /> + <variable name="PROTOBUF_ROOT" type="FILEPATH" value="${env.PROTOBUF_ROOT}" /> + </cmake> + </target> + </project> diff --git a/ipaacalib/cpp/include/ipaaca/ipaaca-buffers.h b/ipaacalib/cpp/include/ipaaca/ipaaca-buffers.h new file mode 100644 index 0000000000000000000000000000000000000000..e0a26c13e7e668977d5e86daea651a3ac5ec54ff --- /dev/null +++ b/ipaacalib/cpp/include/ipaaca/ipaaca-buffers.h @@ -0,0 +1,415 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +#ifndef __ipaaca_buffers_h_INCLUDED__ +#define __ipaaca_buffers_h_INCLUDED__ + +#ifndef __ipaaca_h_INCLUDED__ +#error "Please do not include this file directly, use ipaaca.h instead" +#endif + + +/// Store for local IUs (used in OutputBuffer) +IPAACA_HEADER_EXPORT class IUStore: public std::map<std::string, boost::shared_ptr<IU> > +{ +}; +/// Store for RemotePushIUs (used in InputBuffer) +IPAACA_HEADER_EXPORT class RemotePushIUStore: public std::map<std::string, boost::shared_ptr<RemotePushIU> > // TODO genericize to all remote IU types +{ +}; + +typedef std::set<std::string> LinkSet; +typedef std::map<std::string, LinkSet> LinkMap; + +/// Container for IU links that gracefully returns the empty set if required +IPAACA_HEADER_EXPORT class SmartLinkMap {//{{{ + friend std::ostream& operator<<(std::ostream& os, const SmartLinkMap& obj); + friend class IUInterface; + friend class IU; + friend class IUConverter; + friend class MessageConverter; + public: + IPAACA_HEADER_EXPORT const LinkSet& get_links(const std::string& key); + IPAACA_HEADER_EXPORT const LinkMap& get_all_links(); + + protected: + IPAACA_MEMBER_VAR_EXPORT LinkMap _links; + /// The empty link set is returned if undefined links are read for an IU. + IPAACA_MEMBER_VAR_EXPORT static LinkSet empty_link_set; + IPAACA_HEADER_EXPORT void _add_and_remove_links(const LinkMap& add, const LinkMap& remove); + IPAACA_HEADER_EXPORT void _replace_links(const LinkMap& links); +};//}}} + +/// The empty link set is returned if undefined links are read for an IU. +IPAACA_MEMBER_VAR_EXPORT const LinkSet EMPTY_LINK_SET; + +/// Configuration object that can be passed to Buffer constructors. +IPAACA_HEADER_EXPORT class BufferConfiguration//{{{ +{ + protected: + IPAACA_MEMBER_VAR_EXPORT std::string _basename; + IPAACA_MEMBER_VAR_EXPORT std::vector<std::string> _category_interests; + IPAACA_MEMBER_VAR_EXPORT std::string _channel; + public: + IPAACA_HEADER_EXPORT inline BufferConfiguration(const std::string& basename): _basename(basename), _channel(__ipaaca_static_option_default_channel) { } + IPAACA_HEADER_EXPORT inline const std::string& get_basename() const { return _basename; } + IPAACA_HEADER_EXPORT inline const std::vector<std::string>& get_category_interests() const { return _category_interests; } + IPAACA_HEADER_EXPORT inline const std::string& get_channel() const { return _channel; } + public: + // setters, initialization helpers + IPAACA_HEADER_EXPORT inline BufferConfiguration& set_basename(const std::string& basename) { _basename = basename; return *this; } + IPAACA_HEADER_EXPORT inline BufferConfiguration& add_category_interest(const std::string& category) { _category_interests.push_back(category); return *this; } + IPAACA_HEADER_EXPORT inline BufferConfiguration& set_channel(const std::string& channel) { _channel = channel; return *this; } +};//}}} + +/// Builder object for BufferConfiguration, not required for C++ [DEPRECATED] +IPAACA_HEADER_EXPORT class BufferConfigurationBuilder: private BufferConfiguration//{{{ +{ + public: + [[deprecated("Use setters in BufferConfiguration instead of the Builder")]] + IPAACA_HEADER_EXPORT inline BufferConfigurationBuilder(const std::string& basename): BufferConfiguration(basename) {} + IPAACA_HEADER_EXPORT inline void set_basename(const std::string& basename) + { + _basename = basename; + } + IPAACA_HEADER_EXPORT inline void add_category_interest(const std::string& category) + { + _category_interests.push_back(category); + } + IPAACA_HEADER_EXPORT inline void set_channel(const std::string& channel) + { + _channel = channel; + } + + IPAACA_HEADER_EXPORT const BufferConfiguration& get_buffer_configuration() { return *this; } + +};//}}} + +/** \brief Type of user-space functions that can be registered on a Buffer to receive IU events. + * + * The signature of these functions is void(shared_ptr<IUInterface> iu, IUEventType evt_type, bool local), where:<br/> + * iu can be used mostly like a locally-generated IU reference (e.g. iu->payload() ...)<br/> + * evt_type is one of IU_ADDED, IU_UPDATED, IU_RETRACTED, IU_DELETED, IU_LINKSUPDATED, IU_COMMITTED, IU_MESSAGE<br/> + * local indicates that a remote change to a local IU (in an OutputBuffer) was effected + * + * + */ +IPAACA_HEADER_EXPORT typedef boost::function<void (boost::shared_ptr<IUInterface>, IUEventType, bool)> IUEventHandlerFunction; + +/** \brief Internal handler type used in Buffer (wraps used-specified IUEventHandlerFunction) + */ +IPAACA_HEADER_EXPORT class IUEventHandler {//{{{ + protected: + IPAACA_MEMBER_VAR_EXPORT IUEventHandlerFunction _function; + IPAACA_MEMBER_VAR_EXPORT IUEventType _event_mask; + IPAACA_MEMBER_VAR_EXPORT bool _for_all_categories; + IPAACA_MEMBER_VAR_EXPORT std::set<std::string> _categories; + protected: + IPAACA_HEADER_EXPORT inline bool _condition_met(IUEventType event_type, const std::string& category) + { + return ((_event_mask&event_type)!=0) && (_for_all_categories || (_categories.count(category)>0)); + } + public: + IPAACA_HEADER_EXPORT IUEventHandler(IUEventHandlerFunction function, IUEventType event_mask, const std::string& category); + IPAACA_HEADER_EXPORT IUEventHandler(IUEventHandlerFunction function, IUEventType event_mask, const std::set<std::string>& categories); + //void call(Buffer* buffer, const std::string& uid, bool local, IUEventType event_type, const std::string& category); + IPAACA_HEADER_EXPORT void call(Buffer* buffer, boost::shared_ptr<IUInterface> iu, bool local, IUEventType event_type, const std::string& category); + typedef boost::shared_ptr<IUEventHandler> ptr; +};//}}} + +/** + * \brief Buffer base class. Derived classes use its handler registration functionality. + * + * \b Note: This class is never instantiated directly (use OutputBuffer and InputBuffer, respectively). + */ +IPAACA_HEADER_EXPORT class Buffer { //: public boost::enable_shared_from_this<Buffer> {//{{{ + friend class IU; + friend class RemotePushIU; + friend class CallbackIUPayloadUpdate; + friend class CallbackIULinkUpdate; + friend class CallbackIUCommission; + friend class CallbackIUResendRequest; + protected: + //Lock _handler_lock; + IPAACA_MEMBER_VAR_EXPORT std::string _uuid; + IPAACA_MEMBER_VAR_EXPORT std::string _basename; + IPAACA_MEMBER_VAR_EXPORT std::string _unique_name; + IPAACA_MEMBER_VAR_EXPORT std::string _id_prefix; + IPAACA_MEMBER_VAR_EXPORT std::string _channel; + IPAACA_MEMBER_VAR_EXPORT std::vector<IUEventHandler::ptr> _event_handlers; + protected: + IPAACA_HEADER_EXPORT _IPAACA_ABSTRACT_ virtual void _publish_iu_resend(boost::shared_ptr<IU> iu, const std::string& hidden_scope_name) = 0; + + + IPAACA_HEADER_EXPORT _IPAACA_ABSTRACT_ virtual void _send_iu_link_update(IUInterface* iu, bool is_delta, revision_t revision, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name="undef") = 0; + IPAACA_HEADER_EXPORT _IPAACA_ABSTRACT_ virtual void _send_iu_payload_update(IUInterface* iu, bool is_delta, revision_t revision, const std::map<std::string, PayloadDocumentEntry::ptr>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name="undef") = 0; + IPAACA_HEADER_EXPORT _IPAACA_ABSTRACT_ virtual void _send_iu_commission(IUInterface* iu, revision_t revision, const std::string& writer_name="undef") = 0; +// IPAACA_HEADER_EXPORT _IPAACA_ABSTRACT_ virtual void _send_iu_resendrequest(IUInterface* iu, revision_t revision, const std::string& writer_name="undef") = 0; + IPAACA_HEADER_EXPORT void _allocate_unique_name(const std::string& basename, const std::string& function); + IPAACA_HEADER_EXPORT inline Buffer(const std::string& basename, const std::string& function) { + _allocate_unique_name(basename, function); + _channel = __ipaaca_static_option_default_channel; + } + IPAACA_HEADER_EXPORT void call_iu_event_handlers(boost::shared_ptr<IUInterface> iu, bool local, IUEventType event_type, const std::string& category); + public: + IPAACA_HEADER_EXPORT virtual inline ~Buffer() { } + IPAACA_HEADER_EXPORT inline const std::string& unique_name() { return _unique_name; } + /// This version of register_handler takes a set of several category interests instead of just one. + IPAACA_HEADER_EXPORT void register_handler(IUEventHandlerFunction function, IUEventType event_mask, const std::set<std::string>& categories); + /** \brief Register a user-specified handler for IU events. Unless specified, it triggers for all event types for all category interests of the buffer. + * + * \param function A function [object] that can be converted to #IUEventHandlerFunction (examples below) + * \param event_mask Which event types to relay to the user (default: all) + * \param category The category to filter for (default: do not filter) + * + * \b Examples: + * + * Adding a plain function as a handler:<br/> + * <pre> + * void global_iu_handler(IUInterface::ptr iu, IUEventType type, bool local) { do_something(); } + * ... + * int main() { + * OutputBuffer::ptr outbuf = OutputBuffer::create("mybufname"); + * outbuf->register_handler(global_iu_handler); + * ... + * } + * </pre> + * + * Adding a class member as a handler (using boost::bind):<br/> + * <pre> + * class MyClass { + * protected: + * void my_internal_iu_handler(IUInterface::ptr iu, #IUEventType type, bool local) { do_something(); } + * InputBuffer::ptr inbuf; + * public: + * MyClass() { + * inbuf = InputBuffer::create("bufname", "categoryInterest"); + * inbuf->register_handler(boost::bind(&MyClass::my_internal_iu_handler, this, _1, _2, _3)); + * } + * }; + * </pre> + * + * Adding a lambda function as a handler (C++11):<br/> + * <pre> + * inbuf->register_handler([](IUInterface::ptr iu, #IUEventType event_type, bool local) { + * do_something(); + * }); + * </pre> + * + */ + IPAACA_HEADER_EXPORT void register_handler(IUEventHandlerFunction function, IUEventType event_mask = IU_ALL_EVENTS, const std::string& category=""); + //_IPAACA_ABSTRACT_ virtual void add(boost::shared_ptr<IUInterface> iu) = 0; + IPAACA_HEADER_EXPORT _IPAACA_ABSTRACT_ virtual boost::shared_ptr<IUInterface> get(const std::string& iu_uid) = 0; + IPAACA_HEADER_EXPORT _IPAACA_ABSTRACT_ virtual std::set<boost::shared_ptr<IUInterface> > get_ius() = 0; + + IPAACA_HEADER_EXPORT inline const std::string& channel() { return _channel; } +}; +//}}} + +/** + * \brief A buffer to which own IUs can be added to publish them + * + * Use OutputBuffer::create() to obtain a smart pointer to a new output buffer. + * + * Use OutputBuffer::add() to add (= publish) an IU. + * + * Use OutputBuffer::remove() to remove (= retract) an IU. + * + * Use Buffer::register_handler() to register a handler that will respond to remote changes to own published IUs. + */ +IPAACA_HEADER_EXPORT class OutputBuffer: public Buffer { //, public boost::enable_shared_from_this<OutputBuffer> {//{{{ + friend class IU; + friend class RemotePushIU; + friend class OutputBufferRsbAdaptor; + protected: + protected: + //OutputBufferRsbAdaptor _rsb; + IPAACA_MEMBER_VAR_EXPORT IUStore _iu_store; + IPAACA_MEMBER_VAR_EXPORT Lock _iu_id_counter_lock; +#ifdef IPAACA_EXPOSE_FULL_RSB_API + protected: + IPAACA_MEMBER_VAR_EXPORT std::map<std::string, rsb::Informer<rsb::AnyType>::Ptr> _informer_store; + IPAACA_MEMBER_VAR_EXPORT rsb::patterns::ServerPtr _server; + IPAACA_HEADER_EXPORT rsb::Informer<rsb::AnyType>::Ptr _get_informer(const std::string& category); +#endif + protected: + // informing functions + IPAACA_HEADER_EXPORT void _send_iu_link_update(IUInterface* iu, bool is_delta, revision_t revision, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name="undef") _IPAACA_OVERRIDE_; + IPAACA_HEADER_EXPORT void _publish_iu_resend(boost::shared_ptr<IU> iu, const std::string& hidden_scope_name) _IPAACA_OVERRIDE_; + + IPAACA_HEADER_EXPORT void _send_iu_payload_update(IUInterface* iu, bool is_delta, revision_t revision, const std::map<std::string, PayloadDocumentEntry::ptr>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name="undef") _IPAACA_OVERRIDE_; + IPAACA_HEADER_EXPORT void _send_iu_commission(IUInterface* iu, revision_t revision, const std::string& writer_name) _IPAACA_OVERRIDE_; + //IPAACA_HEADER_EXPORT void _send_iu_resendrequest(IUInterface* iu, revision_t revision, const std::string& writer_name); + // remote access functions + // _remote_update_links(IULinkUpdate) + // _remote_update_payload(IUPayloadUpdate) + // _remote_commit(protobuf::IUCommission) + IPAACA_HEADER_EXPORT void _publish_iu(boost::shared_ptr<IU> iu); + + IPAACA_HEADER_EXPORT void _retract_iu(boost::shared_ptr<IU> iu); + protected: + /// \b Note: constructor is protected. Use create() + IPAACA_HEADER_EXPORT OutputBuffer(const std::string& basename, const std::string& channel=""); // empty string auto-replaced with __ipaaca_static_option_default_channel + IPAACA_HEADER_EXPORT void _initialize_server(); + public: + IPAACA_HEADER_EXPORT static boost::shared_ptr<OutputBuffer> create(const std::string& basename); + IPAACA_HEADER_EXPORT ~OutputBuffer() { + IPAACA_IMPLEMENT_ME + } + IPAACA_HEADER_EXPORT void add(boost::shared_ptr<IU> iu); + IPAACA_HEADER_EXPORT boost::shared_ptr<IU> remove(const std::string& iu_uid); + IPAACA_HEADER_EXPORT boost::shared_ptr<IU> remove(boost::shared_ptr<IU> iu); + IPAACA_HEADER_EXPORT boost::shared_ptr<IUInterface> get(const std::string& iu_uid) _IPAACA_OVERRIDE_; + IPAACA_HEADER_EXPORT std::set<boost::shared_ptr<IUInterface> > get_ius() _IPAACA_OVERRIDE_; + typedef boost::shared_ptr<OutputBuffer> ptr; +}; +//}}} + +/** + * \brief A buffer in which remote IUs (and changes to them) are received. + * + * Use InputBuffer::create() to obtain a smart pointer to a new input buffer. + * + * Set category interests (IU filter) via the different versions of create(). + * + * Use Buffer::register_handler() to register a handler that will respond to relevant remote IUs. + */ +IPAACA_HEADER_EXPORT class InputBuffer: public Buffer { //, public boost::enable_shared_from_this<InputBuffer> {//{{{ + friend class IU; + friend class RemotePushIU; + friend class InputBufferRsbAdaptor; + //InputBufferRsbAdaptor _rsb; +#ifdef IPAACA_EXPOSE_FULL_RSB_API + protected: + IPAACA_MEMBER_VAR_EXPORT std::map<std::string, rsb::ListenerPtr> _listener_store; + IPAACA_MEMBER_VAR_EXPORT std::map<std::string, rsb::patterns::RemoteServerPtr> _remote_server_store; + IPAACA_MEMBER_VAR_EXPORT RemotePushIUStore _iu_store; // TODO genericize + IPAACA_HEADER_EXPORT rsb::patterns::RemoteServerPtr _get_remote_server(const std::string& unique_server_name); + IPAACA_HEADER_EXPORT rsb::ListenerPtr _create_category_listener_if_needed(const std::string& category); + IPAACA_HEADER_EXPORT void _handle_iu_events(rsb::EventPtr event); + IPAACA_HEADER_EXPORT void _trigger_resend_request(rsb::EventPtr event); +#endif + protected: + IPAACA_HEADER_EXPORT inline void _send_iu_link_update(IUInterface* iu, bool is_delta, revision_t revision, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name="undef") _IPAACA_OVERRIDE_ + { + IPAACA_WARNING("(ERROR) InputBuffer::_send_iu_link_update() should never be invoked") + } + IPAACA_HEADER_EXPORT inline void _publish_iu_resend(boost::shared_ptr<IU> iu, const std::string& hidden_scope_name) _IPAACA_OVERRIDE_ + { + IPAACA_WARNING("(ERROR) InputBuffer::_publish_iu_resend() should never be invoked") + } + IPAACA_HEADER_EXPORT inline void _send_iu_payload_update(IUInterface* iu, bool is_delta, revision_t revision, const std::map<std::string, PayloadDocumentEntry::ptr>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name="undef") _IPAACA_OVERRIDE_ + { + IPAACA_WARNING("(ERROR) InputBuffer::_send_iu_payload_update() should never be invoked") + } + IPAACA_HEADER_EXPORT inline void _send_iu_commission(IUInterface* iu, revision_t revision, const std::string& writer_name="undef") _IPAACA_OVERRIDE_ + { + IPAACA_WARNING("(ERROR) InputBuffer::_send_iu_commission() should never be invoked") + } + /*IPAACA_HEADER_EXPORT inline void _send_iu_resendrequest(IUInterface* iu, revision_t revision, const std::string& writer_name="undef") + { + IPAACA_WARNING("(ERROR) InputBuffer::_send_iu_resendrequest() should never be invoked") + }*/ + protected: + /// \b Note: all constructors are protected. Use create() + IPAACA_HEADER_EXPORT InputBuffer(const BufferConfiguration& bufferconfiguration); + IPAACA_HEADER_EXPORT InputBuffer(const std::string& basename, const std::set<std::string>& category_interests); + IPAACA_HEADER_EXPORT InputBuffer(const std::string& basename, const std::vector<std::string>& category_interests); + IPAACA_HEADER_EXPORT InputBuffer(const std::string& basename, const std::string& category_interest1); + IPAACA_HEADER_EXPORT InputBuffer(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2); + IPAACA_HEADER_EXPORT InputBuffer(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2, const std::string& category_interest3); + IPAACA_HEADER_EXPORT InputBuffer(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2, const std::string& category_interest3, const std::string& category_interest4); + + IPAACA_MEMBER_VAR_EXPORT bool triggerResend; + + public: + /// Specify whether old but previously unseen IUs should be requested to be sent to the buffer over a hidden channel. + IPAACA_HEADER_EXPORT void set_resend(bool resendActive); + IPAACA_HEADER_EXPORT bool get_resend(); + /// Create InputBuffer according to configuration in BufferConfiguration object + IPAACA_HEADER_EXPORT static boost::shared_ptr<InputBuffer> create(const BufferConfiguration& bufferconfiguration); + /// Create InputBuffer from name and set of category interests + IPAACA_HEADER_EXPORT static boost::shared_ptr<InputBuffer> create(const std::string& basename, const std::set<std::string>& category_interests); + /// Create InputBuffer from name and vector of category interests + IPAACA_HEADER_EXPORT static boost::shared_ptr<InputBuffer> create(const std::string& basename, const std::vector<std::string>& category_interests); + // /// Create InputBuffer from name and initializer_list of category interests + // IPAACA_HEADER_EXPORT static boost::shared_ptr<InputBuffer> create(const std::string& basename, const std::initializer_list<std::string>& category_interests); + /// Convenience function: create InputBuffer from name and one category interest + IPAACA_HEADER_EXPORT static boost::shared_ptr<InputBuffer> create(const std::string& basename, const std::string& category_interest1); + /// Convenience function: create InputBuffer from name and two category interests [DEPRECATED] + [[deprecated("Use create(string, set<string>) instead")]] + IPAACA_HEADER_EXPORT static boost::shared_ptr<InputBuffer> create(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2); + /// Convenience function: create InputBuffer from name and three category interests [DEPRECATED] + [[deprecated("Use create(string, set<string>) instead")]] + IPAACA_HEADER_EXPORT static boost::shared_ptr<InputBuffer> create(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2, const std::string& category_interest3); + /// Convenience function: create InputBuffer from name and four category interests [DEPRECATED] + [[deprecated("Use create(string, set<string>) instead")]] + IPAACA_HEADER_EXPORT static boost::shared_ptr<InputBuffer> create(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2, const std::string& category_interest3, const std::string& category_interest4); + IPAACA_HEADER_EXPORT ~InputBuffer() { + IPAACA_IMPLEMENT_ME + } + IPAACA_HEADER_EXPORT boost::shared_ptr<IUInterface> get(const std::string& iu_uid) _IPAACA_OVERRIDE_; + IPAACA_HEADER_EXPORT std::set<boost::shared_ptr<IUInterface> > get_ius() _IPAACA_OVERRIDE_; + typedef boost::shared_ptr<InputBuffer> ptr; +}; +//}}} + +/// Internal, transport-independent, representation of payload updates +IPAACA_HEADER_EXPORT class IUPayloadUpdate {//{{{ + public: + IPAACA_MEMBER_VAR_EXPORT std::string uid; + IPAACA_MEMBER_VAR_EXPORT revision_t revision; + IPAACA_MEMBER_VAR_EXPORT std::string writer_name; + IPAACA_MEMBER_VAR_EXPORT bool is_delta; + IPAACA_MEMBER_VAR_EXPORT std::map<std::string, PayloadDocumentEntry::ptr> new_items; + IPAACA_MEMBER_VAR_EXPORT std::vector<std::string> keys_to_remove; + IPAACA_MEMBER_VAR_EXPORT std::string payload_type; // to handle legacy mode + friend std::ostream& operator<<(std::ostream& os, const IUPayloadUpdate& obj); + typedef boost::shared_ptr<IUPayloadUpdate> ptr; +};//}}} + +/// Internal, transport-independent, representation of link updates +IPAACA_HEADER_EXPORT class IULinkUpdate {//{{{ + public: + IPAACA_MEMBER_VAR_EXPORT std::string uid; + IPAACA_MEMBER_VAR_EXPORT revision_t revision; + IPAACA_MEMBER_VAR_EXPORT std::string writer_name; + IPAACA_MEMBER_VAR_EXPORT bool is_delta; + IPAACA_MEMBER_VAR_EXPORT std::map<std::string, std::set<std::string> > new_links; + IPAACA_MEMBER_VAR_EXPORT std::map<std::string, std::set<std::string> > links_to_remove; + friend std::ostream& operator<<(std::ostream& os, const IULinkUpdate& obj); + typedef boost::shared_ptr<IULinkUpdate> ptr; +};//}}} + + +#endif diff --git a/ipaacalib/cpp/include/ipaaca/ipaaca-definitions.h b/ipaacalib/cpp/include/ipaaca/ipaaca-definitions.h new file mode 100644 index 0000000000000000000000000000000000000000..42e4f1024d5e37feba5b932bd4695f80185fc99a --- /dev/null +++ b/ipaacalib/cpp/include/ipaaca/ipaaca-definitions.h @@ -0,0 +1,413 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +/** + * \file ipaaca-definitions.h + * + * \brief Header file for data and exception types and helper functions. + * + * Users should not include this file directly, but use ipaaca.h + * + * \author Ramin Yaghoubzadeh (ryaghoubzadeh@uni-bielefeld.de) + * \date March, 2015 + */ + +#ifndef __ipaaca_definitions_h_INCLUDED__ +#define __ipaaca_definitions_h_INCLUDED__ + +#ifndef __ipaaca_h_INCLUDED__ +#error "Please do not include this file directly, use ipaaca.h instead" +#endif + +// LAST FIXME LAST +//typedef boost::shared_ptr<rapidjson::Document> JsonDocPtr; + +typedef uint32_t revision_t; + +/// Type of the IU event. Realized as an integer to enable bit masks for filters. One of: IU_ADDED, IU_COMMITTED, IU_DELETED, IU_RETRACTED, IU_UPDATED, IU_LINKSUPDATED, IU_MESSAGE +typedef uint32_t IUEventType; +#define IU_ADDED 1 +#define IU_COMMITTED 2 +#define IU_DELETED 4 +#define IU_RETRACTED 8 +#define IU_UPDATED 16 +#define IU_LINKSUPDATED 32 +#define IU_MESSAGE 64 +/// Bit mask for receiving all IU events \see IUEventType +#define IU_ALL_EVENTS 127 + +/// Ipaaca (console) log levels +#define IPAACA_LOG_LEVEL_NONE 0 +#define IPAACA_LOG_LEVEL_CRITICAL 1 +#define IPAACA_LOG_LEVEL_ERROR 2 +#define IPAACA_LOG_LEVEL_WARNING 3 +#define IPAACA_LOG_LEVEL_INFO 4 +#define IPAACA_LOG_LEVEL_DEBUG 5 + +/// Convert an int event type to a human-readable string +IPAACA_HEADER_EXPORT inline std::string iu_event_type_to_str(IUEventType type) +{ + switch(type) { + case IU_ADDED: return "ADDED"; + case IU_COMMITTED: return "COMMITTED"; + case IU_DELETED: return "DELETED"; + case IU_RETRACTED: return "RETRACTED"; + case IU_UPDATED: return "UPDATED"; + case IU_LINKSUPDATED: return "LINKSUPDATED"; + case IU_MESSAGE: return "MESSAGE"; + default: return "(NOT A KNOWN SINGLE IU EVENT TYPE)"; + } +} + +/// IU access mode: PUSH means that updates are broadcast; REMOTE means that reads are RPC calls (currently NOT implemented); MESSAGE means a fire-and-forget message +IPAACA_HEADER_EXPORT enum IUAccessMode { + IU_ACCESS_PUSH, + IU_ACCESS_REMOTE, + IU_ACCESS_MESSAGE +}; + +/// generate a UUID as an ASCII string +IPAACA_HEADER_EXPORT std::string generate_uuid_string(); + +/** + * Exception with string description + */ +IPAACA_HEADER_EXPORT class Exception: public std::exception//{{{ +{ + protected: + IPAACA_MEMBER_VAR_EXPORT std::string _description; + public: + IPAACA_HEADER_EXPORT inline Exception(const std::string& description=""): _description(description) { } + IPAACA_HEADER_EXPORT inline ~Exception() throw() { } + IPAACA_HEADER_EXPORT const char* what() const throw() { + return _description.c_str(); + } +};//}}} +IPAACA_HEADER_EXPORT class Abort: public std::exception//{{{ +{ + protected: + IPAACA_MEMBER_VAR_EXPORT std::string _description; + public: + IPAACA_HEADER_EXPORT inline Abort(const std::string& description=""): _description(description) { } + IPAACA_HEADER_EXPORT inline ~Abort() throw() { } + IPAACA_HEADER_EXPORT const char* what() const throw() { + return _description.c_str(); + } +};//}}} + +/// IU was not found in a buffer +IPAACA_HEADER_EXPORT class IUNotFoundError: public Exception//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline ~IUNotFoundError() throw() { } + IPAACA_HEADER_EXPORT inline IUNotFoundError() { //boost::shared_ptr<IU> iu) { + _description = "IUNotFoundError"; + } +};//}}} +/// IU was already published +IPAACA_HEADER_EXPORT class IUPublishedError: public Exception//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline ~IUPublishedError() throw() { } + IPAACA_HEADER_EXPORT inline IUPublishedError() { //boost::shared_ptr<IU> iu) { + _description = "IUPublishedError"; + } +};//}}} +/// IU had already been committed to +IPAACA_HEADER_EXPORT class IUCommittedError: public Exception//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline ~IUCommittedError() throw() { } + IPAACA_HEADER_EXPORT inline IUCommittedError() { //boost::shared_ptr<IU> iu) { + _description = "IUCommittedError"; + } +};//}}} +/// Remote IU update failed because it had been modified in the mean time +IPAACA_HEADER_EXPORT class IUUpdateFailedError: public Exception//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline ~IUUpdateFailedError() throw() { } + IPAACA_HEADER_EXPORT inline IUUpdateFailedError() { //boost::shared_ptr<IU> iu) { + _description = "IUUpdateFailedError"; + } +};//}}} +/// Requested resend of old IU due to malformed channel specification +IPAACA_HEADER_EXPORT class IUResendRequestFailedError: public Exception//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline ~IUResendRequestFailedError() throw() { } + IPAACA_HEADER_EXPORT inline IUResendRequestFailedError() { //boost::shared_ptr<IU> iu) { + _description = "IUResendRequestFailedError"; + } +};//}}} +/// Write operation failed because IU had been set read-only +IPAACA_HEADER_EXPORT class IUReadOnlyError: public Exception//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline ~IUReadOnlyError() throw() { } + IPAACA_HEADER_EXPORT inline IUReadOnlyError() { //boost::shared_ptr<IU> iu) { + _description = "IUReadOnlyError"; + } +};//}}} +/// Buffer::add() failed because the IU had been previously placed in another buffer +IPAACA_HEADER_EXPORT class IUAlreadyInABufferError: public Exception//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline ~IUAlreadyInABufferError() throw() { } + IPAACA_HEADER_EXPORT inline IUAlreadyInABufferError() { //boost::shared_ptr<IU> iu) { + _description = "IUAlreadyInABufferError"; + } +};//}}} +/// A request was made that is only valid for an already published IU +IPAACA_HEADER_EXPORT class IUUnpublishedError: public Exception//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline ~IUUnpublishedError() throw() { } + IPAACA_HEADER_EXPORT inline IUUnpublishedError() { //boost::shared_ptr<IU> iu) { + _description = "IUUnpublishedError"; + } +};//}}} +/// IU had already been allocated a UID +IPAACA_HEADER_EXPORT class IUAlreadyHasAnUIDError: public Exception//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline ~IUAlreadyHasAnUIDError() throw() { } + IPAACA_HEADER_EXPORT inline IUAlreadyHasAnUIDError() { //boost::shared_ptr<IU> iu) { + _description = "IUAlreadyHasAnUIDError"; + } +};//}}} +/// IU had already been allocated an owner name +IPAACA_HEADER_EXPORT class IUAlreadyHasAnOwnerNameError: public Exception//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline ~IUAlreadyHasAnOwnerNameError() throw() { } + IPAACA_HEADER_EXPORT inline IUAlreadyHasAnOwnerNameError() { //boost::shared_ptr<IU> iu) { + _description = "IUAlreadyHasAnOwnerNameError"; + } +};//}}} +/// UID generation failed (Windows only) +IPAACA_HEADER_EXPORT class UUIDGenerationError: public Exception//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline ~UUIDGenerationError() throw() { } + IPAACA_HEADER_EXPORT inline UUIDGenerationError() { //boost::shared_ptr<IU> iu) { + _description = "UUIDGenerationError"; + } +};//}}} +/// Not implemented (e.g. invalid request parameters via backend) +IPAACA_HEADER_EXPORT class NotImplementedError: public Exception//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline ~NotImplementedError() throw() { } + IPAACA_HEADER_EXPORT inline NotImplementedError() { //boost::shared_ptr<IU> iu) { + _description = "NotImplementedError"; + } +};//}}} +/// PayloadEntryProxy requested type conversion failed (including lenient interpretation) +IPAACA_HEADER_EXPORT class PayloadTypeConversionError: public Exception//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline ~PayloadTypeConversionError() throw() { } + IPAACA_HEADER_EXPORT inline PayloadTypeConversionError() { //boost::shared_ptr<IU> iu) { + _description = "PayloadTypeConversionError"; + } +};//}}} +/// PayloadEntryProxy was addressed as list when not a list or as map when not a map +IPAACA_HEADER_EXPORT class PayloadAddressingError: public Exception//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline ~PayloadAddressingError() throw() { } + IPAACA_HEADER_EXPORT inline PayloadAddressingError() { //boost::shared_ptr<IU> iu) { + _description = "PayloadAddressingError"; + } +};//}}} +/// Malformed json was received for a Payload +IPAACA_HEADER_EXPORT class JsonParsingError: public Exception//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline ~JsonParsingError() throw() { } + IPAACA_HEADER_EXPORT inline JsonParsingError() { //boost::shared_ptr<IU> iu) { + _description = "JsonParsingError"; + } +};//}}} +/// PayloadEntryProxy invalidated (unused) +IPAACA_HEADER_EXPORT class PayloadEntryProxyInvalidatedError: public Exception//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline ~PayloadEntryProxyInvalidatedError() throw() { } + IPAACA_HEADER_EXPORT inline PayloadEntryProxyInvalidatedError() { //boost::shared_ptr<IU> iu) { + _description = "PayloadEntryProxyInvalidatedError"; + } +};//}}} +/// Iterator over Payload entries was invalidated by an intermediate IU update operation +IPAACA_HEADER_EXPORT class PayloadIteratorInvalidError: public Exception//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline ~PayloadIteratorInvalidError() throw() { } + IPAACA_HEADER_EXPORT inline PayloadIteratorInvalidError() { //boost::shared_ptr<IU> iu) { + _description = "PayloadIteratorInvalidError"; + } +};//}}} + +/** \brief Static library initialization for backend + * + * This static class (singleton) is called once (explicitly or on-demand). + * Unless called manually, it is initialized when ipaaca is first used + * (i.e. the first Buffer is created). + */ +IPAACA_HEADER_EXPORT class Initializer +{ + public: + /// Initialize the backend [DEPRECATED] (old name, use initialize_backend() instead) + [[deprecated("Use initialize_backend() instead")]] + IPAACA_HEADER_EXPORT static void initialize_ipaaca_rsb_if_needed(); + /// Explicitly initialize the backend. No effect if already initialized. Automatically called during first Buffer construction. + IPAACA_HEADER_EXPORT static void initialize_backend(); + IPAACA_HEADER_EXPORT static bool initialized(); + IPAACA_HEADER_EXPORT static void dump_current_default_config(); + protected: + IPAACA_HEADER_EXPORT static void auto_configure_rsb(); + IPAACA_MEMBER_VAR_EXPORT static bool _initialized; +}; + +// in ipaaca-cmdline-parser.cc +// additional misc classes ( Command line options )//{{{ +/** \brief Command line argument container for CommandLineParser + * + * Contains the results of argument parsing from CommandLineParser::parse() + * + * The parser is preconfigured to handle some standard options: + * + * Option | Function + * --------------------------------|------------------------------------------------------------------------------ + * --help | Print list of available options + * --verbose | Set verbose flag + * --character-name <name> | Set character name (legacy) + * --component-name <name> | Set component name (legacy) + * --ipaaca-payload-type <type> | Set default ipaaca payload type (default JSON, set STR for legacy protocol) + * --ipaaca-default-channel <name> | Set default channel name (default 'default') + * --ipaaca-enable-logging <level> | Set console log level, one of NONE, DEBUG, INFO, WARNING, ERROR, CRITICAL + * --rsb-enable-logging <level> | Set rsb (transport) log level + * + */ +IPAACA_HEADER_EXPORT class CommandLineOptions { + public: + IPAACA_HEADER_EXPORT inline CommandLineOptions() + //: _unconsumed_argc(0), _unconsumed_argv(nullptr) + { } + IPAACA_HEADER_EXPORT inline ~CommandLineOptions() { + //if (_unconsumed_argv) delete[] _unconsumed_argv; + } + IPAACA_MEMBER_VAR_EXPORT std::map<std::string, std::string> param_opts; + IPAACA_MEMBER_VAR_EXPORT std::map<std::string, bool> param_set; + public: + IPAACA_HEADER_EXPORT void set_option(const std::string& name, bool expect, const char* optarg); + /// Get the option argument or default value (if the option expected an argument) + IPAACA_HEADER_EXPORT std::string get_param(const std::string& o); + /// Check whether option has been set + IPAACA_HEADER_EXPORT bool is_set(const std::string& o); + IPAACA_HEADER_EXPORT void dump(); + public: + //IPAACA_HEADER_EXPORT inline int unconsumed_argc() { return _unconsumed_argc; } + //IPAACA_HEADER_EXPORT inline char** unconsumed_argv() { return _unconsumed_argv; } + protected: + //IPAACA_MEMBER_VAR_EXPORT int _unconsumed_argc; + //IPAACA_MEMBER_VAR_EXPORT char** _unconsumed_argv; + public: + typedef boost::shared_ptr<CommandLineOptions> ptr; +}; + +/** + * \brief Command line parser for ipaaca programs. + * + * The parser is preconfigured to handle some standard options: + * + * Option | Function + * --------------------------------|------------------------------------------------------------------------------ + * --help | Print list of available options + * --verbose | Set verbose flag + * --character-name <name> | Set character name (legacy) + * --component-name <name> | Set component name (legacy) + * --ipaaca-payload-type <type> | Set default ipaaca payload type (default JSON, set STR for legacy protocol) + * --ipaaca-default-channel <name> | Set default channel name (default 'default') + * --ipaaca-enable-logging <level> | Set console log level, one of NONE, DEBUG, INFO, WARNING, ERROR, CRITICAL + * --rsb-enable-logging <level> | Set rsb (transport) log level + * + */ +class CommandLineParser { + protected: + IPAACA_MEMBER_VAR_EXPORT std::map<char, std::string> longopt; // letter->name + IPAACA_MEMBER_VAR_EXPORT std::map<std::string, char> shortopt; // letter->name + IPAACA_MEMBER_VAR_EXPORT std::map<std::string, bool> options; // name / expect_param + IPAACA_MEMBER_VAR_EXPORT std::map<std::string, std::string> defaults; // for opt params + IPAACA_MEMBER_VAR_EXPORT std::map<std::string, int> set_flag; // for paramless opts + protected: + IPAACA_HEADER_EXPORT CommandLineParser(); + IPAACA_MEMBER_VAR_EXPORT bool library_options_handled; + IPAACA_HEADER_EXPORT bool consume_library_option(const std::string& name, bool expect, const char* optarg); + IPAACA_HEADER_EXPORT void ensure_defaults_in( CommandLineOptions::ptr clo ); + public: + IPAACA_HEADER_EXPORT inline ~CommandLineParser() { } + /// Create a new parser object reference. + IPAACA_HEADER_EXPORT static inline boost::shared_ptr<CommandLineParser> create() { + return boost::shared_ptr<CommandLineParser>(new CommandLineParser()); + } + IPAACA_HEADER_EXPORT void initialize_parser_defaults(); + IPAACA_HEADER_EXPORT void dump_options(); + /** \brief Add a user-defined option + * + * \param optname The long option name, e.g. verbose for --verbose + * \param shortn The short option (or \0 for none) + * \param expect_param Whether an argument is expected for the option + * \param defaultv The default string value (unused if expect_param is false) + */ + IPAACA_HEADER_EXPORT void add_option(const std::string& optname, char shortn, bool expect_param, const std::string& defaultv); + /** \brief Parse argument list and return result. + * + * Parse argument list (e.g. from main()) with the parser, consuming the internal options. + * The remaining options are packaged into a CommandLineOptions object. + */ + IPAACA_HEADER_EXPORT CommandLineOptions::ptr parse(int argc, char* const* argv); + typedef boost::shared_ptr<CommandLineParser> ptr; +}; +//}}} +// in ipaaca-string-utils.cc +// additional misc functions ( String splitting / joining )//{{{ +IPAACA_HEADER_EXPORT std::string str_trim(const std::string &s); +IPAACA_HEADER_EXPORT std::string str_join(const std::set<std::string>& set,const std::string& sep); +IPAACA_HEADER_EXPORT std::string str_join(const std::vector<std::string>& vec,const std::string& sep); +IPAACA_HEADER_EXPORT void str_split_wipe(const std::string& str, std::vector<std::string>& tokens, const std::string& delimiters ); +IPAACA_HEADER_EXPORT void str_split_append(const std::string& str, std::vector<std::string>& tokens, const std::string& delimiters ); +//}}} + +#endif diff --git a/ipaacalib/cpp/include/ipaaca/ipaaca-forwards.h b/ipaacalib/cpp/include/ipaaca/ipaaca-forwards.h new file mode 100644 index 0000000000000000000000000000000000000000..0d91d66fcb604b9bea353a8af46ba55b8eec49a3 --- /dev/null +++ b/ipaacalib/cpp/include/ipaaca/ipaaca-forwards.h @@ -0,0 +1,90 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +/** + * \file ipaaca-forwards.h + * + * \brief Header file for forward definitions. + * + * Users should not include this file directly, but use ipaaca.h + * + * \author Ramin Yaghoubzadeh (ryaghoubzadeh@uni-bielefeld.de) + * \date March, 2015 + */ + +#ifndef __ipaaca_forwards_h_INCLUDED__ +#define __ipaaca_forwards_h_INCLUDED__ + +#ifndef __ipaaca_h_INCLUDED__ +#error "Please do not include this file directly, use ipaaca.h instead" +#endif + +/* + * forward declarations + */ +class PayloadDocumentEntry; +//class PayloadDocumentStore; + +class PayloadBatchUpdateLock; +class PayloadEntryProxy; +class Payload; +class PayloadIterator; +class IUInterface; +class IU; +class Message; +class RemotePushIU; +class IULinkUpdate; +class IUPayloadUpdate; +class IUStore; +class FrozenIUStore; +class Buffer; +class InputBuffer; +class OutputBuffer; + +class CallbackIUPayloadUpdate; +class CallbackIULinkUpdate; +class CallbackIUCommission; +class CallbackIUResendRequest; +class CallbackIURetraction; + +class IUConverter; +class MessageConverter; +class IUPayloadUpdateConverter; +class IULinkUpdateConverter; +class IntConverter; + +class BufferConfiguration; +class BufferConfigurationBuilder; + +#endif + diff --git a/ipaacalib/cpp/include/ipaaca/ipaaca-internal.h b/ipaacalib/cpp/include/ipaaca/ipaaca-internal.h new file mode 100644 index 0000000000000000000000000000000000000000..b5609fde3d2a301c1c76fe9fe1067e74d4f05c4a --- /dev/null +++ b/ipaacalib/cpp/include/ipaaca/ipaaca-internal.h @@ -0,0 +1,136 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +/** + * \file ipaaca-internal.h + * + * \brief Header file for internal transport implementation (RSB). + * + * Users should not include this file directly, but use ipaaca.h + * + * \b Note: This file is only included during compilation of ipaaca, + * for regular use, the full internal API is not exposed. + * Users generally need never touch the internal transport layer. + * + * The file provides callback glue from RSB, and wire converters + * for the respective ipaaca classes. + * + * \author Ramin Yaghoubzadeh (ryaghoubzadeh@uni-bielefeld.de) + * \date March, 2015 + */ + +#ifndef __ipaaca_internal_h_INCLUDED__ +#define __ipaaca_internal_h_INCLUDED__ + +#ifndef __ipaaca_h_INCLUDED__ +#error "Please do not include this file directly, use ipaaca.h instead" +#endif + + +#ifdef IPAACA_EXPOSE_FULL_RSB_API + +// ??? ///IPAACA_HEADER_EXPORT inline std::string json_to_string(PayloadDocumentEntry::ptr entry); + +IPAACA_HEADER_EXPORT class CallbackIUPayloadUpdate: public rsb::patterns::Server::Callback<IUPayloadUpdate, int> {//{{{ + protected: + IPAACA_MEMBER_VAR_EXPORT Buffer* _buffer; + public: + IPAACA_HEADER_EXPORT CallbackIUPayloadUpdate(Buffer* buffer); + IPAACA_HEADER_EXPORT boost::shared_ptr<int> call(const std::string& methodName, boost::shared_ptr<IUPayloadUpdate> update); +};//}}} +IPAACA_HEADER_EXPORT class CallbackIULinkUpdate: public rsb::patterns::Server::Callback<IULinkUpdate, int> {//{{{ + protected: + IPAACA_MEMBER_VAR_EXPORT Buffer* _buffer; + public: + IPAACA_HEADER_EXPORT CallbackIULinkUpdate(Buffer* buffer); + public: + IPAACA_HEADER_EXPORT boost::shared_ptr<int> call(const std::string& methodName, boost::shared_ptr<IULinkUpdate> update); +};//}}} +IPAACA_HEADER_EXPORT class CallbackIUCommission: public rsb::patterns::Server::Callback<protobuf::IUCommission, int> {//{{{ + protected: + IPAACA_MEMBER_VAR_EXPORT Buffer* _buffer; + public: + IPAACA_HEADER_EXPORT CallbackIUCommission(Buffer* buffer); + public: + IPAACA_HEADER_EXPORT boost::shared_ptr<int> call(const std::string& methodName, boost::shared_ptr<protobuf::IUCommission> update); +};//}}} +IPAACA_HEADER_EXPORT class CallbackIUResendRequest: public rsb::patterns::Server::Callback<protobuf::IUResendRequest, int> {//{{{ + protected: + IPAACA_MEMBER_VAR_EXPORT Buffer* _buffer; + public: + IPAACA_HEADER_EXPORT CallbackIUResendRequest(Buffer* buffer); + public: + IPAACA_HEADER_EXPORT boost::shared_ptr<int> call(const std::string& methodName, boost::shared_ptr<protobuf::IUResendRequest> update); +};//}}} +IPAACA_HEADER_EXPORT class CallbackIURetraction: public rsb::patterns::Server::Callback<protobuf::IURetraction, int> {//{{{ + protected: + IPAACA_MEMBER_VAR_EXPORT Buffer* _buffer; + public: + IPAACA_HEADER_EXPORT CallbackIURetraction(Buffer* buffer); + public: + IPAACA_HEADER_EXPORT boost::shared_ptr<int> call(const std::string& methodName, boost::shared_ptr<protobuf::IURetraction> update); +};//}}} + +IPAACA_HEADER_EXPORT class IUConverter: public rsb::converter::Converter<std::string> {//{{{ + public: + IPAACA_HEADER_EXPORT IUConverter(); + IPAACA_HEADER_EXPORT std::string serialize(const rsb::AnnotatedData& data, std::string& wire); + IPAACA_HEADER_EXPORT rsb::AnnotatedData deserialize(const std::string& wireSchema, const std::string& wire); +};//}}} +IPAACA_HEADER_EXPORT class MessageConverter: public rsb::converter::Converter<std::string> {//{{{ + public: + IPAACA_HEADER_EXPORT MessageConverter(); + IPAACA_HEADER_EXPORT std::string serialize(const rsb::AnnotatedData& data, std::string& wire); + IPAACA_HEADER_EXPORT rsb::AnnotatedData deserialize(const std::string& wireSchema, const std::string& wire); +};//}}} +IPAACA_HEADER_EXPORT class IUPayloadUpdateConverter: public rsb::converter::Converter<std::string> {//{{{ + public: + IPAACA_HEADER_EXPORT IUPayloadUpdateConverter(); + IPAACA_HEADER_EXPORT std::string serialize(const rsb::AnnotatedData& data, std::string& wire); + IPAACA_HEADER_EXPORT rsb::AnnotatedData deserialize(const std::string& wireSchema, const std::string& wire); +};//}}} +IPAACA_HEADER_EXPORT class IULinkUpdateConverter: public rsb::converter::Converter<std::string> {//{{{ + public: + IPAACA_HEADER_EXPORT IULinkUpdateConverter(); + IPAACA_HEADER_EXPORT std::string serialize(const rsb::AnnotatedData& data, std::string& wire); + IPAACA_HEADER_EXPORT rsb::AnnotatedData deserialize(const std::string& wireSchema, const std::string& wire); +};//}}} +IPAACA_HEADER_EXPORT class IntConverter: public rsb::converter::Converter<std::string> {//{{{ + public: + IPAACA_HEADER_EXPORT IntConverter(); + IPAACA_HEADER_EXPORT std::string serialize(const rsb::AnnotatedData& data, std::string& wire); + IPAACA_HEADER_EXPORT rsb::AnnotatedData deserialize(const std::string& wireSchema, const std::string& wire); +};//}}} +#endif + +#endif diff --git a/ipaacalib/cpp/include/ipaaca/ipaaca-ius.h b/ipaacalib/cpp/include/ipaaca/ipaaca-ius.h new file mode 100644 index 0000000000000000000000000000000000000000..f4fc8fc6044e9c1cd70ca39250f98695a9a1c986 --- /dev/null +++ b/ipaacalib/cpp/include/ipaaca/ipaaca-ius.h @@ -0,0 +1,322 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +/** + * \file ipaaca-ius.h + * + * \brief Header file for IU (incremental unit) classes. + * + * Users should not include this file directly, but use ipaaca.h + * + * \author Ramin Yaghoubzadeh (ryaghoubzadeh@uni-bielefeld.de) + * \date March, 2015 + */ + +#ifndef __ipaaca_ius_h_INCLUDED__ +#define __ipaaca_ius_h_INCLUDED__ + +#ifndef __ipaaca_h_INCLUDED__ +#error "Please do not include this file directly, use ipaaca.h instead" +#endif + + +/** \brief Abstract base class for all IU-type classes + * + * In user programs, classes IU or Message should be instantiated. + * + * This abstract type is used in handler callback functions and + * contains most generic user-space functions. + * + */ +IPAACA_HEADER_EXPORT class IUInterface {//{{{ + friend class IUConverter; + friend class MessageConverter; + friend std::ostream& operator<<(std::ostream& os, const IUInterface& obj); + protected: + IPAACA_HEADER_EXPORT IUInterface(); + public: + IPAACA_HEADER_EXPORT inline virtual ~IUInterface() { } + protected: + IPAACA_MEMBER_VAR_EXPORT std::string _uid; + IPAACA_MEMBER_VAR_EXPORT revision_t _revision; + IPAACA_MEMBER_VAR_EXPORT std::string _category; + IPAACA_MEMBER_VAR_EXPORT std::string _payload_type; // default is taken from __ipaaca_static_option_default_payload_type + IPAACA_MEMBER_VAR_EXPORT std::string _owner_name; + IPAACA_MEMBER_VAR_EXPORT bool _committed; + IPAACA_MEMBER_VAR_EXPORT bool _retracted; + IPAACA_MEMBER_VAR_EXPORT IUAccessMode _access_mode; + IPAACA_MEMBER_VAR_EXPORT bool _read_only; + //boost::shared_ptr<Buffer> _buffer; + IPAACA_MEMBER_VAR_EXPORT Buffer* _buffer; + IPAACA_MEMBER_VAR_EXPORT SmartLinkMap _links; + protected: + friend class Payload; + // Internal functions that perform the update logic, + // e.g. sending a notification across the network + IPAACA_HEADER_EXPORT _IPAACA_ABSTRACT_ virtual void _modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name) = 0; + IPAACA_HEADER_EXPORT _IPAACA_ABSTRACT_ virtual void _modify_payload(bool is_delta, const std::map<std::string, PayloadDocumentEntry::ptr>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name) = 0; + //void _set_buffer(boost::shared_ptr<Buffer> buffer); + IPAACA_HEADER_EXPORT void _associate_with_buffer(Buffer* buffer); + IPAACA_HEADER_EXPORT void _set_buffer(Buffer* buffer); + IPAACA_HEADER_EXPORT void _set_uid(const std::string& uid); + IPAACA_HEADER_EXPORT void _set_owner_name(const std::string& owner_name); + protected: + // internal functions that do not emit update events + IPAACA_HEADER_EXPORT void _add_and_remove_links(const LinkMap& add, const LinkMap& remove) { _links._add_and_remove_links(add, remove); } + IPAACA_HEADER_EXPORT void _replace_links(const LinkMap& links) { _links._replace_links(links); } + public: + /// Return whether IU has already been published (is in a Buffer). + IPAACA_HEADER_EXPORT inline bool is_published() { return (_buffer != 0); } + /// Return auto-generated UID string (set during IU construction) + IPAACA_HEADER_EXPORT inline const std::string& uid() const { return _uid; } + /// Return current IU revision number (incremented for each update) + IPAACA_HEADER_EXPORT inline revision_t revision() const { return _revision; } + /// Return the IU category string (set during IU construction) + IPAACA_HEADER_EXPORT inline const std::string& category() const { return _category; } + /// Return the channel name the IU is resident on (set on publication) + IPAACA_HEADER_EXPORT const std::string& channel(); + /// Return the payload type (default "JSON") + IPAACA_HEADER_EXPORT inline const std::string& payload_type() const { return _payload_type; } + /// Return the owner name (unique fully-qualified buffer name, set on publication) + IPAACA_HEADER_EXPORT inline const std::string& owner_name() const { return _owner_name; } + /// Return whether IU has been committed to (i.e. is complete, confirmed, and henceforth constant) + IPAACA_HEADER_EXPORT inline bool committed() const { return _committed; } + /// Return the access mode (not relevant for the time being) + IPAACA_HEADER_EXPORT inline IUAccessMode access_mode() const { return _access_mode; } + /// Return whether IU is read only (committed, a Message, or explicitly set read-only by owner) + IPAACA_HEADER_EXPORT inline bool read_only() const { return _read_only; } + //inline boost::shared_ptr<Buffer> buffer() { return _buffer; } + /// Return owning buffer [CAVEAT: do not rely on this function for future code] + IPAACA_HEADER_EXPORT inline Buffer* buffer() const { return _buffer; } + /// Return the link set for an arbitrary link type (e.g. "grounded_in"), or EMPTY_LINK_SET + IPAACA_HEADER_EXPORT inline const LinkSet& get_links(std::string type) { return _links.get_links(type); } + /// Return the map of all defined links + IPAACA_HEADER_EXPORT inline const LinkMap& get_all_links() { return _links.get_all_links(); } + // Payload + /// Return the Payload object of this IU, overridden in the derived classes + IPAACA_HEADER_EXPORT _IPAACA_ABSTRACT_ virtual Payload& payload() = 0; + /// Const version of payload() + IPAACA_HEADER_EXPORT _IPAACA_ABSTRACT_ virtual const Payload& const_payload() const = 0; + // setters + /// Commit to an IU (only possible for the IU owner) + IPAACA_HEADER_EXPORT _IPAACA_ABSTRACT_ virtual void commit() = 0; + // functions to modify and update links: + //IPAACA_HEADER_EXPORT void _publish_resend(boost::shared_ptr<IU> iu, const std::string& hidden_scope_name); + + /// Add a set of new UIDs for a specific link type + IPAACA_HEADER_EXPORT void add_links(const std::string& type, const LinkSet& targets, const std::string& writer_name = ""); + /// Remove a set of UIDs from a link type + IPAACA_HEADER_EXPORT void remove_links(const std::string& type, const LinkSet& targets, const std::string& writer_name = ""); + /// Bulk link modification function + IPAACA_HEADER_EXPORT void modify_links(const LinkMap& add, const LinkMap& remove, const std::string& writer_name = ""); + /// Bulk link override function + IPAACA_HEADER_EXPORT void set_links(const LinkMap& links, const std::string& writer_name = ""); + // (with cpp specific convenience functions:) + /// Convenience function (C++): add a single UID string to an arbitrary link set + IPAACA_HEADER_EXPORT void add_link(const std::string& type, const std::string& target, const std::string& writer_name = ""); + /// Convenience function (C++): remove a single UID string from an arbitrary link set (if contained) + IPAACA_HEADER_EXPORT void remove_link(const std::string& type, const std::string& target, const std::string& writer_name = ""); + typedef boost::shared_ptr<IUInterface> ptr; +};//}}} + +/** \brief Class of locally-owned IU objects. + * + * Use IU::create() (static) to create references to new IUs. + * Use IU::payload() to access the IUs payload object. + * Use OutputBuffer::add() to publish IUs. + * + * See IUInterface for a generic description of most user-space member functions. + */ +IPAACA_HEADER_EXPORT class IU: public IUInterface {//{{{ + friend class Buffer; + friend class InputBuffer; + friend class OutputBuffer; + friend class CallbackIUPayloadUpdate; + friend class CallbackIULinkUpdate; + friend class CallbackIUCommission; + friend class CallbackIUResendRequest; + public: + IPAACA_MEMBER_VAR_EXPORT Payload _payload; + protected: + IPAACA_MEMBER_VAR_EXPORT Lock _revision_lock; + protected: + IPAACA_HEADER_EXPORT inline void _increase_revision_number() { _revision++; } + IPAACA_HEADER_EXPORT IU(const std::string& category, IUAccessMode access_mode=IU_ACCESS_PUSH, bool read_only=false, const std::string& payload_type="" ); // __ipaaca_static_option_default_payload_type + public: + IPAACA_HEADER_EXPORT inline ~IU() { + //IPAACA_IMPLEMENT_ME + } + [[deprecated("Please use the new argument order: category, payload_type, read_only")]] + IPAACA_HEADER_EXPORT static boost::shared_ptr<IU> create(const std::string& category, IUAccessMode access_mode, bool read_only=false, const std::string& payload_type="" ); + IPAACA_HEADER_EXPORT static boost::shared_ptr<IU> create(const std::string& category, const std::string& payload_type="", bool read_only=false); + IPAACA_HEADER_EXPORT inline Payload& payload() _IPAACA_OVERRIDE_ { return _payload; } + IPAACA_HEADER_EXPORT inline const Payload& const_payload() const _IPAACA_OVERRIDE_ { return _payload; } + IPAACA_HEADER_EXPORT void commit() _IPAACA_OVERRIDE_; + protected: + IPAACA_HEADER_EXPORT virtual void _modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name = "") _IPAACA_OVERRIDE_; + + //IPAACA_HEADER_EXPORT virtual void _publish_resend(boost::shared_ptr<IU> iu, const std::string& hidden_scope_name); + + IPAACA_HEADER_EXPORT virtual void _modify_payload(bool is_delta, const std::map<std::string, PayloadDocumentEntry::ptr>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name = "") _IPAACA_OVERRIDE_; + protected: + IPAACA_HEADER_EXPORT virtual void _internal_commit(const std::string& writer_name = ""); + public: + typedef boost::shared_ptr<IU> ptr; +};//}}} +/** \brief Class of locally-owned message objects ('one-shot' IUs). + * + * This class works the same as IU, except that it sets the internal + * flags so that it is received as a message (ephemeral object) on + * the remote sides, instead of a persistent objects. + * + * Likewise, it is not actually stored by OutputBuffer::add(), but just broadcast. + * + * See IUInterface for a generic description of most user-space member functions. + * + * \see IU, IUInterface + */ +IPAACA_HEADER_EXPORT class Message: public IU {//{{{ + friend class Buffer; + friend class InputBuffer; + friend class OutputBuffer; + friend class CallbackIUPayloadUpdate; + friend class CallbackIULinkUpdate; + friend class CallbackIUCommission; + friend class CallbackIUResendRequest; + protected: + IPAACA_HEADER_EXPORT Message(const std::string& category, IUAccessMode access_mode=IU_ACCESS_MESSAGE, bool read_only=true, const std::string& payload_type="" ); + public: + IPAACA_HEADER_EXPORT inline ~Message() { + //IPAACA_IMPLEMENT_ME + } + [[deprecated("Please use the new argument order: category, payload_type")]] + IPAACA_HEADER_EXPORT static boost::shared_ptr<Message> create(const std::string& category, IUAccessMode access_mode, bool read_only=true, const std::string& payload_type="" ); + IPAACA_HEADER_EXPORT static boost::shared_ptr<Message> create(const std::string& category, const std::string& payload_type=""); + protected: + IPAACA_HEADER_EXPORT void _modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name = "") _IPAACA_OVERRIDE_; + IPAACA_HEADER_EXPORT void _modify_payload(bool is_delta, const std::map<std::string, PayloadDocumentEntry::ptr>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name = "") _IPAACA_OVERRIDE_; + protected: + IPAACA_HEADER_EXPORT void _internal_commit(const std::string& writer_name = ""); + public: + typedef boost::shared_ptr<Message> ptr; +};//}}} + +/// Copy of a remote IU, received in an InputBuffer. Setter functions call RPC over the backend (RSB). \b Note: Typically handled only as reference in a handler in user space. +IPAACA_HEADER_EXPORT class RemotePushIU: public IUInterface {//{{{ + friend class Buffer; + friend class InputBuffer; + friend class OutputBuffer; + friend class IUConverter; + friend class MessageConverter; + public: + IPAACA_MEMBER_VAR_EXPORT Payload _payload; + protected: + IPAACA_HEADER_EXPORT RemotePushIU(); + IPAACA_HEADER_EXPORT static boost::shared_ptr<RemotePushIU> create(); + public: + IPAACA_HEADER_EXPORT inline ~RemotePushIU() { + //IPAACA_IMPLEMENT_ME + } + IPAACA_HEADER_EXPORT inline Payload& payload() _IPAACA_OVERRIDE_ { return _payload; } + IPAACA_HEADER_EXPORT inline const Payload& const_payload() const _IPAACA_OVERRIDE_ { return _payload; } + IPAACA_HEADER_EXPORT void commit() _IPAACA_OVERRIDE_; + protected: + IPAACA_HEADER_EXPORT void _modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name = "") _IPAACA_OVERRIDE_; + IPAACA_HEADER_EXPORT void _modify_payload(bool is_delta, const std::map<std::string, PayloadDocumentEntry::ptr>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name = "") _IPAACA_OVERRIDE_; + protected: + IPAACA_HEADER_EXPORT void _apply_update(IUPayloadUpdate::ptr update); + IPAACA_HEADER_EXPORT void _apply_link_update(IULinkUpdate::ptr update); + IPAACA_HEADER_EXPORT void _apply_commission(); + IPAACA_HEADER_EXPORT void _apply_retraction(); + typedef boost::shared_ptr<RemotePushIU> ptr; +};//}}} +/// Copy of a remote Message, received in an InputBuffer. Setter functions all fail.\b Note: Typically handled only as reference in a handler in user space. +IPAACA_HEADER_EXPORT class RemoteMessage: public IUInterface {//{{{ + friend class Buffer; + friend class InputBuffer; + friend class OutputBuffer; + friend class IUConverter; + friend class MessageConverter; + public: + IPAACA_MEMBER_VAR_EXPORT Payload _payload; + protected: + IPAACA_HEADER_EXPORT RemoteMessage(); + IPAACA_HEADER_EXPORT static boost::shared_ptr<RemoteMessage> create(); + public: + IPAACA_HEADER_EXPORT inline ~RemoteMessage() { + //IPAACA_IMPLEMENT_ME + } + IPAACA_HEADER_EXPORT inline Payload& payload() _IPAACA_OVERRIDE_ { return _payload; } + IPAACA_HEADER_EXPORT inline const Payload& const_payload() const _IPAACA_OVERRIDE_ { return _payload; } + IPAACA_HEADER_EXPORT void commit() _IPAACA_OVERRIDE_; + protected: + IPAACA_HEADER_EXPORT void _modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name = "") _IPAACA_OVERRIDE_; + IPAACA_HEADER_EXPORT void _modify_payload(bool is_delta, const std::map<std::string, PayloadDocumentEntry::ptr>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name = "") _IPAACA_OVERRIDE_; + protected: + IPAACA_HEADER_EXPORT void _apply_update(IUPayloadUpdate::ptr update); + IPAACA_HEADER_EXPORT void _apply_link_update(IULinkUpdate::ptr update); + IPAACA_HEADER_EXPORT void _apply_commission(); + IPAACA_HEADER_EXPORT void _apply_retraction(); + typedef boost::shared_ptr<RemoteMessage> ptr; +};//}}} + +/// Mock IU for testing purposes. [INTERNAL] +IPAACA_HEADER_EXPORT class FakeIU: public IUInterface {//{{{ + friend class Buffer; + friend class InputBuffer; + friend class OutputBuffer; + friend class IUConverter; + friend class MessageConverter; + protected: + IPAACA_MEMBER_VAR_EXPORT Payload _payload; + IPAACA_HEADER_EXPORT FakeIU(); + public: + IPAACA_HEADER_EXPORT static boost::shared_ptr<FakeIU> create(); + IPAACA_HEADER_EXPORT ~FakeIU(); + IPAACA_HEADER_EXPORT Payload& payload() _IPAACA_OVERRIDE_; + IPAACA_HEADER_EXPORT const Payload& const_payload() const _IPAACA_OVERRIDE_; + IPAACA_HEADER_EXPORT void commit() _IPAACA_OVERRIDE_; + IPAACA_HEADER_EXPORT void add_fake_payload_item(const std::string& key, PayloadDocumentEntry::ptr entry); + protected: + IPAACA_HEADER_EXPORT void _modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name = "") _IPAACA_OVERRIDE_; + IPAACA_HEADER_EXPORT void _modify_payload(bool is_delta, const std::map<std::string, PayloadDocumentEntry::ptr>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name = "") _IPAACA_OVERRIDE_; + protected: + IPAACA_HEADER_EXPORT void _apply_update(IUPayloadUpdate::ptr update); + IPAACA_HEADER_EXPORT void _apply_link_update(IULinkUpdate::ptr update); + IPAACA_HEADER_EXPORT void _apply_commission(); + IPAACA_HEADER_EXPORT void _apply_retraction(); + public: + typedef boost::shared_ptr<FakeIU> ptr; +};//}}} + +#endif diff --git a/ipaacalib/cpp/include/ipaaca/ipaaca-json.h b/ipaacalib/cpp/include/ipaaca/ipaaca-json.h new file mode 100644 index 0000000000000000000000000000000000000000..30cac6c0594617ea28efd941bee08840b07eac93 --- /dev/null +++ b/ipaacalib/cpp/include/ipaaca/ipaaca-json.h @@ -0,0 +1,61 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +/** + * \file ipaaca-json.h + * + * \brief Header file for JSON tests. [superfluous] + * + * Users should not include this file directly, but use ipaaca.h + * + * \author Ramin Yaghoubzadeh (ryaghoubzadeh@uni-bielefeld.de) + * \date March, 2015 + */ + +#ifndef __ipaaca_json_H__ +#define __ipaaca_json_H__ + +#include "rapidjson/document.h" +#include "rapidjson/prettywriter.h" +#include "rapidjson/filestream.h" +#include <cstdio> + +// Notes: +// - From http://stackoverflow.com/questions/10426924/json-root-element +// Actually there are two different JSON specifications. RFC 4627 requires a JSON text to be +// an object or an array. ECMA-262, 5th edition, section 15.12 does not impose this restriction. + + + +#endif // __IPAACA_JSON_H__ + diff --git a/ipaacalib/cpp/include/ipaaca/ipaaca-locking.h b/ipaacalib/cpp/include/ipaaca/ipaaca-locking.h new file mode 100644 index 0000000000000000000000000000000000000000..c4519ed77176466eab03f7a03ebebcf4c573fbe0 --- /dev/null +++ b/ipaacalib/cpp/include/ipaaca/ipaaca-locking.h @@ -0,0 +1,156 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +/** + * \file ipaaca-locking.h + * + * \brief Header file for locking / mutexes. + * + * Users should not include this file directly, but use ipaaca.h + * + * \author Ramin Yaghoubzadeh (ryaghoubzadeh@uni-bielefeld.de) + * \date March, 2015 + */ + +#ifndef __ipaaca_locking_h_INCLUDED__ +#define __ipaaca_locking_h_INCLUDED__ + +#ifndef __ipaaca_h_INCLUDED__ +#error "Please do not include this file directly, use ipaaca.h instead" +#endif + + +/// Reentrant lock/mutex class +#ifdef WIN32 +#include <boost/thread/recursive_mutex.hpp> +#include <boost/thread/thread.hpp> +IPAACA_HEADER_EXPORT class Lock +{ + protected: + IPAACA_MEMBER_VAR_EXPORT boost::recursive_mutex _mutex; + public: + IPAACA_HEADER_EXPORT inline Lock() { + } + IPAACA_HEADER_EXPORT inline ~Lock() { + } + IPAACA_HEADER_EXPORT inline void lock() { + _mutex.lock(); + on_lock(); + } + IPAACA_HEADER_EXPORT inline void unlock() { + on_unlock(); + _mutex.unlock(); + } + IPAACA_HEADER_EXPORT virtual inline void on_lock() { + } + IPAACA_HEADER_EXPORT virtual inline void on_unlock() { + } +}; +#else +#include <pthread.h> +IPAACA_HEADER_EXPORT class Lock +{ + protected: + IPAACA_MEMBER_VAR_EXPORT pthread_mutexattr_t _attrs; + IPAACA_MEMBER_VAR_EXPORT pthread_mutex_t _mutex; + public: + IPAACA_HEADER_EXPORT inline Lock() { + pthread_mutexattr_init(&_attrs); + pthread_mutexattr_settype(&_attrs, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&_mutex, &_attrs); + } + IPAACA_HEADER_EXPORT inline ~Lock() { + pthread_mutex_destroy(&_mutex); + pthread_mutexattr_destroy(&_attrs); + } + IPAACA_HEADER_EXPORT inline void lock() { + pthread_mutex_lock(&_mutex); + on_lock(); + } + IPAACA_HEADER_EXPORT inline void unlock() { + on_unlock(); + pthread_mutex_unlock(&_mutex); + } + IPAACA_HEADER_EXPORT virtual inline void on_lock() { + } + IPAACA_HEADER_EXPORT virtual inline void on_unlock() { + } +}; +#endif + +/** \brief Stack-based lock holder. + * + * Stack-based lock holder. Create in a new stack frame + * (i.e. {}-block) and it will obtain the lock and + * auto-release in on exiting the stack frame. + */ +IPAACA_HEADER_EXPORT class Locker +{ + protected: + IPAACA_MEMBER_VAR_EXPORT Lock* _lock; + private: + IPAACA_HEADER_EXPORT inline Locker(): _lock(NULL) { } // not available + public: + IPAACA_HEADER_EXPORT inline Locker(Lock& lock): _lock(&lock) { + _lock->lock(); + } + IPAACA_HEADER_EXPORT inline ~Locker() { + _lock->unlock(); + } +}; + +/** \brief Locker for existing pthread mutexes. + * + * Stack-based lock holder for existing pthread_mutex_t mutexes. + * + * \see Locker + */ +#ifndef WIN32 +IPAACA_HEADER_EXPORT class PthreadMutexLocker +{ + protected: + IPAACA_MEMBER_VAR_EXPORT pthread_mutex_t* _lock; + private: + IPAACA_HEADER_EXPORT inline PthreadMutexLocker(): _lock(NULL) { } // not available + public: + IPAACA_HEADER_EXPORT inline PthreadMutexLocker(pthread_mutex_t* lock): _lock(lock) { + if (!lock) throw Exception("PthreadMutexLocker got a NULL mutex!"); + pthread_mutex_lock(_lock); + } + IPAACA_HEADER_EXPORT inline ~PthreadMutexLocker() { + pthread_mutex_unlock(_lock); + } +}; +#endif + +#endif diff --git a/ipaacalib/cpp/include/ipaaca/ipaaca-payload.h b/ipaacalib/cpp/include/ipaaca/ipaaca-payload.h new file mode 100644 index 0000000000000000000000000000000000000000..dcf33e81d3d7ecebff94ff721e1e669b1ca0e17d --- /dev/null +++ b/ipaacalib/cpp/include/ipaaca/ipaaca-payload.h @@ -0,0 +1,621 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +/** + * \file ipaaca-payload.h + * + * \brief Header file for IU payload functionality. + * + * Users should not include this file directly, but use ipaaca.h + * + * \author Ramin Yaghoubzadeh (ryaghoubzadeh@uni-bielefeld.de) + * \date March, 2015 + */ + +#ifndef __ipaaca_payload_h_INCLUDED__ +#define __ipaaca_payload_h_INCLUDED__ + +#include <typeinfo> + +#ifndef __ipaaca_h_INCLUDED__ +#error "Please do not include this file directly, use ipaaca.h instead" +#endif + +// casting operators from Value& +/// 'Smart' type conversions, allowing for some leeway type-wise (e.g. string "1.3" can be successfully cast to double or long). Used by PayloadEntryProxy. +IPAACA_HEADER_EXPORT template<typename T> T json_value_cast(const rapidjson::Value&); +IPAACA_HEADER_EXPORT template<typename T> T json_value_cast(const rapidjson::Value* value) { if (!value) return T(); return json_value_cast<T>(*value); } +IPAACA_HEADER_EXPORT template<> long json_value_cast(const rapidjson::Value&); +IPAACA_HEADER_EXPORT template<> int json_value_cast(const rapidjson::Value&); +IPAACA_HEADER_EXPORT template<> double json_value_cast(const rapidjson::Value&); +IPAACA_HEADER_EXPORT template<> bool json_value_cast(const rapidjson::Value&); +IPAACA_HEADER_EXPORT template<> std::string json_value_cast(const rapidjson::Value&); +IPAACA_HEADER_EXPORT template<> std::vector<std::string> json_value_cast(const rapidjson::Value&); +IPAACA_HEADER_EXPORT template<> std::list<std::string> json_value_cast(const rapidjson::Value&); +IPAACA_HEADER_EXPORT template<> std::map<std::string, std::string> json_value_cast(const rapidjson::Value&); + +// helpers to set Value& from various standard types +//IPAACA_HEADER_EXPORT template<typename T> void pack_into_json_value(rapidjson::Value&, rapidjson::Document::AllocatorType&, T t); +/// Setter to store int into json value, used by PayloadEntryProxy. +IPAACA_HEADER_EXPORT void pack_into_json_value(rapidjson::Value&, rapidjson::Document::AllocatorType&, int); +/// Setter to store int into json value, used by PayloadEntryProxy. +IPAACA_HEADER_EXPORT void pack_into_json_value(rapidjson::Value&, rapidjson::Document::AllocatorType&, long); +/// Setter to store long into json value, used by PayloadEntryProxy. +IPAACA_HEADER_EXPORT void pack_into_json_value(rapidjson::Value&, rapidjson::Document::AllocatorType&, double); +/// Setter to store double into json value, used by PayloadEntryProxy. +IPAACA_HEADER_EXPORT void pack_into_json_value(rapidjson::Value&, rapidjson::Document::AllocatorType&, bool); +/// Setter to store bool into json value, used by PayloadEntryProxy. +IPAACA_HEADER_EXPORT void pack_into_json_value(rapidjson::Value&, rapidjson::Document::AllocatorType&, const std::string&); +/// Setter to store std::string into json value, used by PayloadEntryProxy. +IPAACA_HEADER_EXPORT void pack_into_json_value(rapidjson::Value&, rapidjson::Document::AllocatorType&, const char*); +// helpers to set Value& from several standard containers containing the above standard types +/// Setter to store a vector of supported basic types into json value, used by PayloadEntryProxy. +IPAACA_HEADER_EXPORT template<typename T> void pack_into_json_value(rapidjson::Value& valueobject, rapidjson::Document::AllocatorType& allocator, const std::vector<T>& ts) +{ + valueobject.SetArray(); + for (auto& val: ts) { + rapidjson::Value newv; + pack_into_json_value(newv, allocator, val); + valueobject.PushBack(newv, allocator); + } +} +/// Setter to store a list of supported basic types into json value, used by PayloadEntryProxy. +IPAACA_HEADER_EXPORT template<typename T> void pack_into_json_value(rapidjson::Value& valueobject, rapidjson::Document::AllocatorType& allocator, const std::list<T>& ts) +{ + valueobject.SetArray(); + for (auto& val: ts) { + rapidjson::Value newv; + pack_into_json_value(newv, allocator, val); + valueobject.PushBack(newv, allocator); + } +} +/// Setter to store a map of string -> supported basic types into json value, used by PayloadEntryProxy. +IPAACA_HEADER_EXPORT template<typename T> void pack_into_json_value(rapidjson::Value& valueobject, rapidjson::Document::AllocatorType& allocator, const std::map<std::string, T>& ts) +{ + valueobject.SetObject(); + for (auto& val: ts) { + rapidjson::Value key; + key.SetString(val.first, allocator); + rapidjson::Value newv; + pack_into_json_value(newv, allocator, val.second); + valueobject.AddMember(key, newv, allocator); + } +} +/*IPAACA_HEADER_EXPORT template<> void pack_into_json_value(rapidjson::Value&, rapidjson::Document::AllocatorType&, const std::vector<std::string>&); +IPAACA_HEADER_EXPORT template<> void pack_into_json_value(rapidjson::Value&, rapidjson::Document::AllocatorType&, const std::list<std::string>&); +IPAACA_HEADER_EXPORT template<> void pack_into_json_value(rapidjson::Value&, rapidjson::Document::AllocatorType&, const std::map<std::string, std::string>&); +*/ + +// FIXME TODO locking / invalidating proxy on first write of a payload entry + +/// Single payload entry wrapping a rapidjson::Document with some conversion glue. Also handles copy-on-write Document cloning. <b>Internal type</b> - users generally do not see this. +IPAACA_HEADER_EXPORT class PayloadDocumentEntry//{{{ +{ + friend std::ostream& operator<<(std::ostream& os, std::shared_ptr<PayloadDocumentEntry> entry); + public: + IPAACA_MEMBER_VAR_EXPORT ipaaca::Lock lock; + IPAACA_MEMBER_VAR_EXPORT bool modified; + //IPAACA_MEMBER_VAR_EXPORT std::string json_source; + IPAACA_MEMBER_VAR_EXPORT rapidjson::Document document; + IPAACA_HEADER_EXPORT inline PayloadDocumentEntry(): modified(false) { } + IPAACA_HEADER_EXPORT inline ~PayloadDocumentEntry() { } + //IPAACA_HEADER_EXPORT PayloadDocumentEntry(const std::string& source): modified(false), json_source(source), {}; + IPAACA_HEADER_EXPORT std::string to_json_string_representation(); + IPAACA_HEADER_EXPORT static std::shared_ptr<PayloadDocumentEntry> from_json_string_representation(const std::string& input); + IPAACA_HEADER_EXPORT static std::shared_ptr<PayloadDocumentEntry> from_unquoted_string_value(const std::string& input); + IPAACA_HEADER_EXPORT static std::shared_ptr<PayloadDocumentEntry> create_null(); + IPAACA_HEADER_EXPORT std::shared_ptr<PayloadDocumentEntry> clone(); + IPAACA_HEADER_EXPORT rapidjson::Value& get_or_create_nested_value_from_proxy_path(PayloadEntryProxy* pep); + //IPAACA_HEADER_EXPORT void update_json_source(); + typedef std::shared_ptr<PayloadDocumentEntry> ptr; +}; +//}}} + +typedef std::map<std::string, PayloadDocumentEntry::ptr> PayloadDocumentStore; + + +#if 0 +/** \brief Lock to accumulate payload changes into one single update transaction + * + */ +IPAACA_HEADER_EXPORT class PayloadBatchUpdateLock: public ipaaca::Lock +{ + friend class Payload; + protected: + Payload* _payload; + public: + IPAACA_HEADER_EXPORT inline PayloadBatchUpdateLock(): Lock() { } + IPAACA_HEADER_EXPORT void on_lock() override; + IPAACA_HEADER_EXPORT void on_unlock() override; +}; +#endif + +/** \brief Central class containing the user-set payload of any IUInterface class (IU, Message, RemotePushIU or RemoteMessage) + * + * Obtained by calling payload() on any IUInterface derived object. Created during IU creation. + */ +IPAACA_HEADER_EXPORT class Payload: public Lock //{{{ +{ + friend std::ostream& operator<<(std::ostream& os, const Payload& obj); + friend class IUInterface; + friend class IU; + friend class Message; + friend class RemotePushIU; + friend class RemoteMessage; + friend class IUConverter; + friend class MessageConverter; + friend class CallbackIUPayloadUpdate; + friend class PayloadEntryProxy; + friend class PayloadIterator; + friend class FakeIU; + //friend class PayloadBatchUpdateLock; + protected: + IPAACA_MEMBER_VAR_EXPORT std::string _owner_name; + //IPAACA_MEMBER_VAR_EXPORT rapidjson::Document _json_document; + //IPAACA_MEMBER_VAR_EXPORT std::map<std::string, rapidjson::Document> _json_store; + IPAACA_MEMBER_VAR_EXPORT PayloadDocumentStore _document_store; + IPAACA_MEMBER_VAR_EXPORT boost::weak_ptr<IUInterface> _iu; + //IPAACA_MEMBER_VAR_EXPORT PayloadBatchUpdateLock _batch_update_lock; + // + IPAACA_MEMBER_VAR_EXPORT Lock _payload_operation_mode_lock; //< enforcing atomicity wrt the bool flag below + // + IPAACA_MEMBER_VAR_EXPORT bool _update_on_every_change; //< true: batch update not active; false: collecting updates (payload locked) + IPAACA_MEMBER_VAR_EXPORT std::map<std::string, PayloadDocumentEntry::ptr> _collected_modifications; + IPAACA_MEMBER_VAR_EXPORT std::vector<std::string> _collected_removals; + IPAACA_MEMBER_VAR_EXPORT std::string _batch_update_writer_name; + protected: + /// inherited from ipaaca::Lock, starting batch update collection mode + IPAACA_HEADER_EXPORT void on_lock() override; + /// inherited from ipaaca::Lock, finishing batch update collection mode + IPAACA_HEADER_EXPORT void on_unlock() override; + protected: + //IPAACA_HEADER_EXPORT ipaaca::Locker&& batch_update() { return std::move(ipaaca::Locker(*this); } + IPAACA_HEADER_EXPORT void initialize(boost::shared_ptr<IUInterface> iu); + IPAACA_HEADER_EXPORT inline void _set_owner_name(const std::string& name) { _owner_name = name; } + IPAACA_HEADER_EXPORT void _remotely_enforced_wipe(); + IPAACA_HEADER_EXPORT void _remotely_enforced_delitem(const std::string& k); + IPAACA_HEADER_EXPORT void _remotely_enforced_setitem(const std::string& k, PayloadDocumentEntry::ptr entry); + //IPAACA_HEADER_EXPORT void _internal_replace_all(const std::map<std::string, PayloadDocumentEntry::ptr>& new_contents, const std::string& writer_name=""); + IPAACA_HEADER_EXPORT void _internal_replace_all(const std::map<std::string, PayloadDocumentEntry::ptr>& new_contents, const std::string& writer_name=""); + IPAACA_HEADER_EXPORT void _internal_merge(const std::map<std::string, PayloadDocumentEntry::ptr>& contents_to_merge, const std::string& writer_name=""); + IPAACA_HEADER_EXPORT void _internal_set(const std::string& k, PayloadDocumentEntry::ptr v, const std::string& writer_name=""); + IPAACA_HEADER_EXPORT void _internal_remove(const std::string& k, const std::string& writer_name=""); + IPAACA_HEADER_EXPORT void _internal_merge_and_remove(const std::map<std::string, PayloadDocumentEntry::ptr>& contents_to_merge, const std::vector<std::string>& keys_to_remove, const std::string& writer_name=""); + public: + IPAACA_HEADER_EXPORT inline Payload(): _batch_update_writer_name(""), _update_on_every_change(true) { } + IPAACA_HEADER_EXPORT inline const std::string& owner_name() { return _owner_name; } + // access + /// Obtain a payload item by name as a PayloadEntryProxy (returning null-type proxy if undefined) + IPAACA_HEADER_EXPORT PayloadEntryProxy operator[](const std::string& key); + /// Legacy / convenience function: interpret the payload map as a map string->string (casting all entries to string) + IPAACA_HEADER_EXPORT operator std::map<std::string, std::string>(); + /// remove a single payload entry + IPAACA_HEADER_EXPORT inline void remove(const std::string& k) { _internal_remove(k); } + // FIXME: json: these two must support a bunch of standard types, not [only] json (users touch them) + // to be more precise: types of map<string, T> with T several interesting things (string, list<string>, etc.) + //IPAACA_HEADER_EXPORT inline void set(const std::map<std::string, const rapidjson::Document&>& all_elems) { _internal_replace_all(all_elems); } + //IPAACA_HEADER_EXPORT inline void merge(const std::map<std::string, const rapidjson::Document&>& elems_to_merge) { _internal_merge(elems_to_merge); } + // legacy / convenience setter + /// Legacy / convenience function: set the whole payload map from a map string->string (all JSON types are also set as string, no interpretation) + IPAACA_HEADER_EXPORT void set(const std::map<std::string, std::string>& all_elems); + protected: + /// set or overwrite a single payload entry with a PayloadDocumentEntry object (used by PayloadEntryProxy::operator=()). + IPAACA_HEADER_EXPORT inline void set(const std::string& k, PayloadDocumentEntry::ptr entry) { _internal_set(k, entry); } + IPAACA_HEADER_EXPORT PayloadDocumentEntry::ptr get_entry(const std::string& k); // json, changed str to proxy here, too + public: + [[deprecated("Use operator[] and operator std::string() instead")]] + /// Read a single entry as string [DEPRECATED] (use string conversion in PayloadEntryProxy instead) + IPAACA_HEADER_EXPORT std::string get(const std::string& k); // DEPRECATED + protected: + IPAACA_MEMBER_VAR_EXPORT unsigned long internal_revision; + IPAACA_MEMBER_VAR_EXPORT inline void mark_revision_change() { internal_revision++; } + IPAACA_HEADER_EXPORT inline bool revision_changed(unsigned long reference_revision) { return internal_revision != reference_revision; } + public: + /// obtain a standard iterator marking the first entry in the payload + IPAACA_HEADER_EXPORT PayloadIterator begin(); + /// obtain a standard iterator past the last entry in the payload + IPAACA_HEADER_EXPORT PayloadIterator end(); + typedef boost::shared_ptr<Payload> ptr; +};//}}} + +/** \brief Standard iterator for Payload (example below) + * + * \b Examples: + * <pre> + * // Print all key-value pairs from a payload (C++11) + * for (auto kv_pair: myiu->payload()) { + * std::cout << kv_pair.first << " -> " << (std::string) kv_pair.second << std::endl; + * } + * </pre> + * + */ +IPAACA_HEADER_EXPORT class PayloadIterator//{{{ +{ + friend class Payload; + friend std::ostream& operator<<(std::ostream& os, const PayloadIterator& iter); + protected: + IPAACA_MEMBER_VAR_EXPORT Payload* _payload; + IPAACA_MEMBER_VAR_EXPORT unsigned long reference_payload_revision; + IPAACA_MEMBER_VAR_EXPORT PayloadDocumentStore::iterator raw_iterator; + //IPAACA_MEMBER_VAR_EXPORT bool is_end; + protected: + IPAACA_HEADER_EXPORT PayloadIterator(Payload* payload, PayloadDocumentStore::iterator&& pl_iter ); //, bool is_end); + public: + IPAACA_HEADER_EXPORT PayloadIterator(const PayloadIterator& iter); + IPAACA_HEADER_EXPORT PayloadIterator& operator++(); + IPAACA_HEADER_EXPORT std::pair<std::string, PayloadEntryProxy> operator*(); + IPAACA_HEADER_EXPORT std::shared_ptr<std::pair<std::string, PayloadEntryProxy> > operator->(); + IPAACA_HEADER_EXPORT bool operator==(const PayloadIterator& ref); + IPAACA_HEADER_EXPORT bool operator!=(const PayloadIterator& ref); + // constructor to create a new top-most parent proxy (from a payload key) +}; +//}}} + +/// Iterator over a payload entry that is a json map-type object (returned type during dereferencing: pair<string, PayloadEntryProxy>) +IPAACA_HEADER_EXPORT class PayloadEntryProxyMapIterator//{{{ +{ + public: + typedef rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>::MemberIterator RawIterator; + protected: + IPAACA_MEMBER_VAR_EXPORT PayloadEntryProxy* proxy; + IPAACA_MEMBER_VAR_EXPORT RawIterator raw_iterator; + public: + IPAACA_HEADER_EXPORT PayloadEntryProxyMapIterator(PayloadEntryProxy* proxy, RawIterator&& raw_iter); + IPAACA_HEADER_EXPORT PayloadEntryProxyMapIterator& operator++(); + IPAACA_HEADER_EXPORT std::pair<std::string, PayloadEntryProxy> operator*(); + IPAACA_HEADER_EXPORT std::shared_ptr<std::pair<std::string, PayloadEntryProxy> > operator->(); + IPAACA_HEADER_EXPORT bool operator==(const PayloadEntryProxyMapIterator& other_iter); + IPAACA_HEADER_EXPORT bool operator!=(const PayloadEntryProxyMapIterator& other_iter); +}; +//}}} +/// Iterator over a payload entry that is a json list-type object (returned type during dereferencing: PayloadEntryProxy) +IPAACA_HEADER_EXPORT class PayloadEntryProxyListIterator//{{{ +{ + protected: + IPAACA_MEMBER_VAR_EXPORT PayloadEntryProxy* proxy; + IPAACA_MEMBER_VAR_EXPORT size_t current_idx; + IPAACA_MEMBER_VAR_EXPORT size_t size; + public: + IPAACA_HEADER_EXPORT PayloadEntryProxyListIterator(PayloadEntryProxy* proxy, size_t idx, size_t size); + IPAACA_HEADER_EXPORT PayloadEntryProxyListIterator& operator++(); + IPAACA_HEADER_EXPORT PayloadEntryProxy operator*(); + IPAACA_HEADER_EXPORT std::shared_ptr<PayloadEntryProxy> operator->(); + IPAACA_HEADER_EXPORT bool operator==(const PayloadEntryProxyListIterator& other_iter); + IPAACA_HEADER_EXPORT bool operator!=(const PayloadEntryProxyListIterator& other_iter); +}; +//}}} +/// Interpretation of a variant json value as a map-type object +IPAACA_HEADER_EXPORT class PayloadEntryProxyMapDecorator//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline PayloadEntryProxyMapDecorator(PayloadEntryProxy* proxy_): proxy(proxy_) { } + IPAACA_HEADER_EXPORT PayloadEntryProxyMapIterator begin(); + IPAACA_HEADER_EXPORT PayloadEntryProxyMapIterator end(); + protected: + IPAACA_MEMBER_VAR_EXPORT PayloadEntryProxy* proxy; +}; +//}}} +/// Interpretation of a variant json value as a list-type object +IPAACA_HEADER_EXPORT class PayloadEntryProxyListDecorator//{{{ +{ + public: + IPAACA_HEADER_EXPORT inline PayloadEntryProxyListDecorator(PayloadEntryProxy* proxy_): proxy(proxy_) { } + IPAACA_HEADER_EXPORT PayloadEntryProxyListIterator begin(); + IPAACA_HEADER_EXPORT PayloadEntryProxyListIterator end(); + protected: + IPAACA_MEMBER_VAR_EXPORT PayloadEntryProxy* proxy; +}; +//}}} + +/** \brief Reference to an existent or nonexistent payload entry (or a value deeper in the json tree) + * + * This class is returned by IUInterface::operator[]. + * The proxy handles automatic type conversions, requests remote changes of payloads, and enables navigation into and iteration over structured json objects. + * + * \b Examples (reading): + * + * <code>std::string received_name = iu->payload()["name"];</code> // implicit conversion using operator string() + * + * <code>std::vector<double> vd = iu->payload()["double_list"];</code> // some standard container types also supported + * + * <code>auto p = iu->payload()["otherKey"];</code> // auto type is PayloadEntryProxy (conversion is on-demand) + * + * <code>for (auto val: iu->payload()["my_list"].as_list()) { ... }</code> // as_list is required to select list-type iteration (value type in iteration remains variant) + * + * <code>for (auto k_v_map: iu->payload()["my_map"].as_map()) { ... }</code> // as_map is required to select map-type iteration (value type in iteration is a pair, second part remains variant) + * + * \b Examples (writing): + * + * <code>iu->payload()["my_new_item"] = "new value";</code> // most basic operation, set string value + * + * <code>iu->payload()["double_list"][0] = 100.0;</code> // accessing and updating an item in a list + * + * <code>iu->payload()["name_list"] = std::list<std::string>{"Alpha", "Bravo", "Charlie"};</code> // set from basic uniform containers + * + * <code>iu->payload()["name_list"].push_back("--- adding some numbers below ---");</code> // append a supported value to an existing list + * + * <code>iu->payload()["name_list"].extend(iu->payload()["double_list"]);</code> // extend list by items; \b Note: all setters also accept proxies as source values, creating copies of values + */ +IPAACA_HEADER_EXPORT class PayloadEntryProxy//{{{ +{ + friend class Payload; + friend class PayloadIterator; + friend class PayloadDocumentEntry; + friend class PayloadEntryProxyListDecorator; + friend class PayloadEntryProxyListIterator; + friend class PayloadEntryProxyMapDecorator; + friend class PayloadEntryProxyMapIterator; + friend std::ostream& operator<<(std::ostream& os, const PayloadEntryProxy& proxy); + public: + /// Select map-style iteration for this proxy (to select iterator content type). Will throw if not actually map-type. See example in the class description. + IPAACA_HEADER_EXPORT PayloadEntryProxyMapDecorator as_map(); + /// Select list-style iteration for this proxy (to select iterator content type). Will throw if not actually list-type. See example in the class description. + IPAACA_HEADER_EXPORT PayloadEntryProxyListDecorator as_list(); + protected: + //IPAACA_MEMBER_VAR_EXPORT rapidjson::Document* _json_parent_node; + //IPAACA_MEMBER_VAR_EXPORT rapidjson::Document* _json_node; + IPAACA_MEMBER_VAR_EXPORT Payload* _payload; + IPAACA_MEMBER_VAR_EXPORT std::string _key; + // + // new json stuff / hierarchical navigation + // + IPAACA_MEMBER_VAR_EXPORT PayloadEntryProxy* parent; ///< Parent proxy (up to document root -> then null) + IPAACA_MEMBER_VAR_EXPORT PayloadDocumentEntry::ptr document_entry; // contains lock and json Doc + IPAACA_MEMBER_VAR_EXPORT bool existent; ///< Whether Value exists already (or else 'blindly' navigated) + IPAACA_MEMBER_VAR_EXPORT bool addressed_as_array; ///< Whether long or string navigation was used + IPAACA_MEMBER_VAR_EXPORT long addressed_index; ///< Index that was used in list-style access + IPAACA_MEMBER_VAR_EXPORT std::string addressed_key; ///< Key that was used in map-style access + /// currently navigated value in json tree (or a new Null value) + IPAACA_MEMBER_VAR_EXPORT rapidjson::Value* json_value; ///< json value that corresponds to the current navigation (or nullptr) +/* protected: + IPAACA_HEADER_EXPORT void connect_to_existing_parents(); +*/ + protected: + // constructor to create a new top-most parent proxy (from a payload key) + IPAACA_HEADER_EXPORT PayloadEntryProxy(Payload* payload, const std::string& key); + // constructors for navigation through objects + IPAACA_HEADER_EXPORT PayloadEntryProxy(PayloadEntryProxy* parent, const std::string& addressed_key); + IPAACA_HEADER_EXPORT PayloadEntryProxy(PayloadEntryProxy* parent, size_t addressed_index); + public: + /// Return number of contained items (or 0 for non-container types) + IPAACA_HEADER_EXPORT size_t size(); + /// Return whether value corresponds to json 'null'; also true if value is nonexistent so far (e.g. navigated to new map entry) + IPAACA_HEADER_EXPORT bool is_null(); + /// Return whether value is of string type + IPAACA_HEADER_EXPORT bool is_string(); + /// Return whether value is of a numerical type + IPAACA_HEADER_EXPORT bool is_number(); + /// Return whether value is of list type + IPAACA_HEADER_EXPORT bool is_list(); + /// Return whether value is of map type + IPAACA_HEADER_EXPORT bool is_map(); + public: + /// Array-style navigation over json value + IPAACA_HEADER_EXPORT PayloadEntryProxy operator[](size_t index); // array-style navigation + /// Array-style navigation over json value (added to catch [0]) + IPAACA_HEADER_EXPORT PayloadEntryProxy operator[](int index); // int is UNFORTUNATELY required to catch + // [0] (addressing using literal zero) + // because ambiguity with const char* + // arises if only [](size_t) is provided. + // size_t is obviously superior ... + // TODO: remove if better solution known + /// Dict-style navigation over json value + IPAACA_HEADER_EXPORT PayloadEntryProxy operator[](const std::string& key); // dict-style navigation + /// Dict-style navigation over json value + IPAACA_HEADER_EXPORT PayloadEntryProxy operator[](const char* key); + // + /// Set or overwrite some portion of a payload from the point navigated to + IPAACA_HEADER_EXPORT template<typename T> PayloadEntryProxy& operator=(T t) + { + PayloadDocumentEntry::ptr new_entry = document_entry->clone(); // copy-on-write, no lock required + rapidjson::Value& newval = new_entry->get_or_create_nested_value_from_proxy_path(this); + pack_into_json_value(newval, new_entry->document.GetAllocator(), t); + //new_entry->update_json_source(); + _payload->set(_key, new_entry); + return *this; + } + /// Value comparison with other proxy contents + IPAACA_HEADER_EXPORT inline bool operator==(const PayloadEntryProxy& otherproxy) { return (json_value && otherproxy.json_value && ((*json_value)==*(otherproxy.json_value))); } + /// Value comparison with other proxy contents + IPAACA_HEADER_EXPORT inline bool operator!=(const PayloadEntryProxy& otherproxy) { return !operator==(otherproxy); } + /// Value comparison with supported basic types + IPAACA_HEADER_EXPORT template<typename T> bool operator==(T othervalue) + { + if (!json_value) return false; + try { + return json_value_cast<T>(*json_value) == othervalue; + } catch(PayloadTypeConversionError& ex) { + // assume conversion error = type mismatch = unequal + return false; + } + } + /// Value comparison with supported basic types + IPAACA_HEADER_EXPORT template<typename T> bool operator!=(T othervalue) { return !operator==(othervalue); } + /// Value comparison with char* (required to be explicitly added) + IPAACA_HEADER_EXPORT inline bool operator==(const char* othervalue) + { + if (!json_value) return false; + return json_value_cast<std::string>(*json_value) == othervalue; + } + /// Value comparison with char* (required to be explicitly added) + IPAACA_HEADER_EXPORT inline bool operator!=(const char* othervalue) { return !operator==(othervalue); } + + /// Copy value from below other json node, preserving types + IPAACA_HEADER_EXPORT PayloadEntryProxy& operator=(const PayloadEntryProxy& otherproxy); + + //IPAACA_HEADER_EXPORT PayloadEntryProxy& operator=(const std::string& value); + //IPAACA_HEADER_EXPORT PayloadEntryProxy& operator=(const char* value); + //IPAACA_HEADER_EXPORT PayloadEntryProxy& operator=(double value); + //IPAACA_HEADER_EXPORT PayloadEntryProxy& operator=(bool value); + + /// Conversion to std::string (explicit or implicit) + IPAACA_HEADER_EXPORT operator std::string(); + /// Conversion to long (explicit or implicit) + IPAACA_HEADER_EXPORT operator long(); + /// Conversion to double (explicit or implicit) + IPAACA_HEADER_EXPORT operator double(); + /// Conversion to bool (explicit or implicit) + IPAACA_HEADER_EXPORT operator bool(); + /// Conversion to uniform std::vector of supported basic type + IPAACA_HEADER_EXPORT template<typename Inner> operator std::vector<Inner>() { + if ((!json_value) || (!json_value->IsArray())) throw PayloadAddressingError(); + std::vector<Inner> result; + for (auto it = json_value->Begin(); it != json_value->End(); ++it) { + result.push_back( json_value_cast<Inner>(*it) ); + } + return result; + } + /// Conversion to uniform std::list of supported basic type + IPAACA_HEADER_EXPORT template<typename Inner> operator std::list<Inner>() { + if ((!json_value) || (!json_value->IsArray())) throw PayloadAddressingError(); + std::list<Inner> result; + for (auto it = json_value->Begin(); it != json_value->End(); ++it) { + result.push_back( json_value_cast<Inner>(*it) ); + } + return result; + } + /// Conversion to uniform std::map of string -> supported basic type + IPAACA_HEADER_EXPORT template<typename Inner> operator std::map<std::string, Inner>() { + if ((!json_value) || (!json_value->IsObject())) throw PayloadAddressingError(); + std::map<std::string, Inner> result; + for (auto it = json_value->MemberBegin(); it != json_value->MemberEnd(); ++it) { + result[std::string(it->name.GetString())] = json_value_cast<Inner>(it->value); + } + return result; + } + // FIXME why are these needed again? + /// [DECPRECATED] use normal type conversion syntax instead + [[deprecated("Use operator std::string() instead (i.e. explicit or implicit cast)")]] + IPAACA_HEADER_EXPORT std::string to_str(); + //long to_int() { return operator long(); ; + /// [DECPRECATED] use normal type conversion syntax instead + [[deprecated("Use operator long() instead (i.e. explicit or implicit cast)")]] + IPAACA_HEADER_EXPORT long to_long(); + /// [DECPRECATED] use normal type conversion syntax instead + [[deprecated("Use operator double() instead (i.e. explicit or implicit cast)")]] + IPAACA_HEADER_EXPORT double to_float(); + /// [DECPRECATED] use normal type conversion syntax instead + [[deprecated("Use operator bool() instead (i.e. explicit or implicit cast)")]] + IPAACA_HEADER_EXPORT bool to_bool(); + // getters (not needed since conversions are enough?) + //IPAACA_HEADER_EXPORT template<typename T> T get() { return json_value_cast<T>(json_value); } + // setters + //IPAACA_HEADER_EXPORT template<typename T> PayloadEntryProxy& set(T t); + /*{ + pack_into_json_value<T>(t); + connect_to_existing_parents(); + _payload->set(key, document_entry->document); + }*/ + /// Append a supported type to a list-type payload value + IPAACA_HEADER_EXPORT template<typename T> void push_back(T t) + { + if ((!json_value) || (!json_value->IsArray())) throw PayloadAddressingError(); + PayloadDocumentEntry::ptr new_entry = document_entry->clone(); // copy-on-write, no lock required + rapidjson::Value& list = new_entry->get_or_create_nested_value_from_proxy_path(this); + rapidjson::Value newval; + pack_into_json_value(newval, new_entry->document.GetAllocator(), t); + list.PushBack(newval, new_entry->document.GetAllocator()); + _payload->set(_key, new_entry); + } + /// Append the value of another proxy (or a null value) to a list-type value + IPAACA_HEADER_EXPORT void push_back(const PayloadEntryProxy& otherproxy) + { + if ((!json_value) || (!json_value->IsArray())) throw PayloadAddressingError(); + PayloadDocumentEntry::ptr new_entry = document_entry->clone(); // copy-on-write, no lock required + rapidjson::Value& list = new_entry->get_or_create_nested_value_from_proxy_path(this); + rapidjson::Value newval; + auto valueptr = otherproxy.json_value; + if (valueptr) { // only set if value is valid, keep default null value otherwise + newval.CopyFrom(*valueptr, new_entry->document.GetAllocator()); + } + list.PushBack(newval, new_entry->document.GetAllocator()); + _payload->set(_key, new_entry); + } + /// Extend a list-type payload value with a vector containing items of a supported type + IPAACA_HEADER_EXPORT template<typename T> void extend(const std::vector<T>& ts) + { + if ((!json_value) || (!json_value->IsArray())) throw PayloadAddressingError(); + PayloadDocumentEntry::ptr new_entry = document_entry->clone(); // copy-on-write, no lock required + rapidjson::Value& list = new_entry->get_or_create_nested_value_from_proxy_path(this); + for (auto& t: ts) { + rapidjson::Value newval; + pack_into_json_value(newval, new_entry->document.GetAllocator(), t); + list.PushBack(newval, new_entry->document.GetAllocator()); + } + _payload->set(_key, new_entry); + } + /// Extend a list-type payload value with a list containing items of a supported type + IPAACA_HEADER_EXPORT template<typename T> void extend(const std::list<T>& ts) + { + if ((!json_value) || (!json_value->IsArray())) throw PayloadAddressingError(); + PayloadDocumentEntry::ptr new_entry = document_entry->clone(); // copy-on-write, no lock required + rapidjson::Value& list = new_entry->get_or_create_nested_value_from_proxy_path(this); + for (auto& t: ts) { + rapidjson::Value newval; + pack_into_json_value(newval, new_entry->document.GetAllocator(), t); + list.PushBack(newval, new_entry->document.GetAllocator()); + } + _payload->set(_key, new_entry); + } + /// Extend a list-type payload value with items (copies) from another list-type value + IPAACA_HEADER_EXPORT void extend(const PayloadEntryProxy& otherproxy) + { + if ((!json_value) || (!json_value->IsArray())) throw PayloadAddressingError(); + if ((!otherproxy.json_value) || (!(otherproxy.json_value->IsArray()))) throw PayloadAddressingError(); + PayloadDocumentEntry::ptr new_entry = document_entry->clone(); // copy-on-write, no lock required + rapidjson::Value& list = new_entry->get_or_create_nested_value_from_proxy_path(this); + for (size_t i=0; i<otherproxy.json_value->Size(); ++i) { + rapidjson::Value newval; + rapidjson::Value& value = (*(otherproxy.json_value))[i]; + newval.CopyFrom(value, new_entry->document.GetAllocator()); + list.PushBack(newval, new_entry->document.GetAllocator()); + } + _payload->set(_key, new_entry); + } +}; +// Available interpretations of payload entries (or children thereof) below. +// Usage of standard complex data structures (vector etc.) currently entails +// casting all entries to a uniform type (a-priori choice: std::string). +/* +IPAACA_HEADER_EXPORT template<> long PayloadEntryProxy::get(); +IPAACA_HEADER_EXPORT template<> double PayloadEntryProxy::get(); +IPAACA_HEADER_EXPORT template<> bool PayloadEntryProxy::get(); +IPAACA_HEADER_EXPORT template<> std::string PayloadEntryProxy::get(); +IPAACA_HEADER_EXPORT template<> std::vector<std::string> PayloadEntryProxy::get(); +IPAACA_HEADER_EXPORT template<> std::list<std::string> PayloadEntryProxy::get(); +IPAACA_HEADER_EXPORT template<> std::map<std::string, std::string> PayloadEntryProxy::get(); +*/ + +//}}} + +#endif diff --git a/ipaacalib/cpp/include/ipaaca/ipaaca.h b/ipaacalib/cpp/include/ipaaca/ipaaca.h index 057ff191995fcbc14cd4033ba729be485ad828ba..04f62ee0d2b2c45b079747a732007be22451c30e 100644 --- a/ipaacalib/cpp/include/ipaaca/ipaaca.h +++ b/ipaacalib/cpp/include/ipaaca/ipaaca.h @@ -1,10 +1,11 @@ /* * This file is part of IPAACA, the * "Incremental Processing Architecture - * for Artificial Conversational Agents". + * for Artificial Conversational Agents". * - * Copyright (c) 2009-2013 Sociable Agents Group - * CITEC, Bielefeld University + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University * * http://opensource.cit-ec.de/projects/ipaaca/ * http://purl.org/net/ipaaca @@ -21,7 +22,7 @@ * You should have received a copy of the LGPL along with this * program. If not, go to http://www.gnu.org/licenses/lgpl.html * or write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The development of this software was supported by the * Excellence Cluster EXC 277 Cognitive Interaction Technology. @@ -30,27 +31,90 @@ * Excellence Initiative. */ -#ifndef __IPAACA_H__ -#define __IPAACA_H__ +/** + * \file ipaaca.h + * + * \brief Central header file for IPAACA-C++. + * + * This is the central header file for the C++ version of IPAACA. Users should <b>include this file only.</b> + * + * \author Ramin Yaghoubzadeh (ryaghoubzadeh@uni-bielefeld.de) + * \date March, 2015 + */ + +/** +\mainpage Documentation for IPAACA-C++ + +This is the automatically generated documentation for the C++ implementation of IPAACA. + +List of most relevant entry points: + +Buffers: InputBuffer, OutputBuffer + +IUs: IUInterface, IU, Message + +IU handling (user-set): #IUEventHandlerFunction + +IU payload contents: Payload, PayloadEntryProxy +*/ + +#ifndef __ipaaca_h_INCLUDED__ +#define __ipaaca_h_INCLUDED__ /// ipaaca/IU/RSB protocol major version number -#define IPAACA_PROTOCOL_VERSION_MAJOR 1 +#define IPAACA_PROTOCOL_VERSION_MAJOR 2 /// ipaaca/IU/RSB protocol minor version number #define IPAACA_PROTOCOL_VERSION_MINOR 0 /// running release number of ipaaca-c++ -#define IPAACA_CPP_RELEASE_NUMBER 1 +#define IPAACA_CPP_RELEASE_NUMBER 12 /// date of last release number increment -#define IPAACA_CPP_RELEASE_DATE "2012-09-08" +#define IPAACA_CPP_RELEASE_DATE "2015-01-15" + +#ifndef __FUNCTION_NAME__ + #ifdef WIN32 // Windows + #define __FUNCTION_NAME__ __FUNCTION__ + #else // POSIX + #define __FUNCTION_NAME__ __func__ + #endif +#endif + +#ifdef WIN32 + #define IPAACA_SYSTEM_DEPENDENT_CLASS_NAME(c) "class "##c +#else + #define IPAACA_SYSTEM_DEPENDENT_CLASS_NAME(c) c +#endif + +#ifdef WIN32 + #if defined(ipaaca_EXPORTS) + #define IPAACA_EXPORT + #define IPAACA_HEADER_EXPORT __declspec(dllexport) + #define IPAACA_MEMBER_VAR_EXPORT + #else + #define IPAACA_EXPORT + #define IPAACA_HEADER_EXPORT __declspec(dllimport) + #define IPAACA_MEMBER_VAR_EXPORT + #endif +#else + #define IPAACA_EXPORT + #define IPAACA_HEADER_EXPORT + #define IPAACA_MEMBER_VAR_EXPORT +#endif #ifdef IPAACA_DEBUG_MESSAGES -#define IPAACA_INFO(i) std::cout << __FILE__ << ":" << __LINE__ << ": " << __func__ << "() -- " << i << std::endl; -#define IPAACA_WARNING(i) std::cout << __FILE__ << ":" << __LINE__ << ": " << __func__ << "() -- WARNING: " << i << std::endl; -#define IPAACA_IMPLEMENT_ME std::cout << __FILE__ << ":" << __LINE__ << ": " << __func__ << "() -- IMPLEMENT ME" << std::endl; -#define IPAACA_TODO(i) std::cout << __FILE__ << ":" << __LINE__ << ": " << __func__ << "() -- TODO: " << i << std::endl; +#define IPAACA_DEBUG(i) if (__ipaaca_static_option_log_level>=IPAACA_LOG_LEVEL_DEBUG) { std::cout << __FILE__ << ":" << __LINE__ << ": " << __FUNCTION_NAME__ << "() -- Debug: " << i << std::endl; } +#define IPAACA_INFO(i) if (__ipaaca_static_option_log_level>=IPAACA_LOG_LEVEL_INFO) { std::cout << __FILE__ << ":" << __LINE__ << ": " << __FUNCTION_NAME__ << "() -- Info: " << i << std::endl; } +#define IPAACA_WARNING(i) if (__ipaaca_static_option_log_level>=IPAACA_LOG_LEVEL_WARNING) { std::cout << __FILE__ << ":" << __LINE__ << ": " << __FUNCTION_NAME__ << "() -- WARNING: " << i << std::endl; } +#define IPAACA_ERROR(i) if (__ipaaca_static_option_log_level>=IPAACA_LOG_LEVEL_ERROR) { std::cout << __FILE__ << ":" << __LINE__ << ": " << __FUNCTION_NAME__ << "() -- ERROR: " << i << std::endl; } +#define IPAACA_CRITICAL(i) if (__ipaaca_static_option_log_level>=IPAACA_LOG_LEVEL_CRITICAL) { std::cout << __FILE__ << ":" << __LINE__ << ": " << __FUNCTION_NAME__ << "() -- CRITICAL: " << i << std::endl; } +#define IPAACA_IMPLEMENT_ME if (__ipaaca_static_option_log_level>=IPAACA_LOG_LEVEL_INFO) { std::cout << __FILE__ << ":" << __LINE__ << ": " << __FUNCTION_NAME__ << "() -- IMPLEMENT_ME:" << std::endl; } +#define IPAACA_TODO(i) if (__ipaaca_static_option_log_level>=IPAACA_LOG_LEVEL_INFO) { std::cout << __FILE__ << ":" << __LINE__ << ": " << __FUNCTION_NAME__ << "() -- TODO: " << i << std::endl; } #else +#define IPAACA_DEBUG(i) ; #define IPAACA_INFO(i) ; #define IPAACA_WARNING(i) ; +#define IPAACA_ERROR(i) ; +#define IPAACA_CRITICAL(i) ; #define IPAACA_IMPLEMENT_ME ; #define IPAACA_TODO(i) ; #endif @@ -60,16 +124,25 @@ #include <rsb/Factory.h> #include <rsb/Handler.h> #include <rsb/Event.h> +#include <rsb/ParticipantConfig.h> #include <rsb/converter/Repository.h> #include <rsb/converter/ProtocolBufferConverter.h> #include <rsb/converter/Converter.h> #include <rsb/rsbexports.h> #endif +// new json-based payload API, used in several classes +#define RAPIDJSON_HAS_STDSTRING 1 +#include "rapidjson/document.h" +#include "rapidjson/prettywriter.h" +#include "rapidjson/filestream.h" +#include <cstdio> /// marking pure virtual functions for extra readability #define _IPAACA_ABSTRACT_ +#define _IPAACA_OVERRIDE_ override + /// value to return when reading nonexistant payload keys #define IPAACA_PAYLOAD_DEFAULT_STRING_VALUE "" @@ -81,7 +154,25 @@ // for logger #include <iomanip> + +#ifdef WIN32 +// for Windows +#include <time.h> +#else +// for Linux and OS X #include <sys/time.h> +#endif + +#include <cstdlib> + + +#ifdef WIN32 +#include <rpc.h> +#else +#include <uuid/uuid.h> +#include <glob.h> +#endif + #ifndef Q_MOC_RUN #include <boost/bind.hpp> @@ -92,812 +183,52 @@ #include <boost/lexical_cast.hpp> #endif -#include <ipaaca/ipaaca.pb.h> -#include <pthread.h> -#include <uuid/uuid.h> +#include <ipaaca/ipaaca.pb.h> #include <set> +#include <list> +#include <algorithm> +#include <utility> +#include <initializer_list> namespace ipaaca { -typedef uint32_t revision_t; - -/// Type of the IU event. Realized as an integer to enable bit masks for filters. -typedef uint32_t IUEventType; -#define IU_ADDED 1 -#define IU_COMMITTED 2 -#define IU_DELETED 4 -#define IU_RETRACTED 8 -#define IU_UPDATED 16 -#define IU_LINKSUPDATED 32 -#define IU_MESSAGE 64 -/// Bit mask for receiving all events -#define IU_ALL_EVENTS 127 - -/// Convert an int event type to a human-readable string -inline std::string iu_event_type_to_str(IUEventType type) -{ - switch(type) { - case IU_ADDED: return "ADDED"; - case IU_COMMITTED: return "COMMITTED"; - case IU_DELETED: return "DELETED"; - case IU_RETRACTED: return "RETRACTED"; - case IU_UPDATED: return "UPDATED"; - case IU_LINKSUPDATED: return "LINKSUPDATED"; - case IU_MESSAGE: return "MESSAGE"; - default: return "(NOT A KNOWN SINGLE IU EVENT TYPE)"; - } -} - -/// IU access mode: PUSH means that updates are broadcast; REMOTE means that reads are RPC calls; MESSAGE means a fire-and-forget message -enum IUAccessMode { - IU_ACCESS_PUSH, - IU_ACCESS_REMOTE, - IU_ACCESS_MESSAGE -}; - -class PayloadEntryProxy; -class Payload; -class IUInterface; -class IU; -class Message; -class RemotePushIU; -class IULinkUpdate; -class IUPayloadUpdate; -class IUStore; -class FrozenIUStore; -class Buffer; -class InputBuffer; -class OutputBuffer; - -//class InputBufferRsbAdaptor; -//class OutputBufferRsbAdaptor; - -class CallbackIUPayloadUpdate; -class CallbackIULinkUpdate; -class CallbackIUCommission; -class CallbackIURetraction; - -class IUConverter; -class MessageConverter; -class IUPayloadUpdateConverter; -class IULinkUpdateConverter; -class IntConverter; - -/// generate a UUID as an ASCII string -std::string generate_uuid_string(); - -/// store for (local) IUs. TODO Stores need to be unified more -class IUStore: public std::map<std::string, boost::shared_ptr<IU> > -{ -}; -/// store for RemotePushIUs. TODO Stores need to be unified more -class RemotePushIUStore: public std::map<std::string, boost::shared_ptr<RemotePushIU> > // TODO genericize to all remote IU types -{ -}; - -class Exception: public std::exception//{{{ -{ - protected: - std::string _description; - public: - inline Exception(const std::string& description=""): _description(description) { } - inline ~Exception() throw() { } - const char* what() const throw() { - return _description.c_str(); - } -};//}}} -class Abort: public std::exception//{{{ -{ - protected: - std::string _description; - public: - inline Abort(const std::string& description=""): _description(description) { } - inline ~Abort() throw() { } - const char* what() const throw() { - return _description.c_str(); - } -};//}}} - -/// a reentrant lock/mutex -class Lock -{ - protected: - pthread_mutexattr_t _attrs; - pthread_mutex_t _mutex; - public: - inline Lock() { - pthread_mutexattr_init(&_attrs); - pthread_mutexattr_settype(&_attrs, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&_mutex, &_attrs); - } - inline ~Lock() { - pthread_mutex_destroy(&_mutex); - pthread_mutexattr_destroy(&_attrs); - } - inline void lock() { - pthread_mutex_lock(&_mutex); - } - inline void unlock() { - pthread_mutex_unlock(&_mutex); - } -}; - -/// Stack-based lock holder. Create in a new stack frame -/// (i.e. {}-block) and it will obtain the lock and -/// auto-release in on exiting the stack frame. -class Locker -{ - protected: - Lock* _lock; - private: - inline Locker(): _lock(NULL) { } // not available - public: - inline Locker(Lock& lock): _lock(&lock) { - //std::cout << "-- Locker: lock" << std::endl; - _lock->lock(); - } - inline ~Locker() { - //std::cout << "-- Locker: unlock" << std::endl; - _lock->unlock(); - } -}; -class PthreadMutexLocker -{ - protected: - pthread_mutex_t* _lock; - private: - inline PthreadMutexLocker(): _lock(NULL) { } // not available - public: - inline PthreadMutexLocker(pthread_mutex_t* lock): _lock(lock) { - if (!lock) throw Exception("PthreadMutexLocker got a NULL mutex!"); - pthread_mutex_lock(_lock); - } - inline ~PthreadMutexLocker() { - pthread_mutex_unlock(_lock); - } -}; - -typedef std::set<std::string> LinkSet; -typedef std::map<std::string, LinkSet> LinkMap; -class SmartLinkMap { - friend std::ostream& operator<<(std::ostream& os, const SmartLinkMap& obj); - friend class IUInterface; - friend class IU; - friend class IUConverter; - friend class MessageConverter; - public: - const LinkSet& get_links(const std::string& key); - const LinkMap& get_all_links(); - - protected: - LinkMap _links; - static LinkSet empty_link_set; - void _add_and_remove_links(const LinkMap& add, const LinkMap& remove); - void _replace_links(const LinkMap& links); -}; - -const LinkSet EMPTY_LINK_SET; -//const std::set<std::string> EMPTY_LINK_SET; - -//typedef boost::function<void (const std::string&, bool, IUEventType, const std::string&)> IUEventHandlerFunction; -typedef boost::function<void (boost::shared_ptr<IUInterface>, IUEventType, bool)> IUEventHandlerFunction; - -class IUEventHandler { - protected: - IUEventHandlerFunction _function; - IUEventType _event_mask; - bool _for_all_categories; - std::set<std::string> _categories; - protected: - inline bool _condition_met(IUEventType event_type, const std::string& category) - { - return ((_event_mask&event_type)!=0) && (_for_all_categories || (_categories.count(category)>0)); - } - public: - IUEventHandler(IUEventHandlerFunction function, IUEventType event_mask, const std::string& category); - IUEventHandler(IUEventHandlerFunction function, IUEventType event_mask, const std::set<std::string>& categories); - //void call(Buffer* buffer, const std::string& uid, bool local, IUEventType event_type, const std::string& category); - void call(Buffer* buffer, boost::shared_ptr<IUInterface> iu, bool local, IUEventType event_type, const std::string& category); - typedef boost::shared_ptr<IUEventHandler> ptr; -}; - -class Buffer { //: public boost::enable_shared_from_this<Buffer> {//{{{ - friend class IU; - friend class RemotePushIU; - friend class CallbackIUPayloadUpdate; - friend class CallbackIULinkUpdate; - friend class CallbackIUCommission; - protected: - //Lock _handler_lock; - std::string _uuid; - std::string _basename; - std::string _unique_name; - std::string _id_prefix; - std::vector<IUEventHandler::ptr> _event_handlers; - protected: - _IPAACA_ABSTRACT_ virtual void _send_iu_link_update(IUInterface* iu, bool is_delta, revision_t revision, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name="undef") = 0; - _IPAACA_ABSTRACT_ virtual void _send_iu_payload_update(IUInterface* iu, bool is_delta, revision_t revision, const std::map<std::string, std::string>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name="undef") = 0; - _IPAACA_ABSTRACT_ virtual void _send_iu_commission(IUInterface* iu, revision_t revision, const std::string& writer_name="undef") = 0; - void _allocate_unique_name(const std::string& basename, const std::string& function); - inline Buffer(const std::string& basename, const std::string& function) { - _allocate_unique_name(basename, function); - } - void call_iu_event_handlers(boost::shared_ptr<IUInterface> iu, bool local, IUEventType event_type, const std::string& category); - public: - virtual inline ~Buffer() { } - inline const std::string& unique_name() { return _unique_name; } - void register_handler(IUEventHandlerFunction function, IUEventType event_mask, const std::set<std::string>& categories); - void register_handler(IUEventHandlerFunction function, IUEventType event_mask = IU_ALL_EVENTS, const std::string& category=""); - //_IPAACA_ABSTRACT_ virtual void add(boost::shared_ptr<IUInterface> iu) = 0; - _IPAACA_ABSTRACT_ virtual boost::shared_ptr<IUInterface> get(const std::string& iu_uid) = 0; - _IPAACA_ABSTRACT_ virtual std::set<boost::shared_ptr<IUInterface> > get_ius() = 0; -}; -//}}} - -class OutputBuffer: public Buffer { //, public boost::enable_shared_from_this<OutputBuffer> {//{{{ - friend class IU; - friend class RemotePushIU; - friend class OutputBufferRsbAdaptor; - protected: - protected: - //OutputBufferRsbAdaptor _rsb; - IUStore _iu_store; - Lock _iu_id_counter_lock; -#ifdef IPAACA_EXPOSE_FULL_RSB_API - protected: - std::map<std::string, rsb::Informer<rsb::AnyType>::Ptr> _informer_store; - rsb::patterns::ServerPtr _server; - rsb::Informer<rsb::AnyType>::Ptr _get_informer(const std::string& category); -#endif - protected: - // informing functions - void _send_iu_link_update(IUInterface* iu, bool is_delta, revision_t revision, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name="undef"); - void _send_iu_payload_update(IUInterface* iu, bool is_delta, revision_t revision, const std::map<std::string, std::string>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name="undef"); - void _send_iu_commission(IUInterface* iu, revision_t revision, const std::string& writer_name); - // remote access functions - // _remote_update_links(IULinkUpdate) - // _remote_update_payload(IUPayloadUpdate) - // _remote_commit(protobuf::IUCommission) - void _publish_iu(boost::shared_ptr<IU> iu); - void _retract_iu(boost::shared_ptr<IU> iu); - protected: - OutputBuffer(const std::string& basename); - void _initialize_server(); - public: - static boost::shared_ptr<OutputBuffer> create(const std::string& basename); - ~OutputBuffer() { - IPAACA_IMPLEMENT_ME - } - void add(boost::shared_ptr<IU> iu); - boost::shared_ptr<IU> remove(const std::string& iu_uid); - boost::shared_ptr<IU> remove(boost::shared_ptr<IU> iu); - boost::shared_ptr<IUInterface> get(const std::string& iu_uid); - std::set<boost::shared_ptr<IUInterface> > get_ius(); - typedef boost::shared_ptr<OutputBuffer> ptr; -}; -//}}} - -class InputBuffer: public Buffer { //, public boost::enable_shared_from_this<InputBuffer> {//{{{ - friend class IU; - friend class RemotePushIU; - friend class InputBufferRsbAdaptor; - //InputBufferRsbAdaptor _rsb; -#ifdef IPAACA_EXPOSE_FULL_RSB_API - protected: - std::map<std::string, rsb::ListenerPtr> _listener_store; - std::map<std::string, rsb::patterns::RemoteServerPtr> _remote_server_store; - RemotePushIUStore _iu_store; // TODO genericize - rsb::patterns::RemoteServerPtr _get_remote_server(const std::string& unique_server_name); - rsb::ListenerPtr _create_category_listener_if_needed(const std::string& category); - void _handle_iu_events(rsb::EventPtr event); +#include <ipaaca/ipaaca-definitions.h> +#include <ipaaca/ipaaca-forwards.h> + +#include <ipaaca/ipaaca-locking.h> + +// Global static library variables (run-time default configuration) +// Actual initial values are set in ipaaca.cc + +/// Default payload type for new IUs (defaults to "JSON") +IPAACA_MEMBER_VAR_EXPORT extern std::string __ipaaca_static_option_default_payload_type; +/// Default channel for buffers (defaults to "default") +IPAACA_MEMBER_VAR_EXPORT extern std::string __ipaaca_static_option_default_channel; +/// Current console log level (defaults to warning), one of: IPAACA_LOG_LEVEL_CRITICAL, IPAACA_LOG_LEVEL_ERROR, IPAACA_LOG_LEVEL_WARNING, IPAACA_LOG_LEVEL_INFO, IPAACA_LOG_LEVEL_DEBUG, IPAACA_LOG_LEVEL_NONE +IPAACA_MEMBER_VAR_EXPORT extern unsigned int __ipaaca_static_option_log_level; + +IPAACA_MEMBER_VAR_EXPORT Lock& logger_lock(); + +#ifdef WIN32 +#define LOG_IPAACA_CONSOLE(msg) { ipaaca::Locker logging_locker(ipaaca::logger_lock()); std::time_t result = std::time(NULL); std::cout << "[LOG] " << std::asctime(std::localtime(&result)) << " : " << msg << std::endl; } +#else +// use normal gettimeofday() on POSIX +#define LOG_IPAACA_CONSOLE(msg) { ipaaca::Locker logging_locker(ipaaca::logger_lock()); timeval logging_tim; gettimeofday(&logging_tim, NULL); double logging_t1=logging_tim.tv_sec+(logging_tim.tv_usec/1000000.0); std::cout << "[LOG] " << std::setprecision(15) << logging_t1 << " : " << msg << std::endl; } #endif - protected: - inline void _send_iu_link_update(IUInterface* iu, bool is_delta, revision_t revision, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name="undef") - { - IPAACA_WARNING("(ERROR) InputBuffer::_send_iu_link_update() should never be invoked") - } - inline void _send_iu_payload_update(IUInterface* iu, bool is_delta, revision_t revision, const std::map<std::string, std::string>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name="undef") - { - IPAACA_WARNING("(ERROR) InputBuffer::_send_iu_payload_update() should never be invoked") - } - inline void _send_iu_commission(IUInterface* iu, revision_t revision, const std::string& writer_name="undef") - { - IPAACA_WARNING("(ERROR) InputBuffer::_send_iu_commission() should never be invoked") - } - protected: - InputBuffer(const std::string& basename, const std::set<std::string>& category_interests); - InputBuffer(const std::string& basename, const std::vector<std::string>& category_interests); - InputBuffer(const std::string& basename, const std::string& category_interest1); - InputBuffer(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2); - InputBuffer(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2, const std::string& category_interest3); - InputBuffer(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2, const std::string& category_interest3, const std::string& category_interest4); - public: - static boost::shared_ptr<InputBuffer> create(const std::string& basename, const std::set<std::string>& category_interests); - static boost::shared_ptr<InputBuffer> create(const std::string& basename, const std::vector<std::string>& category_interests); - static boost::shared_ptr<InputBuffer> create(const std::string& basename, const std::string& category_interest1); - static boost::shared_ptr<InputBuffer> create(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2); - static boost::shared_ptr<InputBuffer> create(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2, const std::string& category_interest3); - static boost::shared_ptr<InputBuffer> create(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2, const std::string& category_interest3, const std::string& category_interest4); - ~InputBuffer() { - IPAACA_IMPLEMENT_ME - } - boost::shared_ptr<IUInterface> get(const std::string& iu_uid); - std::set<boost::shared_ptr<IUInterface> > get_ius(); - typedef boost::shared_ptr<InputBuffer> ptr; -}; -//}}} - -class IUPayloadUpdate {//{{{ - public: - std::string uid; - revision_t revision; - std::string writer_name; - bool is_delta; - std::map<std::string, std::string> new_items; - std::vector<std::string> keys_to_remove; - friend std::ostream& operator<<(std::ostream& os, const IUPayloadUpdate& obj); - typedef boost::shared_ptr<IUPayloadUpdate> ptr; -};//}}} - -class IULinkUpdate {//{{{ - public: - std::string uid; - revision_t revision; - std::string writer_name; - bool is_delta; - std::map<std::string, std::set<std::string> > new_links; - std::map<std::string, std::set<std::string> > links_to_remove; - friend std::ostream& operator<<(std::ostream& os, const IULinkUpdate& obj); - typedef boost::shared_ptr<IULinkUpdate> ptr; -};//}}} - - - -class Initializer -{ - public: - static void initialize_ipaaca_rsb_if_needed(); - static void initialize_updated_default_config(); - static bool initialized(); - protected: - static bool _initialized; -}; - -class PayloadEntryProxy//{{{ -{ - protected: - Payload* _payload; - std::string _key; - public: - PayloadEntryProxy(Payload* payload, const std::string& key); - PayloadEntryProxy& operator=(const std::string& value); - PayloadEntryProxy& operator=(const char* value); - PayloadEntryProxy& operator=(double value); - PayloadEntryProxy& operator=(bool value); - operator std::string(); - operator long(); - operator double(); - operator bool(); - inline std::string to_str() { return operator std::string(); } - //inline long to_int() { return operator long(); } - inline long to_long() { return operator long(); } - inline double to_float() { return operator double(); } - inline bool to_bool() { return operator bool(); } -};//}}} - -class Payload//{{{ -{ - friend std::ostream& operator<<(std::ostream& os, const Payload& obj); - friend class IUInterface; - friend class IU; - friend class Message; - friend class RemotePushIU; - friend class RemoteMessage; - friend class IUConverter; - friend class MessageConverter; - friend class CallbackIUPayloadUpdate; - protected: - std::string _owner_name; - std::map<std::string, std::string> _store; - boost::weak_ptr<IUInterface> _iu; - protected: - void initialize(boost::shared_ptr<IUInterface> iu); - inline void _set_owner_name(const std::string& name) { _owner_name = name; } - void _remotely_enforced_wipe(); - void _remotely_enforced_delitem(const std::string& k); - void _remotely_enforced_setitem(const std::string& k, const std::string& v); - void _internal_replace_all(const std::map<std::string, std::string>& new_contents, const std::string& writer_name=""); - void _internal_merge(const std::map<std::string, std::string>& contents_to_merge, const std::string& writer_name=""); - void _internal_set(const std::string& k, const std::string& v, const std::string& writer_name=""); - void _internal_remove(const std::string& k, const std::string& writer_name=""); - public: - inline const std::string& owner_name() { return _owner_name; } - // access - PayloadEntryProxy operator[](const std::string& key); - operator std::map<std::string, std::string>(); - inline void set(const std::map<std::string, std::string>& all_elems) { _internal_replace_all(all_elems); } - inline void set(const std::string& k, const std::string& v) { _internal_set(k, v); } - inline void merge(const std::map<std::string, std::string>& elems_to_merge) { _internal_merge(elems_to_merge); } - inline void remove(const std::string& k) { _internal_remove(k); } - std::string get(const std::string& k); - typedef boost::shared_ptr<Payload> ptr; -};//}}} - -class IUInterface {//{{{ - friend class IUConverter; - friend class MessageConverter; - friend std::ostream& operator<<(std::ostream& os, const IUInterface& obj); - protected: - IUInterface(); - public: - inline virtual ~IUInterface() { } - protected: - std::string _uid; - revision_t _revision; - std::string _category; - std::string _payload_type; // default is "MAP" - std::string _owner_name; - bool _committed; - bool _retracted; - IUAccessMode _access_mode; - bool _read_only; - //boost::shared_ptr<Buffer> _buffer; - Buffer* _buffer; - SmartLinkMap _links; - protected: - friend class Payload; - // Internal functions that perform the update logic, - // e.g. sending a notification across the network - _IPAACA_ABSTRACT_ virtual void _modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name) = 0; - _IPAACA_ABSTRACT_ virtual void _modify_payload(bool is_delta, const std::map<std::string, std::string>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name) = 0; - //void _set_buffer(boost::shared_ptr<Buffer> buffer); - void _associate_with_buffer(Buffer* buffer); - void _set_buffer(Buffer* buffer); - void _set_uid(const std::string& uid); - void _set_owner_name(const std::string& owner_name); - protected: - // internal functions that do not emit update events - inline void _add_and_remove_links(const LinkMap& add, const LinkMap& remove) { _links._add_and_remove_links(add, remove); } - inline void _replace_links(const LinkMap& links) { _links._replace_links(links); } - public: - inline bool is_published() { return (_buffer != 0); } - inline const std::string& uid() const { return _uid; } - inline revision_t revision() const { return _revision; } - inline const std::string& category() const { return _category; } - inline const std::string& payload_type() const { return _payload_type; } - inline const std::string& owner_name() const { return _owner_name; } - inline bool committed() const { return _committed; } - inline IUAccessMode access_mode() const { return _access_mode; } - inline bool read_only() const { return _read_only; } - //inline boost::shared_ptr<Buffer> buffer() { return _buffer; } - inline Buffer* buffer() const { return _buffer; } - inline const LinkSet& get_links(std::string type) { return _links.get_links(type); } - inline const LinkMap& get_all_links() { return _links.get_all_links(); } - // Payload - _IPAACA_ABSTRACT_ virtual Payload& payload() = 0; - _IPAACA_ABSTRACT_ virtual const Payload& const_payload() const = 0; - // setters - _IPAACA_ABSTRACT_ virtual void commit() = 0; - // functions to modify and update links: - void add_links(const std::string& type, const LinkSet& targets, const std::string& writer_name = ""); - void remove_links(const std::string& type, const LinkSet& targets, const std::string& writer_name = ""); - void modify_links(const LinkMap& add, const LinkMap& remove, const std::string& writer_name = ""); - void set_links(const LinkMap& links, const std::string& writer_name = ""); - // (with cpp specific convenience functions:) - void add_link(const std::string& type, const std::string& target, const std::string& writer_name = ""); - void remove_link(const std::string& type, const std::string& target, const std::string& writer_name = ""); - typedef boost::shared_ptr<IUInterface> ptr; -};//}}} - -class IU: public IUInterface {//{{{ - friend class Buffer; - friend class InputBuffer; - friend class OutputBuffer; - friend class CallbackIUPayloadUpdate; - friend class CallbackIULinkUpdate; - friend class CallbackIUCommission; - public: - Payload _payload; - protected: - Lock _revision_lock; - protected: - inline void _increase_revision_number() { _revision++; } - IU(const std::string& category, IUAccessMode access_mode=IU_ACCESS_PUSH, bool read_only=false, const std::string& payload_type="MAP" ); - public: - inline ~IU() { - //IPAACA_IMPLEMENT_ME - } - static boost::shared_ptr<IU> create(const std::string& category, IUAccessMode access_mode=IU_ACCESS_PUSH, bool read_only=false, const std::string& payload_type="MAP" ); - inline Payload& payload() { return _payload; } - inline const Payload& const_payload() const { return _payload; } - void commit(); - protected: - virtual void _modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name = ""); - virtual void _modify_payload(bool is_delta, const std::map<std::string, std::string>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name = ""); - protected: - virtual void _internal_commit(const std::string& writer_name = ""); - public: - typedef boost::shared_ptr<IU> ptr; -};//}}} -class Message: public IU {//{{{ - friend class Buffer; - friend class InputBuffer; - friend class OutputBuffer; - friend class CallbackIUPayloadUpdate; - friend class CallbackIULinkUpdate; - friend class CallbackIUCommission; - protected: - Message(const std::string& category, IUAccessMode access_mode=IU_ACCESS_MESSAGE, bool read_only=true, const std::string& payload_type="MAP" ); - public: - inline ~Message() { - //IPAACA_IMPLEMENT_ME - } - static boost::shared_ptr<Message> create(const std::string& category, IUAccessMode access_mode=IU_ACCESS_MESSAGE, bool read_only=true, const std::string& payload_type="MAP" ); - protected: - void _modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name = ""); - void _modify_payload(bool is_delta, const std::map<std::string, std::string>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name = ""); - protected: - void _internal_commit(const std::string& writer_name = ""); - public: - typedef boost::shared_ptr<Message> ptr; -};//}}} - -class RemotePushIU: public IUInterface {//{{{ - friend class Buffer; - friend class InputBuffer; - friend class OutputBuffer; - friend class IUConverter; - friend class MessageConverter; - public: - Payload _payload; - protected: - RemotePushIU(); - static boost::shared_ptr<RemotePushIU> create(); - public: - inline ~RemotePushIU() { - //IPAACA_IMPLEMENT_ME - } - inline Payload& payload() { return _payload; } - inline const Payload& const_payload() const { return _payload; } - void commit(); - protected: - void _modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name = ""); - void _modify_payload(bool is_delta, const std::map<std::string, std::string>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name = ""); - protected: - void _apply_update(IUPayloadUpdate::ptr update); - void _apply_link_update(IULinkUpdate::ptr update); - void _apply_commission(); - void _apply_retraction(); - typedef boost::shared_ptr<RemotePushIU> ptr; -};//}}} -class RemoteMessage: public IUInterface {//{{{ - friend class Buffer; - friend class InputBuffer; - friend class OutputBuffer; - friend class IUConverter; - friend class MessageConverter; - public: - Payload _payload; - protected: - RemoteMessage(); - static boost::shared_ptr<RemoteMessage> create(); - public: - inline ~RemoteMessage() { - //IPAACA_IMPLEMENT_ME - } - inline Payload& payload() { return _payload; } - inline const Payload& const_payload() const { return _payload; } - void commit(); - protected: - void _modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name = ""); - void _modify_payload(bool is_delta, const std::map<std::string, std::string>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name = ""); - protected: - void _apply_update(IUPayloadUpdate::ptr update); - void _apply_link_update(IULinkUpdate::ptr update); - void _apply_commission(); - void _apply_retraction(); - typedef boost::shared_ptr<RemoteMessage> ptr; -};//}}} - -class IUNotFoundError: public Exception//{{{ -{ - public: - inline ~IUNotFoundError() throw() { } - inline IUNotFoundError() { //boost::shared_ptr<IU> iu) { - _description = "IUNotFoundError"; - } -};//}}} -class IUPublishedError: public Exception//{{{ -{ - public: - inline ~IUPublishedError() throw() { } - inline IUPublishedError() { //boost::shared_ptr<IU> iu) { - _description = "IUPublishedError"; - } -};//}}} -class IUCommittedError: public Exception//{{{ -{ - public: - inline ~IUCommittedError() throw() { } - inline IUCommittedError() { //boost::shared_ptr<IU> iu) { - _description = "IUCommittedError"; - } -};//}}} -class IUUpdateFailedError: public Exception//{{{ -{ - public: - inline ~IUUpdateFailedError() throw() { } - inline IUUpdateFailedError() { //boost::shared_ptr<IU> iu) { - _description = "IUUpdateFailedError"; - } -};//}}} -class IUReadOnlyError: public Exception//{{{ -{ - public: - inline ~IUReadOnlyError() throw() { } - inline IUReadOnlyError() { //boost::shared_ptr<IU> iu) { - _description = "IUReadOnlyError"; - } -};//}}} -class IUAlreadyInABufferError: public Exception//{{{ -{ - public: - inline ~IUAlreadyInABufferError() throw() { } - inline IUAlreadyInABufferError() { //boost::shared_ptr<IU> iu) { - _description = "IUAlreadyInABufferError"; - } -};//}}} -class IUAlreadyHasAnUIDError: public Exception//{{{ -{ - public: - inline ~IUAlreadyHasAnUIDError() throw() { } - inline IUAlreadyHasAnUIDError() { //boost::shared_ptr<IU> iu) { - _description = "IUAlreadyHasAnUIDError"; - } -};//}}} -class IUAlreadyHasAnOwnerNameError: public Exception//{{{ -{ - public: - inline ~IUAlreadyHasAnOwnerNameError() throw() { } - inline IUAlreadyHasAnOwnerNameError() { //boost::shared_ptr<IU> iu) { - _description = "IUAlreadyHasAnOwnerNameError"; - } -};//}}} -class NotImplementedError: public Exception//{{{ -{ - public: - inline ~NotImplementedError() throw() { } - inline NotImplementedError() { //boost::shared_ptr<IU> iu) { - _description = "NotImplementedError"; - } -};//}}} +#include <ipaaca/ipaaca-payload.h> +#include <ipaaca/ipaaca-buffers.h> +#include <ipaaca/ipaaca-ius.h> + +/// Full API (including RSB transport) is only exposed during +/// ipaaca compilation, user programs should use abstract API. #ifdef IPAACA_EXPOSE_FULL_RSB_API -class CallbackIUPayloadUpdate: public rsb::patterns::Server::Callback<IUPayloadUpdate, int> {//{{{ - protected: - Buffer* _buffer; - public: - CallbackIUPayloadUpdate(Buffer* buffer); - boost::shared_ptr<int> call(const std::string& methodName, boost::shared_ptr<IUPayloadUpdate> update); -};//}}} -class CallbackIULinkUpdate: public rsb::patterns::Server::Callback<IULinkUpdate, int> {//{{{ - protected: - Buffer* _buffer; - public: - CallbackIULinkUpdate(Buffer* buffer); - public: - boost::shared_ptr<int> call(const std::string& methodName, boost::shared_ptr<IULinkUpdate> update); -};//}}} -class CallbackIUCommission: public rsb::patterns::Server::Callback<protobuf::IUCommission, int> {//{{{ - protected: - Buffer* _buffer; - public: - CallbackIUCommission(Buffer* buffer); - public: - boost::shared_ptr<int> call(const std::string& methodName, boost::shared_ptr<protobuf::IUCommission> update); -};//}}} -class CallbackIURetraction: public rsb::patterns::Server::Callback<protobuf::IURetraction, int> {//{{{ - protected: - Buffer* _buffer; - public: - CallbackIURetraction(Buffer* buffer); - public: - boost::shared_ptr<int> call(const std::string& methodName, boost::shared_ptr<protobuf::IURetraction> update); -};//}}} - -class IUConverter: public rsb::converter::Converter<std::string> {//{{{ - public: - IUConverter(); - std::string serialize(const rsb::AnnotatedData& data, std::string& wire); - rsb::AnnotatedData deserialize(const std::string& wireSchema, const std::string& wire); -};//}}} -class MessageConverter: public rsb::converter::Converter<std::string> {//{{{ - public: - MessageConverter(); - std::string serialize(const rsb::AnnotatedData& data, std::string& wire); - rsb::AnnotatedData deserialize(const std::string& wireSchema, const std::string& wire); -};//}}} -class IUPayloadUpdateConverter: public rsb::converter::Converter<std::string> {//{{{ - public: - IUPayloadUpdateConverter(); - std::string serialize(const rsb::AnnotatedData& data, std::string& wire); - rsb::AnnotatedData deserialize(const std::string& wireSchema, const std::string& wire); -};//}}} -class IULinkUpdateConverter: public rsb::converter::Converter<std::string> {//{{{ - public: - IULinkUpdateConverter(); - std::string serialize(const rsb::AnnotatedData& data, std::string& wire); - rsb::AnnotatedData deserialize(const std::string& wireSchema, const std::string& wire); -};//}}} -class IntConverter: public rsb::converter::Converter<std::string> {//{{{ - public: - IntConverter(); - std::string serialize(const rsb::AnnotatedData& data, std::string& wire); - rsb::AnnotatedData deserialize(const std::string& wireSchema, const std::string& wire); -};//}}} +#include <ipaaca/ipaaca-internal.h> #endif -// additional misc classes ( Command line options )//{{{ -class CommandLineOptions { - public: - inline CommandLineOptions() { } - std::map<std::string, std::string> param_opts; - std::map<std::string, bool> param_set; - public: - void set_option(const std::string& name, bool expect, const char* optarg); - std::string get_param(const std::string& o); - bool is_set(const std::string& o); - void dump(); - typedef boost::shared_ptr<CommandLineOptions> ptr; -}; - -class CommandLineParser { - protected: - std::map<char, std::string> longopt; // letter->name - std::map<std::string, char> shortopt; // letter->name - std::map<std::string, bool> options; // name / expect_param - std::map<std::string, std::string> defaults; // for opt params - std::map<std::string, int> set_flag; // for paramless opts - protected: - CommandLineParser(); - public: - inline ~CommandLineParser() { } - static inline boost::shared_ptr<CommandLineParser> create() { - return boost::shared_ptr<CommandLineParser>(new CommandLineParser()); - } - void initialize_parser_defaults(); - void dump_options(); - void add_option(const std::string& optname, char shortn, bool expect_param, const std::string& defaultv); - void ensure_defaults_in( CommandLineOptions::ptr clo ); - CommandLineOptions::ptr parse(int argc, char* const* argv); - typedef boost::shared_ptr<CommandLineParser> ptr; -}; -//}}} - -// additional misc functions ( String splitting / joining )//{{{ -std::string str_join(const std::set<std::string>& set,const std::string& sep); -std::string str_join(const std::vector<std::string>& vec,const std::string& sep); -void str_split_wipe(const std::string& str, std::vector<std::string>& tokens, const std::string& delimiters ); -void str_split_append(const std::string& str, std::vector<std::string>& tokens, const std::string& delimiters ); -//}}} - -// (snippets) //{{{ -/* -class IUEventFunctionHandler: public rsb::EventFunctionHandler { - protected: - Buffer* _buffer; - public: - inline IUEventFunctionHandler(Buffer* buffer, const EventFunction& function, const std::string& method="") - : EventFunctionHandler(function, method), _buffer(buffer) { } -}; -*/ -//}}} - -Lock& logger_lock(); -#define LOG_IPAACA_CONSOLE(msg) { ipaaca::Locker logging_locker(ipaaca::logger_lock()); timeval logging_tim; gettimeofday(&logging_tim, NULL); double logging_t1=logging_tim.tv_sec+(logging_tim.tv_usec/1000000.0); std::cout << "[LOG] " << std::setprecision(15) << logging_t1 << " : " << msg << std::endl; } } // of namespace ipaaca diff --git a/ipaacalib/cpp/include/ipaaca/util/notifier.h b/ipaacalib/cpp/include/ipaaca/util/notifier.h index 1bd507523c42ce6ab43501b73b8fcfaa4bd7658d..c16a4dfc35be4729f4d447d405ac3ea658118be8 100644 --- a/ipaacalib/cpp/include/ipaaca/util/notifier.h +++ b/ipaacalib/cpp/include/ipaaca/util/notifier.h @@ -30,6 +30,17 @@ * Excellence Initiative. */ +/** + * \file util/notifier.h + * + * \brief Header file for component notification (i.e. module-level introspection). + * + * Include this file in addition to ipaaca.h to use the functionality. + * + * \author Ramin Yaghoubzadeh (ryaghoubzadeh@uni-bielefeld.de) + * \date March, 2015 + */ + #ifndef __IPAACA_UTIL_NOTIFIER_H__ #define __IPAACA_UTIL_NOTIFIER_H__ diff --git a/ipaacalib/cpp/include/rapidjson/LICENSE-rapidjson.txt b/ipaacalib/cpp/include/rapidjson/LICENSE-rapidjson.txt new file mode 100644 index 0000000000000000000000000000000000000000..40832c806d1024e0d4486b8915f2e715f48d8788 --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/LICENSE-rapidjson.txt @@ -0,0 +1,24 @@ +Copyright (C) 2011 Milo Yip + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +-- +This software and license copied in January 2015 from +https://github.com/miloyip/rapidjson/ diff --git a/ipaacalib/cpp/include/rapidjson/allocators.h b/ipaacalib/cpp/include/rapidjson/allocators.h new file mode 100644 index 0000000000000000000000000000000000000000..c0a26eea9f9a74db36c3d6d3eb2c2323d9e5cf1b --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/allocators.h @@ -0,0 +1,245 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_ALLOCATORS_H_ +#define RAPIDJSON_ALLOCATORS_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Allocator + +/*! \class rapidjson::Allocator + \brief Concept for allocating, resizing and freeing memory block. + + Note that Malloc() and Realloc() are non-static but Free() is static. + + So if an allocator need to support Free(), it needs to put its pointer in + the header of memory block. + +\code +concept Allocator { + static const bool kNeedFree; //!< Whether this allocator needs to call Free(). + + // Allocate a memory block. + // \param size of the memory block in bytes. + // \returns pointer to the memory block. + void* Malloc(size_t size); + + // Resize a memory block. + // \param originalPtr The pointer to current memory block. Null pointer is permitted. + // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) + // \param newSize the new size in bytes. + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); + + // Free a memory block. + // \param pointer to the memory block. Null pointer is permitted. + static void Free(void *ptr); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// CrtAllocator + +//! C-runtime library allocator. +/*! This class is just wrapper for standard C library memory routines. + \note implements Allocator concept +*/ +class CrtAllocator { +public: + static const bool kNeedFree = true; + void* Malloc(size_t size) { return std::malloc(size); } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { (void)originalSize; return std::realloc(originalPtr, newSize); } + static void Free(void *ptr) { std::free(ptr); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// MemoryPoolAllocator + +//! Default memory allocator used by the parser and DOM. +/*! This allocator allocate memory blocks from pre-allocated memory chunks. + + It does not free memory blocks. And Realloc() only allocate new memory. + + The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. + + User may also supply a buffer as the first chunk. + + If the user-buffer is full then additional chunks are allocated by BaseAllocator. + + The user-buffer is not deallocated by this allocator. + + \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. + \note implements Allocator concept +*/ +template <typename BaseAllocator = CrtAllocator> +class MemoryPoolAllocator { +public: + static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) + + //! Constructor with chunkSize. + /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + } + + //! Constructor with user-supplied buffer. + /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. + + The user buffer will not be deallocated when this allocator is destructed. + + \param buffer User supplied buffer. + \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). + \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + RAPIDJSON_ASSERT(buffer != 0); + RAPIDJSON_ASSERT(size > sizeof(ChunkHeader)); + chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer); + chunkHead_->capacity = size - sizeof(ChunkHeader); + chunkHead_->size = 0; + chunkHead_->next = 0; + } + + //! Destructor. + /*! This deallocates all memory chunks, excluding the user-supplied buffer. + */ + ~MemoryPoolAllocator() { + Clear(); + RAPIDJSON_DELETE(ownBaseAllocator_); + } + + //! Deallocates all memory chunks, excluding the user-supplied buffer. + void Clear() { + while(chunkHead_ != 0 && chunkHead_ != userBuffer_) { + ChunkHeader* next = chunkHead_->next; + baseAllocator_->Free(chunkHead_); + chunkHead_ = next; + } + } + + //! Computes the total capacity of allocated memory chunks. + /*! \return total capacity in bytes. + */ + size_t Capacity() const { + size_t capacity = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + capacity += c->capacity; + return capacity; + } + + //! Computes the memory blocks allocated. + /*! \return total used bytes. + */ + size_t Size() const { + size_t size = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + size += c->size; + return size; + } + + //! Allocates a memory block. (concept Allocator) + void* Malloc(size_t size) { + size = RAPIDJSON_ALIGN(size); + if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) + AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size); + + void *buffer = reinterpret_cast<char *>(chunkHead_ + 1) + chunkHead_->size; + chunkHead_->size += size; + return buffer; + } + + //! Resizes a memory block (concept Allocator) + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + if (originalPtr == 0) + return Malloc(newSize); + + // Do not shrink if new size is smaller than original + if (originalSize >= newSize) + return originalPtr; + + // Simply expand it if it is the last allocation and there is sufficient space + if (originalPtr == (char *)(chunkHead_ + 1) + chunkHead_->size - originalSize) { + size_t increment = static_cast<size_t>(newSize - originalSize); + increment = RAPIDJSON_ALIGN(increment); + if (chunkHead_->size + increment <= chunkHead_->capacity) { + chunkHead_->size += increment; + return originalPtr; + } + } + + // Realloc process: allocate and copy memory, do not free original buffer. + void* newBuffer = Malloc(newSize); + RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly. + return std::memcpy(newBuffer, originalPtr, originalSize); + } + + //! Frees a memory block (concept Allocator) + static void Free(void *ptr) { (void)ptr; } // Do nothing + +private: + //! Copy constructor is not permitted. + MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */; + //! Copy assignment operator is not permitted. + MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */; + + //! Creates a new chunk. + /*! \param capacity Capacity of the chunk in bytes. + */ + void AddChunk(size_t capacity) { + if (!baseAllocator_) + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator()); + ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(sizeof(ChunkHeader) + capacity)); + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = chunkHead_; + chunkHead_ = chunk; + } + + static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. + + //! Chunk header for perpending to each chunk. + /*! Chunks are stored as a singly linked list. + */ + struct ChunkHeader { + size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). + size_t size; //!< Current size of allocated memory in bytes. + ChunkHeader *next; //!< Next chunk in the linked list. + }; + + ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation. + size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. + void *userBuffer_; //!< User supplied buffer. + BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. + BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object. +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/ipaacalib/cpp/include/rapidjson/document.h b/ipaacalib/cpp/include/rapidjson/document.h new file mode 100644 index 0000000000000000000000000000000000000000..7656f446d6accbc2516273c68b5ecafc45f722c3 --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/document.h @@ -0,0 +1,1932 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_DOCUMENT_H_ +#define RAPIDJSON_DOCUMENT_H_ + +/*! \file document.h */ + +#include "reader.h" +#include "internal/meta.h" +#include "internal/strfunc.h" +#include <new> // placement new + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#elif defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_HAS_STDSTRING +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default +#endif +/*! \def RAPIDJSON_HAS_STDSTRING + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for \c std::string + + By defining this preprocessor symbol to \c 1, several convenience functions for using + \ref rapidjson::GenericValue with \c std::string are enabled, especially + for construction and comparison. + + \hideinitializer +*/ +#include <string> +#endif // RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS +#include <iterator> // std::iterator, std::random_access_iterator_tag +#endif + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include <utility> // std::move +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +// Forward declaration. +template <typename Encoding, typename Allocator> +class GenericValue; + +//! Name-value pair in a JSON object value. +/*! + This class was internal to GenericValue. It used to be a inner struct. + But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct. + https://code.google.com/p/rapidjson/issues/detail?id=64 +*/ +template <typename Encoding, typename Allocator> +struct GenericMember { + GenericValue<Encoding, Allocator> name; //!< name of member (must be a string) + GenericValue<Encoding, Allocator> value; //!< value of member. +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericMemberIterator + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS + +//! (Constant) member iterator for a JSON object value +/*! + \tparam Const Is this a constant iterator? + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. + + This class implements a Random Access Iterator for GenericMember elements + of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements]. + + \note This iterator implementation is mainly intended to avoid implicit + conversions from iterator values to \c NULL, + e.g. from GenericValue::FindMember. + + \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a + pointer-based implementation, if your platform doesn't provide + the C++ <iterator> header. + + \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator + */ +template <bool Const, typename Encoding, typename Allocator> +class GenericMemberIterator + : public std::iterator<std::random_access_iterator_tag + , typename internal::MaybeAddConst<Const,GenericMember<Encoding,Allocator> >::Type> { + + friend class GenericValue<Encoding,Allocator>; + template <bool, typename, typename> friend class GenericMemberIterator; + + typedef GenericMember<Encoding,Allocator> PlainType; + typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType; + typedef std::iterator<std::random_access_iterator_tag,ValueType> BaseType; + +public: + //! Iterator type itself + typedef GenericMemberIterator Iterator; + //! Constant iterator type + typedef GenericMemberIterator<true,Encoding,Allocator> ConstIterator; + //! Non-constant iterator type + typedef GenericMemberIterator<false,Encoding,Allocator> NonConstIterator; + + //! Pointer to (const) GenericMember + typedef typename BaseType::pointer Pointer; + //! Reference to (const) GenericMember + typedef typename BaseType::reference Reference; + //! Signed integer type (e.g. \c ptrdiff_t) + typedef typename BaseType::difference_type DifferenceType; + + //! Default constructor (singular value) + /*! Creates an iterator pointing to no element. + \note All operations, except for comparisons, are undefined on such values. + */ + GenericMemberIterator() : ptr_() {} + + //! Iterator conversions to more const + /*! + \param it (Non-const) iterator to copy from + + Allows the creation of an iterator from another GenericMemberIterator + that is "less const". Especially, creating a non-constant iterator + from a constant iterator are disabled: + \li const -> non-const (not ok) + \li const -> const (ok) + \li non-const -> const (ok) + \li non-const -> non-const (ok) + + \note If the \c Const template parameter is already \c false, this + constructor effectively defines a regular copy-constructor. + Otherwise, the copy constructor is implicitly defined. + */ + GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} + + //! @name stepping + //@{ + Iterator& operator++(){ ++ptr_; return *this; } + Iterator& operator--(){ --ptr_; return *this; } + Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; } + Iterator operator--(int){ Iterator old(*this); --ptr_; return old; } + //@} + + //! @name increment/decrement + //@{ + Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); } + Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); } + + Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; } + Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; } + //@} + + //! @name relations + //@{ + bool operator==(ConstIterator that) const { return ptr_ == that.ptr_; } + bool operator!=(ConstIterator that) const { return ptr_ != that.ptr_; } + bool operator<=(ConstIterator that) const { return ptr_ <= that.ptr_; } + bool operator>=(ConstIterator that) const { return ptr_ >= that.ptr_; } + bool operator< (ConstIterator that) const { return ptr_ < that.ptr_; } + bool operator> (ConstIterator that) const { return ptr_ > that.ptr_; } + //@} + + //! @name dereference + //@{ + Reference operator*() const { return *ptr_; } + Pointer operator->() const { return ptr_; } + Reference operator[](DifferenceType n) const { return ptr_[n]; } + //@} + + //! Distance + DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; } + +private: + //! Internal constructor from plain pointer + explicit GenericMemberIterator(Pointer p) : ptr_(p) {} + + Pointer ptr_; //!< raw pointer +}; + +#else // RAPIDJSON_NOMEMBERITERATORCLASS + +// class-based member iterator implementation disabled, use plain pointers + +template <bool Const, typename Encoding, typename Allocator> +struct GenericMemberIterator; + +//! non-const GenericMemberIterator +template <typename Encoding, typename Allocator> +struct GenericMemberIterator<false,Encoding,Allocator> { + //! use plain pointer as iterator type + typedef GenericMember<Encoding,Allocator>* Iterator; +}; +//! const GenericMemberIterator +template <typename Encoding, typename Allocator> +struct GenericMemberIterator<true,Encoding,Allocator> { + //! use plain const pointer as iterator type + typedef const GenericMember<Encoding,Allocator>* Iterator; +}; + +#endif // RAPIDJSON_NOMEMBERITERATORCLASS + +/////////////////////////////////////////////////////////////////////////////// +// GenericStringRef + +//! Reference to a constant string (not taking a copy) +/*! + \tparam CharType character type of the string + + This helper class is used to automatically infer constant string + references for string literals, especially from \c const \b (!) + character arrays. + + The main use is for creating JSON string values without copying the + source string via an \ref Allocator. This requires that the referenced + string pointers have a sufficient lifetime, which exceeds the lifetime + of the associated GenericValue. + + \b Example + \code + Value v("foo"); // ok, no need to copy & calculate length + const char foo[] = "foo"; + v.SetString(foo); // ok + + const char* bar = foo; + // Value x(bar); // not ok, can't rely on bar's lifetime + Value x(StringRef(bar)); // lifetime explicitly guaranteed by user + Value y(StringRef(bar, 3)); // ok, explicitly pass length + \endcode + + \see StringRef, GenericValue::SetString +*/ +template<typename CharType> +struct GenericStringRef { + typedef CharType Ch; //!< character type of the string + + //! Create string reference from \c const character array + /*! + This constructor implicitly creates a constant string reference from + a \c const character array. It has better performance than + \ref StringRef(const CharType*) by inferring the string \ref length + from the array length, and also supports strings containing null + characters. + + \tparam N length of the string, automatically inferred + + \param str Constant character array, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note Constant complexity. + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ + template<SizeType N> + GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT + : s(str), length(N-1) {} + + //! Explicitly create string reference from \c const character pointer + /*! + This constructor can be used to \b explicitly create a reference to + a constant string pointer. + + \see StringRef(const CharType*) + + \param str Constant character pointer, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ + explicit GenericStringRef(const CharType* str) + : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != NULL); } + + //! Create constant string reference from pointer and length + /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param len length of the string, excluding the trailing NULL terminator + + \post \ref s == str && \ref length == len + \note Constant complexity. + */ + GenericStringRef(const CharType* str, SizeType len) + : s(str), length(len) { RAPIDJSON_ASSERT(s != NULL); } + + //! implicit conversion to plain CharType pointer + operator const Ch *() const { return s; } + + const Ch* const s; //!< plain CharType pointer + const SizeType length; //!< length of the string (excluding the trailing NULL terminator) + +private: + //! Disallow copy-assignment + GenericStringRef operator=(const GenericStringRef&); + //! Disallow construction from non-const array + template<SizeType N> + GenericStringRef(CharType (&str)[N]) /* = delete */; +}; + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + \tparam CharType Character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + + \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember +*/ +template<typename CharType> +inline GenericStringRef<CharType> StringRef(const CharType* str) { + return GenericStringRef<CharType>(str, internal::StrLen(str)); +} + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + This version has better performance with supplied length, and also + supports string containing null characters. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param length The length of source string. + \return GenericStringRef string reference object + \relatesalso GenericStringRef +*/ +template<typename CharType> +inline GenericStringRef<CharType> StringRef(const CharType* str, size_t length) { + return GenericStringRef<CharType>(str, SizeType(length)); +} + +#if RAPIDJSON_HAS_STDSTRING +//! Mark a string object as constant string +/*! Mark a string object (e.g. \c std::string) as a "string literal". + This function can be used to avoid copying a string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. +*/ +template<typename CharType> +inline GenericStringRef<CharType> StringRef(const std::basic_string<CharType>& str) { + return GenericStringRef<CharType>(str.data(), SizeType(str.size())); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue type traits +namespace internal { + +template <typename T, typename Encoding = void, typename Allocator = void> +struct IsGenericValueImpl : FalseType {}; + +// select candidates according to nested encoding and allocator types +template <typename T> struct IsGenericValueImpl<T, typename Void<typename T::EncodingType>::Type, typename Void<typename T::AllocatorType>::Type> + : IsBaseOf<GenericValue<typename T::EncodingType, typename T::AllocatorType>, T>::Type {}; + +// helper to match arbitrary GenericValue instantiations, including derived classes +template <typename T> struct IsGenericValue : IsGenericValueImpl<T>::Type {}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue + +//! Represents a JSON value. Use Value for UTF8 encoding and default allocator. +/*! + A JSON value can be one of 7 types. This class is a variant type supporting + these types. + + Use the Value if UTF8 and default allocator + + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. +*/ +template <typename Encoding, typename Allocator = MemoryPoolAllocator<> > +class GenericValue { +public: + //! Name-value pair in an object. + typedef GenericMember<Encoding, Allocator> Member; + typedef Encoding EncodingType; //!< Encoding type from template parameter. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericStringRef<Ch> StringRefType; //!< Reference to a constant string + typedef typename GenericMemberIterator<false,Encoding,Allocator>::Iterator MemberIterator; //!< Member iterator for iterating in object. + typedef typename GenericMemberIterator<true,Encoding,Allocator>::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. + typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. + typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. + + //!@name Constructors and destructor. + //@{ + + //! Default constructor creates a null value. + GenericValue() RAPIDJSON_NOEXCEPT : data_(), flags_(kNullFlag) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_), flags_(rhs.flags_) { + rhs.flags_ = kNullFlag; // give up contents + } +#endif + +private: + //! Copy constructor is not permitted. + GenericValue(const GenericValue& rhs); + +public: + + //! Constructor with JSON value type. + /*! This creates a Value of specified type with default content. + \param type Type of the value. + \note Default content for number is zero. + */ + explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_(), flags_() { + static const unsigned defaultFlags[7] = { + kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, + kNumberAnyFlag + }; + RAPIDJSON_ASSERT(type <= kNumberType); + flags_ = defaultFlags[type]; + + // Use ShortString to store empty string. + if (type == kStringType) + data_.ss.SetLength(0); + } + + //! Explicit copy constructor (with allocator) + /*! Creates a copy of a Value by using the given Allocator + \tparam SourceAllocator allocator of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \see CopyFrom() + */ + template< typename SourceAllocator > + GenericValue(const GenericValue<Encoding, SourceAllocator>& rhs, Allocator & allocator); + + //! Constructor for boolean value. + /*! \param b Boolean value + \note This constructor is limited to \em real boolean values and rejects + implicitly converted types like arbitrary pointers. Use an explicit cast + to \c bool, if you want to construct a boolean JSON value in such cases. + */ +#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen + template <typename T> + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame<T,bool>))) RAPIDJSON_NOEXCEPT +#else + explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT +#endif + : data_(), flags_(b ? kTrueFlag : kFalseFlag) { + // safe-guard against failing SFINAE + RAPIDJSON_STATIC_ASSERT((internal::IsSame<bool,T>::Value)); + } + + //! Constructor for int value. + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberIntFlag) { + data_.n.i64 = i; + if (i >= 0) + flags_ |= kUintFlag | kUint64Flag; + } + + //! Constructor for unsigned value. + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUintFlag) { + data_.n.u64 = u; + if (!(u & 0x80000000)) + flags_ |= kIntFlag | kInt64Flag; + } + + //! Constructor for int64_t value. + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberInt64Flag) { + data_.n.i64 = i64; + if (i64 >= 0) { + flags_ |= kNumberUint64Flag; + if (!(static_cast<uint64_t>(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + flags_ |= kUintFlag; + if (!(static_cast<uint64_t>(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + flags_ |= kIntFlag; + } + else if (i64 >= static_cast<int64_t>(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + flags_ |= kIntFlag; + } + + //! Constructor for uint64_t value. + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUint64Flag) { + data_.n.u64 = u64; + if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) + flags_ |= kInt64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + flags_ |= kUintFlag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + flags_ |= kIntFlag; + } + + //! Constructor for double value. + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; } + + //! Constructor for constant string (i.e. do not make a copy of string) + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(StringRef(s, length)); } + + //! Constructor for constant string (i.e. do not make a copy of string) + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(s); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s, length), allocator); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor for copy-string from a string object (i.e. do make a copy of string) + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue(const std::basic_string<Ch>& s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } +#endif + + //! Destructor. + /*! Need to destruct elements of array, members of object, or copy-string. + */ + ~GenericValue() { + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + switch(flags_) { + case kArrayFlag: + for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) + v->~GenericValue(); + Allocator::Free(data_.a.elements); + break; + + case kObjectFlag: + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + Allocator::Free(data_.o.members); + break; + + case kCopyStringFlag: + Allocator::Free(const_cast<Ch*>(data_.s.str)); + break; + + default: + break; // Do nothing for other types. + } + } + } + + //@} + + //!@name Assignment operators + //@{ + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. It will become a null value after assignment. + */ + GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + RAPIDJSON_ASSERT(this != &rhs); + this->~GenericValue(); + RawAssign(rhs); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { + return *this = rhs.Move(); + } +#endif + + //! Assignment of constant string reference (no copy) + /*! \param str Constant string reference to be assigned + \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. + \see GenericStringRef, operator=(T) + */ + GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { + GenericValue s(str); + return *this = s; + } + + //! Assignment with primitive types. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value The value to be assigned. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref SetString(const Ch*, Allocator&) (for copying) or + \ref StringRef() (to explicitly mark the pointer as constant) instead. + All other pointer types would implicitly convert to \c bool, + use \ref SetBool() instead. + */ + template <typename T> + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer<T>), (GenericValue&)) + operator=(T value) { + GenericValue v(value); + return *this = v; + } + + //! Deep-copy assignment from Value + /*! Assigns a \b copy of the Value to the current Value object + \tparam SourceAllocator Allocator type of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator to use for copying + */ + template <typename SourceAllocator> + GenericValue& CopyFrom(const GenericValue<Encoding, SourceAllocator>& rhs, Allocator& allocator) { + RAPIDJSON_ASSERT((void*)this != (void const*)&rhs); + this->~GenericValue(); + new (this) GenericValue(rhs, allocator); + return *this; + } + + //! Exchange the contents of this value with those of other. + /*! + \param other Another value. + \note Constant complexity. + */ + GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { + GenericValue temp; + temp.RawAssign(*this); + RawAssign(other); + other.RawAssign(temp); + return *this; + } + + //! Prepare Value for move semantics + /*! \return *this */ + GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } + //@} + + //!@name Equal-to and not-equal-to operators + //@{ + //! Equal-to operator + /*! + \note If an object contains duplicated named member, comparing equality with any object is always \c false. + \note Linear time complexity (number of all values in the subtree and total lengths of all strings). + */ + template <typename SourceAllocator> + bool operator==(const GenericValue<Encoding, SourceAllocator>& rhs) const { + typedef GenericValue<Encoding, SourceAllocator> RhsType; + if (GetType() != rhs.GetType()) + return false; + + switch (GetType()) { + case kObjectType: // Warning: O(n^2) inner-loop + if (data_.o.size != rhs.data_.o.size) + return false; + for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { + typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); + if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) + return false; + } + return true; + + case kArrayType: + if (data_.a.size != rhs.data_.a.size) + return false; + for (SizeType i = 0; i < data_.a.size; i++) + if ((*this)[i] != rhs[i]) + return false; + return true; + + case kStringType: + return StringEqual(rhs); + + case kNumberType: + if (IsDouble() || rhs.IsDouble()) + return GetDouble() == rhs.GetDouble(); // May convert one operand from integer to double. + else + return data_.n.u64 == rhs.data_.n.u64; + + default: // kTrueType, kFalseType, kNullType + return true; + } + } + + //! Equal-to operator with const C-string pointer + bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } + +#if RAPIDJSON_HAS_STDSTRING + //! Equal-to operator with string object + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + bool operator==(const std::basic_string<Ch>& rhs) const { return *this == GenericValue(StringRef(rhs)); } +#endif + + //! Equal-to operator with primitive types + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false + */ + template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>,internal::IsGenericValue<T> >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + + //! Not-equal-to operator + /*! \return !(*this == rhs) + */ + template <typename SourceAllocator> + bool operator!=(const GenericValue<Encoding, SourceAllocator>& rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with const C-string pointer + bool operator!=(const Ch* rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with arbitrary types + /*! \return !(*this == rhs) + */ + template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue<T>), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } + + //! Equal-to operator with arbitrary types (symmetric version) + /*! \return (rhs == lhs) + */ + template <typename T> friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue<T>), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } + + //! Not-Equal-to operator with arbitrary types (symmetric version) + /*! \return !(rhs == lhs) + */ + template <typename T> friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue<T>), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } + //@} + + //!@name Type + //@{ + + Type GetType() const { return static_cast<Type>(flags_ & kTypeMask); } + bool IsNull() const { return flags_ == kNullFlag; } + bool IsFalse() const { return flags_ == kFalseFlag; } + bool IsTrue() const { return flags_ == kTrueFlag; } + bool IsBool() const { return (flags_ & kBoolFlag) != 0; } + bool IsObject() const { return flags_ == kObjectFlag; } + bool IsArray() const { return flags_ == kArrayFlag; } + bool IsNumber() const { return (flags_ & kNumberFlag) != 0; } + bool IsInt() const { return (flags_ & kIntFlag) != 0; } + bool IsUint() const { return (flags_ & kUintFlag) != 0; } + bool IsInt64() const { return (flags_ & kInt64Flag) != 0; } + bool IsUint64() const { return (flags_ & kUint64Flag) != 0; } + bool IsDouble() const { return (flags_ & kDoubleFlag) != 0; } + bool IsString() const { return (flags_ & kStringFlag) != 0; } + + //@} + + //!@name Null + //@{ + + GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } + + //@} + + //!@name Bool + //@{ + + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return flags_ == kTrueFlag; } + //!< Set boolean value + /*! \post IsBool() == true */ + GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } + + //@} + + //!@name Object + //@{ + + //! Set this value as an empty object. + /*! \post IsObject() == true */ + GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } + + //! Get the number of members in the object. + SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } + + //! Check whether the object is empty. + bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType)) + \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. + Since 0.2, if the name is not correct, it will assert. + If user is unsure whether a member exists, user should use HasMember() first. + A better approach is to use FindMember(). + \note Linear time complexity. + */ + template <typename T> + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr<internal::IsSame<typename internal::RemoveConst<T>::Type, Ch> >),(GenericValue&)) operator[](T* name) { + GenericValue n(StringRef(name)); + return (*this)[n]; + } + template <typename T> + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr<internal::IsSame<typename internal::RemoveConst<T>::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast<GenericValue&>(*this)[name]; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam SourceAllocator Allocator of the \c name value + + \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen(). + And it can also handle strings with embedded null characters. + + \note Linear time complexity. + */ + template <typename SourceAllocator> + GenericValue& operator[](const GenericValue<Encoding, SourceAllocator>& name) { + MemberIterator member = FindMember(name); + if (member != MemberEnd()) + return member->value; + else { + RAPIDJSON_ASSERT(false); // see above note + static GenericValue NullValue; + return NullValue; + } + } + template <typename SourceAllocator> + const GenericValue& operator[](const GenericValue<Encoding, SourceAllocator>& name) const { return const_cast<GenericValue&>(*this)[name]; } + + //! Const member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members); } + //! Const \em past-the-end member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members + data_.o.size); } + //! Member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members); } + //! \em Past-the-end member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members + data_.o.size); } + + //! Check whether a member exists in the object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); } + + //! Check whether a member exists in the object with GenericValue name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + template <typename SourceAllocator> + bool HasMember(const GenericValue<Encoding, SourceAllocator>& name) const { return FindMember(name) != MemberEnd(); } + + //! Find member by name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + MemberIterator FindMember(const Ch* name) { + GenericValue n(StringRef(name)); + return FindMember(n); + } + + ConstMemberIterator FindMember(const Ch* name) const { return const_cast<GenericValue&>(*this).FindMember(name); } + + //! Find member by name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + template <typename SourceAllocator> + MemberIterator FindMember(const GenericValue<Encoding, SourceAllocator>& name) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + MemberIterator member = MemberBegin(); + for ( ; member != MemberEnd(); ++member) + if (name.StringEqual(member->name)) + break; + return member; + } + template <typename SourceAllocator> ConstMemberIterator FindMember(const GenericValue<Encoding, SourceAllocator>& name) const { return const_cast<GenericValue&>(*this).FindMember(name); } + + //! Add a member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c name and \c value will be transferred to this object on success. + \pre IsObject() && name.IsString() + \post name.IsNull() && value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + + Object& o = data_.o; + if (o.size >= o.capacity) { + if (o.capacity == 0) { + o.capacity = kDefaultObjectCapacity; + o.members = reinterpret_cast<Member*>(allocator.Malloc(o.capacity * sizeof(Member))); + } + else { + SizeType oldCapacity = o.capacity; + o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 + o.members = reinterpret_cast<Member*>(allocator.Realloc(o.members, oldCapacity * sizeof(Member), o.capacity * sizeof(Member))); + } + } + o.members[o.size].name.RawAssign(name); + o.members[o.size].value.RawAssign(value); + o.size++; + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + + //! Add a member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this object on success. + \pre IsObject() + \post value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A constant string reference as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template <typename T> + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericValue&)) + AddMember(StringRefType name, T value, Allocator& allocator) { + GenericValue n(name); + GenericValue v(value); + return AddMember(n, v, allocator); + } + + //! Remove all members in the object. + /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void RemoveAllMembers() { + RAPIDJSON_ASSERT(IsObject()); + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + data_.o.size = 0; + } + + //! Remove a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Linear time complexity. + */ + bool RemoveMember(const Ch* name) { + GenericValue n(StringRef(name)); + return RemoveMember(n); + } + + template <typename SourceAllocator> + bool RemoveMember(const GenericValue<Encoding, SourceAllocator>& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + RemoveMember(m); + return true; + } + else + return false; + } + + //! Remove a member in object by iterator. + /*! \param m member iterator (obtained by FindMember() or MemberBegin()). + \return the new iterator after removal. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Constant time complexity. + */ + MemberIterator RemoveMember(MemberIterator m) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(data_.o.members != 0); + RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); + + MemberIterator last(data_.o.members + (data_.o.size - 1)); + if (data_.o.size > 1 && m != last) { + // Move the last one to this place + *m = *last; + } + else { + // Only one left, just destroy + m->~Member(); + } + --data_.o.size; + return m; + } + + //! Remove a member from an object by iterator. + /*! \param pos iterator to the member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() + \return Iterator following the removed element. + If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. + \note This function preserves the relative order of the remaining object + members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator). + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator pos) { + return EraseMember(pos, pos +1); + } + + //! Remove members in the range [first, last) from an object. + /*! \param first iterator to the first member to remove + \param last iterator following the last member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() + \return Iterator following the last removed element. + \note This function preserves the relative order of the remaining object + members. + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(data_.o.members != 0); + RAPIDJSON_ASSERT(first >= MemberBegin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= MemberEnd()); + + MemberIterator pos = MemberBegin() + (first - MemberBegin()); + for (MemberIterator itr = pos; itr != last; ++itr) + itr->~Member(); + std::memmove(&*pos, &*last, (MemberEnd() - last) * sizeof(Member)); + data_.o.size -= (last - first); + return pos; + } + + //@} + + //!@name Array + //@{ + + //! Set this value as an empty array. + /*! \post IsArray == true */ + GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + + //! Get the number of elements in array. + SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } + + //! Get the capacity of array. + SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; } + + //! Check whether the array is empty. + bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; } + + //! Remove all elements in the array. + /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void Clear() { + RAPIDJSON_ASSERT(IsArray()); + for (SizeType i = 0; i < data_.a.size; ++i) + data_.a.elements[i].~GenericValue(); + data_.a.size = 0; + } + + //! Get an element from array by index. + /*! \pre IsArray() == true + \param index Zero-based index of element. + \see operator[](T*) + */ + GenericValue& operator[](SizeType index) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(index < data_.a.size); + return data_.a.elements[index]; + } + const GenericValue& operator[](SizeType index) const { return const_cast<GenericValue&>(*this)[index]; } + + //! Element iterator + /*! \pre IsArray() == true */ + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements; } + //! \em Past-the-end element iterator + /*! \pre IsArray() == true */ + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements + data_.a.size; } + //! Constant element iterator + /*! \pre IsArray() == true */ + ConstValueIterator Begin() const { return const_cast<GenericValue&>(*this).Begin(); } + //! Constant \em past-the-end element iterator + /*! \pre IsArray() == true */ + ConstValueIterator End() const { return const_cast<GenericValue&>(*this).End(); } + + //! Request the array to have enough capacity to store elements. + /*! \param newCapacity The capacity that the array at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (newCapacity > data_.a.capacity) { + data_.a.elements = (GenericValue*)allocator.Realloc(data_.a.elements, data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)); + data_.a.capacity = newCapacity; + } + return *this; + } + + //! Append a GenericValue at the end of the array. + /*! \param value Value to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \post value.IsNull() == true + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this array on success. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + */ + GenericValue& PushBack(GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (data_.a.size >= data_.a.capacity) + Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); + data_.a.elements[data_.a.size++].RawAssign(value); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { + return PushBack(value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + //! Append a constant string reference at the end of the array. + /*! \param value Constant string reference to be appended. + \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + \see GenericStringRef + */ + GenericValue& PushBack(StringRefType value, Allocator& allocator) { + return (*this).template PushBack<StringRefType>(value, allocator); + } + + //! Append a primitive value at the end of the array. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value Value of primitive type T to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref PushBack(GenericValue&, Allocator&) or \ref + PushBack(StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized constant time complexity. + */ + template <typename T> + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericValue&)) + PushBack(T value, Allocator& allocator) { + GenericValue v(value); + return PushBack(v, allocator); + } + + //! Remove the last element in the array. + /*! + \note Constant time complexity. + */ + GenericValue& PopBack() { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(!Empty()); + data_.a.elements[--data_.a.size].~GenericValue(); + return *this; + } + + //! Remove an element of array by iterator. + /*! + \param pos iterator to the element to remove + \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() + \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator pos) { + return Erase(pos, pos + 1); + } + + //! Remove elements in the range [first, last) of the array. + /*! + \param first iterator to the first element to remove + \param last iterator following the last element to remove + \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() + \return Iterator following the last removed element. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(data_.a.size > 0); + RAPIDJSON_ASSERT(data_.a.elements != 0); + RAPIDJSON_ASSERT(first >= Begin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= End()); + ValueIterator pos = Begin() + (first - Begin()); + for (ValueIterator itr = pos; itr != last; ++itr) + itr->~GenericValue(); + std::memmove(pos, last, (End() - last) * sizeof(GenericValue)); + data_.a.size -= (last - first); + return pos; + } + + //@} + + //!@name Number + //@{ + + int GetInt() const { RAPIDJSON_ASSERT(flags_ & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(flags_ & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(flags_ & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(flags_ & kUint64Flag); return data_.n.u64; } + + double GetDouble() const { + RAPIDJSON_ASSERT(IsNumber()); + if ((flags_ & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((flags_ & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((flags_ & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((flags_ & kInt64Flag) != 0) return (double)data_.n.i64; // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((flags_ & kUint64Flag) != 0); return (double)data_.n.u64; // uint64_t -> double (may lose precision) + } + + GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } + GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; } + GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } + GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } + GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } + + //@} + + //!@name String + //@{ + + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? data_.ss.str : data_.s.str); } + + //! Get the length of string. + /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). + */ + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } + + //! Set this value as a string without copying source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string pointer. + \param length The length of source string, excluding the trailing null terminator. + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == length + \see SetString(StringRefType) + */ + GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } + + //! Set this value as a string without copying source string. + /*! \param s source string reference + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == s.length + */ + GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } + + //! Set this value as a string by copying from source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string. + \param length The length of source string, excluding the trailing null terminator. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; } + + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue& SetString(const std::basic_string<Ch>& s, Allocator& allocator) { return SetString(s.data(), s.size(), allocator); } +#endif + + //@} + + //! Generate events of this value to a Handler. + /*! This function adopts the GoF visitor pattern. + Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. + It can also be used to deep clone this value via GenericDocument, which is also a Handler. + \tparam Handler type of handler. + \param handler An object implementing concept Handler. + */ + template <typename Handler> + bool Accept(Handler& handler) const { + switch(GetType()) { + case kNullType: return handler.Null(); + case kFalseType: return handler.Bool(false); + case kTrueType: return handler.Bool(true); + + case kObjectType: + if (!handler.StartObject()) + return false; + for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { + RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. + if (!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.flags_ & kCopyFlag) != 0)) + return false; + if (!m->value.Accept(handler)) + return false; + } + return handler.EndObject(data_.o.size); + + case kArrayType: + if (!handler.StartArray()) + return false; + for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) + if (!v->Accept(handler)) + return false; + return handler.EndArray(data_.a.size); + + case kStringType: + return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0); + + case kNumberType: + if (IsInt()) return handler.Int(data_.n.i.i); + else if (IsUint()) return handler.Uint(data_.n.u.u); + else if (IsInt64()) return handler.Int64(data_.n.i64); + else if (IsUint64()) return handler.Uint64(data_.n.u64); + else return handler.Double(data_.n.d); + + default: + RAPIDJSON_ASSERT(false); + } + return false; + } + +private: + template <typename, typename> friend class GenericValue; + template <typename, typename, typename> friend class GenericDocument; + + enum { + kBoolFlag = 0x100, + kNumberFlag = 0x200, + kIntFlag = 0x400, + kUintFlag = 0x800, + kInt64Flag = 0x1000, + kUint64Flag = 0x2000, + kDoubleFlag = 0x4000, + kStringFlag = 0x100000, + kCopyFlag = 0x200000, + kInlineStrFlag = 0x400000, + + // Initial flags of different types. + kNullFlag = kNullType, + kTrueFlag = kTrueType | kBoolFlag, + kFalseFlag = kFalseType | kBoolFlag, + kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag, + kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag, + kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag, + kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag, + kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag, + kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag, + kConstStringFlag = kStringType | kStringFlag, + kCopyStringFlag = kStringType | kStringFlag | kCopyFlag, + kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag, + kObjectFlag = kObjectType, + kArrayFlag = kArrayType, + + kTypeMask = 0xFF // bitwise-and with mask of 0xFF can be optimized by compiler + }; + + static const SizeType kDefaultArrayCapacity = 16; + static const SizeType kDefaultObjectCapacity = 16; + + struct String { + const Ch* str; + SizeType length; + unsigned hashcode; //!< reserved + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars + // (excluding the terminating zero) and store a value to determine the length of the contained + // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string + // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as + // the string terminator as well. For getting the string length back from that value just use + // "MaxSize - str[LenPos]". + // This allows to store 11-chars strings in 32-bit mode and 15-chars strings in 64-bit mode + // inline (for `UTF8`-encoded strings). + struct ShortString { + enum { MaxChars = sizeof(String) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + Ch str[MaxChars]; + + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = (Ch)(MaxSize - len); } + inline SizeType GetLength() const { return (SizeType)(MaxSize - str[LenPos]); } + }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // By using proper binary layout, retrieval of different integer types do not need conversions. + union Number { +#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN + struct I { + int i; + char padding[4]; + }i; + struct U { + unsigned u; + char padding2[4]; + }u; +#else + struct I { + char padding[4]; + int i; + }i; + struct U { + char padding2[4]; + unsigned u; + }u; +#endif + int64_t i64; + uint64_t u64; + double d; + }; // 8 bytes + + struct Object { + Member* members; + SizeType size; + SizeType capacity; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + struct Array { + GenericValue* elements; + SizeType size; + SizeType capacity; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + union Data { + String s; + ShortString ss; + Number n; + Object o; + Array a; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // Initialize this value as array with initial data, without calling destructor. + void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { + flags_ = kArrayFlag; + data_.a.elements = (GenericValue*)allocator.Malloc(count * sizeof(GenericValue)); + std::memcpy(data_.a.elements, values, count * sizeof(GenericValue)); + data_.a.size = data_.a.capacity = count; + } + + //! Initialize this value as object with initial data, without calling destructor. + void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { + flags_ = kObjectFlag; + data_.o.members = (Member*)allocator.Malloc(count * sizeof(Member)); + std::memcpy(data_.o.members, members, count * sizeof(Member)); + data_.o.size = data_.o.capacity = count; + } + + //! Initialize this value as constant string, without calling destructor. + void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { + flags_ = kConstStringFlag; + data_.s.str = s; + data_.s.length = s.length; + } + + //! Initialize this value as copy string with initial data, without calling destructor. + void SetStringRaw(StringRefType s, Allocator& allocator) { + Ch* str = NULL; + if(ShortString::Usable(s.length)) { + flags_ = kShortStringFlag; + data_.ss.SetLength(s.length); + str = data_.ss.str; + } else { + flags_ = kCopyStringFlag; + data_.s.length = s.length; + str = (Ch *)allocator.Malloc((s.length + 1) * sizeof(Ch)); + data_.s.str = str; + } + std::memcpy(str, s, s.length * sizeof(Ch)); + str[s.length] = '\0'; + } + + //! Assignment without calling destructor + void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + data_ = rhs.data_; + flags_ = rhs.flags_; + rhs.flags_ = kNullFlag; + } + + template <typename SourceAllocator> + bool StringEqual(const GenericValue<Encoding, SourceAllocator>& rhs) const { + RAPIDJSON_ASSERT(IsString()); + RAPIDJSON_ASSERT(rhs.IsString()); + + const SizeType len1 = GetStringLength(); + const SizeType len2 = rhs.GetStringLength(); + if(len1 != len2) { return false; } + + const Ch* const str1 = GetString(); + const Ch* const str2 = rhs.GetString(); + if(str1 == str2) { return true; } // fast path for constant string + + return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0); + } + + Data data_; + unsigned flags_; +}; + +//! GenericValue with UTF8 encoding +typedef GenericValue<UTF8<> > Value; + +/////////////////////////////////////////////////////////////////////////////// +// GenericDocument + +//! A document for parsing JSON text as DOM. +/*! + \note implements Handler concept + \tparam Encoding Encoding for both parsing and string storage. + \tparam Allocator Allocator for allocating memory for the DOM + \tparam StackAllocator Allocator for allocating memory for stack during parsing. + \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue. +*/ +template <typename Encoding, typename Allocator = MemoryPoolAllocator<>, typename StackAllocator = CrtAllocator> +class GenericDocument : public GenericValue<Encoding, Allocator> { +public: + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericValue<Encoding, Allocator> ValueType; //!< Value type of the document. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + + //! Constructor + /*! \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + : ValueType(std::move(rhs)), + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(std::move(rhs.stack_)), + parseResult_(rhs.parseResult_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + } +#endif + + ~GenericDocument() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + { + // The cast to ValueType is necessary here, because otherwise it would + // attempt to call GenericValue's templated assignment operator. + ValueType::operator=(std::forward<ValueType>(rhs)); + + // Calling the destructor here would prematurely call stack_'s destructor + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = std::move(rhs.stack_); + parseResult_ = rhs.parseResult_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + + return *this; + } +#endif + + //!@name Parse from stream + //!@{ + + //! Parse JSON text from an input stream (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam SourceEncoding Encoding of input stream + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template <unsigned parseFlags, typename SourceEncoding, typename InputStream> + GenericDocument& ParseStream(InputStream& is) { + ValueType::SetNull(); // Remove existing root if exist + GenericReader<SourceEncoding, Encoding, Allocator> reader(&GetAllocator()); + ClearStackOnExit scope(*this); + parseResult_ = reader.template Parse<parseFlags>(is, *this); + if (parseResult_) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + this->RawAssign(*stack_.template Pop<ValueType>(1)); // Add this-> to prevent issue 13. + } + return *this; + } + + //! Parse JSON text from an input stream + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template <unsigned parseFlags, typename InputStream> + GenericDocument& ParseStream(InputStream& is) { + return ParseStream<parseFlags,Encoding,InputStream>(is); + } + + //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template <typename InputStream> + GenericDocument& ParseStream(InputStream& is) { + return ParseStream<kParseDefaultFlags, Encoding, InputStream>(is); + } + //!@} + + //!@name Parse in-place from mutable string + //!@{ + + //! Parse JSON text from a mutable string (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam SourceEncoding Transcoding from input Encoding + \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + template <unsigned parseFlags, typename SourceEncoding> + GenericDocument& ParseInsitu(Ch* str) { + GenericInsituStringStream<Encoding> s(str); + return ParseStream<parseFlags | kParseInsituFlag, SourceEncoding>(s); + } + + //! Parse JSON text from a mutable string + /*! \tparam parseFlags Combination of \ref ParseFlag. + \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + template <unsigned parseFlags> + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu<parseFlags, Encoding>(str); + } + + //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags) + /*! \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu<kParseDefaultFlags, Encoding>(str); + } + //!@} + + //!@name Parse from read-only string + //!@{ + + //! Parse JSON text from a read-only string (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \tparam SourceEncoding Transcoding from input Encoding + \param str Read-only zero-terminated string to be parsed. + */ + template <unsigned parseFlags, typename SourceEncoding> + GenericDocument& Parse(const Ch* str) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + GenericStringStream<SourceEncoding> s(str); + return ParseStream<parseFlags, SourceEncoding>(s); + } + + //! Parse JSON text from a read-only string + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \param str Read-only zero-terminated string to be parsed. + */ + template <unsigned parseFlags> + GenericDocument& Parse(const Ch* str) { + return Parse<parseFlags, Encoding>(str); + } + + //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags) + /*! \param str Read-only zero-terminated string to be parsed. + */ + GenericDocument& Parse(const Ch* str) { + return Parse<kParseDefaultFlags>(str); + } + //!@} + + //!@name Handling parse errors + //!@{ + + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseError() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + + //!@} + + //! Get the allocator of this document. + Allocator& GetAllocator() { return *allocator_; } + + //! Get the capacity of stack in bytes. + size_t GetStackCapacity() const { return stack_.GetCapacity(); } + +private: + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} + ~ClearStackOnExit() { d_.ClearStack(); } + private: + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + GenericDocument& d_; + }; + + // callers of the following private Handler functions + template <typename,typename,typename> friend class GenericReader; // for parsing + template <typename, typename> friend class GenericValue; // for deep copying + + // Implementation of Handler + bool Null() { new (stack_.template Push<ValueType>()) ValueType(); return true; } + bool Bool(bool b) { new (stack_.template Push<ValueType>()) ValueType(b); return true; } + bool Int(int i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; } + bool Uint(unsigned i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; } + bool Int64(int64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; } + bool Uint64(uint64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; } + bool Double(double d) { new (stack_.template Push<ValueType>()) ValueType(d); return true; } + + bool String(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push<ValueType>()) ValueType(str, length); + return true; + } + + bool StartObject() { new (stack_.template Push<ValueType>()) ValueType(kObjectType); return true; } + + bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount) { + typename ValueType::Member* members = stack_.template Pop<typename ValueType::Member>(memberCount); + stack_.template Top<ValueType>()->SetObjectRaw(members, (SizeType)memberCount, GetAllocator()); + return true; + } + + bool StartArray() { new (stack_.template Push<ValueType>()) ValueType(kArrayType); return true; } + + bool EndArray(SizeType elementCount) { + ValueType* elements = stack_.template Pop<ValueType>(elementCount); + stack_.template Top<ValueType>()->SetArrayRaw(elements, elementCount, GetAllocator()); + return true; + } + +private: + //! Prohibit copying + GenericDocument(const GenericDocument&); + //! Prohibit assignment + GenericDocument& operator=(const GenericDocument&); + + void ClearStack() { + if (Allocator::kNeedFree) + while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects) + (stack_.template Pop<ValueType>(1))->~ValueType(); + else + stack_.Clear(); + stack_.ShrinkToFit(); + } + + void Destroy() { + RAPIDJSON_DELETE(ownAllocator_); + } + + static const size_t kDefaultStackCapacity = 1024; + Allocator* allocator_; + Allocator* ownAllocator_; + internal::Stack<StackAllocator> stack_; + ParseResult parseResult_; +}; + +//! GenericDocument with UTF8 encoding +typedef GenericDocument<UTF8<> > Document; + +// defined here due to the dependency on GenericDocument +template <typename Encoding, typename Allocator> +template <typename SourceAllocator> +inline +GenericValue<Encoding,Allocator>::GenericValue(const GenericValue<Encoding,SourceAllocator>& rhs, Allocator& allocator) +{ + switch (rhs.GetType()) { + case kObjectType: + case kArrayType: { // perform deep copy via SAX Handler + GenericDocument<Encoding,Allocator> d(&allocator); + rhs.Accept(d); + RawAssign(*d.stack_.template Pop<GenericValue>(1)); + } + break; + case kStringType: + if (rhs.flags_ == kConstStringFlag) { + flags_ = rhs.flags_; + data_ = *reinterpret_cast<const Data*>(&rhs.data_); + } else { + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + } + break; + default: // kNumberType, kTrueType, kFalseType, kNullType + flags_ = rhs.flags_; + data_ = *reinterpret_cast<const Data*>(&rhs.data_); + } +} + +RAPIDJSON_NAMESPACE_END + +#if defined(_MSC_VER) || defined(__GNUC__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_DOCUMENT_H_ diff --git a/ipaacalib/cpp/include/rapidjson/encodedstream.h b/ipaacalib/cpp/include/rapidjson/encodedstream.h new file mode 100644 index 0000000000000000000000000000000000000000..c310d2e58bf8db37d58fdc1a3283c8e16cce6d3a --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/encodedstream.h @@ -0,0 +1,290 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_ENCODEDSTREAM_H_ +#define RAPIDJSON_ENCODEDSTREAM_H_ + +#include "rapidjson.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Input byte stream wrapper with a statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam InputByteStream Type of input byte stream. For example, FileReadStream. +*/ +template <typename Encoding, typename InputByteStream> +class EncodedInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedInputStream(InputByteStream& is) : is_(is) { + current_ = Encoding::TakeBOM(is_); + } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); + + InputByteStream& is_; + Ch current_; +}; + +//! Output byte stream wrapper with statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam InputByteStream Type of input byte stream. For example, FileWriteStream. +*/ +template <typename Encoding, typename OutputByteStream> +class EncodedOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { + if (putBOM) + Encoding::PutBOM(os_); + } + + void Put(Ch c) { Encoding::Put(os_, c); } + void Flush() { os_.Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); } + Ch Take() { RAPIDJSON_ASSERT(false); } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedOutputStream(const EncodedOutputStream&); + EncodedOutputStream& operator=(const EncodedOutputStream&); + + OutputByteStream& os_; +}; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x + +//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for reading. + \tparam InputByteStream type of input byte stream to be wrapped. +*/ +template <typename CharType, typename InputByteStream> +class AutoUTFInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param is input stream to be wrapped. + \param type UTF encoding type if it is not detected from the stream. + */ + AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { + DetectType(); + static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; + takeFunc_ = f[type_]; + current_ = takeFunc_(*is_); + } + + UTFType GetType() const { return type_; } + bool HasBOM() const { return hasBOM_; } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } + size_t Tell() const { return is_->Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFInputStream(const AutoUTFInputStream&); + AutoUTFInputStream& operator=(const AutoUTFInputStream&); + + // Detect encoding type with BOM or RFC 4627 + void DetectType() { + // BOM (Byte Order Mark): + // 00 00 FE FF UTF-32BE + // FF FE 00 00 UTF-32LE + // FE FF UTF-16BE + // FF FE UTF-16LE + // EF BB BF UTF-8 + + const unsigned char* c = (const unsigned char *)is_->Peek4(); + if (!c) + return; + + unsigned bom = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24); + hasBOM_ = false; + if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } + + // RFC 4627: Section 3 + // "Since the first two characters of a JSON text will always be ASCII + // characters [RFC0020], it is possible to determine whether an octet + // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking + // at the pattern of nulls in the first four octets." + // 00 00 00 xx UTF-32BE + // 00 xx 00 xx UTF-16BE + // xx 00 00 00 UTF-32LE + // xx 00 xx 00 UTF-16LE + // xx xx xx xx UTF-8 + + if (!hasBOM_) { + unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + switch (pattern) { + case 0x08: type_ = kUTF32BE; break; + case 0x0A: type_ = kUTF16BE; break; + case 0x01: type_ = kUTF32LE; break; + case 0x05: type_ = kUTF16LE; break; + case 0x0F: type_ = kUTF8; break; + default: break; // Use type defined by user. + } + } + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + switch (type_) { + case kUTF8: + // Do nothing + break; + case kUTF16LE: + case kUTF16BE: + RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + break; + case kUTF32LE: + case kUTF32BE: + RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + break; + default: + RAPIDJSON_ASSERT(false); // Invalid type + } + } + + typedef Ch (*TakeFunc)(InputByteStream& is); + InputByteStream* is_; + UTFType type_; + Ch current_; + TakeFunc takeFunc_; + bool hasBOM_; +}; + +//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for writing. + \tparam InputByteStream type of output byte stream to be wrapped. +*/ +template <typename CharType, typename OutputByteStream> +class AutoUTFOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param os output stream to be wrapped. + \param type UTF encoding type. + \param putBOM Whether to write BOM at the beginning of the stream. + */ + AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { + // RUntime check whether the size of character type is sufficient. It only perform checks with assertion. + switch (type_) { + case kUTF16LE: + case kUTF16BE: + RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + break; + case kUTF32LE: + case kUTF32BE: + RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + break; + case kUTF8: + // Do nothing + break; + default: + RAPIDJSON_ASSERT(false); // Invalid UTFType + } + + static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; + putFunc_ = f[type_]; + + if (putBOM) + PutBOM(); + } + + UTFType GetType() const { return type_; } + + void Put(Ch c) { putFunc_(*os_, c); } + void Flush() { os_->Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); } + Ch Take() { RAPIDJSON_ASSERT(false); } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFOutputStream(const AutoUTFOutputStream&); + AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); + + void PutBOM() { + typedef void (*PutBOMFunc)(OutputByteStream&); + static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; + f[type_](*os_); + } + + typedef void (*PutFunc)(OutputByteStream&, Ch); + + OutputByteStream* os_; + UTFType type_; + PutFunc putFunc_; +}; + +#undef RAPIDJSON_ENCODINGS_FUNC + +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/ipaacalib/cpp/include/rapidjson/encodings.h b/ipaacalib/cpp/include/rapidjson/encodings.h new file mode 100644 index 0000000000000000000000000000000000000000..6bc8aec5d5f1ca1a383d09c3c3ae7ee040427ef3 --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/encodings.h @@ -0,0 +1,630 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_ENCODINGS_H_ +#define RAPIDJSON_ENCODINGS_H_ + +#include "rapidjson.h" + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#elif defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Encoding + +/*! \class rapidjson::Encoding + \brief Concept for encoding of Unicode characters. + +\code +concept Encoding { + typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. + + enum { supportUnicode = 1 }; // or 0 if not supporting unicode + + //! \brief Encode a Unicode codepoint to an output stream. + //! \param os Output stream. + //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. + template<typename OutputStream> + static void Encode(OutputStream& os, unsigned codepoint); + + //! \brief Decode a Unicode codepoint from an input stream. + //! \param is Input stream. + //! \param codepoint Output of the unicode codepoint. + //! \return true if a valid codepoint can be decoded from the stream. + template <typename InputStream> + static bool Decode(InputStream& is, unsigned* codepoint); + + //! \brief Validate one Unicode codepoint from an encoded stream. + //! \param is Input stream to obtain codepoint. + //! \param os Output for copying one codepoint. + //! \return true if it is valid. + //! \note This function just validating and copying the codepoint without actually decode it. + template <typename InputStream, typename OutputStream> + static bool Validate(InputStream& is, OutputStream& os); + + // The following functions are deal with byte streams. + + //! Take a character from input byte stream, skip BOM if exist. + template <typename InputByteStream> + static CharType TakeBOM(InputByteStream& is); + + //! Take a character from input byte stream. + template <typename InputByteStream> + static Ch Take(InputByteStream& is); + + //! Put BOM to output byte stream. + template <typename OutputByteStream> + static void PutBOM(OutputByteStream& os); + + //! Put a character to output byte stream. + template <typename OutputByteStream> + static void Put(OutputByteStream& os, Ch c); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// UTF8 + +//! UTF-8 encoding. +/*! http://en.wikipedia.org/wiki/UTF-8 + http://tools.ietf.org/html/rfc3629 + \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. + \note implements Encoding concept +*/ +template<typename CharType = char> +struct UTF8 { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + + template<typename OutputStream> + static void Encode(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + os.Put(static_cast<Ch>(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + os.Put(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF))); + os.Put(static_cast<Ch>(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + os.Put(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF))); + os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF))); + os.Put(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F))); + os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F))); + } + } + + template <typename InputStream> + static bool Decode(InputStream& is, unsigned* codepoint) { +#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | ((unsigned char)c & 0x3Fu) +#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0) +#define TAIL() COPY(); TRANS(0x70) + Ch c = is.Take(); + if (!(c & 0x80)) { + *codepoint = (unsigned char)c; + return true; + } + + unsigned char type = GetRange((unsigned char)c); + *codepoint = (0xFF >> type) & (unsigned char)c; + bool result = true; + switch (type) { + case 2: TAIL(); return result; + case 3: TAIL(); TAIL(); return result; + case 4: COPY(); TRANS(0x50); TAIL(); return result; + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; + case 6: TAIL(); TAIL(); TAIL(); return result; + case 10: COPY(); TRANS(0x20); TAIL(); return result; + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + default: return false; + } +#undef COPY +#undef TRANS +#undef TAIL + } + + template <typename InputStream, typename OutputStream> + static bool Validate(InputStream& is, OutputStream& os) { +#define COPY() os.Put(c = is.Take()) +#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0) +#define TAIL() COPY(); TRANS(0x70) + Ch c; + COPY(); + if (!(c & 0x80)) + return true; + + bool result = true; + switch (GetRange((unsigned char)c)) { + case 2: TAIL(); return result; + case 3: TAIL(); TAIL(); return result; + case 4: COPY(); TRANS(0x50); TAIL(); return result; + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; + case 6: TAIL(); TAIL(); TAIL(); return result; + case 10: COPY(); TRANS(0x20); TAIL(); return result; + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + default: return false; + } +#undef COPY +#undef TRANS +#undef TAIL + } + + static unsigned char GetRange(unsigned char c) { + // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. + static const unsigned char type[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + }; + return type[c]; + } + + template <typename InputByteStream> + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + Ch c = Take(is); + if ((unsigned char)c != 0xEFu) return c; + c = is.Take(); + if ((unsigned char)c != 0xBBu) return c; + c = is.Take(); + if ((unsigned char)c != 0xBFu) return c; + c = is.Take(); + return c; + } + + template <typename InputByteStream> + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return is.Take(); + } + + template <typename OutputByteStream> + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0xEFu); os.Put(0xBBu); os.Put(0xBFu); + } + + template <typename OutputByteStream> + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast<typename OutputByteStream::Ch>(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF16 + +//! UTF-16 encoding. +/*! http://en.wikipedia.org/wiki/UTF-16 + http://tools.ietf.org/html/rfc2781 + \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF16LE and UTF16BE, which handle endianness. +*/ +template<typename CharType = wchar_t> +struct UTF16 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); + + enum { supportUnicode = 1 }; + + template<typename OutputStream> + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + os.Put(static_cast<typename OutputStream::Ch>(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + os.Put(static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800)); + os.Put((v & 0x3FF) | 0xDC00); + } + } + + template <typename InputStream> + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + Ch c = is.Take(); + if (c < 0xD800 || c > 0xDFFF) { + *codepoint = c; + return true; + } + else if (c <= 0xDBFF) { + *codepoint = (c & 0x3FF) << 10; + c = is.Take(); + *codepoint |= (c & 0x3FF); + *codepoint += 0x10000; + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } + + template <typename InputStream, typename OutputStream> + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + Ch c; + os.Put(c = is.Take()); + if (c < 0xD800 || c > 0xDFFF) + return true; + else if (c <= 0xDBFF) { + os.Put(c = is.Take()); + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } +}; + +//! UTF-16 little endian encoding. +template<typename CharType = wchar_t> +struct UTF16LE : UTF16<CharType> { + template <typename InputByteStream> + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return (unsigned short)c == 0xFEFFu ? Take(is) : c; + } + + template <typename InputByteStream> + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = (unsigned char)is.Take(); + c |= (unsigned char)is.Take() << 8; + return c; + } + + template <typename OutputByteStream> + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0xFFu); os.Put(0xFEu); + } + + template <typename OutputByteStream> + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(c & 0xFFu); + os.Put((c >> 8) & 0xFFu); + } +}; + +//! UTF-16 big endian encoding. +template<typename CharType = wchar_t> +struct UTF16BE : UTF16<CharType> { + template <typename InputByteStream> + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return (unsigned short)c == 0xFEFFu ? Take(is) : c; + } + + template <typename InputByteStream> + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = (unsigned char)is.Take() << 8; + c |= (unsigned char)is.Take(); + return c; + } + + template <typename OutputByteStream> + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0xFEu); os.Put(0xFFu); + } + + template <typename OutputByteStream> + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put((c >> 8) & 0xFFu); + os.Put(c & 0xFFu); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF32 + +//! UTF-32 encoding. +/*! http://en.wikipedia.org/wiki/UTF-32 + \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF32LE and UTF32BE, which handle endianness. +*/ +template<typename CharType = unsigned> +struct UTF32 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); + + enum { supportUnicode = 1 }; + + template<typename OutputStream> + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(codepoint); + } + + template <typename InputStream> + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c = is.Take(); + *codepoint = c; + return c <= 0x10FFFF; + } + + template <typename InputStream, typename OutputStream> + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c; + os.Put(c = is.Take()); + return c <= 0x10FFFF; + } +}; + +//! UTF-32 little endian enocoding. +template<typename CharType = unsigned> +struct UTF32LE : UTF32<CharType> { + template <typename InputByteStream> + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return (unsigned)c == 0x0000FEFFu ? Take(is) : c; + } + + template <typename InputByteStream> + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = (unsigned char)is.Take(); + c |= (unsigned char)is.Take() << 8; + c |= (unsigned char)is.Take() << 16; + c |= (unsigned char)is.Take() << 24; + return c; + } + + template <typename OutputByteStream> + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0xFFu); os.Put(0xFEu); os.Put(0x00u); os.Put(0x00u); + } + + template <typename OutputByteStream> + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(c & 0xFFu); + os.Put((c >> 8) & 0xFFu); + os.Put((c >> 16) & 0xFFu); + os.Put((c >> 24) & 0xFFu); + } +}; + +//! UTF-32 big endian encoding. +template<typename CharType = unsigned> +struct UTF32BE : UTF32<CharType> { + template <typename InputByteStream> + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return (unsigned)c == 0x0000FEFFu ? Take(is) : c; + } + + template <typename InputByteStream> + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = (unsigned char)is.Take() << 24; + c |= (unsigned char)is.Take() << 16; + c |= (unsigned char)is.Take() << 8; + c |= (unsigned char)is.Take(); + return c; + } + + template <typename OutputByteStream> + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(0x00u); os.Put(0x00u); os.Put(0xFEu); os.Put(0xFFu); + } + + template <typename OutputByteStream> + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put((c >> 24) & 0xFFu); + os.Put((c >> 16) & 0xFFu); + os.Put((c >> 8) & 0xFFu); + os.Put(c & 0xFFu); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// ASCII + +//! ASCII encoding. +/*! http://en.wikipedia.org/wiki/ASCII + \tparam CharType Code unit for storing 7-bit ASCII data. Default is char. + \note implements Encoding concept +*/ +template<typename CharType = char> +struct ASCII { + typedef CharType Ch; + + enum { supportUnicode = 0 }; + + template<typename OutputStream> + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + os.Put(static_cast<Ch>(codepoint & 0xFF)); + } + + template <typename InputStream> + static bool Decode(InputStream& is, unsigned* codepoint) { + unsigned char c = static_cast<unsigned char>(is.Take()); + *codepoint = c; + return c <= 0X7F; + } + + template <typename InputStream, typename OutputStream> + static bool Validate(InputStream& is, OutputStream& os) { + unsigned char c = is.Take(); + os.Put(c); + return c <= 0x7F; + } + + template <typename InputByteStream> + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + Ch c = Take(is); + return c; + } + + template <typename InputByteStream> + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return is.Take(); + } + + template <typename OutputByteStream> + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + (void)os; + } + + template <typename OutputByteStream> + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast<typename OutputByteStream::Ch>(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// AutoUTF + +//! Runtime-specified UTF encoding type of a stream. +enum UTFType { + kUTF8 = 0, //!< UTF-8. + kUTF16LE = 1, //!< UTF-16 little endian. + kUTF16BE = 2, //!< UTF-16 big endian. + kUTF32LE = 3, //!< UTF-32 little endian. + kUTF32BE = 4 //!< UTF-32 big endian. +}; + +//! Dynamically select encoding according to stream's runtime-specified UTF encoding type. +/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). +*/ +template<typename CharType> +struct AutoUTF { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x + + template<typename OutputStream> + RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; + (*f[os.GetType()])(os, codepoint); + } + + template <typename InputStream> + RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { + typedef bool (*DecodeFunc)(InputStream&, unsigned*); + static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; + return (*f[is.GetType()])(is, codepoint); + } + + template <typename InputStream, typename OutputStream> + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + typedef bool (*ValidateFunc)(InputStream&, OutputStream&); + static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; + return (*f[is.GetType()])(is, os); + } + +#undef RAPIDJSON_ENCODINGS_FUNC +}; + +/////////////////////////////////////////////////////////////////////////////// +// Transcoder + +//! Encoding conversion. +template<typename SourceEncoding, typename TargetEncoding> +struct Transcoder { + //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. + template<typename InputStream, typename OutputStream> + RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::Encode(os, codepoint); + return true; + } + + //! Validate one Unicode codepoint from an encoded stream. + template<typename InputStream, typename OutputStream> + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + return Transcode(is, os); // Since source/target encoding is different, must transcode. + } +}; + +//! Specialization of Transcoder with same source and target encoding. +template<typename Encoding> +struct Transcoder<Encoding, Encoding> { + template<typename InputStream, typename OutputStream> + RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template<typename InputStream, typename OutputStream> + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + return Encoding::Validate(is, os); // source/target encoding are the same + } +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || defined(_MSV_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/ipaacalib/cpp/include/rapidjson/error/en.h b/ipaacalib/cpp/include/rapidjson/error/en.h new file mode 100644 index 0000000000000000000000000000000000000000..01711833e1ef1ebe1392a3a56392eeab44918e18 --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/error/en.h @@ -0,0 +1,71 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_ERROR_EN_H__ +#define RAPIDJSON_ERROR_EN_H__ + +#include "error.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Maps error code of parsing into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param parseErrorCode Error code obtained in parsing. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { + switch (parseErrorCode) { + case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); + case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not follow by other values."); + + case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); + + case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); + case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); + case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); + + case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); + + case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); + case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); + case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); + case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); + case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); + + case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); + case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); + case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); + + case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); + case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); + + default: + return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ERROR_EN_H__ diff --git a/ipaacalib/cpp/include/rapidjson/error/error.h b/ipaacalib/cpp/include/rapidjson/error/error.h new file mode 100644 index 0000000000000000000000000000000000000000..161ef87878ef34a6613c76103e7a5ce44928d25a --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/error/error.h @@ -0,0 +1,150 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_ERROR_ERROR_H__ +#define RAPIDJSON_ERROR_ERROR_H__ + +/*! \file error.h */ + +/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_CHARTYPE + +//! Character type of error messages. +/*! \ingroup RAPIDJSON_ERRORS + The default character type is \c char. + On Windows, user can define this macro as \c TCHAR for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_CHARTYPE +#define RAPIDJSON_ERROR_CHARTYPE char +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_STRING + +//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[]. +/*! \ingroup RAPIDJSON_ERRORS + By default this conversion macro does nothing. + On Windows, user can define this macro as \c _T(x) for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_STRING +#define RAPIDJSON_ERROR_STRING(x) x +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseErrorCode + +//! Error code of parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericReader::Parse, GenericReader::GetParseErrorCode +*/ +enum ParseErrorCode { + kParseErrorNone = 0, //!< No error. + + kParseErrorDocumentEmpty, //!< The document is empty. + kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. + + kParseErrorValueInvalid, //!< Invalid value. + + kParseErrorObjectMissName, //!< Missing a name for object member. + kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. + kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. + + kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. + + kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. + kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. + kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. + kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. + kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. + + kParseErrorNumberTooBig, //!< Number too big to be stored in double. + kParseErrorNumberMissFraction, //!< Miss fraction part in number. + kParseErrorNumberMissExponent, //!< Miss exponent in number. + + kParseErrorTermination, //!< Parsing was terminated. + kParseErrorUnspecificSyntaxError, //!< Unspecific syntax error. +}; + +//! Result of parsing (wraps ParseErrorCode) +/*! + \ingroup RAPIDJSON_ERRORS + \code + Document doc; + ParseResult ok = doc.Parse("[42]"); + if (!ok) { + fprintf(stderr, "JSON parse error: %s (%u)", + GetParseError_En(ok.Code()), ok.Offset()); + exit(EXIT_FAILURE); + } + \endcode + \see GenericReader::Parse, GenericDocument::Parse +*/ +struct ParseResult { + + //! Default constructor, no error. + ParseResult() : code_(kParseErrorNone), offset_(0) {} + //! Constructor to set an error. + ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} + + //! Get the error code. + ParseErrorCode Code() const { return code_; } + //! Get the error offset, if \ref IsError(), 0 otherwise. + size_t Offset() const { return offset_; } + + //! Conversion to \c bool, returns \c true, iff !\ref IsError(). + operator bool() const { return !IsError(); } + //! Whether the result is an error. + bool IsError() const { return code_ != kParseErrorNone; } + + bool operator==(const ParseResult& that) const { return code_ == that.code_; } + bool operator==(ParseErrorCode code) const { return code_ == code; } + friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + + //! Reset error code. + void Clear() { Set(kParseErrorNone); } + //! Update error code and offset. + void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } + +private: + ParseErrorCode code_; + size_t offset_; +}; + +//! Function pointer type of GetParseError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetParseError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetParseErrorFunc GetParseError = GetParseError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ERROR_ERROR_H__ diff --git a/ipaacalib/cpp/include/rapidjson/filereadstream.h b/ipaacalib/cpp/include/rapidjson/filereadstream.h new file mode 100644 index 0000000000000000000000000000000000000000..4ff4ab9c526ec24699a06bb013b1732f2d120f26 --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/filereadstream.h @@ -0,0 +1,94 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_FILEREADSTREAM_H_ +#define RAPIDJSON_FILEREADSTREAM_H_ + +#include "rapidjson.h" +#include <cstdio> + +RAPIDJSON_NAMESPACE_BEGIN + +//! File byte stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileReadStream { +public: + typedef char Ch; //!< Character type (byte). + + //! Constructor. + /*! + \param fp File pointer opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(fp_ != 0); + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); + } + + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast<size_t>(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 <= bufferLast_) ? current_ : 0; + } + +private: + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = fread(buffer_, 1, bufferSize_, fp_); + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (readCount_ < bufferSize_) { + buffer_[readCount_] = '\0'; + ++bufferLast_; + eof_ = true; + } + } + } + + std::FILE* fp_; + Ch *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/ipaacalib/cpp/include/rapidjson/filestream.h b/ipaacalib/cpp/include/rapidjson/filestream.h new file mode 100644 index 0000000000000000000000000000000000000000..a2e7172299f8b9bbf3ea6aa9819a8e2c4d4914a4 --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/filestream.h @@ -0,0 +1,73 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_FILESTREAM_H_ +#define RAPIDJSON_FILESTREAM_H_ + +#include "rapidjson.h" +#include <cstdio> + +RAPIDJSON_NAMESPACE_BEGIN + +//! (Deprecated) Wrapper of C file stream for input or output. +/*! + This simple wrapper does not check the validity of the stream. + \note implements Stream concept + \note deprecated: This was only for basic testing in version 0.1, it is found that the performance is very low by using fgetc(). Use FileReadStream instead. +*/ +class FileStream { +public: + typedef char Ch; //!< Character type. Only support char. + + FileStream(std::FILE* fp) : fp_(fp), current_('\0'), count_(0) { Read(); } + char Peek() const { return current_; } + char Take() { char c = current_; Read(); return c; } + size_t Tell() const { return count_; } + void Put(char c) { fputc(c, fp_); } + void Flush() { fflush(fp_); } + + // Not implemented + char* PutBegin() { return 0; } + size_t PutEnd(char*) { return 0; } + +private: + // Prohibit copy constructor & assignment operator. + FileStream(const FileStream&); + FileStream& operator=(const FileStream&); + + void Read() { + RAPIDJSON_ASSERT(fp_ != 0); + int c = fgetc(fp_); + if (c != EOF) { + current_ = (char)c; + count_++; + } + else if (current_ != '\0') + current_ = '\0'; + } + + std::FILE* fp_; + char current_; + size_t count_; +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/ipaacalib/cpp/include/rapidjson/filewritestream.h b/ipaacalib/cpp/include/rapidjson/filewritestream.h new file mode 100644 index 0000000000000000000000000000000000000000..2f4977d620e346489fa615efbf46d7492d5be0dc --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/filewritestream.h @@ -0,0 +1,97 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_FILEWRITESTREAM_H_ +#define RAPIDJSON_FILEWRITESTREAM_H_ + +#include "rapidjson.h" +#include <cstdio> + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of C file stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileWriteStream { +public: + typedef char Ch; //!< Character type. Only support char. + + FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { + RAPIDJSON_ASSERT(fp_ != 0); + } + + void Put(char c) { + if (current_ >= bufferEnd_) + Flush(); + + *current_++ = c; + } + + void PutN(char c, size_t n) { + size_t avail = static_cast<size_t>(bufferEnd_ - current_); + while (n > avail) { + std::memset(current_, c, avail); + current_ += avail; + Flush(); + n -= avail; + avail = static_cast<size_t>(bufferEnd_ - current_); + } + + if (n > 0) { + std::memset(current_, c, n); + current_ += n; + } + } + + void Flush() { + if (current_ != buffer_) { + fwrite(buffer_, 1, static_cast<size_t>(current_ - buffer_), fp_); + current_ = buffer_; + } + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + // Prohibit copy constructor & assignment operator. + FileWriteStream(const FileWriteStream&); + FileWriteStream& operator=(const FileWriteStream&); + + std::FILE* fp_; + char *buffer_; + char *bufferEnd_; + char *current_; +}; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(FileWriteStream& stream, char c, size_t n) { + stream.PutN(c, n); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/ipaacalib/cpp/include/rapidjson/internal/biginteger.h b/ipaacalib/cpp/include/rapidjson/internal/biginteger.h new file mode 100755 index 0000000000000000000000000000000000000000..72acfc385334cd637646ccda6ac9f1954b25622b --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/internal/biginteger.h @@ -0,0 +1,294 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_BIGINTEGER_H_ +#define RAPIDJSON_BIGINTEGER_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && defined(_M_AMD64) +#include <intrin.h> // for _umul128 +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class BigInteger { +public: + typedef uint64_t Type; + + BigInteger(const BigInteger& rhs) : count_(rhs.count_) { + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + + explicit BigInteger(uint64_t u) : count_(1) { + digits_[0] = u; + } + + BigInteger(const char* decimals, size_t length) : count_(1) { + RAPIDJSON_ASSERT(length > 0); + digits_[0] = 0; + size_t i = 0; + const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19 + while (length >= kMaxDigitPerIteration) { + AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration); + length -= kMaxDigitPerIteration; + i += kMaxDigitPerIteration; + } + + if (length > 0) + AppendDecimal64(decimals + i, decimals + i + length); + } + + BigInteger& operator=(uint64_t u) { + digits_[0] = u; + count_ = 1; + return *this; + } + + BigInteger& operator+=(uint64_t u) { + Type backup = digits_[0]; + digits_[0] += u; + for (size_t i = 0; i < count_ - 1; i++) { + if (digits_[i] >= backup) + return *this; // no carry + backup = digits_[i + 1]; + digits_[i + 1] += 1; + } + + // Last carry + if (digits_[count_ - 1] < backup) + PushBack(1); + + return *this; + } + + BigInteger& operator*=(uint64_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + uint64_t hi; + digits_[i] = MulAdd64(digits_[i], u, k, &hi); + k = hi; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator*=(uint32_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint32_t k = 0; + for (size_t i = 0; i < count_; i++) { + const uint64_t c = digits_[i] >> 32; + const uint64_t d = digits_[i] & 0xFFFFFFFF; + const uint64_t uc = u * c; + const uint64_t ud = u * d; + const uint64_t p0 = ud + k; + const uint64_t p1 = uc + (p0 >> 32); + digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32); + k = p1 >> 32; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator<<=(size_t shift) { + if (IsZero() || shift == 0) return *this; + + size_t offset = shift / kTypeBit; + size_t interShift = shift % kTypeBit; + RAPIDJSON_ASSERT(count_ + offset <= kCapacity); + + if (interShift == 0) { + std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type)); + count_ += offset; + } + else { + digits_[count_] = 0; + for (size_t i = count_; i > 0; i--) + digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift)); + digits_[offset] = digits_[0] << interShift; + count_ += offset; + if (digits_[count_]) + count_++; + } + + std::memset(digits_, 0, offset * sizeof(Type)); + + return *this; + } + + bool operator==(const BigInteger& rhs) const { + return count_ == rhs.count_ && memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0; + } + + bool operator==(const Type rhs) const { + return count_ == 1 && digits_[0] == rhs; + } + + BigInteger& MultiplyPow5(unsigned exp) { + static const uint32_t kPow5[12] = { + 5, + 5 * 5, + 5 * 5 * 5, + 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 + }; + if (exp == 0) return *this; + for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 + for (; exp >= 13; exp -= 13) *this *= 1220703125u; // 5^13 + if (exp > 0) *this *= kPow5[exp - 1]; + return *this; + } + + // Compute absolute difference of this and rhs. + // Return false if this < rhs + bool Difference(const BigInteger& rhs, BigInteger* out) const { + int cmp = Compare(rhs); + if (cmp == 0) { + *out = BigInteger(0); + return false; + } + const BigInteger *a, *b; // Makes a > b + bool ret; + if (cmp < 0) { a = &rhs; b = this; ret = true; } + else { a = this; b = &rhs; ret = false; } + + Type borrow = 0; + for (size_t i = 0; i < a->count_; i++) { + Type d = a->digits_[i] - borrow; + if (i < b->count_) + d -= b->digits_[i]; + borrow = (d > a->digits_[i]) ? 1 : 0; + out->digits_[i] = d; + if (d != 0) + out->count_ = i + 1; + } + + return ret; + } + + int Compare(const BigInteger& rhs) const { + if (count_ != rhs.count_) + return count_ < rhs.count_ ? -1 : 1; + + for (size_t i = count_; i-- > 0;) + if (digits_[i] != rhs.digits_[i]) + return digits_[i] < rhs.digits_[i] ? -1 : 1; + + return 0; + } + + size_t GetCount() const { return count_; } + Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; } + bool IsZero() const { return count_ == 1 && digits_[0] == 0; } + +private: + void AppendDecimal64(const char* begin, const char* end) { + uint64_t u = ParseUint64(begin, end); + if (IsZero()) + *this = u; + else { + unsigned exp = static_cast<unsigned>(end - begin); + (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u + } + } + + void PushBack(Type digit) { + RAPIDJSON_ASSERT(count_ < kCapacity); + digits_[count_++] = digit; + } + + static uint64_t ParseUint64(const char* begin, const char* end) { + uint64_t r = 0; + for (const char* p = begin; p != end; ++p) { + RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); + r = r * 10 + (*p - '0'); + } + return r; + } + + // Assume a * b + k < 2^128 + static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t low = _umul128(a, b, outHigh) + k; + if (low < k) + (*outHigh)++; + return low; +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + unsigned __int128 p = static_cast<unsigned __int128>(a) * static_cast<unsigned __int128>(b); + p += k; + *outHigh = p >> 64; + return static_cast<uint64_t>(p); +#else + const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; + uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1; + x1 += (x0 >> 32); // can't give carry + x1 += x2; + if (x1 < x2) + x3 += (static_cast<uint64_t>(1) << 32); + uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF); + uint64_t hi = x3 + (x1 >> 32); + + lo += k; + if (lo < k) + hi++; + *outHigh = hi; + return lo; +#endif + } + + static Type FullAdd(Type a, Type b, bool inCarry, bool* outCarry) { + Type c = a + b + (inCarry ? 1 : 0); + *outCarry = c < a; + return c; + } + + static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 + static const size_t kCapacity = kBitCount / sizeof(Type); + static const size_t kTypeBit = sizeof(Type) * 8; + + Type digits_[kCapacity]; + size_t count_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_BIGINTEGER_H_ diff --git a/ipaacalib/cpp/include/rapidjson/internal/diyfp.h b/ipaacalib/cpp/include/rapidjson/internal/diyfp.h new file mode 100644 index 0000000000000000000000000000000000000000..174b9facfb0fd0218479e594a288b8e28943724a --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/internal/diyfp.h @@ -0,0 +1,268 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DIYFP_H_ +#define RAPIDJSON_DIYFP_H_ + +#if defined(_MSC_VER) +#include <intrin.h> +#if defined(_M_AMD64) +#pragma intrinsic(_BitScanReverse64) +#endif +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +struct DiyFp { + DiyFp() {} + + DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} + + explicit DiyFp(double d) { + union { + double d; + uint64_t u64; + } u = { d }; + + int biased_e = (u.u64 & kDpExponentMask) >> kDpSignificandSize; + uint64_t significand = (u.u64 & kDpSignificandMask); + if (biased_e != 0) { + f = significand + kDpHiddenBit; + e = biased_e - kDpExponentBias; + } + else { + f = significand; + e = kDpMinExponent + 1; + } + } + + DiyFp operator-(const DiyFp& rhs) const { + return DiyFp(f - rhs.f, e); + } + + DiyFp operator*(const DiyFp& rhs) const { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t h; + uint64_t l = _umul128(f, rhs.f, &h); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + unsigned __int128 p = static_cast<unsigned __int128>(f) * static_cast<unsigned __int128>(rhs.f); + uint64_t h = p >> 64; + uint64_t l = static_cast<uint64_t>(p); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#else + const uint64_t M32 = 0xFFFFFFFF; + const uint64_t a = f >> 32; + const uint64_t b = f & M32; + const uint64_t c = rhs.f >> 32; + const uint64_t d = rhs.f & M32; + const uint64_t ac = a * c; + const uint64_t bc = b * c; + const uint64_t ad = a * d; + const uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); + tmp += 1U << 31; /// mult_round + return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); +#endif + } + + DiyFp Normalize() const { +#if defined(_MSC_VER) && defined(_M_AMD64) + unsigned long index; + _BitScanReverse64(&index, f); + return DiyFp(f << (63 - index), e - (63 - index)); +#elif defined(__GNUC__) && __GNUC__ >= 4 + int s = __builtin_clzll(f); + return DiyFp(f << s, e - s); +#else + DiyFp res = *this; + while (!(res.f & (static_cast<uint64_t>(1) << 63))) { + res.f <<= 1; + res.e--; + } + return res; +#endif + } + + DiyFp NormalizeBoundary() const { + DiyFp res = *this; + while (!(res.f & (kDpHiddenBit << 1))) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); + return res; + } + + void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { + DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); + DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); + mi.f <<= mi.e - pl.e; + mi.e = pl.e; + *plus = pl; + *minus = mi; + } + + double ToDouble() const { + union { + double d; + uint64_t u64; + }u; + uint64_t significand = f; + int exponent = e; + while (significand > kDpHiddenBit + kDpSignificandMask) { + significand >>= 1; + exponent++; + } + while (exponent > kDpDenormalExponent && (significand & kDpHiddenBit) == 0) { + significand <<= 1; + exponent--; + } + if (exponent >= kDpMaxExponent) { + u.u64 = kDpExponentMask; // Infinity + return u.d; + } + else if (exponent < kDpDenormalExponent) + return 0.0; + const uint64_t be = (exponent == kDpDenormalExponent && (significand & kDpHiddenBit) == 0) ? 0 : + static_cast<uint64_t>(exponent + kDpExponentBias); + u.u64 = (significand & kDpSignificandMask) | (be << kDpSignificandSize); + return u.d; + } + + static const int kDiySignificandSize = 64; + static const int kDpSignificandSize = 52; + static const int kDpExponentBias = 0x3FF + kDpSignificandSize; + static const int kDpMaxExponent = 0x7FF - kDpExponentBias; + static const int kDpMinExponent = -kDpExponentBias; + static const int kDpDenormalExponent = -kDpExponentBias + 1; + static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + uint64_t f; + int e; +}; + +inline DiyFp GetCachedPowerByIndex(size_t index) { + // 10^-348, 10^-340, ..., 10^340 + static const uint64_t kCachedPowers_F[] = { + RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), + RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), + RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), + RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), + RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), + RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), + RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), + RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), + RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), + RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), + RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), + RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), + RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), + RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), + RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), + RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), + RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), + RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), + RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), + RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), + RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), + RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), + RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), + RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), + RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), + RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), + RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), + RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), + RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), + RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), + RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), + RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), + RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), + RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), + RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), + RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), + RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), + RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), + RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), + RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), + RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), + RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), + RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), + RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) + }; + static const int16_t kCachedPowers_E[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, + -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, + -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, + -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, + -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, + 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, + 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, + 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, + 907, 933, 960, 986, 1013, 1039, 1066 + }; + return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); +} + +inline DiyFp GetCachedPower(int e, int* K) { + + //int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374; + double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive + int k = static_cast<int>(dk); + if (k != dk) + k++; + + unsigned index = static_cast<unsigned>((k >> 3) + 1); + *K = -(-348 + static_cast<int>(index << 3)); // decimal exponent no need lookup table + + return GetCachedPowerByIndex(index); +} + +inline DiyFp GetCachedPower10(int exp, int *outExp) { + unsigned index = (exp + 348) / 8; + *outExp = -348 + index * 8; + return GetCachedPowerByIndex(index); + } + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DIYFP_H_ diff --git a/ipaacalib/cpp/include/rapidjson/internal/dtoa.h b/ipaacalib/cpp/include/rapidjson/internal/dtoa.h new file mode 100644 index 0000000000000000000000000000000000000000..c0fa2b89085c0de916c27515c96bb2d380595581 --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/internal/dtoa.h @@ -0,0 +1,225 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DTOA_ +#define RAPIDJSON_DTOA_ + +#include "itoa.h" // GetDigitsLut() +#include "diyfp.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { + while (rest < wp_w && delta - rest >= ten_kappa && + (rest + ten_kappa < wp_w || /// closer + wp_w - rest > rest + ten_kappa - wp_w)) { + buffer[len - 1]--; + rest += ten_kappa; + } +} + +inline unsigned CountDecimalDigit32(uint32_t n) { + // Simple pure C++ implementation was faster than __builtin_clz version in this situation. + if (n < 10) return 1; + if (n < 100) return 2; + if (n < 1000) return 3; + if (n < 10000) return 4; + if (n < 100000) return 5; + if (n < 1000000) return 6; + if (n < 10000000) return 7; + if (n < 100000000) return 8; + if (n < 1000000000) return 9; + return 10; +} + +inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { + static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); + const DiyFp wp_w = Mp - W; + uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e); + uint64_t p2 = Mp.f & (one.f - 1); + int kappa = CountDecimalDigit32(p1); + *len = 0; + + while (kappa > 0) { + uint32_t d; + switch (kappa) { + case 10: d = p1 / 1000000000; p1 %= 1000000000; break; + case 9: d = p1 / 100000000; p1 %= 100000000; break; + case 8: d = p1 / 10000000; p1 %= 10000000; break; + case 7: d = p1 / 1000000; p1 %= 1000000; break; + case 6: d = p1 / 100000; p1 %= 100000; break; + case 5: d = p1 / 10000; p1 %= 10000; break; + case 4: d = p1 / 1000; p1 %= 1000; break; + case 3: d = p1 / 100; p1 %= 100; break; + case 2: d = p1 / 10; p1 %= 10; break; + case 1: d = p1; p1 = 0; break; + default: +#if defined(_MSC_VER) + __assume(0); +#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + __builtin_unreachable(); +#else + d = 0; +#endif + } + if (d || *len) + buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d)); + kappa--; + uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2; + if (tmp <= delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f); + return; + } + } + + // kappa = 0 + for (;;) { + p2 *= 10; + delta *= 10; + char d = static_cast<char>(p2 >> -one.e); + if (d || *len) + buffer[(*len)++] = static_cast<char>('0' + d); + p2 &= one.f - 1; + kappa--; + if (p2 < delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-kappa]); + return; + } + } +} + +inline void Grisu2(double value, char* buffer, int* length, int* K) { + const DiyFp v(value); + DiyFp w_m, w_p; + v.NormalizedBoundaries(&w_m, &w_p); + + const DiyFp c_mk = GetCachedPower(w_p.e, K); + const DiyFp W = v.Normalize() * c_mk; + DiyFp Wp = w_p * c_mk; + DiyFp Wm = w_m * c_mk; + Wm.f++; + Wp.f--; + DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); +} + +inline char* WriteExponent(int K, char* buffer) { + if (K < 0) { + *buffer++ = '-'; + K = -K; + } + + if (K >= 100) { + *buffer++ = static_cast<char>('0' + static_cast<char>(K / 100)); + K %= 100; + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else if (K >= 10) { + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else + *buffer++ = static_cast<char>('0' + static_cast<char>(K)); + + return buffer; +} + +inline char* Prettify(char* buffer, int length, int k) { + const int kk = length + k; // 10^(kk-1) <= v < 10^kk + + if (length <= kk && kk <= 21) { + // 1234e7 -> 12340000000 + for (int i = length; i < kk; i++) + buffer[i] = '0'; + buffer[kk] = '.'; + buffer[kk + 1] = '0'; + return &buffer[kk + 2]; + } + else if (0 < kk && kk <= 21) { + // 1234e-2 -> 12.34 + std::memmove(&buffer[kk + 1], &buffer[kk], length - kk); + buffer[kk] = '.'; + return &buffer[length + 1]; + } + else if (-6 < kk && kk <= 0) { + // 1234e-6 -> 0.001234 + const int offset = 2 - kk; + std::memmove(&buffer[offset], &buffer[0], length); + buffer[0] = '0'; + buffer[1] = '.'; + for (int i = 2; i < offset; i++) + buffer[i] = '0'; + return &buffer[length + offset]; + } + else if (length == 1) { + // 1e30 + buffer[1] = 'e'; + return WriteExponent(kk - 1, &buffer[2]); + } + else { + // 1234e30 -> 1.234e33 + std::memmove(&buffer[2], &buffer[1], length - 1); + buffer[1] = '.'; + buffer[length + 1] = 'e'; + return WriteExponent(kk - 1, &buffer[0 + length + 2]); + } +} + +inline char* dtoa(double value, char* buffer) { + if (value == 0) { + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + int length, K; + Grisu2(value, buffer, &length, &K); + return Prettify(buffer, length, K); + } +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DTOA_ diff --git a/ipaacalib/cpp/include/rapidjson/internal/ieee754.h b/ipaacalib/cpp/include/rapidjson/internal/ieee754.h new file mode 100644 index 0000000000000000000000000000000000000000..ab65cc9a8207e68fadfc203c7528aa273955d291 --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/internal/ieee754.h @@ -0,0 +1,90 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_IEEE754_ +#define RAPIDJSON_IEEE754_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class Double { +public: + Double() {} + Double(double d) : d(d) {} + Double(uint64_t u) : u(u) {} + + double Value() const { return d; } + uint64_t Uint64Value() const { return u; } + + double NextPositiveDouble() const { + RAPIDJSON_ASSERT(!Sign()); + return Double(u + 1).Value(); + } + + double PreviousPositiveDouble() const { + RAPIDJSON_ASSERT(!Sign()); + if (d == 0.0) + return 0.0; + else + return Double(u - 1).Value(); + } + + bool Sign() const { return (u & kSignMask) != 0; } + uint64_t Significand() const { return u & kSignificandMask; } + int Exponent() const { return ((u & kExponentMask) >> kSignificandSize) - kExponentBias; } + + bool IsNan() const { return (u & kExponentMask) == kExponentMask && Significand() != 0; } + bool IsInf() const { return (u & kExponentMask) == kExponentMask && Significand() == 0; } + bool IsNormal() const { return (u & kExponentMask) != 0 || Significand() == 0; } + + uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } + int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } + uint64_t ToBias() const { return (u & kSignMask) ? ~u + 1 : u | kSignMask; } + + static unsigned EffectiveSignificandSize(int order) { + if (order >= -1021) + return 53; + else if (order <= -1074) + return 0; + else + return order + 1074; + } + +private: + static const int kSignificandSize = 52; + static const int kExponentBias = 0x3FF; + static const int kDenormalExponent = 1 - kExponentBias; + static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); + static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + union { + double d; + uint64_t u; + }; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_IEEE754_ diff --git a/ipaacalib/cpp/include/rapidjson/internal/itoa.h b/ipaacalib/cpp/include/rapidjson/internal/itoa.h new file mode 100644 index 0000000000000000000000000000000000000000..3684f07363f6ec4632291f59f1ebfea750bfaae5 --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/internal/itoa.h @@ -0,0 +1,306 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_ITOA_ +#define RAPIDJSON_ITOA_ + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline const char* GetDigitsLut() { + static const char cDigitsLut[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' + }; + return cDigitsLut; +} + +inline char* u32toa(uint32_t value, char* buffer) { + const char* cDigitsLut = GetDigitsLut(); + + if (value < 10000) { + const uint32_t d1 = (value / 100) << 1; + const uint32_t d2 = (value % 100) << 1; + + if (value >= 1000) + *buffer++ = cDigitsLut[d1]; + if (value >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else if (value < 100000000) { + // value = bbbbcccc + const uint32_t b = value / 10000; + const uint32_t c = value % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + else { + // value = aabbbbcccc in decimal + + const uint32_t a = value / 100000000; // 1 to 42 + value %= 100000000; + + if (a >= 10) { + const unsigned i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else + *buffer++ = static_cast<char>('0' + static_cast<char>(a)); + + const uint32_t b = value / 10000; // 0 to 9999 + const uint32_t c = value % 10000; // 0 to 9999 + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + return buffer; +} + +inline char* i32toa(int32_t value, char* buffer) { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + + return u32toa(static_cast<uint32_t>(value), buffer); +} + +inline char* u64toa(uint64_t value, char* buffer) { + const char* cDigitsLut = GetDigitsLut(); + const uint64_t kTen8 = 100000000; + const uint64_t kTen9 = kTen8 * 10; + const uint64_t kTen10 = kTen8 * 100; + const uint64_t kTen11 = kTen8 * 1000; + const uint64_t kTen12 = kTen8 * 10000; + const uint64_t kTen13 = kTen8 * 100000; + const uint64_t kTen14 = kTen8 * 1000000; + const uint64_t kTen15 = kTen8 * 10000000; + const uint64_t kTen16 = kTen8 * kTen8; + + if (value < kTen8) { + uint32_t v = static_cast<uint32_t>(value); + if (v < 10000) { + const uint32_t d1 = (v / 100) << 1; + const uint32_t d2 = (v % 100) << 1; + + if (v >= 1000) + *buffer++ = cDigitsLut[d1]; + if (v >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (v >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else { + // value = bbbbcccc + const uint32_t b = v / 10000; + const uint32_t c = v % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + } + else if (value < kTen16) { + const uint32_t v0 = static_cast<uint32_t>(value / kTen8); + const uint32_t v1 = static_cast<uint32_t>(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + if (value >= kTen15) + *buffer++ = cDigitsLut[d1]; + if (value >= kTen14) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= kTen13) + *buffer++ = cDigitsLut[d2]; + if (value >= kTen12) + *buffer++ = cDigitsLut[d2 + 1]; + if (value >= kTen11) + *buffer++ = cDigitsLut[d3]; + if (value >= kTen10) + *buffer++ = cDigitsLut[d3 + 1]; + if (value >= kTen9) + *buffer++ = cDigitsLut[d4]; + if (value >= kTen8) + *buffer++ = cDigitsLut[d4 + 1]; + + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + else { + const uint32_t a = static_cast<uint32_t>(value / kTen16); // 1 to 1844 + value %= kTen16; + + if (a < 10) + *buffer++ = static_cast<char>('0' + static_cast<char>(a)); + else if (a < 100) { + const uint32_t i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else if (a < 1000) { + *buffer++ = static_cast<char>('0' + static_cast<char>(a / 100)); + + const uint32_t i = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else { + const uint32_t i = (a / 100) << 1; + const uint32_t j = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + *buffer++ = cDigitsLut[j]; + *buffer++ = cDigitsLut[j + 1]; + } + + const uint32_t v0 = static_cast<uint32_t>(value / kTen8); + const uint32_t v1 = static_cast<uint32_t>(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + + return buffer; +} + +inline char* i64toa(int64_t value, char* buffer) { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + + return u64toa(static_cast<uint64_t>(value), buffer); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ITOA_ diff --git a/ipaacalib/cpp/include/rapidjson/internal/meta.h b/ipaacalib/cpp/include/rapidjson/internal/meta.h new file mode 100644 index 0000000000000000000000000000000000000000..226465a219f026eb97332f047047c853982e009f --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/internal/meta.h @@ -0,0 +1,189 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_INTERNAL_META_H_ +#define RAPIDJSON_INTERNAL_META_H_ + +#ifndef RAPIDJSON_RAPIDJSON_H_ +#error <rapidjson.h> not yet included. Do not include this file directly. +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif +#if defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(6334) +#endif + +#if RAPIDJSON_HAS_CXX11_TYPETRAITS +#include <type_traits> +#endif + +//@cond RAPIDJSON_INTERNAL +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching +template <typename T> struct Void { typedef void Type; }; + +/////////////////////////////////////////////////////////////////////////////// +// BoolType, TrueType, FalseType +// +template <bool Cond> struct BoolType { + static const bool Value = Cond; + typedef BoolType Type; +}; +typedef BoolType<true> TrueType; +typedef BoolType<false> FalseType; + + +/////////////////////////////////////////////////////////////////////////////// +// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr +// + +template <bool C> struct SelectIfImpl { template <typename T1, typename T2> struct Apply { typedef T1 Type; }; }; +template <> struct SelectIfImpl<false> { template <typename T1, typename T2> struct Apply { typedef T2 Type; }; }; +template <bool C, typename T1, typename T2> struct SelectIfCond : SelectIfImpl<C>::template Apply<T1,T2> {}; +template <typename C, typename T1, typename T2> struct SelectIf : SelectIfCond<C::Value, T1, T2> {}; + +template <bool Cond1, bool Cond2> struct AndExprCond : FalseType {}; +template <> struct AndExprCond<true, true> : TrueType {}; +template <bool Cond1, bool Cond2> struct OrExprCond : TrueType {}; +template <> struct OrExprCond<false, false> : FalseType {}; + +template <typename C> struct BoolExpr : SelectIf<C,TrueType,FalseType>::Type {}; +template <typename C> struct NotExpr : SelectIf<C,FalseType,TrueType>::Type {}; +template <typename C1, typename C2> struct AndExpr : AndExprCond<C1::Value, C2::Value>::Type {}; +template <typename C1, typename C2> struct OrExpr : OrExprCond<C1::Value, C2::Value>::Type {}; + + +/////////////////////////////////////////////////////////////////////////////// +// AddConst, MaybeAddConst, RemoveConst +template <typename T> struct AddConst { typedef const T Type; }; +template <bool Constify, typename T> struct MaybeAddConst : SelectIfCond<Constify, const T, T> {}; +template <typename T> struct RemoveConst { typedef T Type; }; +template <typename T> struct RemoveConst<const T> { typedef T Type; }; + + +/////////////////////////////////////////////////////////////////////////////// +// IsSame, IsConst, IsMoreConst, IsPointer +// +template <typename T, typename U> struct IsSame : FalseType {}; +template <typename T> struct IsSame<T, T> : TrueType {}; + +template <typename T> struct IsConst : FalseType {}; +template <typename T> struct IsConst<const T> : TrueType {}; + +template <typename CT, typename T> +struct IsMoreConst + : AndExpr<IsSame<typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>, + BoolType<IsConst<CT>::Value >= IsConst<T>::Value> >::Type {}; + +template <typename T> struct IsPointer : FalseType {}; +template <typename T> struct IsPointer<T*> : TrueType {}; + +/////////////////////////////////////////////////////////////////////////////// +// IsBaseOf +// +#if RAPIDJSON_HAS_CXX11_TYPETRAITS + +template <typename B, typename D> struct IsBaseOf + : BoolType< ::std::is_base_of<B,D>::value> {}; + +#else // simplified version adopted from Boost + +template<typename B, typename D> struct IsBaseOfImpl { + RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); + RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); + + typedef char (&Yes)[1]; + typedef char (&No) [2]; + + template <typename T> + static Yes Check(const D*, T); + static No Check(const B*, int); + + struct Host { + operator const B*() const; + operator const D*(); + }; + + enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; +}; + +template <typename B, typename D> struct IsBaseOf + : OrExpr<IsSame<B, D>, BoolExpr<IsBaseOfImpl<B, D> > >::Type {}; + +#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS + + +////////////////////////////////////////////////////////////////////////// +// EnableIf / DisableIf +// +template <bool Condition, typename T = void> struct EnableIfCond { typedef T Type; }; +template <typename T> struct EnableIfCond<false, T> { /* empty */ }; + +template <bool Condition, typename T = void> struct DisableIfCond { typedef T Type; }; +template <typename T> struct DisableIfCond<true, T> { /* empty */ }; + +template <typename Condition, typename T = void> +struct EnableIf : EnableIfCond<Condition::Value, T> {}; + +template <typename Condition, typename T = void> +struct DisableIf : DisableIfCond<Condition::Value, T> {}; + +// SFINAE helpers +struct SfinaeTag {}; +template <typename T> struct RemoveSfinaeTag; +template <typename T> struct RemoveSfinaeTag<SfinaeTag&(*)(T)> { typedef T Type; }; + +#define RAPIDJSON_REMOVEFPTR_(type) \ + typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ + < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type + +#define RAPIDJSON_ENABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + <RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL + +#define RAPIDJSON_DISABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + <RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL + +#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + <RAPIDJSON_REMOVEFPTR_(cond), \ + RAPIDJSON_REMOVEFPTR_(returntype)>::Type + +#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + <RAPIDJSON_REMOVEFPTR_(cond), \ + RAPIDJSON_REMOVEFPTR_(returntype)>::Type + +} // namespace internal +RAPIDJSON_NAMESPACE_END +//@endcond + +#if defined(__GNUC__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_META_H_ diff --git a/ipaacalib/cpp/include/rapidjson/internal/pow10.h b/ipaacalib/cpp/include/rapidjson/internal/pow10.h new file mode 100644 index 0000000000000000000000000000000000000000..0252edfe582da7756f5708fbe42f0ff81e9cff33 --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/internal/pow10.h @@ -0,0 +1,59 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_POW10_ +#define RAPIDJSON_POW10_ + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Computes integer powers of 10 in double (10.0^n). +/*! This function uses lookup table for fast and accurate results. + \param n non-negative exponent. Must <= 308. + \return 10.0^n +*/ +inline double Pow10(int n) { + static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes + 1e+0, + 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, + 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, + 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, + 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, + 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, + 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, + 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, + 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, + 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, + 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, + 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, + 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, + 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, + 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, + 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, + 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 + }; + RAPIDJSON_ASSERT(n >= 0 && n <= 308); + return e[n]; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_POW10_ diff --git a/ipaacalib/cpp/include/rapidjson/internal/stack.h b/ipaacalib/cpp/include/rapidjson/internal/stack.h new file mode 100644 index 0000000000000000000000000000000000000000..93c5f45a5a4b9f9a2967105c61ba7a7c207f7259 --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/internal/stack.h @@ -0,0 +1,183 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_INTERNAL_STACK_H_ +#define RAPIDJSON_INTERNAL_STACK_H_ + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// Stack + +//! A type-unsafe stack for storing different types of data. +/*! \tparam Allocator Allocator for allocating stack memory. +*/ +template <typename Allocator> +class Stack { +public: + // Optimization note: Do not allocate memory for stack_ in constructor. + // Do it lazily when first Push() -> Expand() -> Resize(). + Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { + RAPIDJSON_ASSERT(stackCapacity > 0); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack(Stack&& rhs) + : allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(rhs.stack_), + stackTop_(rhs.stackTop_), + stackEnd_(rhs.stackEnd_), + initialCapacity_(rhs.initialCapacity_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } +#endif + + ~Stack() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack& operator=(Stack&& rhs) { + if (&rhs != this) + { + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = rhs.stack_; + stackTop_ = rhs.stackTop_; + stackEnd_ = rhs.stackEnd_; + initialCapacity_ = rhs.initialCapacity_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } + return *this; + } +#endif + + void Clear() { stackTop_ = stack_; } + + void ShrinkToFit() { + if (Empty()) { + // If the stack is empty, completely deallocate the memory. + Allocator::Free(stack_); + stack_ = 0; + stackTop_ = 0; + stackEnd_ = 0; + } + else + Resize(GetSize()); + } + + // Optimization note: try to minimize the size of this function for force inline. + // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. + template<typename T> + RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { + // Expand the stack if needed + if (stackTop_ + sizeof(T) * count >= stackEnd_) + Expand<T>(count); + + T* ret = reinterpret_cast<T*>(stackTop_); + stackTop_ += sizeof(T) * count; + return ret; + } + + template<typename T> + T* Pop(size_t count) { + RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); + stackTop_ -= count * sizeof(T); + return reinterpret_cast<T*>(stackTop_); + } + + template<typename T> + T* Top() { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast<T*>(stackTop_ - sizeof(T)); + } + + template<typename T> + T* Bottom() { return (T*)stack_; } + + Allocator& GetAllocator() { return *allocator_; } + bool Empty() const { return stackTop_ == stack_; } + size_t GetSize() const { return static_cast<size_t>(stackTop_ - stack_); } + size_t GetCapacity() const { return static_cast<size_t>(stackEnd_ - stack_); } + +private: + template<typename T> + void Expand(size_t count) { + // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity. + size_t newCapacity; + if (stack_ == 0) { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + newCapacity = initialCapacity_; + } else { + newCapacity = GetCapacity(); + newCapacity += (newCapacity + 1) / 2; + } + size_t newSize = GetSize() + sizeof(T) * count; + if (newCapacity < newSize) + newCapacity = newSize; + + Resize(newCapacity); + } + + void Resize(size_t newCapacity) { + const size_t size = GetSize(); // Backup the current size + stack_ = (char*)allocator_->Realloc(stack_, GetCapacity(), newCapacity); + stackTop_ = stack_ + size; + stackEnd_ = stack_ + newCapacity; + } + + void Destroy() { + Allocator::Free(stack_); + RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack + } + + // Prohibit copy constructor & assignment operator. + Stack(const Stack&); + Stack& operator=(const Stack&); + + Allocator* allocator_; + Allocator* ownAllocator_; + char *stack_; + char *stackTop_; + char *stackEnd_; + size_t initialCapacity_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STACK_H_ diff --git a/ipaacalib/cpp/include/rapidjson/internal/strfunc.h b/ipaacalib/cpp/include/rapidjson/internal/strfunc.h new file mode 100644 index 0000000000000000000000000000000000000000..c271dac4505c9a466cd3a86e3f106e17bc2510cc --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/internal/strfunc.h @@ -0,0 +1,43 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_ +#define RAPIDJSON_INTERNAL_STRFUNC_H_ + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom strlen() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s Null-terminated input string. + \return Number of characters in the string. + \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. +*/ +template <typename Ch> +inline SizeType StrLen(const Ch* s) { + const Ch* p = s; + while (*p) ++p; + return SizeType(p - s); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_INTERNAL_STRFUNC_H_ diff --git a/ipaacalib/cpp/include/rapidjson/internal/strtod.h b/ipaacalib/cpp/include/rapidjson/internal/strtod.h new file mode 100644 index 0000000000000000000000000000000000000000..1fc6050eeb4470a596f74e9ae4bf21351749f86c --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/internal/strtod.h @@ -0,0 +1,285 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_STRTOD_ +#define RAPIDJSON_STRTOD_ + +#include "../rapidjson.h" +#include "ieee754.h" +#include "biginteger.h" +#include "diyfp.h" +#include "pow10.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline double FastPath(double significand, int exp) { + if (exp < -308) + return 0.0; + else if (exp >= 0) + return significand * internal::Pow10(exp); + else + return significand / internal::Pow10(-exp); +} + +inline double StrtodNormalPrecision(double d, int p) { + if (p < -308) { + // Prevent expSum < -308, making Pow10(p) = 0 + d = FastPath(d, -308); + d = FastPath(d, p + 308); + } + else + d = FastPath(d, p); + return d; +} + +template <typename T> +inline T Min3(T a, T b, T c) { + T m = a; + if (m > b) m = b; + if (m > c) m = c; + return m; +} + +inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp, bool* adjustToNegative) { + const Double db(b); + const uint64_t bInt = db.IntegerSignificand(); + const int bExp = db.IntegerExponent(); + const int hExp = bExp - 1; + + int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; + + // Adjust for decimal exponent + if (dExp >= 0) { + dS_Exp2 += dExp; + dS_Exp5 += dExp; + } + else { + bS_Exp2 -= dExp; + bS_Exp5 -= dExp; + hS_Exp2 -= dExp; + hS_Exp5 -= dExp; + } + + // Adjust for binary exponent + if (bExp >= 0) + bS_Exp2 += bExp; + else { + dS_Exp2 -= bExp; + hS_Exp2 -= bExp; + } + + // Adjust for half ulp exponent + if (hExp >= 0) + hS_Exp2 += hExp; + else { + dS_Exp2 -= hExp; + bS_Exp2 -= hExp; + } + + // Remove common power of two factor from all three scaled values + int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); + dS_Exp2 -= common_Exp2; + bS_Exp2 -= common_Exp2; + hS_Exp2 -= common_Exp2; + + BigInteger dS = d; + dS.MultiplyPow5(dS_Exp5) <<= dS_Exp2; + + BigInteger bS(bInt); + bS.MultiplyPow5(bS_Exp5) <<= bS_Exp2; + + BigInteger hS(1); + hS.MultiplyPow5(hS_Exp5) <<= hS_Exp2; + + BigInteger delta(0); + *adjustToNegative = dS.Difference(bS, &delta); + + int cmp = delta.Compare(hS); + // If delta is within 1/2 ULP, check for special case when significand is power of two. + // In this case, need to compare with 1/2h in the lower bound. + if (cmp < 0 && *adjustToNegative && // within and dS < bS + db.IsNormal() && (bInt & (bInt - 1)) == 0 && // Power of 2 + db.Uint64Value() != RAPIDJSON_UINT64_C2(0x00100000, 0x00000000)) // minimum normal number must not do this + { + delta <<= 1; + return delta.Compare(hS); + } + return cmp; +} + +inline bool StrtodFast(double d, int p, double* result) { + // Use fast path for string-to-double conversion if possible + // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ + if (p > 22 && p < 22 + 16) { + // Fast Path Cases In Disguise + d *= internal::Pow10(p - 22); + p = 22; + } + + if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 + *result = FastPath(d, p); + return true; + } + else + return false; +} + +// Compute an approximation and see if it is within 1/2 ULP +inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) { + uint64_t significand = 0; + size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 + for (; i < length; i++) { + if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || + (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) + break; + significand = significand * 10 + (decimals[i] - '0'); + } + + if (i < length && decimals[i] >= '5') // Rounding + significand++; + + size_t remaining = length - i; + const unsigned kUlpShift = 3; + const unsigned kUlp = 1 << kUlpShift; + int error = (remaining == 0) ? 0 : kUlp / 2; + + DiyFp v(significand, 0); + v = v.Normalize(); + error <<= -v.e; + + const int dExp = (int)decimalPosition - (int)i + exp; + + int actualExp; + DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); + if (actualExp != dExp) { + static const DiyFp kPow10[] = { + DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1 + DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2 + DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3 + DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4 + DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5 + DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6 + DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7 + }; + int adjustment = dExp - actualExp - 1; + RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7); + v = v * kPow10[adjustment]; + if (length + adjustment > 19) // has more digits than decimal digits in 64-bit + error += kUlp / 2; + } + + v = v * cachedPower; + + error += kUlp + (error == 0 ? 0 : 1); + + const int oldExp = v.e; + v = v.Normalize(); + error <<= oldExp - v.e; + + const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + unsigned precisionSize = 64 - effectiveSignificandSize; + if (precisionSize + kUlpShift >= 64) { + unsigned scaleExp = (precisionSize + kUlpShift) - 63; + v.f >>= scaleExp; + v.e += scaleExp; + error = (error >> scaleExp) + 1 + kUlp; + precisionSize -= scaleExp; + } + + DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); + const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; + const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; + if (precisionBits >= halfWay + error) + rounded.f++; + + *result = rounded.ToDouble(); + + return halfWay - error >= precisionBits || precisionBits >= halfWay + error; +} + +inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) { + const BigInteger dInt(decimals, length); + const int dExp = (int)decimalPosition - (int)length + exp; + Double a(approx); + for (int i = 0; i < 10; i++) { + bool adjustToNegative; + int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp, &adjustToNegative); + if (cmp < 0) + return a.Value(); // within half ULP + else if (cmp == 0) { + // Round towards even + if (a.Significand() & 1) + return adjustToNegative ? a.PreviousPositiveDouble() : a.NextPositiveDouble(); + else + return a.Value(); + } + else // adjustment + a = adjustToNegative ? a.PreviousPositiveDouble() : a.NextPositiveDouble(); + } + + // This should not happen, but in case there is really a bug, break the infinite-loop + return a.Value(); +} + +inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { + RAPIDJSON_ASSERT(d >= 0.0); + RAPIDJSON_ASSERT(length >= 1); + + double result; + if (StrtodFast(d, p, &result)) + return result; + + // Trim leading zeros + while (*decimals == '0' && length > 1) { + length--; + decimals++; + decimalPosition--; + } + + // Trim trailing zeros + while (decimals[length - 1] == '0' && length > 1) { + length--; + decimalPosition--; + exp++; + } + + // Trim right-most digits + const int kMaxDecimalDigit = 780; + if ((int)length > kMaxDecimalDigit) { + exp += (int(length) - kMaxDecimalDigit); + length = kMaxDecimalDigit; + } + + // If too small, underflow to zero + if (int(length) + exp < -324) + return 0.0; + + if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result)) + return result; + + // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison + return StrtodBigInteger(result, decimals, length, decimalPosition, exp); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STRTOD_ diff --git a/ipaacalib/cpp/include/rapidjson/memorybuffer.h b/ipaacalib/cpp/include/rapidjson/memorybuffer.h new file mode 100644 index 0000000000000000000000000000000000000000..95c68a38710e4d3ab6a3ed24b15d94dc787e1ea6 --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/memorybuffer.h @@ -0,0 +1,76 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_MEMORYBUFFER_H_ +#define RAPIDJSON_MEMORYBUFFER_H_ + +#include "rapidjson.h" +#include "internal/stack.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output byte stream. +/*! + This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. + + It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. + + Differences between MemoryBuffer and StringBuffer: + 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. + 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. + + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template <typename Allocator = CrtAllocator> +struct GenericMemoryBuffer { + typedef char Ch; // byte + + GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + + void Put(Ch c) { *stack_.template Push<Ch>() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { stack_.ShrinkToFit(); } + Ch* Push(size_t count) { return stack_.template Push<Ch>(count); } + void Pop(size_t count) { stack_.template Pop<Ch>(count); } + + const Ch* GetBuffer() const { + return stack_.template Bottom<Ch>(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack<Allocator> stack_; +}; + +typedef GenericMemoryBuffer<> MemoryBuffer; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { + std::memset(memoryBuffer.stack_.Push<char>(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/ipaacalib/cpp/include/rapidjson/memorystream.h b/ipaacalib/cpp/include/rapidjson/memorystream.h new file mode 100644 index 0000000000000000000000000000000000000000..f994a12fc47f544c3205507c257d6f047b195549 --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/memorystream.h @@ -0,0 +1,67 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_MEMORYSTREAM_H_ +#define RAPIDJSON_MEMORYSTREAM_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory input byte stream. +/*! + This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. + + It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. + + Differences between MemoryStream and StringStream: + 1. StringStream has encoding but MemoryStream is a byte stream. + 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. + 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). + \note implements Stream concept +*/ +struct MemoryStream { + typedef char Ch; // byte + + MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} + + Ch Peek() const { return (src_ == end_) ? '\0' : *src_; } + Ch Take() { return (src_ == end_) ? '\0' : *src_++; } + size_t Tell() const { return static_cast<size_t>(src_ - begin_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return Tell() + 4 <= size_ ? src_ : 0; + } + + const Ch* src_; //!< Current read position. + const Ch* begin_; //!< Original head of the string. + const Ch* end_; //!< End of stream. + size_t size_; //!< Size of the stream. +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/ipaacalib/cpp/include/rapidjson/msinttypes/inttypes.h b/ipaacalib/cpp/include/rapidjson/msinttypes/inttypes.h new file mode 100644 index 0000000000000000000000000000000000000000..af713c9adf54e060e77efe1df234dac350ddf4c0 --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/msinttypes/inttypes.h @@ -0,0 +1,312 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include "stdint.h" + +// miloyip: VC supports inttypes.h since VC2013 +#if _MSC_VER >= 1800 +#include <inttypes.h> +#else + +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +#else // _WIN64 ][ +# define SCNdPTR "ld" +# define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +# define SCNoPTR "I64o" +# define SCNuPTR "I64u" +# define SCNxPTR "I64x" +# define SCNXPTR "I64X" +#else // _WIN64 ][ +# define SCNoPTR "lo" +# define SCNuPTR "lu" +# define SCNxPTR "lx" +# define SCNXPTR "lX" +#endif // _WIN64 ] + +#endif // __STDC_FORMAT_MACROS ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) +{ + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + +#endif // _MSC_VER >= 1800 + +#endif // _MSC_INTTYPES_H_ ] diff --git a/ipaacalib/cpp/include/rapidjson/msinttypes/stdint.h b/ipaacalib/cpp/include/rapidjson/msinttypes/stdint.h new file mode 100644 index 0000000000000000000000000000000000000000..bbad95af1599feb099cf6a8a8def5c16f79c35b1 --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/msinttypes/stdint.h @@ -0,0 +1,296 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010. +#if _MSC_VER >= 1600 // [ +#include <stdint.h> + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +#undef INT8_C +#undef INT16_C +#undef INT32_C +#undef INT64_C +#undef UINT8_C +#undef UINT16_C +#undef UINT32_C +#undef UINT64_C + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>. +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#else // ] _MSC_VER >= 1700 [ + +#include <limits.h> + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#ifdef __cplusplus +extern "C" { +#endif +# include <wchar.h> +#ifdef __cplusplus +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h> +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>. +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#endif // _MSC_VER >= 1600 ] + +#endif // _MSC_STDINT_H_ ] diff --git a/ipaacalib/cpp/include/rapidjson/prettywriter.h b/ipaacalib/cpp/include/rapidjson/prettywriter.h new file mode 100644 index 0000000000000000000000000000000000000000..ca62bb7f693b349a796d228065a3cfdf29207b41 --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/prettywriter.h @@ -0,0 +1,205 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_PRETTYWRITER_H_ +#define RAPIDJSON_PRETTYWRITER_H_ + +#include "writer.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Writer with indentation and spacing. +/*! + \tparam OutputStream Type of ouptut os. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. +*/ +template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator> +class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator> { +public: + typedef Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator> Base; + typedef typename Base::Ch Ch; + + //! Constructor + /*! \param os Output stream. + \param allocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} + + //! Set custom indentation. + /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). + \param indentCharCount Number of indent characters for each indentation level. + \note The default indentation is 4 spaces. + */ + PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { + RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); + indentChar_ = indentChar; + indentCharCount_ = indentCharCount; + return *this; + } + + /*! @name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } + + bool String(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + PrettyPrefix(kStringType); + return Base::WriteString(str, length); + } + + bool StartObject() { + PrettyPrefix(kObjectType); + new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false); + return Base::WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray); + bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + if (!Base::WriteEndObject()) + return false; + if (Base::level_stack_.Empty()) // end of json text + Base::os_->Flush(); + return true; + } + + bool StartArray() { + PrettyPrefix(kArrayType); + new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true); + return Base::WriteStartArray(); + } + + bool EndArray(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray); + bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + if (!Base::WriteEndArray()) + return false; + if (Base::level_stack_.Empty()) // end of json text + Base::os_->Flush(); + return true; + } + + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} +protected: + void PrettyPrefix(Type type) { + (void)type; + if (Base::level_stack_.GetSize() != 0) { // this value is not at root + typename Base::Level* level = Base::level_stack_.template Top<typename Base::Level>(); + + if (level->inArray) { + if (level->valueCount > 0) { + Base::os_->Put(','); // add comma if it is not the first element in array + Base::os_->Put('\n'); + } + else + Base::os_->Put('\n'); + WriteIndent(); + } + else { // in object + if (level->valueCount > 0) { + if (level->valueCount % 2 == 0) { + Base::os_->Put(','); + Base::os_->Put('\n'); + } + else { + Base::os_->Put(':'); + Base::os_->Put(' '); + } + } + else + Base::os_->Put('\n'); + + if (level->valueCount % 2 == 0) + WriteIndent(); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. + Base::hasRoot_ = true; + } + } + + void WriteIndent() { + size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; + PutN(*Base::os_, indentChar_, count); + } + + Ch indentChar_; + unsigned indentCharCount_; + +private: + // Prohibit copy constructor & assignment operator. + PrettyWriter(const PrettyWriter&); + PrettyWriter& operator=(const PrettyWriter&); +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/ipaacalib/cpp/include/rapidjson/rapidjson.h b/ipaacalib/cpp/include/rapidjson/rapidjson.h new file mode 100644 index 0000000000000000000000000000000000000000..b13e0b0adc8f8039a151f59f3d602b3e1047c9cb --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/rapidjson.h @@ -0,0 +1,628 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_RAPIDJSON_H_ +#define RAPIDJSON_RAPIDJSON_H_ + +// Copyright (c) 2011 Milo Yip (miloyip@gmail.com) +// Version 0.1 + +/*!\file rapidjson.h + \brief common definitions and configuration + + \see RAPIDJSON_CONFIG + */ + +/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration + \brief Configuration macros for library features + + Some RapidJSON features are configurable to adapt the library to a wide + variety of platforms, environments and usage scenarios. Most of the + features can be configured in terms of overriden or predefined + preprocessor macros at compile-time. + + Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. + + \note These macros should be given on the compiler command-line + (where applicable) to avoid inconsistent values when compiling + different translation units of a single application. + */ + +#include <cstdlib> // malloc(), realloc(), free(), size_t +#include <cstring> // memset(), memcpy(), memmove(), memcmp() + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NAMESPACE_(BEGIN|END) +/*! \def RAPIDJSON_NAMESPACE + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace + + In order to avoid symbol clashes and/or "One Definition Rule" errors + between multiple inclusions of (different versions of) RapidJSON in + a single binary, users can customize the name of the main RapidJSON + namespace. + + In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE + to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple + levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref + RAPIDJSON_NAMESPACE_END need to be defined as well: + + \code + // in some .cpp file + #define RAPIDJSON_NAMESPACE my::rapidjson + #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { + #define RAPIDJSON_NAMESPACE_END } } + #include "rapidjson/..." + \endcode + + \see rapidjson + */ +/*! \def RAPIDJSON_NAMESPACE_BEGIN + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (opening expression) + \see RAPIDJSON_NAMESPACE +*/ +/*! \def RAPIDJSON_NAMESPACE_END + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (closing expression) + \see RAPIDJSON_NAMESPACE +*/ +#ifndef RAPIDJSON_NAMESPACE +#define RAPIDJSON_NAMESPACE rapidjson +#endif +#ifndef RAPIDJSON_NAMESPACE_BEGIN +#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE { +#endif +#ifndef RAPIDJSON_NAMESPACE_END +#define RAPIDJSON_NAMESPACE_END } +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_INT64DEFINE + +/*! \def RAPIDJSON_NO_INT64DEFINE + \ingroup RAPIDJSON_CONFIG + \brief Use external 64-bit integer types. + + RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types + to be available at global scope. + + If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to + prevent RapidJSON from defining its own types. +*/ +#ifndef RAPIDJSON_NO_INT64DEFINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#ifdef _MSC_VER +#include "msinttypes/stdint.h" +#include "msinttypes/inttypes.h" +#else +// Other compilers should have this. +#include <stdint.h> +#include <inttypes.h> +#endif +//!@endcond +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_INT64DEFINE +#endif +#endif // RAPIDJSON_NO_INT64TYPEDEF + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_FORCEINLINE + +#ifndef RAPIDJSON_FORCEINLINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#ifdef _MSC_VER +#define RAPIDJSON_FORCEINLINE __forceinline +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) +#else +#define RAPIDJSON_FORCEINLINE +#endif +//!@endcond +#endif // RAPIDJSON_FORCEINLINE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ENDIAN +#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine +#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine + +//! Endianness of the machine. +/*! + \def RAPIDJSON_ENDIAN + \ingroup RAPIDJSON_CONFIG + + GCC 4.6 provided macro for detecting endianness of the target machine. But other + compilers may not have this. User can define RAPIDJSON_ENDIAN to either + \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN. + + Default detection implemented with reference to + \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html + \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp +*/ +#ifndef RAPIDJSON_ENDIAN +// Detect with GCC 4.6's macro +# ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __BYTE_ORDER__ +// Detect with GLIBC's endian.h +# elif defined(__GLIBC__) +# include <endian.h> +# if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif (__BYTE_ORDER == __BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __GLIBC__ +// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro +# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +// Detect with architecture macros +# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(RAPIDJSON_DOXYGEN_RUNNING) +# define RAPIDJSON_ENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif +#endif // RAPIDJSON_ENDIAN + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_64BIT + +//! Whether using 64-bit architecture +#ifndef RAPIDJSON_64BIT +#if defined(__LP64__) || defined(_WIN64) +#define RAPIDJSON_64BIT 1 +#else +#define RAPIDJSON_64BIT 0 +#endif +#endif // RAPIDJSON_64BIT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ALIGN + +//! Data alignment of the machine. +/*! \ingroup RAPIDJSON_CONFIG + \param x pointer to align + + Some machines require strict data alignment. Currently the default uses 4 bytes + alignment. User can customize by defining the RAPIDJSON_ALIGN function macro., +*/ +#ifndef RAPIDJSON_ALIGN +#define RAPIDJSON_ALIGN(x) ((x + 3u) & ~3u) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_UINT64_C2 + +//! Construct a 64-bit literal by a pair of 32-bit integer. +/*! + 64-bit literal with or without ULL suffix is prone to compiler warnings. + UINT64_C() is C macro which cause compilation problems. + Use this macro to define 64-bit constants by a pair of 32-bit integer. +*/ +#ifndef RAPIDJSON_UINT64_C2 +#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast<uint64_t>(high32) << 32) | static_cast<uint64_t>(low32)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD + +/*! \def RAPIDJSON_SIMD + \ingroup RAPIDJSON_CONFIG + \brief Enable SSE2/SSE4.2 optimization. + + RapidJSON supports optimized implementations for some parsing operations + based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible + processors. + + To enable these optimizations, two different symbols can be defined; + \code + // Enable SSE2 optimization. + #define RAPIDJSON_SSE2 + + // Enable SSE4.2 optimization. + #define RAPIDJSON_SSE42 + \endcode + + \c RAPIDJSON_SSE42 takes precedence, if both are defined. + + If any of these symbols is defined, RapidJSON defines the macro + \c RAPIDJSON_SIMD to indicate the availability of the optimized code. +*/ +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ + || defined(RAPIDJSON_DOXYGEN_RUNNING) +#define RAPIDJSON_SIMD +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_SIZETYPEDEFINE + +#ifndef RAPIDJSON_NO_SIZETYPEDEFINE +/*! \def RAPIDJSON_NO_SIZETYPEDEFINE + \ingroup RAPIDJSON_CONFIG + \brief User-provided \c SizeType definition. + + In order to avoid using 32-bit size types for indexing strings and arrays, + define this preprocessor symbol and provide the type rapidjson::SizeType + before including RapidJSON: + \code + #define RAPIDJSON_NO_SIZETYPEDEFINE + namespace rapidjson { typedef ::std::size_t SizeType; } + #include "rapidjson/..." + \endcode + + \see rapidjson::SizeType +*/ +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_SIZETYPEDEFINE +#endif +RAPIDJSON_NAMESPACE_BEGIN +//! Size type (for string lengths, array sizes, etc.) +/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms, + instead of using \c size_t. Users may override the SizeType by defining + \ref RAPIDJSON_NO_SIZETYPEDEFINE. +*/ +typedef unsigned SizeType; +RAPIDJSON_NAMESPACE_END +#endif + +// always import std::size_t to rapidjson namespace +RAPIDJSON_NAMESPACE_BEGIN +using std::size_t; +RAPIDJSON_NAMESPACE_END + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ASSERT + +//! Assertion. +/*! \ingroup RAPIDJSON_CONFIG + By default, rapidjson uses C \c assert() for internal assertions. + User can override it by defining RAPIDJSON_ASSERT(x) macro. + + \note Parsing errors are handled and can be customized by the + \ref RAPIDJSON_ERRORS APIs. +*/ +#ifndef RAPIDJSON_ASSERT +#include <cassert> +#define RAPIDJSON_ASSERT(x) assert(x) +#endif // RAPIDJSON_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_STATIC_ASSERT + +// Adopt from boost +#ifndef RAPIDJSON_STATIC_ASSERT +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +RAPIDJSON_NAMESPACE_BEGIN +template <bool x> struct STATIC_ASSERTION_FAILURE; +template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; }; +template<int x> struct StaticAssertTest {}; +RAPIDJSON_NAMESPACE_END + +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y + +#if defined(__GNUC__) +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif +//!@endcond + +/*! \def RAPIDJSON_STATIC_ASSERT + \brief (Internal) macro to check for conditions at compile-time + \param x compile-time condition + \hideinitializer + */ +#define RAPIDJSON_STATIC_ASSERT(x) \ + typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ + sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE<bool(x) >)> \ + RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Helpers + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#define RAPIDJSON_MULTILINEMACRO_BEGIN do { +#define RAPIDJSON_MULTILINEMACRO_END \ +} while((void)0, 0) + +// adopted from Boost +#define RAPIDJSON_VERSION_CODE(x,y,z) \ + (((x)*100000) + ((y)*100) + (z)) + +// token stringification +#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) +#define RAPIDJSON_DO_STRINGIFY(x) #x + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF + +#if defined(__GNUC__) +#define RAPIDJSON_GNUC \ + RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) +#endif + +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) + +#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) +#define RAPIDJSON_DIAG_OFF(x) \ + RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) + +// push/pop support in Clang and GCC>=4.6 +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) +#else // GCC >= 4.2, < 4.6 +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ +#endif + +#elif defined(_MSC_VER) + +// pragma (MSVC specific) +#define RAPIDJSON_PRAGMA(x) __pragma(x) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x)) + +#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) + +#else + +#define RAPIDJSON_DIAG_OFF(x) /* ignored */ +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ + +#endif // RAPIDJSON_DIAG_* + +/////////////////////////////////////////////////////////////////////////////// +// C++11 features + +#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS __has_feature(cxx_rvalue_references) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) + +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + +#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) +// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#else +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 +#endif +#endif +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT noexcept +#else +#define RAPIDJSON_NOEXCEPT /* noexcept */ +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT + +// no automatic detection, yet +#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 +#endif + +//!@endcond + +/////////////////////////////////////////////////////////////////////////////// +// new/delete + +#ifndef RAPIDJSON_NEW +///! customization point for global \c new +#define RAPIDJSON_NEW(x) new x +#endif +#ifndef RAPIDJSON_DELETE +///! customization point for global \c delete +#define RAPIDJSON_DELETE(x) delete x +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Allocators and Encodings + +#include "allocators.h" +#include "encodings.h" + +/*! \namespace rapidjson + \brief main RapidJSON namespace + \see RAPIDJSON_NAMESPACE +*/ +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Stream + +/*! \class rapidjson::Stream + \brief Concept for reading and writing characters. + + For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). + + For write-only stream, only need to implement Put() and Flush(). + +\code +concept Stream { + typename Ch; //!< Character type of the stream. + + //! Read the current character from stream without moving the read cursor. + Ch Peek() const; + + //! Read the current character from stream and moving the read cursor to next character. + Ch Take(); + + //! Get the current read cursor. + //! \return Number of characters read from start. + size_t Tell(); + + //! Begin writing operation at the current read pointer. + //! \return The begin writer pointer. + Ch* PutBegin(); + + //! Write a character. + void Put(Ch c); + + //! Flush the buffer. + void Flush(); + + //! End the writing operation. + //! \param begin The begin write pointer returned by PutBegin(). + //! \return Number of characters written. + size_t PutEnd(Ch* begin); +} +\endcode +*/ + +//! Provides additional information for stream. +/*! + By using traits pattern, this type provides a default configuration for stream. + For custom stream, this type can be specialized for other configuration. + See TEST(Reader, CustomStringStream) in readertest.cpp for example. +*/ +template<typename Stream> +struct StreamTraits { + //! Whether to make local copy of stream for optimization during parsing. + /*! + By default, for safety, streams do not use local copy optimization. + Stream that can be copied fast should specialize this, like StreamTraits<StringStream>. + */ + enum { copyOptimization = 0 }; +}; + +//! Put N copies of a character to a stream. +template<typename Stream, typename Ch> +inline void PutN(Stream& stream, Ch c, size_t n) { + for (size_t i = 0; i < n; i++) + stream.Put(c); +} + +/////////////////////////////////////////////////////////////////////////////// +// StringStream + +//! Read-only string stream. +/*! \note implements Stream concept +*/ +template <typename Encoding> +struct GenericStringStream { + typedef typename Encoding::Ch Ch; + + GenericStringStream(const Ch *src) : src_(src), head_(src) {} + + Ch Peek() const { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() const { return static_cast<size_t>(src_ - head_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. +}; + +template <typename Encoding> +struct StreamTraits<GenericStringStream<Encoding> > { + enum { copyOptimization = 1 }; +}; + +//! String stream with UTF8 encoding. +typedef GenericStringStream<UTF8<> > StringStream; + +/////////////////////////////////////////////////////////////////////////////// +// InsituStringStream + +//! A read-write string stream. +/*! This string stream is particularly designed for in-situ parsing. + \note implements Stream concept +*/ +template <typename Encoding> +struct GenericInsituStringStream { + typedef typename Encoding::Ch Ch; + + GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} + + // Read + Ch Peek() { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() { return static_cast<size_t>(src_ - head_); } + + // Write + void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } + + Ch* PutBegin() { return dst_ = src_; } + size_t PutEnd(Ch* begin) { return static_cast<size_t>(dst_ - begin); } + void Flush() {} + + Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } + void Pop(size_t count) { dst_ -= count; } + + Ch* src_; + Ch* dst_; + Ch* head_; +}; + +template <typename Encoding> +struct StreamTraits<GenericInsituStringStream<Encoding> > { + enum { copyOptimization = 1 }; +}; + +//! Insitu string stream with UTF8 encoding. +typedef GenericInsituStringStream<UTF8<> > InsituStringStream; + +/////////////////////////////////////////////////////////////////////////////// +// Type + +//! Type of JSON value +enum Type { + kNullType = 0, //!< null + kFalseType = 1, //!< false + kTrueType = 2, //!< true + kObjectType = 3, //!< object + kArrayType = 4, //!< array + kStringType = 5, //!< string + kNumberType = 6 //!< number +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/ipaacalib/cpp/include/rapidjson/reader.h b/ipaacalib/cpp/include/rapidjson/reader.h new file mode 100644 index 0000000000000000000000000000000000000000..f0aa318cb61fcfa288ddc8e4e4b9add7ace901bc --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/reader.h @@ -0,0 +1,1444 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_READER_H_ +#define RAPIDJSON_READER_H_ + +/*! \file reader.h */ + +#include "rapidjson.h" +#include "encodings.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strtod.h" + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include <intrin.h> +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include <nmmintrin.h> +#elif defined(RAPIDJSON_SSE2) +#include <emmintrin.h> +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define RAPIDJSON_NOTHING /* deliberately empty */ +#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + if (HasParseError()) { return value; } \ + RAPIDJSON_MULTILINEMACRO_END +#endif +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) +//!@endcond + +/*! \def RAPIDJSON_PARSE_ERROR_NORETURN + \ingroup RAPIDJSON_ERRORS + \brief Macro to indicate a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + This macros can be used as a customization point for the internal + error handling mechanism of RapidJSON. + + A common usage model is to throw an exception instead of requiring the + caller to explicitly check the \ref rapidjson::GenericReader::Parse's + return value: + + \code + #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ + throw ParseException(parseErrorCode, #parseErrorCode, offset) + + #include <stdexcept> // std::runtime_error + #include "rapidjson/error/error.h" // rapidjson::ParseResult + + struct ParseException : std::runtime_error, rapidjson::ParseResult { + ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) + : std::runtime_error(msg), ParseResult(code, offset) {} + }; + + #include "rapidjson/reader.h" + \endcode + + \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse + */ +#ifndef RAPIDJSON_PARSE_ERROR_NORETURN +#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ + SetParseError(parseErrorCode, offset); \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +/*! \def RAPIDJSON_PARSE_ERROR + \ingroup RAPIDJSON_ERRORS + \brief (Internal) macro to indicate and handle a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. + + \see RAPIDJSON_PARSE_ERROR_NORETURN + \hideinitializer + */ +#ifndef RAPIDJSON_PARSE_ERROR +#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +#include "error/error.h" // ParseErrorCode, ParseResult + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseFlag + +/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kParseDefaultFlags definition. + + User can define this as any \c ParseFlag combinations. +*/ +#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS +#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags +#endif + +//! Combination of parseFlags +/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream + */ +enum ParseFlag { + kParseNoFlags = 0, //!< No flags are set. + kParseInsituFlag = 1, //!< In-situ(destructive) parsing. + kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. + kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. + kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. + kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). + kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Handler + +/*! \class rapidjson::Handler + \brief Concept for receiving events from GenericReader upon parsing. + The functions return true if no error occurs. If they return false, + the event publisher should terminate the process. +\code +concept Handler { + typename Ch; + + bool Null(); + bool Bool(bool b); + bool Int(int i); + bool Uint(unsigned i); + bool Int64(int64_t i); + bool Uint64(uint64_t i); + bool Double(double d); + bool String(const Ch* str, SizeType length, bool copy); + bool StartObject(); + bool Key(const Ch* str, SizeType length, bool copy); + bool EndObject(SizeType memberCount); + bool StartArray(); + bool EndArray(SizeType elementCount); +}; +\endcode +*/ +/////////////////////////////////////////////////////////////////////////////// +// BaseReaderHandler + +//! Default implementation of Handler. +/*! This can be used as base class of any reader handler. + \note implements Handler concept +*/ +template<typename Encoding = UTF8<>, typename Derived = void> +struct BaseReaderHandler { + typedef typename Encoding::Ch Ch; + + typedef typename internal::SelectIf<internal::IsSame<Derived, void>, BaseReaderHandler, Derived>::Type Override; + + bool Default() { return true; } + bool Null() { return static_cast<Override&>(*this).Default(); } + bool Bool(bool) { return static_cast<Override&>(*this).Default(); } + bool Int(int) { return static_cast<Override&>(*this).Default(); } + bool Uint(unsigned) { return static_cast<Override&>(*this).Default(); } + bool Int64(int64_t) { return static_cast<Override&>(*this).Default(); } + bool Uint64(uint64_t) { return static_cast<Override&>(*this).Default(); } + bool Double(double) { return static_cast<Override&>(*this).Default(); } + bool String(const Ch*, SizeType, bool) { return static_cast<Override&>(*this).Default(); } + bool StartObject() { return static_cast<Override&>(*this).Default(); } + bool Key(const Ch* str, SizeType len, bool copy) { return static_cast<Override&>(*this).String(str, len, copy); } + bool EndObject(SizeType) { return static_cast<Override&>(*this).Default(); } + bool StartArray() { return static_cast<Override&>(*this).Default(); } + bool EndArray(SizeType) { return static_cast<Override&>(*this).Default(); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamLocalCopy + +namespace internal { + +template<typename Stream, int = StreamTraits<Stream>::copyOptimization> +class StreamLocalCopy; + +//! Do copy optimization. +template<typename Stream> +class StreamLocalCopy<Stream, 1> { +public: + StreamLocalCopy(Stream& original) : s(original), original_(original) {} + ~StreamLocalCopy() { original_ = s; } + + Stream s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; + + Stream& original_; +}; + +//! Keep reference. +template<typename Stream> +class StreamLocalCopy<Stream, 0> { +public: + StreamLocalCopy(Stream& original) : s(original) {} + + Stream& s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// SkipWhitespace + +//! Skip the JSON white spaces in a stream. +/*! \param is A input stream for skipping white spaces. + \note This function has SSE2/SSE4.2 specialization. +*/ +template<typename InputStream> +void SkipWhitespace(InputStream& is) { + internal::StreamLocalCopy<InputStream> copy(is); + InputStream& s(copy.s); + + while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t') + s.Take(); +} + +#ifdef RAPIDJSON_SSE42 +//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & ~15); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_load_si128((const __m128i *)&whitespace[0]); + + for (;; p += 16) { + const __m128i s = _mm_load_si128((const __m128i *)p); + const unsigned r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); + if (r != 0) { // some of characters is non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +#elif defined(RAPIDJSON_SSE2) + +//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & ~15); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string + static const char whitespaces[4][17] = { + " ", + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"}; + + const __m128i w0 = _mm_loadu_si128((const __m128i *)&whitespaces[0][0]); + const __m128i w1 = _mm_loadu_si128((const __m128i *)&whitespaces[1][0]); + const __m128i w2 = _mm_loadu_si128((const __m128i *)&whitespaces[2][0]); + const __m128i w3 = _mm_loadu_si128((const __m128i *)&whitespaces[3][0]); + + for (;; p += 16) { + const __m128i s = _mm_load_si128((const __m128i *)p); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = (unsigned short)~_mm_movemask_epi8(x); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +#endif // RAPIDJSON_SSE2 + +#ifdef RAPIDJSON_SIMD +//! Template function specialization for InsituStringStream +template<> inline void SkipWhitespace(InsituStringStream& is) { + is.src_ = const_cast<char*>(SkipWhitespace_SIMD(is.src_)); +} + +//! Template function specialization for StringStream +template<> inline void SkipWhitespace(StringStream& is) { + is.src_ = SkipWhitespace_SIMD(is.src_); +} +#endif // RAPIDJSON_SIMD + +/////////////////////////////////////////////////////////////////////////////// +// GenericReader + +//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. +/*! GenericReader parses JSON text from a stream, and send events synchronously to an + object implementing Handler concept. + + It needs to allocate a stack for storing a single decoded string during + non-destructive parsing. + + For in-situ parsing, the decoded string is directly written to the source + text string, no temporary buffer is required. + + A GenericReader object can be reused for parsing multiple JSON text. + + \tparam SourceEncoding Encoding of the input stream. + \tparam TargetEncoding Encoding of the parse output. + \tparam StackAllocator Allocator type for stack. +*/ +template <typename SourceEncoding, typename TargetEncoding, typename StackAllocator = CrtAllocator> +class GenericReader { +public: + typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type + + //! Constructor. + /*! \param allocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) + \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) + */ + GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_() {} + + //! Parse JSON text. + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template <unsigned parseFlags, typename InputStream, typename Handler> + ParseResult Parse(InputStream& is, Handler& handler) { + if (parseFlags & kParseIterativeFlag) + return IterativeParse<parseFlags>(is, handler); + + parseResult_.Clear(); + + ClearStackOnExit scope(*this); + + SkipWhitespace(is); + + if (is.Peek() == '\0') { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + else { + ParseValue<parseFlags>(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (!(parseFlags & kParseStopWhenDoneFlag)) { + SkipWhitespace(is); + + if (is.Peek() != '\0') { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + } + } + + return parseResult_; + } + + //! Parse JSON text (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template <typename InputStream, typename Handler> + ParseResult Parse(InputStream& is, Handler& handler) { + return Parse<kParseDefaultFlags>(is, handler); + } + + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + +protected: + void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } + +private: + // Prohibit copy constructor & assignment operator. + GenericReader(const GenericReader&); + GenericReader& operator=(const GenericReader&); + + void ClearStack() { stack_.Clear(); } + + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericReader& r) : r_(r) {} + ~ClearStackOnExit() { r_.ClearStack(); } + private: + GenericReader& r_; + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + }; + + // Parse object: { string : value, ... } + template<unsigned parseFlags, typename InputStream, typename Handler> + void ParseObject(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '{'); + is.Take(); // Skip '{' + + if (!handler.StartObject()) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespace(is); + + if (is.Peek() == '}') { + is.Take(); + if (!handler.EndObject(0)) // empty object + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType memberCount = 0;;) { + if (is.Peek() != '"') + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + + ParseString<parseFlags>(is, handler, true); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespace(is); + + if (is.Take() != ':') + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + + SkipWhitespace(is); + + ParseValue<parseFlags>(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespace(is); + + ++memberCount; + + switch (is.Take()) { + case ',': SkipWhitespace(is); break; + case '}': + if (!handler.EndObject(memberCount)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + default: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); + } + } + } + + // Parse array: [ value, ... ] + template<unsigned parseFlags, typename InputStream, typename Handler> + void ParseArray(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '['); + is.Take(); // Skip '[' + + if (!handler.StartArray()) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespace(is); + + if (is.Peek() == ']') { + is.Take(); + if (!handler.EndArray(0)) // empty array + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType elementCount = 0;;) { + ParseValue<parseFlags>(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++elementCount; + SkipWhitespace(is); + + switch (is.Take()) { + case ',': SkipWhitespace(is); break; + case ']': + if (!handler.EndArray(elementCount)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + default: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + } + } + } + + template<unsigned parseFlags, typename InputStream, typename Handler> + void ParseNull(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'n'); + is.Take(); + + if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l') { + if (!handler.Null()) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + } + + template<unsigned parseFlags, typename InputStream, typename Handler> + void ParseTrue(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 't'); + is.Take(); + + if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e') { + if (!handler.Bool(true)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + } + + template<unsigned parseFlags, typename InputStream, typename Handler> + void ParseFalse(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'f'); + is.Take(); + + if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e') { + if (!handler.Bool(false)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); + } + + // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). + template<typename InputStream> + unsigned ParseHex4(InputStream& is) { + unsigned codepoint = 0; + for (int i = 0; i < 4; i++) { + Ch c = is.Take(); + codepoint <<= 4; + codepoint += static_cast<unsigned>(c); + if (c >= '0' && c <= '9') + codepoint -= '0'; + else if (c >= 'A' && c <= 'F') + codepoint -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + codepoint -= 'a' - 10; + else { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, is.Tell() - 1); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); + } + } + return codepoint; + } + + template <typename CharType> + class StackStream { + public: + typedef CharType Ch; + + StackStream(internal::Stack<StackAllocator>& stack) : stack_(stack), length_(0) {} + RAPIDJSON_FORCEINLINE void Put(Ch c) { + *stack_.template Push<Ch>() = c; + ++length_; + } + size_t Length() const { return length_; } + Ch* Pop() { + return stack_.template Pop<Ch>(length_); + } + + private: + StackStream(const StackStream&); + StackStream& operator=(const StackStream&); + + internal::Stack<StackAllocator>& stack_; + SizeType length_; + }; + + // Parse string and generate String event. Different code paths for kParseInsituFlag. + template<unsigned parseFlags, typename InputStream, typename Handler> + void ParseString(InputStream& is, Handler& handler, bool isKey = false) { + internal::StreamLocalCopy<InputStream> copy(is); + InputStream& s(copy.s); + + bool success = false; + if (parseFlags & kParseInsituFlag) { + typename InputStream::Ch *head = s.PutBegin(); + ParseStringToStream<parseFlags, SourceEncoding, SourceEncoding>(s, s); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + size_t length = s.PutEnd(head) - 1; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + const typename TargetEncoding::Ch* const str = (typename TargetEncoding::Ch*)head; + success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); + } + else { + StackStream<typename TargetEncoding::Ch> stackStream(stack_); + ParseStringToStream<parseFlags, SourceEncoding, TargetEncoding>(s, stackStream); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SizeType length = static_cast<SizeType>(stackStream.Length()) - 1; + const typename TargetEncoding::Ch* const str = stackStream.Pop(); + success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); + } + if (!success) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // Parse string to an output is + // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. + template<unsigned parseFlags, typename SEncoding, typename TEncoding, typename InputStream, typename OutputStream> + RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + static const char escape[256] = { + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', + Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, + 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, + 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 + }; +#undef Z16 +//!@endcond + + RAPIDJSON_ASSERT(is.Peek() == '\"'); + is.Take(); // Skip '\"' + + for (;;) { + Ch c = is.Peek(); + if (c == '\\') { // Escape + is.Take(); + Ch e = is.Take(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && escape[(unsigned char)e]) { + os.Put(escape[(unsigned char)e]); + } + else if (e == 'u') { // Unicode + unsigned codepoint = ParseHex4(is); + if (codepoint >= 0xD800 && codepoint <= 0xDBFF) { + // Handle UTF-16 surrogate pair + if (is.Take() != '\\' || is.Take() != 'u') + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); + unsigned codepoint2 = ParseHex4(is); + if (codepoint2 < 0xDC00 || codepoint2 > 0xDFFF) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); + codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; + } + TEncoding::Encode(os, codepoint); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); + } + else if (c == '"') { // Closing double quote + is.Take(); + os.Put('\0'); // null-terminate the string + return; + } + else if (c == '\0') + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell() - 1); + else if ((unsigned)c < 0x20) // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); + else { + if (parseFlags & kParseValidateEncodingFlag ? + !Transcoder<SEncoding, TEncoding>::Validate(is, os) : + !Transcoder<SEncoding, TEncoding>::Transcode(is, os)) + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); + } + } + } + + template<typename InputStream, bool backup> + class NumberStream {}; + + template<typename InputStream> + class NumberStream<InputStream, false> { + public: + NumberStream(GenericReader& reader, InputStream& is) : is(is) { (void)reader; } + ~NumberStream() {} + + RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } + RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } + RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } + size_t Tell() { return is.Tell(); } + size_t Length() { return 0; } + const char* Pop() { return 0; } + + protected: + NumberStream& operator=(const NumberStream&); + + InputStream& is; + }; + + template<typename InputStream> + class NumberStream<InputStream, true> : public NumberStream<InputStream, false> { + typedef NumberStream<InputStream, false> Base; + public: + NumberStream(GenericReader& reader, InputStream& is) : NumberStream<InputStream, false>(reader, is), stackStream(reader.stack_) {} + ~NumberStream() {} + + RAPIDJSON_FORCEINLINE Ch TakePush() { + stackStream.Put((char)Base::is.Peek()); + return Base::is.Take(); + } + + size_t Length() { return stackStream.Length(); } + + const char* Pop() { + stackStream.Put('\0'); + return stackStream.Pop(); + } + + private: + StackStream<char> stackStream; + }; + + template<unsigned parseFlags, typename InputStream, typename Handler> + void ParseNumber(InputStream& is, Handler& handler) { + internal::StreamLocalCopy<InputStream> copy(is); + NumberStream<InputStream, (parseFlags & kParseFullPrecisionFlag) != 0> s(*this, copy.s); + + // Parse minus + bool minus = false; + if (s.Peek() == '-') { + minus = true; + s.Take(); + } + + // Parse int: zero / ( digit1-9 *DIGIT ) + unsigned i = 0; + uint64_t i64 = 0; + bool use64bit = false; + int significandDigit = 0; + if (s.Peek() == '0') { + i = 0; + s.TakePush(); + } + else if (s.Peek() >= '1' && s.Peek() <= '9') { + i = static_cast<unsigned>(s.TakePush() - '0'); + + if (minus) + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i >= 214748364) { // 2^31 = 2147483648 + if (i != 214748364 || s.Peek() > '8') { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast<unsigned>(s.TakePush() - '0'); + significandDigit++; + } + else + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i >= 429496729) { // 2^32 - 1 = 4294967295 + if (i != 429496729 || s.Peek() > '5') { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast<unsigned>(s.TakePush() - '0'); + significandDigit++; + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + + // Parse 64bit int + bool useDouble = false; + double d = 0.0; + if (use64bit) { + if (minus) + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC)) // 2^63 = 9223372036854775808 + if (i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8') { + d = i64; + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast<unsigned>(s.TakePush() - '0'); + significandDigit++; + } + else + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999)) // 2^64 - 1 = 18446744073709551615 + if (i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5') { + d = i64; + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast<unsigned>(s.TakePush() - '0'); + significandDigit++; + } + } + + // Force double for big integer + if (useDouble) { + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (d >= 1.7976931348623157e307) // DBL_MAX / 10.0 + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + d = d * 10 + (s.TakePush() - '0'); + } + } + + // Parse frac = decimal-point 1*DIGIT + int expFrac = 0; + size_t decimalPosition; + if (s.Peek() == '.') { + s.Take(); + decimalPosition = s.Length(); + + if (!(s.Peek() >= '0' && s.Peek() <= '9')) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); + + if (!useDouble) { +#if RAPIDJSON_64BIT + // Use i64 to store significand in 64-bit architecture + if (!use64bit) + i64 = i; + + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path + break; + else { + i64 = i64 * 10 + static_cast<unsigned>(s.TakePush() - '0'); + --expFrac; + if (i64 != 0) + significandDigit++; + } + } + + d = (double)i64; +#else + // Use double to store significand in 32-bit architecture + d = use64bit ? (double)i64 : (double)i; +#endif + useDouble = true; + } + + while (s.Peek() >= '0' && s.Peek() <= '9') { + if (significandDigit < 17) { + d = d * 10.0 + (s.TakePush() - '0'); + --expFrac; + if (d != 0.0) + significandDigit++; + } + else + s.TakePush(); + } + } + else + decimalPosition = s.Length(); // decimal position at the end of integer. + + // Parse exp = e [ minus / plus ] 1*DIGIT + int exp = 0; + if (s.Peek() == 'e' || s.Peek() == 'E') { + if (!useDouble) { + d = use64bit ? i64 : i; + useDouble = true; + } + s.Take(); + + bool expMinus = false; + if (s.Peek() == '+') + s.Take(); + else if (s.Peek() == '-') { + s.Take(); + expMinus = true; + } + + if (s.Peek() >= '0' && s.Peek() <= '9') { + exp = s.Take() - '0'; + while (s.Peek() >= '0' && s.Peek() <= '9') { + exp = exp * 10 + (s.Take() - '0'); + if (exp > 308 && !expMinus) // exp > 308 should be rare, so it should be checked first. + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, s.Tell()); + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); + + if (expMinus) + exp = -exp; + } + + // Finish parsing, call event according to the type of number. + bool cont = true; + size_t length = s.Length(); + const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. + + if (useDouble) { + int p = exp + expFrac; + if (parseFlags & kParseFullPrecisionFlag) + d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); + else + d = internal::StrtodNormalPrecision(d, p); + + cont = handler.Double(minus ? -d : d); + } + else { + if (use64bit) { + if (minus) + cont = handler.Int64(-(int64_t)i64); + else + cont = handler.Uint64(i64); + } + else { + if (minus) + cont = handler.Int(-(int)i); + else + cont = handler.Uint(i); + } + } + if (!cont) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // Parse any JSON value + template<unsigned parseFlags, typename InputStream, typename Handler> + void ParseValue(InputStream& is, Handler& handler) { + switch (is.Peek()) { + case 'n': ParseNull <parseFlags>(is, handler); break; + case 't': ParseTrue <parseFlags>(is, handler); break; + case 'f': ParseFalse <parseFlags>(is, handler); break; + case '"': ParseString<parseFlags>(is, handler); break; + case '{': ParseObject<parseFlags>(is, handler); break; + case '[': ParseArray <parseFlags>(is, handler); break; + default : ParseNumber<parseFlags>(is, handler); + } + } + + // Iterative Parsing + + // States + enum IterativeParsingState { + IterativeParsingStartState = 0, + IterativeParsingFinishState, + IterativeParsingErrorState, + + // Object states + IterativeParsingObjectInitialState, + IterativeParsingMemberKeyState, + IterativeParsingKeyValueDelimiterState, + IterativeParsingMemberValueState, + IterativeParsingMemberDelimiterState, + IterativeParsingObjectFinishState, + + // Array states + IterativeParsingArrayInitialState, + IterativeParsingElementState, + IterativeParsingElementDelimiterState, + IterativeParsingArrayFinishState, + + // Single value state + IterativeParsingValueState, + + cIterativeParsingStateCount + }; + + // Tokens + enum Token { + LeftBracketToken = 0, + RightBracketToken, + + LeftCurlyBracketToken, + RightCurlyBracketToken, + + CommaToken, + ColonToken, + + StringToken, + FalseToken, + TrueToken, + NullToken, + NumberToken, + + kTokenCount + }; + + RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) { + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define N NumberToken +#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N + // Maps from ASCII to Token + static const unsigned char tokenMap[256] = { + N16, // 00~0F + N16, // 10~1F + N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F + N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F + N16, // 40~4F + N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F + N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F + N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F + N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF + }; +#undef N +#undef N16 +//!@endcond + + if (sizeof(Ch) == 1 || unsigned(c) < 256) + return (Token)tokenMap[(unsigned char)c]; + else + return NumberToken; + } + + RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) { + // current state x one lookahead token -> new state + static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Start + { + IterativeParsingArrayInitialState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingValueState, // String + IterativeParsingValueState, // False + IterativeParsingValueState, // True + IterativeParsingValueState, // Null + IterativeParsingValueState // Number + }, + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ObjectInitial + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberKey + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingKeyValueDelimiterState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // KeyValueDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, + // MemberValue + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingMemberDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberDelimiter + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ObjectFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ArrayInitial + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // Element + { + IterativeParsingErrorState, // Left bracket + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingElementDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ElementDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // ArrayFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Single Value (sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + } + }; // End of G + + return (IterativeParsingState)G[state][token]; + } + + // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). + // May return a new state on state pop. + template <unsigned parseFlags, typename InputStream, typename Handler> + RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { + switch (dst) { + case IterativeParsingStartState: + RAPIDJSON_ASSERT(false); + return IterativeParsingErrorState; + + case IterativeParsingFinishState: + return dst; + + case IterativeParsingErrorState: + return dst; + + case IterativeParsingObjectInitialState: + case IterativeParsingArrayInitialState: + { + // Push the state(Element or MemeberValue) if we are nested in another array or value of member. + // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. + IterativeParsingState n = src; + if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) + n = IterativeParsingElementState; + else if (src == IterativeParsingKeyValueDelimiterState) + n = IterativeParsingMemberValueState; + // Push current state. + *stack_.template Push<SizeType>(1) = n; + // Initialize and push the member/element count. + *stack_.template Push<SizeType>(1) = 0; + // Call handler + bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return dst; + } + } + + case IterativeParsingMemberKeyState: + ParseString<parseFlags>(is, handler, true); + if (HasParseError()) + return IterativeParsingErrorState; + else + return dst; + + case IterativeParsingKeyValueDelimiterState: + if (token == ColonToken) { + is.Take(); + return dst; + } + else + return IterativeParsingErrorState; + + case IterativeParsingMemberValueState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue<parseFlags>(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingElementState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue<parseFlags>(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingMemberDelimiterState: + case IterativeParsingElementDelimiterState: + is.Take(); + // Update member/element count. + *stack_.template Top<SizeType>() = *stack_.template Top<SizeType>() + 1; + return dst; + + case IterativeParsingObjectFinishState: + { + // Get member count. + SizeType c = *stack_.template Pop<SizeType>(1); + // If the object is not empty, count the last member. + if (src == IterativeParsingMemberValueState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast<IterativeParsingState>(*stack_.template Pop<SizeType>(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndObject(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + case IterativeParsingArrayFinishState: + { + // Get element count. + SizeType c = *stack_.template Pop<SizeType>(1); + // If the array is not empty, count the last element. + if (src == IterativeParsingElementState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast<IterativeParsingState>(*stack_.template Pop<SizeType>(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndArray(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + case IterativeParsingValueState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue<parseFlags>(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return IterativeParsingFinishState; + + default: + RAPIDJSON_ASSERT(false); + return IterativeParsingErrorState; + } + } + + template <typename InputStream> + void HandleError(IterativeParsingState src, InputStream& is) { + if (HasParseError()) { + // Error flag has been set. + return; + } + + switch (src) { + case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); + case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); + case IterativeParsingObjectInitialState: + case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); + case IterativeParsingElementState: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + default: RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + } + } + + template <unsigned parseFlags, typename InputStream, typename Handler> + ParseResult IterativeParse(InputStream& is, Handler& handler) { + parseResult_.Clear(); + ClearStackOnExit scope(*this); + IterativeParsingState state = IterativeParsingStartState; + + SkipWhitespace(is); + while (is.Peek() != '\0') { + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state, t); + IterativeParsingState d = Transit<parseFlags>(state, t, n, is, handler); + + if (d == IterativeParsingErrorState) { + HandleError(state, is); + break; + } + + state = d; + + // Do not further consume streams if a root JSON has been parsed. + if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) + break; + + SkipWhitespace(is); + } + + // Handle the end of file. + if (state != IterativeParsingFinishState) + HandleError(state, is); + + return parseResult_; + } + + static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. + internal::Stack<StackAllocator> stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. + ParseResult parseResult_; +}; // class GenericReader + +//! Reader with UTF8 encoding and default allocator. +typedef GenericReader<UTF8<>, UTF8<> > Reader; + +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_READER_H_ diff --git a/ipaacalib/cpp/include/rapidjson/stringbuffer.h b/ipaacalib/cpp/include/rapidjson/stringbuffer.h new file mode 100644 index 0000000000000000000000000000000000000000..db00af3efcf0cf9a92cfe36edf6bd6425421b9c5 --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/stringbuffer.h @@ -0,0 +1,99 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_STRINGBUFFER_H_ +#define RAPIDJSON_STRINGBUFFER_H_ + +#include "rapidjson.h" + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include <utility> // std::move +#endif + +#include "internal/stack.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output stream. +/*! + \tparam Encoding Encoding of the stream. + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template <typename Encoding, typename Allocator = CrtAllocator> +class GenericStringBuffer { +public: + typedef typename Encoding::Ch Ch; + + GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {} + GenericStringBuffer& operator=(GenericStringBuffer&& rhs) { + if (&rhs != this) + stack_ = std::move(rhs.stack_); + return *this; + } +#endif + + void Put(Ch c) { *stack_.template Push<Ch>() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { + // Push and pop a null terminator. This is safe. + *stack_.template Push<Ch>() = '\0'; + stack_.ShrinkToFit(); + stack_.template Pop<Ch>(1); + } + Ch* Push(size_t count) { return stack_.template Push<Ch>(count); } + void Pop(size_t count) { stack_.template Pop<Ch>(count); } + + const Ch* GetString() const { + // Push and pop a null terminator. This is safe. + *stack_.template Push<Ch>() = '\0'; + stack_.template Pop<Ch>(1); + + return stack_.template Bottom<Ch>(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack<Allocator> stack_; + +private: + // Prohibit copy constructor & assignment operator. + GenericStringBuffer(const GenericStringBuffer&); + GenericStringBuffer& operator=(const GenericStringBuffer&); +}; + +//! String buffer with UTF8 encoding +typedef GenericStringBuffer<UTF8<> > StringBuffer; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(GenericStringBuffer<UTF8<> >& stream, char c, size_t n) { + std::memset(stream.stack_.Push<char>(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/ipaacalib/cpp/include/rapidjson/writer.h b/ipaacalib/cpp/include/rapidjson/writer.h new file mode 100644 index 0000000000000000000000000000000000000000..02d66803d4daeeed7488818ed7b4f50381cf3f98 --- /dev/null +++ b/ipaacalib/cpp/include/rapidjson/writer.h @@ -0,0 +1,391 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef RAPIDJSON_WRITER_H_ +#define RAPIDJSON_WRITER_H_ + +#include "rapidjson.h" +#include "internal/stack.h" +#include "internal/strfunc.h" +#include "internal/dtoa.h" +#include "internal/itoa.h" +#include "stringbuffer.h" +#include <new> // placement new + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! JSON writer +/*! Writer implements the concept Handler. + It generates JSON text by events to an output os. + + User may programmatically calls the functions of a writer to generate JSON text. + + On the other side, a writer can also be passed to objects that generates events, + + for example Reader::Parse() and Document::Accept(). + + \tparam OutputStream Type of output stream. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. + \note implements Handler concept +*/ +template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator> +class Writer { +public: + typedef typename SourceEncoding::Ch Ch; + + //! Constructor + /*! \param os Output stream. + \param stackAllocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit + Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), hasRoot_(false) {} + + explicit + Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), hasRoot_(false) {} + + //! Reset the writer with a new stream. + /*! + This function reset the writer with a new stream and default settings, + in order to make a Writer object reusable for output multiple JSONs. + + \param os New output stream. + \code + Writer<OutputStream> writer(os1); + writer.StartObject(); + // ... + writer.EndObject(); + + writer.Reset(os2); + writer.StartObject(); + // ... + writer.EndObject(); + \endcode + */ + void Reset(OutputStream& os) { + os_ = &os; + hasRoot_ = false; + level_stack_.Clear(); + } + + //! Checks whether the output is a complete JSON. + /*! + A complete JSON has a complete root object or array. + */ + bool IsComplete() const { + return hasRoot_ && level_stack_.Empty(); + } + + /*!@name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { Prefix(kNullType); return WriteNull(); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); } + bool Int(int i) { Prefix(kNumberType); return WriteInt(i); } + bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); } + bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return WriteUint64(u64); } + + //! Writes the given \c double value to the stream + /*! + \param d The value to be written. + \return Whether it is succeed. + */ + bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); } + + bool String(const Ch* str, SizeType length, bool copy = false) { + (void)copy; + Prefix(kStringType); + return WriteString(str, length); + } + + bool StartObject() { + Prefix(kObjectType); + new (level_stack_.template Push<Level>()) Level(false); + return WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray); + level_stack_.template Pop<Level>(1); + bool ret = WriteEndObject(); + if (level_stack_.Empty()) // end of json text + os_->Flush(); + return ret; + } + + bool StartArray() { + Prefix(kArrayType); + new (level_stack_.template Push<Level>()) Level(true); + return WriteStartArray(); + } + + bool EndArray(SizeType elementCount = 0) { + (void)elementCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray); + level_stack_.template Pop<Level>(1); + bool ret = WriteEndArray(); + if (level_stack_.Empty()) // end of json text + os_->Flush(); + return ret; + } + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} + +protected: + //! Information for each nested level + struct Level { + Level(bool inArray_) : valueCount(0), inArray(inArray_) {} + size_t valueCount; //!< number of values in this level + bool inArray; //!< true if in array, otherwise in object + }; + + static const size_t kDefaultLevelDepth = 32; + + bool WriteNull() { + os_->Put('n'); os_->Put('u'); os_->Put('l'); os_->Put('l'); return true; + } + + bool WriteBool(bool b) { + if (b) { + os_->Put('t'); os_->Put('r'); os_->Put('u'); os_->Put('e'); + } + else { + os_->Put('f'); os_->Put('a'); os_->Put('l'); os_->Put('s'); os_->Put('e'); + } + return true; + } + + bool WriteInt(int i) { + char buffer[11]; + const char* end = internal::i32toa(i, buffer); + for (const char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteUint(unsigned u) { + char buffer[10]; + const char* end = internal::u32toa(u, buffer); + for (const char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteInt64(int64_t i64) { + char buffer[21]; + const char* end = internal::i64toa(i64, buffer); + for (const char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteUint64(uint64_t u64) { + char buffer[20]; + char* end = internal::u64toa(u64, buffer); + for (char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteDouble(double d) { + char buffer[25]; + char* end = internal::dtoa(d, buffer); + for (char* p = buffer; p != end; ++p) + os_->Put(*p); + return true; + } + + bool WriteString(const Ch* str, SizeType length) { + static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const char escape[256] = { +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 + 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 + Z16, Z16, // 30~4F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF +#undef Z16 + }; + + os_->Put('\"'); + GenericStringStream<SourceEncoding> is(str); + while (is.Tell() < length) { + const Ch c = is.Peek(); + if (!TargetEncoding::supportUnicode && (unsigned)c >= 0x80) { + // Unicode escaping + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + os_->Put('\\'); + os_->Put('u'); + if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { + os_->Put(hexDigits[(codepoint >> 12) & 15]); + os_->Put(hexDigits[(codepoint >> 8) & 15]); + os_->Put(hexDigits[(codepoint >> 4) & 15]); + os_->Put(hexDigits[(codepoint ) & 15]); + } + else if (codepoint >= 0x010000 && codepoint <= 0x10FFFF) { + // Surrogate pair + unsigned s = codepoint - 0x010000; + unsigned lead = (s >> 10) + 0xD800; + unsigned trail = (s & 0x3FF) + 0xDC00; + os_->Put(hexDigits[(lead >> 12) & 15]); + os_->Put(hexDigits[(lead >> 8) & 15]); + os_->Put(hexDigits[(lead >> 4) & 15]); + os_->Put(hexDigits[(lead ) & 15]); + os_->Put('\\'); + os_->Put('u'); + os_->Put(hexDigits[(trail >> 12) & 15]); + os_->Put(hexDigits[(trail >> 8) & 15]); + os_->Put(hexDigits[(trail >> 4) & 15]); + os_->Put(hexDigits[(trail ) & 15]); + } + else + return false; // invalid code point + } + else if ((sizeof(Ch) == 1 || (unsigned)c < 256) && escape[(unsigned char)c]) { + is.Take(); + os_->Put('\\'); + os_->Put(escape[(unsigned char)c]); + if (escape[(unsigned char)c] == 'u') { + os_->Put('0'); + os_->Put('0'); + os_->Put(hexDigits[(unsigned char)c >> 4]); + os_->Put(hexDigits[(unsigned char)c & 0xF]); + } + } + else + Transcoder<SourceEncoding, TargetEncoding>::Transcode(is, *os_); + } + os_->Put('\"'); + return true; + } + + bool WriteStartObject() { os_->Put('{'); return true; } + bool WriteEndObject() { os_->Put('}'); return true; } + bool WriteStartArray() { os_->Put('['); return true; } + bool WriteEndArray() { os_->Put(']'); return true; } + + void Prefix(Type type) { + (void)type; + if (level_stack_.GetSize() != 0) { // this value is not at root + Level* level = level_stack_.template Top<Level>(); + if (level->valueCount > 0) { + if (level->inArray) + os_->Put(','); // add comma if it is not the first element in array + else // in object + os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. + hasRoot_ = true; + } + } + + OutputStream* os_; + internal::Stack<StackAllocator> level_stack_; + bool hasRoot_; + +private: + // Prohibit copy constructor & assignment operator. + Writer(const Writer&); + Writer& operator=(const Writer&); +}; + +// Full specialization for StringStream to prevent memory copying + +template<> +inline bool Writer<StringBuffer>::WriteInt(int i) { + char *buffer = os_->Push(11); + const char* end = internal::i32toa(i, buffer); + os_->Pop(11 - (end - buffer)); + return true; +} + +template<> +inline bool Writer<StringBuffer>::WriteUint(unsigned u) { + char *buffer = os_->Push(10); + const char* end = internal::u32toa(u, buffer); + os_->Pop(10 - (end - buffer)); + return true; +} + +template<> +inline bool Writer<StringBuffer>::WriteInt64(int64_t i64) { + char *buffer = os_->Push(21); + const char* end = internal::i64toa(i64, buffer); + os_->Pop(21 - (end - buffer)); + return true; +} + +template<> +inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) { + char *buffer = os_->Push(20); + const char* end = internal::u64toa(u, buffer); + os_->Pop(20 - (end - buffer)); + return true; +} + +template<> +inline bool Writer<StringBuffer>::WriteDouble(double d) { + char *buffer = os_->Push(25); + char* end = internal::dtoa(d, buffer); + os_->Pop(25 - (end - buffer)); + return true; +} + +RAPIDJSON_NAMESPACE_END + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/ipaacalib/cpp/src/.gitignore b/ipaacalib/cpp/src/.gitignore index 05c3e637fa52c0fb86ceb4bcc7022728da9210b5..d275adff65a1e1fbf3b51632769bc1073f82d614 100644 --- a/ipaacalib/cpp/src/.gitignore +++ b/ipaacalib/cpp/src/.gitignore @@ -1,3 +1,5 @@ ipaaca.pb.cc ipaaca.pb.h +*.sw? +*~ diff --git a/ipaacalib/cpp/src/Makefile b/ipaacalib/cpp/src/Makefile deleted file mode 100644 index 45ba15946eff401b5ad8cbfae12a09f78153baa0..0000000000000000000000000000000000000000 --- a/ipaacalib/cpp/src/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -ifeq ($(WBS_ARCH),mac) - LIB_SUFFIX=.dylib -else - LIB_SUFFIX=.so -endif - -CONFIG = -DIPAACA_DEBUG_MESSAGES -IPAACASOURCES = ipaaca.cc ipaaca.pb.cc -SOURCES = ${IPAACASOURCES} ipaaca-test-main.cc -TEXTSOURCES = ${IPAACASOURCES} textsender.cc -CCFLAGS=-I. -I/usr/local/include -I/opt/local/include ${CONFIG} -LIBFLAGS=-fPIC -shared -BOOSTLIBS = -L/opt/local/lib -lboost_regex-mt -lboost_date_time-mt -lboost_program_options-mt -lboost_thread-mt -lboost_filesystem-mt -lboost_signals-mt -lboost_system-mt -PROTOLIBS = -L/opt/local/lib -lprotobuf -LIBS = ${BOOSTLIBS} ${PROTOLIBS} -L/usr/local/lib -lrsc -lrsbcore - -COMPILER = gfilt - -all: lib - - -lib: - ${COMPILER} ${CCFLAGS} ${IPAACASOURCES} ${LIBS} ${LIBFLAGS} -o libipaaca${LIB_SUFFIX} - -receiver: - ${COMPILER} ${CCFLAGS} -DMAKE_RECEIVER -o ipaaca-receiver ${SOURCES} ${LIBS} - -sender: - ${COMPILER} ${CCFLAGS} -DMAKE_SENDER -o ipaaca-sender ${SOURCES} ${LIBS} - -main: - ${COMPILER} ${CCFLAGS} -o ipaaca-main ${SOURCES} ${LIBS} - -protoc: - protoc --proto_path=../../proto ../../proto/ipaaca.proto --cpp_out=. - -clean: - rm -f libipaaca${LIB_SUFFIX} ipaaca-main ipaaca-sender ipaaca-receiver ipaaca.pb.h ipaaca.pb.cc - - diff --git a/ipaacalib/cpp/src/ipaaca-buffers.cc b/ipaacalib/cpp/src/ipaaca-buffers.cc new file mode 100644 index 0000000000000000000000000000000000000000..8c99b7d3b2a6db1c952694645bf61e0de70a4ca0 --- /dev/null +++ b/ipaacalib/cpp/src/ipaaca-buffers.cc @@ -0,0 +1,757 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +#include <ipaaca/ipaaca.h> + +#define VERBOSE_HANDLERS 0 // remove later + +namespace ipaaca { + +using namespace rsb; +using namespace rsb::filter; +using namespace rsb::converter; +using namespace rsb::patterns; + +IPAACA_EXPORT std::ostream& operator<<(std::ostream& os, const IUPayloadUpdate& obj)//{{{ +{ + os << "PayloadUpdate(uid=" << obj.uid << ", revision=" << obj.revision; + os << ", writer_name=" << obj.writer_name << ", is_delta=" << (obj.is_delta?"True":"False"); + os << ", new_items = {"; + bool first = true; + for (auto& newit: obj.new_items) { + if (first) { first=false; } else { os << ", "; } + //os << "'" << it->first << "':'" << it->second << "'"; + os << "'" << newit.first << "': " << newit.second; + } + os << "}, keys_to_remove = ["; + first = true; + for (std::vector<std::string>::const_iterator it=obj.keys_to_remove.begin(); it!=obj.keys_to_remove.end(); ++it) { + if (first) { first=false; } else { os << ", "; } + os << "'" << *it << "'"; + } + os << "])"; + return os; +} +//}}} +IPAACA_EXPORT std::ostream& operator<<(std::ostream& os, const IULinkUpdate& obj)//{{{ +{ + os << "LinkUpdate(uid=" << obj.uid << ", revision=" << obj.revision; + os << ", writer_name=" << obj.writer_name << ", is_delta=" << (obj.is_delta?"True":"False"); + os << ", new_links = {"; + bool first = true; + for (std::map<std::string, std::set<std::string> >::const_iterator it=obj.new_links.begin(); it!=obj.new_links.end(); ++it) { + if (first) { first=false; } else { os << ", "; } + os << "'" << it->first << "': ["; + bool ffirst = true; + for (std::set<std::string>::const_iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) { + if (ffirst) { ffirst=false; } else { os << ", "; } + os << "'" << *it2 << "'"; + } + os << "]"; + } + os << "}, links_to_remove = {"; + first = true; + for (std::map<std::string, std::set<std::string> >::const_iterator it=obj.links_to_remove.begin(); it!=obj.links_to_remove.end(); ++it) { + if (first) { first=false; } else { os << ", "; } + os << "'" << it->first << "': ["; + bool ffirst = true; + for (std::set<std::string>::const_iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) { + if (ffirst) { ffirst=false; } else { os << ", "; } + os << "'" << *it2 << "'"; + } + os << "]"; + } + os << "})"; + return os; +} +//}}} + +// IUEventHandler//{{{ +IPAACA_EXPORT IUEventHandler::IUEventHandler(IUEventHandlerFunction function, IUEventType event_mask, const std::string& category) +: _function(function), _event_mask(event_mask), _for_all_categories(false) +{ + if (category=="") { + _for_all_categories = true; + } else { + _categories.insert(category); + } +} +IPAACA_EXPORT IUEventHandler::IUEventHandler(IUEventHandlerFunction function, IUEventType event_mask, const std::set<std::string>& categories) +: _function(function), _event_mask(event_mask), _for_all_categories(false) +{ + if (categories.size()==0) { + _for_all_categories = true; + } else { + _categories = categories; + } +} +IPAACA_EXPORT void IUEventHandler::call(Buffer* buffer, boost::shared_ptr<IUInterface> iu, bool local, IUEventType event_type, const std::string& category) +{ + if (_condition_met(event_type, category)) { + //IUInterface::ptr iu = buffer->get(uid); + //if (iu) { +#if VERBOSE_HANDLERS == 1 + std::cout << "[" << pthread_self() << " handler ENTER]" << std::endl; +#endif + _function(iu, event_type, local); +#if VERBOSE_HANDLERS == 1 + std::cout << "[" << pthread_self() << " handler EXIT]" << std::endl; +#endif + //} + } +} +//}}} + +// Buffer//{{{ +IPAACA_EXPORT void Buffer::_allocate_unique_name(const std::string& basename, const std::string& function) { + std::string uuid = ipaaca::generate_uuid_string(); + _basename = basename; + _uuid = uuid.substr(0,8); + _unique_name = "/ipaaca/component/" + _basename + "ID" + _uuid + "/" + function; +} +IPAACA_EXPORT void Buffer::register_handler(IUEventHandlerFunction function, IUEventType event_mask, const std::set<std::string>& categories) +{ + std::cout << "register_handler " << function << " " << event_mask << " " << categories << std::endl; + IUEventHandler::ptr handler = IUEventHandler::ptr(new IUEventHandler(function, event_mask, categories)); + _event_handlers.push_back(handler); +} +IPAACA_EXPORT void Buffer::register_handler(IUEventHandlerFunction function, IUEventType event_mask, const std::string& category) +{ + std::cout << "register_handler " << function << " " << event_mask << " " << category << std::endl; + IUEventHandler::ptr handler = IUEventHandler::ptr(new IUEventHandler(function, event_mask, category)); + _event_handlers.push_back(handler); +} +IPAACA_EXPORT void Buffer::call_iu_event_handlers(boost::shared_ptr<IUInterface> iu, bool local, IUEventType event_type, const std::string& category) +{ + //IPAACA_INFO("handling an event " << ipaaca::iu_event_type_to_str(event_type) << " for IU " << iu->uid()) + //std::cout << "handling an event " << ipaaca::iu_event_type_to_str(event_type) << " for IU " << iu->uid() << std::endl; + for (std::vector<IUEventHandler::ptr>::iterator it = _event_handlers.begin(); it != _event_handlers.end(); ++it) { + (*it)->call(this, iu, local, event_type, category); + } +} +//}}} + +// Callbacks for OutputBuffer//{{{ +IPAACA_EXPORT CallbackIUPayloadUpdate::CallbackIUPayloadUpdate(Buffer* buffer): _buffer(buffer) { } +IPAACA_EXPORT CallbackIULinkUpdate::CallbackIULinkUpdate(Buffer* buffer): _buffer(buffer) { } +IPAACA_EXPORT CallbackIUCommission::CallbackIUCommission(Buffer* buffer): _buffer(buffer) { } +// dlw +IPAACA_EXPORT CallbackIUResendRequest::CallbackIUResendRequest(Buffer* buffer): _buffer(buffer) { } + +IPAACA_EXPORT boost::shared_ptr<int> CallbackIUPayloadUpdate::call(const std::string& methodName, boost::shared_ptr<IUPayloadUpdate> update) +{ + IPAACA_INFO("") + //std::cout << "-- Received a modify_payload with " << update->new_items.size() << " keys to merge." << std::endl; + IUInterface::ptr iui = _buffer->get(update->uid); + if (! iui) { + IPAACA_WARNING("Remote InBuffer tried to spuriously write non-existent IU " << update->uid) + return boost::shared_ptr<int>(new int(0)); + } + IU::ptr iu = boost::static_pointer_cast<IU>(iui); + iu->_revision_lock.lock(); + if ((update->revision != 0) && (update->revision != iu->_revision)) { + IPAACA_INFO("Remote write operation failed because request was out of date; IU " << update->uid) + IPAACA_INFO(" Referred-to revision was " << update->revision << " while local one is " << iu->_revision) + iu->_revision_lock.unlock(); + return boost::shared_ptr<int>(new int(0)); + } + if (update->is_delta) { + // FIXME FIXME this is an unsolved problem atm: deletes in a delta update are + // sent individually. We should have something like _internal_merge_and_remove + for (std::vector<std::string>::const_iterator it=update->keys_to_remove.begin(); it!=update->keys_to_remove.end(); ++it) { + iu->payload()._internal_remove(*it, update->writer_name); //_buffer->unique_name()); + } + // but it is solved for pure merges: + iu->payload()._internal_merge(update->new_items, update->writer_name); + } else { + iu->payload()._internal_replace_all(update->new_items, update->writer_name); //_buffer->unique_name()); + } + //std::cout << "-- Calling update handler due to remote write." << std::endl; + _buffer->call_iu_event_handlers(iu, true, IU_UPDATED, iu->category()); + revision_t revision = iu->revision(); + iu->_revision_lock.unlock(); + return boost::shared_ptr<int>(new int(revision)); +} + +IPAACA_EXPORT boost::shared_ptr<int> CallbackIULinkUpdate::call(const std::string& methodName, boost::shared_ptr<IULinkUpdate> update) +{ + IUInterface::ptr iui = _buffer->get(update->uid); + if (! iui) { + IPAACA_WARNING("Remote InBuffer tried to spuriously write non-existent IU " << update->uid) + return boost::shared_ptr<int>(new int(0)); + } + IU::ptr iu = boost::static_pointer_cast<IU>(iui); + iu->_revision_lock.lock(); + if ((update->revision != 0) && (update->revision != iu->_revision)) { + IPAACA_INFO("Remote write operation failed because request was out of date; IU " << update->uid) + iu->_revision_lock.unlock(); + return boost::shared_ptr<int>(new int(0)); + } + if (update->is_delta) { + iu->modify_links(update->new_links, update->links_to_remove, update->writer_name); + } else { + iu->set_links(update->new_links, update->writer_name); + } + _buffer->call_iu_event_handlers(iu, true, IU_LINKSUPDATED, iu->category()); + revision_t revision = iu->revision(); + iu->_revision_lock.unlock(); + return boost::shared_ptr<int>(new int(revision)); +} +IPAACA_EXPORT boost::shared_ptr<int> CallbackIUCommission::call(const std::string& methodName, boost::shared_ptr<protobuf::IUCommission> update) +{ + IUInterface::ptr iui = _buffer->get(update->uid()); + if (! iui) { + IPAACA_WARNING("Remote InBuffer tried to spuriously write non-existent IU " << update->uid()) + return boost::shared_ptr<int>(new int(0)); + } + IU::ptr iu = boost::static_pointer_cast<IU>(iui); + iu->_revision_lock.lock(); + if ((update->revision() != 0) && (update->revision() != iu->_revision)) { + IPAACA_INFO("Remote write operation failed because request was out of date; IU " << update->uid()) + iu->_revision_lock.unlock(); + return boost::shared_ptr<int>(new int(0)); + } + if (iu->committed()) { + return boost::shared_ptr<int>(new int(0)); + } else { + } + iu->_internal_commit(update->writer_name()); + _buffer->call_iu_event_handlers(iu, true, IU_LINKSUPDATED, iu->category()); + revision_t revision = iu->revision(); + iu->_revision_lock.unlock(); + return boost::shared_ptr<int>(new int(revision)); +} +/** dlw */ +IPAACA_EXPORT boost::shared_ptr<int> CallbackIUResendRequest::call(const std::string& methodName, boost::shared_ptr<protobuf::IUResendRequest> update) +{ + IUInterface::ptr iui = _buffer->get(update->uid()); + if (! iui) { + IPAACA_WARNING("Remote InBuffer tried to spuriously write non-existent IU " << update->uid()) + return boost::shared_ptr<int>(new int(0)); + } + IU::ptr iu = boost::static_pointer_cast<IU>(iui); + if ((update->has_hidden_scope_name() == true)&&(update->hidden_scope_name().compare("") != 0)){ + //_buffer->call_iu_event_handlers(iu, true, IU_UPDATED, update->hidden_scope_name()); + revision_t revision = iu->revision(); + + _buffer->_publish_iu_resend(iu, update->hidden_scope_name()); + //iu->_publish_resend(iu, update->hidden_scope_name()); + + return boost::shared_ptr<int>(new int(revision)); + } else { + revision_t revision = 0; + return boost::shared_ptr<int>(new int(revision)); + } +} +//}}} + +// OutputBuffer//{{{ + +IPAACA_EXPORT OutputBuffer::OutputBuffer(const std::string& basename, const std::string& channel) +:Buffer(basename, "OB") +{ + //IPAACA_INFO("Entering ...") + _id_prefix = _basename + "-" + _uuid + "-IU-"; + _channel = (channel=="") ? __ipaaca_static_option_default_channel: channel; + _initialize_server(); + //IPAACA_INFO("... exiting.") +} +IPAACA_EXPORT void OutputBuffer::_initialize_server() +{ + //IPAACA_INFO("Entering ...") + _server = getFactory().createLocalServer( Scope( _unique_name ) ); + //_server = getFactory().createServer( Scope( _unique_name ) ); + //IPAACA_INFO("Registering methods") + _server->registerMethod("updatePayload", Server::CallbackPtr(new CallbackIUPayloadUpdate(this))); + _server->registerMethod("updateLinks", Server::CallbackPtr(new CallbackIULinkUpdate(this))); + _server->registerMethod("commit", Server::CallbackPtr(new CallbackIUCommission(this))); + // dlw + _server->registerMethod("resendRequest", Server::CallbackPtr(new CallbackIUResendRequest(this))); + + //IPAACA_INFO("... exiting.") +} +IPAACA_EXPORT OutputBuffer::ptr OutputBuffer::create(const std::string& basename) +{ + Initializer::initialize_backend(); + return OutputBuffer::ptr(new OutputBuffer(basename)); +} +IPAACA_EXPORT IUInterface::ptr OutputBuffer::get(const std::string& iu_uid) +{ + IUStore::iterator it = _iu_store.find(iu_uid); + if (it==_iu_store.end()) return IUInterface::ptr(); + return it->second; +} +IPAACA_EXPORT std::set<IUInterface::ptr> OutputBuffer::get_ius() +{ + std::set<IUInterface::ptr> set; + for (IUStore::iterator it=_iu_store.begin(); it!=_iu_store.end(); ++it) set.insert(it->second); + return set; +} + +IPAACA_EXPORT void OutputBuffer::_send_iu_link_update(IUInterface* iu, bool is_delta, revision_t revision, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name) +{ + IULinkUpdate* lup = new ipaaca::IULinkUpdate(); + Informer<ipaaca::IULinkUpdate>::DataPtr ldata(lup); + lup->uid = iu->uid(); + lup->is_delta = is_delta; + lup->revision = revision; + lup->is_delta = true; + lup->new_links = new_links; + if (is_delta) lup->links_to_remove = links_to_remove; + if (writer_name=="") lup->writer_name = _unique_name; + else lup->writer_name = writer_name; + Informer<AnyType>::Ptr informer = _get_informer(iu->category()); + informer->publish(ldata); +} + +IPAACA_EXPORT void OutputBuffer::_send_iu_payload_update(IUInterface* iu, bool is_delta, revision_t revision, const std::map<std::string, PayloadDocumentEntry::ptr>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name) +{ + IUPayloadUpdate* pup = new ipaaca::IUPayloadUpdate(); + Informer<ipaaca::IUPayloadUpdate>::DataPtr pdata(pup); + pup->payload_type = iu->payload_type(); + pup->uid = iu->uid(); + pup->is_delta = is_delta; + pup->revision = revision; + pup->new_items = new_items; + if (is_delta) pup->keys_to_remove = keys_to_remove; + if (writer_name=="") pup->writer_name = _unique_name; + else pup->writer_name = writer_name; + Informer<AnyType>::Ptr informer = _get_informer(iu->category()); + informer->publish(pdata); +} + +IPAACA_EXPORT void OutputBuffer::_send_iu_commission(IUInterface* iu, revision_t revision, const std::string& writer_name) +{ + Informer<protobuf::IUCommission>::DataPtr data(new protobuf::IUCommission()); + data->set_uid(iu->uid()); + data->set_revision(revision); + if (writer_name=="") data->set_writer_name(_unique_name); + else data->set_writer_name(writer_name); + + Informer<AnyType>::Ptr informer = _get_informer(iu->category()); + informer->publish(data); +} + + + + + + +IPAACA_EXPORT void OutputBuffer::add(IU::ptr iu) +{ + if (_iu_store.count(iu->uid()) > 0) { + throw IUPublishedError(); + } + if (iu->is_published()) { + throw IUPublishedError(); + } + if (iu->access_mode() != IU_ACCESS_MESSAGE) { + // (for Message-type IUs: do not actually store them) + _iu_store[iu->uid()] = iu; + } + iu->_associate_with_buffer(this); //shared_from_this()); + _publish_iu(iu); +} + +IPAACA_EXPORT void OutputBuffer::_publish_iu(IU::ptr iu) +{ + Informer<AnyType>::Ptr informer = _get_informer(iu->_category); + Informer<ipaaca::IU>::DataPtr iu_data(iu); + informer->publish(iu_data); +} + +IPAACA_EXPORT void OutputBuffer::_publish_iu_resend(IU::ptr iu, const std::string& hidden_scope_name) +{ + Informer<AnyType>::Ptr informer = _get_informer(hidden_scope_name); + Informer<ipaaca::IU>::DataPtr iu_data(iu); + informer->publish(iu_data); +} + + + + +IPAACA_EXPORT Informer<AnyType>::Ptr OutputBuffer::_get_informer(const std::string& category) +{ + if (_informer_store.count(category) > 0) { + return _informer_store[category]; + } else { + //IPAACA_INFO("Making new informer for category " << category) + std::string scope_string = "/ipaaca/channel/" + _channel + "/category/" + category; + IPAACA_INFO("Adding informer on " << scope_string) + + Informer<AnyType>::Ptr informer = getFactory().createInformer<AnyType> ( Scope(scope_string)); + _informer_store[category] = informer; + return informer; + } +} +IPAACA_EXPORT boost::shared_ptr<IU> OutputBuffer::remove(const std::string& iu_uid) +{ + IUStore::iterator it = _iu_store.find(iu_uid); + if (it == _iu_store.end()) { + IPAACA_WARNING("Removal of IU " << iu_uid << " requested, but not present in our OutputBuffer") + //throw IUNotFoundError(); + } + IU::ptr iu = it->second; + _retract_iu(iu); + _iu_store.erase(iu_uid); + return iu; +} +IPAACA_EXPORT boost::shared_ptr<IU> OutputBuffer::remove(IU::ptr iu) +{ + return remove(iu->uid()); // to make sure it is in the store +} + +IPAACA_EXPORT void OutputBuffer::_retract_iu(IU::ptr iu) +{ + Informer<protobuf::IURetraction>::DataPtr data(new protobuf::IURetraction()); + data->set_uid(iu->uid()); + data->set_revision(iu->revision()); + Informer<AnyType>::Ptr informer = _get_informer(iu->category()); + informer->publish(data); +} + + +//}}} + +// InputBuffer//{{{ +IPAACA_EXPORT InputBuffer::InputBuffer(const BufferConfiguration& bufferconfiguration) +:Buffer(bufferconfiguration.get_basename(), "IB") +{ + _channel = bufferconfiguration.get_channel(); + + for (std::vector<std::string>::const_iterator it=bufferconfiguration.get_category_interests().begin(); it!=bufferconfiguration.get_category_interests().end(); ++it) { + _create_category_listener_if_needed(*it); + } + _create_category_listener_if_needed(_uuid); + triggerResend = false; +} +IPAACA_EXPORT InputBuffer::InputBuffer(const std::string& basename, const std::set<std::string>& category_interests) +:Buffer(basename, "IB") +{ + _channel = __ipaaca_static_option_default_channel; + + for (std::set<std::string>::const_iterator it=category_interests.begin(); it!=category_interests.end(); ++it) { + _create_category_listener_if_needed(*it); + } + _create_category_listener_if_needed(_uuid); + triggerResend = false; +} +IPAACA_EXPORT InputBuffer::InputBuffer(const std::string& basename, const std::vector<std::string>& category_interests) +:Buffer(basename, "IB") +{ + _channel = __ipaaca_static_option_default_channel; + + for (std::vector<std::string>::const_iterator it=category_interests.begin(); it!=category_interests.end(); ++it) { + _create_category_listener_if_needed(*it); + } + _create_category_listener_if_needed(_uuid); + triggerResend = false; +} +/*IPAACA_EXPORT InputBuffer::InputBuffer(const std::string& basename, const std::initializer_list<std::string>& category_interests) +:Buffer(basename, "IB") +{ + _channel = __ipaaca_static_option_default_channel; + + for (std::initializer_list<std::string>::const_iterator it=category_interests.begin(); it!=category_interests.end(); ++it) { + _create_category_listener_if_needed(*it); + } + _create_category_listener_if_needed(_uuid); + triggerResend = false; +}*/ +IPAACA_EXPORT InputBuffer::InputBuffer(const std::string& basename, const std::string& category_interest1) +:Buffer(basename, "IB") +{ + _channel = __ipaaca_static_option_default_channel; + + _create_category_listener_if_needed(category_interest1); + _create_category_listener_if_needed(_uuid); + triggerResend = false; +} +IPAACA_EXPORT InputBuffer::InputBuffer(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2) +:Buffer(basename, "IB") +{ + _channel = __ipaaca_static_option_default_channel; + + _create_category_listener_if_needed(category_interest1); + _create_category_listener_if_needed(category_interest2); + _create_category_listener_if_needed(_uuid); + triggerResend = false; +} +IPAACA_EXPORT InputBuffer::InputBuffer(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2, const std::string& category_interest3) +:Buffer(basename, "IB") +{ + _channel = __ipaaca_static_option_default_channel; + + _create_category_listener_if_needed(category_interest1); + _create_category_listener_if_needed(category_interest2); + _create_category_listener_if_needed(category_interest3); + _create_category_listener_if_needed(_uuid); + triggerResend = false; +} +IPAACA_EXPORT InputBuffer::InputBuffer(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2, const std::string& category_interest3, const std::string& category_interest4) +:Buffer(basename, "IB") +{ + _channel = __ipaaca_static_option_default_channel; + + _create_category_listener_if_needed(category_interest1); + _create_category_listener_if_needed(category_interest2); + _create_category_listener_if_needed(category_interest3); + _create_category_listener_if_needed(category_interest4); + _create_category_listener_if_needed(_uuid); + triggerResend = false; +} + +IPAACA_EXPORT InputBuffer::ptr InputBuffer::create(const BufferConfiguration& bufferconfiguration) +{ + Initializer::initialize_backend(); + return InputBuffer::ptr(new InputBuffer(bufferconfiguration)); +} +IPAACA_EXPORT InputBuffer::ptr InputBuffer::create(const std::string& basename, const std::set<std::string>& category_interests) +{ + Initializer::initialize_backend(); + return InputBuffer::ptr(new InputBuffer(basename, category_interests)); +} +IPAACA_EXPORT InputBuffer::ptr InputBuffer::create(const std::string& basename, const std::vector<std::string>& category_interests) +{ + Initializer::initialize_backend(); + return InputBuffer::ptr(new InputBuffer(basename, category_interests)); +} +IPAACA_EXPORT InputBuffer::ptr InputBuffer::create(const std::string& basename, const std::string& category_interest1) +{ + Initializer::initialize_backend(); + return InputBuffer::ptr(new InputBuffer(basename, category_interest1)); +} +IPAACA_EXPORT InputBuffer::ptr InputBuffer::create(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2) +{ + Initializer::initialize_backend(); + return InputBuffer::ptr(new InputBuffer(basename, category_interest1, category_interest2)); +} +IPAACA_EXPORT InputBuffer::ptr InputBuffer::create(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2, const std::string& category_interest3) +{ + Initializer::initialize_backend(); + return InputBuffer::ptr(new InputBuffer(basename, category_interest1, category_interest2, category_interest3)); +} +IPAACA_EXPORT InputBuffer::ptr InputBuffer::create(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2, const std::string& category_interest3, const std::string& category_interest4) +{ + Initializer::initialize_backend(); + return InputBuffer::ptr(new InputBuffer(basename, category_interest1, category_interest2, category_interest3, category_interest4)); +} + + +IPAACA_EXPORT void InputBuffer::set_resend(bool resendActive) +{ + triggerResend = resendActive; +} + +IPAACA_EXPORT bool InputBuffer::get_resend() +{ + return triggerResend; +} + + +IPAACA_EXPORT IUInterface::ptr InputBuffer::get(const std::string& iu_uid) +{ + RemotePushIUStore::iterator it = _iu_store.find(iu_uid); // TODO genericize + if (it==_iu_store.end()) return IUInterface::ptr(); + return it->second; +} +IPAACA_EXPORT std::set<IUInterface::ptr> InputBuffer::get_ius() +{ + std::set<IUInterface::ptr> set; + for (RemotePushIUStore::iterator it=_iu_store.begin(); it!=_iu_store.end(); ++it) set.insert(it->second); // TODO genericize + return set; +} + + +IPAACA_EXPORT RemoteServerPtr InputBuffer::_get_remote_server(const std::string& unique_server_name) +{ + std::map<std::string, RemoteServerPtr>::iterator it = _remote_server_store.find(unique_server_name); + if (it!=_remote_server_store.end()) return it->second; + RemoteServerPtr remote_server = getFactory().createRemoteServer(Scope(unique_server_name)); + _remote_server_store[unique_server_name] = remote_server; + return remote_server; +} + +IPAACA_EXPORT ListenerPtr InputBuffer::_create_category_listener_if_needed(const std::string& category) +{ + std::map<std::string, ListenerPtr>::iterator it = _listener_store.find(category); + if (it!=_listener_store.end()) { + return it->second; + } + //IPAACA_INFO("Creating a new listener for category " << category) + std::string scope_string = "/ipaaca/channel/" + _channel + "/category/" + category; + + ListenerPtr listener = getFactory().createListener( Scope(scope_string) ); + IPAACA_INFO("Adding listener on " << scope_string) + HandlerPtr event_handler = HandlerPtr( + new EventFunctionHandler( + boost::bind(&InputBuffer::_handle_iu_events, this, _1) + ) + ); + listener->addHandler(event_handler); + _listener_store[category] = listener; + return listener; +} +IPAACA_EXPORT void InputBuffer::_trigger_resend_request(EventPtr event) { + if (!triggerResend) return; + std::string type = event->getType(); + std::string uid = ""; + std::string writerName = ""; + if (type == "ipaaca::IUPayloadUpdate") { + boost::shared_ptr<IUPayloadUpdate> update = boost::static_pointer_cast<IUPayloadUpdate>(event->getData()); + uid = update->uid; + writerName = update->writer_name; + } else if (type == "ipaaca::IULinkUpdate") { + boost::shared_ptr<IULinkUpdate> update = boost::static_pointer_cast<IULinkUpdate>(event->getData()); + uid = update->uid; + writerName = update->writer_name; + } else if (type == "ipaaca::protobuf::IUCommission") { + boost::shared_ptr<protobuf::IUCommission> update = boost::static_pointer_cast<protobuf::IUCommission>(event->getData()); + uid = update->uid(); + writerName = update->writer_name(); + } else { + std::cout << "_trigger_resend_request: unhandled event type " << type << std::endl; + } + + if (!writerName.empty()) { + RemoteServerPtr server = _get_remote_server(writerName); + if (!uid.empty()) { + boost::shared_ptr<protobuf::IUResendRequest> update = boost::shared_ptr<protobuf::IUResendRequest>(new protobuf::IUResendRequest()); + update->set_uid(uid); + update->set_hidden_scope_name(_uuid); + boost::shared_ptr<int> result = server->call<int>("resendRequest", update, IPAACA_REMOTE_SERVER_TIMEOUT); + if (*result == 0) { + throw IUResendRequestFailedError(); + } else { + //std::cout << "revision " << *result << std::endl; + } + } + } +} +IPAACA_EXPORT void InputBuffer::_handle_iu_events(EventPtr event) +{ + std::string type = event->getType(); + if (type == "ipaaca::RemotePushIU") { + boost::shared_ptr<RemotePushIU> iu = boost::static_pointer_cast<RemotePushIU>(event->getData()); + if (_iu_store.count(iu->category()) > 0) { + // already got the IU... ignore + } else { + _iu_store[iu->uid()] = iu; + iu->_set_buffer(this); + call_iu_event_handlers(iu, false, IU_ADDED, iu->category() ); + } + //IPAACA_INFO( "New RemotePushIU state: " << (*iu) ) + } else if (type == "ipaaca::RemoteMessage") { + boost::shared_ptr<RemoteMessage> iu = boost::static_pointer_cast<RemoteMessage>(event->getData()); + //_iu_store[iu->uid()] = iu; + //iu->_set_buffer(this); + //std::cout << "REFCNT after cast, before calling handlers: " << iu.use_count() << std::endl; + call_iu_event_handlers(iu, false, IU_MESSAGE, iu->category() ); + //_iu_store.erase(iu->uid()); + } else { + RemotePushIUStore::iterator it; + if (type == "ipaaca::IUPayloadUpdate") { + boost::shared_ptr<IUPayloadUpdate> update = boost::static_pointer_cast<IUPayloadUpdate>(event->getData()); + //IPAACA_INFO("** writer name: " << update->writer_name) + std::cout << "writer name " << update->writer_name << std::endl; + if (update->writer_name == _unique_name) { + return; + } + it = _iu_store.find(update->uid); + if (it == _iu_store.end()) { + _trigger_resend_request(event); + IPAACA_INFO("Using UPDATED message for an IU that we did not fully receive before") + return; + } + + it->second->_apply_update(update); + call_iu_event_handlers(it->second, false, IU_UPDATED, it->second->category() ); + + } else if (type == "ipaaca::IULinkUpdate") { + boost::shared_ptr<IULinkUpdate> update = boost::static_pointer_cast<IULinkUpdate>(event->getData()); + if (update->writer_name == _unique_name) { + return; + } + it = _iu_store.find(update->uid); + if (it == _iu_store.end()) { + _trigger_resend_request(event); + IPAACA_INFO("Ignoring LINKSUPDATED message for an IU that we did not fully receive before") + return; + } + + it->second->_apply_link_update(update); + call_iu_event_handlers(it->second, false, IU_LINKSUPDATED, it->second->category() ); + + } else if (type == "ipaaca::protobuf::IUCommission") { + boost::shared_ptr<protobuf::IUCommission> update = boost::static_pointer_cast<protobuf::IUCommission>(event->getData()); + if (update->writer_name() == _unique_name) { + return; + } + it = _iu_store.find(update->uid()); + if (it == _iu_store.end()) { + _trigger_resend_request(event); + IPAACA_INFO("Ignoring COMMITTED message for an IU that we did not fully receive before") + return; + } + // + it->second->_apply_commission(); + it->second->_revision = update->revision(); + call_iu_event_handlers(it->second, false, IU_COMMITTED, it->second->category() ); + // + // + } else if (type == "ipaaca::protobuf::IURetraction") { + boost::shared_ptr<protobuf::IURetraction> update = boost::static_pointer_cast<protobuf::IURetraction>(event->getData()); + it = _iu_store.find(update->uid()); + if (it == _iu_store.end()) { + _trigger_resend_request(event); + IPAACA_INFO("Ignoring RETRACTED message for an IU that we did not fully receive before") + return; + } + // + it->second->_revision = update->revision(); + it->second->_apply_retraction(); + // remove from InputBuffer FIXME: this is a crossover between retracted and deleted behavior + _iu_store.erase(it->first); + // and call the handler. IU reference is still valid for this call, although removed from buffer. + call_iu_event_handlers(it->second, false, IU_COMMITTED, it->second->category() ); + // + } else { + std::cout << "(Unhandled Event type " << type << " !)" << std::endl; + return; + } + //IPAACA_INFO( "New RemotePushIU state: " << *(it->second) ) + } +} +//}}} + +} // of namespace ipaaca diff --git a/ipaacalib/cpp/src/ipaaca-cmdline-parser.cc b/ipaacalib/cpp/src/ipaaca-cmdline-parser.cc index 6430d28e21a26880a7d0960a73db9d6117c69c3b..e08b33ea34b07381ac4b58e5fadced473706f362 100644 --- a/ipaacalib/cpp/src/ipaaca-cmdline-parser.cc +++ b/ipaacalib/cpp/src/ipaaca-cmdline-parser.cc @@ -31,7 +31,10 @@ */ #include <ipaaca/ipaaca.h> + +#ifndef WIN32 #include <getopt.h> +#endif namespace ipaaca { @@ -72,6 +75,7 @@ void CommandLineOptions::dump() { // CommandLineParser::CommandLineParser() +: library_options_handled(true) { initialize_parser_defaults(); } @@ -82,6 +86,60 @@ void CommandLineParser::initialize_parser_defaults() add_option("verbose", 'v', false, ""); add_option("character-name", 'c', true, "UnknownCharacter"); add_option("component-name", 'n', true, "UnknownComponent"); + if (library_options_handled) { + add_option("ipaaca-payload-type", 0, true, "JSON"); + add_option("ipaaca-default-channel", 0, true, "default"); + add_option("ipaaca-enable-logging", 0, true, "WARNING"); + add_option("rsb-enable-logging", 0, true, "ERROR"); + } +} + +bool CommandLineParser::consume_library_option(const std::string& name, bool expect, const char* optarg) +{ + if (name=="ipaaca-payload-type") { + std::string newtype = optarg; + if (newtype=="MAP") newtype="STR"; + if ((newtype=="JSON") || (newtype=="STR")) { + IPAACA_DEBUG("Setting default payload type " << newtype) + __ipaaca_static_option_default_payload_type = newtype; + } else { + IPAACA_WARNING("Ignoring unknown default payload type " << newtype << " - should be one of JSON or STR") + } + } else if (name=="ipaaca-default-channel") { + std::string newch = optarg; + IPAACA_DEBUG("Setting default channel " << newch) + __ipaaca_static_option_default_channel = newch; + } else if (name=="ipaaca-enable-logging") { + std::string level(optarg); + if ((level=="NONE") || (level=="SILENT")) { + IPAACA_DEBUG("Will set log level to NONE") + __ipaaca_static_option_log_level = IPAACA_LOG_LEVEL_NONE; + } else if (level=="DEBUG") { + __ipaaca_static_option_log_level = IPAACA_LOG_LEVEL_DEBUG; + IPAACA_DEBUG("Just set log level to DEBUG") + } else if (level=="INFO") { + IPAACA_DEBUG("Set log level to INFO") + __ipaaca_static_option_log_level = IPAACA_LOG_LEVEL_INFO; + } else if (level=="WARNING") { + IPAACA_DEBUG("Set log level to WARNING") + __ipaaca_static_option_log_level = IPAACA_LOG_LEVEL_WARNING; + } else if (level=="ERROR") { + IPAACA_DEBUG("Set log level to ERROR") + __ipaaca_static_option_log_level = IPAACA_LOG_LEVEL_ERROR; + } else if (level=="CRITICAL") { + IPAACA_DEBUG("Set log level to CRITICAL") + __ipaaca_static_option_log_level = IPAACA_LOG_LEVEL_CRITICAL; + } else { + IPAACA_WARNING("Unknown log level " << optarg) + IPAACA_WARNING("Valid levels are: NONE, DEBUG, INFO, WARNING, ERROR, CRITICAL ") + } + } else if (name=="rsb-enable-logging") { + IPAACA_WARNING("Unimplemented option ignored: " << name) + IPAACA_IMPLEMENT_ME + } else { + return false; + } + return true; } void CommandLineParser::dump_options() @@ -114,6 +172,11 @@ void CommandLineParser::add_option(const std::string& optname, char shortoptn, b CommandLineOptions::ptr CommandLineParser::parse(int argc, char* const* argv) { +#ifdef WIN32 + LOG_IPAACA_CONSOLE("IMPLEMENT ME: command line parsing for Windows. (req'd: getopt)") + throw NotImplementedError(); +#else + IPAACA_DEBUG("") int len = options.size(); struct option long_options[len+1]; int i=0; @@ -148,19 +211,23 @@ CommandLineOptions::ptr CommandLineParser::parse(int argc, char* const* argv) // Detect the end of the options. if (c == -1) break; + bool do_set_option = false; + std::string longname; + std::string longoption; + bool expect; switch (c) { case 0: { - std::string longname = long_options[option_index].name; + longname = long_options[option_index].name; if (longname == "help") { std::cout << "Options:" << std::endl; dump_options(); exit(0); } - std::string longoption = long_options[option_index].name; - bool expect = options[longoption]; - clo->set_option(longoption, expect, optarg); + longoption = long_options[option_index].name; + expect = options[longoption]; + do_set_option = true; } break; @@ -170,13 +237,22 @@ CommandLineOptions::ptr CommandLineParser::parse(int argc, char* const* argv) default: std::string s; s += c; - std::string longoption = longopt[c]; - bool expect = options[longoption]; + longoption = longopt[c]; + expect = options[longoption]; + do_set_option = true; + } + if (do_set_option) { + if (library_options_handled) { + do_set_option = ! consume_library_option(longoption, expect, optarg ); + } + if (do_set_option) { clo->set_option(longoption, expect, optarg); + } } } ensure_defaults_in( clo ); return clo; +#endif } void CommandLineParser::ensure_defaults_in( CommandLineOptions::ptr clo ) diff --git a/ipaacalib/cpp/src/ipaaca-fake.cc b/ipaacalib/cpp/src/ipaaca-fake.cc new file mode 100644 index 0000000000000000000000000000000000000000..c0d27977a0a6574ae9219a3f733a9f1800d7e9a9 --- /dev/null +++ b/ipaacalib/cpp/src/ipaaca-fake.cc @@ -0,0 +1,65 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +#include <ipaaca/ipaaca.h> + +namespace ipaaca { + +IPAACA_EXPORT inline FakeIU::FakeIU() { + IPAACA_INFO("") +} +IPAACA_EXPORT boost::shared_ptr<FakeIU> FakeIU::create() +{ + IPAACA_INFO(""); + auto iu = boost::shared_ptr<FakeIU>(new FakeIU()); + iu->_payload.initialize(iu); + return iu; +} +IPAACA_EXPORT void FakeIU::add_fake_payload_item(const std::string& key, PayloadDocumentEntry::ptr entry) +{ + _payload._remotely_enforced_setitem(key, entry); +} +IPAACA_EXPORT inline FakeIU::~FakeIU() { } +IPAACA_EXPORT inline Payload& FakeIU::payload() { return _payload; } +IPAACA_EXPORT inline const Payload& FakeIU::const_payload() const { return _payload; } +IPAACA_EXPORT inline void FakeIU::commit() { } +IPAACA_EXPORT inline void FakeIU::_modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name) { } +IPAACA_EXPORT inline void FakeIU::_modify_payload(bool is_delta, const std::map<std::string, PayloadDocumentEntry::ptr>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name) { } +IPAACA_EXPORT inline void FakeIU::_apply_update(IUPayloadUpdate::ptr update) { } +IPAACA_EXPORT inline void FakeIU::_apply_link_update(IULinkUpdate::ptr update) { } +IPAACA_EXPORT inline void FakeIU::_apply_commission() { } +IPAACA_EXPORT inline void FakeIU::_apply_retraction() { } + +} // of namespace ipaaca + + diff --git a/ipaacalib/cpp/src/ipaaca-internal.cc b/ipaacalib/cpp/src/ipaaca-internal.cc new file mode 100644 index 0000000000000000000000000000000000000000..1ce8f5f467648919b4728b2ce2e694447c5ba54c --- /dev/null +++ b/ipaacalib/cpp/src/ipaaca-internal.cc @@ -0,0 +1,623 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +#include <ipaaca/ipaaca.h> + +namespace ipaaca { + +using namespace rsb; +using namespace rsb::filter; +using namespace rsb::converter; +using namespace rsb::patterns; + +// static library Initializer +IPAACA_EXPORT bool Initializer::_initialized = false; +IPAACA_EXPORT bool Initializer::initialized() { return _initialized; } +IPAACA_EXPORT void Initializer::initialize_ipaaca_rsb_if_needed() +{ + initialize_backend(); +} +IPAACA_EXPORT void Initializer::initialize_backend()//{{{ +{ + if (_initialized) return; + + //IPAACA_INFO("Calling auto_configure_rsb()") + auto_configure_rsb(); + + // RYT FIXME This configuration stuff has been simply removed in rsb! + //ParticipantConfig config = ParticipantConfig::fromConfiguration(); + //getFactory().setDefaultParticipantConfig(config); + + //IPAACA_INFO("Creating and registering Converters") + boost::shared_ptr<IUConverter> iu_converter(new IUConverter()); + converterRepository<std::string>()->registerConverter(iu_converter); + + boost::shared_ptr<MessageConverter> message_converter(new MessageConverter()); + converterRepository<std::string>()->registerConverter(message_converter); + + boost::shared_ptr<IUPayloadUpdateConverter> payload_update_converter(new IUPayloadUpdateConverter()); + converterRepository<std::string>()->registerConverter(payload_update_converter); + + boost::shared_ptr<IULinkUpdateConverter> link_update_converter(new IULinkUpdateConverter()); + converterRepository<std::string>()->registerConverter(link_update_converter); + + boost::shared_ptr<ProtocolBufferConverter<protobuf::IUCommission> > iu_commission_converter(new ProtocolBufferConverter<protobuf::IUCommission> ()); + converterRepository<std::string>()->registerConverter(iu_commission_converter); + + // dlw + boost::shared_ptr<ProtocolBufferConverter<protobuf::IUResendRequest> > iu_resendrequest_converter(new ProtocolBufferConverter<protobuf::IUResendRequest> ()); + converterRepository<std::string>()->registerConverter(iu_resendrequest_converter); + + boost::shared_ptr<ProtocolBufferConverter<protobuf::IURetraction> > iu_retraction_converter(new ProtocolBufferConverter<protobuf::IURetraction> ()); + converterRepository<std::string>()->registerConverter(iu_retraction_converter); + + boost::shared_ptr<IntConverter> int_converter(new IntConverter()); + converterRepository<std::string>()->registerConverter(int_converter); + + //IPAACA_INFO("Initialization complete.") + _initialized = true; + //IPAACA_TODO("initialize all converters") +}//}}} +IPAACA_EXPORT void Initializer::dump_current_default_config()//{{{ +{ + IPAACA_INFO("--- Dumping current default participant configuration ---") + rsb::ParticipantConfig config = getFactory().getDefaultParticipantConfig(); + std::set<rsb::ParticipantConfig::Transport> transports = config.getTransports(); + for (std::set<rsb::ParticipantConfig::Transport>::const_iterator it=transports.begin(); it!=transports.end(); ++it) { + IPAACA_INFO( "Active transport: " << it->getName() ) + } + IPAACA_INFO("--- End of configuration dump ---") + //ParticipantConfig::Transport inprocess = config.getTransport("inprocess"); + //inprocess.setEnabled(true); + //config.addTransport(inprocess); +}//}}} +IPAACA_EXPORT void Initializer::auto_configure_rsb()//{{{ +{ + // quick hack to iterate through the pwd parents + // and find the closest rsb plugin dir + // + // but only if not yet defined + const char* plugin_path = getenv("RSB_PLUGINS_CPP_PATH"); + if (!plugin_path) { +#ifdef WIN32 + LOG_IPAACA_CONSOLE("WARNING: RSB_PLUGINS_CPP_PATH not set - in Windows it has to be specified.") + //throw NotImplementedError(); +#else + LOG_IPAACA_CONSOLE("RSB_PLUGINS_CPP_PATH not set; looking here and up to 7 dirs up.") + std::string pathstr = "./"; + for (int i=0; i< 8 /* depth EIGHT (totally arbitrary..) */ ; i++) { + std::string where_str = pathstr+"deps/lib/rsb*/plugins"; + const char* where = where_str.c_str(); + glob_t g; + glob(where, 0, NULL, &g); + if (g.gl_pathc>0) { + const char* found_path = g.gl_pathv[0]; + LOG_IPAACA_CONSOLE("Found an RSB plugin dir which will be used automatically: " << found_path) + setenv("RSB_PLUGINS_CPP_PATH", found_path, 1); + break; + } // else keep going + globfree(&g); + pathstr += "../"; + } +#endif + } else { + LOG_IPAACA_CONSOLE("RSB_PLUGINS_CPP_PATH already defined: " << plugin_path) + } +}//}}} + +// RSB backend Converters +// IUConverter//{{{ + +IPAACA_EXPORT IUConverter::IUConverter() +: Converter<std::string> (IPAACA_SYSTEM_DEPENDENT_CLASS_NAME("ipaaca::IU"), "ipaaca-iu", true) +{ +} + +IPAACA_EXPORT std::string IUConverter::serialize(const AnnotatedData& data, std::string& wire) +{ + //std::cout << "serialize" << std::endl; + // Ensure that DATA actually holds a datum of the data-type we expect. + assert(data.first == getDataType()); // "ipaaca::IU" + // NOTE: a dynamic_pointer_cast cannot be used from void* + boost::shared_ptr<const IU> obj = boost::static_pointer_cast<const IU> (data.second); + boost::shared_ptr<protobuf::IU> pbo(new protobuf::IU()); + // transfer obj data to pbo + pbo->set_uid(obj->uid()); + pbo->set_revision(obj->revision()); + pbo->set_category(obj->category()); + pbo->set_payload_type(obj->payload_type()); + pbo->set_owner_name(obj->owner_name()); + pbo->set_committed(obj->committed()); + ipaaca::protobuf::IU_AccessMode a_m; + switch(obj->access_mode()) { + case IU_ACCESS_PUSH: + a_m = ipaaca::protobuf::IU_AccessMode_PUSH; + break; + case IU_ACCESS_REMOTE: + a_m = ipaaca::protobuf::IU_AccessMode_REMOTE; + break; + case IU_ACCESS_MESSAGE: + a_m = ipaaca::protobuf::IU_AccessMode_MESSAGE; + break; + } + pbo->set_access_mode(a_m); + pbo->set_read_only(obj->read_only()); + for (auto& kv: obj->_payload._document_store) { + protobuf::PayloadItem* item = pbo->add_payload(); + item->set_key(kv.first); + //item->set_value( kv.second->to_json_string_representation() ); + //item->set_type("JSON"); + IPAACA_DEBUG("Payload type: " << obj->_payload_type) + if (obj->_payload_type=="JSON") { + item->set_value( kv.second->to_json_string_representation() ); + item->set_type("JSON"); + } else if ((obj->_payload_type=="MAP") || (obj->_payload_type=="STR")) { + // legacy mode + item->set_value( json_value_cast<std::string>(kv.second->document)); + item->set_type("STR"); + } + } + for (LinkMap::const_iterator it=obj->_links._links.begin(); it!=obj->_links._links.end(); ++it) { + protobuf::LinkSet* links = pbo->add_links(); + links->set_type(it->first); + for (std::set<std::string>::const_iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) { + links->add_targets(*it2); + } + } + pbo->SerializeToString(&wire); + switch(obj->access_mode()) { + case IU_ACCESS_PUSH: + //std::cout << "Requesting to send as ipaaca-iu" << std::endl; + return "ipaaca-iu"; + case IU_ACCESS_MESSAGE: + //std::cout << "Requesting to send as ipaaca-messageiu" << std::endl; + return "ipaaca-messageiu"; + default: + //std::cout << "Requesting to send as default" << std::endl; + return getWireSchema(); + } + +} + +IPAACA_EXPORT AnnotatedData IUConverter::deserialize(const std::string& wireSchema, const std::string& wire) { + //std::cout << "deserialize" << std::endl; + assert(wireSchema == getWireSchema()); // "ipaaca-iu" + boost::shared_ptr<protobuf::IU> pbo(new protobuf::IU()); + pbo->ParseFromString(wire); + IUAccessMode mode = static_cast<IUAccessMode>(pbo->access_mode()); + switch(mode) { + case IU_ACCESS_PUSH: + { + // Create a "remote push IU" + boost::shared_ptr<RemotePushIU> obj = RemotePushIU::create(); + // transfer pbo data to obj + obj->_uid = pbo->uid(); + obj->_revision = pbo->revision(); + obj->_category = pbo->category(); + obj->_payload_type = pbo->payload_type(); + obj->_owner_name = pbo->owner_name(); + obj->_committed = pbo->committed(); + obj->_read_only = pbo->read_only(); + obj->_access_mode = IU_ACCESS_PUSH; + for (int i=0; i<pbo->payload_size(); i++) { + const protobuf::PayloadItem& it = pbo->payload(i); + PayloadDocumentEntry::ptr entry; + if (it.type() == "JSON") { + // fully parse json text + entry = PayloadDocumentEntry::from_json_string_representation( it.value() ); + } else { + // assuming legacy "str" -> just copy value to raw string in document + entry = std::make_shared<PayloadDocumentEntry>(); + entry->document.SetString(it.value(), entry->document.GetAllocator()); + } + obj->_payload._document_store[it.key()] = entry; + } + for (int i=0; i<pbo->links_size(); i++) { + const protobuf::LinkSet& pls = pbo->links(i); + LinkSet& ls = obj->_links._links[pls.type()]; + for (int j=0; j<pls.targets_size(); j++) { + ls.insert(pls.targets(j)); + } + } + //return std::make_pair(getDataType(), obj); + return std::make_pair("ipaaca::RemotePushIU", obj); + break; + } + case IU_ACCESS_MESSAGE: + { + // Create a "Message-type IU" + boost::shared_ptr<RemoteMessage> obj = RemoteMessage::create(); + //std::cout << "REFCNT after create: " << obj.use_count() << std::endl; + // transfer pbo data to obj + obj->_uid = pbo->uid(); + obj->_revision = pbo->revision(); + obj->_category = pbo->category(); + obj->_payload_type = pbo->payload_type(); + obj->_owner_name = pbo->owner_name(); + obj->_committed = pbo->committed(); + obj->_read_only = pbo->read_only(); + obj->_access_mode = IU_ACCESS_MESSAGE; + for (int i=0; i<pbo->payload_size(); i++) { + const protobuf::PayloadItem& it = pbo->payload(i); + PayloadDocumentEntry::ptr entry; + if (it.type() == "JSON") { + // fully parse json text + entry = PayloadDocumentEntry::from_json_string_representation( it.value() ); + } else { + // assuming legacy "str" -> just copy value to raw string in document + entry = std::make_shared<PayloadDocumentEntry>(); + entry->document.SetString(it.value(), entry->document.GetAllocator()); + } + obj->_payload._document_store[it.key()] = entry; + } + for (int i=0; i<pbo->links_size(); i++) { + const protobuf::LinkSet& pls = pbo->links(i); + LinkSet& ls = obj->_links._links[pls.type()]; + for (int j=0; j<pls.targets_size(); j++) { + ls.insert(pls.targets(j)); + } + } + //return std::make_pair(getDataType(), obj); + return std::make_pair("ipaaca::RemoteMessage", obj); + break; + } + default: + // other cases not handled yet! ( TODO ) + throw NotImplementedError(); + } +} + +//}}} +// MessageConverter//{{{ + +IPAACA_EXPORT MessageConverter::MessageConverter() +: Converter<std::string> (IPAACA_SYSTEM_DEPENDENT_CLASS_NAME("ipaaca::Message"), "ipaaca-messageiu", true) +{ +} + +IPAACA_EXPORT std::string MessageConverter::serialize(const AnnotatedData& data, std::string& wire) +{ + // Ensure that DATA actually holds a datum of the data-type we expect. + assert(data.first == getDataType()); // "ipaaca::Message" + // NOTE: a dynamic_pointer_cast cannot be used from void* + boost::shared_ptr<const Message> obj = boost::static_pointer_cast<const Message> (data.second); + boost::shared_ptr<protobuf::IU> pbo(new protobuf::IU()); + // transfer obj data to pbo + pbo->set_uid(obj->uid()); + pbo->set_revision(obj->revision()); + pbo->set_category(obj->category()); + pbo->set_payload_type(obj->payload_type()); + pbo->set_owner_name(obj->owner_name()); + pbo->set_committed(obj->committed()); + ipaaca::protobuf::IU_AccessMode a_m; + switch(obj->access_mode()) { + case IU_ACCESS_PUSH: + a_m = ipaaca::protobuf::IU_AccessMode_PUSH; + break; + case IU_ACCESS_REMOTE: + a_m = ipaaca::protobuf::IU_AccessMode_REMOTE; + break; + case IU_ACCESS_MESSAGE: + a_m = ipaaca::protobuf::IU_AccessMode_MESSAGE; + break; + } + pbo->set_access_mode(a_m); + pbo->set_read_only(obj->read_only()); + for (auto& kv: obj->_payload._document_store) { + protobuf::PayloadItem* item = pbo->add_payload(); + item->set_key(kv.first); + //item->set_value( kv.second->to_json_string_representation() ); + //item->set_type("JSON"); + if (obj->_payload_type=="JSON") { + item->set_value( kv.second->to_json_string_representation() ); + item->set_type("JSON"); + } else if ((obj->_payload_type=="MAP") || (obj->_payload_type=="STR")) { + // legacy mode + item->set_value( json_value_cast<std::string>(kv.second->document)); + item->set_type("STR"); + } + } + for (LinkMap::const_iterator it=obj->_links._links.begin(); it!=obj->_links._links.end(); ++it) { + protobuf::LinkSet* links = pbo->add_links(); + links->set_type(it->first); + for (std::set<std::string>::const_iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) { + links->add_targets(*it2); + } + } + pbo->SerializeToString(&wire); + switch(obj->access_mode()) { + case IU_ACCESS_PUSH: + return "ipaaca-iu"; + case IU_ACCESS_MESSAGE: + return "ipaaca-messageiu"; + default: + //std::cout << "Requesting to send as default" << std::endl; + return getWireSchema(); + } + +} + +IPAACA_EXPORT AnnotatedData MessageConverter::deserialize(const std::string& wireSchema, const std::string& wire) { + assert(wireSchema == getWireSchema()); // "ipaaca-iu" + boost::shared_ptr<protobuf::IU> pbo(new protobuf::IU()); + pbo->ParseFromString(wire); + IUAccessMode mode = static_cast<IUAccessMode>(pbo->access_mode()); + switch(mode) { + case IU_ACCESS_PUSH: + { + // Create a "remote push IU" + boost::shared_ptr<RemotePushIU> obj = RemotePushIU::create(); + // transfer pbo data to obj + obj->_uid = pbo->uid(); + obj->_revision = pbo->revision(); + obj->_category = pbo->category(); + obj->_payload_type = pbo->payload_type(); + obj->_owner_name = pbo->owner_name(); + obj->_committed = pbo->committed(); + obj->_read_only = pbo->read_only(); + obj->_access_mode = IU_ACCESS_PUSH; + for (int i=0; i<pbo->payload_size(); i++) { + const protobuf::PayloadItem& it = pbo->payload(i); + PayloadDocumentEntry::ptr entry; + if (it.type() == "JSON") { + // fully parse json text + entry = PayloadDocumentEntry::from_json_string_representation( it.value() ); + } else { + // assuming legacy "str" -> just copy value to raw string in document + entry = std::make_shared<PayloadDocumentEntry>(); + entry->document.SetString(it.value(), entry->document.GetAllocator()); + } + obj->_payload._document_store[it.key()] = entry; + } + for (int i=0; i<pbo->links_size(); i++) { + const protobuf::LinkSet& pls = pbo->links(i); + LinkSet& ls = obj->_links._links[pls.type()]; + for (int j=0; j<pls.targets_size(); j++) { + ls.insert(pls.targets(j)); + } + } + //return std::make_pair(getDataType(), obj); + return std::make_pair("ipaaca::RemotePushIU", obj); + break; + } + case IU_ACCESS_MESSAGE: + { + // Create a "Message-type IU" + boost::shared_ptr<RemoteMessage> obj = RemoteMessage::create(); + // transfer pbo data to obj + obj->_uid = pbo->uid(); + obj->_revision = pbo->revision(); + obj->_category = pbo->category(); + obj->_payload_type = pbo->payload_type(); + obj->_owner_name = pbo->owner_name(); + obj->_committed = pbo->committed(); + obj->_read_only = pbo->read_only(); + obj->_access_mode = IU_ACCESS_MESSAGE; + for (int i=0; i<pbo->payload_size(); i++) { + const protobuf::PayloadItem& it = pbo->payload(i); + PayloadDocumentEntry::ptr entry; + if (it.type() == "JSON") { + // fully parse json text + entry = PayloadDocumentEntry::from_json_string_representation( it.value() ); + } else { + // assuming legacy "str" -> just copy value to raw string in document + entry = std::make_shared<PayloadDocumentEntry>(); + entry->document.SetString(it.value(), entry->document.GetAllocator()); + } + obj->_payload._document_store[it.key()] = entry; + } + for (int i=0; i<pbo->links_size(); i++) { + const protobuf::LinkSet& pls = pbo->links(i); + LinkSet& ls = obj->_links._links[pls.type()]; + for (int j=0; j<pls.targets_size(); j++) { + ls.insert(pls.targets(j)); + } + } + //return std::make_pair(getDataType(), obj); + return std::make_pair("ipaaca::RemoteMessage", obj); + break; + } + default: + // other cases not handled yet! ( TODO ) + throw NotImplementedError(); + } +} + +//}}} +// IUPayloadUpdateConverter//{{{ + +IPAACA_EXPORT IUPayloadUpdateConverter::IUPayloadUpdateConverter() +: Converter<std::string> (IPAACA_SYSTEM_DEPENDENT_CLASS_NAME("ipaaca::IUPayloadUpdate"), "ipaaca-iu-payload-update", true) +{ +} + +IPAACA_EXPORT std::string IUPayloadUpdateConverter::serialize(const AnnotatedData& data, std::string& wire) +{ + assert(data.first == getDataType()); // "ipaaca::IUPayloadUpdate" + boost::shared_ptr<const IUPayloadUpdate> obj = boost::static_pointer_cast<const IUPayloadUpdate> (data.second); + boost::shared_ptr<protobuf::IUPayloadUpdate> pbo(new protobuf::IUPayloadUpdate()); + // transfer obj data to pbo + pbo->set_uid(obj->uid); + pbo->set_revision(obj->revision); + pbo->set_writer_name(obj->writer_name); + pbo->set_is_delta(obj->is_delta); + for (auto& kv: obj->new_items) { + protobuf::PayloadItem* item = pbo->add_new_items(); + item->set_key(kv.first); + if (obj->payload_type=="JSON") { + item->set_value( kv.second->to_json_string_representation() ); + item->set_type("JSON"); + } else if ((obj->payload_type=="MAP") || (obj->payload_type=="STR")) { + // legacy mode + item->set_value( json_value_cast<std::string>(kv.second->document)); + item->set_type("STR"); + } else { + IPAACA_ERROR("Uninitialized payload update type!") + throw NotImplementedError(); + } + IPAACA_DEBUG("Adding updated item (type " << item->type() << "): " << item->key() << " -> " << item->value() ) + } + for (auto& key: obj->keys_to_remove) { + pbo->add_keys_to_remove(key); + IPAACA_DEBUG("Adding removed key: " << key) + } + pbo->SerializeToString(&wire); + return getWireSchema(); + +} + +AnnotatedData IUPayloadUpdateConverter::deserialize(const std::string& wireSchema, const std::string& wire) { + assert(wireSchema == getWireSchema()); // "ipaaca-iu-payload-update" + boost::shared_ptr<protobuf::IUPayloadUpdate> pbo(new protobuf::IUPayloadUpdate()); + pbo->ParseFromString(wire); + boost::shared_ptr<IUPayloadUpdate> obj(new IUPayloadUpdate()); + // transfer pbo data to obj + obj->uid = pbo->uid(); + obj->revision = pbo->revision(); + obj->writer_name = pbo->writer_name(); + obj->is_delta = pbo->is_delta(); + for (int i=0; i<pbo->new_items_size(); i++) { + const protobuf::PayloadItem& it = pbo->new_items(i); + PayloadDocumentEntry::ptr entry; + if (it.type() == "JSON") { + // fully parse json text + entry = PayloadDocumentEntry::from_json_string_representation( it.value() ); + IPAACA_INFO("New/updated payload entry: " << it.key() << " -> " << it.value() ) + } else { + // assuming legacy "str" -> just copy value to raw string in document + entry = std::make_shared<PayloadDocumentEntry>(); + entry->document.SetString(it.value(), entry->document.GetAllocator()); + } + obj->new_items[it.key()] = entry; + } + for (int i=0; i<pbo->keys_to_remove_size(); i++) { + obj->keys_to_remove.push_back(pbo->keys_to_remove(i)); + } + return std::make_pair(getDataType(), obj); +} + +//}}} +// IULinkUpdateConverter//{{{ + +IPAACA_EXPORT IULinkUpdateConverter::IULinkUpdateConverter() +: Converter<std::string> (IPAACA_SYSTEM_DEPENDENT_CLASS_NAME("ipaaca::IULinkUpdate"), "ipaaca-iu-link-update", true) +{ +} + +IPAACA_EXPORT std::string IULinkUpdateConverter::serialize(const AnnotatedData& data, std::string& wire) +{ + assert(data.first == getDataType()); // "ipaaca::IULinkUpdate" + boost::shared_ptr<const IULinkUpdate> obj = boost::static_pointer_cast<const IULinkUpdate> (data.second); + boost::shared_ptr<protobuf::IULinkUpdate> pbo(new protobuf::IULinkUpdate()); + // transfer obj data to pbo + pbo->set_uid(obj->uid); + pbo->set_revision(obj->revision); + pbo->set_writer_name(obj->writer_name); + pbo->set_is_delta(obj->is_delta); + for (std::map<std::string, std::set<std::string> >::const_iterator it=obj->new_links.begin(); it!=obj->new_links.end(); ++it) { + protobuf::LinkSet* links = pbo->add_new_links(); + links->set_type(it->first); + for (std::set<std::string>::const_iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) { + links->add_targets(*it2); + } + } + for (std::map<std::string, std::set<std::string> >::const_iterator it=obj->links_to_remove.begin(); it!=obj->links_to_remove.end(); ++it) { + protobuf::LinkSet* links = pbo->add_links_to_remove(); + links->set_type(it->first); + for (std::set<std::string>::const_iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) { + links->add_targets(*it2); + } + } + pbo->SerializeToString(&wire); + return getWireSchema(); + +} + +AnnotatedData IULinkUpdateConverter::deserialize(const std::string& wireSchema, const std::string& wire) { + assert(wireSchema == getWireSchema()); // "ipaaca-iu-link-update" + boost::shared_ptr<protobuf::IULinkUpdate> pbo(new protobuf::IULinkUpdate()); + pbo->ParseFromString(wire); + boost::shared_ptr<IULinkUpdate> obj(new IULinkUpdate()); + // transfer pbo data to obj + obj->uid = pbo->uid(); + obj->revision = pbo->revision(); + obj->writer_name = pbo->writer_name(); + obj->is_delta = pbo->is_delta(); + for (int i=0; i<pbo->new_links_size(); ++i) { + const protobuf::LinkSet& it = pbo->new_links(i); + for (int j=0; j<it.targets_size(); ++j) { + obj->new_links[it.type()].insert(it.targets(j)); // = vec; + } + } + for (int i=0; i<pbo->links_to_remove_size(); ++i) { + const protobuf::LinkSet& it = pbo->links_to_remove(i); + for (int j=0; j<it.targets_size(); ++j) { + obj->links_to_remove[it.type()].insert(it.targets(j)); + } + } + return std::make_pair(getDataType(), obj); +} + +//}}} +// IntConverter//{{{ + +IPAACA_EXPORT IntConverter::IntConverter() +: Converter<std::string> ("int", "int32", true) +{ +} + +IPAACA_EXPORT std::string IntConverter::serialize(const AnnotatedData& data, std::string& wire) +{ + // Ensure that DATA actually holds a datum of the data-type we expect. + assert(data.first == getDataType()); // "int" + // NOTE: a dynamic_pointer_cast cannot be used from void* + boost::shared_ptr<const int> obj = boost::static_pointer_cast<const int> (data.second); + boost::shared_ptr<protobuf::IntMessage> pbo(new protobuf::IntMessage()); + // transfer obj data to pbo + pbo->set_value(*obj); + pbo->SerializeToString(&wire); + return getWireSchema(); + +} + +IPAACA_EXPORT AnnotatedData IntConverter::deserialize(const std::string& wireSchema, const std::string& wire) { + assert(wireSchema == getWireSchema()); // "int" + boost::shared_ptr<protobuf::IntMessage> pbo(new protobuf::IntMessage()); + pbo->ParseFromString(wire); + boost::shared_ptr<int> obj = boost::shared_ptr<int>(new int(pbo->value())); + return std::make_pair("int", obj); +} + +//}}} + +} // of namespace ipaaca diff --git a/ipaacalib/cpp/src/ipaaca-iuinterface.cc b/ipaacalib/cpp/src/ipaaca-iuinterface.cc new file mode 100644 index 0000000000000000000000000000000000000000..2c36848cbc6c29df0fc794e31e2927f6689d289e --- /dev/null +++ b/ipaacalib/cpp/src/ipaaca-iuinterface.cc @@ -0,0 +1,156 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +#include <ipaaca/ipaaca.h> + +namespace ipaaca { + +using namespace rsb; +using namespace rsb::filter; +using namespace rsb::converter; +using namespace rsb::patterns; + +IPAACA_EXPORT std::ostream& operator<<(std::ostream& os, const IUInterface& obj)//{{{ +{ + os << "IUInterface(uid='" << obj.uid() << "'"; + os << ", category='" << obj.category() << "'"; + os << ", revision=" << obj.revision(); + os << ", committed=" << (obj.committed()?"True":"False"); + os << ", owner_name='" << obj.owner_name() << "'"; + os << ", payload="; + os << obj.const_payload(); + os << ", links="; + os << obj._links; + os << ")"; + return os; +} +//}}} + +// IUInterface//{{{ + +IPAACA_EXPORT IUInterface::IUInterface() +: _buffer(NULL), _committed(false), _retracted(false) +{ +} + +IPAACA_EXPORT void IUInterface::_set_uid(const std::string& uid) { + if (_uid != "") { + throw IUAlreadyHasAnUIDError(); + } + _uid = uid; +} + +IPAACA_EXPORT void IUInterface::_set_buffer(Buffer* buffer) { //boost::shared_ptr<Buffer> buffer) { + if (_buffer) { + throw IUAlreadyInABufferError(); + } + _buffer = buffer; + +} + +IPAACA_EXPORT void IUInterface::_set_owner_name(const std::string& owner_name) { + if (_owner_name != "") { + throw IUAlreadyHasAnOwnerNameError(); + } + _owner_name = owner_name; +} + +/// set the buffer pointer and the owner names of IU and Payload +IPAACA_EXPORT void IUInterface::_associate_with_buffer(Buffer* buffer) { //boost::shared_ptr<Buffer> buffer) { + _set_buffer(buffer); // will throw if already set + _set_owner_name(buffer->unique_name()); + payload()._set_owner_name(buffer->unique_name()); +} + +/// C++-specific convenience function to add one single link +IPAACA_EXPORT void IUInterface::add_link(const std::string& type, const std::string& target, const std::string& writer_name) +{ + LinkMap none; + LinkMap add; + add[type].insert(target); + _modify_links(true, add, none, writer_name); + _add_and_remove_links(add, none); +} +/// C++-specific convenience function to remove one single link +IPAACA_EXPORT void IUInterface::remove_link(const std::string& type, const std::string& target, const std::string& writer_name) +{ + LinkMap none; + LinkMap remove; + remove[type].insert(target); + _modify_links(true, none, remove, writer_name); + _add_and_remove_links(none, remove); +} + +IPAACA_EXPORT void IUInterface::add_links(const std::string& type, const LinkSet& targets, const std::string& writer_name) +{ + LinkMap none; + LinkMap add; + add[type] = targets; + _modify_links(true, add, none, writer_name); + _add_and_remove_links(add, none); +} + +IPAACA_EXPORT void IUInterface::remove_links(const std::string& type, const LinkSet& targets, const std::string& writer_name) +{ + LinkMap none; + LinkMap remove; + remove[type] = targets; + _modify_links(true, none, remove, writer_name); + _add_and_remove_links(none, remove); +} + +IPAACA_EXPORT void IUInterface::modify_links(const LinkMap& add, const LinkMap& remove, const std::string& writer_name) +{ + _modify_links(true, add, remove, writer_name); + _add_and_remove_links(add, remove); +} + +IPAACA_EXPORT void IUInterface::set_links(const LinkMap& links, const std::string& writer_name) +{ + LinkMap none; + _modify_links(false, links, none, writer_name); + _replace_links(links); +} + +IPAACA_HEADER_EXPORT const std::string& IUInterface::channel() +{ + if (_buffer == NULL) + throw IUUnpublishedError(); + else + return _buffer->channel(); + +} + +//}}} + +} // of namespace ipaaca diff --git a/ipaacalib/cpp/src/ipaaca-ius.cc b/ipaacalib/cpp/src/ipaaca-ius.cc new file mode 100644 index 0000000000000000000000000000000000000000..07caeb65b2ed42aa132b265f75113c3e8bb6a87a --- /dev/null +++ b/ipaacalib/cpp/src/ipaaca-ius.cc @@ -0,0 +1,370 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +#include <ipaaca/ipaaca.h> + +namespace ipaaca { + +using namespace rsb; +using namespace rsb::filter; +using namespace rsb::converter; +using namespace rsb::patterns; + +// IU//{{{ +IPAACA_EXPORT IU::ptr IU::create(const std::string& category, IUAccessMode access_mode, bool read_only, const std::string& payload_type) +{ + return IU::create(category, payload_type, read_only); +} +IPAACA_EXPORT IU::ptr IU::create(const std::string& category, const std::string& payload_type, bool read_only) +{ + IU::ptr iu = IU::ptr(new IU(category, IU_ACCESS_PUSH, read_only, (payload_type=="")?__ipaaca_static_option_default_payload_type:payload_type)); /* params */ //)); + iu->_payload.initialize(iu); + return iu; +} + +IPAACA_EXPORT IU::IU(const std::string& category, IUAccessMode access_mode, bool read_only, const std::string& payload_type) +{ + _revision = 1; + _uid = ipaaca::generate_uuid_string(); + _category = category; + _payload_type = (payload_type=="")?__ipaaca_static_option_default_payload_type:payload_type; + // payload initialization deferred to IU::create(), above + _read_only = read_only; + _access_mode = access_mode; + _committed = false; +} + +IPAACA_EXPORT void IU::_modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name) +{ + _revision_lock.lock(); + if (_committed) { + _revision_lock.unlock(); + throw IUCommittedError(); + } + _increase_revision_number(); + if (is_published()) { + _buffer->_send_iu_link_update(this, is_delta, _revision, new_links, links_to_remove, writer_name); + } + _revision_lock.unlock(); +} + + +/* + * IPAACA_EXPORT void IU::_publish_resend(IU::ptr iu, const std::string& hidden_scope_name) +{ + //_revision_lock.lock(); + //if (_committed) { + // _revision_lock.unlock(); + // throw IUCommittedError(); + //} + //_increase_revision_number(); + //if (is_published()) { + //IUInterface* iu, bool is_delta, revision_t revision, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name + _buffer->_publish_iu_resend(iu, hidden_scope_name); + //} + //_revision_lock.unlock(); +} +*/ + + + + +IPAACA_EXPORT void IU::_modify_payload(bool is_delta, const std::map<std::string, PayloadDocumentEntry::ptr>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name) +{ + IPAACA_INFO("") + _revision_lock.lock(); + if (_committed) { + _revision_lock.unlock(); + throw IUCommittedError(); + } + _increase_revision_number(); + if (is_published()) { + IPAACA_DEBUG("Sending a payload update, new entries:") + for (auto& kv: new_items) { + IPAACA_DEBUG(" " << kv.first << " -> " << kv.second) + } + IPAACA_DEBUG("and with removed keys:") + for (auto& k: keys_to_remove) { + IPAACA_DEBUG(" " << k) + } + _buffer->_send_iu_payload_update(this, is_delta, _revision, new_items, keys_to_remove, writer_name); + IPAACA_DEBUG("... sent.") + } + _revision_lock.unlock(); +} + +IPAACA_EXPORT void IU::commit() +{ + _internal_commit(); +} + +IPAACA_EXPORT void IU::_internal_commit(const std::string& writer_name) +{ + _revision_lock.lock(); + if (_committed) { + _revision_lock.unlock(); + throw IUCommittedError(); + } + _increase_revision_number(); + _committed = true; + if (is_published()) { + _buffer->_send_iu_commission(this, _revision, writer_name); + } + _revision_lock.unlock(); +} +//}}} +// Message//{{{ +Message::ptr Message::create(const std::string& category, IUAccessMode access_mode, bool read_only, const std::string& payload_type) +{ + return Message::create(category, (payload_type=="")?__ipaaca_static_option_default_payload_type:payload_type); +} +Message::ptr Message::create(const std::string& category, const std::string& payload_type) +{ + Message::ptr iu = Message::ptr(new Message(category, IU_ACCESS_MESSAGE, true, (payload_type=="")?__ipaaca_static_option_default_payload_type:payload_type)); /* params */ //)); + iu->_payload.initialize(iu); + return iu; +} + +Message::Message(const std::string& category, IUAccessMode access_mode, bool read_only, const std::string& payload_type) +: IU(category, access_mode, read_only, payload_type) +{ +} + +void Message::_modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name) +{ + if (is_published()) { + IPAACA_INFO("Info: modifying a Message after sending has no global effects") + } +} +void Message::_modify_payload(bool is_delta, const std::map<std::string, PayloadDocumentEntry::ptr>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name) +{ + if (is_published()) { + IPAACA_INFO("Info: modifying a Message after sending has no global effects") + } +} + +void Message::_internal_commit(const std::string& writer_name) +{ + if (is_published()) { + IPAACA_INFO("Info: committing to a Message after sending has no global effects") + } + +} +//}}} + +// RemotePushIU//{{{ + +IPAACA_EXPORT RemotePushIU::ptr RemotePushIU::create() +{ + RemotePushIU::ptr iu = RemotePushIU::ptr(new RemotePushIU(/* params */)); + iu->_payload.initialize(iu); + return iu; +} +IPAACA_EXPORT RemotePushIU::RemotePushIU() +{ + // nothing +} +IPAACA_EXPORT void RemotePushIU::_modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name) +{ + if (_committed) { + throw IUCommittedError(); + } + if (_read_only) { + throw IUReadOnlyError(); + } + RemoteServerPtr server = boost::static_pointer_cast<InputBuffer>(_buffer)->_get_remote_server(_owner_name); + IULinkUpdate::ptr update = IULinkUpdate::ptr(new IULinkUpdate()); + update->uid = _uid; + update->revision = _revision; + update->is_delta = is_delta; + update->writer_name = _buffer->unique_name(); + update->new_links = new_links; + update->links_to_remove = links_to_remove; + boost::shared_ptr<int> result = server->call<int>("updateLinks", update, IPAACA_REMOTE_SERVER_TIMEOUT); // TODO + if (*result == 0) { + throw IUUpdateFailedError(); + } else { + _revision = *result; + } +} +IPAACA_EXPORT void RemotePushIU::_modify_payload(bool is_delta, const std::map<std::string, PayloadDocumentEntry::ptr>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name) +{ + //std::cout << "-- Sending a modify_payload with " << new_items.size() << " keys to merge." << std::endl; + if (_committed) { + throw IUCommittedError(); + } + if (_read_only) { + throw IUReadOnlyError(); + } + RemoteServerPtr server = boost::static_pointer_cast<InputBuffer>(_buffer)->_get_remote_server(_owner_name); + IUPayloadUpdate::ptr update = IUPayloadUpdate::ptr(new IUPayloadUpdate()); + update->uid = _uid; + update->revision = _revision; + update->is_delta = is_delta; + update->writer_name = _buffer->unique_name(); + update->new_items = new_items; + update->keys_to_remove = keys_to_remove; + update->payload_type = _payload_type; + boost::shared_ptr<int> result = server->call<int>("updatePayload", update, IPAACA_REMOTE_SERVER_TIMEOUT); // TODO + if (*result == 0) { + throw IUUpdateFailedError(); + } else { + _revision = *result; + } +} + +IPAACA_EXPORT void RemotePushIU::commit() +{ + if (_read_only) { + throw IUReadOnlyError(); + } + if (_committed) { + // Following python version: ignoring multiple commit + return; + } + RemoteServerPtr server = boost::static_pointer_cast<InputBuffer>(_buffer)->_get_remote_server(_owner_name); + boost::shared_ptr<protobuf::IUCommission> update = boost::shared_ptr<protobuf::IUCommission>(new protobuf::IUCommission()); + update->set_uid(_uid); + update->set_revision(_revision); + update->set_writer_name(_buffer->unique_name()); + boost::shared_ptr<int> result = server->call<int>("commit", update, IPAACA_REMOTE_SERVER_TIMEOUT); // TODO + if (*result == 0) { + throw IUUpdateFailedError(); + } else { + _revision = *result; + } +} + +IPAACA_EXPORT void RemotePushIU::_apply_link_update(IULinkUpdate::ptr update) +{ + _revision = update->revision; + if (update->is_delta) { + _add_and_remove_links(update->new_links, update->links_to_remove); + } else { + _replace_links(update->new_links); + } +} +IPAACA_EXPORT void RemotePushIU::_apply_update(IUPayloadUpdate::ptr update) +{ + _revision = update->revision; + if (update->is_delta) { + for (std::vector<std::string>::const_iterator it=update->keys_to_remove.begin(); it!=update->keys_to_remove.end(); ++it) { + _payload._remotely_enforced_delitem(*it); + } + for (std::map<std::string, PayloadDocumentEntry::ptr>::const_iterator it=update->new_items.begin(); it!=update->new_items.end(); ++it) { + _payload._remotely_enforced_setitem(it->first, it->second); + } + } else { + _payload._remotely_enforced_wipe(); + for (std::map<std::string, PayloadDocumentEntry::ptr>::const_iterator it=update->new_items.begin(); it!=update->new_items.end(); ++it) { + _payload._remotely_enforced_setitem(it->first, it->second); + } + } +} +IPAACA_EXPORT void RemotePushIU::_apply_commission() +{ + _committed = true; +} +IPAACA_EXPORT void RemotePushIU::_apply_retraction() +{ + _retracted = true; +} +//}}} + +// RemoteMessage//{{{ + +IPAACA_EXPORT RemoteMessage::ptr RemoteMessage::create() +{ + RemoteMessage::ptr iu = RemoteMessage::ptr(new RemoteMessage(/* params */)); + iu->_payload.initialize(iu); + return iu; +} +IPAACA_EXPORT RemoteMessage::RemoteMessage() +{ + // nothing +} +IPAACA_EXPORT void RemoteMessage::_modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name) +{ + IPAACA_INFO("Info: modifying a RemoteMessage only has local effects") +} +IPAACA_EXPORT void RemoteMessage::_modify_payload(bool is_delta, const std::map<std::string, PayloadDocumentEntry::ptr>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name) +{ + IPAACA_INFO("Info: modifying a RemoteMessage only has local effects") +} +IPAACA_EXPORT void RemoteMessage::commit() +{ + IPAACA_INFO("Info: committing to a RemoteMessage only has local effects") +} + +IPAACA_EXPORT void RemoteMessage::_apply_link_update(IULinkUpdate::ptr update) +{ + IPAACA_WARNING("Warning: should never be called: RemoteMessage::_apply_link_update") + _revision = update->revision; + if (update->is_delta) { + _add_and_remove_links(update->new_links, update->links_to_remove); + } else { + _replace_links(update->new_links); + } +} +IPAACA_EXPORT void RemoteMessage::_apply_update(IUPayloadUpdate::ptr update) +{ + IPAACA_WARNING("Warning: should never be called: RemoteMessage::_apply_update") + _revision = update->revision; + if (update->is_delta) { + for (std::vector<std::string>::const_iterator it=update->keys_to_remove.begin(); it!=update->keys_to_remove.end(); ++it) { + _payload._remotely_enforced_delitem(*it); + } + for (std::map<std::string, PayloadDocumentEntry::ptr>::const_iterator it=update->new_items.begin(); it!=update->new_items.end(); ++it) { + _payload._remotely_enforced_setitem(it->first, it->second); + } + } else { + _payload._remotely_enforced_wipe(); + for (std::map<std::string, PayloadDocumentEntry::ptr>::const_iterator it=update->new_items.begin(); it!=update->new_items.end(); ++it) { + _payload._remotely_enforced_setitem(it->first, it->second); + } + } +} +IPAACA_EXPORT void RemoteMessage::_apply_commission() +{ + IPAACA_WARNING("Warning: should never be called: RemoteMessage::_apply_commission") + _committed = true; +} +IPAACA_EXPORT void RemoteMessage::_apply_retraction() +{ + IPAACA_WARNING("Warning: should never be called: RemoteMessage::_apply_retraction") + _retracted = true; +} + +//}}} + +} // of namespace ipaaca diff --git a/ipaacalib/cpp/src/ipaaca-json.cc b/ipaacalib/cpp/src/ipaaca-json.cc new file mode 100644 index 0000000000000000000000000000000000000000..dfdd83ee63adc0603c0f9340cd017d33bd14f9e7 --- /dev/null +++ b/ipaacalib/cpp/src/ipaaca-json.cc @@ -0,0 +1,500 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +#include <ipaaca/ipaaca.h> +#include <ipaaca/ipaaca-json.h> + +#include <iomanip> + +using namespace rapidjson; +using namespace std; + +int batch_update_main(int argc, char** argv)//{{{ +{ + std::string json_source("[\n\ + \"old\",\n\ + [\n\ + \"str\",\n\ + null\n\ + ],\n\ + 3,\n\ + {\n\ + \"key1\": \"value1\",\n\ + \"key2\": \"value2\"\n\ + }\n\ +]"); + + ipaaca::OutputBuffer::ptr ob = ipaaca::OutputBuffer::create("myprog"); + std::cout << std::endl << "Setting up an IU with initial contents" << std::endl; + ipaaca::IU::ptr iu = ipaaca::IU::create("testcategory"); + iu->payload()["a"] = "OLD: initial contents of payload"; + iu->payload()["b"] = "OLD: initial value for b"; + std::cout << std::endl << "Initial contents of payload:" << std::endl; + for (auto it: iu->payload()) { + std::cout << " " << it.first << " -> " << it.second << std::endl; + } + + std::cout << std::endl << "Publishing IU (sniffer should receive one ADDED)" << std::endl; + ob->add(iu); + + std::cout << std::endl << "Batch-writing some stuff (sniffer should receive a single UPDATED)" << std::endl; + { + ipaaca::Locker locker(iu->payload()); + iu->payload().set(std::map<std::string, std::string>{{"b", "VALUE"}, {"bPrime", "VALUE"}}); + iu->payload()["a"] = std::map<std::string, long>{{"a", 1},{"b", 2},{"c", 3}}; + iu->payload()["remove_me"] = "WARNING: this should not be in the payload where an update is received!"; + iu->payload()["c"] = "WARNING: this should read abc123, not this warning message!"; + iu->payload()["d"] = 100; + iu->payload().remove("d"); + iu->payload()["d"] = 200; + iu->payload()["d"] = 300; + iu->payload().remove("d"); + iu->payload()["d"] = 400; + iu->payload()["e"] = "Note: a key 'd' should exist with value 400, and 'b' and 'bPrime' should be equal"; + iu->payload()["f"] = "12.5000"; + iu->payload()["g"] = std::vector<std::string>{"g1", "g2"}; + iu->payload().remove("remove_me"); + iu->payload()["c"] = "abc123"; + } + + std::cout << std::endl << "Adding another key 'XYZ' outside batch mode (sniffer -> UPDATED)" << std::endl; + iu->payload()["XYZ"] = "blabla"; + + std::cout << std::endl << "Final batch update, wiping most (sniffer should receive a third UPDATED, with 3 keys remaining in the payload)" << std::endl; + { + ipaaca::Locker locker(iu->payload()); + iu->payload()["SHOULD_NOT_EXIST"] = "WARNING: this key should never be visible"; + iu->payload().set(std::map<std::string, std::string>{{"A", "Final contents (3 entries)"}, {"B", "Final stuff (3 entries)"}}); + iu->payload()["C"] = std::vector<std::string>{"payload ", "should ", "have ", "three ", "entries, ", "A ", "B ", "and ", "C"}; + } + + std::cout << std::endl << "Final contents of payload:" << std::endl; + for (auto it: iu->payload()) { + std::cout << " " << it.first << " -> " << it.second << std::endl; + } + + return 0; +} +//}}} + +int iterators_main(int argc, char** argv)//{{{ +{ + std::string json_source("[\n\ + \"old\",\n\ + [\n\ + \"str\",\n\ + null\n\ + ],\n\ + 3,\n\ + {\n\ + \"key1\": \"value1\",\n\ + \"key2\": \"value2\"\n\ + }\n\ +]"); + + std::cout << "Using this JSON document as initial payload entry 'a':" << std::endl << json_source << std::endl; + ipaaca::PayloadDocumentEntry::ptr entry = ipaaca::PayloadDocumentEntry::from_json_string_representation(json_source); + + std::cout << std::endl << "Setting up payload by adding some additional values" << std::endl; + ipaaca::FakeIU::ptr iu = ipaaca::FakeIU::create(); + iu->add_fake_payload_item("a", entry); + iu->payload()["b"] = "simpleString"; + iu->payload()["bPrime"] = "simpleString"; + iu->payload()["c"] = "anotherSimpleString"; + iu->payload()["d"] = 100; + iu->payload()["e"] = 3l; + iu->payload()["f"] = "12.5000"; + iu->payload()["g"] = std::vector<std::string>{"g1", "g2"}; + + std::cout << std::endl << "Iterate over payload" << std::endl; + for (auto it = iu->payload().begin(); it != iu->payload().end(); ++it) { + std::cout << " " << it->first << " -> " << it->second << std::endl; + } + std::cout << std::endl << "Iterate over payload, range-based" << std::endl; + for (auto it: iu->payload()) { + std::cout << " " << it.first << " -> " << it.second << std::endl; + } + + std::cout << std::endl << "Comparisons" << std::endl; + bool eq; + eq = iu->payload()["a"] == iu->payload()["b"]; + std::cout << " a==b ? : " << (eq?"true":"false") << std::endl; + eq = iu->payload()["b"] == iu->payload()["bPrime"]; + std::cout << " b==bPrime ? : " << (eq?"true":"false") << std::endl; + eq = iu->payload()["b"] == "simpleString"; + std::cout << " b==\"simpleString\" ? : " << (eq?"true":"false") << std::endl; + eq = iu->payload()["b"] == 100; + std::cout << " b==100 ? : " << (eq?"true":"false") << std::endl; + eq = iu->payload()["d"] == 100; + std::cout << " d==100 ? : " << (eq?"true":"false") << std::endl; + eq = iu->payload()["a"][2] == iu->payload()["e"]; + std::cout << " a[2]==e ? : " << (eq?"true":"false") << std::endl; + + std::cout << std::endl << "Type checks" << std::endl; + std::cout << " a[3] is_null() ? : " << ((iu->payload()["a"][3].is_null())?"true":"false") << std::endl; + std::cout << " a[3] is_string() ? : " << ((iu->payload()["a"][3].is_string())?"true":"false") << std::endl; + std::cout << " a[3] is_number() ? : " << ((iu->payload()["a"][3].is_number())?"true":"false") << std::endl; + std::cout << " a[3] is_list() ? : " << ((iu->payload()["a"][3].is_list())?"true":"false") << std::endl; + std::cout << " a[3] is_map() ? : " << ((iu->payload()["a"][3].is_map())?"true":"false") << std::endl; + std::cout << std::endl; + std::cout << " f is_null() ? : " << ((iu->payload()["f"].is_null())?"true":"false") << std::endl; + std::cout << " f is_string() ? : " << ((iu->payload()["f"].is_string())?"true":"false") << std::endl; + std::cout << " f is_number() ? : " << ((iu->payload()["f"].is_number())?"true":"false") << std::endl; + std::cout << " f is_list() ? : " << ((iu->payload()["f"].is_list())?"true":"false") << std::endl; + std::cout << " f is_map() ? : " << ((iu->payload()["f"].is_map())?"true":"false") << std::endl; + + std::cout << std::endl << "Inner iterators, map (printing values as strings)" << std::endl; + try { + auto inner = iu->payload()["a"][3]; + std::cout << "Map iteration over payload['a'][3], which equals " << inner << std::endl; + std::cout << "Reported size is " << inner.size() << std::endl; + for (auto kv: inner.as_map()) { + std::cout << " \"" << kv.first << "\" -> \"" << kv.second << "\"" << std::endl; + } + } catch (ipaaca::Exception& ex) { + std::cout << " Unexpected exception: " << ex.what() << std::endl; + } + try { + auto inner = iu->payload()["a"][2]; + std::cout << "Map iteration over payload['a'][2], which equals " << inner << std::endl; + std::cout << "Reported size is " << inner.size() << std::endl; + for (auto kv: inner.as_map()) { + std::cout << " \"" << kv.first << "\" -> \"" << kv.second << "\"" << std::endl; + } + } catch (ipaaca::PayloadTypeConversionError& ex) { + std::cout << " Failed as expected with " << ex.what() << std::endl; + } catch (ipaaca::Exception& ex) { + std::cout << " Unexpected exception: " << ex.what() << std::endl; + } + + std::cout << std::endl << "Inner iterators, list (printing values as strings)" << std::endl; + try { + auto inner = iu->payload()["a"][1]; + std::cout << "List iteration over payload['a'][1], which equals " << inner << std::endl; + std::cout << "Reported size is " << inner.size() << std::endl; + for (auto proxy: inner.as_list()) { + std::cout << " \"" << proxy << "\"" << std::endl; + } + } catch (ipaaca::Exception& ex) { + std::cout << " Unexpected exception: " << ex.what() << std::endl; + } + try { + auto inner = iu->payload()["a"][1][1]; + std::cout << "List iteration over payload['a'][1][1], which equals " << inner << std::endl; + std::cout << "Reported size is " << inner.size() << std::endl; + for (auto proxy: inner.as_list()) { + std::cout << " \"" << proxy << "\"" << std::endl; + } + } catch (ipaaca::PayloadTypeConversionError& ex) { + std::cout << " Failed as expected with " << ex.what() << std::endl; + } catch (ipaaca::Exception& ex) { + std::cout << " Unexpected exception: " << ex.what() << std::endl; + } + + std::cout << std::endl << "Appending a string item to the end of payload['a']" << std::endl; + iu->payload()["a"].push_back("appended string entry"); + std::cout << "Resulting entries in payload['a']:" << std::endl; + for (auto v: iu->payload()["a"].as_list()) { + std::cout << " " << v << std::endl; + } + + std::cout << std::endl << "Extending payload['a'] by a list of three bools" << std::endl; + iu->payload()["a"].extend(std::list<bool>{false, false, true}); + std::cout << "Resulting entries in payload['a']:" << std::endl; + for (auto v: iu->payload()["a"].as_list()) { + std::cout << " " << v << std::endl; + } + + std::cout << std::endl << "Extending payload['a'] by payload['g'] and appending payload['f']" << std::endl; + iu->payload()["a"].extend(iu->payload()["g"]); + iu->payload()["a"].push_back(iu->payload()["f"]); + std::cout << "Resulting entries in payload['a']:" << std::endl; + for (auto v: iu->payload()["a"].as_list()) { + std::cout << " " << v << std::endl; + } + + return 0; +} +//}}} + +int json_testbed_main(int argc, char** argv)//{{{ +{ + std::string json_source("[\"old\",2,3,4]"); + ipaaca::PayloadDocumentEntry::ptr entry = ipaaca::PayloadDocumentEntry::from_json_string_representation(json_source); + + std::string newinner("{\"K\":\"V\"}"); + ipaaca::PayloadDocumentEntry::ptr entrynew = ipaaca::PayloadDocumentEntry::from_json_string_representation(newinner); + + ipaaca::FakeIU::ptr iu = ipaaca::FakeIU::create(); + iu->add_fake_payload_item("a", entry); + iu->add_fake_payload_item("b", entrynew); + iu->payload()["c"] = "simpleString"; + + auto proxy = iu->payload()["a"][3]; + + std::cout << "IU payload before: " << iu->payload() << std::endl; + std::cout << "Entry before: " << entry << std::endl; + std::cout << "EntryNew before: " << entrynew << std::endl; + + /* + proxy.json_value->CopyFrom(entrynew->document, proxy.document_entry->document.GetAllocator()); + proxy.document_entry->update_json_source(); + */ + proxy = iu->payload()["b"]; + + std::cout << "Newly written part: " << iu->payload()["a"][3] << std::endl; + iu->payload()["a"][3]["addkey"] = "addvalue"; + + std::cout << "IU payload after: " << iu->payload() << std::endl; + std::cout << "Entry after: " << entry << std::endl; + std::cout << "EntryNew after: " << entrynew << std::endl; + + return 0; +} +//}}} + +int fakeiu_main(int argc, char** argv)//{{{ +{ + //if (argc<2) { + // std::cout << "Please provide json content as the first argument." << std::endl; + // return 0; + //} + // + std::string json_source("[\"old\",2,3,4]"); + ipaaca::PayloadDocumentEntry::ptr entry = ipaaca::PayloadDocumentEntry::from_json_string_representation(json_source); + + ipaaca::FakeIU::ptr iu = ipaaca::FakeIU::create(); + iu->add_fake_payload_item("a", entry); + iu->payload()["b"] = "anotherValue"; + iu->payload()["c"] = "yetAnotherValue"; + + auto a = iu->payload()["a"]; + //auto a0 = a[0]; + std::cout << "entry as string: " << (std::string) a << std::endl; + std::cout << "entry as long: " << (long) a << std::endl; + std::cout << "entry as double: " << (double) a << std::endl; + std::cout << "entry as bool: " << ((bool) a?"true":"false") << std::endl; + // std::vector + std::cout << "entry as vector<string>: "; + try { + std::vector<std::string> v = a; + std::for_each(v.begin(), v.end(), [](std::string& s) { + std::cout << s << " "; + }); + std::cout << std::endl; + } catch (...) { + std::cout << "(n/a)" << std::endl; + } + std::cout << "entry as vector<long>: "; + try { + std::vector<long> v = a; + std::for_each(v.begin(), v.end(), [](long& s) { + std::cout << s << " "; + }); + std::cout << std::endl; + } catch (...) { + std::cout << "(n/a)" << std::endl; + } + std::cout << "entry as vector<bool>: "; + try { + std::vector<bool> v = a; + std::for_each(v.begin(), v.end(), [](bool s) { + std::cout << (s?"true":"false") << " "; + }); + std::cout << std::endl; + } catch (...) { + std::cout << "(n/a)" << std::endl; + } + // std::map + std::cout << "entry as map<string, string>: "; + try { + std::map<std::string, std::string> m = a; + for (auto it = m.begin(); it != m.end(); ++it) { + std::cout << it->first << ":" << it->second << " "; + } + std::cout << std::endl; + } catch (...) { + std::cout << "(n/a)" << std::endl; + } + std::cout << "entry as map<string, long>: "; + try { + std::map<std::string, long> m = a; + for (auto it = m.begin(); it != m.end(); ++it) { + std::cout << it->first << ":" << it->second << " "; + } + std::cout << std::endl; + } catch (...) { + std::cout << "(n/a)" << std::endl; + } + std::cout << "entry as map<string, double>: "; + try { + std::map<std::string, double> m = a; + for (auto it = m.begin(); it != m.end(); ++it) { + std::cout << it->first << ":" << it->second << " "; + } + std::cout << std::endl; + } catch (...) { + std::cout << "(n/a)" << std::endl; + } + std::cout << "entry as map<string, bool>: "; + try { + std::map<std::string, bool> m = a; + for (auto it = m.begin(); it != m.end(); ++it) { + std::cout << it->first << ":" << (it->second?"true":"false") << " "; + } + std::cout << std::endl; + } catch (...) { + std::cout << "(n/a)" << std::endl; + } + + std::cout << "Setting value [0] in the object:" << std::endl; + try { + iu->payload()["a"][0] = "CHANGED_BY_USER"; + } catch (ipaaca::PayloadAddressingError& e) { + std::cout << " Error - the provided object was not a suitable array" << std::endl; + } + //iu->payload()["a"]["A"] = "set by pep::op="; + + + std::cout << "Appending two words to key 'b' the currently wrong way:" << std::endl; + auto proxy = iu->payload()["b"]; + proxy = (std::string) proxy + " WORD1"; + proxy = (std::string) proxy + " WORD2"; + + std::cout << "Appending two words to key 'c' the compatible way:" << std::endl; + iu->payload()["c"] = (std::string) iu->payload()["c"] + " WORD1"; + iu->payload()["c"] = (std::string) iu->payload()["c"] + " WORD2"; + + std::cout << "Printing final payload using PayloadIterator:" << std::endl; + for (auto it = iu->payload().begin(); it != iu->payload().end(); ++it) { + std::cout << " " << std::left << std::setw(15) << ((*it).first+": ") << (*it).second << std::endl; + } + + std::cout << "Final payload (cast to map, printed as strings):" << std::endl; + std::map<std::string, std::string> pl_flat = iu->payload(); + for (auto& kv: pl_flat) { + std::cout << " " << std::left << std::setw(15) << (kv.first+": ") << kv.second << std::endl; + } + return 0; +} +//}}} + +int legacy_iu_main(int argc, char** argv)//{{{ +{ + // produce and fill a new and a legacy IU with identical contents + + ipaaca::OutputBuffer::ptr ob = ipaaca::OutputBuffer::create("jsonTestSenderLegacy"); + ob->register_handler([](ipaaca::IUInterface::ptr iu, ipaaca::IUEventType event_type, bool local) { + std::cout << "Received remote update, new payload: " << iu->payload() << std::endl; + }); + std::cout << "--- Create IUs with category jsonTest" << std::endl; + ipaaca::IU::ptr iu1 = ipaaca::IU::create("jsonTest"); + ipaaca::IU::ptr iu2 = ipaaca::IU::create("jsonTest", "STR"); // explicity request old payload + std::map<std::string, long> newmap = { {"fifty", 50}, {"ninety-nine", 99} }; + std::cout << "--- Set map" << std::endl; + iu1->payload()["map"] = newmap; + iu1->payload()["array"] = std::vector<std::string>{"aaa", "bbb", "ccc"}; + iu2->payload()["map"] = newmap; + iu2->payload()["array"] = std::vector<std::string>{"aaa", "bbb", "ccc"}; + std::cout << "--- Publishing IUs with this payload:" << std::endl; + std::cout << iu1->payload() << std::endl; + ob->add(iu1); + ob->add(iu2); + std::cout << "--- Waiting for changes for 5s " << std::endl; + sleep(5); + return 0; +} +//}}} + +int iu_main(int argc, char** argv)//{{{ +{ + ipaaca::InputBuffer::ptr ib = ipaaca::InputBuffer::create("jsonTestReceiver", "jsonTest"); + ib->register_handler([](ipaaca::IUInterface::ptr iu, ipaaca::IUEventType event_type, bool local) { + if (event_type==IU_ADDED) { + std::cout << "Received a new IU, payload: " << iu->payload() << std::endl; + std::cout << "Will write something." << std::endl; + //iu->commit(); + try { + iu->payload()["list"][0] = "Overridden from C++"; + } catch (ipaaca::PayloadAddressingError& e) { + iu->payload()["newKey"] = std::vector<long>{2,4,6,8}; + std::cout << " (item ['list'][0] could not be addressed, wrote new key)" << std::endl; + } + } + }); + std::cout << "--- Waiting for IUs for 10s " << std::endl; + sleep(10); + return 0; + + ipaaca::OutputBuffer::ptr ob = ipaaca::OutputBuffer::create("jsonTestSender"); + ob->register_handler([](ipaaca::IUInterface::ptr iu, ipaaca::IUEventType event_type, bool local) { + std::cout << "Received remote update, new payload: " << iu->payload() << std::endl; + }); + std::cout << "--- Create IU with category jsonTest" << std::endl; + ipaaca::IU::ptr iu = ipaaca::IU::create("jsonTest"); + std::map<std::string, long> newmap = { {"fifty", 50}, {"ninety-nine", 99} }; + std::cout << "--- Set map" << std::endl; + iu->payload()["map"] = newmap; + std::cout << "--- Publishing IU with this payload:" << std::endl; + std::cout << iu->payload() << std::endl; + ob->add(iu); + std::cout << "--- Waiting for changes for 5s before next write" << std::endl; + sleep(5); + std::cout << "--- Contents of map after 5s" << std::endl; + std::cout << iu->payload()["map"] << std::endl; + // + std::cout << "--- Creating a list" << std::endl; + iu->payload()["list"] = std::vector<long>{1, 0} ; + std::cout << "--- Waiting for changes for 5s " << std::endl; + sleep(5); + std::cout << "--- Final map " << std::endl; + std::cout << iu->payload()["map"] << std::endl; + std::cout << "--- Final list " << std::endl; + std::cout << iu->payload()["list"] << std::endl; + std::cout << "--- Terminating " << std::endl; + return 0; +} +//}}} + +int main(int argc, char** argv) +{ + ipaaca::CommandLineParser::ptr parser = ipaaca::CommandLineParser::create(); + ipaaca::CommandLineOptions::ptr options = parser->parse(argc, argv); + + return batch_update_main(argc, argv); + //return iterators_main(argc, argv); + //return json_testbed_main(argc, argv); + //return legacy_iu_main(argc, argv); + //return fakeiu_main(argc, argv); + //return iu_main(argc, argv); +} diff --git a/ipaacalib/cpp/src/ipaaca-links.cc b/ipaacalib/cpp/src/ipaaca-links.cc new file mode 100644 index 0000000000000000000000000000000000000000..e0a6229068126ee2004768a040a4327efe8a32b4 --- /dev/null +++ b/ipaacalib/cpp/src/ipaaca-links.cc @@ -0,0 +1,106 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +#include <ipaaca/ipaaca.h> + +namespace ipaaca { + +using namespace rsb; +using namespace rsb::filter; +using namespace rsb::converter; +using namespace rsb::patterns; + +IPAACA_EXPORT std::ostream& operator<<(std::ostream& os, const SmartLinkMap& obj)//{{{ +{ + os << "{"; + bool first = true; + for (LinkMap::const_iterator it=obj._links.begin(); it!=obj._links.end(); ++it) { + if (first) { first=false; } else { os << ", "; } + os << "'" << it->first << "': ["; + bool firstinner = true; + for (LinkSet::const_iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) { + if (firstinner) { firstinner=false; } else { os << ", "; } + os << "'" << *it2 << "'"; + } + os << "]"; + } + os << "}"; + return os; +} +//}}} + +// SmartLinkMap//{{{ + +IPAACA_EXPORT LinkSet SmartLinkMap::empty_link_set; +IPAACA_EXPORT void SmartLinkMap::_add_and_remove_links(const LinkMap& add, const LinkMap& remove) +{ + // remove specified links + for (LinkMap::const_iterator it = remove.begin(); it != remove.end(); ++it ) { + // if link type exists + if (_links.count(it->first) > 0) { + // remove one by one + for (LinkSet::const_iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) { + _links[it->first].erase(*it2); + } + // wipe the type key if no more links are left + if (_links[it->first].size() == 0) { + _links.erase(it->first); + } + } + } + // add specified links + for (LinkMap::const_iterator it = add.begin(); it != add.end(); ++it ) { + for (LinkSet::const_iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) { + _links[it->first].insert(*it2); + } + } +} +IPAACA_EXPORT void SmartLinkMap::_replace_links(const LinkMap& links) +{ + //_links.clear(); + _links=links; +} +IPAACA_EXPORT const LinkSet& SmartLinkMap::get_links(const std::string& key) +{ + LinkMap::const_iterator it = _links.find(key); + if (it==_links.end()) return empty_link_set; + return it->second; +} +IPAACA_EXPORT const LinkMap& SmartLinkMap::get_all_links() +{ + return _links; +} +//}}} + +} // of namespace ipaaca + diff --git a/ipaacalib/cpp/src/ipaaca-locking.cc b/ipaacalib/cpp/src/ipaaca-locking.cc new file mode 100644 index 0000000000000000000000000000000000000000..96748cd530f19d6e34505050724cf07af4806d80 --- /dev/null +++ b/ipaacalib/cpp/src/ipaaca-locking.cc @@ -0,0 +1,43 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +#include <ipaaca/ipaaca.h> + +namespace ipaaca { + +Lock& logger_lock() { + static Lock lock; + return lock; +} + +} // of namespace ipaaca diff --git a/ipaacalib/cpp/src/ipaaca-payload.cc b/ipaacalib/cpp/src/ipaaca-payload.cc new file mode 100644 index 0000000000000000000000000000000000000000..4047162a7630dc363318f2b4d62fc008e860fdc3 --- /dev/null +++ b/ipaacalib/cpp/src/ipaaca-payload.cc @@ -0,0 +1,990 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +#include <ipaaca/ipaaca.h> + +namespace ipaaca { + +using namespace rapidjson; + +using namespace rsb; +using namespace rsb::filter; +using namespace rsb::converter; +using namespace rsb::patterns; + +// temporary helper to show rapidjson internal type +std::string value_diagnosis(rapidjson::Value* val) +{ + if (!val) return "nullptr"; + if (val->IsNull()) return "null"; + if (val->IsString()) return "string"; + if (val->IsNumber()) return "number"; + if (val->IsBool()) return "bool"; + if (val->IsArray()) return "array"; + if (val->IsObject()) return "object"; + return "other"; + +} + + +IPAACA_EXPORT std::ostream& operator<<(std::ostream& os, const rapidjson::Value& val)//{{{ +{ + os << json_value_cast<std::string>(val); + return os; +} +//}}} +IPAACA_EXPORT std::ostream& operator<<(std::ostream& os, const PayloadEntryProxy& proxy)//{{{ +{ + if (proxy.json_value) os << *(proxy.json_value); + else os << "null"; + return os; +} +//}}} +IPAACA_EXPORT std::ostream& operator<<(std::ostream& os, PayloadDocumentEntry::ptr entry)//{{{ +{ + os << json_value_cast<std::string>(entry->document); + return os; +} +//}}} + +IPAACA_EXPORT std::ostream& operator<<(std::ostream& os, const Payload& obj)//{{{ +{ + os << "{"; + bool first = true; + for (auto& kv: obj._document_store) { + if (first) { first=false; } else { os << ", "; } + os << "\"" << kv.first << "\":" << kv.second->to_json_string_representation() << ""; + } + os << "}"; + return os; +} +//}}} + +double strict_numerical_interpretation(const std::string& str) +{ + char* endptr; + auto s = str_trim(str); + const char* startptr = s.c_str(); + long l = strtod(startptr, &endptr); + if ((*endptr)=='\0') { + // everything could be parsed + return l; + } else { + throw PayloadTypeConversionError(); + } +} + +// json_value_cast//{{{ +IPAACA_EXPORT template<> std::string json_value_cast(const rapidjson::Value& v) +{ + if (v.IsString()) return v.GetString(); + if (v.IsNull()) return ""; + rapidjson::StringBuffer buffer; + rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); + v.Accept(writer); + return buffer.GetString(); +} +IPAACA_EXPORT template<> long json_value_cast(const rapidjson::Value& v) +{ + if (v.IsString()) return (long) strict_numerical_interpretation(v.GetString()); + if (v.IsInt()) return v.GetInt(); + if (v.IsUint()) return v.GetUint(); + if (v.IsInt64()) return v.GetInt64(); + if (v.IsUint64()) return v.GetUint64(); + if (v.IsDouble()) return (long) v.GetDouble(); + if (v.IsBool()) return v.GetBool() ? 1l : 0l; + if (v.IsNull()) return 0l; + // default: return parse of string version (should always be 0 though?) + throw PayloadTypeConversionError(); + /* + rapidjson::StringBuffer buffer; + rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); + v.Accept(writer); + return atol(std::string(buffer.GetString()).c_str()); + */ +} +IPAACA_EXPORT template<> int json_value_cast(const rapidjson::Value& v) +{ + if (v.IsString()) return (int) strict_numerical_interpretation(v.GetString()); + if (v.IsInt()) return v.GetInt(); + if (v.IsUint()) return v.GetUint(); + if (v.IsInt64()) return v.GetInt64(); + if (v.IsUint64()) return v.GetUint64(); + if (v.IsDouble()) return (long) v.GetDouble(); + if (v.IsBool()) return v.GetBool() ? 1l : 0l; + if (v.IsNull()) return 0l; + throw PayloadTypeConversionError(); +} +IPAACA_EXPORT template<> double json_value_cast(const rapidjson::Value& v) +{ + if (v.IsString()) return strict_numerical_interpretation(v.GetString()); + if (v.IsDouble()) return v.GetDouble(); + if (v.IsInt()) return (double) v.GetInt(); + if (v.IsUint()) return (double) v.GetUint(); + if (v.IsInt64()) return (double) v.GetInt64(); + if (v.IsUint64()) return (double) v.GetUint64(); + if (v.IsBool()) return v.GetBool() ? 1.0 : 0.0; + if (v.IsNull()) return 0.0; + throw PayloadTypeConversionError(); +} +IPAACA_EXPORT template<> bool json_value_cast(const rapidjson::Value& v) +{ + if (v.IsString()) { + std::string s = v.GetString(); + return !((s=="")||(s=="false")||(s=="False")||(s=="0")); + //return ((s=="1")||(s=="true")||(s=="True")); + } + if (v.IsBool()) return v.GetBool(); + if (v.IsNull()) return false; + if (v.IsInt()) return v.GetInt() != 0; + if (v.IsUint()) return v.GetUint() != 0; + if (v.IsInt64()) return v.GetInt64() != 0; + if (v.IsUint64()) return v.GetUint64() != 0; + if (v.IsDouble()) return v.GetDouble() != 0.0; + // default: assume "pointer-like" semantics (i.e. objects are TRUE) + return true; +} +/* + * std::map<std::string, std::string> result; + std::for_each(_document_store.begin(), _document_store.end(), [&result](std::pair<std::string, PayloadDocumentEntry::ptr> pair) { + result[pair.first] = pair.second->document.GetString(); + }); + */ +//}}} + +IPAACA_EXPORT void pack_into_json_value(rapidjson::Value& valueobject, rapidjson::Document::AllocatorType& allocator, int newvalue) +{ + valueobject.SetInt(newvalue); +} +IPAACA_EXPORT void pack_into_json_value(rapidjson::Value& valueobject, rapidjson::Document::AllocatorType& allocator, long newvalue) +{ + valueobject.SetInt(newvalue); +} +IPAACA_EXPORT void pack_into_json_value(rapidjson::Value& valueobject, rapidjson::Document::AllocatorType& allocator, double newvalue) +{ + valueobject.SetDouble(newvalue); +} +IPAACA_EXPORT void pack_into_json_value(rapidjson::Value& valueobject, rapidjson::Document::AllocatorType& allocator, bool newvalue) +{ + valueobject.SetBool(newvalue); +} +IPAACA_EXPORT void pack_into_json_value(rapidjson::Value& valueobject, rapidjson::Document::AllocatorType& allocator, const std::string& newvalue) +{ + valueobject.SetString(newvalue.c_str(), allocator); +} +IPAACA_EXPORT void pack_into_json_value(rapidjson::Value& valueobject, rapidjson::Document::AllocatorType& allocator, const char* newvalue) +{ + valueobject.SetString(newvalue, allocator); +} +/* +IPAACA_EXPORT template<> void pack_into_json_value(rapidjson::Value& valueobject, rapidjson::Document::AllocatorType& allocator, const std::vector<std::string>& newvalue) +{ + valueobject.SetArray(); + for (auto& str: newvalue) { + rapidjson::Value sv; + sv.SetString(str, allocator); + valueobject.PushBack(sv, allocator); + } +} +IPAACA_EXPORT template<> void pack_into_json_value(rapidjson::Value& valueobject, rapidjson::Document::AllocatorType& allocator, const std::list<std::string>& newvalue) +{ + IPAACA_IMPLEMENT_ME +} +IPAACA_EXPORT template<> void pack_into_json_value(rapidjson::Value& valueobject, rapidjson::Document::AllocatorType& allocator, const std::map<std::string, std::string>& newvalue) +{ + IPAACA_IMPLEMENT_ME +} +*/ + +// PayloadDocumentEntry//{{{ +IPAACA_EXPORT std::string PayloadDocumentEntry::to_json_string_representation() +{ + rapidjson::StringBuffer buffer; + rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); + document.Accept(writer); + return buffer.GetString(); +} +IPAACA_EXPORT PayloadDocumentEntry::ptr PayloadDocumentEntry::from_json_string_representation(const std::string& json_str) +{ + PayloadDocumentEntry::ptr entry = std::make_shared<ipaaca::PayloadDocumentEntry>(); + if (entry->document.Parse(json_str.c_str()).HasParseError()) { + throw JsonParsingError(); + } + //entry->json_source = json_str; + return entry; +} +IPAACA_EXPORT PayloadDocumentEntry::ptr PayloadDocumentEntry::from_unquoted_string_value(const std::string& str) +{ + PayloadDocumentEntry::ptr entry = std::make_shared<ipaaca::PayloadDocumentEntry>(); + entry->document.SetString(str.c_str(), entry->document.GetAllocator()); + //entry->update_json_source(); + return entry; +} + +/// update json_source after a write operation (on newly cloned entries) +/* +IPAACA_EXPORT void PayloadDocumentEntry::update_json_source() +{ + json_source = to_json_string_representation(); +} +*/ + +IPAACA_EXPORT PayloadDocumentEntry::ptr PayloadDocumentEntry::create_null() +{ + PayloadDocumentEntry::ptr entry = std::make_shared<ipaaca::PayloadDocumentEntry>(); + //entry->json_source = "null"; // rapidjson::Document value is also null implicitly + return entry; +} +IPAACA_EXPORT PayloadDocumentEntry::ptr PayloadDocumentEntry::clone() +{ + //auto entry = PayloadDocumentEntry::from_json_string_representation(this->json_source); + auto entry = PayloadDocumentEntry::create_null(); + entry->document.CopyFrom(this->document, entry->document.GetAllocator()); + IPAACA_DEBUG("Cloned for copy-on-write, contents: " << entry) + return entry; +} +IPAACA_EXPORT rapidjson::Value& PayloadDocumentEntry::get_or_create_nested_value_from_proxy_path(PayloadEntryProxy* pep) +{ + if (!(pep->parent)) { + return document; + } + rapidjson::Value& parent_value = get_or_create_nested_value_from_proxy_path(pep->parent); + if (pep->addressed_as_array) { + IPAACA_DEBUG("Addressed as array with index " << pep->addressed_index) + if (! parent_value.IsArray()) { + throw PayloadAddressingError(); + } else { + long idx = pep->addressed_index; + long s = parent_value.Size(); + if (idx<s) { + return parent_value[idx]; + } else { + throw PayloadAddressingError(); + } + } + // for append / push_back? : + /*if (parent_value.IsNull()) { + wasnull = true; + parent_value.SetArray(); + } + if (wasnull || parent_value.IsArray()) { + long idx = pep->addressed_index; + long s = parent_value.Size(); + if (idx<s) { + // existing element modified + parent_value[idx] = *json_value; + } else { + // implicitly initialize missing elements to null values + if (idx>s) { + long missing_elements = pep->addressed_index - p; + for (int i=0; i<missing_elements; ++i) { + parent_value.PushBack(, allocator) + } + } + } + if (s == + } else { + throw PayloadAddressingError(); + }*/ + } else { + IPAACA_DEBUG("Addressed as dict with key " << pep->addressed_key) + // addressed as object (dict) + //rapidjson::Value& parent_value = *(pep->parent->json_value); + if (! parent_value.IsObject()) { + IPAACA_DEBUG("parent is not of type Object") + throw PayloadAddressingError(); + } else { + rapidjson::Document::AllocatorType& allocator = document.GetAllocator(); + //Value key; + //key.SetString(pep->addressed_key, allocator); + //parent_value.AddMember(key, *json_value, allocator); + rapidjson::Value key; + key.SetString(pep->addressed_key, allocator); + auto it = parent_value.FindMember(key); + if (it != parent_value.MemberEnd()) { + return parent_value[key]; + } else { + rapidjson::Value val; + parent_value.AddMember(key, val, allocator); + rapidjson::Value rkey; + rkey.SetString(pep->addressed_key, allocator); + return parent_value[rkey]; + } + } + } +} + +//}}} + +// PayloadEntryProxy//{{{ + + // only if not top-level +#if 0 +IPAACA_EXPORT void PayloadEntryProxy::connect_to_existing_parents() +{ + rapidjson::Document::AllocatorType& allocator = document_entry->document.GetAllocator(); + PayloadEntryProxy* pep = this; + while (!(pep->existent) && pep->parent) { // only if not top-level + if (pep->addressed_as_array) { + rapidjson::Value& parent_value = *(pep->parent->json_value); + if (! parent_value.IsArray()) { + throw PayloadAddressingError(); + } else { + long idx = pep->addressed_index; + long s = parent_value.Size(); + if (idx<s) { + parent_value[idx] = *json_value; + } else { + throw PayloadAddressingError(); + } + } + /*if (parent_value.IsNull()) { + wasnull = true; + parent_value.SetArray(); + } + if (wasnull || parent_value.IsArray()) { + long idx = pep->addressed_index; + long s = parent_value.Size(); + if (idx<s) { + // existing element modified + parent_value[idx] = *json_value; + } else { + // implicitly initialize missing elements to null values + if (idx>s) { + long missing_elements = pep->addressed_index - p; + for (int i=0; i<missing_elements; ++i) { + parent_value.PushBack(, allocator) + } + } + } + if (s == + } else { + throw PayloadAddressingError(); + }*/ + } else { + // addressed as object (dict) + rapidjson::Value& parent_value = *(pep->parent->json_value); + if (! parent_value.IsObject()) { + throw PayloadAddressingError(); + } else { + Value key; + key.SetString(pep->addressed_key, allocator); + parent_value.AddMember(key, *json_value, allocator); + } + } + // repeat for next parent in the tree + pep = pep->parent; + } +} +#endif + + +IPAACA_EXPORT PayloadEntryProxy::PayloadEntryProxy(Payload* payload, const std::string& key) +: _payload(payload), _key(key), parent(nullptr) +{ + document_entry = _payload->get_entry(key); + json_value = &(document_entry->document); +} +IPAACA_EXPORT PayloadEntryProxy::PayloadEntryProxy(PayloadEntryProxy* parent_, const std::string& addr_key_) +: parent(parent_), addressed_key(addr_key_), addressed_as_array(false) +{ + _payload = parent->_payload; + _key = parent->_key; + document_entry = parent->document_entry; + auto it = parent->json_value->FindMember(addr_key_.c_str()); + if (it != parent->json_value->MemberEnd()) { + json_value = &(parent->json_value->operator[](addr_key_.c_str())); + existent = true; + } else { + json_value = nullptr; // avoid heap construction here + existent = false; + } +} +IPAACA_EXPORT PayloadEntryProxy::PayloadEntryProxy(PayloadEntryProxy* parent_, size_t addr_idx_) +: parent(parent_), addressed_index(addr_idx_), addressed_as_array(true) +{ + _payload = parent->_payload; + _key = parent->_key; + document_entry = parent->document_entry; + json_value = &(parent->json_value->operator[](addr_idx_)); + existent = true; +} + +IPAACA_EXPORT PayloadEntryProxy PayloadEntryProxy::operator[](const char* addr_key_) +{ + return operator[](std::string(addr_key_)); +} +IPAACA_EXPORT PayloadEntryProxy PayloadEntryProxy::operator[](const std::string& addr_key_) +{ + if (!json_value) { + IPAACA_DEBUG("Invalid json_value!") + throw PayloadAddressingError(); + } + if (! json_value->IsObject()) { + IPAACA_DEBUG("Expected Object for operator[](string)!") + throw PayloadAddressingError(); + } + return PayloadEntryProxy(this, addr_key_); +} +IPAACA_EXPORT PayloadEntryProxy PayloadEntryProxy::operator[](size_t addr_idx_) +{ + if (!json_value) { + IPAACA_DEBUG("Invalid json_value!") + throw PayloadAddressingError(); + } + if (! json_value->IsArray()) { + IPAACA_DEBUG("Expected Array for operator[](size_t)!") + throw PayloadAddressingError(); + } + long s = json_value->Size(); + if (addr_idx_>=s) { + IPAACA_DEBUG("Array out of bounds!") + throw PayloadAddressingError(); + } + return PayloadEntryProxy(this, addr_idx_); +} +IPAACA_EXPORT PayloadEntryProxy PayloadEntryProxy::operator[](int addr_idx_) +{ + if (addr_idx_ < 0) { + IPAACA_DEBUG("Negative index!") + throw PayloadAddressingError(); + } + return operator[]((size_t) addr_idx_); +} + +IPAACA_EXPORT PayloadEntryProxy& PayloadEntryProxy::operator=(const PayloadEntryProxy& otherproxy) +{ + PayloadDocumentEntry::ptr new_entry = document_entry->clone(); // copy-on-write, no lock required + rapidjson::Value& newval = new_entry->get_or_create_nested_value_from_proxy_path(this); + auto valueptr = otherproxy.json_value; + if (valueptr) { // only set if value is valid, keep default null value otherwise + newval.CopyFrom(*valueptr, new_entry->document.GetAllocator()); + } + //new_entry->update_json_source(); + _payload->set(_key, new_entry); + return *this; +} + +/* +IPAACA_EXPORT PayloadEntryProxy& PayloadEntryProxy::operator=(const std::string& value) +{ + //std::cout << "operator=(string)" << std::endl; + IPAACA_IMPLEMENT_ME + //_payload->set(_key, value); + return *this; +} +IPAACA_EXPORT PayloadEntryProxy& PayloadEntryProxy::operator=(const char* value) +{ + //std::cout << "operator=(const char*)" << std::endl; + IPAACA_IMPLEMENT_ME + //_payload->set(_key, value); + return *this; +} +IPAACA_EXPORT PayloadEntryProxy& PayloadEntryProxy::operator=(double value) +{ + //std::cout << "operator=(double)" << std::endl; + IPAACA_IMPLEMENT_ME + //_payload->set(_key, boost::lexical_cast<std::string>(value)); + return *this; +} +IPAACA_EXPORT PayloadEntryProxy& PayloadEntryProxy::operator=(bool value) +{ + //std::cout << "operator=(bool)" << std::endl; + IPAACA_IMPLEMENT_ME + //_payload->set(_key, boost::lexical_cast<std::string>(value)); + return *this; +} +*/ + +IPAACA_EXPORT PayloadEntryProxy::operator std::string() +{ + return json_value_cast<std::string>(json_value); + //PayloadEntryProxy::get<std::string>(); +} +IPAACA_EXPORT PayloadEntryProxy::operator long() +{ + return json_value_cast<long>(json_value); + //return PayloadEntryProxy::get<long>(); +} +IPAACA_EXPORT PayloadEntryProxy::operator double() +{ + return json_value_cast<double>(json_value); + //return PayloadEntryProxy::get<double>(); +} +IPAACA_EXPORT PayloadEntryProxy::operator bool() +{ + return json_value_cast<bool>(json_value); + //return PayloadEntryProxy::get<bool>(); +} +IPAACA_EXPORT std::string PayloadEntryProxy::to_str() +{ + return json_value_cast<std::string>(json_value); + //return PayloadEntryProxy::get<std::string>(); +} +IPAACA_EXPORT long PayloadEntryProxy::to_long() +{ + return json_value_cast<long>(json_value); + //return PayloadEntryProxy::get<long>(); +} +IPAACA_EXPORT double PayloadEntryProxy::to_float() +{ + return json_value_cast<double>(json_value); + //return PayloadEntryProxy::get<double>(); +} +IPAACA_EXPORT bool PayloadEntryProxy::to_bool() +{ + return json_value_cast<bool>(json_value); + //return PayloadEntryProxy::get<bool>(); +} + +IPAACA_EXPORT PayloadEntryProxyMapDecorator PayloadEntryProxy::as_map() +{ + if (json_value && json_value->IsObject()) return PayloadEntryProxyMapDecorator(this); + throw PayloadTypeConversionError(); +} + +IPAACA_EXPORT PayloadEntryProxyListDecorator PayloadEntryProxy::as_list() +{ + if (json_value && json_value->IsArray()) return PayloadEntryProxyListDecorator(this); + throw PayloadTypeConversionError(); +} + +IPAACA_EXPORT size_t PayloadEntryProxy::size() +{ + if (!json_value) return 0; + if (json_value->IsArray()) return json_value->Size(); + if (json_value->IsObject()) return json_value->MemberCount(); + return 0; +} +IPAACA_EXPORT bool PayloadEntryProxy::is_null() +{ + return (!json_value) || json_value->IsNull(); +} +IPAACA_EXPORT bool PayloadEntryProxy::is_string() +{ + return json_value && json_value->IsString(); +} +/// is_number => whether it is *interpretable* as +/// a numerical value (i.e. including conversions) +IPAACA_EXPORT bool PayloadEntryProxy::is_number() +{ + if (!json_value) return false; + try { + double dummy = json_value_cast<double>(*json_value); + return true; + } catch (PayloadTypeConversionError& ex) { + return false; + } +} +IPAACA_EXPORT bool PayloadEntryProxy::is_list() +{ + return json_value && json_value->IsArray(); +} +IPAACA_EXPORT bool PayloadEntryProxy::is_map() +{ + return json_value && json_value->IsObject(); +} + +// +// new stuff for protocol v2 +// + +/* +IPAACA_EXPORT template<> std::string PayloadEntryProxy::get<std::string>() +{ + if (!json_value) return ""; + //IPAACA_INFO( value_diagnosis(json_value) ) + if (json_value->IsString()) return json_value->GetString(); + if (json_value->IsNull()) return ""; + rapidjson::StringBuffer buffer; + rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); + json_value->Accept(writer); + return buffer.GetString(); + + //return _payload->get(_key); +} +IPAACA_EXPORT template<> long PayloadEntryProxy::get<long>() +{ + return atof(operator std::string().c_str()); +} +IPAACA_EXPORT template<> double PayloadEntryProxy::get<double>() +{ + return atol(operator std::string().c_str()); +} +IPAACA_EXPORT template<> bool PayloadEntryProxy::get<bool>() +{ + std::string s = operator std::string(); + return ((s=="1")||(s=="true")||(s=="True")); +} +// complex types +IPAACA_EXPORT template<> std::list<std::string> PayloadEntryProxy::get<std::list<std::string> >() +{ + std::list<std::string> l; + l.push_back(PayloadEntryProxy::get<std::string>()); + return l; +} +IPAACA_EXPORT template<> std::vector<std::string> PayloadEntryProxy::get<std::vector<std::string> >() +{ + std::vector<std::string> v; + v.push_back(PayloadEntryProxy::get<std::string>()); + return v; +} +IPAACA_EXPORT template<> std::map<std::string, std::string> PayloadEntryProxy::get<std::map<std::string, std::string> >() +{ + std::map<std::string, std::string> m; + m["__automatic__"] = PayloadEntryProxy::get<std::string>(); + return m; +} +*/ + +//}}} + +// Payload//{{{ + +IPAACA_EXPORT void Payload::on_lock() +{ + Locker locker(_payload_operation_mode_lock); + IPAACA_DEBUG("Starting batch update mode ...") + _update_on_every_change = false; +} +IPAACA_EXPORT void Payload::on_unlock() +{ + Locker locker(_payload_operation_mode_lock); + IPAACA_DEBUG("... applying batch update with " << _collected_modifications.size() << " modifications and " << _collected_removals.size() << " removals ...") + _internal_merge_and_remove(_collected_modifications, _collected_removals, _batch_update_writer_name); + _update_on_every_change = true; + _batch_update_writer_name = ""; + _collected_modifications.clear(); + _collected_removals.clear(); + IPAACA_DEBUG("... exiting batch update mode.") +} + +IPAACA_EXPORT void Payload::initialize(boost::shared_ptr<IUInterface> iu) +{ + _iu = boost::weak_ptr<IUInterface>(iu); +} + +IPAACA_EXPORT PayloadEntryProxy Payload::operator[](const std::string& key) +{ + // TODO atomicize + //boost::shared_ptr<PayloadEntryProxy> p(new PayloadEntryProxy(this, key)); + return PayloadEntryProxy(this, key); +} + +IPAACA_EXPORT Payload::operator std::map<std::string, std::string>() +{ + std::map<std::string, std::string> result; + std::for_each(_document_store.begin(), _document_store.end(), [&result](std::pair<std::string, PayloadDocumentEntry::ptr> pair) { + result[pair.first] = json_value_cast<std::string>(pair.second->document); + }); + return result; +} + +IPAACA_EXPORT void Payload::_internal_set(const std::string& k, PayloadDocumentEntry::ptr v, const std::string& writer_name) { + Locker locker(_payload_operation_mode_lock); + if (_update_on_every_change) { + std::map<std::string, PayloadDocumentEntry::ptr> _new; + std::vector<std::string> _remove; + _new[k] = v; + _iu.lock()->_modify_payload(true, _new, _remove, writer_name ); + IPAACA_DEBUG(" Setting local payload item \"" << k << "\" to " << v) + _document_store[k] = v; + mark_revision_change(); + } else { + IPAACA_DEBUG("queueing a payload set operation") + _batch_update_writer_name = writer_name; + _collected_modifications[k] = v; + // revoke deletions of this updated key + std::vector<std::string> new_removals; + for (auto& rk: _collected_removals) { + if (rk!=k) new_removals.push_back(rk); + } + _collected_removals = new_removals; + } +} +IPAACA_EXPORT void Payload::_internal_remove(const std::string& k, const std::string& writer_name) { + Locker locker(_payload_operation_mode_lock); + if (_update_on_every_change) { + std::map<std::string, PayloadDocumentEntry::ptr> _new; + std::vector<std::string> _remove; + _remove.push_back(k); + _iu.lock()->_modify_payload(true, _new, _remove, writer_name ); + _document_store.erase(k); + mark_revision_change(); + } else { + IPAACA_DEBUG("queueing a payload remove operation") + _batch_update_writer_name = writer_name; + _collected_removals.push_back(k); + // revoke updates of this deleted key + _collected_modifications.erase(k); + } +} +IPAACA_EXPORT void Payload::_internal_replace_all(const std::map<std::string, PayloadDocumentEntry::ptr>& new_contents, const std::string& writer_name) +{ + Locker locker(_payload_operation_mode_lock); + if (_update_on_every_change) { + std::vector<std::string> _remove; + _iu.lock()->_modify_payload(false, new_contents, _remove, writer_name ); + _document_store = new_contents; + mark_revision_change(); + } else { + IPAACA_DEBUG("queueing a payload replace_all operation") + _batch_update_writer_name = writer_name; + _collected_modifications.clear(); + for (auto& kv: new_contents) { + _collected_modifications[kv.first] = kv.second; + } + // take all existing keys and flag to remove them, unless overridden in current update + for (auto& kv: _document_store) { + if (! new_contents.count(kv.first)) { + _collected_removals.push_back(kv.first); + _collected_modifications.erase(kv.first); + } + } + } +} +IPAACA_EXPORT void Payload::_internal_merge(const std::map<std::string, PayloadDocumentEntry::ptr>& contents_to_merge, const std::string& writer_name) +{ + Locker locker(_payload_operation_mode_lock); + if (_update_on_every_change) { + std::vector<std::string> _remove; + _iu.lock()->_modify_payload(true, contents_to_merge, _remove, writer_name ); + for (auto& kv: contents_to_merge) { + _document_store[kv.first] = kv.second; + } + mark_revision_change(); + } else { + IPAACA_DEBUG("queueing a payload merge operation") + std::set<std::string> updated_keys; + _batch_update_writer_name = writer_name; + for (auto& kv: contents_to_merge) { + _collected_modifications[kv.first] = kv.second; + updated_keys.insert(kv.first); + } + // revoke deletions of updated keys + std::vector<std::string> new_removals; + for (auto& rk: _collected_removals) { + if (! updated_keys.count(rk)) new_removals.push_back(rk); + } + _collected_removals = new_removals; + } +} +IPAACA_EXPORT void Payload::_internal_merge_and_remove(const std::map<std::string, PayloadDocumentEntry::ptr>& contents_to_merge, const std::vector<std::string>& keys_to_remove, const std::string& writer_name) +{ + // this function is called by exiting the batch update mode only, so no extra locking here + _iu.lock()->_modify_payload(true, contents_to_merge, keys_to_remove, writer_name ); + for (auto& k: keys_to_remove) { + _document_store.erase(k); + } + for (auto& kv: contents_to_merge) { + _document_store[kv.first] = kv.second; + } + mark_revision_change(); +} +IPAACA_EXPORT PayloadDocumentEntry::ptr Payload::get_entry(const std::string& k) { + if (_document_store.count(k)>0) return _document_store[k]; + else return PayloadDocumentEntry::create_null(); // contains Document with 'null' value +} +IPAACA_EXPORT std::string Payload::get(const std::string& k) { // DEPRECATED + if (_document_store.count(k)>0) return _document_store[k]->document.GetString(); + return ""; +} + +IPAACA_EXPORT void Payload::set(const std::map<std::string, std::string>& all_elems) +{ + std::map<std::string, PayloadDocumentEntry::ptr> newmap; + for (auto& kv: all_elems) { + /*PayloadDocumentEntry::ptr newit = PayloadDocumentEntry::create_null(); + newit->document.SetString(kv.second, newit->document.GetAllocator()); + newit->update_json_source(); + newmap[kv.first] = newit;*/ + newmap[kv.first] = PayloadDocumentEntry::from_unquoted_string_value(kv.second); + } + _internal_replace_all(newmap); +} + +IPAACA_EXPORT void Payload::_remotely_enforced_wipe() +{ + _document_store.clear(); + mark_revision_change(); +} +IPAACA_EXPORT void Payload::_remotely_enforced_delitem(const std::string& k) +{ + _document_store.erase(k); + mark_revision_change(); +} +IPAACA_EXPORT void Payload::_remotely_enforced_setitem(const std::string& k, PayloadDocumentEntry::ptr entry) +{ + _document_store[k] = entry; + mark_revision_change(); +} +IPAACA_EXPORT PayloadIterator Payload::begin() +{ + return PayloadIterator(this, _document_store.begin()); +} +IPAACA_EXPORT PayloadIterator Payload::end() +{ + return PayloadIterator(this, _document_store.end()); +} + +//}}} + +// PayloadIterator//{{{ +IPAACA_EXPORT PayloadIterator::PayloadIterator(Payload* payload, PayloadDocumentStore::iterator&& ref_it) +: _payload(payload), reference_payload_revision(payload->internal_revision), raw_iterator(std::move(ref_it)) +{ +} +IPAACA_EXPORT PayloadIterator::PayloadIterator(const PayloadIterator& iter) +: _payload(iter._payload), reference_payload_revision(iter.reference_payload_revision), raw_iterator(iter.raw_iterator) +{ +} + +IPAACA_EXPORT PayloadIterator& PayloadIterator::operator++() +{ + if (_payload->revision_changed(reference_payload_revision)) throw PayloadIteratorInvalidError(); + ++raw_iterator; + return *this; +} + +IPAACA_EXPORT std::pair<std::string, PayloadEntryProxy> PayloadIterator::operator*() +{ + if (_payload->revision_changed(reference_payload_revision)) throw PayloadIteratorInvalidError(); + if (raw_iterator == _payload->_document_store.end()) throw PayloadIteratorInvalidError(); + return std::pair<std::string, PayloadEntryProxy>(raw_iterator->first, PayloadEntryProxy(_payload, raw_iterator->first)); +} +IPAACA_EXPORT std::shared_ptr<std::pair<std::string, PayloadEntryProxy> > PayloadIterator::operator->() +{ + if (_payload->revision_changed(reference_payload_revision)) throw PayloadIteratorInvalidError(); + if (raw_iterator == _payload->_document_store.end()) throw PayloadIteratorInvalidError(); + return std::make_shared<std::pair<std::string, PayloadEntryProxy> >(raw_iterator->first, PayloadEntryProxy(_payload, raw_iterator->first)); +} + +IPAACA_EXPORT bool PayloadIterator::operator==(const PayloadIterator& ref) +{ + if (_payload->revision_changed(reference_payload_revision)) throw PayloadIteratorInvalidError(); + return (raw_iterator==ref.raw_iterator); +} +IPAACA_EXPORT bool PayloadIterator::operator!=(const PayloadIterator& ref) +{ + if (_payload->revision_changed(reference_payload_revision)) throw PayloadIteratorInvalidError(); + return (raw_iterator!=ref.raw_iterator); +} +//}}} + +// PayloadEntryProxyMapIterator//{{{ +IPAACA_EXPORT PayloadEntryProxyMapIterator::PayloadEntryProxyMapIterator(PayloadEntryProxy* proxy_, RawIterator&& raw_iter) +: proxy(proxy_), raw_iterator(std::move(raw_iter)) +{ +} + +IPAACA_EXPORT PayloadEntryProxyMapIterator& PayloadEntryProxyMapIterator::operator++() +{ + // prevent increase beyond end() ? + raw_iterator++; + return *this; +} + +IPAACA_EXPORT std::pair<std::string, PayloadEntryProxy> PayloadEntryProxyMapIterator::operator*() +{ + std::string key = raw_iterator->name.GetString(); + return std::pair<std::string, PayloadEntryProxy>(key, (*proxy)[key] ); // generates child Proxy +} + +IPAACA_EXPORT std::shared_ptr<std::pair<std::string, PayloadEntryProxy> > PayloadEntryProxyMapIterator::operator->() +{ + std::string key = raw_iterator->name.GetString(); + return std::make_shared<std::pair<std::string, PayloadEntryProxy> >(key, (*proxy)[key] ); // generates child Proxy +} +IPAACA_EXPORT bool PayloadEntryProxyMapIterator::operator==(const PayloadEntryProxyMapIterator& other_iter) +{ + return raw_iterator==other_iter.raw_iterator; +} +IPAACA_EXPORT bool PayloadEntryProxyMapIterator::operator!=(const PayloadEntryProxyMapIterator& other_iter) +{ + return raw_iterator!=other_iter.raw_iterator; +} +//}}} +// PayloadEntryProxyMapDecorator//{{{ +PayloadEntryProxyMapIterator PayloadEntryProxyMapDecorator::begin() +{ + return PayloadEntryProxyMapIterator(proxy, proxy->json_value->MemberBegin()); +} +PayloadEntryProxyMapIterator PayloadEntryProxyMapDecorator::end() +{ + return PayloadEntryProxyMapIterator(proxy, proxy->json_value->MemberEnd()); +} +//}}} + +// PayloadEntryProxyListIterator//{{{ +IPAACA_EXPORT PayloadEntryProxyListIterator::PayloadEntryProxyListIterator(PayloadEntryProxy* proxy_, size_t idx, size_t size_) +: proxy(proxy_), current_idx(idx), size(size_) +{ +} + +IPAACA_EXPORT PayloadEntryProxyListIterator& PayloadEntryProxyListIterator::operator++() +{ + if (current_idx!=size) current_idx++; + return *this; +} + +IPAACA_EXPORT PayloadEntryProxy PayloadEntryProxyListIterator::operator*() +{ + return (*proxy)[current_idx]; +} + +IPAACA_EXPORT std::shared_ptr<PayloadEntryProxy> PayloadEntryProxyListIterator::operator->() +{ + return std::make_shared<PayloadEntryProxy>((*proxy)[current_idx]); +} +IPAACA_EXPORT bool PayloadEntryProxyListIterator::operator==(const PayloadEntryProxyListIterator& other_iter) +{ + return (proxy==other_iter.proxy) && (current_idx==other_iter.current_idx); +} +IPAACA_EXPORT bool PayloadEntryProxyListIterator::operator!=(const PayloadEntryProxyListIterator& other_iter) +{ + return (current_idx!=other_iter.current_idx) || (proxy!=other_iter.proxy); +} +//}}} +// PayloadEntryProxyListDecorator//{{{ +PayloadEntryProxyListIterator PayloadEntryProxyListDecorator::begin() +{ + return PayloadEntryProxyListIterator(proxy, 0, proxy->json_value->Size()); +} +PayloadEntryProxyListIterator PayloadEntryProxyListDecorator::end() +{ + size_t size = proxy->json_value->Size(); + return PayloadEntryProxyListIterator(proxy, size, size); +} +//}}} + +} // of namespace ipaaca diff --git a/ipaacalib/cpp/src/ipaaca-string-utils.cc b/ipaacalib/cpp/src/ipaaca-string-utils.cc index 992d7cd005288caf2e43511133892639663a5b19..d3192fc858aed0814dbde2494ddb822172e98095 100644 --- a/ipaacalib/cpp/src/ipaaca-string-utils.cc +++ b/ipaacalib/cpp/src/ipaaca-string-utils.cc @@ -32,8 +32,20 @@ #include <ipaaca/ipaaca.h> +#include <cctype> +#include <string> +#include <algorithm> + + namespace ipaaca { +std::string str_trim(const std::string &s) +{ + auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c){ return std::isspace(c); } ); + auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c){ return std::isspace(c); } ).base(); + return (wsback<=wsfront ? std::string() : std::string(wsfront, wsback)); +} + std::string str_join(const std::set<std::string>& set,const std::string& sep) { if(set.size()==0) diff --git a/ipaacalib/cpp/src/ipaaca.cc b/ipaacalib/cpp/src/ipaaca.cc index 32264161d4abf28389ad35163d07530e1e019916..2e7f99510dcb8afda09c5019da4e40c9bce9583a 100644 --- a/ipaacalib/cpp/src/ipaaca.cc +++ b/ipaacalib/cpp/src/ipaaca.cc @@ -1,10 +1,11 @@ /* * This file is part of IPAACA, the * "Incremental Processing Architecture - * for Artificial Conversational Agents". + * for Artificial Conversational Agents". * - * Copyright (c) 2009-2013 Sociable Agents Group - * CITEC, Bielefeld University + * Copyright (c) 2009-2015 Social Cognitive Systems Group + * (formerly the Sociable Agents Group) + * CITEC, Bielefeld University * * http://opensource.cit-ec.de/projects/ipaaca/ * http://purl.org/net/ipaaca @@ -21,7 +22,7 @@ * You should have received a copy of the LGPL along with this * program. If not, go to http://www.gnu.org/licenses/lgpl.html * or write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The development of this software was supported by the * Excellence Cluster EXC 277 Cognitive Interaction Technology. @@ -31,108 +32,49 @@ */ #include <ipaaca/ipaaca.h> -#include <cstdlib> -#include <glob.h> namespace ipaaca { -using namespace rsb; -using namespace rsb::filter; -using namespace rsb::converter; -using namespace rsb::patterns; - -#define VERBOSE_HANDLERS 0 - -Lock& logger_lock() { - static Lock lock; - return lock; -} - -// util and init//{{{ - -bool Initializer::_initialized = false; - -//const LinkSet EMPTY_LINK_SET = LinkSet(); -//const std::set<std::string> EMPTY_LINK_SET(); -bool Initializer::initialized() { return _initialized; } -void Initializer::initialize_ipaaca_rsb_if_needed() -{ - if (_initialized) return; - - initialize_updated_default_config(); - - // RYT FIXME This configuration stuff has been simply removed in rsb! - //ParticipantConfig config = ParticipantConfig::fromConfiguration(); - //getFactory().setDefaultParticipantConfig(config); - - boost::shared_ptr<IUConverter> iu_converter(new IUConverter()); - converterRepository<std::string>()->registerConverter(iu_converter); - - boost::shared_ptr<MessageConverter> message_converter(new MessageConverter()); - converterRepository<std::string>()->registerConverter(message_converter); - - boost::shared_ptr<IUPayloadUpdateConverter> payload_update_converter(new IUPayloadUpdateConverter()); - converterRepository<std::string>()->registerConverter(payload_update_converter); - - boost::shared_ptr<IULinkUpdateConverter> link_update_converter(new IULinkUpdateConverter()); - converterRepository<std::string>()->registerConverter(link_update_converter); - - boost::shared_ptr<ProtocolBufferConverter<protobuf::IUCommission> > iu_commission_converter(new ProtocolBufferConverter<protobuf::IUCommission> ()); - converterRepository<std::string>()->registerConverter(iu_commission_converter); - - boost::shared_ptr<ProtocolBufferConverter<protobuf::IURetraction> > iu_retraction_converter(new ProtocolBufferConverter<protobuf::IURetraction> ()); - converterRepository<std::string>()->registerConverter(iu_retraction_converter); - - boost::shared_ptr<IntConverter> int_converter(new IntConverter()); - converterRepository<std::string>()->registerConverter(int_converter); - - _initialized = true; - //IPAACA_TODO("initialize all converters") -} - -void Initializer::initialize_updated_default_config() -{ - // quick hack to iterate through the pwd parents - // and find the closest rsb plugin dir - // - // but only if not yet defined - const char* plugin_path = getenv("RSB_PLUGINS_CPP_PATH"); - if (!plugin_path) { - LOG_IPAACA_CONSOLE("RSB_PLUGINS_CPP_PATH not set; looking here and up to 7 dirs up.") - std::string pathstr = "./"; - for (int i=0; i< 8 /* depth EIGHT (totally arbitrary..) */ ; i++) { - std::string where_str = pathstr+"deps/lib/rsb*/plugins"; - const char* where = where_str.c_str(); - glob_t g; - glob(where, 0, NULL, &g); - if (g.gl_pathc>0) { - const char* found_path = g.gl_pathv[0]; - LOG_IPAACA_CONSOLE("Found an RSB plugin dir which will be used automatically: " << found_path) - setenv("RSB_PLUGINS_CPP_PATH", found_path, 1); - break; - } // else keep going - globfree(&g); - pathstr += "../"; +// UUID generation +IPAACA_EXPORT std::string generate_uuid_string()//{{{ +{ +#ifdef WIN32 + // Windows + UUID uuid; + RPC_STATUS stat; + stat = UuidCreate(&uuid); + if (stat == RPC_S_OK) { + unsigned char* uuid_str = NULL; + stat = UuidToString(&uuid, &uuid_str); + if (stat == RPC_S_OK) { + std::string result((const char*) uuid_str, 16); + RpcStringFree(&uuid_str); + return result; } } else { - LOG_IPAACA_CONSOLE("RSB_PLUGINS_CPP_PATH already defined: " << plugin_path) + throw UUIDGenerationError(); } -} - -std::string generate_uuid_string() -{ +#else + // POSIX uuid_t uuidt; uuid_generate(uuidt); #ifdef __MACOSX__ + // (Mac) uuid_string_t uuidstr; uuid_unparse_lower(uuidt, uuidstr); return uuidstr; #else + // (Linux) char result_c[37]; uuid_unparse_lower(uuidt, result_c); return result_c; #endif -} +#endif +}//}}} + +IPAACA_EXPORT std::string __ipaaca_static_option_default_payload_type("JSON"); +IPAACA_EXPORT std::string __ipaaca_static_option_default_channel("default"); +IPAACA_EXPORT unsigned int __ipaaca_static_option_log_level(IPAACA_LOG_LEVEL_WARNING); /* void init_inprocess_too() { @@ -144,1565 +86,7 @@ void init_inprocess_too() { getFactory().setDefaultParticipantConfig(config); } */ -//}}} - -std::ostream& operator<<(std::ostream& os, const SmartLinkMap& obj)//{{{ -{ - os << "{"; - bool first = true; - for (LinkMap::const_iterator it=obj._links.begin(); it!=obj._links.end(); ++it) { - if (first) { first=false; } else { os << ", "; } - os << "'" << it->first << "': ["; - bool firstinner = true; - for (LinkSet::const_iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) { - if (firstinner) { firstinner=false; } else { os << ", "; } - os << "'" << *it2 << "'"; - } - os << "]"; - } - os << "}"; - return os; -} -//}}} -std::ostream& operator<<(std::ostream& os, const Payload& obj)//{{{ -{ - os << "{"; - bool first = true; - for (std::map<std::string, std::string>::const_iterator it=obj._store.begin(); it!=obj._store.end(); ++it) { - if (first) { first=false; } else { os << ", "; } - os << "'" << it->first << "':'" << it->second << "'"; - } - os << "}"; - return os; -} -//}}} -std::ostream& operator<<(std::ostream& os, const IUInterface& obj)//{{{ -{ - os << "IUInterface(uid='" << obj.uid() << "'"; - os << ", category='" << obj.category() << "'"; - os << ", revision=" << obj.revision(); - os << ", committed=" << (obj.committed()?"True":"False"); - os << ", owner_name='" << obj.owner_name() << "'"; - os << ", payload="; - os << obj.const_payload(); - os << ", links="; - os << obj._links; - os << ")"; - return os; -} -//}}} -std::ostream& operator<<(std::ostream& os, const IUPayloadUpdate& obj)//{{{ -{ - os << "PayloadUpdate(uid=" << obj.uid << ", revision=" << obj.revision; - os << ", writer_name=" << obj.writer_name << ", is_delta=" << (obj.is_delta?"True":"False"); - os << ", new_items = {"; - bool first = true; - for (std::map<std::string, std::string>::const_iterator it=obj.new_items.begin(); it!=obj.new_items.end(); ++it) { - if (first) { first=false; } else { os << ", "; } - os << "'" << it->first << "':'" << it->second << "'"; - } - os << "}, keys_to_remove = ["; - first = true; - for (std::vector<std::string>::const_iterator it=obj.keys_to_remove.begin(); it!=obj.keys_to_remove.end(); ++it) { - if (first) { first=false; } else { os << ", "; } - os << "'" << *it << "'"; - } - os << "])"; - return os; -} -//}}} -std::ostream& operator<<(std::ostream& os, const IULinkUpdate& obj)//{{{ -{ - os << "LinkUpdate(uid=" << obj.uid << ", revision=" << obj.revision; - os << ", writer_name=" << obj.writer_name << ", is_delta=" << (obj.is_delta?"True":"False"); - os << ", new_links = {"; - bool first = true; - for (std::map<std::string, std::set<std::string> >::const_iterator it=obj.new_links.begin(); it!=obj.new_links.end(); ++it) { - if (first) { first=false; } else { os << ", "; } - os << "'" << it->first << "': ["; - bool ffirst = true; - for (std::set<std::string>::const_iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) { - if (ffirst) { ffirst=false; } else { os << ", "; } - os << "'" << *it2 << "'"; - } - os << "]"; - } - os << "}, links_to_remove = {"; - first = true; - for (std::map<std::string, std::set<std::string> >::const_iterator it=obj.links_to_remove.begin(); it!=obj.links_to_remove.end(); ++it) { - if (first) { first=false; } else { os << ", "; } - os << "'" << it->first << "': ["; - bool ffirst = true; - for (std::set<std::string>::const_iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) { - if (ffirst) { ffirst=false; } else { os << ", "; } - os << "'" << *it2 << "'"; - } - os << "]"; - } - os << "})"; - return os; -} -//}}} - -// SmartLinkMap//{{{ - -LinkSet SmartLinkMap::empty_link_set; -void SmartLinkMap::_add_and_remove_links(const LinkMap& add, const LinkMap& remove) -{ - // remove specified links - for (LinkMap::const_iterator it = remove.begin(); it != remove.end(); ++it ) { - // if link type exists - if (_links.count(it->first) > 0) { - // remove one by one - for (LinkSet::const_iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) { - _links[it->first].erase(*it2); - } - // wipe the type key if no more links are left - if (_links[it->first].size() == 0) { - _links.erase(it->first); - } - } - } - // add specified links - for (LinkMap::const_iterator it = add.begin(); it != add.end(); ++it ) { - for (LinkSet::const_iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) { - _links[it->first].insert(*it2); - } - } -} -void SmartLinkMap::_replace_links(const LinkMap& links) -{ - //_links.clear(); - _links=links; -} -const LinkSet& SmartLinkMap::get_links(const std::string& key) -{ - LinkMap::const_iterator it = _links.find(key); - if (it==_links.end()) return empty_link_set; - return it->second; -} -const LinkMap& SmartLinkMap::get_all_links() -{ - return _links; -} -//}}} - -// IUEventHandler//{{{ -IUEventHandler::IUEventHandler(IUEventHandlerFunction function, IUEventType event_mask, const std::string& category) -: _function(function), _event_mask(event_mask), _for_all_categories(false) -{ - if (category=="") { - _for_all_categories = true; - } else { - _categories.insert(category); - } -} -IUEventHandler::IUEventHandler(IUEventHandlerFunction function, IUEventType event_mask, const std::set<std::string>& categories) -: _function(function), _event_mask(event_mask), _for_all_categories(false) -{ - if (categories.size()==0) { - _for_all_categories = true; - } else { - _categories = categories; - } -} -void IUEventHandler::call(Buffer* buffer, boost::shared_ptr<IUInterface> iu, bool local, IUEventType event_type, const std::string& category) -{ - if (_condition_met(event_type, category)) { - //IUInterface::ptr iu = buffer->get(uid); - //if (iu) { -#if VERBOSE_HANDLERS == 1 - std::cout << "[" << pthread_self() << " handler ENTER]" << std::endl; -#endif - _function(iu, event_type, local); -#if VERBOSE_HANDLERS == 1 - std::cout << "[" << pthread_self() << " handler EXIT]" << std::endl; -#endif - //} - } -} -//}}} - -// Buffer//{{{ -void Buffer::_allocate_unique_name(const std::string& basename, const std::string& function) { - std::string uuid = ipaaca::generate_uuid_string(); - _basename = basename; - _uuid = uuid.substr(0,8); - _unique_name = "/ipaaca/component/" + _basename + "ID" + _uuid + "/" + function; -} -void Buffer::register_handler(IUEventHandlerFunction function, IUEventType event_mask, const std::set<std::string>& categories) -{ - IUEventHandler::ptr handler = IUEventHandler::ptr(new IUEventHandler(function, event_mask, categories)); - _event_handlers.push_back(handler); -} -void Buffer::register_handler(IUEventHandlerFunction function, IUEventType event_mask, const std::string& category) -{ - IUEventHandler::ptr handler = IUEventHandler::ptr(new IUEventHandler(function, event_mask, category)); - _event_handlers.push_back(handler); -} -void Buffer::call_iu_event_handlers(boost::shared_ptr<IUInterface> iu, bool local, IUEventType event_type, const std::string& category) -{ - //IPAACA_INFO("handling an event " << ipaaca::iu_event_type_to_str(event_type) << " for IU " << iu->uid()) - for (std::vector<IUEventHandler::ptr>::iterator it = _event_handlers.begin(); it != _event_handlers.end(); ++it) { - (*it)->call(this, iu, local, event_type, category); - } -} -//}}} - -// Callbacks for OutputBuffer//{{{ -CallbackIUPayloadUpdate::CallbackIUPayloadUpdate(Buffer* buffer): _buffer(buffer) { } -CallbackIULinkUpdate::CallbackIULinkUpdate(Buffer* buffer): _buffer(buffer) { } -CallbackIUCommission::CallbackIUCommission(Buffer* buffer): _buffer(buffer) { } - -boost::shared_ptr<int> CallbackIUPayloadUpdate::call(const std::string& methodName, boost::shared_ptr<IUPayloadUpdate> update) -{ - //std::cout << "-- Received a modify_payload with " << update->new_items.size() << " keys to merge." << std::endl; - IUInterface::ptr iui = _buffer->get(update->uid); - if (! iui) { - IPAACA_WARNING("Remote InBuffer tried to spuriously write non-existent IU " << update->uid) - return boost::shared_ptr<int>(new int(0)); - } - IU::ptr iu = boost::static_pointer_cast<IU>(iui); - iu->_revision_lock.lock(); - if ((update->revision != 0) && (update->revision != iu->_revision)) { - IPAACA_INFO("Remote write operation failed because request was out of date; IU " << update->uid) - iu->_revision_lock.unlock(); - return boost::shared_ptr<int>(new int(0)); - } - if (update->is_delta) { - // FIXME FIXME this is an unsolved problem atm: deletes in a delta update are - // sent individually. We should have something like _internal_merge_and_remove - for (std::vector<std::string>::const_iterator it=update->keys_to_remove.begin(); it!=update->keys_to_remove.end(); ++it) { - iu->payload()._internal_remove(*it, update->writer_name); //_buffer->unique_name()); - } - // but it is solved for pure merges: - iu->payload()._internal_merge(update->new_items, update->writer_name); - } else { - iu->payload()._internal_replace_all(update->new_items, update->writer_name); //_buffer->unique_name()); - } - //std::cout << "-- Calling update handler due to remote write." << std::endl; - _buffer->call_iu_event_handlers(iu, true, IU_UPDATED, iu->category()); - revision_t revision = iu->revision(); - iu->_revision_lock.unlock(); - return boost::shared_ptr<int>(new int(revision)); -} - -boost::shared_ptr<int> CallbackIULinkUpdate::call(const std::string& methodName, boost::shared_ptr<IULinkUpdate> update) -{ - IUInterface::ptr iui = _buffer->get(update->uid); - if (! iui) { - IPAACA_WARNING("Remote InBuffer tried to spuriously write non-existent IU " << update->uid) - return boost::shared_ptr<int>(new int(0)); - } - IU::ptr iu = boost::static_pointer_cast<IU>(iui); - iu->_revision_lock.lock(); - if ((update->revision != 0) && (update->revision != iu->_revision)) { - IPAACA_INFO("Remote write operation failed because request was out of date; IU " << update->uid) - iu->_revision_lock.unlock(); - return boost::shared_ptr<int>(new int(0)); - } - if (update->is_delta) { - iu->modify_links(update->new_links, update->links_to_remove, update->writer_name); - } else { - iu->set_links(update->new_links, update->writer_name); - } - _buffer->call_iu_event_handlers(iu, true, IU_LINKSUPDATED, iu->category()); - revision_t revision = iu->revision(); - iu->_revision_lock.unlock(); - return boost::shared_ptr<int>(new int(revision)); -} -boost::shared_ptr<int> CallbackIUCommission::call(const std::string& methodName, boost::shared_ptr<protobuf::IUCommission> update) -{ - IUInterface::ptr iui = _buffer->get(update->uid()); - if (! iui) { - IPAACA_WARNING("Remote InBuffer tried to spuriously write non-existent IU " << update->uid()) - return boost::shared_ptr<int>(new int(0)); - } - IU::ptr iu = boost::static_pointer_cast<IU>(iui); - iu->_revision_lock.lock(); - if ((update->revision() != 0) && (update->revision() != iu->_revision)) { - IPAACA_INFO("Remote write operation failed because request was out of date; IU " << update->uid()) - iu->_revision_lock.unlock(); - return boost::shared_ptr<int>(new int(0)); - } - if (iu->committed()) { - return boost::shared_ptr<int>(new int(0)); - } else { - } - iu->_internal_commit(update->writer_name()); - _buffer->call_iu_event_handlers(iu, true, IU_LINKSUPDATED, iu->category()); - revision_t revision = iu->revision(); - iu->_revision_lock.unlock(); - return boost::shared_ptr<int>(new int(revision)); -} - -//}}} - -// OutputBuffer//{{{ - -OutputBuffer::OutputBuffer(const std::string& basename) -:Buffer(basename, "OB") -{ - _id_prefix = _basename + "-" + _uuid + "-IU-"; - _initialize_server(); -} -void OutputBuffer::_initialize_server() -{ - _server = getFactory().createServer( Scope( _unique_name ) ); - _server->registerMethod("updatePayload", Server::CallbackPtr(new CallbackIUPayloadUpdate(this))); - _server->registerMethod("updateLinks", Server::CallbackPtr(new CallbackIULinkUpdate(this))); - _server->registerMethod("commit", Server::CallbackPtr(new CallbackIUCommission(this))); -} -OutputBuffer::ptr OutputBuffer::create(const std::string& basename) -{ - Initializer::initialize_ipaaca_rsb_if_needed(); - return OutputBuffer::ptr(new OutputBuffer(basename)); -} -IUInterface::ptr OutputBuffer::get(const std::string& iu_uid) -{ - IUStore::iterator it = _iu_store.find(iu_uid); - if (it==_iu_store.end()) return IUInterface::ptr(); - return it->second; -} -std::set<IUInterface::ptr> OutputBuffer::get_ius() -{ - std::set<IUInterface::ptr> set; - for (IUStore::iterator it=_iu_store.begin(); it!=_iu_store.end(); ++it) set.insert(it->second); - return set; -} - -void OutputBuffer::_send_iu_link_update(IUInterface* iu, bool is_delta, revision_t revision, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name) -{ - IULinkUpdate* lup = new ipaaca::IULinkUpdate(); - Informer<ipaaca::IULinkUpdate>::DataPtr ldata(lup); - lup->uid = iu->uid(); - lup->is_delta = is_delta; - lup->revision = revision; - lup->is_delta = true; - lup->new_links = new_links; - if (is_delta) lup->links_to_remove = links_to_remove; - if (writer_name=="") lup->writer_name = _unique_name; - else lup->writer_name = writer_name; - Informer<AnyType>::Ptr informer = _get_informer(iu->category()); - informer->publish(ldata); -} - -void OutputBuffer::_send_iu_payload_update(IUInterface* iu, bool is_delta, revision_t revision, const std::map<std::string, std::string>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name) -{ - IUPayloadUpdate* pup = new ipaaca::IUPayloadUpdate(); - Informer<ipaaca::IUPayloadUpdate>::DataPtr pdata(pup); - pup->uid = iu->uid(); - pup->is_delta = is_delta; - pup->revision = revision; - pup->new_items = new_items; - if (is_delta) pup->keys_to_remove = keys_to_remove; - if (writer_name=="") pup->writer_name = _unique_name; - else pup->writer_name = writer_name; - Informer<AnyType>::Ptr informer = _get_informer(iu->category()); - informer->publish(pdata); -} - -void OutputBuffer::_send_iu_commission(IUInterface* iu, revision_t revision, const std::string& writer_name) -{ - Informer<protobuf::IUCommission>::DataPtr data(new protobuf::IUCommission()); - data->set_uid(iu->uid()); - data->set_revision(revision); - if (writer_name=="") data->set_writer_name(_unique_name); - else data->set_writer_name(writer_name); - - Informer<AnyType>::Ptr informer = _get_informer(iu->category()); - informer->publish(data); -} - -void OutputBuffer::add(IU::ptr iu) -{ - if (_iu_store.count(iu->uid()) > 0) { - throw IUPublishedError(); - } - if (iu->is_published()) { - throw IUPublishedError(); - } - if (iu->access_mode() != IU_ACCESS_MESSAGE) { - // (for Message-type IUs: do not actually store them) - _iu_store[iu->uid()] = iu; - } - iu->_associate_with_buffer(this); //shared_from_this()); - _publish_iu(iu); -} - -void OutputBuffer::_publish_iu(IU::ptr iu) -{ - Informer<AnyType>::Ptr informer = _get_informer(iu->_category); - Informer<ipaaca::IU>::DataPtr iu_data(iu); - informer->publish(iu_data); -} - -Informer<AnyType>::Ptr OutputBuffer::_get_informer(const std::string& category) -{ - if (_informer_store.count(category) > 0) { - return _informer_store[category]; - } else { - //IPAACA_INFO("Making new informer for category " << category) - std::string scope_string = "/ipaaca/category/" + category; - Informer<AnyType>::Ptr informer = getFactory().createInformer<AnyType> ( Scope(scope_string)); - _informer_store[category] = informer; - return informer; - } -} -boost::shared_ptr<IU> OutputBuffer::remove(const std::string& iu_uid) -{ - IUStore::iterator it = _iu_store.find(iu_uid); - if (it == _iu_store.end()) { - IPAACA_WARNING("Removal of IU " << iu_uid << " requested, but not present in our OutputBuffer") - //throw IUNotFoundError(); - } - IU::ptr iu = it->second; - _retract_iu(iu); - _iu_store.erase(iu_uid); - return iu; -} -boost::shared_ptr<IU> OutputBuffer::remove(IU::ptr iu) -{ - return remove(iu->uid()); // to make sure it is in the store -} - -void OutputBuffer::_retract_iu(IU::ptr iu) -{ - Informer<protobuf::IURetraction>::DataPtr data(new protobuf::IURetraction()); - data->set_uid(iu->uid()); - data->set_revision(iu->revision()); - Informer<AnyType>::Ptr informer = _get_informer(iu->category()); - informer->publish(data); -} - - -//}}} - -// InputBuffer//{{{ -InputBuffer::InputBuffer(const std::string& basename, const std::set<std::string>& category_interests) -:Buffer(basename, "IB") -{ - for (std::set<std::string>::const_iterator it=category_interests.begin(); it!=category_interests.end(); ++it) { - _create_category_listener_if_needed(*it); - } -} -InputBuffer::InputBuffer(const std::string& basename, const std::vector<std::string>& category_interests) -:Buffer(basename, "IB") -{ - for (std::vector<std::string>::const_iterator it=category_interests.begin(); it!=category_interests.end(); ++it) { - _create_category_listener_if_needed(*it); - } -} -InputBuffer::InputBuffer(const std::string& basename, const std::string& category_interest1) -:Buffer(basename, "IB") -{ - _create_category_listener_if_needed(category_interest1); -} -InputBuffer::InputBuffer(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2) -:Buffer(basename, "IB") -{ - _create_category_listener_if_needed(category_interest1); - _create_category_listener_if_needed(category_interest2); -} -InputBuffer::InputBuffer(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2, const std::string& category_interest3) -:Buffer(basename, "IB") -{ - _create_category_listener_if_needed(category_interest1); - _create_category_listener_if_needed(category_interest2); - _create_category_listener_if_needed(category_interest3); -} -InputBuffer::InputBuffer(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2, const std::string& category_interest3, const std::string& category_interest4) -:Buffer(basename, "IB") -{ - _create_category_listener_if_needed(category_interest1); - _create_category_listener_if_needed(category_interest2); - _create_category_listener_if_needed(category_interest3); - _create_category_listener_if_needed(category_interest4); -} - - -InputBuffer::ptr InputBuffer::create(const std::string& basename, const std::set<std::string>& category_interests) -{ - Initializer::initialize_ipaaca_rsb_if_needed(); - return InputBuffer::ptr(new InputBuffer(basename, category_interests)); -} -InputBuffer::ptr InputBuffer::create(const std::string& basename, const std::vector<std::string>& category_interests) -{ - Initializer::initialize_ipaaca_rsb_if_needed(); - return InputBuffer::ptr(new InputBuffer(basename, category_interests)); -} -InputBuffer::ptr InputBuffer::create(const std::string& basename, const std::string& category_interest1) -{ - Initializer::initialize_ipaaca_rsb_if_needed(); - return InputBuffer::ptr(new InputBuffer(basename, category_interest1)); -} -InputBuffer::ptr InputBuffer::create(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2) -{ - Initializer::initialize_ipaaca_rsb_if_needed(); - return InputBuffer::ptr(new InputBuffer(basename, category_interest1, category_interest2)); -} -InputBuffer::ptr InputBuffer::create(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2, const std::string& category_interest3) -{ - Initializer::initialize_ipaaca_rsb_if_needed(); - return InputBuffer::ptr(new InputBuffer(basename, category_interest1, category_interest2, category_interest3)); -} -InputBuffer::ptr InputBuffer::create(const std::string& basename, const std::string& category_interest1, const std::string& category_interest2, const std::string& category_interest3, const std::string& category_interest4) -{ - Initializer::initialize_ipaaca_rsb_if_needed(); - return InputBuffer::ptr(new InputBuffer(basename, category_interest1, category_interest2, category_interest3, category_interest4)); -} - -IUInterface::ptr InputBuffer::get(const std::string& iu_uid) -{ - RemotePushIUStore::iterator it = _iu_store.find(iu_uid); // TODO genericize - if (it==_iu_store.end()) return IUInterface::ptr(); - return it->second; -} -std::set<IUInterface::ptr> InputBuffer::get_ius() -{ - std::set<IUInterface::ptr> set; - for (RemotePushIUStore::iterator it=_iu_store.begin(); it!=_iu_store.end(); ++it) set.insert(it->second); // TODO genericize - return set; -} - - -RemoteServerPtr InputBuffer::_get_remote_server(const std::string& unique_server_name) -{ - std::map<std::string, RemoteServerPtr>::iterator it = _remote_server_store.find(unique_server_name); - if (it!=_remote_server_store.end()) return it->second; - RemoteServerPtr remote_server = getFactory().createRemoteServer(Scope(unique_server_name)); - _remote_server_store[unique_server_name] = remote_server; - return remote_server; -} - -ListenerPtr InputBuffer::_create_category_listener_if_needed(const std::string& category) -{ - std::map<std::string, ListenerPtr>::iterator it = _listener_store.find(category); - if (it!=_listener_store.end()) return it->second; - //IPAACA_INFO("Creating a new listener for category " << category) - std::string scope_string = "/ipaaca/category/" + category; - ListenerPtr listener = getFactory().createListener( Scope(scope_string) ); - HandlerPtr event_handler = HandlerPtr( - new EventFunctionHandler( - boost::bind(&InputBuffer::_handle_iu_events, this, _1) - ) - ); - listener->addHandler(event_handler); - _listener_store[category] = listener; - return listener; - /* - '''Return (or create, store and return) a category listener.''' - if iu_category in self._listener_store: return self._informer_store[iu_category] - cat_listener = rsb.createListener(rsb.Scope("/ipaaca/category/"+str(iu_category)), config=self._participant_config) - cat_listener.addHandler(self._handle_iu_events) - self._listener_store[iu_category] = cat_listener - self._category_interests.append(iu_category) - logger.info("Added listener in scope "+"/ipaaca/category/"+iu_category) - return cat_listener - */ -} -void InputBuffer::_handle_iu_events(EventPtr event) -{ - std::string type = event->getType(); - if (type == "ipaaca::RemotePushIU") { - boost::shared_ptr<RemotePushIU> iu = boost::static_pointer_cast<RemotePushIU>(event->getData()); - if (_iu_store.count(iu->category()) > 0) { - // already got the IU... ignore - } else { - _iu_store[iu->uid()] = iu; - iu->_set_buffer(this); - call_iu_event_handlers(iu, false, IU_ADDED, iu->category() ); - } - //IPAACA_INFO( "New RemotePushIU state: " << (*iu) ) - } else if (type == "ipaaca::RemoteMessage") { - boost::shared_ptr<RemoteMessage> iu = boost::static_pointer_cast<RemoteMessage>(event->getData()); - //_iu_store[iu->uid()] = iu; - //iu->_set_buffer(this); - //std::cout << "REFCNT after cast, before calling handlers: " << iu.use_count() << std::endl; - call_iu_event_handlers(iu, false, IU_MESSAGE, iu->category() ); - //_iu_store.erase(iu->uid()); - } else { - RemotePushIUStore::iterator it; - if (type == "ipaaca::IUPayloadUpdate") { - boost::shared_ptr<IUPayloadUpdate> update = boost::static_pointer_cast<IUPayloadUpdate>(event->getData()); - //IPAACA_INFO("** writer name: " << update->writer_name) - if (update->writer_name == _unique_name) { - return; - } - it = _iu_store.find(update->uid); - if (it == _iu_store.end()) { - IPAACA_INFO("Ignoring UPDATED message for an IU that we did not fully receive before") - return; - } - // - it->second->_apply_update(update); - call_iu_event_handlers(it->second, false, IU_UPDATED, it->second->category() ); - // - // - } else if (type == "ipaaca::IULinkUpdate") { - boost::shared_ptr<IULinkUpdate> update = boost::static_pointer_cast<IULinkUpdate>(event->getData()); - if (update->writer_name == _unique_name) { - return; - } - it = _iu_store.find(update->uid); - if (it == _iu_store.end()) { - IPAACA_INFO("Ignoring LINKSUPDATED message for an IU that we did not fully receive before") - return; - } - // - it->second->_apply_link_update(update); - call_iu_event_handlers(it->second, false, IU_LINKSUPDATED, it->second->category() ); - // - // - } else if (type == "ipaaca::protobuf::IUCommission") { - boost::shared_ptr<protobuf::IUCommission> update = boost::static_pointer_cast<protobuf::IUCommission>(event->getData()); - if (update->writer_name() == _unique_name) { - return; - } - it = _iu_store.find(update->uid()); - if (it == _iu_store.end()) { - IPAACA_INFO("Ignoring COMMITTED message for an IU that we did not fully receive before") - return; - } - // - it->second->_apply_commission(); - it->second->_revision = update->revision(); - call_iu_event_handlers(it->second, false, IU_COMMITTED, it->second->category() ); - // - // - } else if (type == "ipaaca::protobuf::IURetraction") { - boost::shared_ptr<protobuf::IURetraction> update = boost::static_pointer_cast<protobuf::IURetraction>(event->getData()); - it = _iu_store.find(update->uid()); - if (it == _iu_store.end()) { - IPAACA_INFO("Ignoring RETRACTED message for an IU that we did not fully receive before") - return; - } - // - it->second->_revision = update->revision(); - it->second->_apply_retraction(); - // remove from InputBuffer FIXME: this is a crossover between retracted and deleted behavior - _iu_store.erase(it->first); - // and call the handler. IU reference is still valid for this call, although removed from buffer. - call_iu_event_handlers(it->second, false, IU_COMMITTED, it->second->category() ); - // - } else { - std::cout << "(Unhandled Event type " << type << " !)" << std::endl; - return; - } - //IPAACA_INFO( "New RemotePushIU state: " << *(it->second) ) - } -} - -//}}} - - - -// IUInterface//{{{ - -IUInterface::IUInterface() -: _buffer(NULL), _committed(false), _retracted(false) -{ -} - -void IUInterface::_set_uid(const std::string& uid) { - if (_uid != "") { - throw IUAlreadyHasAnUIDError(); - } - _uid = uid; -} - -void IUInterface::_set_buffer(Buffer* buffer) { //boost::shared_ptr<Buffer> buffer) { - if (_buffer) { - throw IUAlreadyInABufferError(); - } - _buffer = buffer; - -} - -void IUInterface::_set_owner_name(const std::string& owner_name) { - if (_owner_name != "") { - throw IUAlreadyHasAnOwnerNameError(); - } - _owner_name = owner_name; -} - -/// set the buffer pointer and the owner names of IU and Payload -void IUInterface::_associate_with_buffer(Buffer* buffer) { //boost::shared_ptr<Buffer> buffer) { - _set_buffer(buffer); // will throw if already set - _set_owner_name(buffer->unique_name()); - payload()._set_owner_name(buffer->unique_name()); -} - -/// C++-specific convenience function to add one single link -void IUInterface::add_link(const std::string& type, const std::string& target, const std::string& writer_name) -{ - LinkMap none; - LinkMap add; - add[type].insert(target); - _modify_links(true, add, none, writer_name); - _add_and_remove_links(add, none); -} -/// C++-specific convenience function to remove one single link -void IUInterface::remove_link(const std::string& type, const std::string& target, const std::string& writer_name) -{ - LinkMap none; - LinkMap remove; - remove[type].insert(target); - _modify_links(true, none, remove, writer_name); - _add_and_remove_links(none, remove); -} - -void IUInterface::add_links(const std::string& type, const LinkSet& targets, const std::string& writer_name) -{ - LinkMap none; - LinkMap add; - add[type] = targets; - _modify_links(true, add, none, writer_name); - _add_and_remove_links(add, none); -} - -void IUInterface::remove_links(const std::string& type, const LinkSet& targets, const std::string& writer_name) -{ - LinkMap none; - LinkMap remove; - remove[type] = targets; - _modify_links(true, none, remove, writer_name); - _add_and_remove_links(none, remove); -} - -void IUInterface::modify_links(const LinkMap& add, const LinkMap& remove, const std::string& writer_name) -{ - _modify_links(true, add, remove, writer_name); - _add_and_remove_links(add, remove); -} - -void IUInterface::set_links(const LinkMap& links, const std::string& writer_name) -{ - LinkMap none; - _modify_links(false, links, none, writer_name); - _replace_links(links); -} - -//}}} - -// IU//{{{ -IU::ptr IU::create(const std::string& category, IUAccessMode access_mode, bool read_only, const std::string& payload_type) -{ - IU::ptr iu = IU::ptr(new IU(category, access_mode, read_only, payload_type)); /* params */ //)); - iu->_payload.initialize(iu); - return iu; -} - -IU::IU(const std::string& category, IUAccessMode access_mode, bool read_only, const std::string& payload_type) -{ - _revision = 1; - _uid = ipaaca::generate_uuid_string(); - _category = category; - _payload_type = payload_type; - // payload initialization deferred to IU::create(), above - _read_only = read_only; - _access_mode = access_mode; - _committed = false; -} - -void IU::_modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name) -{ - _revision_lock.lock(); - if (_committed) { - _revision_lock.unlock(); - throw IUCommittedError(); - } - _increase_revision_number(); - if (is_published()) { - _buffer->_send_iu_link_update(this, is_delta, _revision, new_links, links_to_remove, writer_name); - } - _revision_lock.unlock(); -} -void IU::_modify_payload(bool is_delta, const std::map<std::string, std::string>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name) -{ - _revision_lock.lock(); - if (_committed) { - _revision_lock.unlock(); - throw IUCommittedError(); - } - _increase_revision_number(); - if (is_published()) { - //std::cout << "Sending a payload update with " << new_items.size() << " entries to merge." << std::endl; - _buffer->_send_iu_payload_update(this, is_delta, _revision, new_items, keys_to_remove, writer_name); - } - _revision_lock.unlock(); -} - -void IU::commit() -{ - _internal_commit(); -} - -void IU::_internal_commit(const std::string& writer_name) -{ - _revision_lock.lock(); - if (_committed) { - _revision_lock.unlock(); - throw IUCommittedError(); - } - _increase_revision_number(); - _committed = true; - if (is_published()) { - _buffer->_send_iu_commission(this, _revision, writer_name); - } - _revision_lock.unlock(); -} -//}}} -// Message//{{{ -Message::ptr Message::create(const std::string& category, IUAccessMode access_mode, bool read_only, const std::string& payload_type) -{ - Message::ptr iu = Message::ptr(new Message(category, access_mode, read_only, payload_type)); /* params */ //)); - iu->_payload.initialize(iu); - return iu; -} - -Message::Message(const std::string& category, IUAccessMode access_mode, bool read_only, const std::string& payload_type) -: IU(category, access_mode, read_only, payload_type) -{ -} - -void Message::_modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name) -{ - if (is_published()) { - IPAACA_INFO("Info: modifying a Message after sending has no global effects") - } -} -void Message::_modify_payload(bool is_delta, const std::map<std::string, std::string>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name) -{ - if (is_published()) { - IPAACA_INFO("Info: modifying a Message after sending has no global effects") - } -} - -void Message::_internal_commit(const std::string& writer_name) -{ - if (is_published()) { - IPAACA_INFO("Info: committing to a Message after sending has no global effects") - } - -} -//}}} - -// RemotePushIU//{{{ - -RemotePushIU::ptr RemotePushIU::create() -{ - RemotePushIU::ptr iu = RemotePushIU::ptr(new RemotePushIU(/* params */)); - iu->_payload.initialize(iu); - return iu; -} -RemotePushIU::RemotePushIU() -{ - // nothing -} -void RemotePushIU::_modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name) -{ - if (_committed) { - throw IUCommittedError(); - } - if (_read_only) { - throw IUReadOnlyError(); - } - RemoteServerPtr server = boost::static_pointer_cast<InputBuffer>(_buffer)->_get_remote_server(_owner_name); - IULinkUpdate::ptr update = IULinkUpdate::ptr(new IULinkUpdate()); - update->uid = _uid; - update->revision = _revision; - update->is_delta = is_delta; - update->writer_name = _buffer->unique_name(); - update->new_links = new_links; - update->links_to_remove = links_to_remove; - boost::shared_ptr<int> result = server->call<int>("updateLinks", update, IPAACA_REMOTE_SERVER_TIMEOUT); // TODO - if (*result == 0) { - throw IUUpdateFailedError(); - } else { - _revision = *result; - } -} -void RemotePushIU::_modify_payload(bool is_delta, const std::map<std::string, std::string>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name) -{ - //std::cout << "-- Sending a modify_payload with " << new_items.size() << " keys to merge." << std::endl; - if (_committed) { - throw IUCommittedError(); - } - if (_read_only) { - throw IUReadOnlyError(); - } - RemoteServerPtr server = boost::static_pointer_cast<InputBuffer>(_buffer)->_get_remote_server(_owner_name); - IUPayloadUpdate::ptr update = IUPayloadUpdate::ptr(new IUPayloadUpdate()); - update->uid = _uid; - update->revision = _revision; - update->is_delta = is_delta; - update->writer_name = _buffer->unique_name(); - update->new_items = new_items; - update->keys_to_remove = keys_to_remove; - boost::shared_ptr<int> result = server->call<int>("updatePayload", update, IPAACA_REMOTE_SERVER_TIMEOUT); // TODO - if (*result == 0) { - throw IUUpdateFailedError(); - } else { - _revision = *result; - } -} - -void RemotePushIU::commit() -{ - if (_read_only) { - throw IUReadOnlyError(); - } - if (_committed) { - // Following python version: ignoring multiple commit - return; - } - RemoteServerPtr server = boost::static_pointer_cast<InputBuffer>(_buffer)->_get_remote_server(_owner_name); - boost::shared_ptr<protobuf::IUCommission> update = boost::shared_ptr<protobuf::IUCommission>(new protobuf::IUCommission()); - update->set_uid(_uid); - update->set_revision(_revision); - update->set_writer_name(_buffer->unique_name()); - boost::shared_ptr<int> result = server->call<int>("commit", update, IPAACA_REMOTE_SERVER_TIMEOUT); // TODO - if (*result == 0) { - throw IUUpdateFailedError(); - } else { - _revision = *result; - } -} - -void RemotePushIU::_apply_link_update(IULinkUpdate::ptr update) -{ - _revision = update->revision; - if (update->is_delta) { - _add_and_remove_links(update->new_links, update->links_to_remove); - } else { - _replace_links(update->new_links); - } -} -void RemotePushIU::_apply_update(IUPayloadUpdate::ptr update) -{ - _revision = update->revision; - if (update->is_delta) { - for (std::vector<std::string>::const_iterator it=update->keys_to_remove.begin(); it!=update->keys_to_remove.end(); ++it) { - _payload._remotely_enforced_delitem(*it); - } - for (std::map<std::string, std::string>::const_iterator it=update->new_items.begin(); it!=update->new_items.end(); ++it) { - _payload._remotely_enforced_setitem(it->first, it->second); - } - } else { - _payload._remotely_enforced_wipe(); - for (std::map<std::string, std::string>::const_iterator it=update->new_items.begin(); it!=update->new_items.end(); ++it) { - _payload._remotely_enforced_setitem(it->first, it->second); - } - } -} -void RemotePushIU::_apply_commission() -{ - _committed = true; -} -void RemotePushIU::_apply_retraction() -{ - _retracted = true; -} -//}}} - -// RemoteMessage//{{{ - -RemoteMessage::ptr RemoteMessage::create() -{ - RemoteMessage::ptr iu = RemoteMessage::ptr(new RemoteMessage(/* params */)); - iu->_payload.initialize(iu); - return iu; -} -RemoteMessage::RemoteMessage() -{ - // nothing -} -void RemoteMessage::_modify_links(bool is_delta, const LinkMap& new_links, const LinkMap& links_to_remove, const std::string& writer_name) -{ - IPAACA_INFO("Info: modifying a RemoteMessage only has local effects") -} -void RemoteMessage::_modify_payload(bool is_delta, const std::map<std::string, std::string>& new_items, const std::vector<std::string>& keys_to_remove, const std::string& writer_name) -{ - IPAACA_INFO("Info: modifying a RemoteMessage only has local effects") -} -void RemoteMessage::commit() -{ - IPAACA_INFO("Info: committing to a RemoteMessage only has local effects") -} - -void RemoteMessage::_apply_link_update(IULinkUpdate::ptr update) -{ - IPAACA_WARNING("Warning: should never be called: RemoteMessage::_apply_link_update") - _revision = update->revision; - if (update->is_delta) { - _add_and_remove_links(update->new_links, update->links_to_remove); - } else { - _replace_links(update->new_links); - } -} -void RemoteMessage::_apply_update(IUPayloadUpdate::ptr update) -{ - IPAACA_WARNING("Warning: should never be called: RemoteMessage::_apply_update") - _revision = update->revision; - if (update->is_delta) { - for (std::vector<std::string>::const_iterator it=update->keys_to_remove.begin(); it!=update->keys_to_remove.end(); ++it) { - _payload._remotely_enforced_delitem(*it); - } - for (std::map<std::string, std::string>::const_iterator it=update->new_items.begin(); it!=update->new_items.end(); ++it) { - _payload._remotely_enforced_setitem(it->first, it->second); - } - } else { - _payload._remotely_enforced_wipe(); - for (std::map<std::string, std::string>::const_iterator it=update->new_items.begin(); it!=update->new_items.end(); ++it) { - _payload._remotely_enforced_setitem(it->first, it->second); - } - } -} -void RemoteMessage::_apply_commission() -{ - IPAACA_WARNING("Warning: should never be called: RemoteMessage::_apply_commission") - _committed = true; -} -void RemoteMessage::_apply_retraction() -{ - IPAACA_WARNING("Warning: should never be called: RemoteMessage::_apply_retraction") - _retracted = true; -} - -//}}} - - - -// PayloadEntryProxy//{{{ - -PayloadEntryProxy::PayloadEntryProxy(Payload* payload, const std::string& key) -: _payload(payload), _key(key) -{ -} -PayloadEntryProxy& PayloadEntryProxy::operator=(const std::string& value) -{ - //std::cout << "operator=(string)" << std::endl; - _payload->set(_key, value); - return *this; -} -PayloadEntryProxy& PayloadEntryProxy::operator=(const char* value) -{ - //std::cout << "operator=(const char*)" << std::endl; - _payload->set(_key, value); - return *this; -} -PayloadEntryProxy& PayloadEntryProxy::operator=(double value) -{ - //std::cout << "operator=(double)" << std::endl; - _payload->set(_key, boost::lexical_cast<std::string>(value)); - return *this; -} -PayloadEntryProxy& PayloadEntryProxy::operator=(bool value) -{ - //std::cout << "operator=(bool)" << std::endl; - _payload->set(_key, boost::lexical_cast<std::string>(value)); - return *this; -} -PayloadEntryProxy::operator std::string() -{ - return _payload->get(_key); -} -PayloadEntryProxy::operator bool() -{ - std::string s = operator std::string(); - return ((s=="1")||(s=="true")||(s=="True")); -} -PayloadEntryProxy::operator long() -{ - //return boost::lexical_cast<long>(operator std::string().c_str()); - return atof(operator std::string().c_str()); -} -PayloadEntryProxy::operator double() -{ - //return boost::lexical_cast<double>(operator std::string().c_str()); - return atol(operator std::string().c_str()); -} -//}}} - -// Payload//{{{ - -void Payload::initialize(boost::shared_ptr<IUInterface> iu) -{ - _iu = boost::weak_ptr<IUInterface>(iu); -} - -PayloadEntryProxy Payload::operator[](const std::string& key) -{ - //boost::shared_ptr<PayloadEntryProxy> p(new PayloadEntryProxy(this, key)); - return PayloadEntryProxy(this, key); -} -Payload::operator std::map<std::string, std::string>() -{ - return _store; -} - -inline void Payload::_internal_set(const std::string& k, const std::string& v, const std::string& writer_name) { - std::map<std::string, std::string> _new; - std::vector<std::string> _remove; - _new[k]=v; - _iu.lock()->_modify_payload(true, _new, _remove, writer_name ); - _store[k] = v; -} -inline void Payload::_internal_remove(const std::string& k, const std::string& writer_name) { - std::map<std::string, std::string> _new; - std::vector<std::string> _remove; - _remove.push_back(k); - _iu.lock()->_modify_payload(true, _new, _remove, writer_name ); - _store.erase(k); -} -void Payload::_internal_replace_all(const std::map<std::string, std::string>& new_contents, const std::string& writer_name) -{ - std::vector<std::string> _remove; - _iu.lock()->_modify_payload(false, new_contents, _remove, writer_name ); - _store = new_contents; -} -void Payload::_internal_merge(const std::map<std::string, std::string>& contents_to_merge, const std::string& writer_name) -{ - std::vector<std::string> _remove; - _iu.lock()->_modify_payload(true, contents_to_merge, _remove, writer_name ); - _store.insert(contents_to_merge.begin(), contents_to_merge.end()); - //for (std::map<std::string, std::string>::iterator it = contents_to_merge.begin(); it!=contents_to_merge.end(); i++) { - // _store[it->first] = it->second; - //} -} -inline std::string Payload::get(const std::string& k) { - if (_store.count(k)>0) return _store[k]; - else return IPAACA_PAYLOAD_DEFAULT_STRING_VALUE; -} -void Payload::_remotely_enforced_wipe() -{ - _store.clear(); -} -void Payload::_remotely_enforced_delitem(const std::string& k) -{ - _store.erase(k); -} -void Payload::_remotely_enforced_setitem(const std::string& k, const std::string& v) -{ - _store[k] = v; -} - -//}}} - -// IUConverter//{{{ - -IUConverter::IUConverter() -: Converter<std::string> ("ipaaca::IU", "ipaaca-iu", true) -{ -} - -std::string IUConverter::serialize(const AnnotatedData& data, std::string& wire) -{ - //std::cout << "serialize" << std::endl; - // Ensure that DATA actually holds a datum of the data-type we expect. - assert(data.first == getDataType()); // "ipaaca::IU" - // NOTE: a dynamic_pointer_cast cannot be used from void* - boost::shared_ptr<const IU> obj = boost::static_pointer_cast<const IU> (data.second); - boost::shared_ptr<protobuf::IU> pbo(new protobuf::IU()); - // transfer obj data to pbo - pbo->set_uid(obj->uid()); - pbo->set_revision(obj->revision()); - pbo->set_category(obj->category()); - pbo->set_payload_type(obj->payload_type()); - pbo->set_owner_name(obj->owner_name()); - pbo->set_committed(obj->committed()); - ipaaca::protobuf::IU_AccessMode a_m; - switch(obj->access_mode()) { - case IU_ACCESS_PUSH: - a_m = ipaaca::protobuf::IU_AccessMode_PUSH; - break; - case IU_ACCESS_REMOTE: - a_m = ipaaca::protobuf::IU_AccessMode_REMOTE; - break; - case IU_ACCESS_MESSAGE: - a_m = ipaaca::protobuf::IU_AccessMode_MESSAGE; - break; - } - pbo->set_access_mode(a_m); - pbo->set_read_only(obj->read_only()); - for (std::map<std::string, std::string>::const_iterator it=obj->_payload._store.begin(); it!=obj->_payload._store.end(); ++it) { - protobuf::PayloadItem* item = pbo->add_payload(); - item->set_key(it->first); - item->set_value(it->second); - item->set_type("str"); // FIXME other types than str (later) - } - for (LinkMap::const_iterator it=obj->_links._links.begin(); it!=obj->_links._links.end(); ++it) { - protobuf::LinkSet* links = pbo->add_links(); - links->set_type(it->first); - for (std::set<std::string>::const_iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) { - links->add_targets(*it2); - } - } - pbo->SerializeToString(&wire); - switch(obj->access_mode()) { - case IU_ACCESS_PUSH: - //std::cout << "Requesting to send as ipaaca-iu" << std::endl; - return "ipaaca-iu"; - case IU_ACCESS_MESSAGE: - //std::cout << "Requesting to send as ipaaca-messageiu" << std::endl; - return "ipaaca-messageiu"; - default: - //std::cout << "Requesting to send as default" << std::endl; - return getWireSchema(); - } - -} - -AnnotatedData IUConverter::deserialize(const std::string& wireSchema, const std::string& wire) { - //std::cout << "deserialize" << std::endl; - assert(wireSchema == getWireSchema()); // "ipaaca-iu" - boost::shared_ptr<protobuf::IU> pbo(new protobuf::IU()); - pbo->ParseFromString(wire); - IUAccessMode mode = static_cast<IUAccessMode>(pbo->access_mode()); - switch(mode) { - case IU_ACCESS_PUSH: - { - // Create a "remote push IU" - boost::shared_ptr<RemotePushIU> obj = RemotePushIU::create(); - // transfer pbo data to obj - obj->_uid = pbo->uid(); - obj->_revision = pbo->revision(); - obj->_category = pbo->category(); - obj->_payload_type = pbo->payload_type(); - obj->_owner_name = pbo->owner_name(); - obj->_committed = pbo->committed(); - obj->_read_only = pbo->read_only(); - obj->_access_mode = IU_ACCESS_PUSH; - for (int i=0; i<pbo->payload_size(); i++) { - const protobuf::PayloadItem& it = pbo->payload(i); - obj->_payload._store[it.key()] = it.value(); - } - for (int i=0; i<pbo->links_size(); i++) { - const protobuf::LinkSet& pls = pbo->links(i); - LinkSet& ls = obj->_links._links[pls.type()]; - for (int j=0; j<pls.targets_size(); j++) { - ls.insert(pls.targets(j)); - } - } - //return std::make_pair(getDataType(), obj); - return std::make_pair("ipaaca::RemotePushIU", obj); - break; - } - case IU_ACCESS_MESSAGE: - { - // Create a "Message-type IU" - boost::shared_ptr<RemoteMessage> obj = RemoteMessage::create(); - //std::cout << "REFCNT after create: " << obj.use_count() << std::endl; - // transfer pbo data to obj - obj->_uid = pbo->uid(); - obj->_revision = pbo->revision(); - obj->_category = pbo->category(); - obj->_payload_type = pbo->payload_type(); - obj->_owner_name = pbo->owner_name(); - obj->_committed = pbo->committed(); - obj->_read_only = pbo->read_only(); - obj->_access_mode = IU_ACCESS_MESSAGE; - for (int i=0; i<pbo->payload_size(); i++) { - const protobuf::PayloadItem& it = pbo->payload(i); - obj->_payload._store[it.key()] = it.value(); - } - for (int i=0; i<pbo->links_size(); i++) { - const protobuf::LinkSet& pls = pbo->links(i); - LinkSet& ls = obj->_links._links[pls.type()]; - for (int j=0; j<pls.targets_size(); j++) { - ls.insert(pls.targets(j)); - } - } - //return std::make_pair(getDataType(), obj); - return std::make_pair("ipaaca::RemoteMessage", obj); - break; - } - default: - // other cases not handled yet! ( TODO ) - throw NotImplementedError(); - } -} - -//}}} - -// MessageConverter//{{{ - -MessageConverter::MessageConverter() -: Converter<std::string> ("ipaaca::Message", "ipaaca-messageiu", true) -{ -} - -std::string MessageConverter::serialize(const AnnotatedData& data, std::string& wire) -{ - // Ensure that DATA actually holds a datum of the data-type we expect. - assert(data.first == getDataType()); // "ipaaca::Message" - // NOTE: a dynamic_pointer_cast cannot be used from void* - boost::shared_ptr<const Message> obj = boost::static_pointer_cast<const Message> (data.second); - boost::shared_ptr<protobuf::IU> pbo(new protobuf::IU()); - // transfer obj data to pbo - pbo->set_uid(obj->uid()); - pbo->set_revision(obj->revision()); - pbo->set_category(obj->category()); - pbo->set_payload_type(obj->payload_type()); - pbo->set_owner_name(obj->owner_name()); - pbo->set_committed(obj->committed()); - ipaaca::protobuf::IU_AccessMode a_m; - switch(obj->access_mode()) { - case IU_ACCESS_PUSH: - a_m = ipaaca::protobuf::IU_AccessMode_PUSH; - break; - case IU_ACCESS_REMOTE: - a_m = ipaaca::protobuf::IU_AccessMode_REMOTE; - break; - case IU_ACCESS_MESSAGE: - a_m = ipaaca::protobuf::IU_AccessMode_MESSAGE; - break; - } - pbo->set_access_mode(a_m); - pbo->set_read_only(obj->read_only()); - for (std::map<std::string, std::string>::const_iterator it=obj->_payload._store.begin(); it!=obj->_payload._store.end(); ++it) { - protobuf::PayloadItem* item = pbo->add_payload(); - item->set_key(it->first); - item->set_value(it->second); - item->set_type("str"); // FIXME other types than str (later) - } - for (LinkMap::const_iterator it=obj->_links._links.begin(); it!=obj->_links._links.end(); ++it) { - protobuf::LinkSet* links = pbo->add_links(); - links->set_type(it->first); - for (std::set<std::string>::const_iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) { - links->add_targets(*it2); - } - } - pbo->SerializeToString(&wire); - switch(obj->access_mode()) { - case IU_ACCESS_PUSH: - return "ipaaca-iu"; - case IU_ACCESS_MESSAGE: - return "ipaaca-messageiu"; - default: - //std::cout << "Requesting to send as default" << std::endl; - return getWireSchema(); - } - -} - -AnnotatedData MessageConverter::deserialize(const std::string& wireSchema, const std::string& wire) { - assert(wireSchema == getWireSchema()); // "ipaaca-iu" - boost::shared_ptr<protobuf::IU> pbo(new protobuf::IU()); - pbo->ParseFromString(wire); - IUAccessMode mode = static_cast<IUAccessMode>(pbo->access_mode()); - switch(mode) { - case IU_ACCESS_PUSH: - { - // Create a "remote push IU" - boost::shared_ptr<RemotePushIU> obj = RemotePushIU::create(); - // transfer pbo data to obj - obj->_uid = pbo->uid(); - obj->_revision = pbo->revision(); - obj->_category = pbo->category(); - obj->_payload_type = pbo->payload_type(); - obj->_owner_name = pbo->owner_name(); - obj->_committed = pbo->committed(); - obj->_read_only = pbo->read_only(); - obj->_access_mode = IU_ACCESS_PUSH; - for (int i=0; i<pbo->payload_size(); i++) { - const protobuf::PayloadItem& it = pbo->payload(i); - obj->_payload._store[it.key()] = it.value(); - } - for (int i=0; i<pbo->links_size(); i++) { - const protobuf::LinkSet& pls = pbo->links(i); - LinkSet& ls = obj->_links._links[pls.type()]; - for (int j=0; j<pls.targets_size(); j++) { - ls.insert(pls.targets(j)); - } - } - //return std::make_pair(getDataType(), obj); - return std::make_pair("ipaaca::RemotePushIU", obj); - break; - } - case IU_ACCESS_MESSAGE: - { - // Create a "Message-type IU" - boost::shared_ptr<RemoteMessage> obj = RemoteMessage::create(); - // transfer pbo data to obj - obj->_uid = pbo->uid(); - obj->_revision = pbo->revision(); - obj->_category = pbo->category(); - obj->_payload_type = pbo->payload_type(); - obj->_owner_name = pbo->owner_name(); - obj->_committed = pbo->committed(); - obj->_read_only = pbo->read_only(); - obj->_access_mode = IU_ACCESS_MESSAGE; - for (int i=0; i<pbo->payload_size(); i++) { - const protobuf::PayloadItem& it = pbo->payload(i); - obj->_payload._store[it.key()] = it.value(); - } - for (int i=0; i<pbo->links_size(); i++) { - const protobuf::LinkSet& pls = pbo->links(i); - LinkSet& ls = obj->_links._links[pls.type()]; - for (int j=0; j<pls.targets_size(); j++) { - ls.insert(pls.targets(j)); - } - } - //return std::make_pair(getDataType(), obj); - return std::make_pair("ipaaca::RemoteMessage", obj); - break; - } - default: - // other cases not handled yet! ( TODO ) - throw NotImplementedError(); - } -} - -//}}} - -// IUPayloadUpdateConverter//{{{ - -IUPayloadUpdateConverter::IUPayloadUpdateConverter() -: Converter<std::string> ("ipaaca::IUPayloadUpdate", "ipaaca-iu-payload-update", true) -{ -} - -std::string IUPayloadUpdateConverter::serialize(const AnnotatedData& data, std::string& wire) -{ - assert(data.first == getDataType()); // "ipaaca::IUPayloadUpdate" - boost::shared_ptr<const IUPayloadUpdate> obj = boost::static_pointer_cast<const IUPayloadUpdate> (data.second); - boost::shared_ptr<protobuf::IUPayloadUpdate> pbo(new protobuf::IUPayloadUpdate()); - // transfer obj data to pbo - pbo->set_uid(obj->uid); - pbo->set_revision(obj->revision); - pbo->set_writer_name(obj->writer_name); - pbo->set_is_delta(obj->is_delta); - for (std::map<std::string, std::string>::const_iterator it=obj->new_items.begin(); it!=obj->new_items.end(); ++it) { - protobuf::PayloadItem* item = pbo->add_new_items(); - item->set_key(it->first); - item->set_value(it->second); - item->set_type("str"); // FIXME other types than str (later) - } - for (std::vector<std::string>::const_iterator it=obj->keys_to_remove.begin(); it!=obj->keys_to_remove.end(); ++it) { - pbo->add_keys_to_remove(*it); - } - pbo->SerializeToString(&wire); - return getWireSchema(); - -} - -AnnotatedData IUPayloadUpdateConverter::deserialize(const std::string& wireSchema, const std::string& wire) { - assert(wireSchema == getWireSchema()); // "ipaaca-iu-payload-update" - boost::shared_ptr<protobuf::IUPayloadUpdate> pbo(new protobuf::IUPayloadUpdate()); - pbo->ParseFromString(wire); - boost::shared_ptr<IUPayloadUpdate> obj(new IUPayloadUpdate()); - // transfer pbo data to obj - obj->uid = pbo->uid(); - obj->revision = pbo->revision(); - obj->writer_name = pbo->writer_name(); - obj->is_delta = pbo->is_delta(); - for (int i=0; i<pbo->new_items_size(); i++) { - const protobuf::PayloadItem& it = pbo->new_items(i); - obj->new_items[it.key()] = it.value(); - } - for (int i=0; i<pbo->keys_to_remove_size(); i++) { - obj->keys_to_remove.push_back(pbo->keys_to_remove(i)); - } - return std::make_pair(getDataType(), obj); -} - -//}}} - -// IULinkUpdateConverter//{{{ - -IULinkUpdateConverter::IULinkUpdateConverter() -: Converter<std::string> ("ipaaca::IULinkUpdate", "ipaaca-iu-link-update", true) -{ -} - -std::string IULinkUpdateConverter::serialize(const AnnotatedData& data, std::string& wire) -{ - assert(data.first == getDataType()); // "ipaaca::IULinkUpdate" - boost::shared_ptr<const IULinkUpdate> obj = boost::static_pointer_cast<const IULinkUpdate> (data.second); - boost::shared_ptr<protobuf::IULinkUpdate> pbo(new protobuf::IULinkUpdate()); - // transfer obj data to pbo - pbo->set_uid(obj->uid); - pbo->set_revision(obj->revision); - pbo->set_writer_name(obj->writer_name); - pbo->set_is_delta(obj->is_delta); - for (std::map<std::string, std::set<std::string> >::const_iterator it=obj->new_links.begin(); it!=obj->new_links.end(); ++it) { - protobuf::LinkSet* links = pbo->add_new_links(); - links->set_type(it->first); - for (std::set<std::string>::const_iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) { - links->add_targets(*it2); - } - } - for (std::map<std::string, std::set<std::string> >::const_iterator it=obj->links_to_remove.begin(); it!=obj->links_to_remove.end(); ++it) { - protobuf::LinkSet* links = pbo->add_links_to_remove(); - links->set_type(it->first); - for (std::set<std::string>::const_iterator it2=it->second.begin(); it2!=it->second.end(); ++it2) { - links->add_targets(*it2); - } - } - pbo->SerializeToString(&wire); - return getWireSchema(); - -} - -AnnotatedData IULinkUpdateConverter::deserialize(const std::string& wireSchema, const std::string& wire) { - assert(wireSchema == getWireSchema()); // "ipaaca-iu-link-update" - boost::shared_ptr<protobuf::IULinkUpdate> pbo(new protobuf::IULinkUpdate()); - pbo->ParseFromString(wire); - boost::shared_ptr<IULinkUpdate> obj(new IULinkUpdate()); - // transfer pbo data to obj - obj->uid = pbo->uid(); - obj->revision = pbo->revision(); - obj->writer_name = pbo->writer_name(); - obj->is_delta = pbo->is_delta(); - for (int i=0; i<pbo->new_links_size(); ++i) { - const protobuf::LinkSet& it = pbo->new_links(i); - for (int j=0; j<it.targets_size(); ++j) { - obj->new_links[it.type()].insert(it.targets(j)); // = vec; - } - } - for (int i=0; i<pbo->links_to_remove_size(); ++i) { - const protobuf::LinkSet& it = pbo->links_to_remove(i); - for (int j=0; j<it.targets_size(); ++j) { - obj->links_to_remove[it.type()].insert(it.targets(j)); - } - } - return std::make_pair(getDataType(), obj); -} - -//}}} - -// IntConverter//{{{ - -IntConverter::IntConverter() -: Converter<std::string> ("int", "int32", true) -{ -} - -std::string IntConverter::serialize(const AnnotatedData& data, std::string& wire) -{ - // Ensure that DATA actually holds a datum of the data-type we expect. - assert(data.first == getDataType()); // "int" - // NOTE: a dynamic_pointer_cast cannot be used from void* - boost::shared_ptr<const int> obj = boost::static_pointer_cast<const int> (data.second); - boost::shared_ptr<protobuf::IntMessage> pbo(new protobuf::IntMessage()); - // transfer obj data to pbo - pbo->set_value(*obj); - pbo->SerializeToString(&wire); - return getWireSchema(); - -} - -AnnotatedData IntConverter::deserialize(const std::string& wireSchema, const std::string& wire) { - assert(wireSchema == getWireSchema()); // "int" - boost::shared_ptr<protobuf::IntMessage> pbo(new protobuf::IntMessage()); - pbo->ParseFromString(wire); - boost::shared_ptr<int> obj = boost::shared_ptr<int>(new int(pbo->value())); - return std::make_pair("int", obj); -} -//}}} } // of namespace ipaaca diff --git a/ipaacalib/cpp/src/rsb.conf b/ipaacalib/cpp/src/rsb.conf deleted file mode 100644 index 3fb0a683fded31bf2f123d947dcc6ff6a7bfeea1..0000000000000000000000000000000000000000 --- a/ipaacalib/cpp/src/rsb.conf +++ /dev/null @@ -1,5 +0,0 @@ -[transport.spread] -host = localhost -port = 4803 -enabled = 1 - diff --git a/ipaacalib/cpp/test/CMakeLists.txt b/ipaacalib/cpp/test/CMakeLists.txt index 48a93b3076055ea696023cad76ca03d201e024ab..6a56b612432f325165ffeeb542e002aa06ae4130 100644 --- a/ipaacalib/cpp/test/CMakeLists.txt +++ b/ipaacalib/cpp/test/CMakeLists.txt @@ -3,6 +3,9 @@ cmake_minimum_required (VERSION 2.6) # project name project (ipaaca_cpp_test) +# use C++11 (starting with proto v2 / ipaaca-c++ release 12) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + ## use the following line to enable console debug messages in ipaaca set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DIPAACA_DEBUG_MESSAGES") diff --git a/ipaacalib/cpp/test/src/Makefile b/ipaacalib/cpp/test/src/Makefile index 11a2336bdded24ce11d4832b37928f1b9ec3beb3..ff3b45be572668655974721966705405b016ae11 100644 --- a/ipaacalib/cpp/test/src/Makefile +++ b/ipaacalib/cpp/test/src/Makefile @@ -2,7 +2,7 @@ CONFIG = -DIPAACA_DEBUG_MESSAGES #IPAACASOURCES = ../../src/ipaaca.cc ipaaca.pb.cc #TEXTSOURCES = ${IPAACASOURCES} testipaaca.cc TEXTSOURCES = testipaaca.cc -CCFLAGS=-I../../../../deps/include -I../../../../dist/include -I. -I../../src -I/usr/local/include -I/opt/local/include ${CONFIG} +CCFLAGS=-I../../build -I../../../../deps/include -I../../../../dist/include -I. -I../../src -I/usr/local/include -I/opt/local/include ${CONFIG} BOOSTLIBS = -L/opt/local/lib -lboost_regex-mt -lboost_date_time-mt -lboost_thread-mt PROTOLIBS = -L/opt/local/lib -lprotobuf #LIBS = ${BOOSTLIBS} ${PROTOLIBS} -L/usr/local/lib -lrsc -lrsbcore diff --git a/ipaacalib/cpp/src/ipaaca-test-main.cc b/ipaacalib/cpp/test/src/ipaaca-test-main.cc similarity index 86% rename from ipaacalib/cpp/src/ipaaca-test-main.cc rename to ipaacalib/cpp/test/src/ipaaca-test-main.cc index b6ce132704a81a95ccbe1aba63293ef31bde00f7..46946dbd8698df57e565cd8dec654e0de8e7cb36 100644 --- a/ipaacalib/cpp/src/ipaaca-test-main.cc +++ b/ipaacalib/cpp/test/src/ipaaca-test-main.cc @@ -30,7 +30,7 @@ * Excellence Initiative. */ -#include <ipaaca.h> +#include <../include/ipaaca/ipaaca.h> #include <typeinfo> //#include <rsc/logging/Logger.h> @@ -56,7 +56,7 @@ int main() { try{ //initialize_ipaaca_rsb(); - InputBuffer::ptr ib = InputBuffer::create("Tester", "testcategory"); + InputBuffer::ptr ib = InputBuffer::create("Tester", "spam"); ib->register_handler(my_first_iu_handler); while (true) { @@ -82,7 +82,7 @@ int main() { ob->register_handler(iu_handler_for_remote_changes); //std::cout << "Buffer: " << ob->unique_name() << std::endl; - IU::ptr iu = IU::create("testcategory"); + IU::ptr iu = IU::create("spam"); ob->add(iu); std::cout << "Updating in 1 sec" << std::endl; @@ -98,11 +98,20 @@ int main() { iu->add_link("grin", "DUMMY_IU_UID_1234efef1234"); - std::cout << "Interpreted as long value: " << iu->_payload["TEST"].to_int() << std::endl; - std::cout << "Interpreted as double value: " << iu->_payload["TEST"].to_float() << std::endl; + //std::cout << "Interpreted as long value: " << iu->_payload["TEST"].to_int() << std::endl; + //std::cout << "Interpreted as double value: " << iu->_payload["TEST"].to_float() << std::endl; std::cout << "Committing and quitting in 1 sec" << std::endl; sleep(1); + int c = 0; + while(true) { + std::stringstream ss; + ss << c; + iu->_payload["data"] = ss.str(); + std::cout << "iu->_payload[\"data\"]=" << ss.str() << std::endl; + c++; + sleep(1); + } iu->commit(); } catch (ipaaca::Exception& e) { std::cout << "== IPAACA EXCEPTION == " << e.what() << std::endl; diff --git a/ipaacalib/cpp/test/src/ipaaca-test-resend.cc b/ipaacalib/cpp/test/src/ipaaca-test-resend.cc new file mode 100644 index 0000000000000000000000000000000000000000..a1af0f572ab7d603932aaa448eae1970f4b6743c --- /dev/null +++ b/ipaacalib/cpp/test/src/ipaaca-test-resend.cc @@ -0,0 +1,63 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2013 Sociable Agents Group + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +#include <../include/ipaaca/ipaaca.h> +#include <typeinfo> +#include <iostream> + +using namespace ipaaca; + +void my_first_iu_handler(IUInterface::ptr iu, IUEventType type, bool local) +{ + std::cout<<"Update!"<<std::endl; +} + +int main() { + try{ + OutputBuffer::ptr output_buffer = OutputBuffer::create("Tester"); + IU::ptr iu = IU::create("cat1"); + output_buffer->add(iu); + + InputBuffer::ptr input_buffer = InputBuffer::create("InputTester","cat1"); + input_buffer->set_resend(true); + input_buffer->register_handler(my_first_iu_handler); + iu->payload()["key"]="Dummy"; + sleep(1); + std::cout<<"Input buffer #of ius: "<<input_buffer->get_ius().size()<<std::endl; + + } catch (ipaaca::Exception& e) { + std::cout << "== IPAACA EXCEPTION == " << e.what() << std::endl; + } +} + + + diff --git a/ipaacalib/cpp/test/src/testipaaca.cc b/ipaacalib/cpp/test/src/testipaaca.cc index 0de077af57c46d9bed0a44077c18709d9a9c0873..828136ba81da19eaf49546b225f229788c5ed986 100644 --- a/ipaacalib/cpp/test/src/testipaaca.cc +++ b/ipaacalib/cpp/test/src/testipaaca.cc @@ -138,7 +138,7 @@ void TextSender::publish_text_to_print(const std::string& text, const std::strin } } -int old_main() { +int main() { TextSender sender; sleep(1); sender.publish_text_to_print("(INIT)"); @@ -146,7 +146,7 @@ int old_main() { while (true) sleep(1); } -int main() { +int old_main() { std::cerr << "TODO: implement Ipaaca C++ test cases." << std::endl; return 0; } diff --git a/ipaacalib/java/build.properties b/ipaacalib/java/build.properties index be895bca6a60f3dace498ccc0d61db2abe088746..9549960d4a1a9ff6ee946360d00244d737171de3 100644 --- a/ipaacalib/java/build.properties +++ b/ipaacalib/java/build.properties @@ -6,5 +6,5 @@ run.jvmargs= -Xms128m -Xmx512m -Xss5M rebuild.list= publish.resolver=asap.sftp.publish dist.dir=../../dist -javac.source=1.6 -javac.target=1.6 +#javac.source=1.6 +#javac.target=1.6 diff --git a/ipaacalib/java/ivy.xml b/ipaacalib/java/ivy.xml index 8023c27a1dc4f2f71d0d7cfcebe49d55bd8a2475..e1baa4bdf6aa9cbd3606fabf7e9c053a45c286fa 100644 --- a/ipaacalib/java/ivy.xml +++ b/ipaacalib/java/ivy.xml @@ -9,5 +9,6 @@ <dependency org="google" name="protobuf-java" rev="latest.release" /> <dependency org="rsb" name="rsb" rev="latest.release" /> <dependency org="lombok" name="lombok" rev="latest.release" /> + <dependency org="apache" name="commons-lang" rev="latest.release" /> </dependencies> </ivy-module> diff --git a/ipaacalib/java/src/ipaaca/AbstractIU.java b/ipaacalib/java/src/ipaaca/AbstractIU.java index 83533c2ca3e67f0c061f7e5c1248ad900a09a6d2..d79f1622871ec4f960781e6630604e9fe3054510 100644 --- a/ipaacalib/java/src/ipaaca/AbstractIU.java +++ b/ipaacalib/java/src/ipaaca/AbstractIU.java @@ -231,7 +231,7 @@ public abstract class AbstractIU List<PayloadItem> items = new ArrayList<PayloadItem>(); for (Entry<String, String> entry : newPayload.entrySet()) { - PayloadItem item = PayloadItem.newBuilder().setKey(entry.getKey()).setValue(entry.getValue()).setType("") // TODO:default type? + PayloadItem item = PayloadItem.newBuilder().setKey(entry.getKey()).setValue(entry.getValue()).setType("STR") .build(); items.add(item); } diff --git a/ipaacalib/java/src/ipaaca/Buffer.java b/ipaacalib/java/src/ipaaca/Buffer.java index 993c2d27fc1c035e474462b7328b3a98fd036c96..02b6b36f7eb190ae64344a53e8a7ce663ca80bad 100644 --- a/ipaacalib/java/src/ipaaca/Buffer.java +++ b/ipaacalib/java/src/ipaaca/Buffer.java @@ -1,10 +1,10 @@ /* * This file is part of IPAACA, the * "Incremental Processing Architecture - * for Artificial Conversational Agents". + * for Artificial Conversational Agents". * * Copyright (c) 2009-2013 Sociable Agents Group - * CITEC, Bielefeld University + * CITEC, Bielefeld University * * http://opensource.cit-ec.de/projects/ipaaca/ * http://purl.org/net/ipaaca @@ -21,7 +21,7 @@ * You should have received a copy of the LGPL along with this * program. If not, go to http://www.gnu.org/licenses/lgpl.html * or write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The development of this software was supported by the * Excellence Cluster EXC 277 Cognitive Interaction Technology. @@ -53,7 +53,7 @@ public abstract class Buffer { return owningComponentName + "ID" + uuid; } - + public String getUniqueName() { return uniqueName; @@ -107,25 +107,25 @@ public abstract class Buffer { eventHandlers.add(handler); } - + public void registerHandler(HandlerFunctor func) { IUEventHandler handler; handler = new IUEventHandler(func); registerHandler(handler); } - + public void registerHandler(HandlerFunctor func, Set<String> categories) { IUEventHandler handler; handler = new IUEventHandler(func, categories); registerHandler(handler); } - + public void registerHandler(HandlerFunctor func, EnumSet<IUEventType> eventTypes) { IUEventHandler handler; handler = new IUEventHandler(func, eventTypes); registerHandler(handler); } - + public void registerHandler(HandlerFunctor func, EnumSet<IUEventType> eventTypes, Set<String> categories) { IUEventHandler handler; handler = new IUEventHandler(func, eventTypes, categories); diff --git a/ipaacalib/java/src/ipaaca/BufferConfiguration.java b/ipaacalib/java/src/ipaaca/BufferConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..ec33e1acb2d5adf6dca5293fe38513e4b95161b6 --- /dev/null +++ b/ipaacalib/java/src/ipaaca/BufferConfiguration.java @@ -0,0 +1,78 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2013 Sociable Agents Group + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +package ipaaca; + + +import java.util.HashSet; +import java.util.Set; + + +class BufferConfiguration { + String _owningComponentName; + Set<String> _category_interests; + String _channel; + boolean _resendActive; + // protected: + // IPAACA_MEMBER_VAR_EXPORT std::string _basename; + // IPAACA_MEMBER_VAR_EXPORT std::vector<std::string> _category_interests; + // IPAACA_MEMBER_VAR_EXPORT std::string _channel; + + public BufferConfiguration(String owningComponentName) { + this._owningComponentName = owningComponentName; + this._channel = "default"; + this._resendActive = false; + this._category_interests = new HashSet<String>(); + } + + public String getOwningComponentName() { + return this._owningComponentName; + } + + public Set<String> getCategoryInterests() { + return this._category_interests; + } + + public String getChannel() { + return this._channel; + } + + public boolean getResendActive() { + return this._resendActive; + } + // public: + // IPAACA_HEADER_EXPORT inline BufferConfiguration(const std::string basename) { _basename = basename; } + // IPAACA_HEADER_EXPORT const std::string get_basename() const { return _basename; } + // IPAACA_HEADER_EXPORT const std::vector<std::string> get_category_interests() const { return _category_interests; } + // IPAACA_HEADER_EXPORT const std::string get_channel() const { return _channel; } + +} diff --git a/ipaacalib/java/src/ipaaca/BufferConfigurationBuilder.java b/ipaacalib/java/src/ipaaca/BufferConfigurationBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..3f536a6994709d21bf8ed402f2518d94cb9f0e1d --- /dev/null +++ b/ipaacalib/java/src/ipaaca/BufferConfigurationBuilder.java @@ -0,0 +1,78 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2013 Sociable Agents Group + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + + +package ipaaca; + +public class BufferConfigurationBuilder extends BufferConfiguration { + + public BufferConfigurationBuilder(String owningComponentName) { + super(owningComponentName); + } + + public void setOwningComponentName(String owningComponentName) { + this._owningComponentName = owningComponentName; + } + + public void addCategoryInterest(String category) { + this._category_interests.add(category); + } + + public void setChannel(String channel) { + this._channel = channel; + } + + public void setResendActive(boolean resendActive) { + this._resendActive = resendActive; + } + + public BufferConfiguration getBufferConfiguration() { + return this; + } + + // public: + // IPAACA_HEADER_EXPORT inline BufferConfigurationBuilder(const std::string basename):BufferConfiguration(basename) {} + // IPAACA_HEADER_EXPORT inline void set_basename(const std::string& basename) + // { + // _basename = basename; + // } + // IPAACA_HEADER_EXPORT inline void add_category_interest(const std::string& category) + // { + // _category_interests.push_back(category); + // } + // IPAACA_HEADER_EXPORT inline void set_channel(const std::string& channel) + // { + // _channel = channel; + // } + + // IPAACA_HEADER_EXPORT const BufferConfiguration& get_BufferConfiguration() { return *this; } +} diff --git a/ipaacalib/java/src/ipaaca/IUConverter.java b/ipaacalib/java/src/ipaaca/IUConverter.java index 1b6a3c5768fcd72691658bdab67500daeaae57ad..19f86de7cd2eeb684c6c1c6101b670078cc92760 100644 --- a/ipaacalib/java/src/ipaaca/IUConverter.java +++ b/ipaacalib/java/src/ipaaca/IUConverter.java @@ -1,10 +1,10 @@ /* * This file is part of IPAACA, the * "Incremental Processing Architecture - * for Artificial Conversational Agents". + * for Artificial Conversational Agents". * * Copyright (c) 2009-2013 Sociable Agents Group - * CITEC, Bielefeld University + * CITEC, Bielefeld University * * http://opensource.cit-ec.de/projects/ipaaca/ * http://purl.org/net/ipaaca @@ -21,7 +21,7 @@ * You should have received a copy of the LGPL along with this * program. If not, go to http://www.gnu.org/licenses/lgpl.html * or write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The development of this software was supported by the * Excellence Cluster EXC 277 Cognitive Interaction Technology. @@ -55,7 +55,7 @@ import com.google.protobuf.InvalidProtocolBufferException; /** * Serializes AbstractIUs into protocolbuffer IUs and vice versa. * @author hvanwelbergen - * + * */ public class IUConverter implements Converter<ByteBuffer> { @@ -79,7 +79,7 @@ public class IUConverter implements Converter<ByteBuffer> List<PayloadItem> payloadItems = new ArrayList<PayloadItem>(); for (Entry<String, String> entry : iua.getPayload().entrySet()) { - payloadItems.add(PayloadItem.newBuilder().setKey(entry.getKey()).setValue(entry.getValue()).setType("").build()); + payloadItems.add(PayloadItem.newBuilder().setKey(entry.getKey()).setValue(entry.getValue()).setType("STR").build()); } List<LinkSet> links = new ArrayList<LinkSet>(); @@ -95,11 +95,12 @@ public class IUConverter implements Converter<ByteBuffer> } IU iu = IU.newBuilder().setUid(iua.getUid()).setRevision(iua.getRevision()).setCategory(iua.getCategory()) .setOwnerName(iua.getOwnerName()).setCommitted(iua.isCommitted()).setAccessMode(accessMode) - .setReadOnly(iua.isReadOnly()).setPayloadType("MAP").addAllPayload(payloadItems).addAllLinks(links).build(); - return new WireContents<ByteBuffer>(ByteBuffer.wrap(iu.toByteArray()), "ipaaca-iu"); + .setReadOnly(iua.isReadOnly()).setPayloadType("STR").addAllPayload(payloadItems).addAllLinks(links).build(); + String wireFormat = (accessMode == IU.AccessMode.MESSAGE) ? "ipaaca-messageiu" : "ipaaca-iu"; + return new WireContents<ByteBuffer>(ByteBuffer.wrap(iu.toByteArray()), wireFormat); } - + @Override public UserData<?> deserialize(String wireSchema, ByteBuffer buffer) throws ConversionException { @@ -107,6 +108,12 @@ public class IUConverter implements Converter<ByteBuffer> try { iu = IU.newBuilder().mergeFrom(buffer.array()).build(); + // If there are rsb buffer read-only issues in some build, use this code instead of the above line: + //int size = buffer.capacity(); + //byte[] array = new byte[size]; + //buffer.get(array, 0, size); + //iu = IU.newBuilder().mergeFrom(array).build(); + } catch (InvalidProtocolBufferException e) { diff --git a/ipaacalib/java/src/ipaaca/IUResendFailedException.java b/ipaacalib/java/src/ipaaca/IUResendFailedException.java new file mode 100644 index 0000000000000000000000000000000000000000..04d1a06431552ea060cac8d5f066bc8c5a92a6bc --- /dev/null +++ b/ipaacalib/java/src/ipaaca/IUResendFailedException.java @@ -0,0 +1,55 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2013 Sociable Agents Group + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +package ipaaca; + +/** + * Error indicating that an IU is immutable because it has been committed to. + * @author hvanwelbergen + * + */ +public class IUResendFailedException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + private final AbstractIU iu; + + public AbstractIU getIU() + { + return iu; + } + + public IUResendFailedException(AbstractIU iu) + { + super("Resending IU " + iu.getUid() + " failed."); + this.iu = iu; + } +} diff --git a/ipaacalib/java/src/ipaaca/IUStore.java b/ipaacalib/java/src/ipaaca/IUStore.java index 0edc34782123f388d725bd9b304ac324546f65db..a81b9b11530b137bd362c37bb6ae5fe12fa28044 100644 --- a/ipaacalib/java/src/ipaaca/IUStore.java +++ b/ipaacalib/java/src/ipaaca/IUStore.java @@ -32,7 +32,7 @@ package ipaaca; -import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; /** * An IUStore maps an IUid to an IU @@ -40,7 +40,7 @@ import java.util.HashMap; * * @param <X> type of AbstractIU stored in the store */ -public class IUStore<X extends AbstractIU> extends HashMap<String, X> +public class IUStore<X extends AbstractIU> extends ConcurrentHashMap<String, X> { private static final long serialVersionUID = 1L; } diff --git a/ipaacalib/java/src/ipaaca/Initializer.java b/ipaacalib/java/src/ipaaca/Initializer.java index e835f43456241ced846f580f861a873bd3f9c027..0af784bd8e7051dd1889edb0465ef07add9de566 100644 --- a/ipaacalib/java/src/ipaaca/Initializer.java +++ b/ipaacalib/java/src/ipaaca/Initializer.java @@ -33,6 +33,7 @@ package ipaaca; import ipaaca.protobuf.Ipaaca.IUCommission; +import ipaaca.protobuf.Ipaaca.IUResendRequest; import rsb.converter.ConverterSignature; import rsb.converter.DefaultConverterRepository; import rsb.converter.ProtocolBufferConverter; @@ -55,6 +56,9 @@ public final class Initializer DefaultConverterRepository.getDefaultConverterRepository().addConverter(new IntConverter()); DefaultConverterRepository.getDefaultConverterRepository().addConverter( new ProtocolBufferConverter<IUCommission>(IUCommission.getDefaultInstance())); + // dlw + DefaultConverterRepository.getDefaultConverterRepository().addConverter( + new ProtocolBufferConverter<IUResendRequest>(IUResendRequest.getDefaultInstance())); DefaultConverterRepository.getDefaultConverterRepository().addConverter( new IUConverter(new ConverterSignature("ipaaca-iu", RemotePushIU.class))); diff --git a/ipaacalib/java/src/ipaaca/InputBuffer.java b/ipaacalib/java/src/ipaaca/InputBuffer.java index de2be7281d55a79225b83d513c3e7a46d9ef66a9..ccaabc922935c84a6808678a10c0dad5c0608e05 100644 --- a/ipaacalib/java/src/ipaaca/InputBuffer.java +++ b/ipaacalib/java/src/ipaaca/InputBuffer.java @@ -1,10 +1,10 @@ /* * This file is part of IPAACA, the * "Incremental Processing Architecture - * for Artificial Conversational Agents". + * for Artificial Conversational Agents". * * Copyright (c) 2009-2013 Sociable Agents Group - * CITEC, Bielefeld University + * CITEC, Bielefeld University * * http://opensource.cit-ec.de/projects/ipaaca/ * http://purl.org/net/ipaaca @@ -21,7 +21,7 @@ * You should have received a copy of the LGPL along with this * program. If not, go to http://www.gnu.org/licenses/lgpl.html * or write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The development of this software was supported by the * Excellence Cluster EXC 277 Cognitive Interaction Technology. @@ -33,8 +33,10 @@ package ipaaca; import ipaaca.protobuf.Ipaaca.IUCommission; +import ipaaca.protobuf.Ipaaca.IUResendRequest; import ipaaca.protobuf.Ipaaca.IULinkUpdate; import ipaaca.protobuf.Ipaaca.IUPayloadUpdate; +import ipaaca.protobuf.Ipaaca.PayloadItem; import java.util.Collection; import java.util.HashMap; @@ -51,6 +53,10 @@ import rsb.Event; import rsb.Factory; import rsb.Handler; import rsb.InitializeException; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + import rsb.Listener; import rsb.RSBException; import rsb.Scope; @@ -70,6 +76,9 @@ public class InputBuffer extends Buffer private IUStore<RemotePushIU> iuStore = new IUStore<RemotePushIU>(); private IUStore<RemoteMessageIU> messageStore = new IUStore<RemoteMessageIU>(); + private String channel = "default"; + private boolean resendActive; + public void close() { for (Listener listener : listenerStore.values()) @@ -121,14 +130,53 @@ public class InputBuffer extends Buffer // for cat in category_interests: // self._create_category_listener_if_needed(cat) public InputBuffer(String owningComponentName, Set<String> categoryInterests) + { + this(owningComponentName, categoryInterests, "default"); + } + + + public InputBuffer(String owningComponentName, Set<String> categoryInterests, String ipaaca_channel) { super(owningComponentName); - uniqueName = "/ipaaca/component/" + getUniqueShortName() + "/IB"; + resendActive = false; + String shortIDName = getUniqueShortName(); + uniqueName = "/ipaaca/component/" + shortIDName + "/IB"; + this.channel = ipaaca_channel; + for (String cat : categoryInterests) { createCategoryListenerIfNeeded(cat); } + + // add own uuid as identifier for hidden channel. (dlw) + createCategoryListenerIfNeeded(shortIDName); + } + + /** Pass resendActive to toggle resendRequest-functionality. */ + public InputBuffer(BufferConfiguration bufferconfiguration) + { + super(bufferconfiguration.getOwningComponentName()); + this.resendActive = bufferconfiguration.getResendActive(); + String shortIDName = getUniqueShortName(); + uniqueName = "/ipaaca/component/" + shortIDName + "/IB"; + + for (String cat : bufferconfiguration.getCategoryInterests()) + { + createCategoryListenerIfNeeded(cat); + } + this.channel = bufferconfiguration.getChannel(); + + // add own uuid as identifier for hidden channel. (dlw) + createCategoryListenerIfNeeded(shortIDName); + } + + public boolean isResendActive() { + return this.resendActive; + } + + public void setResendActive(boolean active) { + this.resendActive = active; } // def _get_remote_server(self, iu): @@ -162,6 +210,30 @@ public class InputBuffer extends Buffer return remoteServer; } + protected RemoteServer getRemoteServer(String ownerName) + { + if (remoteServerStore.containsKey(ownerName)) + { + return remoteServerStore.get(ownerName); + } + logger.debug("Getting remote server for {}", ownerName); + RemoteServer remoteServer = Factory.getInstance().createRemoteServer(new Scope(ownerName)); + try + { + remoteServer.activate(); + } + catch (InitializeException e) + { + throw new RuntimeException(e); + } + catch (RSBException e) + { + throw new RuntimeException(e); + } + remoteServerStore.put(ownerName, remoteServer); + return remoteServer; + } + // def _create_category_listener_if_needed(self, iu_category): // '''Return (or create, store and return) a category listener.''' // if iu_category in self._listener_store: return self._informer_store[iu_category] @@ -180,7 +252,7 @@ public class InputBuffer extends Buffer Listener listener; try { - listener = Factory.getInstance().createListener(new Scope("/ipaaca/category/" + category)); + listener = Factory.getInstance().createListener(new Scope("/ipaaca/channel/" + this.channel + "/category/" + category)); } catch (InitializeException e1) { @@ -222,7 +294,7 @@ public class InputBuffer extends Buffer } } - + // def _handle_iu_events(self, event): // '''Dispatch incoming IU events. // @@ -269,8 +341,15 @@ public class InputBuffer extends Buffer if (event.getData() instanceof RemoteMessageIU) { RemoteMessageIU rm = (RemoteMessageIU) event.getData(); + if (messageStore.containsKey(rm.getUid())) { + logger.warn("Spurious RemoteMessage event: already got this UID: "+rm.getUid()); + return; + } + //logger.info("Adding Message "+rm.getUid()); messageStore.put(rm.getUid(), rm); + //logger.info("Calling handlers for Message "+rm.getUid()); callIuEventHandlers(rm.getUid(),false, IUEventType.MESSAGE, rm.getCategory()); + //logger.info("Removing Message "+rm.getUid()); messageStore.remove(rm.getUid()); } else if (event.getData() instanceof RemotePushIU) @@ -301,7 +380,12 @@ public class InputBuffer extends Buffer } if (!iuStore.containsKey(iuLinkUpdate.getUid())) { - logger.warn("Link update message for IU which we did not fully receive before."); + if (resendActive) + { + triggerResendRequest(event.getData(), getUniqueShortName()); + } else { + logger.warn("Link update message for IU which we did not fully receive before."); + } return; } RemotePushIU iu = this.iuStore.get(iuLinkUpdate.getUid()); @@ -319,7 +403,12 @@ public class InputBuffer extends Buffer } if (!iuStore.containsKey(iuUpdate.getUid())) { - logger.warn("Update message for IU which we did not fully receive before."); + if (resendActive) + { + triggerResendRequest(event.getData(), getUniqueShortName()); + } else { + logger.warn("Update message for IU which we did not fully receive before."); + } return; } RemotePushIU iu = this.iuStore.get(iuUpdate.getUid()); @@ -339,7 +428,12 @@ public class InputBuffer extends Buffer } if (!iuStore.containsKey(iuc.getUid())) { - logger.warn("Update message for IU which we did not fully receive before."); + if (resendActive) + { + triggerResendRequest(event.getData(), getUniqueShortName()); + } else { + logger.warn("Update message for IU which we did not fully receive before."); + } return; } RemotePushIU iu = this.iuStore.get(iuc.getUid()); @@ -350,6 +444,52 @@ public class InputBuffer extends Buffer } } + private void triggerResendRequest(Object aiuObj, String hiddenScopeName) + { + String uid = null; + String writerName = null; + if (aiuObj instanceof IULinkUpdate) { + IULinkUpdate tmp = (IULinkUpdate)aiuObj; + uid = tmp.getUid(); + writerName = tmp.getWriterName(); + } else if (aiuObj instanceof IUPayloadUpdate) { + IUPayloadUpdate tmp = (IUPayloadUpdate)aiuObj; + uid = tmp.getUid(); + writerName = tmp.getWriterName(); + } else if (aiuObj instanceof IUCommission) { + IUCommission tmp = (IUCommission)aiuObj; + uid = tmp.getUid(); + writerName = tmp.getWriterName(); + } + RemoteServer rServer = null; + if (writerName != null) + rServer = getRemoteServer(writerName); + if ((rServer != null)&&(uid != null)) { + IUResendRequest iurr = IUResendRequest.newBuilder().setUid(uid).setHiddenScopeName(hiddenScopeName).build(); + int rRevision = 0; + try + { + rRevision = (Integer) rServer.call("resendRequest", iurr); + } + catch (RSBException e) + { + throw new RuntimeException(e); + } + catch (ExecutionException e) + { + throw new RuntimeException(e); + } + catch (TimeoutException e) + { + throw new RuntimeException(e); + } + if (rRevision == 0) + { + //throw new IUResendFailedException(aiu); // TODO + } + } + } + public InputBuffer(String owningComponentName) { super(owningComponentName); @@ -368,6 +508,14 @@ public class InputBuffer extends Buffer } } + public void addCategoryInterest(String... categories) + { + for(String cat:categories) + { + createCategoryListenerIfNeeded(cat); + } + } + public Collection<RemotePushIU> getIUs() { return iuStore.values(); diff --git a/ipaacalib/java/src/ipaaca/LocalIU.java b/ipaacalib/java/src/ipaaca/LocalIU.java index 82bf3ad84f1edae232ed1c11579eff7a409f8245..6e87a82484f882a25dddda7e314dfd76ebdb3060 100644 --- a/ipaacalib/java/src/ipaaca/LocalIU.java +++ b/ipaacalib/java/src/ipaaca/LocalIU.java @@ -230,7 +230,7 @@ public class LocalIU extends AbstractIU if (isPublished()) { // send update to remote holders - PayloadItem newItem = PayloadItem.newBuilder().setKey(key).setValue(value).setType("") // TODO: fix this, default in .proto? + PayloadItem newItem = PayloadItem.newBuilder().setKey(key).setValue(value).setType("STR") .build(); IUPayloadUpdate update = IUPayloadUpdate.newBuilder().setUid(getUid()).setRevision(getRevision()).setIsDelta(true) .setWriterName(writer == null ? getOwnerName() : writer).addNewItems(newItem).build(); @@ -256,7 +256,7 @@ public class LocalIU extends AbstractIU .setWriterName(writer == null ? getOwnerName() : writer); for (Map.Entry<? extends String, ? extends String> item : newItems.entrySet()) { - PayloadItem newItem = PayloadItem.newBuilder().setKey(item.getKey()).setValue(item.getValue()).setType("") // TODO: fix this, default in .proto? + PayloadItem newItem = PayloadItem.newBuilder().setKey(item.getKey()).setValue(item.getValue()).setType("STR") .build(); builder.addNewItems(newItem); diff --git a/ipaacalib/java/src/ipaaca/OutputBuffer.java b/ipaacalib/java/src/ipaaca/OutputBuffer.java index 35bdae69e8eb5917791cda1ff811869fecfe2820..818621b07d24b974f7928e3d30fa59126caa5fd7 100644 --- a/ipaacalib/java/src/ipaaca/OutputBuffer.java +++ b/ipaacalib/java/src/ipaaca/OutputBuffer.java @@ -1,10 +1,10 @@ /* * This file is part of IPAACA, the * "Incremental Processing Architecture - * for Artificial Conversational Agents". + * for Artificial Conversational Agents". * * Copyright (c) 2009-2013 Sociable Agents Group - * CITEC, Bielefeld University + * CITEC, Bielefeld University * * http://opensource.cit-ec.de/projects/ipaaca/ * http://purl.org/net/ipaaca @@ -21,7 +21,7 @@ * You should have received a copy of the LGPL along with this * program. If not, go to http://www.gnu.org/licenses/lgpl.html * or write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The development of this software was supported by the * Excellence Cluster EXC 277 Cognitive Interaction Technology. @@ -34,6 +34,7 @@ package ipaaca; import ipaaca.protobuf.Ipaaca; import ipaaca.protobuf.Ipaaca.IUCommission; +import ipaaca.protobuf.Ipaaca.IUResendRequest; import ipaaca.protobuf.Ipaaca.IULinkUpdate; import ipaaca.protobuf.Ipaaca.IUPayloadUpdate; import ipaaca.protobuf.Ipaaca.LinkSet; @@ -69,6 +70,7 @@ public class OutputBuffer extends Buffer private Map<String, Informer<Object>> informerStore = new HashMap<String, Informer<Object>>(); // category -> informer map private final static Logger logger = LoggerFactory.getLogger(OutputBuffer.class.getName()); private IUStore<LocalIU> iuStore = new IUStore<LocalIU>(); + private String channel = "default"; // def __init__(self, owning_component_name, participant_config=None): // '''Create an Output Buffer. @@ -90,6 +92,16 @@ public class OutputBuffer extends Buffer * @param owningComponentName name of the entity that own this buffer */ public OutputBuffer(String owningComponentName) + { + this(owningComponentName, "default"); + + } + + /** + * @param owningComponentName name of the entity that own this buffer + * @param channel name of the ipaaca channel this buffer is using + */ + public OutputBuffer(String owningComponentName, String ipaaca_channel) { super(owningComponentName); @@ -101,6 +113,8 @@ public class OutputBuffer extends Buffer server.addMethod("updatePayload", new RemoteUpdatePayload()); server.addMethod("updateLinks", new RemoteUpdateLinks()); server.addMethod("commit", new RemoteCommit()); + // add method to trigger a resend request. (dlw) + server.addMethod("resendRequest", new RemoteResendRequest()); server.activate(); } catch (InitializeException e) @@ -112,6 +126,7 @@ public class OutputBuffer extends Buffer throw new RuntimeException(e); } + this.channel = ipaaca_channel; } private final class RemoteUpdatePayload extends DataCallback<Integer, IUPayloadUpdate> @@ -147,6 +162,17 @@ public class OutputBuffer extends Buffer } + private final class RemoteResendRequest extends DataCallback<Integer, IUResendRequest> + { + @Override + public Integer invoke(IUResendRequest data) throws Throwable + { + logger.debug("remoteResendRequest"); + return remoteResendRequest(data); + } + + } + // def _remote_update_payload(self, update): // '''Apply a remotely requested update to one of the stored IUs.''' // if update.uid not in self._iu_store: @@ -192,7 +218,7 @@ public class OutputBuffer extends Buffer { iu.getPayload().remove(k, update.getWriterName()); } - if (update.getNewItemsList().size() > 0) + if (update.getNewItemsList().size() > 0) { HashMap<String, String> payloadUpdate = new HashMap<String, String>(); @@ -305,6 +331,35 @@ public class OutputBuffer extends Buffer } } + /* + * Resend an requested iu over the specific hidden channel. (dlw) TODO + */ + private int remoteResendRequest(IUResendRequest iu_resend_request_pack) + { + if (!iuStore.containsKey(iu_resend_request_pack.getUid())) + { + logger.warn("Remote InBuffer tried to spuriously write non-existent IU {}", iu_resend_request_pack.getUid()); + return 0; + } + AbstractIU iu = iuStore.get(iu_resend_request_pack.getUid()); + if ((iu_resend_request_pack.hasHiddenScopeName() == true)&&(!iu_resend_request_pack.getHiddenScopeName().equals(""))) + { + Informer<Object> informer = getInformer(iu_resend_request_pack.getHiddenScopeName()); + try + { + informer.send(iu); + } + catch (RSBException e) + { + throw new RuntimeException(e); + } + return iu.getRevision(); + } else + { + return 0; + } + } + // def _get_informer(self, iu_category): // '''Return (or create, store and return) an informer object for IUs of the specified category.''' // if iu_category in self._informer_store: @@ -329,7 +384,7 @@ public class OutputBuffer extends Buffer Informer<Object> informer; try { - informer = Factory.getInstance().createInformer("/ipaaca/category/" + category); + informer = Factory.getInstance().createInformer("/ipaaca/channel/" + this.channel + "/category/" + category); } catch (InitializeException e1) { @@ -337,7 +392,7 @@ public class OutputBuffer extends Buffer } informerStore.put(category, informer); - logger.info("Added informer on " + category); + logger.info("Added informer on channel " + this.channel + " and category " + category); try { diff --git a/ipaacalib/java/src/ipaaca/Payload.java b/ipaacalib/java/src/ipaaca/Payload.java index 5dc056b67f4fe60b949b90802c86c8a67b80ccee..5d1ed1d61a36cfd11e687b5904706e629adcf253 100644 --- a/ipaacalib/java/src/ipaaca/Payload.java +++ b/ipaacalib/java/src/ipaaca/Payload.java @@ -34,6 +34,8 @@ package ipaaca; import ipaaca.protobuf.Ipaaca.PayloadItem; +import org.apache.commons.lang.StringEscapeUtils; + import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -55,18 +57,6 @@ public class Payload implements Map<String, String> this.iu = iu; } - // def __init__(self, remote_push_iu, new_payload): - // """Create remote payload object. - // - // Keyword arguments: - // remote_push_iu -- remote IU holding this payload - // new_payload -- payload dict to initialise this remote payload with - // """ - // super(RemotePushPayload, self).__init__() - // self._remote_push_iu = remote_push_iu - // if new_payload is not None: - // for k,v in new_payload.items(): - // dict.__setitem__(self, k, v) public Payload(AbstractIU iu, List<PayloadItem> payloadItems) { this(iu, payloadItems, null); @@ -102,21 +92,29 @@ public class Payload implements Map<String, String> map.clear(); for (PayloadItem item : newPayload) { - map.put(item.getKey(), item.getValue()); + map.put(item.getKey(), pseudoConvertFromJSON(item.getValue(), item.getType())); + } + } + + public String pseudoConvertFromJSON(String value, String type) { + if (type.equals("JSON")) { + if (value.startsWith("\"")) { + //return value.replaceAll("\\\"", ""); + return StringEscapeUtils.unescapeJava(value.substring(1, value.length() - 1)); + } else if (value.startsWith("{") || value.startsWith("[") || value.matches("true") || value.matches("false") || value.matches("-?[0-9]*[.,]?[0-9][0-9]*.*")) { + return value; + } else if (value.equals("null")) { + return ""; + } } + return value; } - // def _remotely_enforced_setitem(self, k, v): - // """Sets an item when requested remotely.""" - // return dict.__setitem__(self, k, v) void enforcedSetItem(String key, String value) { map.put(key, value); } - // def _remotely_enforced_delitem(self, k): - // """Deletes an item when requested remotely.""" - // return dict.__delitem__(self, k) void enforcedRemoveItem(String key) { map.remove(key); @@ -168,31 +166,6 @@ public class Payload implements Map<String, String> return map.keySet(); } - // def __setitem__(self, k, v): - // """Set item in this payload. - // - // Requests item setting from the OutputBuffer holding the local version - // of this IU. Returns when permission is granted and item is set; - // otherwise raises an IUUpdateFailedError. - // """ - // if self._remote_push_iu.committed: - // raise IUCommittedError(self._remote_push_iu) - // if self._remote_push_iu.read_only: - // raise IUReadOnlyError(self._remote_push_iu) - // requested_update = IUPayloadUpdate( - // uid=self._remote_push_iu.uid, - // revision=self._remote_push_iu.revision, - // is_delta=True, - // writer_name=self._remote_push_iu.buffer.unique_name, - // new_items={k:v}, - // keys_to_remove=[]) - // remote_server = self._remote_push_iu.buffer._get_remote_server(self._remote_push_iu) - // new_revision = remote_server.updatePayload(requested_update) - // if new_revision == 0: - // raise IUUpdateFailedError(self._remote_push_iu) - // else: - // self._remote_push_iu._revision = new_revision - // dict.__setitem__(self, k, v) /** * Set item in this payload. * Requests item setting from the OutputBuffer holding the local version @@ -205,32 +178,6 @@ public class Payload implements Map<String, String> return map.put(key, value); } - // - // def __delitem__(self, k): - // """Delete item in this payload. - // - // Requests item deletion from the OutputBuffer holding the local version - // of this IU. Returns when permission is granted and item is deleted; - // otherwise raises an IUUpdateFailedError. - // """ - // if self._remote_push_iu.committed: - // raise IUCommittedError(self._remote_push_iu) - // if self._remote_push_iu.read_only: - // raise IUReadOnlyError(self._remote_push_iu) - // requested_update = IUPayloadUpdate( - // uid=self._remote_push_iu.uid, - // revision=self._remote_push_iu.revision, - // is_delta=True, - // writer_name=self._remote_push_iu.buffer.unique_name, - // new_items={}, - // keys_to_remove=[k]) - // remote_server = self._remote_push_iu.buffer._get_remote_server(self._remote_push_iu) - // new_revision = remote_server.updatePayload(requested_update) - // if new_revision == 0: - // raise IUUpdateFailedError(self._remote_push_iu) - // else: - // self._remote_push_iu._revision = new_revision - // dict.__delitem__(self, k) /** * Delete item in this payload.// * Requests item deletion from the OutputBuffer holding the local version @@ -251,7 +198,7 @@ public class Payload implements Map<String, String> public void putAll(Map<? extends String, ? extends String> newItems) { - putAll(newItems); + putAll(newItems, null); } public void putAll(Map<? extends String, ? extends String> newItems, String writer) diff --git a/ipaacalib/java/src/ipaaca/RemotePushIU.java b/ipaacalib/java/src/ipaaca/RemotePushIU.java index 45af9e8096ac38160dcddb42124cf3fb85515d7f..bfb8060403c42031dfbf70c7b8c55eadaf72b334 100644 --- a/ipaacalib/java/src/ipaaca/RemotePushIU.java +++ b/ipaacalib/java/src/ipaaca/RemotePushIU.java @@ -71,14 +71,6 @@ public class RemotePushIU extends AbstractIU return inputBuffer; } - // def __init__(self, uid, revision, read_only, owner_name, category, type, committed, payload): - // super(RemotePushIU, self).__init__(uid=uid, access_mode=IUAccessMode.PUSH, read_only=read_only) - // self._revision = revision - // self._category = category - // self.owner_name = owner_name - // self._type = type - // self._committed = committed - // self._payload = RemotePushPayload(remote_push_iu=self, new_payload=payload) public RemotePushIU(String uid) { super(uid); @@ -107,7 +99,7 @@ public class RemotePushIU extends AbstractIU { throw new IUReadOnlyException(this); } - PayloadItem newItem = PayloadItem.newBuilder().setKey(key).setValue(value).setType("").build();// TODO use default type in .proto + PayloadItem newItem = PayloadItem.newBuilder().setKey(key).setValue(value).setType("STR").build(); IUPayloadUpdate update = IUPayloadUpdate.newBuilder().setIsDelta(true).setUid(getUid()).setRevision(getRevision()) .setWriterName(getBuffer().getUniqueName()).addNewItems(newItem).build(); @@ -152,7 +144,7 @@ public class RemotePushIU extends AbstractIU .setWriterName(getBuffer().getUniqueName()); for (Map.Entry<? extends String, ? extends String> item : newItems.entrySet()) { - PayloadItem newItem = PayloadItem.newBuilder().setKey(item.getKey()).setValue(item.getValue()).setType("") // TODO: fix this, default in .proto? + PayloadItem newItem = PayloadItem.newBuilder().setKey(item.getKey()).setValue(item.getValue()).setType("STR") .build(); builder.addNewItems(newItem); @@ -187,25 +179,6 @@ public class RemotePushIU extends AbstractIU setRevision(newRevision); } - // def commit(self): - // """Commit to this IU.""" - // if self.read_only: - // raise IUReadOnlyError(self) - // if self._committed: - // # ignore commit requests when already committed - // return - // else: - // commission_request = iuProtoBuf_pb2.IUCommission() - // commission_request.uid = self.uid - // commission_request.revision = self.revision - // commission_request.writer_name = self.buffer.unique_name - // remote_server = self.buffer._get_remote_server(self) - // new_revision = remote_server.commit(commission_request) - // if new_revision == 0: - // raise IUUpdateFailedError(self) - // else: - // self._revision = new_revision - // self._committed = True @Override public void commit(String writerName) { @@ -251,17 +224,6 @@ public class RemotePushIU extends AbstractIU } } - // def __str__(self): - // s = "RemotePushIU{ " - // s += "uid="+self._uid+" " - // s += "(buffer="+(self.buffer.unique_name if self.buffer is not None else "<None>")+") " - // s += "owner_name=" + ("<None>" if self.owner_name is None else self.owner_name) + " " - // s += "payload={ " - // for k,v in self.payload.items(): - // s += k+":'"+v+"', " - // s += "} " - // s += "}" - // return s @Override public String toString() { @@ -280,37 +242,11 @@ public class RemotePushIU extends AbstractIU return b.toString(); } - // - // def _get_payload(self): - // return self._payload public Payload getPayload() { return payload; } - // def _set_payload(self, new_pl): - // if self.committed: - // raise IUCommittedError(self) - // if self.read_only: - // raise IUReadOnlyError(self) - // requested_update = IUPayloadUpdate( - // uid=self.uid, - // revision=self.revision, - // is_delta=False, - // writer_name=self.buffer.unique_name, - // new_items=new_pl, - // keys_to_remove=[]) - // remote_server = self.buffer._get_remote_server(self) - // new_revision = remote_server.updatePayload(requested_update) - // if new_revision == 0: - // raise IUUpdateFailedError(self) - // else: - // self._revision = new_revision - // self._payload = RemotePushPayload(remote_push_iu=self, new_payload=new_pl) - // payload = property( - // fget=_get_payload, - // fset=_set_payload, - // doc='Payload dictionary of the IU.') @Override public void setPayload(List<PayloadItem> newItems, String writerName) { @@ -354,37 +290,33 @@ public class RemotePushIU extends AbstractIU } } - // def _apply_update(self, update): - // """Apply a IUPayloadUpdate to the IU.""" - // self._revision = update.revision - // if update.is_delta: - // for k in update.keys_to_remove: self.payload._remotely_enforced_delitem(k) - // for k, v in update.new_items.items(): self.payload._remotely_enforced_setitem(k, v) - // else: - // # using '_payload' to circumvent the local writing methods - // self._payload = RemotePushPayload(remote_push_iu=self, new_payload=update.new_items) /** * Apply a IUPayloadUpdate to the IU. * @param update */ - public void applyUpdate(IUPayloadUpdate update) - { - revision = update.getRevision(); - if (update.getIsDelta()) - { - for (String key : update.getKeysToRemoveList()) - { - payload.enforcedRemoveItem(key); - } - for (PayloadItem item : update.getNewItemsList()) - { - payload.enforcedSetItem(item.getKey(), item.getValue()); - } - } - else - { - payload = new Payload(this, update.getNewItemsList()); - } + public void applyUpdate(IUPayloadUpdate update) { + revision = update.getRevision(); + if (update.getIsDelta()) { + for (String key : update.getKeysToRemoveList()) { + payload.enforcedRemoveItem(key); + } + for (PayloadItem item : update.getNewItemsList()) { + if (item.getType().equals("STR")) { + payload.enforcedSetItem(item.getKey(), item.getValue()); + } else if (item.getType().equals("JSON")) { + String value = item.getValue(); + if (value.startsWith("\"")) { + payload.enforcedSetItem(item.getKey(), value.replaceAll("\\\"", "")); + } else if (value.startsWith("{") || value.startsWith("[") || value.matches("true") || value.matches("false") || value.matches("-?[0-9]*[.,]?[0-9][0-9]*.*")) { + payload.enforcedSetItem(item.getKey(), value); + } else if (value.equals("null")) { + payload.enforcedSetItem(item.getKey(), ""); + } + } + } + } else { + payload = new Payload(this, update.getNewItemsList()); + } } public void applyLinkUpdate(IULinkUpdate update) @@ -420,9 +352,6 @@ public class RemotePushIU extends AbstractIU } - // def _apply_commission(self): - // """Apply commission to the IU""" - // self._committed = True public void applyCommmision() { committed = true; @@ -466,25 +395,7 @@ public class RemotePushIU extends AbstractIU setRevision(newRevision); } - // def _modify_payload(self, payload, is_delta=True, new_items={}, keys_to_remove=[], writer_name=None): - // """Modify the payload: add or remove item from this payload remotely and send update.""" - // if self.committed: - // raise IUCommittedError(self) - // if self.read_only: - // raise IUReadOnlyError(self) - // requested_update = IUPayloadUpdate( - // uid=self.uid, - // revision=self.revision, - // is_delta=is_delta, - // writer_name=self.buffer.unique_name, - // new_items=new_items, - // keys_to_remove=keys_to_remove) - // remote_server = self.buffer._get_remote_server(self) - // new_revision = remote_server.updatePayload(requested_update) - // if new_revision == 0: - // raise IUUpdateFailedError(self) - // else: - // self._revision = new_revision + @Override void modifyLinks(boolean isDelta, SetMultimap<String, String> linksToAdd, SetMultimap<String, String> linksToRemove, String writerName) { diff --git a/ipaacalib/java/src/ipaaca/util/Blackboard.java b/ipaacalib/java/src/ipaaca/util/Blackboard.java new file mode 100644 index 0000000000000000000000000000000000000000..789182b44e2174812921d6b3eadf2384f986ed8e --- /dev/null +++ b/ipaacalib/java/src/ipaaca/util/Blackboard.java @@ -0,0 +1,129 @@ +package ipaaca.util; + +import ipaaca.AbstractIU; +import ipaaca.HandlerFunctor; +import ipaaca.IUEventType; +import ipaaca.InputBuffer; +import ipaaca.LocalIU; +import ipaaca.OutputBuffer; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ImmutableSet; + +/** + * A simple key-value blackboard + * @author hvanwelbergen + */ +public class Blackboard +{ + private final OutputBuffer outBuffer; + private final InputBuffer inBuffer; + private final LocalIU iu; + private final ComponentNotifier notifier; + private static final String DUMMY_KEY = "DUMMY_KEY"; + public static final String MESSAGE_SUFFIX = "MESSAGE"; + private int dummyValue = 0; + private List<BlackboardUpdateListener> listeners = Collections.synchronizedList(new ArrayList<BlackboardUpdateListener>()); + + public Blackboard(String id, String category) + { + this(id, category, "default"); + } + + private void updateListeners() + { + synchronized (listeners) + { + for (BlackboardUpdateListener listener : listeners) + { + listener.update(); + } + } + } + public Blackboard(String id, String category, String channel) + { + outBuffer = new OutputBuffer(id, channel); + iu = new LocalIU(category); + outBuffer.add(iu); + outBuffer.registerHandler(new HandlerFunctor() + { + @Override + public void handle(AbstractIU iu, IUEventType type, boolean local) + { + updateListeners(); + } + }); + inBuffer = new InputBuffer(id, ImmutableSet.of(ComponentNotifier.NOTIFY_CATEGORY, category + MESSAGE_SUFFIX), channel); + notifier = new ComponentNotifier(id, category, ImmutableSet.of(category), new HashSet<String>(), outBuffer, inBuffer); + notifier.addNotificationHandler(new HandlerFunctor() + { + @Override + public void handle(AbstractIU iuNotify, IUEventType type, boolean local) + { + dummyValue++; + iu.getPayload().put(DUMMY_KEY, "" + dummyValue); + } + }); + notifier.initialize(); + inBuffer.registerHandler(new HandlerFunctor() + { + @Override + public void handle(AbstractIU iuMessage, IUEventType type, boolean local) + { + iu.getPayload().putAll(iuMessage.getPayload()); + updateListeners(); + } + }, ImmutableSet.of(category + MESSAGE_SUFFIX)); + } + + public String put(String key, String value) + { + return iu.getPayload().put(key, value); + } + + public void putAll(Map<String, String> newItems) + { + iu.getPayload().putAll(newItems); + } + + /** + * Get the value corresponding to the key, or null if it is not available + */ + public String get(String key) + { + return iu.getPayload().get(key); + } + + public void addUpdateListener(BlackboardUpdateListener listener) + { + listeners.add(listener); + } + + public Set<String> keySet() + { + return iu.getPayload().keySet(); + } + + public Set<Map.Entry<String, String>> entrySet() + { + return iu.getPayload().entrySet(); + } + + public Collection<String> values() + { + return iu.getPayload().values(); + } + + public void close() + { + outBuffer.close(); + inBuffer.close(); + } +} diff --git a/ipaacalib/java/src/ipaaca/util/BlackboardClient.java b/ipaacalib/java/src/ipaaca/util/BlackboardClient.java new file mode 100644 index 0000000000000000000000000000000000000000..6f70e78f922a289a29e2214b3861a0466755a063 --- /dev/null +++ b/ipaacalib/java/src/ipaaca/util/BlackboardClient.java @@ -0,0 +1,129 @@ +package ipaaca.util; + +import ipaaca.AbstractIU; +import ipaaca.HandlerFunctor; +import ipaaca.IUEventType; +import ipaaca.InputBuffer; +import ipaaca.LocalMessageIU; +import ipaaca.OutputBuffer; +import ipaaca.protobuf.Ipaaca.IU; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ImmutableSet; + +/** + * Client to get/set key value pairs on a Blackboard + * @author hvanwelbergen + * + */ +public class BlackboardClient +{ + private final InputBuffer inBuffer; + private final OutputBuffer outBuffer; + private List<BlackboardUpdateListener> listeners = Collections.synchronizedList(new ArrayList<BlackboardUpdateListener>()); + private final String category; + + public BlackboardClient(String id, String category) + { + this(id, category, "default"); + } + + public BlackboardClient(String id, String category, String channel) + { + this.category = category; + inBuffer = new InputBuffer(id, ImmutableSet.of(category, ComponentNotifier.NOTIFY_CATEGORY), channel); + inBuffer.setResendActive(true); + inBuffer.registerHandler(new HandlerFunctor() + { + @Override + public void handle(AbstractIU iu, IUEventType type, boolean local) + { + synchronized (listeners) + { + for (BlackboardUpdateListener listener : listeners) + { + listener.update(); + } + } + } + }, ImmutableSet.of(category)); + outBuffer = new OutputBuffer(id); + ComponentNotifier notifier = new ComponentNotifier(id, category, new HashSet<String>(), ImmutableSet.of(category), + outBuffer, inBuffer); + notifier.initialize(); + } + + public void close() + { + inBuffer.close(); + outBuffer.close(); + } + + public void waitForBlackboardConnection() + { + while(inBuffer.getIUs().isEmpty()); + } + + public String get(String key) + { + if (inBuffer.getIUs().isEmpty()) + { + return null; + } + return inBuffer.getIUs().iterator().next().getPayload().get(key); + } + + public void put(String key, String value) + { + LocalMessageIU iu = new LocalMessageIU(category+Blackboard.MESSAGE_SUFFIX); + iu.getPayload().put(key,value); + outBuffer.add(iu); + } + + public void putAll(Map<String,String> values) + { + LocalMessageIU iu = new LocalMessageIU(category+Blackboard.MESSAGE_SUFFIX); + iu.getPayload().putAll(values); + outBuffer.add(iu); + } + + private boolean hasIU() + { + return !inBuffer.getIUs().isEmpty(); + } + + private AbstractIU getIU() + { + return inBuffer.getIUs().iterator().next(); + } + + public Set<String> keySet() + { + if(!hasIU())return new HashSet<>(); + return getIU().getPayload().keySet(); + } + + public Set<Map.Entry<String, String>> entrySet() + { + if(!hasIU())return new HashSet<>(); + return getIU().getPayload().entrySet(); + } + + public Collection<String> values() + { + if(!hasIU())return new HashSet<>(); + return getIU().getPayload().values(); + } + + public void addUpdateListener(BlackboardUpdateListener listener) + { + listeners.add(listener); + } +} diff --git a/ipaacalib/java/src/ipaaca/util/BlackboardUpdateListener.java b/ipaacalib/java/src/ipaaca/util/BlackboardUpdateListener.java new file mode 100644 index 0000000000000000000000000000000000000000..e1ebdde021533542463fdea4e5a3a20534d0fba4 --- /dev/null +++ b/ipaacalib/java/src/ipaaca/util/BlackboardUpdateListener.java @@ -0,0 +1,6 @@ +package ipaaca.util; + +public interface BlackboardUpdateListener +{ + void update(); +} diff --git a/ipaacalib/java/src/ipaacademo/TestListener.java b/ipaacalib/java/src/ipaacademo/TestListener.java new file mode 100644 index 0000000000000000000000000000000000000000..4759207badc3fc5c609ca5f9c61949af0c4c3d7c --- /dev/null +++ b/ipaacalib/java/src/ipaacademo/TestListener.java @@ -0,0 +1,125 @@ +/* + * This file is part of IPAACA, the + * "Incremental Processing Architecture + * for Artificial Conversational Agents". + * + * Copyright (c) 2009-2013 Sociable Agents Group + * CITEC, Bielefeld University + * + * http://opensource.cit-ec.de/projects/ipaaca/ + * http://purl.org/net/ipaaca + * + * This file may be licensed under the terms of of the + * GNU Lesser General Public License Version 3 (the ``LGPL''), + * or (at your option) any later version. + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the LGPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the LGPL along with this + * program. If not, go to http://www.gnu.org/licenses/lgpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The development of this software was supported by the + * Excellence Cluster EXC 277 Cognitive Interaction Technology. + * The Excellence Cluster EXC 277 is a grant of the Deutsche + * Forschungsgemeinschaft (DFG) in the context of the German + * Excellence Initiative. + */ + +package ipaacademo; + +import ipaaca.AbstractIU; +import ipaaca.HandlerFunctor; +import ipaaca.IUEventHandler; +import ipaaca.IUEventType; +import ipaaca.Initializer; +import ipaaca.InputBuffer; +import ipaaca.OutputBuffer; +import ipaaca.RemotePushIU; + +import java.util.EnumSet; +import java.util.Set; + +import javax.swing.JFrame; +import javax.swing.JLabel; + +import com.google.common.collect.ImmutableSet; + +public class TestListener +{ + + private static final class MyEventHandler implements HandlerFunctor + { + @Override + public void handle(AbstractIU iu, IUEventType type, boolean local) + { + switch(type) + { + case ADDED: System.out.println("IU added "+iu.getPayload().get("CONTENT")); break; + case COMMITTED: System.out.println("IU committed"); break; + case UPDATED: System.out.println("IU updated "+iu.getPayload()); break; + case LINKSUPDATED: System.out.println("IU links updated"); break; + case RETRACTED: break; + case DELETED: break; + } + } + + } + + static + { + Initializer.initializeIpaacaRsb(); + } + + private static final String CATEGORY = "spam"; + private static final double RATE = 0.5; + private UpdateThread updateThread; + + public TestListener() + { + Set<String> categories = new ImmutableSet.Builder<String>().add(CATEGORY).build(); + JLabel label = new JLabel(""); + + updateThread = new UpdateThread(new InputBuffer("TestListener", categories),label); + } + + public void start() + { + updateThread.start(); + } + + private static class UpdateThread extends Thread + { + private InputBuffer inBuffer; + private JLabel label; + + public UpdateThread(InputBuffer inBuffer, JLabel label) + { + this.inBuffer = inBuffer; + this.label = label; + + EnumSet<IUEventType> types = EnumSet.of(IUEventType.ADDED,IUEventType.COMMITTED,IUEventType.UPDATED,IUEventType.LINKSUPDATED); + Set<String> categories = new ImmutableSet.Builder<String>().add(CATEGORY).build(); + MyEventHandler printingEventHandler; + printingEventHandler = new MyEventHandler(); + this.inBuffer.registerHandler(new IUEventHandler(printingEventHandler,types,categories)); + } + + @Override + public void run() + { + System.out.println("Starting!"); + } + + } + + public static void main(String args[]) + { + TestListener tl = new TestListener(); + tl.start(); + } +} diff --git a/ipaacalib/java/src/ipaacademo/TextPrinter.java b/ipaacalib/java/src/ipaacademo/TextPrinter.java index 13ef1a1fd287649b1d82c211eb910cca308f00dd..5161ad3b7eb1c91ea02e23f4c0f6ccb4399ef500 100644 --- a/ipaacalib/java/src/ipaacademo/TextPrinter.java +++ b/ipaacalib/java/src/ipaacademo/TextPrinter.java @@ -222,7 +222,8 @@ public class TextPrinter TextPrinter tp = new TextPrinter(); - OutputBuffer outBuffer = new OutputBuffer("componentX"); + //OutputBuffer outBuffer = new OutputBuffer("componentX"); + /*String[] inputString = {"h","e","l","l","o"," ","w","o","r","l","d","!"}; LocalIU predIU = null; for(String str:inputString) diff --git a/ipaacalib/java/test/src/ipaaca/InputBufferTest.java b/ipaacalib/java/test/src/ipaaca/InputBufferTest.java index d0ff20a710a44ca5da7b8cce805cc7e86f9223d8..420a3687480cc2f0a2dbc2f425ac83f163858907 100644 --- a/ipaacalib/java/test/src/ipaaca/InputBufferTest.java +++ b/ipaacalib/java/test/src/ipaaca/InputBufferTest.java @@ -22,7 +22,7 @@ import com.google.common.collect.ImmutableSet; public class InputBufferTest { private static final String COMPID = "Comp1"; - private static final String CATEGORY = "category1"; + private static final String CATEGORY = "testcat"; private InputBuffer inBuffer; @@ -47,10 +47,10 @@ public class InputBufferTest @Test public void testHandleRemotePushEvent() throws RSBException, InterruptedException { - Informer<Object> informer = Factory.getInstance().createInformer("/ipaaca/category/"+CATEGORY); + Informer<Object> informer = Factory.getInstance().createInformer("/ipaaca/channel/default/category/"+CATEGORY); informer.activate(); RemotePushIU iu = new RemotePushIU("uid1"); - iu.setCategory("/ipaaca/category/"+CATEGORY); + iu.setCategory("/ipaaca/channel/default/category/"+CATEGORY); iu.setOwnerName("owner"); iu.setReadOnly(false); iu.setRevision(1); diff --git a/ipaacalib/java/test/src/ipaaca/util/BlackboardIntegrationTest.java b/ipaacalib/java/test/src/ipaaca/util/BlackboardIntegrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..afd10b00ea0b3b18536bdfa6430c40d3f2bdd48a --- /dev/null +++ b/ipaacalib/java/test/src/ipaaca/util/BlackboardIntegrationTest.java @@ -0,0 +1,129 @@ +package ipaaca.util; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import ipaaca.Initializer; + +import org.junit.After; +import org.junit.Test; + +import com.google.common.collect.ImmutableMap; + +/** + * Integration tests for the blackboard + * @author hvanwelbergen + */ +public class BlackboardIntegrationTest +{ + static + { + Initializer.initializeIpaacaRsb(); + } + + private Blackboard bb = new Blackboard("myblackboard","blackboardx"); + private BlackboardClient bbc; + + @After + public void after() + { + bb.close(); + if(bbc!=null) + { + bbc.close(); + } + } + + @Test + public void testGetValueFromBlackboardBeforeConnection() + { + bb.put("key1","value1"); + bbc = new BlackboardClient("myblackboardclient","blackboardx"); + bbc.waitForBlackboardConnection(); + assertEquals("value1", bbc.get("key1")); + } + + @Test + public void testGetValueFromBlackboardAfterConnection() throws InterruptedException + { + bbc = new BlackboardClient("myblackboardclient","blackboardx"); + bbc.waitForBlackboardConnection(); + bb.put("key1","value1"); + Thread.sleep(200); + assertEquals("value1", bbc.get("key1")); + } + + @Test + public void testSetValueOnBlackboard() throws InterruptedException + { + bbc = new BlackboardClient("myblackboardclient","blackboardx"); + bbc.waitForBlackboardConnection(); + bbc.put("key2","value2"); + Thread.sleep(300); + assertEquals("value2", bb.get("key2")); + } + + @Test + public void testBlackboardUpdateHandler()throws InterruptedException + { + BlackboardUpdateListener mockListener = mock(BlackboardUpdateListener.class); + bb.addUpdateListener(mockListener); + bbc = new BlackboardClient("myblackboardclient","blackboardx"); + bbc.waitForBlackboardConnection(); + bbc.put("key2","value2"); + Thread.sleep(200); + bb.put("key2","value3"); + verify(mockListener,times(1)).update(); + } + + @Test + public void testBlackboardClientUpdateHandler() throws InterruptedException + { + BlackboardUpdateListener mockListener = mock(BlackboardUpdateListener.class); + bbc = new BlackboardClient("myblackboardclient","blackboardx"); + bbc.waitForBlackboardConnection(); + bbc.addUpdateListener(mockListener); + bb.put("key3","value3"); + Thread.sleep(200); + bbc.put("key3","value4"); + verify(mockListener,times(2)).update(); + } + + @Test + public void testSetManyValuesOnBlackboard() throws InterruptedException + { + bbc = new BlackboardClient("myblackboardclient","blackboardx"); + bbc.waitForBlackboardConnection(); + for(int i=0;i<100;i++) + { + bbc.put("key"+i,"value"+i); + bb.put("key"+i,"value"+i); + } + Thread.sleep(300); + assertEquals("value2", bb.get("key2")); + assertEquals("value3", bb.get("key3")); + } + + @Test + public void testSetValuesOnClient() throws InterruptedException + { + bbc = new BlackboardClient("myblackboardclient","blackboardx"); + bbc.waitForBlackboardConnection(); + bbc.putAll(ImmutableMap.of("key1","value1","key2","value2")); + Thread.sleep(200); + assertEquals("value1", bb.get("key1")); + assertEquals("value2", bb.get("key2")); + } + + @Test + public void testSetValuesOnBlackBoard() throws InterruptedException + { + bbc = new BlackboardClient("myblackboardclient","blackboardx"); + bbc.waitForBlackboardConnection(); + bb.putAll(ImmutableMap.of("key1","value1","key2","value2")); + Thread.sleep(200); + assertEquals("value1", bbc.get("key1")); + assertEquals("value2", bbc.get("key2")); + } +} diff --git a/ipaacalib/java/test/src/ipaaca/util/ComponentNotifierIntegrationTest.java b/ipaacalib/java/test/src/ipaaca/util/ComponentNotifierIntegrationTest.java index aef0e5ae662d6c3b16c384b2980e6b41bb7013b3..5acbed6b861d6ae736a65ac4d01702d25e0cb1af 100644 --- a/ipaacalib/java/test/src/ipaaca/util/ComponentNotifierIntegrationTest.java +++ b/ipaacalib/java/test/src/ipaaca/util/ComponentNotifierIntegrationTest.java @@ -1,21 +1,22 @@ package ipaaca.util; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import ipaaca.AbstractIU; import ipaaca.HandlerFunctor; import ipaaca.IUEventType; import ipaaca.Initializer; import ipaaca.InputBuffer; +import ipaaca.LocalIU; import ipaaca.OutputBuffer; -import ipaaca.util.ComponentNotifier; import java.util.Set; +import lombok.Getter; + import org.junit.After; import org.junit.Test; -import lombok.Getter; - import com.google.common.collect.ImmutableSet; /** @@ -29,7 +30,7 @@ public class ComponentNotifierIntegrationTest private ComponentNotifier notifier2; private InputBuffer inBuffer; private OutputBuffer outBuffer; - + private static final String OTHER_CATEGORY="OTHER"; static { Initializer.initializeIpaacaRsb(); @@ -67,6 +68,13 @@ public class ComponentNotifierIntegrationTest return new ComponentNotifier(id, "test", ImmutableSet.copyOf(sendList), ImmutableSet.copyOf(recvList), outBuffer, inBuffer); } + private ComponentNotifier setupCompNotifierWithOtherCategoryInputBuffer(String id, Set<String> sendList, Set<String> recvList) + { + inBuffer = new InputBuffer(id + "in", ImmutableSet.of(ComponentNotifier.NOTIFY_CATEGORY, OTHER_CATEGORY)); + outBuffer = new OutputBuffer(id + "out"); + return new ComponentNotifier(id, "test", ImmutableSet.copyOf(sendList), ImmutableSet.copyOf(recvList), outBuffer, inBuffer); + } + @Test public void testSelf() throws InterruptedException { @@ -97,4 +105,19 @@ public class ComponentNotifierIntegrationTest assertEquals(1, h1.getNumCalled()); assertEquals(1, h2.getNumCalled()); } + + @Test + public void testOtherCategoryInInputBuffer() throws InterruptedException + { + notifier1 = setupCompNotifierWithOtherCategoryInputBuffer("not1", ImmutableSet.of("a1", "b1"), ImmutableSet.of("a3", "b1")); + MyHandlerFunctor h1 = new MyHandlerFunctor(); + notifier1.addNotificationHandler(h1); + + OutputBuffer out = new OutputBuffer("out"); + LocalIU iu = new LocalIU(OTHER_CATEGORY); + out.add(iu); + Thread.sleep(500); + assertEquals(0, h1.getNumCalled()); + assertNotNull(inBuffer.getIU(iu.getUid())); + } } diff --git a/ipaacalib/proto/ipaaca.proto b/ipaacalib/proto/ipaaca.proto index 7288dc84a0f8dcef4689166b26571c2d936c6200..c0e1d1d196c31a34d05aaf18996bc7c1f9480d6d 100755 --- a/ipaacalib/proto/ipaaca.proto +++ b/ipaacalib/proto/ipaaca.proto @@ -2,7 +2,7 @@ // "Incremental Processing Architecture // for Artificial Conversational Agents". // -// Copyright (c) 2009-2013 Sociable Agents Group +// Copyright (c) 2009-2014 Social Cognitive Systems Group // CITEC, Bielefeld University // // http://opensource.cit-ec.de/projects/ipaaca/ @@ -84,6 +84,11 @@ message IUCommission { required string writer_name = 3; } +message IUResendRequest { + required string uid = 1; + required string hidden_scope_name = 2; +} + message IULinkUpdate { required string uid = 1; required uint32 revision = 2; @@ -92,5 +97,3 @@ message IULinkUpdate { required bool is_delta = 5 [default = false]; required string writer_name = 6; } - - diff --git a/ipaacalib/python/ivy.xml b/ipaacalib/python/ivy.xml index 1b948cd692f7b7df1b4aeaf22f200435e5f498b5..e559706ba1ca9a0010dc3d0d75e8218907fe1278 100644 --- a/ipaacalib/python/ivy.xml +++ b/ipaacalib/python/ivy.xml @@ -10,3 +10,4 @@ </dependencies> </ivy-module> + diff --git a/ipaacalib/python/src/ipaaca/__init__.py b/ipaacalib/python/src/ipaaca/__init__.py index db2f873b9ca7f6405ba6ba4ba8a27b61f0a3f29d..09321c0bd26070239ee9a44f354e62d5f40eeaff 100755 --- a/ipaacalib/python/src/ipaaca/__init__.py +++ b/ipaacalib/python/src/ipaaca/__init__.py @@ -2,10 +2,10 @@ # This file is part of IPAACA, the # "Incremental Processing Architecture -# for Artificial Conversational Agents". +# for Artificial Conversational Agents". # -# Copyright (c) 2009-2013 Sociable Agents Group -# CITEC, Bielefeld University +# Copyright (c) 2009-2014 Social Cognitive Systems Group +# CITEC, Bielefeld University # # http://opensource.cit-ec.de/projects/ipaaca/ # http://purl.org/net/ipaaca @@ -22,7 +22,7 @@ # You should have received a copy of the LGPL along with this # program. If not, go to http://www.gnu.org/licenses/lgpl.html # or write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # The development of this software was supported by the # Excellence Cluster EXC 277 Cognitive Interaction Technology. @@ -30,1590 +30,60 @@ # Forschungsgemeinschaft (DFG) in the context of the German # Excellence Initiative. -from __future__ import print_function, division - -import logging -import sys -import threading -import uuid -import collections -import copy -import time +from __future__ import division, print_function import rsb import rsb.converter import ipaaca_pb2 +import ipaaca.converter +from ipaaca.buffer import InputBuffer, OutputBuffer +from ipaaca.exception import * +from ipaaca.iu import IU, Message, IUAccessMode, IUEventType +from ipaaca.misc import enable_logging, IpaacaArgumentParser +from ipaaca.payload import Payload -_DEFAULT_PAYLOAD_UPDATE_TIMEOUT = 0.1 - -# IDEAS -# We should think about relaying the update event (or at least the -# affected keys in the payload / links) to the event handlers! - -# THOUGHTS -# Output buffers could generate UIDs for IUs on request, without -# publishing them at that time. Then UID could then be used -# for internal links etc. The IU may be published later through -# the same buffer that allocated the UID. - -# WARNINGS -# category is now the FIRST argument for IU constructors - -__all__ = [ - 'IUEventType', - 'IUAccessMode', - 'InputBuffer', 'OutputBuffer', - 'IU', - 'IUPublishedError', 'IUUpdateFailedError', 'IUCommittedError', 'IUReadOnlyError', 'IUNotFoundError', - 'logger' -] - - -## --- Utilities ------------------------------------------------------------- - - -def enum(*sequential, **named): - """Create an enum type. - - Based on suggestion of Alec Thomas on stackoverflow.com: - http://stackoverflow.com/questions/36932/ - whats-the-best-way-to-implement-an-enum-in-python/1695250#1695250 - """ - enums = dict(zip(sequential, range(len(sequential))), **named) - return type('Enum', (), enums) - - -def pack_typed_payload_item(protobuf_object, key, value): - protobuf_object.key = key - protobuf_object.value = value - protobuf_object.type = 'str' # TODO: more types - - -def unpack_typed_payload_item(protobuf_object): - # TODO: more types - return (protobuf_object.key, protobuf_object.value) - - -class IpaacaLoggingHandler(logging.Handler): - - def __init__(self, level=logging.DEBUG): - logging.Handler.__init__(self, level) - - def emit(self, record): - meta = '[ipaaca] (' + str(record.levelname) + ') ' - msg = str(record.msg.format(record.args)) - print(meta + msg) - - -## --- Global Definitions ---------------------------------------------------- - - -IUEventType = enum( - ADDED = 'ADDED', - COMMITTED = 'COMMITTED', - DELETED = 'DELETED', - RETRACTED = 'RETRACTED', - UPDATED = 'UPDATED', - LINKSUPDATED = 'LINKSUPDATED', - MESSAGE = 'MESSAGE' -) - - -IUAccessMode = enum( - "PUSH", - "REMOTE", - "MESSAGE" -) - - -## --- Errors and Exceptions ------------------------------------------------- - - -class IUPublishedError(Exception): - """Error publishing of an IU failed since it is already in the buffer.""" - def __init__(self, iu): - super(IUPublishedError, self).__init__('IU ' + str(iu.uid) + ' is already present in the output buffer.') - - -class IUUpdateFailedError(Exception): - """Error indicating that a remote IU update failed.""" - def __init__(self, iu): - super(IUUpdateFailedError, self).__init__('Remote update failed for IU ' + str(iu.uid) + '.') - - -class IUCommittedError(Exception): - """Error indicating that an IU is immutable because it has been committed to.""" - def __init__(self, iu): - super(IUCommittedError, self).__init__('Writing to IU ' + str(iu.uid) + ' failed -- it has been committed to.') - - -class IUReadOnlyError(Exception): - """Error indicating that an IU is immutable because it is 'read only'.""" - def __init__(self, iu): - super(IUReadOnlyError, self).__init__('Writing to IU ' + str(iu.uid) + ' failed -- it is read-only.') - -class IUNotFoundError(Exception): - """Error indicating that an IU UID was unexpectedly not found in an internal store.""" - def __init__(self, iu_uid): - super(IUNotFoundError, self).__init__('Lookup of IU ' + str(iu_uid) + ' failed.') - -class IUPayloadLockTimeoutError(Exception): - """Error indicating that exclusive access to the Payload could not be obtained in time.""" - def __init__(self, iu): - super(IUPayloadLockTimeoutError, self).__init__('Timeout while accessing payload of IU ' + str(iu.uid) + '.') - -class IUPayloadLockedError(Exception): - """Error indicating that exclusive access to the Payload could not be obtained because someone actually locked it.""" - def __init__(self, iu): - super(IUPayloadLockedError, self).__init__('IU '+str(iu.uid)+' was locked during access attempt.') - - -## --- Generation Architecture ----------------------------------------------- - -class Payload(dict): - def __init__(self, iu, writer_name=None, new_payload=None, omit_init_update_message=False, update_timeout=_DEFAULT_PAYLOAD_UPDATE_TIMEOUT): - self.iu = iu - pl1 = {} if new_payload is None else new_payload - pl = {} - for k,v in pl1.items(): - if type(k)==str: - k=unicode(k,'utf8') - if type(v)==str: - v=unicode(v,'utf8') - pl[k] = v - # NOTE omit_init_update_message is necessary to prevent checking for - # exceptions and sending updates in the case where we just receive - # a whole new payload from the remote side and overwrite it locally. - for k, v in pl.items(): - dict.__setitem__(self, k, v) - if (not omit_init_update_message) and (self.iu.buffer is not None): - self.iu._modify_payload(is_delta=False, new_items=pl, keys_to_remove=[], writer_name=writer_name) - self._update_on_every_change = True - self._collected_modifications = {} - self._collected_removals = [] - self._update_timeout = update_timeout - self._batch_update_writer_name = None # name of remote buffer or None - self._batch_update_lock = threading.RLock() - self._batch_update_cond = threading.Condition(threading.RLock()) - - def merge(self, payload, writer_name=None): - self._batch_update_lock.acquire(True) - #if not self._batch_update_lock.acquire(False): - # print('Someone failed a lock trying to merge '+str(payload.keys())) - # raise IUPayloadLockedError(self.iu) - #print("Payload.merge() IN, Merging "+str(payload.keys())) - for k, v in payload.items(): - if type(k)==str: - k=unicode(k,'utf8') - if type(v)==str: - v=unicode(v,'utf8') - self.iu._modify_payload(is_delta=True, new_items=payload, keys_to_remove=[], writer_name=writer_name) - r = dict.update(payload) # batch update - #print("Payload.merge() OUT") - self._batch_update_lock.release() - return r - - def __setitem__(self, k, v, writer_name=None): - self._batch_update_lock.acquire(True) - #if not self._batch_update_lock.acquire(False): - # print('Someone failed a lock trying to set '+k+' to '+v) - # raise IUPayloadLockedError(self.iu) - #print("Payload.__setitem__() IN, Setting "+k+' to '+v) - #print(" by writer "+str(writer_name)) - if type(k)==str: - k=unicode(k,'utf8') - if type(v)==str: - v=unicode(v,'utf8') - if self._update_on_every_change: - #print(" running _modify_payload with writer name "+str(writer_name)) - self.iu._modify_payload(is_delta=True, new_items={k:v}, keys_to_remove=[], writer_name=writer_name) - else: # Collect additions/modifications - self._batch_update_writer_name = writer_name - self._collected_modifications[k] = v - r = dict.__setitem__(self, k, v) - #print("Payload.__setitem__() OUT") - self._batch_update_lock.release() - return r - - def __delitem__(self, k, writer_name=None): - self._batch_update_lock.acquire(True) - #if not self._batch_update_lock.acquire(False): - # print('Someone failed a lock trying to del '+k) - # raise IUPayloadLockedError(self.iu) - #print("Payload.__delitem__() IN, Deleting "+k) - if type(k)==str: - k=unicode(k,'utf8') - if self._update_on_every_change: - self.iu._modify_payload(is_delta=True, new_items={}, keys_to_remove=[k], writer_name=writer_name) - else: # Collect additions/modifications - self._batch_update_writer_name = writer_name - self._collected_removals.append(k) - r = dict.__delitem__(self, k) - #print("Payload.__delitem__() OUT") - self._batch_update_lock.release() - return r - - # Context-manager based batch updates, not yet thread-safe (on remote updates) - def __enter__(self): - #print('running Payload.__enter__()') - self._wait_batch_update_lock(self._update_timeout) - self._update_on_every_change = False - - def __exit__(self, type, value, traceback): - #print('running Payload.__exit__()') - self.iu._modify_payload(is_delta=True, new_items=self._collected_modifications, keys_to_remove=self._collected_removals, writer_name=self._batch_update_writer_name) - self._collected_modifications = {} - self._collected_removals = [] - self._update_on_every_change = True - self._batch_update_writer_name = None - self._batch_update_lock.release() - - def _remotely_enforced_setitem(self, k, v): - """Sets an item when requested remotely.""" - return dict.__setitem__(self, k, v) - - def _remotely_enforced_delitem(self, k): - """Deletes an item when requested remotely.""" - return dict.__delitem__(self, k) - - def _wait_batch_update_lock(self, timeout): - # wait lock with time-out http://stackoverflow.com/a/8393033 - with self._batch_update_cond: - current_time = start_time = time.time() - while current_time < start_time + timeout: - if self._batch_update_lock.acquire(False): - return True - else: - self._batch_update_cond.wait(timeout - current_time + start_time) - current_time = time.time() - raise IUPayloadLockTimeoutError(self.iu) - - -class IUInterface(object): #{{{ - - """Base class of all specialised IU classes.""" - - def __init__(self, uid, access_mode=IUAccessMode.PUSH, read_only=False): - """Creates an IU. - - Keyword arguments: - uid -- unique ID of this IU - access_mode -- access mode of this IU - read_only -- flag indicating whether this IU is read_only or not - """ - self._uid = uid - self._revision = None - self._category = None - self._payload_type = None - self._owner_name = None - self._committed = False - self._retracted = False - self._access_mode = access_mode - self._read_only = read_only - self._buffer = None - # payload is not present here - self._links = collections.defaultdict(set) - - def __str__(self): - s = unicode(self.__class__)+"{ " - s += "category="+("<None>" if self._category is None else self._category)+" " - s += "uid="+self._uid+" " - s += "(buffer="+(self.buffer.unique_name if self.buffer is not None else "<None>")+") " - s += "owner_name=" + ("<None>" if self.owner_name is None else self.owner_name) + " " - s += "payload={ " - for k,v in self.payload.items(): - s += k+":'"+v+"', " - s += "} " - s += "links={ " - for t,ids in self.get_all_links().items(): - s += t+":'"+str(ids)+"', " - s += "} " - s += "}" - return s - - - def _add_and_remove_links(self, add, remove): - '''Just add and remove the new links in our links set, do not send an update here''' - '''Note: Also used for remotely enforced links updates.''' - for type in remove.keys(): self._links[type] -= set(remove[type]) - for type in add.keys(): self._links[type] |= set(add[type]) - def _replace_links(self, links): - '''Just wipe and replace our links set, do not send an update here''' - '''Note: Also used for remotely enforced links updates.''' - self._links = collections.defaultdict(set) - for type in links.keys(): self._links[type] |= set(links[type]) - - def add_links(self, type, targets, writer_name=None): - '''Attempt to add links if the conditions are met - and send an update message. Then call the local setter.''' - if not hasattr(targets, '__iter__'): targets=[targets] - self._modify_links(is_delta=True, new_links={type:targets}, links_to_remove={}, writer_name=writer_name) - self._add_and_remove_links( add={type:targets}, remove={} ) - def remove_links(self, type, targets, writer_name=None): - '''Attempt to remove links if the conditions are met - and send an update message. Then call the local setter.''' - if not hasattr(targets, '__iter__'): targets=[targets] - self._modify_links(is_delta=True, new_links={}, links_to_remove={type:targets}, writer_name=writer_name) - self._add_and_remove_links( add={}, remove={type:targets} ) - def modify_links(self, add, remove, writer_name=None): - '''Attempt to modify links if the conditions are met - and send an update message. Then call the local setter.''' - self._modify_links(is_delta=True, new_links=add, links_to_remove=remove, writer_name=writer_name) - self._add_and_remove_links( add=add, remove=remove ) - def set_links(self, links, writer_name=None): - '''Attempt to set (replace) links if the conditions are met - and send an update message. Then call the local setter.''' - self._modify_links(is_delta=False, new_links=links, links_to_remove={}, writer_name=writer_name) - self._replace_links( links=links ) - def get_links(self, type): - return set(self._links[type]) - def get_all_links(self): - return copy.deepcopy(self._links) - - def _get_revision(self): - return self._revision - revision = property(fget=_get_revision, doc='Revision number of the IU.') - - def _get_category(self): - return self._category - category = property(fget=_get_category, doc='Category of the IU.') - - def _get_payload_type(self): - return self._payload_type - payload_type = property(fget=_get_payload_type, doc='Type of the IU payload') - - def _get_committed(self): - return self._committed - committed = property( - fget=_get_committed, - doc='Flag indicating whether this IU has been committed to.') - - def _get_retracted(self): - return self._retracted - retracted = property( - fget=_get_retracted, - doc='Flag indicating whether this IU has been retracted.') - - def _get_uid(self): - return self._uid - uid = property(fget=_get_uid, doc='Unique ID of the IU.') - - def _get_access_mode(self): - return self._access_mode - access_mode = property(fget=_get_access_mode, doc='Access mode of the IU.') - - def _get_read_only(self): - return self._read_only - read_only = property( - fget=_get_read_only, - doc='Flag indicating whether this IU is read only.') - - def _get_buffer(self): - return self._buffer - def _set_buffer(self, buffer): - if self._buffer is not None: - raise Exception('The IU is already in a buffer, cannot move it.') - self._buffer = buffer - buffer = property( - fget=_get_buffer, - fset=_set_buffer, - doc='Buffer this IU is held in.') - - def _get_owner_name(self): - return self._owner_name - def _set_owner_name(self, owner_name): - if self._owner_name is not None: - raise Exception('The IU already has an owner name, cannot change it.') - self._owner_name = owner_name - owner_name = property( - fget=_get_owner_name, - fset=_set_owner_name, - doc="The IU's owner's name.") -#}}} - -class IU(IUInterface):#{{{ - - """A local IU.""" - - def __init__(self, category='undef', access_mode=IUAccessMode.PUSH, read_only=False, _payload_type='MAP'): - super(IU, self).__init__(uid=None, access_mode=access_mode, read_only=read_only) - self._revision = 1 - self.uid = str(uuid.uuid4()) - self._category = category - self._payload_type = _payload_type - self.revision_lock = threading.RLock() - self._payload = Payload(iu=self) - - def _modify_links(self, is_delta=False, new_links={}, links_to_remove={}, writer_name=None): - if self.committed: - raise IUCommittedError(self) - with self.revision_lock: - # modify links locally - self._increase_revision_number() - if self.is_published: - # send update to remote holders - self.buffer._send_iu_link_update( - self, - revision=self.revision, - is_delta=is_delta, - new_links=new_links, - links_to_remove=links_to_remove, - writer_name=self.owner_name if writer_name is None else writer_name) - - def _modify_payload(self, is_delta=True, new_items={}, keys_to_remove=[], writer_name=None): - """Modify the payload: add or remove items from this payload locally and send update.""" - if self.committed: - raise IUCommittedError(self) - with self.revision_lock: - # set item locally - # FIXME: Is it actually set locally? - self._increase_revision_number() - if self.is_published: - #print(' _modify_payload: running send_iu_pl_upd with writer name '+str(writer_name)) - # send update to remote holders - self.buffer._send_iu_payload_update( - self, - revision=self.revision, - is_delta=is_delta, - new_items=new_items, - keys_to_remove=keys_to_remove, - writer_name=self.owner_name if writer_name is None else writer_name) - - def _increase_revision_number(self): - self._revision += 1 - - def _internal_commit(self, writer_name=None): - if self.committed: - raise IUCommittedError(self) - with self.revision_lock: - if not self._committed: - self._increase_revision_number() - self._committed = True - if self.buffer is not None: - self.buffer._send_iu_commission(self, writer_name=writer_name) - - def commit(self): - """Commit to this IU.""" - return self._internal_commit() - - def _get_payload(self): - return self._payload - def _set_payload(self, new_pl, writer_name=None): - if self.committed: - raise IUCommittedError(self) - with self.revision_lock: - self._increase_revision_number() - self._payload = Payload( - iu=self, - writer_name=None if self.buffer is None else (self.buffer.unique_name if writer_name is None else writer_name), - new_payload=new_pl) - payload = property( - fget=_get_payload, - fset=_set_payload, - doc='Payload dictionary of this IU.') - - def _get_is_published(self): - return self.buffer is not None - is_published = property( - fget=_get_is_published, - doc='Flag indicating whether this IU has been published or not.') - - def _set_buffer(self, buffer): - if self._buffer is not None: - raise Exception('The IU is already in a buffer, cannot move it.') - self._buffer = buffer - self.owner_name = buffer.unique_name - self._payload.owner_name = buffer.unique_name - buffer = property( - fget=IUInterface._get_buffer, - fset=_set_buffer, - doc='Buffer this IU is held in.') - - def _set_uid(self, uid): - if self._uid is not None: - raise AttributeError('The uid of IU ' + self.uid + ' has already been set, cannot change it.') - self._uid = uid - uid = property( - fget=IUInterface._get_uid, - fset=_set_uid, - doc='Unique ID of the IU.') -#}}} - -class Message(IU):#{{{ - """Local IU of Message sub-type. Can be handled like a normal IU, but on the remote side it is only existent during the handler calls.""" - def __init__(self, category='undef', access_mode=IUAccessMode.MESSAGE, read_only=True, _payload_type='MAP'): - super(Message, self).__init__(category=category, access_mode=access_mode, read_only=read_only, _payload_type=_payload_type) - - def _modify_links(self, is_delta=False, new_links={}, links_to_remove={}, writer_name=None): - if self.is_published: - logger.info('Info: modifying a Message after sending has no global effects') - - def _modify_payload(self, is_delta=True, new_items={}, keys_to_remove=[], writer_name=None): - if self.is_published: - logger.info('Info: modifying a Message after sending has no global effects') - - def _increase_revision_number(self): - self._revision += 1 - - def _internal_commit(self, writer_name=None): - if self.is_published: - logger.info('Info: committing to a Message after sending has no global effects') - - def commit(self): - return self._internal_commit() - - def _get_payload(self): - return self._payload - def _set_payload(self, new_pl, writer_name=None): - if self.is_published: - logger.info('Info: modifying a Message after sending has no global effects') - else: - if self.committed: - raise IUCommittedError(self) - with self.revision_lock: - self._increase_revision_number() - self._payload = Payload( - iu=self, - writer_name=None if self.buffer is None else (self.buffer.unique_name if writer_name is None else writer_name), - new_payload=new_pl) - payload = property( - fget=_get_payload, - fset=_set_payload, - doc='Payload dictionary of this IU.') - - def _get_is_published(self): - return self.buffer is not None - is_published = property( - fget=_get_is_published, - doc='Flag indicating whether this IU has been published or not.') - - def _set_buffer(self, buffer): - if self._buffer is not None: - raise Exception('The IU is already in a buffer, cannot move it.') - self._buffer = buffer - self.owner_name = buffer.unique_name - self._payload.owner_name = buffer.unique_name - buffer = property( - fget=IUInterface._get_buffer, - fset=_set_buffer, - doc='Buffer this IU is held in.') - - def _set_uid(self, uid): - if self._uid is not None: - raise AttributeError('The uid of IU ' + self.uid + ' has already been set, cannot change it.') - self._uid = uid - uid = property( - fget=IUInterface._get_uid, - fset=_set_uid, - doc='Unique ID of the IU.') -#}}} - -class RemoteMessage(IUInterface):#{{{ - - """A remote IU with access mode 'MESSAGE'.""" - - def __init__(self, uid, revision, read_only, owner_name, category, payload_type, committed, payload, links): - super(RemoteMessage, self).__init__(uid=uid, access_mode=IUAccessMode.PUSH, read_only=read_only) - self._revision = revision - self._category = category - self.owner_name = owner_name - self._payload_type = payload_type - self._committed = committed - self._retracted = False - # NOTE Since the payload is an already-existant Payload which we didn't modify ourselves, - # don't try to invoke any modification checks or network updates ourselves either. - # We are just receiving it here and applying the new data. - self._payload = Payload(iu=self, new_payload=payload, omit_init_update_message=True) - self._links = links - - def _modify_links(self, is_delta=False, new_links={}, links_to_remove={}, writer_name=None): - logger.info('Info: modifying a RemoteMessage only has local effects') - - def _modify_payload(self, is_delta=True, new_items={}, keys_to_remove=[], writer_name=None): - logger.info('Info: modifying a RemoteMessage only has local effects') - - def commit(self): - logger.info('Info: committing to a RemoteMessage only has local effects') - - def _get_payload(self): - return self._payload - def _set_payload(self, new_pl): - logger.info('Info: modifying a RemoteMessage only has local effects') - self._payload = Payload(iu=self, new_payload=new_pl, omit_init_update_message=True) - payload = property( - fget=_get_payload, - fset=_set_payload, - doc='Payload dictionary of the IU.') - - def _apply_link_update(self, update): - """Apply a IULinkUpdate to the IU.""" - logger.warning('Warning: should never be called: RemoteMessage._apply_link_update') - self._revision = update.revision - if update.is_delta: - self._add_and_remove_links(add=update.new_links, remove=update.links_to_remove) - else: - self._replace_links(links=update.new_links) - - def _apply_update(self, update): - """Apply a IUPayloadUpdate to the IU.""" - logger.warning('Warning: should never be called: RemoteMessage._apply_update') - self._revision = update.revision - if update.is_delta: - for k in update.keys_to_remove: self.payload._remotely_enforced_delitem(k) - for k, v in update.new_items.items(): self.payload._remotely_enforced_setitem(k, v) - else: - # NOTE Please read the comment in the constructor - self._payload = Payload(iu=self, new_payload=update.new_items, omit_init_update_message=True) - - def _apply_commission(self): - """Apply commission to the IU""" - logger.warning('Warning: should never be called: RemoteMessage._apply_commission') - self._committed = True - - def _apply_retraction(self): - """Apply retraction to the IU""" - logger.warning('Warning: should never be called: RemoteMessage._apply_retraction') - self._retracted = True -#}}} - -class RemotePushIU(IUInterface):#{{{ - - """A remote IU with access mode 'PUSH'.""" - - def __init__(self, uid, revision, read_only, owner_name, category, payload_type, committed, payload, links): - super(RemotePushIU, self).__init__(uid=uid, access_mode=IUAccessMode.PUSH, read_only=read_only) - self._revision = revision - self._category = category - self.owner_name = owner_name - self._payload_type = payload_type - self._committed = committed - self._retracted = False - # NOTE Since the payload is an already-existant Payload which we didn't modify ourselves, - # don't try to invoke any modification checks or network updates ourselves either. - # We are just receiving it here and applying the new data. - self._payload = Payload(iu=self, new_payload=payload, omit_init_update_message=True) - self._links = links - - def _modify_links(self, is_delta=False, new_links={}, links_to_remove={}, writer_name=None): - """Modify the links: add or remove item from this payload remotely and send update.""" - if self.committed: - raise IUCommittedError(self) - if self.read_only: - raise IUReadOnlyError(self) - requested_update = IULinkUpdate( - uid=self.uid, - revision=self.revision, - is_delta=is_delta, - writer_name=self.buffer.unique_name, - new_links=new_links, - links_to_remove=links_to_remove) - remote_server = self.buffer._get_remote_server(self) - new_revision = remote_server.updateLinks(requested_update) - if new_revision == 0: - raise IUUpdateFailedError(self) - else: - self._revision = new_revision - - def _modify_payload(self, is_delta=True, new_items={}, keys_to_remove=[], writer_name=None): - """Modify the payload: add or remove item from this payload remotely and send update.""" - if self.committed: - raise IUCommittedError(self) - if self.read_only: - raise IUReadOnlyError(self) - requested_update = IUPayloadUpdate( - uid=self.uid, - revision=self.revision, - is_delta=is_delta, - writer_name=self.buffer.unique_name, - new_items=new_items, - keys_to_remove=keys_to_remove) - remote_server = self.buffer._get_remote_server(self) - new_revision = remote_server.updatePayload(requested_update) - if new_revision == 0: - raise IUUpdateFailedError(self) - else: - self._revision = new_revision - - def commit(self): - """Commit to this IU.""" - if self.read_only: - raise IUReadOnlyError(self) - if self._committed: - # ignore commit requests when already committed - return - else: - commission_request = ipaaca_pb2.IUCommission() - commission_request.uid = self.uid - commission_request.revision = self.revision - commission_request.writer_name = self.buffer.unique_name - remote_server = self.buffer._get_remote_server(self) - new_revision = remote_server.commit(commission_request) - if new_revision == 0: - raise IUUpdateFailedError(self) - else: - self._revision = new_revision - self._committed = True - - def _get_payload(self): - return self._payload - def _set_payload(self, new_pl): - if self.committed: - raise IUCommittedError(self) - if self.read_only: - raise IUReadOnlyError(self) - requested_update = IUPayloadUpdate( - uid=self.uid, - revision=self.revision, - is_delta=False, - writer_name=self.buffer.unique_name, - new_items=new_pl, - keys_to_remove=[]) - remote_server = self.buffer._get_remote_server(self) - new_revision = remote_server.updatePayload(requested_update) - if new_revision == 0: - raise IUUpdateFailedError(self) - else: - self._revision = new_revision - # NOTE Please read the comment in the constructor - self._payload = Payload(iu=self, new_payload=new_pl, omit_init_update_message=True) - payload = property( - fget=_get_payload, - fset=_set_payload, - doc='Payload dictionary of the IU.') - - def _apply_link_update(self, update): - """Apply a IULinkUpdate to the IU.""" - self._revision = update.revision - if update.is_delta: - self._add_and_remove_links(add=update.new_links, remove=update.links_to_remove) - else: - self._replace_links(links=update.new_links) - - def _apply_update(self, update): - """Apply a IUPayloadUpdate to the IU.""" - self._revision = update.revision - if update.is_delta: - for k in update.keys_to_remove: self.payload._remotely_enforced_delitem(k) - for k, v in update.new_items.items(): self.payload._remotely_enforced_setitem(k, v) - else: - # NOTE Please read the comment in the constructor - self._payload = Payload(iu=self, new_payload=update.new_items, omit_init_update_message=True) - - def _apply_commission(self): - """Apply commission to the IU""" - self._committed = True - - def _apply_retraction(self): - """Apply retraction to the IU""" - self._retracted = True -#}}} - - -class IntConverter(rsb.converter.Converter):#{{{ - """Convert Python int objects to Protobuf ints and vice versa.""" - def __init__(self, wireSchema="int", dataType=int): - super(IntConverter, self).__init__(bytearray, dataType, wireSchema) - - def serialize(self, value): - pbo = ipaaca_pb2.IntMessage() - pbo.value = value - return bytearray(pbo.SerializeToString()), self.wireSchema - - def deserialize(self, byte_stream, ws): - pbo = ipaaca_pb2.IntMessage() - pbo.ParseFromString( str(byte_stream) ) - return pbo.value -#}}} - - -class IUConverter(rsb.converter.Converter):#{{{ - ''' - Converter class for Full IU representations - wire:bytearray <-> wire-schema:ipaaca-full-iu <-> class ipaacaRSB.IU - ''' - def __init__(self, wireSchema="ipaaca-iu", dataType=IU): - super(IUConverter, self).__init__(bytearray, dataType, wireSchema) - - def serialize(self, iu): - pbo = ipaaca_pb2.IU() - pbo.uid = iu._uid - pbo.revision = iu._revision - pbo.category = iu._category - pbo.payload_type = iu._payload_type - pbo.owner_name = iu._owner_name - pbo.committed = iu._committed - am=ipaaca_pb2.IU.PUSH #default - if iu._access_mode == IUAccessMode.MESSAGE: - am=ipaaca_pb2.IU.MESSAGE - # TODO add other types later - pbo.access_mode = am - pbo.read_only = iu._read_only - for k,v in iu._payload.items(): - entry = pbo.payload.add() - pack_typed_payload_item(entry, k, v) - for type_ in iu._links.keys(): - linkset = pbo.links.add() - linkset.type = type_ - linkset.targets.extend(iu._links[type_]) - ws = "ipaaca-messageiu" if iu._access_mode == IUAccessMode.MESSAGE else self.wireSchema - return bytearray(pbo.SerializeToString()), ws - - def deserialize(self, byte_stream, ws): - type = self.getDataType() - #print('IUConverter.deserialize got a '+str(type)+' over wireSchema '+ws) - if type == IU or type == Message: - pbo = ipaaca_pb2.IU() - pbo.ParseFromString( str(byte_stream) ) - if pbo.access_mode == ipaaca_pb2.IU.PUSH: - _payload = {} - for entry in pbo.payload: - k, v = unpack_typed_payload_item(entry) - _payload[k] = v - _links = collections.defaultdict(set) - for linkset in pbo.links: - for target_uid in linkset.targets: - _links[linkset.type].add(target_uid) - remote_push_iu = RemotePushIU( - uid=pbo.uid, - revision=pbo.revision, - read_only = pbo.read_only, - owner_name = pbo.owner_name, - category = pbo.category, - payload_type = pbo.payload_type, - committed = pbo.committed, - payload=_payload, - links=_links - ) - return remote_push_iu - elif pbo.access_mode == ipaaca_pb2.IU.MESSAGE: - _payload = {} - for entry in pbo.payload: - k, v = unpack_typed_payload_item(entry) - _payload[k] = v - _links = collections.defaultdict(set) - for linkset in pbo.links: - for target_uid in linkset.targets: - _links[linkset.type].add(target_uid) - remote_message = RemoteMessage( - uid=pbo.uid, - revision=pbo.revision, - read_only = pbo.read_only, - owner_name = pbo.owner_name, - category = pbo.category, - payload_type = pbo.payload_type, - committed = pbo.committed, - payload=_payload, - links=_links - ) - return remote_message - else: - raise Exception("We can only handle IUs with access mode 'PUSH' or 'MESSAGE' for now!") - else: - raise ValueError("Inacceptable dataType %s" % type) -#}}} - -class MessageConverter(rsb.converter.Converter):#{{{ - ''' - Converter class for Full IU representations - wire:bytearray <-> wire-schema:ipaaca-full-iu <-> class ipaacaRSB.IU - ''' - def __init__(self, wireSchema="ipaaca-messageiu", dataType=Message): - super(IUConverter, self).__init__(bytearray, dataType, wireSchema) - - def serialize(self, iu): - pbo = ipaaca_pb2.IU() - pbo.uid = iu._uid - pbo.revision = iu._revision - pbo.category = iu._category - pbo.payload_type = iu._payload_type - pbo.owner_name = iu._owner_name - pbo.committed = iu._committed - am=ipaaca_pb2.IU.PUSH #default - if iu._access_mode == IUAccessMode.MESSAGE: - am=ipaaca_pb2.IU.MESSAGE - # TODO add other types later - pbo.access_mode = am - pbo.read_only = iu._read_only - for k,v in iu._payload.items(): - entry = pbo.payload.add() - pack_typed_payload_item(entry, k, v) - for type_ in iu._links.keys(): - linkset = pbo.links.add() - linkset.type = type_ - linkset.targets.extend(iu._links[type_]) - ws = "ipaaca-messageiu" if iu._access_mode == IUAccessMode.MESSAGE else self.wireSchema - return bytearray(pbo.SerializeToString()), ws - - def deserialize(self, byte_stream, ws): - type = self.getDataType() - #print('MessageConverter.deserialize got a '+str(type)+' over wireSchema '+ws) - if type == IU or type == Message: - pbo = ipaaca_pb2.IU() - pbo.ParseFromString( str(byte_stream) ) - if pbo.access_mode == ipaaca_pb2.IU.PUSH: - _payload = {} - for entry in pbo.payload: - k, v = unpack_typed_payload_item(entry) - _payload[k] = v - _links = collections.defaultdict(set) - for linkset in pbo.links: - for target_uid in linkset.targets: - _links[linkset.type].add(target_uid) - remote_push_iu = RemotePushIU( - uid=pbo.uid, - revision=pbo.revision, - read_only = pbo.read_only, - owner_name = pbo.owner_name, - category = pbo.category, - payload_type = pbo.payload_type, - committed = pbo.committed, - payload=_payload, - links=_links - ) - return remote_push_iu - elif pbo.access_mode == ipaaca_pb2.IU.MESSAGE: - _payload = {} - for entry in pbo.payload: - k, v = unpack_typed_payload_item(entry) - _payload[k] = v - _links = collections.defaultdict(set) - for linkset in pbo.links: - for target_uid in linkset.targets: - _links[linkset.type].add(target_uid) - remote_message = RemoteMessage( - uid=pbo.uid, - revision=pbo.revision, - read_only = pbo.read_only, - owner_name = pbo.owner_name, - category = pbo.category, - payload_type = pbo.payload_type, - committed = pbo.committed, - payload=_payload, - links=_links - ) - return remote_message - else: - raise Exception("We can only handle IUs with access mode 'PUSH' or 'MESSAGE' for now!") - else: - raise ValueError("Inacceptable dataType %s" % type) -#}}} - - -class IULinkUpdate(object):#{{{ - - def __init__(self, uid, revision, is_delta, writer_name="undef", new_links=None, links_to_remove=None): - super(IULinkUpdate, self).__init__() - self.uid = uid - self.revision = revision - self.writer_name = writer_name - self.is_delta = is_delta - self.new_links = collections.defaultdict(set) if new_links is None else collections.defaultdict(set, new_links) - self.links_to_remove = collections.defaultdict(set) if links_to_remove is None else collections.defaultdict(set, links_to_remove) - - def __str__(self): - s = 'LinkUpdate(' + 'uid=' + self.uid + ', ' - s += 'revision='+str(self.revision)+', ' - s += 'writer_name='+str(self.writer_name)+', ' - s += 'is_delta='+str(self.is_delta)+', ' - s += 'new_links = '+str(self.new_links)+', ' - s += 'links_to_remove = '+str(self.links_to_remove)+')' - return s -#}}} - -class IUPayloadUpdate(object):#{{{ - - def __init__(self, uid, revision, is_delta, writer_name="undef", new_items=None, keys_to_remove=None): - super(IUPayloadUpdate, self).__init__() - self.uid = uid - self.revision = revision - self.writer_name = writer_name - self.is_delta = is_delta - self.new_items = {} if new_items is None else new_items - self.keys_to_remove = [] if keys_to_remove is None else keys_to_remove - - def __str__(self): - s = 'PayloadUpdate(' + 'uid=' + self.uid + ', ' - s += 'revision='+str(self.revision)+', ' - s += 'writer_name='+str(self.writer_name)+', ' - s += 'is_delta='+str(self.is_delta)+', ' - s += 'new_items = '+str(self.new_items)+', ' - s += 'keys_to_remove = '+str(self.keys_to_remove)+')' - return s -#}}} - -class IULinkUpdateConverter(rsb.converter.Converter):#{{{ - def __init__(self, wireSchema="ipaaca-iu-link-update", dataType=IULinkUpdate): - super(IULinkUpdateConverter, self).__init__(bytearray, dataType, wireSchema) - - def serialize(self, iu_link_update): - pbo = ipaaca_pb2.IULinkUpdate() - pbo.uid = iu_link_update.uid - pbo.writer_name = iu_link_update.writer_name - pbo.revision = iu_link_update.revision - for type_ in iu_link_update.new_links.keys(): - linkset = pbo.new_links.add() - linkset.type = type_ - linkset.targets.extend(iu_link_update.new_links[type_]) - for type_ in iu_link_update.links_to_remove.keys(): - linkset = pbo.links_to_remove.add() - linkset.type = type_ - linkset.targets.extend(iu_link_update.links_to_remove[type_]) - pbo.is_delta = iu_link_update.is_delta - return bytearray(pbo.SerializeToString()), self.wireSchema - - def deserialize(self, byte_stream, ws): - type = self.getDataType() - if type == IULinkUpdate: - pbo = ipaaca_pb2.IULinkUpdate() - pbo.ParseFromString( str(byte_stream) ) - logger.debug('received an IULinkUpdate for revision '+str(pbo.revision)) - iu_link_up = IULinkUpdate( uid=pbo.uid, revision=pbo.revision, writer_name=pbo.writer_name, is_delta=pbo.is_delta) - for entry in pbo.new_links: - iu_link_up.new_links[str(entry.type)] = set(entry.targets) - for entry in pbo.links_to_remove: - iu_link_up.links_to_remove[str(entry.type)] = set(entry.targets) - return iu_link_up - else: - raise ValueError("Inacceptable dataType %s" % type) -#}}} - -class IUPayloadUpdateConverter(rsb.converter.Converter):#{{{ - def __init__(self, wireSchema="ipaaca-iu-payload-update", dataType=IUPayloadUpdate): - super(IUPayloadUpdateConverter, self).__init__(bytearray, dataType, wireSchema) - - def serialize(self, iu_payload_update): - pbo = ipaaca_pb2.IUPayloadUpdate() - pbo.uid = iu_payload_update.uid - pbo.writer_name = iu_payload_update.writer_name - pbo.revision = iu_payload_update.revision - for k,v in iu_payload_update.new_items.items(): - entry = pbo.new_items.add() - pack_typed_payload_item(entry, k, v) - pbo.keys_to_remove.extend(iu_payload_update.keys_to_remove) - pbo.is_delta = iu_payload_update.is_delta - return bytearray(pbo.SerializeToString()), self.wireSchema - - def deserialize(self, byte_stream, ws): - type = self.getDataType() - if type == IUPayloadUpdate: - pbo = ipaaca_pb2.IUPayloadUpdate() - pbo.ParseFromString( str(byte_stream) ) - logger.debug('received an IUPayloadUpdate for revision '+str(pbo.revision)) - iu_up = IUPayloadUpdate( uid=pbo.uid, revision=pbo.revision, writer_name=pbo.writer_name, is_delta=pbo.is_delta) - for entry in pbo.new_items: - k, v = unpack_typed_payload_item(entry) - iu_up.new_items[k] = v - iu_up.keys_to_remove = pbo.keys_to_remove[:] - return iu_up - else: - raise ValueError("Inacceptable dataType %s" % type) -#}}} - - -class IUStore(dict): - """A dictionary storing IUs.""" - def __init__(self): - super(IUStore, self).__init__() - -class FrozenIUStore(IUStore): - """A read-only version of a dictionary storing IUs. (TODO: might be slow)""" - def __init__(self, original_iu_store): - super(FrozenIUStore, self).__init__() - map(lambda p: super(FrozenIUStore, self).__setitem__(p[0], p[1]), original_iu_store.items()) - def __delitem__(self, k): - raise AttributeError() - def __setitem__(self, k, v): - raise AttributeError() - -class IUEventHandler(object): - - """Wrapper for IU event handling functions.""" - - def __init__(self, handler_function, for_event_types=None, for_categories=None): - """Create an IUEventHandler. - - Keyword arguments: - handler_function -- the handler function with the signature - (IU, event_type, local) - for_event_types -- a list of event types or None if handler should - be called for all event types - for_categories -- a list of category names or None if handler should - be called for all categoires - """ - super(IUEventHandler, self).__init__() - self._handler_function = handler_function - self._for_event_types = ( - None if for_event_types is None else - (for_event_types[:] if hasattr(for_event_types, '__iter__') else [for_event_types])) - self._for_categories = ( - None if for_categories is None else - (for_categories[:] if hasattr(for_categories, '__iter__') else [for_categories])) - - def condition_met(self, event_type, category): - """Check whether this IUEventHandler should be called. - - Keyword arguments: - event_type -- type of the IU event - category -- category of the IU which triggered the event - """ - type_condition_met = (self._for_event_types is None or event_type in self._for_event_types) - cat_condition_met = (self._for_categories is None or category in self._for_categories) - return type_condition_met and cat_condition_met - - def call(self, buffer, iu_uid, local, event_type, category): - """Call this IUEventHandler's function, if it applies. - - Keyword arguments: - buffer -- the buffer in which the IU is stored - iu_uid -- the uid of the IU - local -- is the IU local or remote to this component? @RAMIN: Is this correct? - event_type -- IU event type - category -- category of the IU - """ - if self.condition_met(event_type, category): - iu = buffer._iu_store[iu_uid] - self._handler_function(iu, event_type, local) - - -class Buffer(object): - - """Base class for InputBuffer and OutputBuffer.""" - - def __init__(self, owning_component_name, participant_config=None): - '''Create a Buffer. - - Keyword arguments: - owning_compontent_name -- name of the entity that owns this Buffer - participant_config -- RSB configuration - ''' - super(Buffer, self).__init__() - self._owning_component_name = owning_component_name - self._participant_config = rsb.ParticipantConfig.fromDefaultSources() if participant_config is None else participant_config - self._uuid = str(uuid.uuid4())[0:8] - # Initialise with a temporary, but already unique, name - self._unique_name = "undef-"+self._uuid - self._iu_store = IUStore() - self._iu_event_handlers = [] - - def _get_frozen_iu_store(self): - return FrozenIUStore(original_iu_store = self._iu_store) - iu_store = property(fget=_get_frozen_iu_store, doc='Copy-on-read version of the internal IU store') - - def register_handler(self, handler_function, for_event_types=None, for_categories=None): - """Register a new IU event handler function. - - Keyword arguments: - handler_function -- a function with the signature (IU, event_type, local) - for_event_types -- a list of event types or None if handler should - be called for all event types - for_categories -- a list of category names or None if handler should - be called for all categories - - """ - handler = IUEventHandler(handler_function=handler_function, for_event_types=for_event_types, for_categories=for_categories) - self._iu_event_handlers.append(handler) - - def call_iu_event_handlers(self, uid, local, event_type, category): - """Call registered IU event handler functions registered for this event_type and category.""" - for h in self._iu_event_handlers: - h.call(self, uid, local=local, event_type=event_type, category=category) - - def _get_owning_component_name(self): - """Return the name of this Buffer's owning component""" - return self._owning_component_name - owning_component_name = property(_get_owning_component_name) - - def _get_unique_name(self): - """Return the Buffer's unique name.""" - return self._unique_name - unique_name = property(_get_unique_name) - - - -class InputBuffer(Buffer): - - """An InputBuffer that holds remote IUs.""" - - def __init__(self, owning_component_name, category_interests=None, participant_config=None): - '''Create an InputBuffer. - - Keyword arguments: - owning_compontent_name -- name of the entity that owns this InputBuffer - category_interests -- list of IU categories this Buffer is interested in - participant_config = RSB configuration - ''' - super(InputBuffer, self).__init__(owning_component_name, participant_config) - self._unique_name = '/ipaaca/component/'+str(owning_component_name)+'ID'+self._uuid+'/IB' - self._listener_store = {} # one per IU category - self._remote_server_store = {} # one per remote-IU-owning Component - self._category_interests = [] - if category_interests is not None: - for cat in category_interests: - self._add_category_listener(cat) - - def _get_remote_server(self, iu): - '''Return (or create, store and return) a remote server.''' - if iu.owner_name in self._remote_server_store: - return self._remote_server_store[iu.owner_name] - # TODO remove the str() when unicode is supported (issue #490) - remote_server = rsb.createRemoteServer(rsb.Scope(str(iu.owner_name))) - self._remote_server_store[iu.owner_name] = remote_server - return remote_server - - def _add_category_listener(self, iu_category): - '''Return (or create, store and return) a category listener.''' - if iu_category not in self._listener_store: - cat_listener = rsb.createListener(rsb.Scope("/ipaaca/category/"+str(iu_category)), config=self._participant_config) - cat_listener.addHandler(self._handle_iu_events) - self._listener_store[iu_category] = cat_listener - self._category_interests.append(iu_category) - logger.info("Added listener in scope "+"/ipaaca/category/"+iu_category) - - def _handle_iu_events(self, event): - '''Dispatch incoming IU events. - - Adds incoming IU's to the store, applies payload and commit updates to - IU, calls IU event handlers.' - - Keyword arguments: - event -- a converted RSB event - ''' - type_ = type(event.data) - if type_ is RemotePushIU: - # a new IU - if event.data.uid in self._iu_store: - # already in our store - pass - else: - self._iu_store[ event.data.uid ] = event.data - event.data.buffer = self - self.call_iu_event_handlers(event.data.uid, local=False, event_type=IUEventType.ADDED, category=event.data.category) - elif type_ is RemoteMessage: - # a new Message, an ephemeral IU that is removed after calling handlers - self._iu_store[ event.data.uid ] = event.data - event.data.buffer = self - self.call_iu_event_handlers(event.data.uid, local=False, event_type=IUEventType.MESSAGE, category=event.data.category) - del self._iu_store[ event.data.uid ] - else: - # an update to an existing IU - if event.data.uid not in self._iu_store: - # TODO: we should request the IU's owner to send us the IU - logger.warning("Update message for IU which we did not fully receive before.") - return - if type_ is ipaaca_pb2.IURetraction: - # IU retraction (cannot be triggered remotely) - iu = self._iu_store[event.data.uid] - iu._revision = event.data.revision - iu._apply_retraction() # for now - just sets the _rectracted flag. - self.call_iu_event_handlers(event.data.uid, local=False, event_type=IUEventType.RETRACTED, category=iu.category) - # SPECIAL CASE: allow the handlers (which will need to find the IU - # in the buffer) to operate on the IU - then delete it afterwards! - # FIXME: for now: retracted == deleted! Think about this later - del(self._iu_store[iu.uid]) - else: - if event.data.writer_name == self.unique_name: - # Notify only for remotely triggered events; - # Discard updates that originate from this buffer - return - #else: - # print('Got update written by buffer '+str(event.data.writer_name)) - - if type_ is ipaaca_pb2.IUCommission: - # IU commit - iu = self._iu_store[event.data.uid] - iu._apply_commission() - iu._revision = event.data.revision - self.call_iu_event_handlers(event.data.uid, local=False, event_type=IUEventType.COMMITTED, category=iu.category) - elif type_ is IUPayloadUpdate: - # IU payload update - iu = self._iu_store[event.data.uid] - iu._apply_update(event.data) - self.call_iu_event_handlers(event.data.uid, local=False, event_type=IUEventType.UPDATED, category=iu.category) - elif type_ is IULinkUpdate: - # IU link update - iu = self._iu_store[event.data.uid] - iu._apply_link_update(event.data) - self.call_iu_event_handlers(event.data.uid, local=False, event_type=IUEventType.LINKSUPDATED, category=iu.category) - else: - logger.warning('Warning: _handle_iu_events failed to handle an object of type '+str(type_)) - - def add_category_interests(self, category_interests): - for interest in category_interests: - self._add_category_listener(interest) - - -class OutputBuffer(Buffer): - - """An OutputBuffer that holds local IUs.""" - - def __init__(self, owning_component_name, participant_config=None): - '''Create an Output Buffer. - - Keyword arguments: - owning_component_name -- name of the entity that own this buffer - participant_config -- RSB configuration - ''' - super(OutputBuffer, self).__init__(owning_component_name, participant_config) - self._unique_name = '/ipaaca/component/' + str(owning_component_name) + 'ID' + self._uuid + '/OB' - self._server = rsb.createServer(rsb.Scope(self._unique_name)) - self._server.addMethod('updateLinks', self._remote_update_links, IULinkUpdate, int) - self._server.addMethod('updatePayload', self._remote_update_payload, IUPayloadUpdate, int) - self._server.addMethod('commit', self._remote_commit, ipaaca_pb2.IUCommission, int) - self._informer_store = {} - self._id_prefix = str(owning_component_name)+'-'+str(self._uuid)+'-IU-' - self.__iu_id_counter_lock = threading.Lock() - #self.__iu_id_counter = 0 # hbuschme: IUs now have their Ids assigned on creation - - def _create_own_name_listener(self, iu_category): - # FIXME replace this - '''Create an own name listener.''' - #if iu_category in self._listener_store: return self._informer_store[iu_category] - #cat_listener = rsb.createListener(rsb.Scope("/ipaaca/category/"+str(iu_category)), config=self._participant_config) - #cat_listener.addHandler(self._handle_iu_events) - #self._listener_store[iu_category] = cat_listener - #self._category_interests.append(iu_category) - #logger.info("Added category listener for "+iu_category) - #return cat_listener - pass - - # hbuschme: IUs now have their Ids assigned on creation - #def _generate_iu_uid(self): - # '''Generate a unique IU id of the form ????''' - # with self.__iu_id_counter_lock: - # self.__iu_id_counter += 1 - # number = self.__iu_id_counter - # return self._id_prefix + str(number) - - def _remote_update_links(self, update): - '''Apply a remotely requested update to one of the stored IU's links.''' - if update.uid not in self._iu_store: - logger.warning("Remote InBuffer tried to spuriously write non-existent IU "+str(update.uid)) - return 0 - iu = self._iu_store[update.uid] - with iu.revision_lock: - if (update.revision != 0) and (update.revision != iu.revision): - # (0 means "do not pay attention to the revision number" -> "force update") - logger.warning("Remote write operation failed because request was out of date; IU "+str(update.uid)) - return 0 - if update.is_delta: - iu.modify_links(add=update.new_links, remove=update.links_to_remove, writer_name=update.writer_name) - else: - iu.set_links(links=update.new_links, writer_name=update.writer_name) - self.call_iu_event_handlers(update.uid, local=True, event_type=IUEventType.LINKSUPDATED, category=iu.category) - return iu.revision - - def _remote_update_payload(self, update): - '''Apply a remotely requested update to one of the stored IU's payload.''' - if update.uid not in self._iu_store: - logger.warning("Remote InBuffer tried to spuriously write non-existent IU "+str(update.uid)) - return 0 - iu = self._iu_store[update.uid] - with iu.revision_lock: - if (update.revision != 0) and (update.revision != iu.revision): - # (0 means "do not pay attention to the revision number" -> "force update") - logger.warning(u"Remote update_payload operation failed because request was out of date; IU "+str(update.uid)) - logger.warning(u" Writer was: "+update.writer_name) - logger.warning(u" Requested update was: (New keys:) "+','.join(update.new_items.keys())+' (Removed keys:) '+','.join(update.keys_to_remove)) - logger.warning(u" Referred-to revision was "+str(update.revision)+' while local revision is '+str(iu.revision)) - return 0 - if update.is_delta: - #print('Writing delta update by '+str(update.writer_name)) - with iu.payload: - for k in update.keys_to_remove: - iu.payload.__delitem__(k, writer_name=update.writer_name) - for k,v in update.new_items.items(): - iu.payload.__setitem__(k, v, writer_name=update.writer_name) - else: - #print('Writing non-incr update by '+str(update.writer_name)) - iu._set_payload(update.new_items, writer_name=update.writer_name) - # _set_payload etc. have also incremented the revision number - self.call_iu_event_handlers(update.uid, local=True, event_type=IUEventType.UPDATED, category=iu.category) - return iu.revision - - def _remote_commit(self, iu_commission): - '''Apply a remotely requested commit to one of the stored IUs.''' - if iu_commission.uid not in self._iu_store: - logger.warning("Remote InBuffer tried to spuriously write non-existent IU "+str(iu_commission.uid)) - return 0 - iu = self._iu_store[iu_commission.uid] - with iu.revision_lock: - if (iu_commission.revision != 0) and (iu_commission.revision != iu.revision): - # (0 means "do not pay attention to the revision number" -> "force update") - logger.warning("Remote write operation failed because request was out of date; IU "+str(iu_commission.uid)) - return 0 - if iu.committed: - return 0 - else: - iu._internal_commit(writer_name=iu_commission.writer_name) - self.call_iu_event_handlers(iu_commission.uid, local=True, event_type=IUEventType.COMMITTED, category=iu.category) - return iu.revision - - def _get_informer(self, iu_category): - '''Return (or create, store and return) an informer object for IUs of the specified category.''' - if iu_category in self._informer_store: - logger.info("Returning informer on scope "+"/ipaaca/category/"+str(iu_category)) - return self._informer_store[iu_category] - informer_iu = rsb.createInformer( - rsb.Scope("/ipaaca/category/"+str(iu_category)), - config=self._participant_config, - dataType=object) - self._informer_store[iu_category] = informer_iu #new_tuple - logger.info("Returning NEW informer on scope "+"/ipaaca/category/"+str(iu_category)) - return informer_iu #return new_tuple - - def add(self, iu): - '''Add an IU to the IU store, assign an ID and publish it.''' - # hbuschme: IUs now have their Ids assigned on creation - #if iu._uid is not None: - # raise IUPublishedError(iu) - #iu.uid = self._generate_iu_uid() - if iu.uid in self._iu_store: - raise IUPublishedError(iu) - if iu.buffer is not None: - raise IUPublishedError(iu) - if iu.access_mode != IUAccessMode.MESSAGE: - # Messages are not really stored in the OutputBuffer - self._iu_store[iu.uid] = iu - iu.buffer = self - self._publish_iu(iu) - - def remove(self, iu=None, iu_uid=None): - '''Remove the iu or an IU corresponding to iu_uid from the OutputBuffer, retracting it from the system.''' - if iu is None: - if iu_uid is None: - return None - else: - if iu_uid not in self. _iu_store: - raise IUNotFoundError(iu_uid) - iu = self._iu_store[iu_uid] - # unpublish the IU - self._retract_iu(iu) - del self._iu_store[iu.uid] - return iu - - def _publish_iu(self, iu): - '''Publish an IU.''' - informer = self._get_informer(iu._category) - informer.publishData(iu) - - def _retract_iu(self, iu): - '''Retract (unpublish) an IU.''' - iu_retraction = ipaaca_pb2.IURetraction() - iu_retraction.uid = iu.uid - iu_retraction.revision = iu.revision - informer = self._get_informer(iu._category) - informer.publishData(iu_retraction) - - def _send_iu_commission(self, iu, writer_name): - '''Send IU commission. - - Keyword arguments: - iu -- the IU that has been committed to - writer_name -- name of the Buffer that initiated this commit, necessary - to enable remote components to filter out updates that originated - from their own operations - ''' - # a raw Protobuf object for IUCommission is produced - # (unlike updates, where we have an intermediate class) - iu_commission = ipaaca_pb2.IUCommission() - iu_commission.uid = iu.uid - iu_commission.revision = iu.revision - iu_commission.writer_name = iu.owner_name if writer_name is None else writer_name - informer = self._get_informer(iu._category) - informer.publishData(iu_commission) - - def _send_iu_link_update(self, iu, is_delta, revision, new_links=None, links_to_remove=None, writer_name="undef"): - '''Send an IU link update. - - Keyword arguments: - iu -- the IU being updated - is_delta -- whether this is an incremental update or a replacement - the whole link dictionary - revision -- the new revision number - new_links -- a dictionary of new link sets - links_to_remove -- a dict of the link sets that shall be removed - writer_name -- name of the Buffer that initiated this update, necessary - to enable remote components to filter out updates that originate d - from their own operations - ''' - if new_links is None: - new_links = {} - if links_to_remove is None: - links_to_remove = {} - link_update = IULinkUpdate(iu._uid, is_delta=is_delta, revision=revision) - link_update.new_links = new_links - if is_delta: - link_update.links_to_remove = links_to_remove - link_update.writer_name = writer_name - informer = self._get_informer(iu._category) - informer.publishData(link_update) - # FIXME send the notification to the target, if the target is not the writer_name - - def _send_iu_payload_update(self, iu, is_delta, revision, new_items=None, keys_to_remove=None, writer_name="undef"): - '''Send an IU payload update. - - Keyword arguments: - iu -- the IU being updated - is_delta -- whether this is an incremental update or a replacement - revision -- the new revision number - new_items -- a dictionary of new payload items - keys_to_remove -- a list of the keys that shall be removed from the - payload - writer_name -- name of the Buffer that initiated this update, necessary - to enable remote components to filter out updates that originate d - from their own operations - ''' - if new_items is None: - new_items = {} - if keys_to_remove is None: - keys_to_remove = [] - payload_update = IUPayloadUpdate(iu._uid, is_delta=is_delta, revision=revision) - payload_update.new_items = new_items - if is_delta: - payload_update.keys_to_remove = keys_to_remove - payload_update.writer_name = writer_name - informer = self._get_informer(iu._category) - informer.publishData(payload_update) - #print(" -- Sent update with writer name "+str(writer_name)) - - -## --- RSB ------------------------------------------------------------------- - - -def initialize_ipaaca_rsb():#{{{ +def initialize_ipaaca_rsb(): + ''''Register own RSB Converters and initialise RSB from default config file.''' rsb.converter.registerGlobalConverter( - IntConverter(wireSchema="int32", dataType=int)) + ipaaca.converter.IntConverter( + wireSchema="int32", + dataType=int)) + rsb.converter.registerGlobalConverter( - IUConverter(wireSchema="ipaaca-iu", dataType=IU)) + ipaaca.converter.IUConverter( + wireSchema="ipaaca-iu", + dataType=IU)) + rsb.converter.registerGlobalConverter( - IUConverter(wireSchema="ipaaca-messageiu", dataType=Message)) + ipaaca.converter.MessageConverter( + wireSchema="ipaaca-messageiu", + dataType=Message)) + rsb.converter.registerGlobalConverter( - IULinkUpdateConverter( + ipaaca.converter.IULinkUpdateConverter( wireSchema="ipaaca-iu-link-update", - dataType=IULinkUpdate)) + dataType=converter.IULinkUpdate)) + rsb.converter.registerGlobalConverter( - IUPayloadUpdateConverter( + ipaaca.converter.IUPayloadUpdateConverter( wireSchema="ipaaca-iu-payload-update", - dataType=IUPayloadUpdate)) + dataType=converter.IUPayloadUpdate)) + rsb.converter.registerGlobalConverter( rsb.converter.ProtocolBufferConverter( messageClass=ipaaca_pb2.IUCommission)) + rsb.converter.registerGlobalConverter( rsb.converter.ProtocolBufferConverter( - messageClass=ipaaca_pb2.IURetraction)) - rsb.__defaultParticipantConfig = rsb.ParticipantConfig.fromDefaultSources() - #t = rsb.ParticipantConfig.Transport('spread', {'enabled':'true'}) - #rsb.__defaultParticipantConfig = rsb.ParticipantConfig.fromFile('rsb.cfg') -#}}} + messageClass=ipaaca_pb2.IUResendRequest)) + rsb.converter.registerGlobalConverter( + rsb.converter.ProtocolBufferConverter( + messageClass=ipaaca_pb2.IURetraction)) -## --- Module initialisation ------------------------------------------------- + rsb.__defaultParticipantConfig = rsb.ParticipantConfig.fromDefaultSources() -# register our own RSB Converters +# Initialise module initialize_ipaaca_rsb() - -# Create a global logger for this module -logger = logging.getLogger('ipaaca') -logger.addHandler(IpaacaLoggingHandler(level=logging.INFO)) diff --git a/ipaacalib/python/src/ipaaca/buffer.py b/ipaacalib/python/src/ipaaca/buffer.py new file mode 100644 index 0000000000000000000000000000000000000000..c4b75a4450cbd1301717caf94fa36dfb46e8b2f1 --- /dev/null +++ b/ipaacalib/python/src/ipaaca/buffer.py @@ -0,0 +1,588 @@ +# -*- coding: utf-8 -*- + +# This file is part of IPAACA, the +# "Incremental Processing Architecture +# for Artificial Conversational Agents". +# +# Copyright (c) 2009-2014 Social Cognitive Systems Group +# CITEC, Bielefeld University +# +# http://opensource.cit-ec.de/projects/ipaaca/ +# http://purl.org/net/ipaaca +# +# This file may be licensed under the terms of of the +# GNU Lesser General Public License Version 3 (the ``LGPL''), +# or (at your option) any later version. +# +# Software distributed under the License is distributed +# on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either +# express or implied. See the LGPL for the specific language +# governing rights and limitations. +# +# You should have received a copy of the LGPL along with this +# program. If not, go to http://www.gnu.org/licenses/lgpl.html +# or write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The development of this software was supported by the +# Excellence Cluster EXC 277 Cognitive Interaction Technology. +# The Excellence Cluster EXC 277 is a grant of the Deutsche +# Forschungsgemeinschaft (DFG) in the context of the German +# Excellence Initiative. + +from __future__ import division, print_function + + +import threading +import uuid +import traceback + +import rsb + +import ipaaca_pb2 +import ipaaca.defaults +import ipaaca.exception +import ipaaca.converter +import ipaaca.iu + + + +__all__ = [ + 'InputBuffer', + 'OutputBuffer', +] + +LOGGER = ipaaca.misc.get_library_logger() + +class IUStore(dict): + """A dictionary storing IUs.""" + def __init__(self): + super(IUStore, self).__init__() + + +class FrozenIUStore(IUStore): + """A read-only version of a dictionary storing IUs. (TODO: might be slow)""" + def __init__(self, original_iu_store): + super(FrozenIUStore, self).__init__() + map(lambda p: super(FrozenIUStore, self).__setitem__(p[0], p[1]), original_iu_store.items()) + def __delitem__(self, k): + raise AttributeError() + def __setitem__(self, k, v): + raise AttributeError() + + +class IUEventHandler(object): + + """Wrapper for IU event handling functions.""" + + def __init__(self, handler_function, for_event_types=None, for_categories=None): + """Create an IUEventHandler. + + Keyword arguments: + handler_function -- the handler function with the signature + (IU, event_type, local) + for_event_types -- a list of event types or None if handler should + be called for all event types + for_categories -- a list of category names or None if handler should + be called for all categoires + """ + super(IUEventHandler, self).__init__() + self._handler_function = handler_function + self._for_event_types = ( + None if for_event_types is None else + (for_event_types[:] if hasattr(for_event_types, '__iter__') else [for_event_types])) + self._for_categories = ( + None if for_categories is None else + (for_categories[:] if hasattr(for_categories, '__iter__') else [for_categories])) + + def condition_met(self, event_type, category): + """Check whether this IUEventHandler should be called. + + Keyword arguments: + event_type -- type of the IU event + category -- category of the IU which triggered the event + """ + type_condition_met = (self._for_event_types is None or event_type in self._for_event_types) + cat_condition_met = (self._for_categories is None or category in self._for_categories) + return type_condition_met and cat_condition_met + + def call(self, buffer, iu_uid, local, event_type, category): + """Call this IUEventHandler's function, if it applies. + + Keyword arguments: + buffer -- the buffer in which the IU is stored + iu_uid -- the uid of the IU + local -- is the IU local or remote to this component? @RAMIN: Is this correct? + event_type -- IU event type + category -- category of the IU + """ + if self.condition_met(event_type, category): + iu = buffer._iu_store[iu_uid] + self._handler_function(iu, event_type, local) + + +class Buffer(object): + + """Base class for InputBuffer and OutputBuffer.""" + + def __init__(self, owning_component_name, channel=None, participant_config=None): + '''Create a Buffer. + + Keyword arguments: + owning_compontent_name -- name of the entity that owns this Buffer + participant_config -- RSB configuration + ''' + super(Buffer, self).__init__() + self._owning_component_name = owning_component_name + self._channel = channel if channel is not None else ipaaca.defaults.IPAACA_DEFAULT_CHANNEL + self._participant_config = rsb.ParticipantConfig.fromDefaultSources() if participant_config is None else participant_config + self._uuid = str(uuid.uuid4())[0:8] + # Initialise with a temporary, but already unique, name + self._unique_name = "undef-"+self._uuid + self._iu_store = IUStore() + self._iu_event_handlers = [] + + def _get_frozen_iu_store(self): + return FrozenIUStore(original_iu_store = self._iu_store) + iu_store = property(fget=_get_frozen_iu_store, doc='Copy-on-read version of the internal IU store') + + def register_handler(self, handler_function, for_event_types=None, for_categories=None): + """Register a new IU event handler function. + + Keyword arguments: + handler_function -- a function with the signature (IU, event_type, local) + for_event_types -- a list of event types or None if handler should + be called for all event types + for_categories -- a list of category names or None if handler should + be called for all categories + """ + if handler_function in [h._handler_function for h in self._iu_event_handlers]: + LOGGER.warn("The handler function '" + handler_function.__name__ + '" has been registered before.') + handler = IUEventHandler(handler_function=handler_function, for_event_types=for_event_types, for_categories=for_categories) + self._iu_event_handlers.append(handler) + return handler + + def call_iu_event_handlers(self, uid, local, event_type, category): + """Call registered IU event handler functions registered for this event_type and category.""" + for h in self._iu_event_handlers: + try: + h.call(self, uid, local=local, event_type=event_type, category=category) + except Exception as e: + if local: + LOGGER.error('Local IU handler raised an exception upon remote write.' + unicode(e)) + else: + print(unicode(traceback.format_exc())) + raise e + + def _get_owning_component_name(self): + """Return the name of this Buffer's owning component""" + return self._owning_component_name + owning_component_name = property(_get_owning_component_name) + + def _get_unique_name(self): + """Return the Buffer's unique name.""" + return self._unique_name + unique_name = property(_get_unique_name) + + +class InputBuffer(Buffer): + + """An InputBuffer that holds remote IUs.""" + + def __init__(self, owning_component_name, category_interests=None, channel=None, participant_config=None, resend_active=False): + '''Create an InputBuffer. + + Keyword arguments: + owning_compontent_name -- name of the entity that owns this InputBuffer + category_interests -- list of IU categories this Buffer is interested in + participant_config = RSB configuration + ''' + super(InputBuffer, self).__init__(owning_component_name, channel, participant_config) + self._unique_name = '/ipaaca/component/'+str(owning_component_name)+'ID'+self._uuid+'/IB' + self._resend_active = resend_active + self._listener_store = {} # one per IU category + self._remote_server_store = {} # one per remote-IU-owning Component + self._category_interests = [] + # add own uuid as identifier for hidden category. + self._add_category_listener(str(self._uuid)) + if category_interests is not None: + self.add_category_interests(category_interests) + + def _get_remote_server(self, iu): + '''Return (or create, store and return) a remote server.''' + _owner = None + if hasattr(iu,'owner_name'): + _owner = iu.owner_name + elif hasattr(iu,'writer_name'): + _owner = iu.writer_name + if _owner is not None: + if _owner in self._remote_server_store: + return self._remote_server_store[_owner] + # TODO remove the str() when unicode is supported (issue #490) + remote_server = rsb.createRemoteServer(rsb.Scope(str(_owner))) + self._remote_server_store[_owner] = remote_server + return remote_server + + def _add_category_listener(self, iu_category): + '''Create and store a listener on a specific category.''' + if iu_category not in self._listener_store: + cat_listener = rsb.createListener(rsb.Scope("/ipaaca/channel/"+str(self._channel)+"/category/"+str(iu_category)), config=self._participant_config) + cat_listener.addHandler(self._handle_iu_events) + self._listener_store[iu_category] = cat_listener + self._category_interests.append(iu_category) + LOGGER.info("Added listener in scope /ipaaca/channel/" + str(self._channel) + "/category/" + iu_category) + + def _remove_category_listener(self, iu_category): + '''Remove the listener for a specific category.''' + if iu_category in self._listener_store and iu_category in self._category_interests: + del self._listener_store[iu_category] + self._category_interests.remove(iu_category) + LOGGER.info("Removed listener in scope /ipaaca/channel/" + str(self._channel) + "/category/ " + iu_category) + + def _handle_iu_events(self, event): + '''Dispatch incoming IU events. + + Adds incoming IU's to the store, applies payload and commit updates to + IU, calls IU event handlers.' + + Keyword arguments: + event -- a converted RSB event + ''' + type_ = type(event.data) + if type_ is ipaaca.iu.RemotePushIU: + # a new IU + if event.data.uid not in self._iu_store: + self._iu_store[event.data.uid] = event.data + event.data.buffer = self + self.call_iu_event_handlers(event.data.uid, local=False, event_type=ipaaca.iu.IUEventType.ADDED, category=event.data.category) + else: + # IU already in our store, overwrite local IU, but do not call + # event handler. This functionality is necessary to undo + # destructive changes after a failing remote updates (undo is + # done via the resend request mechanism). + self._iu_store[event.data.uid] = event.data + event.data.buffer = self + elif type_ is ipaaca.iu.RemoteMessage: + # a new Message, an ephemeral IU that is removed after calling handlers + self._iu_store[ event.data.uid ] = event.data + event.data.buffer = self + self.call_iu_event_handlers(event.data.uid, local=False, event_type=ipaaca.iu.IUEventType.MESSAGE, category=event.data.category) + del self._iu_store[ event.data.uid ] + else: + if event.data.uid not in self._iu_store: + if self._resend_active: + # send resend request to remote server + self._request_remote_resend(event.data) + else: + LOGGER.warning("Received an update for an IU which we did not receive before.") + return + # an update to an existing IU + if type_ is ipaaca_pb2.IURetraction: + # IU retraction (cannot be triggered remotely) + iu = self._iu_store[event.data.uid] + iu._revision = event.data.revision + iu._apply_retraction() # for now - just sets the _rectracted flag. + self.call_iu_event_handlers(event.data.uid, local=False, event_type=ipaaca.iu.IUEventType.RETRACTED, category=iu.category) + # SPECIAL CASE: allow the handlers (which will need to find the IU + # in the buffer) to operate on the IU - then delete it afterwards! + # FIXME: for now: retracted == deleted! Think about this later + del(self._iu_store[iu.uid]) + else: + if event.data.writer_name == self.unique_name: + # Notify only for remotely triggered events; + # Discard updates that originate from this buffer + return + if type_ is ipaaca_pb2.IUCommission: + # IU commit + iu = self._iu_store[event.data.uid] + iu._apply_commission() + iu._revision = event.data.revision + self.call_iu_event_handlers(event.data.uid, local=False, event_type=ipaaca.iu.IUEventType.COMMITTED, category=iu.category) + elif type_ is ipaaca.converter.IUPayloadUpdate: + # IU payload update + iu = self._iu_store[event.data.uid] + iu._apply_update(event.data) + self.call_iu_event_handlers(event.data.uid, local=False, event_type=ipaaca.iu.IUEventType.UPDATED, category=iu.category) + elif type_ is ipaaca.converter.IULinkUpdate: + # IU link update + iu = self._iu_store[event.data.uid] + iu._apply_link_update(event.data) + self.call_iu_event_handlers(event.data.uid, local=False, event_type=ipaaca.iu.IUEventType.LINKSUPDATED, category=iu.category) + else: + LOGGER.warning('Warning: _handle_iu_events failed to handle an object of type '+str(type_)) + + def add_category_interests(self, category_interests): + if hasattr(category_interests, '__iter__'): + for interest in category_interests: + self._add_category_listener(interest) + else: + self._add_category_listener(category_interests) + + def remove_category_interests(self, category_interests): + if hasattr(category_interests, '__iter__'): + for interest in category_interests: + self._remove_category_listener(interest) + else: + self._remove_category_listener(category_interests) + + def _request_remote_resend(self, iu): + remote_server = self._get_remote_server(iu) + resend_request = ipaaca_pb2.IUResendRequest() + resend_request.uid = iu.uid # target iu + resend_request.hidden_scope_name = str(self._uuid) # hidden category name + remote_revision = remote_server.requestResend(resend_request) + if remote_revision == 0: + raise ipaaca.exception.IUResendRequestFailedError() + + def register_handler(self, handler_function, for_event_types=None, for_categories=None): + """Register a new IU event handler function. + + Keyword arguments: + handler_function -- a function with the signature (IU, event_type, local) + for_event_types -- a list of event types or None if handler should + be called for all event types + for_categories -- a list of category names or None if handler should + be called for all categories + """ + handler = super(InputBuffer, self).register_handler(handler_function, for_event_types, for_categories) + try: + for category in handler._for_categories: + self.add_category_interests(category) + except TypeError: + # i.e., None was provided to the handler + pass + return handler + + def is_resend_active(self): + return self._resend_active + + def set_resend_active(self, active=True): + self._resend_active = active + + +class OutputBuffer(Buffer): + + """An OutputBuffer that holds local IUs.""" + + def __init__(self, owning_component_name, channel=None, participant_config=None): + '''Create an Output Buffer. + + Keyword arguments: + owning_component_name -- name of the entity that own this buffer + participant_config -- RSB configuration + ''' + super(OutputBuffer, self).__init__(owning_component_name, channel, participant_config) + self._unique_name = '/ipaaca/component/' + str(owning_component_name) + 'ID' + self._uuid + '/OB' + self._server = rsb.createLocalServer(rsb.Scope(self._unique_name)) + self._server.addMethod('updateLinks', self._remote_update_links, ipaaca.converter.IULinkUpdate, int) + self._server.addMethod('updatePayload', self._remote_update_payload, ipaaca.converter.IUPayloadUpdate, int) + self._server.addMethod('commit', self._remote_commit, ipaaca_pb2.IUCommission, int) + self._server.addMethod('requestResend', self._remote_request_resend, ipaaca_pb2.IUResendRequest, int) + self._informer_store = {} + self._id_prefix = str(owning_component_name)+'-'+str(self._uuid)+'-IU-' + self.__iu_id_counter_lock = threading.Lock() + + def _remote_update_links(self, update): + '''Apply a remotely requested update to one of the stored IU's links.''' + if update.uid not in self._iu_store: + LOGGER.warning("Remote InBuffer tried to spuriously write non-existent IU "+str(update.uid)) + return 0 + iu = self._iu_store[update.uid] + with iu.revision_lock: + if (update.revision != 0) and (update.revision != iu.revision): + # (0 means "do not pay attention to the revision number" -> "force update") + LOGGER.warning("Remote write operation failed because request was out of date; IU "+str(update.uid)) + return 0 + if update.is_delta: + iu.modify_links(add=update.new_links, remove=update.links_to_remove, writer_name=update.writer_name) + else: + iu.set_links(links=update.new_links, writer_name=update.writer_name) + self.call_iu_event_handlers(update.uid, local=True, event_type=ipaaca.iu.IUEventType.LINKSUPDATED, category=iu.category) + return iu.revision + + def _remote_update_payload(self, update): + '''Apply a remotely requested update to one of the stored IU's payload.''' + if update.uid not in self._iu_store: + LOGGER.warning("Remote InBuffer tried to spuriously write non-existent IU "+str(update.uid)) + return 0 + iu = self._iu_store[update.uid] + with iu.revision_lock: + if (update.revision != 0) and (update.revision != iu.revision): + # (0 means "do not pay attention to the revision number" -> "force update") + LOGGER.warning(u"Remote update_payload operation failed because request was out of date; IU "+str(update.uid)) + LOGGER.warning(u" Writer was: "+update.writer_name) + LOGGER.warning(u" Requested update was: (New keys:) "+','.join(update.new_items.keys())+' (Removed keys:) '+','.join(update.keys_to_remove)) + LOGGER.warning(u" Referred-to revision was "+str(update.revision)+' while local revision is '+str(iu.revision)) + return 0 + if update.is_delta: + #print('Writing delta update by '+str(update.writer_name)) + with iu.payload: + for k in update.keys_to_remove: + iu.payload.__delitem__(k, writer_name=update.writer_name) + for k,v in update.new_items.items(): + iu.payload.__setitem__(k, v, writer_name=update.writer_name) + else: + #print('Writing non-incr update by '+str(update.writer_name)) + iu._set_payload(update.new_items, writer_name=update.writer_name) + # _set_payload etc. have also incremented the revision number + self.call_iu_event_handlers(update.uid, local=True, event_type=ipaaca.iu.IUEventType.UPDATED, category=iu.category) + return iu.revision + + def _remote_request_resend(self, iu_resend_request_pack): + ''' Resend a requested IU over the specific hidden category.''' + if iu_resend_request_pack.uid not in self._iu_store: + LOGGER.warning("Remote side requested resending of non-existent IU "+str(iu_resend_request_pack.uid)) + return 0 + iu = self._iu_store[iu_resend_request_pack.uid] + with iu.revision_lock: + if iu_resend_request_pack.hidden_scope_name is not None and iu_resend_request_pack.hidden_scope_name is not '': + informer = self._get_informer(iu_resend_request_pack.hidden_scope_name) + informer.publishData(iu) + return iu.revision + else: + return 0 + + def _remote_commit(self, iu_commission): + '''Apply a remotely requested commit to one of the stored IUs.''' + if iu_commission.uid not in self._iu_store: + LOGGER.warning("Remote InBuffer tried to spuriously write non-existent IU "+str(iu_commission.uid)) + return 0 + iu = self._iu_store[iu_commission.uid] + with iu.revision_lock: + if (iu_commission.revision != 0) and (iu_commission.revision != iu.revision): + # (0 means "do not pay attention to the revision number" -> "force update") + LOGGER.warning("Remote write operation failed because request was out of date; IU "+str(iu_commission.uid)) + return 0 + if iu.committed: + return 0 + else: + iu._internal_commit(writer_name=iu_commission.writer_name) + self.call_iu_event_handlers(iu_commission.uid, local=True, event_type=ipaaca.iu.IUEventType.COMMITTED, category=iu.category) + return iu.revision + + def _get_informer(self, iu_category): + '''Return (or create, store and return) an informer object for IUs of the specified category.''' + if iu_category in self._informer_store: + LOGGER.info("Returning informer on scope "+"/ipaaca/channel/"+str(self._channel)+"/category/"+str(iu_category)) + return self._informer_store[iu_category] + informer_iu = rsb.createInformer( + rsb.Scope("/ipaaca/channel/"+str(self._channel)+"/category/"+str(iu_category)), + config=self._participant_config, + dataType=object) + self._informer_store[iu_category] = informer_iu #new_tuple + LOGGER.info("Returning NEW informer on scope "+"/ipaaca/channel/"+str(self._channel)+"/category/"+str(iu_category)) + return informer_iu #return new_tuple + + def add(self, iu): + '''Add an IU to the IU store, assign an ID and publish it.''' + if iu.uid in self._iu_store: + raise ipaaca.exception.IUPublishedError(iu) + if iu.buffer is not None: + raise ipaaca.exception.IUPublishedError(iu) + if iu.access_mode != ipaaca.iu.IUAccessMode.MESSAGE: + # Messages are not really stored in the OutputBuffer + self._iu_store[iu.uid] = iu + iu.buffer = self + self._publish_iu(iu) + + def remove(self, iu=None, iu_uid=None): + '''Remove the iu or an IU corresponding to iu_uid from the OutputBuffer, retracting it from the system.''' + if iu is None: + if iu_uid is None: + return None + else: + if iu_uid not in self. _iu_store: + raise ipaaca.exception.IUNotFoundError(iu_uid) + iu = self._iu_store[iu_uid] + # unpublish the IU + self._retract_iu(iu) + del self._iu_store[iu.uid] + return iu + + def _publish_iu(self, iu): + '''Publish an IU.''' + informer = self._get_informer(iu._category) + informer.publishData(iu) + + def _retract_iu(self, iu): + '''Retract (unpublish) an IU.''' + iu_retraction = ipaaca_pb2.IURetraction() + iu_retraction.uid = iu.uid + iu_retraction.revision = iu.revision + informer = self._get_informer(iu._category) + informer.publishData(iu_retraction) + + def _send_iu_commission(self, iu, writer_name): + '''Send IU commission. + + Keyword arguments: + iu -- the IU that has been committed to + writer_name -- name of the Buffer that initiated this commit, necessary + to enable remote components to filter out updates that originated + from their own operations + ''' + # a raw Protobuf object for IUCommission is produced + # (unlike updates, where we have an intermediate class) + iu_commission = ipaaca_pb2.IUCommission() + iu_commission.uid = iu.uid + iu_commission.revision = iu.revision + iu_commission.writer_name = iu.owner_name if writer_name is None else writer_name + informer = self._get_informer(iu._category) + informer.publishData(iu_commission) + + def _send_iu_link_update(self, iu, is_delta, revision, new_links=None, links_to_remove=None, writer_name="undef"): + '''Send an IU link update. + + Keyword arguments: + iu -- the IU being updated + is_delta -- whether this is an incremental update or a replacement + the whole link dictionary + revision -- the new revision number + new_links -- a dictionary of new link sets + links_to_remove -- a dict of the link sets that shall be removed + writer_name -- name of the Buffer that initiated this update, necessary + to enable remote components to filter out updates that originate d + from their own operations + ''' + if new_links is None: + new_links = {} + if links_to_remove is None: + links_to_remove = {} + link_update = ipaaca.converter.IULinkUpdate(iu._uid, is_delta=is_delta, revision=revision) + link_update.new_links = new_links + if is_delta: + link_update.links_to_remove = links_to_remove + link_update.writer_name = writer_name + informer = self._get_informer(iu._category) + informer.publishData(link_update) + # FIXME send the notification to the target, if the target is not the writer_name + + def _send_iu_payload_update(self, iu, is_delta, revision, new_items=None, keys_to_remove=None, writer_name="undef"): + '''Send an IU payload update. + + Keyword arguments: + iu -- the IU being updated + is_delta -- whether this is an incremental update or a replacement + revision -- the new revision number + new_items -- a dictionary of new payload items + keys_to_remove -- a list of the keys that shall be removed from the + payload + writer_name -- name of the Buffer that initiated this update, necessary + to enable remote components to filter out updates that originate d + from their own operations + ''' + if new_items is None: + new_items = {} + if keys_to_remove is None: + keys_to_remove = [] + payload_update = ipaaca.converter.IUPayloadUpdate( + uid=iu._uid, + revision=revision, + is_delta=is_delta, + payload_type=iu.payload_type) + payload_update.new_items = new_items + if is_delta: + payload_update.keys_to_remove = keys_to_remove + payload_update.writer_name = writer_name + informer = self._get_informer(iu._category) + informer.publishData(payload_update) diff --git a/ipaacalib/python/src/ipaaca/converter.py b/ipaacalib/python/src/ipaaca/converter.py new file mode 100644 index 0000000000000000000000000000000000000000..2bd3e690e32cf404a94070ed541e0e975e74c513 --- /dev/null +++ b/ipaacalib/python/src/ipaaca/converter.py @@ -0,0 +1,274 @@ +# -*- coding: utf-8 -*- + +# This file is part of IPAACA, the +# "Incremental Processing Architecture +# for Artificial Conversational Agents". +# +# Copyright (c) 2009-2014 Social Cognitive Systems Group +# CITEC, Bielefeld University +# +# http://opensource.cit-ec.de/projects/ipaaca/ +# http://purl.org/net/ipaaca +# +# This file may be licensed under the terms of of the +# GNU Lesser General Public License Version 3 (the ``LGPL''), +# or (at your option) any later version. +# +# Software distributed under the License is distributed +# on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either +# express or implied. See the LGPL for the specific language +# governing rights and limitations. +# +# You should have received a copy of the LGPL along with this +# program. If not, go to http://www.gnu.org/licenses/lgpl.html +# or write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The development of this software was supported by the +# Excellence Cluster EXC 277 Cognitive Interaction Technology. +# The Excellence Cluster EXC 277 is a grant of the Deutsche +# Forschungsgemeinschaft (DFG) in the context of the German +# Excellence Initiative. + +from __future__ import division, print_function + +import collections + +import rsb.converter + +import ipaaca_pb2 +import ipaaca.defaults +import ipaaca.exception +import ipaaca.iu +import ipaaca.misc + +LOGGER = ipaaca.misc.get_library_logger() + +try: + import simplejson as json +except ImportError: + import json + LOGGER.warn('INFO: Using module "json" instead of "simplejson". Install "simplejson" for better performance.') + + +__all__ = [ + 'IntConverter', + 'IUConverter', + 'IULinkUpdate', + 'IULinkUpdateConverter', + 'IUPayloadUpdate', + 'IUPayloadUpdateConverter', + 'MessageConverter', +] + + +class IntConverter(rsb.converter.Converter): + """Convert Python int objects to Protobuf ints and vice versa.""" + def __init__(self, wireSchema="int", dataType=int): + super(IntConverter, self).__init__(bytearray, dataType, wireSchema) + + def serialize(self, value): + pbo = ipaaca_pb2.IntMessage() + pbo.value = value + return bytearray(pbo.SerializeToString()), self.wireSchema + + def deserialize(self, byte_stream, ws): + pbo = ipaaca_pb2.IntMessage() + pbo.ParseFromString( str(byte_stream) ) + return pbo.value + + +def pack_payload_entry(entry, key, value, _type=ipaaca.iu.IUPayloadType.JSON): + entry.key = key + if _type == ipaaca.iu.IUPayloadType.JSON: + entry.value = json.dumps(value) + elif _type == ipaaca.iu.IUPayloadType.STR or _type == 'MAP': + entry.value = str(value) + else: + raise ipaaca.exception.IpaacaException('Asked to send payload entry with unsupported type "' + _type + '".') + entry.type = _type + + +def unpack_payload_entry(entry): + # We assume that the only transfer types are 'STR' or 'JSON'. Both are transparently handled by json.loads + if entry.type == ipaaca.iu.IUPayloadType.JSON: + return json.loads(entry.value) + elif entry.type == ipaaca.iu.IUPayloadType.STR or entry.type == 'str': + return entry.value + else: + LOGGER.warn('Received payload entry with unsupported type "' + entry.type + '".') + return entry.value + + +class IUConverter(rsb.converter.Converter): + ''' + Converter class for Full IU representations + wire:bytearray <-> wire-schema:ipaaca-full-iu <-> class ipaacaRSB.IU + ''' + def __init__(self, wireSchema="ipaaca-iu", dataType=ipaaca.iu.IU): + super(IUConverter, self).__init__(bytearray, dataType, wireSchema) + self._access_mode = ipaaca_pb2.IU.PUSH + self._remote_data_type = ipaaca.iu.RemotePushIU + + def serialize(self, iu): + pbo = ipaaca_pb2.IU() + pbo.access_mode = self._access_mode + pbo.uid = iu._uid + pbo.revision = iu._revision + pbo.category = iu._category + pbo.payload_type = iu._payload_type + pbo.owner_name = iu._owner_name + pbo.committed = iu._committed + pbo.read_only = iu._read_only + for k, v in iu._payload.iteritems(): + entry = pbo.payload.add() + pack_payload_entry(entry, k, v, iu.payload_type) + for type_ in iu._links.keys(): + linkset = pbo.links.add() + linkset.type = type_ + linkset.targets.extend(iu._links[type_]) + return bytearray(pbo.SerializeToString()), self.wireSchema + + def deserialize(self, byte_stream, ws): + pbo = ipaaca_pb2.IU() + pbo.ParseFromString(str(byte_stream)) + _payload = {} + for entry in pbo.payload: + _payload[entry.key] = unpack_payload_entry(entry) + _links = collections.defaultdict(set) + for linkset in pbo.links: + for target_uid in linkset.targets: + _links[linkset.type].add(target_uid) + return self._remote_data_type( + uid=pbo.uid, + revision=pbo.revision, + read_only = pbo.read_only, + owner_name = pbo.owner_name, + category = pbo.category, + payload_type = 'str' if pbo.payload_type is 'MAP' else pbo.payload_type, + committed = pbo.committed, + payload=_payload, + links=_links) + + +class MessageConverter(IUConverter): + ''' + Converter class for Full IU representations + wire:bytearray <-> wire-schema:ipaaca-full-iu <-> class ipaacaRSB.IU + ''' + def __init__(self, wireSchema="ipaaca-messageiu", dataType=ipaaca.iu.Message): + super(MessageConverter, self).__init__(wireSchema, dataType) + self._access_mode = ipaaca_pb2.IU.MESSAGE + self._remote_data_type = ipaaca.iu.RemoteMessage + + +class IULinkUpdate(object): + + def __init__(self, uid, revision, is_delta, writer_name="undef", new_links=None, links_to_remove=None): + super(IULinkUpdate, self).__init__() + self.uid = uid + self.revision = revision + self.writer_name = writer_name + self.is_delta = is_delta + self.new_links = collections.defaultdict(set) if new_links is None else collections.defaultdict(set, new_links) + self.links_to_remove = collections.defaultdict(set) if links_to_remove is None else collections.defaultdict(set, links_to_remove) + + def __str__(self): + s = 'LinkUpdate(' + 'uid=' + self.uid + ', ' + s += 'revision='+str(self.revision)+', ' + s += 'writer_name='+str(self.writer_name)+', ' + s += 'is_delta='+str(self.is_delta)+', ' + s += 'new_links = '+str(self.new_links)+', ' + s += 'links_to_remove = '+str(self.links_to_remove)+')' + return s + + +class IULinkUpdateConverter(rsb.converter.Converter): + + def __init__(self, wireSchema="ipaaca-iu-link-update", dataType=IULinkUpdate): + super(IULinkUpdateConverter, self).__init__(bytearray, dataType, wireSchema) + + def serialize(self, iu_link_update): + pbo = ipaaca_pb2.IULinkUpdate() + pbo.uid = iu_link_update.uid + pbo.writer_name = iu_link_update.writer_name + pbo.revision = iu_link_update.revision + for type_ in iu_link_update.new_links.keys(): + linkset = pbo.new_links.add() + linkset.type = type_ + linkset.targets.extend(iu_link_update.new_links[type_]) + for type_ in iu_link_update.links_to_remove.keys(): + linkset = pbo.links_to_remove.add() + linkset.type = type_ + linkset.targets.extend(iu_link_update.links_to_remove[type_]) + pbo.is_delta = iu_link_update.is_delta + return bytearray(pbo.SerializeToString()), self.wireSchema + + def deserialize(self, byte_stream, ws): + type = self.getDataType() + if type == IULinkUpdate: + pbo = ipaaca_pb2.IULinkUpdate() + pbo.ParseFromString( str(byte_stream) ) + LOGGER.debug('received an IULinkUpdate for revision '+str(pbo.revision)) + iu_link_up = IULinkUpdate( uid=pbo.uid, revision=pbo.revision, writer_name=pbo.writer_name, is_delta=pbo.is_delta) + for entry in pbo.new_links: + iu_link_up.new_links[str(entry.type)] = set(entry.targets) + for entry in pbo.links_to_remove: + iu_link_up.links_to_remove[str(entry.type)] = set(entry.targets) + return iu_link_up + else: + raise ValueError("Inacceptable dataType %s" % type) + + +class IUPayloadUpdate(object): + + def __init__(self, uid, revision, is_delta, payload_type, writer_name="undef", new_items=None, keys_to_remove=None): + super(IUPayloadUpdate, self).__init__() + self.uid = uid + self.revision = revision + self.payload_type = payload_type + self.writer_name = writer_name + self.is_delta = is_delta + self.new_items = {} if new_items is None else new_items + self.keys_to_remove = [] if keys_to_remove is None else keys_to_remove + + def __str__(self): + s = 'PayloadUpdate(' + 'uid=' + self.uid + ', ' + s += 'revision='+str(self.revision)+', ' + s += 'writer_name='+str(self.writer_name)+', ' + s += 'payload_type='+str(self.payload_type)+', ' + s += 'is_delta='+str(self.is_delta)+', ' + s += 'new_items = '+str(self.new_items)+', ' + s += 'keys_to_remove = '+str(self.keys_to_remove)+')' + return s + + +class IUPayloadUpdateConverter(rsb.converter.Converter): + def __init__(self, wireSchema="ipaaca-iu-payload-update", dataType=IUPayloadUpdate): + super(IUPayloadUpdateConverter, self).__init__(bytearray, dataType, wireSchema) + + def serialize(self, iu_payload_update): + pbo = ipaaca_pb2.IUPayloadUpdate() + pbo.uid = iu_payload_update.uid + pbo.writer_name = iu_payload_update.writer_name + pbo.revision = iu_payload_update.revision + for k, v in iu_payload_update.new_items.items(): + entry = pbo.new_items.add() + pack_payload_entry(entry, k, v, iu_payload_update.payload_type) + pbo.keys_to_remove.extend(iu_payload_update.keys_to_remove) + pbo.is_delta = iu_payload_update.is_delta + return bytearray(pbo.SerializeToString()), self.wireSchema + + def deserialize(self, byte_stream, ws): + type = self.getDataType() + if type == IUPayloadUpdate: + pbo = ipaaca_pb2.IUPayloadUpdate() + pbo.ParseFromString( str(byte_stream) ) + LOGGER.debug('received an IUPayloadUpdate for revision '+str(pbo.revision)) + iu_up = IUPayloadUpdate( uid=pbo.uid, revision=pbo.revision, payload_type=None, writer_name=pbo.writer_name, is_delta=pbo.is_delta) + for entry in pbo.new_items: + iu_up.new_items[entry.key] = unpack_payload_entry(entry) + iu_up.keys_to_remove = pbo.keys_to_remove[:] + return iu_up + else: + raise ValueError("Inacceptable dataType %s" % type) diff --git a/ipaacalib/python/src/ipaaca/defaults.py b/ipaacalib/python/src/ipaaca/defaults.py new file mode 100644 index 0000000000000000000000000000000000000000..706c714b1d270838f04a84f3cd20dffe432d747c --- /dev/null +++ b/ipaacalib/python/src/ipaaca/defaults.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +# This file is part of IPAACA, the +# "Incremental Processing Architecture +# for Artificial Conversational Agents". +# +# Copyright (c) 2009-2014 Social Cognitive Systems Group +# CITEC, Bielefeld University +# +# http://opensource.cit-ec.de/projects/ipaaca/ +# http://purl.org/net/ipaaca +# +# This file may be licensed under the terms of of the +# GNU Lesser General Public License Version 3 (the ``LGPL''), +# or (at your option) any later version. +# +# Software distributed under the License is distributed +# on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either +# express or implied. See the LGPL for the specific language +# governing rights and limitations. +# +# You should have received a copy of the LGPL along with this +# program. If not, go to http://www.gnu.org/licenses/lgpl.html +# or write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The development of this software was supported by the +# Excellence Cluster EXC 277 Cognitive Interaction Technology. +# The Excellence Cluster EXC 277 is a grant of the Deutsche +# Forschungsgemeinschaft (DFG) in the context of the German +# Excellence Initiative. + +IPAACA_DEFAULT_CHANNEL = 'default' + +IPAACA_LOGGER_NAME = 'ipaaca' +IPAACA_DEFAULT_LOGGING_LEVEL = 'WARNING' + +IPAACA_DEFAULT_IU_PAYLOAD_TYPE = 'JSON' # one of ipaaca.iu.IUPayloadType diff --git a/ipaacalib/python/src/ipaaca/exception.py b/ipaacalib/python/src/ipaaca/exception.py new file mode 100644 index 0000000000000000000000000000000000000000..f90a6756ef4f2935004e2b60fd76e14c612f60f4 --- /dev/null +++ b/ipaacalib/python/src/ipaaca/exception.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- + +# This file is part of IPAACA, the +# "Incremental Processing Architecture +# for Artificial Conversational Agents". +# +# Copyright (c) 2009-2014 Social Cognitive Systems Group +# CITEC, Bielefeld University +# +# http://opensource.cit-ec.de/projects/ipaaca/ +# http://purl.org/net/ipaaca +# +# This file may be licensed under the terms of of the +# GNU Lesser General Public License Version 3 (the ``LGPL''), +# or (at your option) any later version. +# +# Software distributed under the License is distributed +# on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either +# express or implied. See the LGPL for the specific language +# governing rights and limitations. +# +# You should have received a copy of the LGPL along with this +# program. If not, go to http://www.gnu.org/licenses/lgpl.html +# or write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The development of this software was supported by the +# Excellence Cluster EXC 277 Cognitive Interaction Technology. +# The Excellence Cluster EXC 277 is a grant of the Deutsche +# Forschungsgemeinschaft (DFG) in the context of the German +# Excellence Initiative. + +from __future__ import division, print_function + + +__all__ = [ + 'IpaacaError', + 'IUCommittedError', + 'IUNotFoundError', + 'IUPayloadLockedError', + 'IUPayloadLockTimeoutError', + 'IUPublishedError', + 'IUReadOnlyError', + 'IUResendRequestFailedError', + 'IUUpdateFailedError', +] + + +class IpaacaError(Exception): pass + + +class IUCommittedError(IpaacaError): + """Error indicating that an IU is immutable because it has been committed to.""" + def __init__(self, iu): + super(IUCommittedError, self).__init__('Writing to IU ' + str(iu.uid) + ' failed -- it has been committed to.') + + +class IUNotFoundError(IpaacaError): + """Error indicating that an IU UID was unexpectedly not found in an internal store.""" + def __init__(self, iu_uid): + super(IUNotFoundError, self).__init__('Lookup of IU ' + str(iu_uid) + ' failed.') + + +class IUPayloadLockTimeoutError(IpaacaError): + """Error indicating that exclusive access to the Payload could not be obtained in time.""" + def __init__(self, iu): + super(IUPayloadLockTimeoutError, self).__init__('Timeout while accessing payload of IU ' + str(iu.uid) + '.') + + +class IUPayloadLockedError(IpaacaError): + """Error indicating that exclusive access to the Payload could not be obtained because someone actually locked it.""" + def __init__(self, iu): + super(IUPayloadLockedError, self).__init__('IU '+str(iu.uid)+' was locked during access attempt.') + + +class IUPublishedError(IpaacaError): + """Error publishing of an IU failed since it is already in the buffer.""" + def __init__(self, iu): + super(IUPublishedError, self).__init__('IU ' + str(iu.uid) + ' is already present in the output buffer.') + + +class IUReadOnlyError(IpaacaError): + """Error indicating that an IU is immutable because it is 'read only'.""" + def __init__(self, iu): + super(IUReadOnlyError, self).__init__('Writing to IU ' + str(iu.uid) + ' failed -- it is read-only.') + + +class IUResendRequestFailedError(IpaacaError): + """Error indicating that a remote IU resend failed.""" + def __init__(self, iu): + super(IUResendFailedError, self).__init__('Remote resend failed for IU ' + str(iu.uid) + '.') + + +class IUUpdateFailedError(IpaacaError): + """Error indicating that a remote IU update failed.""" + def __init__(self, iu): + super(IUUpdateFailedError, self).__init__('Remote update failed for IU ' + str(iu.uid) + '.') diff --git a/ipaacalib/python/src/ipaaca/iu.py b/ipaacalib/python/src/ipaaca/iu.py new file mode 100644 index 0000000000000000000000000000000000000000..fd55bd78a3dc7b15ae248432cbce202331d8323e --- /dev/null +++ b/ipaacalib/python/src/ipaaca/iu.py @@ -0,0 +1,604 @@ +# -*- coding: utf-8 -*- + +# This file is part of IPAACA, the +# "Incremental Processing Architecture +# for Artificial Conversational Agents". +# +# Copyright (c) 2009-2014 Social Cognitive Systems Group +# CITEC, Bielefeld University +# +# http://opensource.cit-ec.de/projects/ipaaca/ +# http://purl.org/net/ipaaca +# +# This file may be licensed under the terms of of the +# GNU Lesser General Public License Version 3 (the ``LGPL''), +# or (at your option) any later version. +# +# Software distributed under the License is distributed +# on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either +# express or implied. See the LGPL for the specific language +# governing rights and limitations. +# +# You should have received a copy of the LGPL along with this +# program. If not, go to http://www.gnu.org/licenses/lgpl.html +# or write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The development of this software was supported by the +# Excellence Cluster EXC 277 Cognitive Interaction Technology. +# The Excellence Cluster EXC 277 is a grant of the Deutsche +# Forschungsgemeinschaft (DFG) in the context of the German +# Excellence Initiative. + +from __future__ import division, print_function + +import collections +import copy +import threading +import uuid + +import ipaaca_pb2 +import ipaaca.converter +import ipaaca.exception +import ipaaca.misc +import ipaaca.payload + + +__all__ = [ + 'IUAccessMode', + 'IUEventType', + 'IUPayloadType', + 'IU', + 'Message', +] + +LOGGER = ipaaca.misc.get_library_logger() + +IUAccessMode = ipaaca.misc.enum( + PUSH = 'PUSH', + REMOTE = 'REMOTE', + MESSAGE = 'MESSAGE' +) + + +IUEventType = ipaaca.misc.enum( + ADDED = 'ADDED', + COMMITTED = 'COMMITTED', + DELETED = 'DELETED', + RETRACTED = 'RETRACTED', + UPDATED = 'UPDATED', + LINKSUPDATED = 'LINKSUPDATED', + MESSAGE = 'MESSAGE' +) + + +IUPayloadType = ipaaca.misc.enum( + JSON = 'JSON', + STR = 'STR' +) + + +class IUInterface(object): + + """Base class of all specialised IU classes.""" + + def __init__(self, uid, access_mode=IUAccessMode.PUSH, read_only=False, payload_type=None): + """Creates an IU. + + Keyword arguments: + uid -- unique ID of this IU + access_mode -- access mode of this IU + read_only -- flag indicating whether this IU is read_only or not + """ + self._uid = uid + self._revision = None + self._category = None + self._owner_name = None + self._committed = False + self._retracted = False + self._access_mode = access_mode + self._read_only = read_only + self._payload_type = payload_type if payload_type is not None else ipaaca.defaults.IPAACA_DEFAULT_IU_PAYLOAD_TYPE + self._buffer = None + # payload is not present here + self._links = collections.defaultdict(set) + + def __str__(self): + s = unicode(self.__class__)+"{ " + s += "category="+("<None>" if self._category is None else self._category)+" " + s += "uid="+self._uid+" " + s += "(buffer="+(self.buffer.unique_name if self.buffer is not None else "<None>")+") " + s += "owner_name=" + ("<None>" if self.owner_name is None else self.owner_name) + " " + s += "payload={ " + for k,v in self.payload.items(): + s += k+":'"+unicode(v)+"', " + s += "} " + s += "links={ " + for t,ids in self.get_all_links().items(): + s += t+":'"+unicode(ids)+"', " + s += "} " + s += "}" + return s + + + def _add_and_remove_links(self, add, remove): + '''Just add and remove the new links in our links set, do not send an update here''' + '''Note: Also used for remotely enforced links updates.''' + for type in remove.keys(): self._links[type] -= set(remove[type]) + for type in add.keys(): self._links[type] |= set(add[type]) + def _replace_links(self, links): + '''Just wipe and replace our links set, do not send an update here''' + '''Note: Also used for remotely enforced links updates.''' + self._links = collections.defaultdict(set) + for type in links.keys(): self._links[type] |= set(links[type]) + + def add_links(self, type, targets, writer_name=None): + '''Attempt to add links if the conditions are met + and send an update message. Then call the local setter.''' + if not hasattr(targets, '__iter__'): targets=[targets] + self._modify_links(is_delta=True, new_links={type:targets}, links_to_remove={}, writer_name=writer_name) + self._add_and_remove_links( add={type:targets}, remove={} ) + def remove_links(self, type, targets, writer_name=None): + '''Attempt to remove links if the conditions are met + and send an update message. Then call the local setter.''' + if not hasattr(targets, '__iter__'): targets=[targets] + self._modify_links(is_delta=True, new_links={}, links_to_remove={type:targets}, writer_name=writer_name) + self._add_and_remove_links( add={}, remove={type:targets} ) + def modify_links(self, add, remove, writer_name=None): + '''Attempt to modify links if the conditions are met + and send an update message. Then call the local setter.''' + self._modify_links(is_delta=True, new_links=add, links_to_remove=remove, writer_name=writer_name) + self._add_and_remove_links( add=add, remove=remove ) + def set_links(self, links, writer_name=None): + '''Attempt to set (replace) links if the conditions are met + and send an update message. Then call the local setter.''' + self._modify_links(is_delta=False, new_links=links, links_to_remove={}, writer_name=writer_name) + self._replace_links( links=links ) + def get_links(self, type): + return set(self._links[type]) + def get_all_links(self): + return copy.deepcopy(self._links) + + def _get_revision(self): + return self._revision + revision = property(fget=_get_revision, doc='Revision number of the IU.') + + def _get_category(self): + return self._category + category = property(fget=_get_category, doc='Category of the IU.') + + def _get_payload_type(self): + return self._payload_type + def _set_payload_type(self, type): + if self._buffer is None: + self._payload_type = type + else: + raise IpaacaException('The IU is already in a buffer, cannot change payload type anymore.') + payload_type = property( + fget=_get_payload_type, + fset=_set_payload_type, + doc='Type of the IU payload') + + def _get_committed(self): + return self._committed + committed = property( + fget=_get_committed, + doc='Flag indicating whether this IU has been committed to.') + + def _get_retracted(self): + return self._retracted + retracted = property( + fget=_get_retracted, + doc='Flag indicating whether this IU has been retracted.') + + def _get_uid(self): + return self._uid + uid = property(fget=_get_uid, doc='Unique ID of the IU.') + + def _get_access_mode(self): + return self._access_mode + access_mode = property(fget=_get_access_mode, doc='Access mode of the IU.') + + def _get_read_only(self): + return self._read_only + read_only = property( + fget=_get_read_only, + doc='Flag indicating whether this IU is read only.') + + def _get_buffer(self): + return self._buffer + def _set_buffer(self, buffer): + if self._buffer is not None: + raise IpaacaException('The IU is already in a buffer, cannot move it.') + self._buffer = buffer + buffer = property( + fget=_get_buffer, + fset=_set_buffer, + doc='Buffer this IU is held in.') + + def _get_owner_name(self): + return self._owner_name + def _set_owner_name(self, owner_name): + if self._owner_name is not None: + raise Exception('The IU already has an owner name, cannot change it.') + self._owner_name = owner_name + owner_name = property( + fget=_get_owner_name, + fset=_set_owner_name, + doc="The IU's owner's name.") + + +class IU(IUInterface): + + """A local IU.""" + + def __init__(self, category='undef', access_mode=IUAccessMode.PUSH, read_only=False, payload_type=None): + super(IU, self).__init__(uid=None, access_mode=access_mode, read_only=read_only, payload_type=payload_type) + self._revision = 1 + self.uid = str(uuid.uuid4()) + self._category = str(category) + self.revision_lock = threading.RLock() + self._payload = ipaaca.payload.Payload(iu=self) + + def _modify_links(self, is_delta=False, new_links={}, links_to_remove={}, writer_name=None): + if self.committed: + raise ipaaca.exception.IUCommittedError(self) + with self.revision_lock: + # modify links locally + self._increase_revision_number() + if self.is_published: + # send update to remote holders + self.buffer._send_iu_link_update( + self, + revision=self.revision, + is_delta=is_delta, + new_links=new_links, + links_to_remove=links_to_remove, + writer_name=self.owner_name if writer_name is None else writer_name) + + def _modify_payload(self, is_delta=True, new_items={}, keys_to_remove=[], writer_name=None): + """Modify the payload: add or remove items from this payload locally and send update.""" + if self.committed: + raise ipaaca.exception.IUCommittedError(self) + with self.revision_lock: + # set item locally + # FIXME: Is it actually set locally? + self._increase_revision_number() + if self.is_published: + #print(' _modify_payload: running send_iu_pl_upd with writer name '+str(writer_name)) + # send update to remote holders + self.buffer._send_iu_payload_update( + self, + revision=self.revision, + is_delta=is_delta, + new_items=new_items, + keys_to_remove=keys_to_remove, + writer_name=self.owner_name if writer_name is None else writer_name) + + def _increase_revision_number(self): + self._revision += 1 + + def _internal_commit(self, writer_name=None): + if self.committed: + raise ipaaca.exception.IUCommittedError(self) + with self.revision_lock: + if not self._committed: + self._increase_revision_number() + self._committed = True + if self.buffer is not None: + self.buffer._send_iu_commission(self, writer_name=writer_name) + + def commit(self): + """Commit to this IU.""" + return self._internal_commit() + + def _get_payload(self): + return self._payload + def _set_payload(self, new_pl, writer_name=None): + if self.committed: + raise ipaaca.exception.IUCommittedError(self) + with self.revision_lock: + self._increase_revision_number() + self._payload = ipaaca.payload.Payload( + iu=self, + writer_name=None if self.buffer is None else (self.buffer.unique_name if writer_name is None else writer_name), + new_payload=new_pl) + payload = property( + fget=_get_payload, + fset=_set_payload, + doc='Payload dictionary of this IU.') + + def _get_is_published(self): + return self.buffer is not None + is_published = property( + fget=_get_is_published, + doc='Flag indicating whether this IU has been published or not.') + + def _set_buffer(self, buffer): + if self._buffer is not None: + raise Exception('The IU is already in a buffer, cannot move it.') + self._buffer = buffer + self.owner_name = buffer.unique_name + self._payload.owner_name = buffer.unique_name + buffer = property( + fget=IUInterface._get_buffer, + fset=_set_buffer, + doc='Buffer this IU is held in.') + + def _set_uid(self, uid): + if self._uid is not None: + raise AttributeError('The uid of IU ' + self.uid + ' has already been set, cannot change it.') + self._uid = uid + uid = property( + fget=IUInterface._get_uid, + fset=_set_uid, + doc='Unique ID of the IU.') + + +class Message(IU): + """Local IU of Message sub-type. Can be handled like a normal IU, but on the remote side it is only existent during the handler calls.""" + def __init__(self, category='undef', access_mode=IUAccessMode.MESSAGE, read_only=True, payload_type=None): + super(Message, self).__init__(category=str(category), access_mode=access_mode, read_only=read_only, payload_type=payload_type) + + def _modify_links(self, is_delta=False, new_links={}, links_to_remove={}, writer_name=None): + if self.is_published: + LOGGER.info('Info: modifying a Message after sending has no global effects') + + def _modify_payload(self, is_delta=True, new_items={}, keys_to_remove=[], writer_name=None): + if self.is_published: + LOGGER.info('Info: modifying a Message after sending has no global effects') + + def _increase_revision_number(self): + self._revision += 1 + + def _internal_commit(self, writer_name=None): + if self.is_published: + LOGGER.info('Info: committing to a Message after sending has no global effects') + + def commit(self): + return self._internal_commit() + + def _get_payload(self): + return self._payload + def _set_payload(self, new_pl, writer_name=None): + if self.is_published: + LOGGER.info('Info: modifying a Message after sending has no global effects') + else: + if self.committed: + raise ipaaca.exception.IUCommittedError(self) + with self.revision_lock: + self._increase_revision_number() + self._payload = ipaaca.payload.Payload( + iu=self, + writer_name=None if self.buffer is None else (self.buffer.unique_name if writer_name is None else writer_name), + new_payload=new_pl) + payload = property( + fget=_get_payload, + fset=_set_payload, + doc='Payload dictionary of this IU.') + + def _get_is_published(self): + return self.buffer is not None + is_published = property( + fget=_get_is_published, + doc='Flag indicating whether this IU has been published or not.') + + def _set_buffer(self, buffer): + if self._buffer is not None: + raise Exception('The IU is already in a buffer, cannot move it.') + self._buffer = buffer + self.owner_name = buffer.unique_name + self._payload.owner_name = buffer.unique_name + buffer = property( + fget=IUInterface._get_buffer, + fset=_set_buffer, + doc='Buffer this IU is held in.') + + def _set_uid(self, uid): + if self._uid is not None: + raise AttributeError('The uid of IU ' + self.uid + ' has already been set, cannot change it.') + self._uid = uid + uid = property( + fget=IUInterface._get_uid, + fset=_set_uid, + doc='Unique ID of the IU.') + + +class RemoteMessage(IUInterface): + + """A remote IU with access mode 'MESSAGE'.""" + + def __init__(self, uid, revision, read_only, owner_name, category, payload_type, committed, payload, links): + super(RemoteMessage, self).__init__(uid=uid, access_mode=IUAccessMode.PUSH, read_only=read_only, payload_type=payload_type) + self._revision = revision + self._category = category + self.owner_name = owner_name + self._committed = committed + self._retracted = False + # NOTE Since the payload is an already-existant Payload which we didn't modify ourselves, + # don't try to invoke any modification checks or network updates ourselves either. + # We are just receiving it here and applying the new data. + self._payload = ipaaca.payload.Payload(iu=self, new_payload=payload, omit_init_update_message=True) + self._links = links + + def _modify_links(self, is_delta=False, new_links={}, links_to_remove={}, writer_name=None): + LOGGER.info('Info: modifying a RemoteMessage only has local effects') + + def _modify_payload(self, is_delta=True, new_items={}, keys_to_remove=[], writer_name=None): + LOGGER.info('Info: modifying a RemoteMessage only has local effects') + + def commit(self): + LOGGER.info('Info: committing to a RemoteMessage only has local effects') + + def _get_payload(self): + return self._payload + def _set_payload(self, new_pl): + LOGGER.info('Info: modifying a RemoteMessage only has local effects') + self._payload = ipaaca.payload.Payload(iu=self, new_payload=new_pl, omit_init_update_message=True) + payload = property( + fget=_get_payload, + fset=_set_payload, + doc='Payload dictionary of the IU.') + + def _apply_link_update(self, update): + """Apply a IULinkUpdate to the IU.""" + LOGGER.warning('Warning: should never be called: RemoteMessage._apply_link_update') + self._revision = update.revision + if update.is_delta: + self._add_and_remove_links(add=update.new_links, remove=update.links_to_remove) + else: + self._replace_links(links=update.new_links) + + def _apply_update(self, update): + """Apply a IUPayloadUpdate to the IU.""" + LOGGER.warning('Warning: should never be called: RemoteMessage._apply_update') + self._revision = update.revision + if update.is_delta: + for k in update.keys_to_remove: self.payload._remotely_enforced_delitem(k) + for k, v in update.new_items.items(): self.payload._remotely_enforced_setitem(k, v) + else: + # NOTE Please read the comment in the constructor + self._payload = ipaaca.payload.Payload(iu=self, new_payload=update.new_items, omit_init_update_message=True) + + def _apply_commission(self): + """Apply commission to the IU""" + LOGGER.warning('Warning: should never be called: RemoteMessage._apply_commission') + self._committed = True + + def _apply_retraction(self): + """Apply retraction to the IU""" + LOGGER.warning('Warning: should never be called: RemoteMessage._apply_retraction') + self._retracted = True + + +class RemotePushIU(IUInterface): + + """A remote IU with access mode 'PUSH'.""" + + def __init__(self, uid, revision, read_only, owner_name, category, payload_type, committed, payload, links): + super(RemotePushIU, self).__init__(uid=uid, access_mode=IUAccessMode.PUSH, read_only=read_only, payload_type=payload_type) + self._revision = revision + self._category = category + self.owner_name = owner_name + self._committed = committed + self._retracted = False + # NOTE Since the payload is an already-existant Payload which we didn't modify ourselves, + # don't try to invoke any modification checks or network updates ourselves either. + # We are just receiving it here and applying the new data. + self._payload = ipaaca.payload.Payload(iu=self, new_payload=payload, omit_init_update_message=True) + self._links = links + + def _modify_links(self, is_delta=False, new_links={}, links_to_remove={}, writer_name=None): + """Modify the links: add or remove item from this payload remotely and send update.""" + if self.committed: + raise ipaaca.exception.IUCommittedError(self) + if self.read_only: + raise ipaaca.exception.IUReadOnlyError(self) + requested_update = ipaaca.converter.IULinkUpdate( + uid=self.uid, + revision=self.revision, + is_delta=is_delta, + writer_name=self.buffer.unique_name, + new_links=new_links, + links_to_remove=links_to_remove) + remote_server = self.buffer._get_remote_server(self) + new_revision = remote_server.updateLinks(requested_update) + if new_revision == 0: + raise ipaaca.exception.IUUpdateFailedError(self) + else: + self._revision = new_revision + + def _modify_payload(self, is_delta=True, new_items={}, keys_to_remove=[], writer_name=None): + """Modify the payload: add or remove item from this payload remotely and send update.""" + if self.committed: + raise ipaaca.exception.IUCommittedError(self) + if self.read_only: + raise ipaaca.exception.IUReadOnlyError(self) + requested_update = ipaaca.converter.IUPayloadUpdate( + uid=self.uid, + revision=self.revision, + payload_type=self.payload_type, + is_delta=is_delta, + writer_name=self.buffer.unique_name, + new_items=new_items, + keys_to_remove=keys_to_remove) + remote_server = self.buffer._get_remote_server(self) + new_revision = remote_server.updatePayload(requested_update) + if new_revision == 0: + raise ipaaca.exception.IUUpdateFailedError(self) + else: + self._revision = new_revision + + def commit(self): + """Commit to this IU.""" + if self.read_only: + raise ipaaca.exception.IUReadOnlyError(self) + if self._committed: + # ignore commit requests when already committed + return + else: + commission_request = ipaaca_pb2.IUCommission() + commission_request.uid = self.uid + commission_request.revision = self.revision + commission_request.writer_name = self.buffer.unique_name + remote_server = self.buffer._get_remote_server(self) + new_revision = remote_server.commit(commission_request) + if new_revision == 0: + raise ipaaca.exception.IUUpdateFailedError(self) + else: + self._revision = new_revision + self._committed = True + + def _get_payload(self): + return self._payload + def _set_payload(self, new_pl): + if self.committed: + raise ipaaca.exception.IUCommittedError(self) + if self.read_only: + raise ipaaca.exception.IUReadOnlyError(self) + requested_update = ipaaca.converter.IUPayloadUpdate( + uid=self.uid, + revision=self.revision, + payload_type=self.payload_type, + is_delta=False, + writer_name=self.buffer.unique_name, + new_items=new_pl, + keys_to_remove=[]) + remote_server = self.buffer._get_remote_server(self) + new_revision = remote_server.updatePayload(requested_update) + if new_revision == 0: + raise ipaaca.exception.IUUpdateFailedError(self) + else: + self._revision = new_revision + # NOTE Please read the comment in the constructor + self._payload = ipaaca.payload.Payload(iu=self, new_payload=new_pl, omit_init_update_message=True) + payload = property( + fget=_get_payload, + fset=_set_payload, + doc='Payload dictionary of the IU.') + + def _apply_link_update(self, update): + """Apply a IULinkUpdate to the IU.""" + self._revision = update.revision + if update.is_delta: + self._add_and_remove_links(add=update.new_links, remove=update.links_to_remove) + else: + self._replace_links(links=update.new_links) + + def _apply_update(self, update): + """Apply a IUPayloadUpdate to the IU.""" + self._revision = update.revision + if update.is_delta: + for k in update.keys_to_remove: self.payload._remotely_enforced_delitem(k) + for k, v in update.new_items.items(): self.payload._remotely_enforced_setitem(k, v) + else: + # NOTE Please read the comment in the constructor + self._payload = ipaaca.payload.Payload(iu=self, new_payload=update.new_items, omit_init_update_message=True) + + def _apply_commission(self): + """Apply commission to the IU""" + self._committed = True + + def _apply_retraction(self): + """Apply retraction to the IU""" + self._retracted = True diff --git a/ipaacalib/python/src/ipaaca/misc.py b/ipaacalib/python/src/ipaaca/misc.py new file mode 100644 index 0000000000000000000000000000000000000000..44932d5f47442df1b4ce44c5399a31433a374431 --- /dev/null +++ b/ipaacalib/python/src/ipaaca/misc.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- + +# This file is part of IPAACA, the +# "Incremental Processing Architecture +# for Artificial Conversational Agents". +# +# Copyright (c) 2009-2014 Social Cognitive Systems Group +# CITEC, Bielefeld University +# +# http://opensource.cit-ec.de/projects/ipaaca/ +# http://purl.org/net/ipaaca +# +# This file may be licensed under the terms of of the +# GNU Lesser General Public License Version 3 (the ``LGPL''), +# or (at your option) any later version. +# +# Software distributed under the License is distributed +# on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either +# express or implied. See the LGPL for the specific language +# governing rights and limitations. +# +# You should have received a copy of the LGPL along with this +# program. If not, go to http://www.gnu.org/licenses/lgpl.html +# or write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The development of this software was supported by the +# Excellence Cluster EXC 277 Cognitive Interaction Technology. +# The Excellence Cluster EXC 277 is a grant of the Deutsche +# Forschungsgemeinschaft (DFG) in the context of the German +# Excellence Initiative. + +from __future__ import division, print_function + + +import argparse +import logging + +import ipaaca.defaults + + +__all__ = [ + 'enum', + 'IpaacaArgumentParser', +] + + +def enum(*sequential, **named): + """Create an enum type. + + Based on suggestion of Alec Thomas on stackoverflow.com: + http://stackoverflow.com/questions/36932/ + whats-the-best-way-to-implement-an-enum-in-python/1695250#1695250 + """ + enums = dict(zip(sequential, range(len(sequential))), **named) + enums['_choices'] = enums.keys() + return type('Enum', (object,), enums) + + +class IpaacaLoggingHandler(logging.Handler): + '''A logging handler that prints to stdout.''' + + def __init__(self, prefix='IPAACA', level=logging.NOTSET): + logging.Handler.__init__(self, level) + self._prefix = prefix + + def emit(self, record): + meta = '[%s: %s] ' % (self._prefix, str(record.levelname)) + msg = str(record.msg.format(record.args)) + print(meta + msg) + + +class GenericNoLoggingHandler(logging.Handler): + '''A logging handler that produces no output''' + def emit(self, record): pass + + +def get_library_logger(): + '''Get ipaaca's library-wide logger object.''' + return logging.getLogger(ipaaca.defaults.IPAACA_LOGGER_NAME) + + +__IPAACA_LOGGING_HANDLER = IpaacaLoggingHandler('IPAACA') +__GENERIC_NO_LOG_HANDLER = GenericNoLoggingHandler() + +# By default, suppress library logging +# - for IPAACA +get_library_logger().addHandler(__GENERIC_NO_LOG_HANDLER) +# - for RSB +logging.getLogger('rsb').addHandler(__GENERIC_NO_LOG_HANDLER) + + +def enable_logging(level=None): + '''Enable ipaaca's 'library-wide logging.''' + ipaaca_logger = get_library_logger() + ipaaca_logger.addHandler(__IPAACA_LOGGING_HANDLER) + ipaaca_logger.removeHandler(__GENERIC_NO_LOG_HANDLER) + ipaaca_logger.setLevel(level=level if level is not None else + ipaaca.defaults.IPAACA_DEFAULT_LOGGING_LEVEL) + + +class IpaacaArgumentParser(argparse.ArgumentParser): + + class IpaacaDefaultChannelAction(argparse.Action): + + def __call__(self, parser, namespace, values, option_string=None): + ipaaca.defaults.IPAACA_DEFAULT_CHANNEL = values + + class IpaacaPayloadTypeAction(argparse.Action): + + def __call__(self, parser, namespace, values, option_string=None): + ipaaca.defaults.IPAACA_DEFAULT_IU_PAYLOAD_TYPE = values + + class IpaacaLoggingLevelAction(argparse.Action): + + def __call__(self, parser, namespace, values, option_string=None): + enable_logging(values) + + class IpaacaRSBLoggingLevelAction(argparse.Action): + + def __call__(self, parser, namespace, values, option_string=None): + rsb_logger = logging.getLogger('rsb') + rsb_logger.addHandler(IpaacaLoggingHandler('RSB')) + rsb_logger.removeHandler(__GENERIC_NO_LOG_HANDLER) + rsb_logger.setLevel(level=values) + + def __init__(self, prog=None, usage=None, description=None, epilog=None, + parents=[], formatter_class=argparse.HelpFormatter, + prefix_chars='-', fromfile_prefix_chars=None, + argument_default=None, conflict_handler='error', add_help=True): + super(IpaacaArgumentParser, self).__init__(prog=prog, usage=usage, + description=description, epilog=epilog, parents=parents, + formatter_class=formatter_class, prefix_chars=prefix_chars, + fromfile_prefix_chars=fromfile_prefix_chars, + argument_default=argument_default, + conflict_handler=conflict_handler, add_help=add_help) + + def _add_ipaaca_lib_arguments(self): + ipaacalib_group = self.add_argument_group('IPAACA library arguments') + ipaacalib_group.add_argument( + '--ipaaca-payload-type', + action=self.IpaacaPayloadTypeAction, + choices=['JSON', 'STR'], # one of ipaaca.iu.IUPayloadTypes + dest='_ipaaca_payload_type_', + default='JSON', + help="specify payload type (default: 'JSON')") + ipaacalib_group.add_argument( + '--ipaaca-default-channel', + action=self.IpaacaDefaultChannelAction, + default='default', + metavar='NAME', + dest='_ipaaca_default_channel_', + help="specify default IPAACA channel name (default: 'default')") + ipaacalib_group.add_argument( + '--ipaaca-enable-logging', + action=self.IpaacaLoggingLevelAction, + choices=['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG'], + dest='_ipaaca_logging_level_', + help="enable IPAACA logging with threshold") + rsblib_group = self.add_argument_group('RSB library arguments') + rsblib_group.add_argument( + '--rsb-enable-logging', + action=self.IpaacaRSBLoggingLevelAction, + choices=['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG'], + dest='_ipaaca_rsb_enable_logging_', + help="enable RSB logging with threshold") + + def parse_args(self, args=None, namespace=None): + self._add_ipaaca_lib_arguments() # Add ipaaca-args just before parsing + result = super(IpaacaArgumentParser, self).parse_args(args, namespace) + # Delete ipaaca specific arguments (beginning with '_ipaaca' and + # ending with an underscore) from the resulting Namespace object. + for item in dir(result): + if item.startswith('_ipaaca') and item.endswith('_'): + delattr(result, item) + return result diff --git a/ipaacalib/python/src/ipaaca/payload.py b/ipaacalib/python/src/ipaaca/payload.py new file mode 100644 index 0000000000000000000000000000000000000000..13a5e36524e380954f3cb4ba0bb0f2ced624e54c --- /dev/null +++ b/ipaacalib/python/src/ipaaca/payload.py @@ -0,0 +1,296 @@ +# -*- coding: utf-8 -*- + +# This file is part of IPAACA, the +# "Incremental Processing Architecture +# for Artificial Conversational Agents". +# +# Copyright (c) 2009-2014 Social Cognitive Systems Group +# CITEC, Bielefeld University +# +# http://opensource.cit-ec.de/projects/ipaaca/ +# http://purl.org/net/ipaaca +# +# This file may be licensed under the terms of of the +# GNU Lesser General Public License Version 3 (the ``LGPL''), +# or (at your option) any later version. +# +# Software distributed under the License is distributed +# on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either +# express or implied. See the LGPL for the specific language +# governing rights and limitations. +# +# You should have received a copy of the LGPL along with this +# program. If not, go to http://www.gnu.org/licenses/lgpl.html +# or write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The development of this software was supported by the +# Excellence Cluster EXC 277 Cognitive Interaction Technology. +# The Excellence Cluster EXC 277 is a grant of the Deutsche +# Forschungsgemeinschaft (DFG) in the context of the German +# Excellence Initiative. + +from __future__ import division, print_function + +import threading +import time + +import ipaaca.exception + + +__all__ = [ + 'Payload', + 'PayloadItemDictProxy', + 'PayloadItemListProxy', +] + + +_DEFAULT_PAYLOAD_UPDATE_TIMEOUT = 0.1 + + +class Payload(dict): + + def __init__(self, iu, writer_name=None, new_payload=None, omit_init_update_message=False, update_timeout=_DEFAULT_PAYLOAD_UPDATE_TIMEOUT): + self.iu = iu + _pl = {} + for k, v in ({} if new_payload is None else new_payload).iteritems(): + _pl[unicode(k, 'utf8') if type(k) == str else k] = unicode(v, 'utf8') if type(v) == str else v + # NOTE omit_init_update_message is necessary to prevent checking for + # exceptions and sending updates in the case where we just receive + # a whole new payload from the remote side and overwrite it locally. + for k, v in _pl.iteritems(): + dict.__setitem__(self, k, v) + if (not omit_init_update_message) and (self.iu.buffer is not None): + self.iu._modify_payload( + is_delta=False, + new_items=_pl, + keys_to_remove=[], + writer_name=writer_name) + self._update_on_every_change = True + self._update_timeout = update_timeout + self._collected_modifications = {} + self._collected_removals = [] + self._batch_update_writer_name = None # name of remote buffer or None + self._batch_update_lock = threading.RLock() + self._batch_update_cond = threading.Condition(threading.RLock()) + + def __getitem__(self, k): + value = dict.__getitem__(self, k) + if isinstance(value, dict): + return PayloadItemDictProxy(value, self, k) + elif isinstance(value, list): + return PayloadItemListProxy(value, self, k) + else: + return value + + def __setitem__(self, k, v, writer_name=None): + with self._batch_update_lock: + k = unicode(k, 'utf8') if type(k) == str else k + v = unicode(v, 'utf8') if type(v) == str else v + if self._update_on_every_change: + self.iu._modify_payload( + is_delta=True, + new_items={k:v}, + keys_to_remove=[], + writer_name=writer_name) + else: # Collect additions/modifications + self._batch_update_writer_name = writer_name + self._collected_modifications[k] = v + # revoke deletion of item since a new version has been added + self._collected_removals = [i for i in self._collected_removals if i!=k] + return dict.__setitem__(self, k, v) + + def __delitem__(self, k, writer_name=None): + with self._batch_update_lock: + k = unicode(k, 'utf8') if type(k) == str else k + if self._update_on_every_change: + self.iu._modify_payload( + is_delta=True, + new_items={}, + keys_to_remove=[k], + writer_name=writer_name) + else: # Collect additions/modifications + self._batch_update_writer_name = writer_name + self._collected_removals.append(k) + # revoke older update of item since more recent changes take precedence + if k in self._collected_modifications: del self._collected_modifications[k] + return dict.__delitem__(self, k) + + # Context-manager based batch updates, not thread-safe (on remote updates) + def __enter__(self): + self._wait_batch_update_lock(self._update_timeout) + self._update_on_every_change = False + + # Context-manager based batch updates, not thread-safe (on remote updates) + def __exit__(self, type, value, traceback): + self.iu._modify_payload( + is_delta=True, + new_items=self._collected_modifications, + keys_to_remove=self._collected_removals, + writer_name=self._batch_update_writer_name) + self._collected_modifications = {} + self._collected_removals = [] + self._update_on_every_change = True + self._batch_update_writer_name = None + self._batch_update_lock.release() + + def merge(self, payload, writer_name=None): + with self._batch_update_lock: + for k, v in payload.iteritems(): + k = unicode(k, 'utf8') if type(k) == str else k + v = unicode(v, 'utf8') if type(v) == str else v + self.iu._modify_payload( + is_delta=True, + new_items=payload, + keys_to_remove=[], + writer_name=writer_name) + return dict.update(self, payload) # batch update + + def _remotely_enforced_setitem(self, k, v): + """Sets an item when requested remotely.""" + dict.__setitem__(self, k, v) + + def _remotely_enforced_delitem(self, k): + """Deletes an item when requested remotely.""" + if k in self: dict.__delitem__(self, k) + + def _wait_batch_update_lock(self, timeout): + # wait lock with time-out http://stackoverflow.com/a/8393033 + with self._batch_update_cond: + current_time = start_time = time.time() + while current_time < start_time + timeout: + if self._batch_update_lock.acquire(False): + return True + else: + self._batch_update_cond.wait(timeout - current_time + start_time) + current_time = time.time() + raise ipaaca.exception.IUPayloadLockTimeoutError(self.iu) + + +class PayloadItemProxy(object): + + def __init__(self, content, payload, identifier_in_payload): + self.payload = payload + self.content = content + self.identifier_in_payload = identifier_in_payload + + def _notify_payload(self): + try: + self.payload[self.identifier_in_payload] = self.content + except ipaaca.exception.IUUpdateFailedError as e: + # IU update failed. Use the ResendRequest mechanism + # to replace the altered RemotePushIU with the unchanged + # payload from its OutputBuffer.'' + iu = self.payload.iu + iu.buffer._request_remote_resend(iu) + raise e # re-raise IUUpdateFailedError from aboves + + def _create_proxy(self, obj, identifier_in_payload): + if isinstance(obj, dict): + return PayloadItemDictProxy(obj, self.payload, identifier_in_payload) + elif isinstance(obj, list): + return PayloadItemListProxy(obj, self.payload, identifier_in_payload) + else: + return obj + + def __setitem__(self, k, v): + self.content.__setitem__(k,v) + self._notify_payload() + + def __getitem__(self, k): + item = self.content.__getitem__(k) + return self._create_proxy(item, k) + + def __delitem__(self, k): + self.content.__delitem__(k) + self._notify_payload() + + +class PayloadItemDictProxy(PayloadItemProxy, dict): + + def __init__(self, content, payload, identifier_in_payload): + dict.__init__(self, content) + PayloadItemProxy.__init__(self, content, payload, identifier_in_payload) + + def clear(self): + self.content.clear() + self._notify_payload() + + def get(self, key, default=None): + value = self.content.get(key, default) + return self._create_proxy(value, key) + + def items(self): + return [(key, value) for key, value in self.iteritems()] + + def iteritems(self): + for key, value in self.content.iteritems(): + yield key, self._create_proxy(value, key) + + def values(self): + return [value for value in self.itervalues()] + + def itervalues(self): + for key, value in self.content.iteritems(): + yield self._create_proxy(value, key) + + def pop(self, key, *args): + x = self.content.pop(key, *args) + self._notify_payload() + return x + + def popitem(self): + x = self.content.popitem() + self._notify_payload() + return x + + def setdefault(self, key, default=None): + notification_necessary = not key in self.content + x = self.content.setdefault(key, default) + if notification_necessary: + self._notify_payload() + return x + + def update(self, *args, **kwargs): + self.content.update(*args, **kwargs) + self._notify_payload() + + +class PayloadItemListProxy(PayloadItemProxy, list): + + def __init__(self, content, payload, identifier_in_payload): + list.__init__(self, content) + PayloadItemProxy.__init__(self, content, payload, identifier_in_payload) + + def __iter__(self): + for index, item in enumerate(self.content): + yield self._create_proxy(item, index) + + def append(self, x): + self.content.append(x) + self._notify_payload() + + def extend(self, l): + self.content.extend(l) + self._notify_payload() + + def insert(self, i, x): + self.content.insert(i, x) + self._notify_payload() + + def remove(self, x): + self.content.remove(x) + self._notify_payload() + + def pop(self, *args, **kwargs): + x = self.content.pop(*args, **kwargs) + self._notify_payload() + return x + + def sort(self, cmp=None, key=None, reverse=False): + self.content.sort(cmp, key, reverse) + self._notify_payload() + + def reverse(self): + self.content.reverse() + self._notify_payload() diff --git a/ipaacalib/python/src/ipaaca/util/__init__.py b/ipaacalib/python/src/ipaaca/util/__init__.py index 199d379b66d80b4e2f7a9c375d9e4aa7c53bef7a..c6727687b13273ea1f6fcb0acfe369ae90d2b213 100644 --- a/ipaacalib/python/src/ipaaca/util/__init__.py +++ b/ipaacalib/python/src/ipaaca/util/__init__.py @@ -1,9 +1,11 @@ +# -*- coding: utf-8 -*- + # This file is part of IPAACA, the # "Incremental Processing Architecture -# for Artificial Conversational Agents". +# for Artificial Conversational Agents". # -# Copyright (c) 2009-2013 Sociable Agents Group -# CITEC, Bielefeld University +# Copyright (c) 2009-2014 Social Cognitive Systems Group +# CITEC, Bielefeld University # # http://opensource.cit-ec.de/projects/ipaaca/ # http://purl.org/net/ipaaca @@ -20,7 +22,7 @@ # You should have received a copy of the LGPL along with this # program. If not, go to http://www.gnu.org/licenses/lgpl.html # or write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # The development of this software was supported by the # Excellence Cluster EXC 277 Cognitive Interaction Technology. @@ -28,4 +30,6 @@ # Forschungsgemeinschaft (DFG) in the context of the German # Excellence Initiative. +from __future__ import division, print_function + from notifier import ComponentNotifier diff --git a/ipaacalib/python/src/ipaaca/util/notifier.py b/ipaacalib/python/src/ipaaca/util/notifier.py index 1ee991e833af8a776b8c040ac8fc2d68e687b7b0..750d39affd5b657cf6c761d2a919d96fbbda9f8c 100644 --- a/ipaacalib/python/src/ipaaca/util/notifier.py +++ b/ipaacalib/python/src/ipaaca/util/notifier.py @@ -2,10 +2,10 @@ # This file is part of IPAACA, the # "Incremental Processing Architecture -# for Artificial Conversational Agents". +# for Artificial Conversational Agents". # -# Copyright (c) 2009-2013 Sociable Agents Group -# CITEC, Bielefeld University +# Copyright (c) 2009-2014 Social Cognitive Systems Group +# CITEC, Bielefeld University # # http://opensource.cit-ec.de/projects/ipaaca/ # http://purl.org/net/ipaaca @@ -22,7 +22,7 @@ # You should have received a copy of the LGPL along with this # program. If not, go to http://www.gnu.org/licenses/lgpl.html # or write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # The development of this software was supported by the # Excellence Cluster EXC 277 Cognitive Interaction Technology. @@ -30,23 +30,37 @@ # Forschungsgemeinschaft (DFG) in the context of the German # Excellence Initiative. -from __future__ import print_function, with_statement + +from __future__ import division, print_function import threading -import ipaaca -from ipaaca.util.timesync import * -NotificationState = ipaaca.enum( - NEW = 'new', - OLD = 'old', - DOWN = 'down' - ) +import ipaaca.buffer +import ipaaca.iu +import ipaaca.misc +import ipaaca.util.timesync + + +__all__ = [ + 'NotificationState', + 'ComponentError', + 'ComponentNotifier' +] + + +NotificationState = ipaaca.misc.enum( + NEW = 'new', + OLD = 'old', + DOWN = 'down' +) + class ComponentError(Exception): def __init__(self, msg): super(ComponentError, self).__init__(msg) + class ComponentNotifier(object): NOTIFY_CATEGORY = "componentNotify" @@ -61,8 +75,8 @@ class ComponentNotifier(object): self.component_function = component_function self.send_categories = frozenset(send_categories) self.receive_categories = frozenset(receive_categories) - self.in_buffer = in_buffer if in_buffer is not None else ipaaca.InputBuffer(component_name + 'Notifier') - self.out_buffer = out_buffer if out_buffer is not None else ipaaca.OutputBuffer(component_name + 'Notifier') + self.in_buffer = in_buffer if in_buffer is not None else ipaaca.buffer.InputBuffer(component_name + 'Notifier') + self.out_buffer = out_buffer if out_buffer is not None else ipaaca.buffer.OutputBuffer(component_name + 'Notifier') self.terminated = False self.initialized = False self.notification_handlers = [] @@ -77,28 +91,28 @@ class ComponentNotifier(object): def _submit_notify(self, is_new): with self.submit_lock: - notify_iu = ipaaca.Message(ComponentNotifier.NOTIFY_CATEGORY) + notify_iu = ipaaca.iu.Message(ComponentNotifier.NOTIFY_CATEGORY) notify_iu.payload = { ComponentNotifier.NAME: self.component_name, ComponentNotifier.FUNCTION: self.component_function, ComponentNotifier.SEND_CATEGORIES: ",".join(self.send_categories), ComponentNotifier.RECEIVE_CATEGORIES: ",".join(self.receive_categories), ComponentNotifier.STATE: NotificationState.NEW if is_new else NotificationState.OLD, - } + } self.out_buffer.add(notify_iu) - + def terminate(self): with self.submit_lock: if self.terminated: return self.terminated = True - notify_iu = ipaaca.Message(ComponentNotifier.NOTIFY_CATEGORY) + notify_iu = ipaaca.iu.Message(ComponentNotifier.NOTIFY_CATEGORY) notify_iu.payload = { ComponentNotifier.NAME: self.component_name, ComponentNotifier.FUNCTION: self.component_function, ComponentNotifier.SEND_CATEGORIES: ",".join(self.send_categories), ComponentNotifier.RECEIVE_CATEGORIES: ",".join(self.receive_categories), ComponentNotifier.STATE: NotificationState.DOWN, - } + } self.out_buffer.add(notify_iu) def _handle_iu_event(self, iu, event_type, local): @@ -125,30 +139,28 @@ class ComponentNotifier(object): def add_timesync_slave_handler(self, handler): self.timesync_slave_handlers.append(handler) - + def add_timesync_master_handler(self, handler): self.timesync_master_handlers.append(handler) - + def send_master_timesync(self): #if len(self.timesync_master_handlers)==0: # print('Warning: Sending a master timesync without a registered result callback.') self.timesync_master.send_master_timesync() - + def initialize(self): with self.initialize_lock: if self.terminated: raise ComponentError('Attempted to reinitialize component '+component_name+' after termination') if (not self.initialized): - self.timesync_slave = TimesyncSlave(component_name=self.component_name, timing_handler=self.launch_timesync_slave_handlers) - self.timesync_master = TimesyncMaster(component_name=self.component_name, timing_handler=self.launch_timesync_master_handlers) - self.in_buffer.register_handler(self._handle_iu_event, ipaaca.IUEventType.MESSAGE, ComponentNotifier.NOTIFY_CATEGORY) + self.timesync_slave = ipaaca.util.timesync.TimesyncSlave(component_name=self.component_name, timing_handler=self.launch_timesync_slave_handlers) + self.timesync_master = ipaaca.util.timesync.TimesyncMaster(component_name=self.component_name, timing_handler=self.launch_timesync_master_handlers) + self.in_buffer.register_handler(self._handle_iu_event, ipaaca.iu.IUEventType.MESSAGE, ComponentNotifier.NOTIFY_CATEGORY) self._submit_notify(True) self.initialized = True - + def __enter__(self): self.initialize() - + def __exit__(self, t, v, tb): self.terminate() - - diff --git a/ipaacalib/python/src/ipaaca/util/timesync.py b/ipaacalib/python/src/ipaaca/util/timesync.py index e3ec33e58398d37eb2b714c8625bbd3af22d1107..950a50f1cce1929f89d6701d20b1d9dfe62db120 100644 --- a/ipaacalib/python/src/ipaaca/util/timesync.py +++ b/ipaacalib/python/src/ipaaca/util/timesync.py @@ -1,13 +1,47 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- -import ipaaca +# This file is part of IPAACA, the +# "Incremental Processing Architecture +# for Artificial Conversational Agents". +# +# Copyright (c) 2009-2014 Social Cognitive Systems Group +# CITEC, Bielefeld University +# +# http://opensource.cit-ec.de/projects/ipaaca/ +# http://purl.org/net/ipaaca +# +# This file may be licensed under the terms of of the +# GNU Lesser General Public License Version 3 (the ``LGPL''), +# or (at your option) any later version. +# +# Software distributed under the License is distributed +# on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either +# express or implied. See the LGPL for the specific language +# governing rights and limitations. +# +# You should have received a copy of the LGPL along with this +# program. If not, go to http://www.gnu.org/licenses/lgpl.html +# or write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The development of this software was supported by the +# Excellence Cluster EXC 277 Cognitive Interaction Technology. +# The Excellence Cluster EXC 277 is a grant of the Deutsche +# Forschungsgemeinschaft (DFG) in the context of the German +# Excellence Initiative. + +from __future__ import division, print_function + import time +import ipaaca.buffer +import ipaaca.iu + + class TimesyncMaster(object): def __init__(self, component_name=None, timing_handler=None, debug_offset=0): - self.ob = ipaaca.OutputBuffer(('' if component_name is None else component_name)+'TimesyncMaster') - self.ib = ipaaca.InputBuffer(('' if component_name is None else component_name)+'TimesyncMaster', ['timesyncReply']) + self.ob = ipaaca.buffer.OutputBuffer(('' if component_name is None else component_name)+'TimesyncMaster') + self.ib = ipaaca.buffer.InputBuffer(('' if component_name is None else component_name)+'TimesyncMaster', ['timesyncReply']) # component name to report (None => use buffer name) self.component_name = component_name if component_name is not None else self.ob.unique_name # @@ -29,7 +63,7 @@ class TimesyncMaster(object): self.timing_handler = timing_handler def send_master_timesync(self): - iu = ipaaca.IU('timesyncRequest') + iu = ipaaca.iu.IU('timesyncRequest') self.master_t1 = self.get_time() iu.payload = { 'stage':'0', @@ -85,8 +119,8 @@ class TimesyncMaster(object): class TimesyncSlave(object): def __init__(self, component_name=None, timing_handler=None, debug_offset=0): - self.ob = ipaaca.OutputBuffer(('' if component_name is None else component_name)+'TimesyncSlave') - self.ib = ipaaca.InputBuffer(('' if component_name is None else component_name)+'TimesyncSlave', ['timesyncRequest']) + self.ob = ipaaca.buffer.OutputBuffer(('' if component_name is None else component_name)+'TimesyncSlave') + self.ib = ipaaca.buffer.InputBuffer(('' if component_name is None else component_name)+'TimesyncSlave', ['timesyncRequest']) # component name to report (None => use buffer name) self.component_name = component_name if component_name is not None else self.ib.unique_name self.ob.register_handler(self.handle_timesync_slave) @@ -113,7 +147,7 @@ class TimesyncSlave(object): if stage=='0': #print('Received stage 0 from master '+master) # initial reply to master - self.my_iu = ipaaca.IU('timesyncReply') + self.my_iu = ipaaca.iu.IU('timesyncReply') # TODO: add grounded_in link too? t1 = self.get_time() self.my_iu.payload = iu.payload @@ -146,5 +180,3 @@ class TimesyncSlave(object): self.timing_handler(master, self.component_name, latency, offset) def get_time(self): return time.time() + self.debug_offset - - diff --git a/ipaacalib/python/test/ivy.xml b/ipaacalib/python/test/ivy.xml index 9912781fe761ded2dc8e8bf0da44981473a56ed2..03db403ef45bbe4494c7fa1838a7d91ba1fc3f5f 100644 --- a/ipaacalib/python/test/ivy.xml +++ b/ipaacalib/python/test/ivy.xml @@ -6,3 +6,4 @@ <dependency org="nose" name="nose" rev="latest.release" /> </dependencies> </ivy-module> + diff --git a/ipaacasoa/cpp/CMakeLists.txt b/ipaacasoa/cpp/CMakeLists.txt index 9830e75d75edd7b6322c9bdaf7e1b59d942a70a9..1e5ddebc00ad53984cb295b1b5da40d8f2c2eb75 100644 --- a/ipaacasoa/cpp/CMakeLists.txt +++ b/ipaacasoa/cpp/CMakeLists.txt @@ -5,7 +5,7 @@ project (ipaaca_soa_cpp) install( DIRECTORY include - DESTINATION / + DESTINATION . FILES_MATCHING PATTERN "*.h" PATTERN "*.hh" PATTERN "*.hpp" PATTERN "*.inl" ) diff --git a/ipaacatools/scripts/ipaaca-iu-injector b/ipaacatools/scripts/ipaaca-iu-injector new file mode 100755 index 0000000000000000000000000000000000000000..5f464da52355b3b3cc6bdf8c975282c758a6bc97 --- /dev/null +++ b/ipaacatools/scripts/ipaaca-iu-injector @@ -0,0 +1,119 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of IPAACA, the +# "Incremental Processing Architecture +# for Artificial Conversational Agents". +# +# Copyright (c) 2009-2015 Social Cognitive Systems Group +# CITEC, Bielefeld University +# +# http://opensource.cit-ec.de/projects/ipaaca/ +# http://purl.org/net/ipaaca +# +# This file may be licensed under the terms of of the +# GNU Lesser General Public License Version 3 (the ``LGPL''), +# or (at your option) any later version. +# +# Software distributed under the License is distributed +# on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either +# express or implied. See the LGPL for the specific language +# governing rights and limitations. +# +# You should have received a copy of the LGPL along with this +# program. If not, go to http://www.gnu.org/licenses/lgpl.html +# or write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The development of this software was supported by the +# Excellence Cluster EXC 277 Cognitive Interaction Technology. +# The Excellence Cluster EXC 277 is a grant of the Deutsche +# Forschungsgemeinschaft (DFG) in the context of the German +# Excellence Initiative. + +from __future__ import division, print_function + +import itertools +import os +import platform +import sys +import time + +import ipaaca + + +def iu_update_handler(iu, event_type, local): + try: + print(event_type + ': ' + unicode(iu)) + except: + print(u"An error occurred printing an IU for an event of type "+event_type) + + +parser = ipaaca.IpaacaArgumentParser(description='Ipaaca IU Injector -- Sent ipaaca messages or IUs from the command line') +parser.add_argument( + '-t', '--type', + default='Message', + dest='iu_type', + choices = ['Message', 'IU'], + help="choose IU type to be send (default: 'Message')") +parser.add_argument( + '-k', '--keep-alive', + default=3.0, + dest='keep_alive', + metavar='SECONDS', + type=float, + help='set time in seconds to wait for potential IU updates (default: 3.0)') +parser.add_argument( + '-j', '--json-payload', + dest='json_payload', + action='store_true', + help='allow structured data to be sent (treats payload VALUE arguments as Python expressions)') +parser.add_argument( + '-c', '--category', + dest='category', + metavar='CATEGORY', + required=True, + help='set Message/IU category') +parser.add_argument( + '-p', '--payload', + default=[], + dest='payload', + metavar='KEY VALUE', + nargs='+', + help='set Message/IU payload ') + + +if __name__ == '__main__': + arguments = parser.parse_args() + + ob = ipaaca.OutputBuffer('IpaacaIUInjector') + ob.register_handler(iu_update_handler) + iu = ipaaca.Message(arguments.category) if arguments.iu_type == 'Message' else ipaaca.IU(arguments.category) + if arguments.json_payload: + # Treat payload values as Python expressions + iu.payload = {k: eval(v) for (k, v) in itertools.izip_longest(arguments.payload[::2], arguments.payload[1::2])} + else: + iu.payload = {k: v for (k, v) in itertools.izip_longest(arguments.payload[::2], arguments.payload[1::2])} + ob.add(iu) + + print( + u'Sent {iu_type} with category "{category}" and payload {{'.format(**vars(arguments)), + end='\n' if len(iu.payload) > 0 else '') + for k, v in iu.payload.items(): + print(u" '{key}': {value},".format(key=k, value=v)) + print(u'}.') + + # Wait for updates to the IU + try: + if arguments.iu_type == 'IU': + print('Waiting %s s for the IU to be updated.' % arguments.keep_alive) + time.sleep(arguments.keep_alive) + else: + time.sleep(0.1) + except KeyboardInterrupt: + pass + + if platform.system() == 'Windows': + os._exit(0) + else: + sys.exit(0) diff --git a/ipaacatools/scripts/ipaaca-iu-injector.py b/ipaacatools/scripts/ipaaca-iu-injector.py deleted file mode 100755 index b6a56de5658860b3a469d6a6f328d9c34d7ab41f..0000000000000000000000000000000000000000 --- a/ipaacatools/scripts/ipaaca-iu-injector.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env python - -# This file is part of IPAACA, the -# "Incremental Processing Architecture -# for Artificial Conversational Agents". -# -# Copyright (c) 2009-2013 Sociable Agents Group -# CITEC, Bielefeld University -# -# http://opensource.cit-ec.de/projects/ipaaca/ -# http://purl.org/net/ipaaca -# -# This file may be licensed under the terms of of the -# GNU Lesser General Public License Version 3 (the ``LGPL''), -# or (at your option) any later version. -# -# Software distributed under the License is distributed -# on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either -# express or implied. See the LGPL for the specific language -# governing rights and limitations. -# -# You should have received a copy of the LGPL along with this -# program. If not, go to http://www.gnu.org/licenses/lgpl.html -# or write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# The development of this software was supported by the -# Excellence Cluster EXC 277 Cognitive Interaction Technology. -# The Excellence Cluster EXC 277 is a grant of the Deutsche -# Forschungsgemeinschaft (DFG) in the context of the German -# Excellence Initiative. - -import logging -import sys -import time - -import ipaaca - -def my_update_handler(iu, event_type, local): - print(event_type+': '+str(iu)) - -if len(sys.argv)<2: - print "Please use the program as follows:" - print " "+sys.argv[0]+" [--class IU|Message] [--timeout <sec>] <categoryname> [<payloadkey> <payloadvalue>] [<k2> <v2>] ..." - sys.exit(1) - -iu_class = 'Message' -timeout = 3.0 -idx = 1 -keep_going = True -while keep_going: - keep_going = False - if sys.argv[idx]=='--class': - t = sys.argv[idx+1] - if t in ['Message', 'IU']: - iu_class = t - else: - print "Unknown IU class: "+t - sys.exit(1) - idx += 2 - keep_going = True - elif sys.argv[idx]=='--timeout': - timeout = float(sys.argv[idx+1]) - idx += 2 - keep_going = True - -cate = sys.argv[idx] -idx += 1 -pl={} -while len(sys.argv)>idx+1: - pl[sys.argv[idx]] = sys.argv[idx+1] - idx+=2 - -print "Sending "+iu_class+" of category "+cate -print " with payload "+str(pl) - -ob = ipaaca.OutputBuffer('IUInjector') -ob.register_handler(my_update_handler) -iu_top = ipaaca.IU(cate) -iu_top.payload = pl -ob.add(iu_top) -print iu_class+" sent." - -if iu_class=="IU": - print "Waiting "+str(timeout)+" sec for remote modifications..." - time.sleep(timeout) -else: - time.sleep(0.1) -print "done." - diff --git a/ipaacatools/scripts/ipaaca-iu-sniffer b/ipaacatools/scripts/ipaaca-iu-sniffer new file mode 100755 index 0000000000000000000000000000000000000000..a4db5847098f8a1078cf50d4051719a84642f8f6 --- /dev/null +++ b/ipaacatools/scripts/ipaaca-iu-sniffer @@ -0,0 +1,170 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of IPAACA, the +# "Incremental Processing Architecture +# for Artificial Conversational Agents". +# +# Copyright (c) 2009-2015 Social Cognitive Systems Group +# CITEC, Bielefeld University +# +# http://opensource.cit-ec.de/projects/ipaaca/ +# http://purl.org/net/ipaaca +# +# This file may be licensed under the terms of of the +# GNU Lesser General Public License Version 3 (the ``LGPL''), +# or (at your option) any later version. +# +# Software distributed under the License is distributed +# on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either +# express or implied. See the LGPL for the specific language +# governing rights and limitations. +# +# You should have received a copy of the LGPL along with this +# program. If not, go to http://www.gnu.org/licenses/lgpl.html +# or write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The development of this software was supported by the +# Excellence Cluster EXC 277 Cognitive Interaction Technology. +# The Excellence Cluster EXC 277 is a grant of the Deutsche +# Forschungsgemeinschaft (DFG) in the context of the German +# Excellence Initiative. + +from __future__ import division, print_function + +import logging +import os +import platform +import re +import sys +import time + +import ipaaca + + +def event_type_color(typ): + colors = { + 'ADDED': '32;1', + 'RETRACTED': '31;1', + 'UPDATED': '33;1', + 'MESSAGE': '34;1', + 'COMMITTED': '35;1', + 'LINKSUPDATED': '36;1', + 'RETRACTED': '37;1', + } + return colors.get(typ, '1') + +def highlight_if_color(s, c='1'): + return s if not arguments.color else '[' + c + 'm' + s +'[m' + +def pretty_printed_dict(d): + s='{\n' + for k, v in d.items(): + if isinstance(v, unicode) or isinstance(v, str): + v = "'"+unicode(v)+"'" + else: + v = unicode(v) + v2 = v if len(v) <= arguments.max_size else v[:arguments.max_size] + '<excess length omitted>' + v2.replace('\\','\\\\').replace('\n', highlight_if_color('\\n')) + s += "\t '%s': %s,\n" % (highlight_if_color(unicode(k),'1'), unicode(v2)) + s+='}' + return s + +def pretty_printed_iu_event(iu, event_type, local): + s = '' + t = time.time() + lt = time.localtime(t) + s += highlight_if_color('%.3f' % t, '1') + s += ' %02d:%02d:%02d' % (lt.tm_hour, lt.tm_min, lt.tm_sec) + s += ' ' + highlight_if_color('%-9s' % event_type, event_type_color(event_type)) + s += ' category=' + highlight_if_color(iu.category,event_type_color(event_type)) + s += ' channel=' + iu.buffer._channel + s += ' uid=' + iu.uid + s += ' owner=' + iu.owner_name + if event_type is not 'MESSAGE': + s += '\nlinks=' + pretty_printed_dict(iu.get_all_links()) + s += '\npayload=' + pretty_printed_dict(iu.payload) + return s + +def my_update_handler(iu, event_type, local): + if arguments.regex: + for cat in arguments.categories: # actually now regexs, not categories + if re.match(cat, iu.category): + break + else: + return + print(pretty_printed_iu_event(iu, event_type, local), end='\n\n') + + + +parser = ipaaca.IpaacaArgumentParser(description='Ipaaca IU Sniffer -- Selectively listen to IPAACA traffic') +parser.add_argument( + '-r', '--regex', + action='store_true', + dest='regex', + help='match categories by regular expressions') +parser.add_argument( + '-c', '--color', + action='store_true', + dest='color', + help='colorize output') +parser.add_argument( + '--channels', + dest='channels', + default=['default'], + metavar='CHANNEL', + nargs='+', + help="set the channels to listen on (otherwise 'default')") +parser.add_argument( + '--categories', + default=[''], + dest='categories', + metavar='CATEGORY', + nargs='+', + help='set categories (or regex patterns) to be matched') +parser.add_argument( + '--size-limit', + default=2048, + dest='max_size', + metavar='LIMIT', + type=int, + help='limit payload display chars (default: 2048)') + + +if __name__ == '__main__': + arguments = parser.parse_args() + + buffers = {} + for channel in arguments.channels: + buffers[channel] = ipaaca.InputBuffer( + 'IpaacaIUSniffer', + category_interests=arguments.categories if not arguments.regex else [''], + channel=channel, + resend_active=True) + buffers[channel].register_handler(my_update_handler) + + channellist = 's ' if len(arguments.channels) > 1 else ' ' + channellist += ', '.join(arguments.channels) + + print('Listening on channel' + channellist + ' for IU events of ', end='') + if arguments.categories == ['']: + print('any category ...') + else: + if arguments.regex: + print('whose category matches one of the regexes:') + else: + print('categories:') + print('\t' + ', '.join(arguments.categories)) + print('') + try: + while True: + time.sleep(1) + except KeyboardInterrupt: + pass + + if platform.system() == 'Windows': + os._exit(0) + else: + sys.exit(0) + diff --git a/ipaacatools/scripts/ipaaca-iu-sniffer.py b/ipaacatools/scripts/ipaaca-iu-sniffer.py deleted file mode 100755 index c7034c1d7a49e872946efd13848bf271f0e9fbe0..0000000000000000000000000000000000000000 --- a/ipaacatools/scripts/ipaaca-iu-sniffer.py +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env python - -# This file is part of IPAACA, the -# "Incremental Processing Architecture -# for Artificial Conversational Agents". -# -# Copyright (c) 2009-2013 Sociable Agents Group -# CITEC, Bielefeld University -# -# http://opensource.cit-ec.de/projects/ipaaca/ -# http://purl.org/net/ipaaca -# -# This file may be licensed under the terms of of the -# GNU Lesser General Public License Version 3 (the ``LGPL''), -# or (at your option) any later version. -# -# Software distributed under the License is distributed -# on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either -# express or implied. See the LGPL for the specific language -# governing rights and limitations. -# -# You should have received a copy of the LGPL along with this -# program. If not, go to http://www.gnu.org/licenses/lgpl.html -# or write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# The development of this software was supported by the -# Excellence Cluster EXC 277 Cognitive Interaction Technology. -# The Excellence Cluster EXC 277 is a grant of the Deutsche -# Forschungsgemeinschaft (DFG) in the context of the German -# Excellence Initiative. - -import logging -import sys -import time -import ipaaca - -color = False -max_size = 2048 - -def highlight_if_color(s, c='1'): - return s if not color else '['+c+'m'+s+'[m' - -def pretty_printed_iu_payload(iu): - s='{ ' - for k,v in iu.payload.items(): - v2 = (('\''+v+'\'') if len(v)<=max_size else ('\''+v[:max_size]+'\'<excess length omitted>')).replace('\\','\\\\').replace('\n',highlight_if_color('\\n')) - s += '\n' + '\t\t\'' + highlight_if_color(unicode(k),'1')+'\': '+unicode(v2)+', ' - s+=' }' - return s - -def event_type_color(typ): - colors={'ADDED':'32;1', 'RETRACTED':'31;1', 'UPDATED':'33;1', 'MESSAGE':'34;1'} - return '1' if typ not in colors else colors[typ] - -def pretty_printed_iu_event(iu, event_type, local): - s='' - t=time.time() - lt=time.localtime(t) - s += highlight_if_color('%.3f'%t, '1')+' '+"%02d:%02d:%02d"%(lt.tm_hour, lt.tm_min, lt.tm_sec) - s += ' '+highlight_if_color('%-9s'%event_type,event_type_color(event_type))+' category='+highlight_if_color(iu.category,event_type_color(event_type))+' uid='+iu.uid+' owner='+iu.owner_name+' payload='+pretty_printed_iu_payload(iu) - return s - -def my_update_handler(iu, event_type, local): - t=time.localtime() - print pretty_printed_iu_event(iu, event_type, local) - -cats = [] - -keep_going=True -idx = 1 -while keep_going: - opt = sys.argv[idx] if idx<len(sys.argv) else None - if opt=='--help': - print('IU sniffer - usage:') - print(' '+sys.argv[0]+' [--options] [<category1> [<category2 ...]]') - print(' Listen to specified categories (default: all)') - print(' Option --color : colorize output') - print(' Option --size-limit <size> : limit payload display, chars (def: 2048)') - sys.exit(0) - elif opt=='--color': - color = True - idx += 1 - elif opt=='--size-limit': - if len(sys.argv)<idx+2: - print('Please specify a max size') - sys.exit(1) - max_size = int(sys.argv[idx+1]) - idx += 2 - else: - cats = sys.argv[idx:] - keep_going = False - -ib = ipaaca.InputBuffer('SnifferIn', [''] if len(cats)==0 else cats) -ib.register_handler(my_update_handler) - -print('') -print('Ipaaca IU Sniffer - run with --help to see options') -print('Listening for IU events of '+('any category...' if len(cats)==0 else 'categories: '+' '.join(cats))) -print('') -while True: - time.sleep(1) -